Merge "Camera2: fix video stabilization test" into marshmallow-cts-dev
am: adfddc122c
* commit 'adfddc122c709a85ac9a75ba0a9fcdf7c17aca18':
Camera2: fix video stabilization test
diff --git a/CtsTestCaseList.mk b/CtsTestCaseList.mk
index 4d30dda..975ac47 100644
--- a/CtsTestCaseList.mk
+++ b/CtsTestCaseList.mk
@@ -100,6 +100,7 @@
CtsManagedProfileApp \
CtsMonkeyApp \
CtsMonkeyApp2 \
+ CtsPackageInstallerApp \
CtsPermissionApp \
CtsSimpleApp \
CtsSimplePreMApp \
@@ -176,6 +177,7 @@
CtsNdefTestCases \
CtsNetTestCases \
CtsNetTestCasesLegacyApi22 \
+ CtsNetTestCasesLegacyPermission22 \
CtsOpenGLTestCases \
CtsOpenGlPerfTestCases \
CtsOsTestCases \
@@ -192,6 +194,7 @@
CtsSecurityTestCases \
CtsSignatureTestCases \
CtsSpeechTestCases \
+ CtsSystemUiTestCases \
CtsTelecomTestCases \
CtsTelecomTestCases2 \
CtsTelephonyTestCases \
diff --git a/apps/CameraITS/pymodules/its/device.py b/apps/CameraITS/pymodules/its/device.py
index 756f959..835a4a4 100644
--- a/apps/CameraITS/pymodules/its/device.py
+++ b/apps/CameraITS/pymodules/its/device.py
@@ -448,8 +448,8 @@
The out_surfaces field can specify the width(s), height(s), and
format(s) of the captured image. The formats may be "yuv", "jpeg",
- "dng", "raw", "raw10", or "raw12". The default is a YUV420 frame ("yuv")
- corresponding to a full sensor frame.
+ "dng", "raw", "raw10", "raw12", or "rawStats". The default is a YUV420
+ frame ("yuv") corresponding to a full sensor frame.
Note that one or more surfaces can be specified, allowing a capture to
request images back in multiple formats (e.g.) raw+yuv, raw+jpeg,
@@ -536,6 +536,25 @@
yuv_caps = do_capture( [req1,req2], yuv_fmt )
yuv_caps, raw_caps = do_capture( [req1,req2], [yuv_fmt,raw_fmt] )
+ The "rawStats" format processes the raw image and returns a new image
+ of statistics from the raw image. The format takes additional keys,
+ "gridWidth" and "gridHeight" which are size of grid cells in a 2D grid
+ of the raw image. For each grid cell, the mean and variance of each raw
+ channel is computed, and the do_capture call returns two 4-element float
+ images of dimensions (rawWidth / gridWidth, rawHeight / gridHeight),
+ concatenated back-to-back, where the first iamge contains the 4-channel
+ means and the second contains the 4-channel variances.
+
+ For the rawStats format, if the gridWidth is not provided then the raw
+ image width is used as the default, and similarly for gridHeight. With
+ this, the following is an example of a output description that computes
+ the mean and variance across each image row:
+
+ {
+ "gridHeight": 1,
+ "format": "rawStats"
+ }
+
Args:
cap_request: The Python dict/list specifying the capture(s), which
will be converted to JSON and sent to the device.
@@ -550,7 +569,8 @@
* data: the image data as a numpy array of bytes.
* width: the width of the captured image.
* height: the height of the captured image.
- * format: image the format, in ["yuv","jpeg","raw","raw10","dng"].
+ * format: image the format, in [
+ "yuv","jpeg","raw","raw10","raw12","rawStats","dng"].
* metadata: the capture result object (Python dictionary).
"""
cmd = {}
@@ -577,9 +597,13 @@
nsurf = 1 if out_surfaces is None else len(cmd["outputSurfaces"])
if len(formats) > len(set(formats)):
raise its.error.Error('Duplicate format requested')
- if "dng" in formats and "raw" in formats or \
- "dng" in formats and "raw10" in formats or \
- "raw" in formats and "raw10" in formats:
+ raw_formats = 0;
+ raw_formats += 1 if "dng" in formats else 0
+ raw_formats += 1 if "raw" in formats else 0
+ raw_formats += 1 if "raw10" in formats else 0
+ raw_formats += 1 if "raw12" in formats else 0
+ raw_formats += 1 if "rawStats" in formats else 0
+ if raw_formats > 1:
raise its.error.Error('Different raw formats not supported')
# Detect long exposure time and set timeout accordingly
@@ -603,14 +627,16 @@
# the burst, however individual images of different formats can come
# out in any order for that capture.
nbufs = 0
- bufs = {"yuv":[], "raw":[], "raw10":[], "dng":[], "jpeg":[]}
+ bufs = {"yuv":[], "raw":[], "raw10":[], "raw12":[],
+ "rawStats":[], "dng":[], "jpeg":[]}
mds = []
widths = None
heights = None
while nbufs < ncap*nsurf or len(mds) < ncap:
jsonObj,buf = self.__read_response_from_socket()
if jsonObj['tag'] in ['jpegImage', 'yuvImage', 'rawImage', \
- 'raw10Image', 'dngImage'] and buf is not None:
+ 'raw10Image', 'raw12Image', 'rawStatsImage', 'dngImage'] \
+ and buf is not None:
fmt = jsonObj['tag'][:-5]
bufs[fmt].append(buf)
nbufs += 1
diff --git a/apps/CameraITS/pymodules/its/image.py b/apps/CameraITS/pymodules/its/image.py
index ea01a3e..a5ac60b 100644
--- a/apps/CameraITS/pymodules/its/image.py
+++ b/apps/CameraITS/pymodules/its/image.py
@@ -81,6 +81,25 @@
else:
raise its.error.Error('Invalid format %s' % (cap["format"]))
+def unpack_rawstats_capture(cap):
+ """Unpack a rawStats capture to the mean and variance images.
+
+ Args:
+ cap: A capture object as returned by its.device.do_capture.
+
+ Returns:
+ Tuple (mean_image var_image) of float-4 images, with non-normalized
+ pixel values computed from the RAW16 images on the device
+ """
+ assert(cap["format"] == "rawStats")
+ w = cap["width"]
+ h = cap["height"]
+ img = numpy.ndarray(shape=(2*h*w*4,), dtype='<f', buffer=cap["data"])
+ analysis_image = img.reshape(2,h,w,4)
+ mean_image = analysis_image[0,:,:,:].reshape(h,w,4)
+ var_image = analysis_image[1,:,:,:].reshape(h,w,4)
+ return mean_image, var_image
+
def unpack_raw10_capture(cap, props):
"""Unpack a raw-10 capture to a raw-16 capture.
@@ -604,6 +623,21 @@
variances.append(numpy.var(img[:,:,i], dtype=numpy.float64))
return variances
+def compute_image_snrs(img):
+ """Calculate the SNR (db) of each color channel in the image.
+
+ Args:
+ img: Numpy float image array, with pixel values in [0,1].
+
+ Returns:
+ A list of SNR value, one per color channel in the image.
+ """
+ means = compute_image_means(img)
+ variances = compute_image_variances(img)
+ std_devs = [math.sqrt(v) for v in variances]
+ snr = [20 * math.log10(m/s) for m,s in zip(means, std_devs)]
+ return snr
+
def write_image(img, fname, apply_gamma=False):
"""Save a float-3 numpy array image to a file.
diff --git a/apps/CameraITS/pymodules/its/objects.py b/apps/CameraITS/pymodules/its/objects.py
index 82346ec..ac384fb 100644
--- a/apps/CameraITS/pymodules/its/objects.py
+++ b/apps/CameraITS/pymodules/its/objects.py
@@ -152,24 +152,37 @@
return req
-def get_available_output_sizes(fmt, props):
+def get_available_output_sizes(fmt, props, max_size=None, match_ar_size=None):
"""Return a sorted list of available output sizes for a given format.
Args:
fmt: the output format, as a string in
["jpg", "yuv", "raw", "raw10", "raw12"].
props: the object returned from its.device.get_camera_properties().
+ max_size: (Optional) A (w,h) tuple.
+ Sizes larger than max_size (either w or h) will be discarded.
+ match_ar_size: (Optional) A (w,h) tuple.
+ Sizes not matching the aspect ratio of match_ar_size will be
+ discarded.
Returns:
A sorted list of (w,h) tuples (sorted large-to-small).
"""
- fmt_codes = {"raw":0x20, "raw10":0x25, "raw12":0x26, "yuv":0x23,
+ AR_TOLERANCE = 0.03
+ fmt_codes = {"raw":0x20, "raw10":0x25, "raw12":0x26,"yuv":0x23,
"jpg":0x100, "jpeg":0x100}
configs = props['android.scaler.streamConfigurationMap']\
['availableStreamConfigurations']
fmt_configs = [cfg for cfg in configs if cfg['format'] == fmt_codes[fmt]]
out_configs = [cfg for cfg in fmt_configs if cfg['input'] == False]
out_sizes = [(cfg['width'],cfg['height']) for cfg in out_configs]
+ if max_size:
+ out_sizes = [s for s in out_sizes if
+ s[0] <= max_size[0] and s[1] <= max_size[1]]
+ if match_ar_size:
+ ar = match_ar_size[0] / float(match_ar_size[1])
+ out_sizes = [s for s in out_sizes if
+ abs(ar - s[0] / float(s[1])) <= AR_TOLERANCE]
out_sizes.sort(reverse=True)
return out_sizes
diff --git a/apps/CameraITS/tests/inprog/test_rawstats.py b/apps/CameraITS/tests/inprog/test_rawstats.py
new file mode 100644
index 0000000..8083f0b
--- /dev/null
+++ b/apps/CameraITS/tests/inprog/test_rawstats.py
@@ -0,0 +1,48 @@
+# Copyright 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.
+
+import its.image
+import its.caps
+import its.device
+import its.objects
+import its.target
+import os.path
+import math
+
+def main():
+ """Test capturing some rawstats data.
+ """
+ NAME = os.path.basename(__file__).split(".")[0]
+
+ with its.device.ItsSession() as cam:
+
+ cam.do_3a(do_af=False);
+ req = its.objects.auto_capture_request()
+
+ for (gw,gh) in [(16,16)]:#,(4080,1)]:
+ cap = cam.do_capture(req,
+ {"format":"rawStats","gridWidth":gw,"gridHeight":gh})
+ mean_image, var_image = its.image.unpack_rawstats_capture(cap)
+
+ if gw > 1 and gh > 1:
+ h,w,_ = mean_image.shape
+ for ch in range(4):
+ m = mean_image[:,:,ch].reshape(h,w,1)/1023.0
+ v = var_image[:,:,ch].reshape(h,w,1)
+ its.image.write_image(m, "%s_mean_ch%d.jpg" % (NAME,ch), True)
+ its.image.write_image(v, "%s_var_ch%d.jpg" % (NAME,ch), True)
+
+if __name__ == '__main__':
+ main()
+
diff --git a/apps/CameraITS/tests/scene1/test_exposure.py b/apps/CameraITS/tests/scene1/test_exposure.py
index d217bdb..89bc724 100644
--- a/apps/CameraITS/tests/scene1/test_exposure.py
+++ b/apps/CameraITS/tests/scene1/test_exposure.py
@@ -35,12 +35,16 @@
THRESHOLD_MAX_OUTLIER_DIFF = 0.1
THRESHOLD_MIN_LEVEL = 0.1
THRESHOLD_MAX_LEVEL = 0.9
- THRESHOLD_MAX_LEVEL_DIFF = 0.025
+ THRESHOLD_MAX_LEVEL_DIFF = 0.03
+ THRESHOLD_MAX_LEVEL_DIFF_WIDE_RANGE = 0.05
+ THRESHOLD_ROUND_DOWN_GAIN = 0.1
+ THRESHOLD_ROUND_DOWN_EXP = 0.05
mults = []
r_means = []
g_means = []
b_means = []
+ threshold_max_level_diff = THRESHOLD_MAX_LEVEL_DIFF
with its.device.ItsSession() as cam:
props = cam.get_camera_properties()
@@ -48,27 +52,44 @@
its.caps.per_frame_control(props))
e,s = its.target.get_target_exposure_combos(cam)["minSensitivity"]
+ s_e_product = s*e
expt_range = props['android.sensor.info.exposureTimeRange']
sens_range = props['android.sensor.info.sensitivityRange']
- m = 1
+ m = 1.0
while s*m < sens_range[1] and e/m > expt_range[0]:
mults.append(m)
- req = its.objects.manual_capture_request(s*m, e/m)
+ s_test = round(s*m)
+ e_test = s_e_product / s_test
+ print "Testsing s:", s_test, "e:", e_test
+ req = its.objects.manual_capture_request(s_test, e_test)
cap = cam.do_capture(req)
+ s_res = cap["metadata"]["android.sensor.sensitivity"]
+ e_res = cap["metadata"]["android.sensor.exposureTime"]
+ assert(0 <= s_test - s_res < s_test * THRESHOLD_ROUND_DOWN_GAIN)
+ assert(0 <= e_test - e_res < e_test * THRESHOLD_ROUND_DOWN_EXP)
+ s_e_product_res = s_res * e_res
+ request_result_ratio = s_e_product / s_e_product_res
+ print "Capture result s:", s_test, "e:", e_test
img = its.image.convert_capture_to_rgb_image(cap)
- its.image.write_image(img, "%s_mult=%02d.jpg" % (NAME, m))
+ its.image.write_image(img, "%s_mult=%3.2f.jpg" % (NAME, m))
tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
rgb_means = its.image.compute_image_means(tile)
- r_means.append(rgb_means[0])
- g_means.append(rgb_means[1])
- b_means.append(rgb_means[2])
- m = m + 4
+ # Adjust for the difference between request and result
+ r_means.append(rgb_means[0] * request_result_ratio)
+ g_means.append(rgb_means[1] * request_result_ratio)
+ b_means.append(rgb_means[2] * request_result_ratio)
+ # Test 3 steps per 2x gain
+ m = m * pow(2, 1.0 / 3)
+
+ # Allow more threshold for devices with wider exposure range
+ if m >= 64.0:
+ threshold_max_level_diff = THRESHOLD_MAX_LEVEL_DIFF_WIDE_RANGE
# Draw a plot.
- pylab.plot(mults, r_means, 'r')
- pylab.plot(mults, g_means, 'g')
- pylab.plot(mults, b_means, 'b')
+ pylab.plot(mults, r_means, 'r.-')
+ pylab.plot(mults, g_means, 'g.-')
+ pylab.plot(mults, b_means, 'b.-')
pylab.ylim([0,1])
matplotlib.pyplot.savefig("%s_plot_means.png" % (NAME))
@@ -83,7 +104,7 @@
max_diff = max_val - min_val
print "Channel %d line fit (y = mx+b): m = %f, b = %f" % (chan, m, b)
print "Channel max %f min %f diff %f" % (max_val, min_val, max_diff)
- assert(max_diff < THRESHOLD_MAX_LEVEL_DIFF)
+ assert(max_diff < threshold_max_level_diff)
assert(b > THRESHOLD_MIN_LEVEL and b < THRESHOLD_MAX_LEVEL)
for v in values:
assert(v > THRESHOLD_MIN_LEVEL and v < THRESHOLD_MAX_LEVEL)
diff --git a/apps/CameraITS/tests/scene1/test_param_noise_reduction.py b/apps/CameraITS/tests/scene1/test_param_noise_reduction.py
index 2c8d73b..1072684 100644
--- a/apps/CameraITS/tests/scene1/test_param_noise_reduction.py
+++ b/apps/CameraITS/tests/scene1/test_param_noise_reduction.py
@@ -35,13 +35,13 @@
"""
NAME = os.path.basename(__file__).split(".")[0]
- RELATIVE_ERROR_TOLERANCE = 0.1
+ NUM_SAMPLES_PER_MODE = 4
+ SNR_TOLERANCE = 3 # unit in db
+ # List of SNRs for R,G,B.
+ snrs = [[], [], []]
- # List of variances for Y,U,V.
- variances = [[],[],[]]
-
- # Reference (baseline) variance for each of Y,U,V.
- ref_variance = []
+ # Reference (baseline) SNR for each of R,G,B.
+ ref_snr = []
nr_modes_reported = []
@@ -56,74 +56,89 @@
req = its.objects.manual_capture_request(s, e)
req["android.noiseReduction.mode"] = 0
cap = cam.do_capture(req)
+ rgb_image = its.image.convert_capture_to_rgb_image(cap)
its.image.write_image(
- its.image.convert_capture_to_rgb_image(cap),
+ rgb_image,
"%s_low_gain.jpg" % (NAME))
- planes = its.image.convert_capture_to_planes(cap)
- for j in range(3):
- img = planes[j]
- tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
- ref_variance.append(its.image.compute_image_variances(tile)[0])
- print "Ref variances:", ref_variance
+ rgb_tile = its.image.get_image_patch(rgb_image, 0.45, 0.45, 0.1, 0.1)
+ ref_snr = its.image.compute_image_snrs(rgb_tile)
+ print "Ref SNRs:", ref_snr
+ e, s = its.target.get_target_exposure_combos(cam)["maxSensitivity"]
# NR modes 0, 1, 2, 3, 4 with high gain
for mode in range(5):
# Skip unavailable modes
if not its.caps.noise_reduction_mode(props, mode):
nr_modes_reported.append(mode)
for channel in range(3):
- variances[channel].append(0)
+ snrs[channel].append(0)
continue;
- e, s = its.target.get_target_exposure_combos(cam)["maxSensitivity"]
- req = its.objects.manual_capture_request(s, e)
- req["android.noiseReduction.mode"] = mode
- cap = cam.do_capture(req)
- nr_modes_reported.append(
- cap["metadata"]["android.noiseReduction.mode"])
- its.image.write_image(
- its.image.convert_capture_to_rgb_image(cap),
- "%s_high_gain_nr=%d.jpg" % (NAME, mode))
- planes = its.image.convert_capture_to_planes(cap)
- for j in range(3):
- img = planes[j]
- tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
- variance = its.image.compute_image_variances(tile)[0]
- variances[j].append(variance / ref_variance[j])
- print "Variances with NR mode [0,1,2,3,4]:", variances
+ rgb_snr_list = []
+ # Capture several images to account for per frame noise variations
+ for n in range(NUM_SAMPLES_PER_MODE):
+ req = its.objects.manual_capture_request(s, e)
+ req["android.noiseReduction.mode"] = mode
+ cap = cam.do_capture(req)
+ rgb_image = its.image.convert_capture_to_rgb_image(cap)
+ if n == 0:
+ nr_modes_reported.append(
+ cap["metadata"]["android.noiseReduction.mode"])
+ its.image.write_image(
+ rgb_image,
+ "%s_high_gain_nr=%d.jpg" % (NAME, mode))
+ rgb_tile = its.image.get_image_patch(
+ rgb_image, 0.45, 0.45, 0.1, 0.1)
+ rgb_snrs = its.image.compute_image_snrs(rgb_tile)
+ rgb_snr_list.append(rgb_snrs)
+
+ r_snrs = [rgb[0] for rgb in rgb_snr_list]
+ g_snrs = [rgb[1] for rgb in rgb_snr_list]
+ b_snrs = [rgb[2] for rgb in rgb_snr_list]
+ rgb_snrs = [numpy.mean(r_snrs), numpy.mean(g_snrs), numpy.mean(b_snrs)]
+ print "NR mode", mode, "SNRs:"
+ print " R SNR:", rgb_snrs[0],\
+ "Min:", min(r_snrs), "Max:", max(r_snrs)
+ print " G SNR:", rgb_snrs[1],\
+ "Min:", min(g_snrs), "Max:", max(g_snrs)
+ print " B SNR:", rgb_snrs[2],\
+ "Min:", min(b_snrs), "Max:", max(b_snrs)
+
+ for chan in range(3):
+ snrs[chan].append(rgb_snrs[chan])
# Draw a plot.
for j in range(3):
- pylab.plot(range(5), variances[j], "rgb"[j])
- matplotlib.pyplot.savefig("%s_plot_variances.png" % (NAME))
+ pylab.plot(range(5), snrs[j], "rgb"[j])
+ matplotlib.pyplot.savefig("%s_plot_SNRs.png" % (NAME))
assert(nr_modes_reported == [0,1,2,3,4])
for j in range(3):
- # Smaller variance is better
+ # Larger SNR is better
# Verify OFF(0) is not better than FAST(1)
- assert(variances[j][0] >
- variances[j][1] * (1.0 - RELATIVE_ERROR_TOLERANCE))
+ assert(snrs[j][0] <
+ snrs[j][1] + SNR_TOLERANCE)
# Verify FAST(1) is not better than HQ(2)
- assert(variances[j][1] >
- variances[j][2] * (1.0 - RELATIVE_ERROR_TOLERANCE))
+ assert(snrs[j][1] <
+ snrs[j][2] + SNR_TOLERANCE)
# Verify HQ(2) is better than OFF(0)
- assert(variances[j][0] > variances[j][2])
+ assert(snrs[j][0] < snrs[j][2])
if its.caps.noise_reduction_mode(props, 3):
# Verify OFF(0) is not better than MINIMAL(3)
- assert(variances[j][0] >
- variances[j][3] * (1.0 - RELATIVE_ERROR_TOLERANCE))
+ assert(snrs[j][0] <
+ snrs[j][3] + SNR_TOLERANCE)
# Verify MINIMAL(3) is not better than HQ(2)
- assert(variances[j][3] >
- variances[j][2] * (1.0 - RELATIVE_ERROR_TOLERANCE))
+ assert(snrs[j][3] <
+ snrs[j][2] + SNR_TOLERANCE)
if its.caps.noise_reduction_mode(props, 4):
# Verify ZSL(4) is close to MINIMAL(3)
- assert(numpy.isclose(variances[j][4], variances[j][3],
- RELATIVE_ERROR_TOLERANCE))
+ assert(numpy.isclose(snrs[j][4], snrs[j][3],
+ atol=SNR_TOLERANCE))
elif its.caps.noise_reduction_mode(props, 4):
# Verify ZSL(4) is close to OFF(0)
- assert(numpy.isclose(variances[j][4], variances[j][0],
- RELATIVE_ERROR_TOLERANCE))
+ assert(numpy.isclose(snrs[j][4], snrs[j][0],
+ atol=SNR_TOLERANCE))
if __name__ == '__main__':
main()
diff --git a/apps/CameraITS/tests/scene1/test_raw_burst_sensitivity.py b/apps/CameraITS/tests/scene1/test_raw_burst_sensitivity.py
index 6c2b5c1..e176312 100644
--- a/apps/CameraITS/tests/scene1/test_raw_burst_sensitivity.py
+++ b/apps/CameraITS/tests/scene1/test_raw_burst_sensitivity.py
@@ -44,6 +44,8 @@
# Expose for the scene with min sensitivity
sens_min, sens_max = props['android.sensor.info.sensitivityRange']
+ # Digital gains might not be visible on RAW data
+ sens_max = props['android.sensor.maxAnalogSensitivity']
sens_step = (sens_max - sens_min) / NUM_STEPS
s_ae,e_ae,_,_,_ = cam.do_3a(get_results=True)
s_e_prod = s_ae * e_ae
diff --git a/apps/CameraITS/tests/scene1/test_raw_sensitivity.py b/apps/CameraITS/tests/scene1/test_raw_sensitivity.py
index 14c5eb0..cc0ce14 100644
--- a/apps/CameraITS/tests/scene1/test_raw_sensitivity.py
+++ b/apps/CameraITS/tests/scene1/test_raw_sensitivity.py
@@ -42,6 +42,8 @@
# Expose for the scene with min sensitivity
sens_min, sens_max = props['android.sensor.info.sensitivityRange']
+ # Digital gains might not be visible on RAW data
+ sens_max = props['android.sensor.maxAnalogSensitivity']
sens_step = (sens_max - sens_min) / NUM_STEPS
s_ae,e_ae,_,_,_ = cam.do_3a(get_results=True)
s_e_prod = s_ae * e_ae
diff --git a/apps/CameraITS/tests/scene1/test_reprocess_noise_reduction.py b/apps/CameraITS/tests/scene1/test_reprocess_noise_reduction.py
index 757dfeb..f0a6fbe 100644
--- a/apps/CameraITS/tests/scene1/test_reprocess_noise_reduction.py
+++ b/apps/CameraITS/tests/scene1/test_reprocess_noise_reduction.py
@@ -38,7 +38,8 @@
NAME = os.path.basename(__file__).split(".")[0]
- RELATIVE_ERROR_TOLERANCE = 0.1
+ NUM_SAMPLES_PER_MODE = 4
+ SNR_TOLERANCE = 3 # unit in db
with its.device.ItsSession() as cam:
props = cam.get_camera_properties()
@@ -60,7 +61,7 @@
for reprocess_format in reprocess_formats:
# List of variances for R, G, B.
- variances = []
+ snrs = [[], [], []]
nr_modes_reported = []
# NR mode 0 with low gain
@@ -77,71 +78,90 @@
img = its.image.decompress_jpeg_to_rgb_image(cap["data"])
its.image.write_image(img, "%s_low_gain_fmt=jpg.jpg" % (NAME))
tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
- ref_variance = its.image.compute_image_variances(tile)
- print "Ref variances:", ref_variance
+ ref_snr = its.image.compute_image_snrs(tile)
+ print "Ref SNRs:", ref_snr
+ e, s = its.target.get_target_exposure_combos(cam)["maxSensitivity"]
for nr_mode in range(5):
# Skip unavailable modes
if not its.caps.noise_reduction_mode(props, nr_mode):
nr_modes_reported.append(nr_mode)
- variances.append(0)
+ for channel in range(3):
+ snrs[channel].append(0)
continue
- # NR modes with high gain
- e, s = its.target.get_target_exposure_combos(cam) \
- ["maxSensitivity"]
- req = its.objects.manual_capture_request(s, e)
- req["android.noiseReduction.mode"] = nr_mode
- cap = cam.do_capture(req, out_surface, reprocess_format)
- nr_modes_reported.append(
- cap["metadata"]["android.noiseReduction.mode"])
+ rgb_snr_list = []
+ # Capture several images to account for per frame noise
+ # variations
+ for n in range(NUM_SAMPLES_PER_MODE):
+ req = its.objects.manual_capture_request(s, e)
+ req["android.noiseReduction.mode"] = nr_mode
+ cap = cam.do_capture(req, out_surface, reprocess_format)
- img = its.image.decompress_jpeg_to_rgb_image(cap["data"])
- its.image.write_image(
- img, "%s_high_gain_nr=%d_fmt=jpg.jpg" % (NAME, nr_mode))
- tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
- # Get the variances for R, G, and B channels
- variance = its.image.compute_image_variances(tile)
- variances.append(
- [variance[chan] / ref_variance[chan] for chan in range(3)])
- print "Variances with NR mode [0,1,2,3,4]:", variances
+ img = its.image.decompress_jpeg_to_rgb_image(cap["data"])
+ if n == 0:
+ its.image.write_image(
+ img,
+ "%s_high_gain_nr=%d_fmt=jpg.jpg"
+ %(NAME, nr_mode))
+ nr_modes_reported.append(
+ cap["metadata"]["android.noiseReduction.mode"])
+
+ tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
+ # Get the variances for R, G, and B channels
+ rgb_snrs = its.image.compute_image_snrs(tile)
+ rgb_snr_list.append(rgb_snrs)
+
+ r_snrs = [rgb[0] for rgb in rgb_snr_list]
+ g_snrs = [rgb[1] for rgb in rgb_snr_list]
+ b_snrs = [rgb[2] for rgb in rgb_snr_list]
+ rgb_snrs = [numpy.mean(r_snrs),
+ numpy.mean(g_snrs),
+ numpy.mean(b_snrs)]
+ print "NR mode", nr_mode, "SNRs:"
+ print " R SNR:", rgb_snrs[0],\
+ "Min:", min(r_snrs), "Max:", max(r_snrs)
+ print " G SNR:", rgb_snrs[1],\
+ "Min:", min(g_snrs), "Max:", max(g_snrs)
+ print " B SNR:", rgb_snrs[2],\
+ "Min:", min(b_snrs), "Max:", max(b_snrs)
+
+ for chan in range(3):
+ snrs[chan].append(rgb_snrs[chan])
# Draw a plot.
- for chan in range(3):
- line = []
- for nr_mode in range(5):
- line.append(variances[nr_mode][chan])
- pylab.plot(range(5), line, "rgb"[chan])
+ for channel in range(3):
+ pylab.plot(range(5), snrs[channel], "rgb"[channel])
- matplotlib.pyplot.savefig("%s_plot_%s_variances.png" %
+ matplotlib.pyplot.savefig("%s_plot_%s_SNRs.png" %
(NAME, reprocess_format))
assert(nr_modes_reported == [0,1,2,3,4])
for j in range(3):
- # Smaller variance is better
+ # Larger is better
# Verify OFF(0) is not better than FAST(1)
- assert(variances[0][j] >
- variances[1][j] * (1.0 - RELATIVE_ERROR_TOLERANCE))
+ assert(snrs[j][0] <
+ snrs[j][1] + SNR_TOLERANCE)
# Verify FAST(1) is not better than HQ(2)
- assert(variances[1][j] >
- variances[2][j] * (1.0 - RELATIVE_ERROR_TOLERANCE))
+ assert(snrs[j][1] <
+ snrs[j][2] + SNR_TOLERANCE)
# Verify HQ(2) is better than OFF(0)
- assert(variances[0][j] > variances[2][j])
+ assert(snrs[j][0] < snrs[j][2])
if its.caps.noise_reduction_mode(props, 3):
# Verify OFF(0) is not better than MINIMAL(3)
- assert(variances[0][j] >
- variances[3][j] * (1.0 - RELATIVE_ERROR_TOLERANCE))
+ assert(snrs[j][0] <
+ snrs[j][3] + SNR_TOLERANCE)
# Verify MINIMAL(3) is not better than HQ(2)
- assert(variances[3][j] >
- variances[2][j] * (1.0 - RELATIVE_ERROR_TOLERANCE))
+ assert(snrs[j][3] <
+ snrs[j][2] + SNR_TOLERANCE)
# Verify ZSL(4) is close to MINIMAL(3)
- assert(numpy.isclose(variances[4][j], variances[3][j],
- RELATIVE_ERROR_TOLERANCE))
+ assert(numpy.isclose(snrs[j][4], snrs[j][3],
+ atol=SNR_TOLERANCE))
else:
# Verify ZSL(4) is close to OFF(0)
- assert(numpy.isclose(variances[4][j], variances[0][j],
- RELATIVE_ERROR_TOLERANCE))
+ assert(numpy.isclose(snrs[j][4], snrs[j][0],
+ atol=SNR_TOLERANCE))
if __name__ == '__main__':
main()
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_dng.py b/apps/CameraITS/tests/scene1/test_yuv_plus_dng.py
index 33e7763..268b64a 100644
--- a/apps/CameraITS/tests/scene1/test_yuv_plus_dng.py
+++ b/apps/CameraITS/tests/scene1/test_yuv_plus_dng.py
@@ -31,6 +31,12 @@
cam.do_3a()
req = its.objects.auto_capture_request()
+ max_dng_size = \
+ its.objects.get_available_output_sizes("raw", props)[0]
+ w,h = its.objects.get_available_output_sizes(
+ "yuv", props, (1920, 1080), max_dng_size)[0]
+ out_surfaces = [{"format":"dng"},
+ {"format":"yuv", "width":w, "height":h}]
cap_dng, cap_yuv = cam.do_capture(req, cam.CAP_DNG_YUV)
img = its.image.convert_capture_to_rgb_image(cap_yuv)
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_jpeg.py b/apps/CameraITS/tests/scene1/test_yuv_plus_jpeg.py
index 9ce8d76..78378eb 100644
--- a/apps/CameraITS/tests/scene1/test_yuv_plus_jpeg.py
+++ b/apps/CameraITS/tests/scene1/test_yuv_plus_jpeg.py
@@ -27,13 +27,17 @@
THRESHOLD_MAX_RMS_DIFF = 0.01
- fmt_yuv = {"format":"yuv"}
- fmt_jpeg = {"format":"jpeg"}
-
with its.device.ItsSession() as cam:
props = cam.get_camera_properties()
its.caps.skip_unless(its.caps.compute_target_exposure(props))
+ max_jpeg_size = \
+ its.objects.get_available_output_sizes("jpeg", props)[0]
+ w,h = its.objects.get_available_output_sizes(
+ "yuv", props, (1920, 1080), max_jpeg_size)[0]
+ fmt_yuv = {"format":"yuv", "width":w, "height":h}
+ fmt_jpeg = {"format":"jpeg"}
+
# Use a manual request with a linear tonemap so that the YUV and JPEG
# should look the same (once converted by the its.image module).
e, s = its.target.get_target_exposure_combos(cam)["midExposureTime"]
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_raw.py b/apps/CameraITS/tests/scene1/test_yuv_plus_raw.py
index f13801b..bfa6a28 100644
--- a/apps/CameraITS/tests/scene1/test_yuv_plus_raw.py
+++ b/apps/CameraITS/tests/scene1/test_yuv_plus_raw.py
@@ -38,7 +38,13 @@
e, s = its.target.get_target_exposure_combos(cam)["midExposureTime"]
req = its.objects.manual_capture_request(s, e, True, props)
- cap_raw, cap_yuv = cam.do_capture(req, cam.CAP_RAW_YUV)
+ max_raw_size = \
+ its.objects.get_available_output_sizes("raw", props)[0]
+ w,h = its.objects.get_available_output_sizes(
+ "yuv", props, (1920, 1080), max_raw_size)[0]
+ out_surfaces = [{"format":"raw"},
+ {"format":"yuv", "width":w, "height":h}]
+ cap_raw, cap_yuv = cam.do_capture(req, out_surfaces)
img = its.image.convert_capture_to_rgb_image(cap_yuv)
its.image.write_image(img, "%s_yuv.jpg" % (NAME), True)
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_raw10.py b/apps/CameraITS/tests/scene1/test_yuv_plus_raw10.py
index e52946d..322af10 100644
--- a/apps/CameraITS/tests/scene1/test_yuv_plus_raw10.py
+++ b/apps/CameraITS/tests/scene1/test_yuv_plus_raw10.py
@@ -38,8 +38,13 @@
e, s = its.target.get_target_exposure_combos(cam)["midExposureTime"]
req = its.objects.manual_capture_request(s, e, True, props)
+ max_raw10_size = \
+ its.objects.get_available_output_sizes("raw10", props)[0]
+ w,h = its.objects.get_available_output_sizes(
+ "yuv", props, (1920, 1080), max_raw10_size)[0]
cap_raw, cap_yuv = cam.do_capture(req,
- [{"format":"raw10"}, {"format":"yuv"}])
+ [{"format":"raw10"},
+ {"format":"yuv", "width":w, "height":h}])
img = its.image.convert_capture_to_rgb_image(cap_yuv)
its.image.write_image(img, "%s_yuv.jpg" % (NAME), True)
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_raw12.py b/apps/CameraITS/tests/scene1/test_yuv_plus_raw12.py
index c5c3c73..b3cca0b 100644
--- a/apps/CameraITS/tests/scene1/test_yuv_plus_raw12.py
+++ b/apps/CameraITS/tests/scene1/test_yuv_plus_raw12.py
@@ -38,8 +38,13 @@
e, s = its.target.get_target_exposure_combos(cam)["midExposureTime"]
req = its.objects.manual_capture_request(s, e, True, props)
+ max_raw12_size = \
+ its.objects.get_available_output_sizes("raw12", props)[0]
+ w,h = its.objects.get_available_output_sizes(
+ "yuv", props, (1920, 1080), max_raw12_size)[0]
cap_raw, cap_yuv = cam.do_capture(req,
- [{"format":"raw12"}, {"format":"yuv"}])
+ [{"format":"raw12"},
+ {"format":"yuv", "width":w, "height":h}])
img = its.image.convert_capture_to_rgb_image(cap_yuv)
its.image.write_image(img, "%s_yuv.jpg" % (NAME), True)
diff --git a/apps/CameraITS/tests/scene3/test_reprocess_edge_enhancement.py b/apps/CameraITS/tests/scene3/test_reprocess_edge_enhancement.py
index 73834cb..e96a9ee 100644
--- a/apps/CameraITS/tests/scene3/test_reprocess_edge_enhancement.py
+++ b/apps/CameraITS/tests/scene3/test_reprocess_edge_enhancement.py
@@ -53,22 +53,28 @@
"""
NAME = os.path.basename(__file__).split(".")[0]
+ NUM_SAMPLES = 4
req = its.objects.manual_capture_request(sensitivity, exp)
req["android.lens.focusDistance"] = fd
req["android.edge.mode"] = edge_mode
if (reprocess_format != None):
req["android.reprocess.effectiveExposureFactor"] = 1.0
- cap = cam.do_capture(req, out_surface, reprocess_format)
- img = its.image.decompress_jpeg_to_rgb_image(cap["data"])
- its.image.write_image(img, "%s_edge=%d_reprocess_fmt_%s.jpg" %
- (NAME, edge_mode, reprocess_format))
- tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
+ sharpness_list = []
+ for n in range(NUM_SAMPLES):
+ cap = cam.do_capture(req, out_surface, reprocess_format)
+ img = its.image.decompress_jpeg_to_rgb_image(cap["data"])
+ if n == 0:
+ its.image.write_image(img, "%s_reprocess_fmt_%s_edge=%d.jpg" %
+ (NAME, reprocess_format, edge_mode))
+ res_edge_mode = cap["metadata"]["android.edge.mode"]
+ tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
+ sharpness_list.append(its.image.compute_image_sharpness(tile))
ret = {}
- ret["edge_mode"] = cap["metadata"]["android.edge.mode"]
- ret["sharpness"] = its.image.compute_image_sharpness(tile)
+ ret["edge_mode"] = res_edge_mode
+ ret["sharpness"] = numpy.mean(sharpness_list)
return ret
diff --git a/apps/CtsVerifier/Android.mk b/apps/CtsVerifier/Android.mk
index 8cec7ea..34246cc 100644
--- a/apps/CtsVerifier/Android.mk
+++ b/apps/CtsVerifier/Android.mk
@@ -41,7 +41,7 @@
LOCAL_PACKAGE_NAME := CtsVerifier
-LOCAL_AAPT_FLAGS += --version-name "6.0_r0 $(BUILD_NUMBER)"
+LOCAL_AAPT_FLAGS += --version-name "6.0_r1 $(BUILD_NUMBER)"
LOCAL_JNI_SHARED_LIBRARIES := libctsverifier_jni libaudioloopback_jni
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 217913c..ea16821 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -363,6 +363,7 @@
<meta-data android:name="test_category" android:value="@string/test_category_security" />
<meta-data android:name="test_excluded_features"
android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch" />
+ <meta-data android:name="test_required_features" android:value="android.hardware.fingerprint" />
</activity>
<activity android:name=".security.ScreenLockBoundKeysTest"
android:label="@string/sec_lock_bound_key_test"
@@ -753,7 +754,7 @@
android:screenOrientation="locked" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
- <category android:name="android.cts.intent.category.MANUAL_TEST"/>
+ <category android:name="android.cts.intent.category.MANUAL_TEST_disabled"/>
</intent-filter>
<meta-data
@@ -762,8 +763,6 @@
<meta-data
android:name="test_required_features"
android:value="android.hardware.sensor.accelerometer:android.hardware.sensor.gyroscope:android.hardware.sensor.compass:android.hardware.camera.any" />
- <meta-data android:name="test_excluded_features"
- android:value="android.hardware.type.television" />
</activity>
<activity
android:name=".sensors.RVCVRecordActivity"
@@ -1397,6 +1396,13 @@
</intent-filter>
</activity-alias>
+ <activity android:name=".managedprovisioning.AuthenticationBoundKeyTestActivity">
+ <intent-filter>
+ <action android:name="com.android.cts.verifier.managedprovisioning.action.AUTH_BOUND_KEY_TEST" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
<activity android:name=".managedprovisioning.ByodFlowTestActivity"
android:launchMode="singleTask"
android:label="@string/provisioning_byod">
@@ -1424,6 +1430,8 @@
<action android:name="com.android.cts.verifier.managedprovisioning.BYOD_KEYGUARD_DISABLED_FEATURES" />
<action android:name="com.android.cts.verifier.managedprovisioning.BYOD_LOCKNOW" />
<action android:name="com.android.cts.verifier.managedprovisioning.TEST_NFC_BEAM" />
+ <action android:name="com.android.cts.verifier.managedprovisioning.action.TEST_CROSS_PROFILE_INTENTS_DIALOG" />
+ <action android:name="com.android.cts.verifier.managedprovisioning.action.TEST_APP_LINKING_DIALOG" />
<category android:name="android.intent.category.DEFAULT"></category>
</intent-filter>
</activity>
@@ -1451,8 +1459,87 @@
<activity android:name=".managedprovisioning.CrossProfileTestActivity">
<intent-filter>
- <action android:name="com.android.cts.verifier.managedprovisioning.CROSS_PROFILE" />
- <category android:name="android.intent.category.DEFAULT"></category>
+ <action android:name="com.android.cts.verifier.managedprovisioning.CROSS_PROFILE_TO_PERSONAL" />
+ <action android:name="com.android.cts.verifier.managedprovisioning.CROSS_PROFILE_TO_WORK" />
+ <!-- We need to have at least one activity listening to these intents on the device
+ to test if these are forwarded from the managed profile to the parent or
+ the other way around. -->
+ <action android:name="android.provider.MediaStore.RECORD_SOUND" />
+ <action android:name="android.speech.action.RECOGNIZE_SPEECH" />
+ <action android:name="android.app.action.SET_NEW_PASSWORD" />
+ <action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH" />
+ <action android:name="android.intent.action.WEB_SEARCH" />
+ <action android:name="android.intent.action.VIEW_DOWNLOADS" />
+ <action android:name="android.media.action.DISPLAY_AUDIO_EFFECT_CONTROL_PANEL" />
+ <action android:name="android.settings.SHOW_INPUT_METHOD_PICKER" />
+ <action android:name="android.intent.action.MANAGE_NETWORK_USAGE" />
+ <action android:name="com.android.settings.TTS_SETTINGS" />
+ <action android:name="android.settings.ZEN_MODE_SETTINGS" />
+ <action android:name="android.settings.BATTERY_SAVER_SETTINGS" />
+ <action android:name="android.settings.INPUT_METHOD_SETTINGS" />
+ <action android:name="android.settings.INPUT_METHOD_SUBTYPE_SETTINGS" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.SEND" />
+ <action android:name="android.intent.action.SEND_MULTIPLE" />
+ <data android:mimeType="*/*" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.GET_CONTENT" />
+ <action android:name="android.intent.action.OPEN_DOCUMENT" />
+ <data android:mimeType="*/*" />
+ <category android:name="android.intent.category.OPENABLE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW" />
+ <action android:name="android.intent.action.SENDTO" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="sms" />
+ <data android:scheme="smsto" />
+ <data android:scheme="mms" />
+ <data android:scheme="mmsto" />
+ <data android:scheme="mailto" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW" />
+ <action android:name="android.intent.action.CALL" />
+ <action android:name="android.intent.action.DIAL" />
+ <action android:name="android.intent.action.CALL_PRIVILEGED" />
+ <action android:name="android.intent.action.CALL_EMERGENCY" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="tel" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.INSERT" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="content" />
+ <data android:mimeType="*/*" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="http" android:host="com.android.cts.verifier" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="http" />
+ <data android:mimeType="video/mp4" />
+ <data android:mimeType="audio/*" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="http" />
+ <data android:scheme="geo" />
+ <data android:scheme="market" />
</intent-filter>
</activity>
@@ -1570,6 +1657,18 @@
android:value="android.software.live_tv" />
</activity>
+ <activity android:name=".tv.AppLinkTestActivity"
+ android:label="@string/tv_app_link_test"
+ android:launchMode="singleTask">
+ <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_tv" />
+ <meta-data android:name="test_required_features"
+ android:value="android.software.live_tv" />
+ </activity>
+
<activity android:name=".screenpinning.ScreenPinningTestActivity"
android:label="@string/screen_pinning_test">
<intent-filter>
@@ -1609,8 +1708,8 @@
<meta-data android:name="test_required_features" android:value="android.hardware.microphone" />
</activity>
- <activity android:name=".audio.AudioDeviceNotificationsActivity"
- android:label="@string/audio_devices_notifications_test">
+ <activity android:name=".audio.AudioOutputDeviceNotificationsActivity"
+ android:label="@string/audio_in_devices_notifications_test">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.cts.intent.category.MANUAL_TEST" />
@@ -1619,18 +1718,37 @@
<!--
<meta-data android:name="test_required_features" android:value="android.hardware.microphone" />
-->
+ <meta-data android:name="test_required_features" android:value="android.hardware.audio.output" />
</activity>
- <activity android:name=".audio.AudioRoutingNotificationsActivity"
- android:label="@string/audio_routingnotifications_test">
+ <activity android:name=".audio.AudioInputDeviceNotificationsActivity"
+ android:label="@string/audio_in_devices_notifications_test">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.cts.intent.category.MANUAL_TEST" />
</intent-filter>
<meta-data android:name="test_category" android:value="@string/test_category_audio" />
- <!--
<meta-data android:name="test_required_features" android:value="android.hardware.microphone" />
- -->
+ </activity>
+
+ <activity android:name=".audio.AudioOutputRoutingNotificationsActivity"
+ android:label="@string/audio_output_routingnotifications_test">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.cts.intent.category.MANUAL_TEST" />
+ </intent-filter>
+ <meta-data android:name="test_category" android:value="@string/test_category_audio" />
+ <meta-data android:name="test_required_features" android:value="android.hardware.audio.output" />
+ </activity>
+
+ <activity android:name=".audio.AudioInputRoutingNotificationsActivity"
+ android:label="@string/audio_input_routingnotifications_test">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.cts.intent.category.MANUAL_TEST" />
+ </intent-filter>
+ <meta-data android:name="test_category" android:value="@string/test_category_audio" />
+ <meta-data android:name="test_required_features" android:value="android.hardware.microphone" />
</activity>
<activity android:name=".audio.AudioLoopbackActivity"
@@ -1641,6 +1759,7 @@
</intent-filter>
<meta-data android:name="test_category" android:value="@string/test_category_audio" />
<meta-data android:name="test_required_features" android:value="android.hardware.microphone" />
+ <meta-data android:name="test_required_features" android:value="android.hardware.audio.output" />
<meta-data android:name="test_excluded_features" android:value="android.hardware.type.watch" />
<meta-data android:name="test_excluded_features" android:value="android.hardware.type.television" />
</activity>
diff --git a/apps/CtsVerifier/jni/verifier/Android.mk b/apps/CtsVerifier/jni/verifier/Android.mk
index da4687d..4840e62 100644
--- a/apps/CtsVerifier/jni/verifier/Android.mk
+++ b/apps/CtsVerifier/jni/verifier/Android.mk
@@ -25,8 +25,11 @@
LOCAL_SRC_FILES := \
CtsVerifierJniOnLoad.cpp \
- com_android_cts_verifier_os_FileUtils.cpp
+ com_android_cts_verifier_camera_StatsImage.cpp \
+ com_android_cts_verifier_os_FileUtils.cpp
LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
+LOCAL_SHARED_LIBRARIES := liblog
+
include $(BUILD_SHARED_LIBRARY)
diff --git a/apps/CtsVerifier/jni/verifier/CtsVerifierJniOnLoad.cpp b/apps/CtsVerifier/jni/verifier/CtsVerifierJniOnLoad.cpp
index 81e5690..399275b 100644
--- a/apps/CtsVerifier/jni/verifier/CtsVerifierJniOnLoad.cpp
+++ b/apps/CtsVerifier/jni/verifier/CtsVerifierJniOnLoad.cpp
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,6 +18,7 @@
#include <stdio.h>
extern int register_com_android_cts_verifier_os_FileUtils(JNIEnv*);
+extern int register_com_android_cts_verifier_camera_its_StatsImage(JNIEnv*);
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = NULL;
@@ -30,5 +31,9 @@
return JNI_ERR;
}
+ if (register_com_android_cts_verifier_camera_its_StatsImage(env)) {
+ return JNI_ERR;
+ }
+
return JNI_VERSION_1_4;
}
diff --git a/apps/CtsVerifier/jni/verifier/com_android_cts_verifier_camera_StatsImage.cpp b/apps/CtsVerifier/jni/verifier/com_android_cts_verifier_camera_StatsImage.cpp
new file mode 100644
index 0000000..16dff85
--- /dev/null
+++ b/apps/CtsVerifier/jni/verifier/com_android_cts_verifier_camera_StatsImage.cpp
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "ITS-StatsImage-JNI"
+// #define LOG_NDEBUG 0
+#include <android/log.h>
+#include <utils/Log.h>
+
+#include <jni.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <inttypes.h>
+#include <string.h>
+
+jfloatArray com_android_cts_verifier_camera_its_computeStatsImage(JNIEnv* env, jobject thiz,
+ jbyteArray img, jint width, jint height, jint gridWidth, jint gridHeight)
+{
+ int bufSize = (int)(env->GetArrayLength(img));
+ unsigned char *buf = (unsigned char*)env->GetByteArrayElements(img, /*is_copy*/NULL);
+
+ // Size of the raw image.
+ const int w = width;
+ const int h = height;
+ // Size of each grid cell.
+ const int gw = gridWidth;
+ const int gh = gridHeight;
+ // Number of grid cells (rounding down to full cells only at right+bottom edges).
+ const int ngx = w / gw;
+ const int ngy = h / gh;
+
+ float *mean = new float[ngy*ngx*4];
+ float *var = new float[ngy*ngx*4];
+ for (int gy = 0; gy < ngy; gy++) {
+ for (int gx = 0; gx < ngx; gx++) {
+ float sum[4] = {0};
+ float sumSq[4] = {0};
+ int count[4] = {0};
+ for (int y = gy*gh; y < (gy+1)*gh; y++) {
+ int chnOffset = (y & 0x1) * 2;
+ unsigned char *pbuf = buf + 2*y*w + 2*gx*gw;
+ for (int x = gx*gw; x < (gx+1)*gw; x++) {
+ // input is RAW16
+ int byte0 = *pbuf++;
+ int byte1 = *pbuf++;
+ int pixelValue = (byte1 << 8) | byte0;
+ int ch = chnOffset + (x & 1);
+ sum[ch] += pixelValue;
+ sumSq[ch] += pixelValue * pixelValue;
+ count[ch] += 1;
+ }
+ }
+ for (int ch = 0; ch < 4; ch++) {
+ float m = (float)sum[ch] / count[ch];
+ float mSq = (float)sumSq[ch] / count[ch];
+ mean[gy*ngx*4 + gx*4 + ch] = m;
+ var[gy*ngx*4 + gx*4 + ch] = mSq - m*m;
+ }
+ }
+ }
+
+ jfloatArray ret = env->NewFloatArray(ngx*ngy*4*2);
+ env->SetFloatArrayRegion(ret, 0, ngx*ngy*4, (float*)mean);
+ env->SetFloatArrayRegion(ret, ngx*ngy*4, ngx*ngy*4, (float*)var);
+ delete [] mean;
+ delete [] var;
+ return ret;
+}
+
+static JNINativeMethod gMethods[] = {
+ { "computeStatsImage", "([BIIII)[F",
+ (void *) com_android_cts_verifier_camera_its_computeStatsImage },
+};
+
+int register_com_android_cts_verifier_camera_its_StatsImage(JNIEnv* env)
+{
+ jclass clazz = env->FindClass("com/android/cts/verifier/camera/its/StatsImage");
+
+ return env->RegisterNatives(clazz, gMethods,
+ sizeof(gMethods) / sizeof(JNINativeMethod));
+}
diff --git a/apps/CtsVerifier/res/drawable/app_link_img.png b/apps/CtsVerifier/res/drawable/app_link_img.png
new file mode 100644
index 0000000..851fc6f
--- /dev/null
+++ b/apps/CtsVerifier/res/drawable/app_link_img.png
Binary files differ
diff --git a/apps/CtsVerifier/res/layout/audio_dev_notify.xml b/apps/CtsVerifier/res/layout/audio_dev_notify.xml
index 98dbd8b..0975ab9 100644
--- a/apps/CtsVerifier/res/layout/audio_dev_notify.xml
+++ b/apps/CtsVerifier/res/layout/audio_dev_notify.xml
@@ -24,8 +24,7 @@
android:layout_height="wrap_content"
android:scrollbars="vertical"
android:gravity="bottom"
- android:id="@+id/info_text"
- android:text="@string/audio_devices_notification_instructions" />
+ android:id="@+id/info_text"/>
<LinearLayout
android:layout_width="match_parent"
diff --git a/apps/CtsVerifier/res/layout/audio_frequency_line_activity.xml b/apps/CtsVerifier/res/layout/audio_frequency_line_activity.xml
index 69e3bc7..c1b62af 100644
--- a/apps/CtsVerifier/res/layout/audio_frequency_line_activity.xml
+++ b/apps/CtsVerifier/res/layout/audio_frequency_line_activity.xml
@@ -13,60 +13,105 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="10dip"
+ android:orientation="vertical"
+>
+ <ScrollView
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:padding="10dip"
- android:orientation="vertical">
+ android:layout_height="match_parent"
+ android:id="@+id/scrollView"
+ >
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:scrollbars="vertical"
- android:gravity="bottom"
- android:id="@+id/info_text"
- android:text="@string/audio_frequency_line_instructions" />
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
- <Button
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:id="@+id/audio_frequency_line_plug_ready_btn"
- android:text="@string/audio_frequency_line_plug_ready_btn"/>
-
- <LinearLayout
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:id="@+id/audio_frequency_line_layout">
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/audio_frequency_line_test_btn"
- android:id="@+id/audio_frequency_line_test_btn"/>
-
- <ProgressBar
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/audio_frequency_line_progress_bar"/>
- </LinearLayout>
-
- <TextView
- android:layout_width="wrap_content"
+ <LinearLayout
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="@string/audio_frequency_line_results_text"
- android:id="@+id/audio_frequency_line_results_text"/>
+ android:orientation="vertical"
+ >
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:scrollbars="vertical"
+ android:gravity="bottom"
+ android:id="@+id/audio_general_headset_port_exists"
+ android:text="@string/audio_general_headset_port_exists" />
- </LinearLayout>
- </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
- <include layout="@layout/pass_fail_buttons" />
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/audio_general_headset_no"
+ android:text="@string/audio_general_headset_no" />
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/audio_general_headset_yes"
+ android:text="@string/audio_general_headset_yes" />
+
+ </LinearLayout>
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:scrollbars="vertical"
+ android:gravity="bottom"
+ android:id="@+id/info_text"
+ android:text="@string/audio_frequency_line_instructions" />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ >
+ <Button
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/audio_frequency_line_plug_ready_btn"
+ android:text="@string/audio_frequency_line_plug_ready_btn" />
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ >
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:id="@+id/audio_frequency_line_layout"
+ >
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/audio_frequency_line_test_btn"
+ android:id="@+id/audio_frequency_line_test_btn" />
+
+ <ProgressBar
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/audio_frequency_line_progress_bar" />
+ </LinearLayout>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/audio_frequency_line_results_text"
+ android:id="@+id/audio_frequency_line_results_text" />
+
+ </LinearLayout>
+ </LinearLayout>
+
+ <include layout="@layout/pass_fail_buttons" />
+ </LinearLayout>
+ </ScrollView>
</LinearLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/audio_frequency_mic_activity.xml b/apps/CtsVerifier/res/layout/audio_frequency_mic_activity.xml
index 10b0003..db52998 100644
--- a/apps/CtsVerifier/res/layout/audio_frequency_mic_activity.xml
+++ b/apps/CtsVerifier/res/layout/audio_frequency_mic_activity.xml
@@ -18,116 +18,151 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dip"
- android:orientation="vertical">
+ android:orientation="vertical"
+>
<ScrollView
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:id="@+id/scrollView">
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/scrollView"
+ >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical">
+ android:orientation="vertical"
+ >
- <LinearLayout
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:scrollbars="vertical"
+ android:gravity="bottom"
+ android:id="@+id/audio_general_headset_port_exists"
+ android:text="@string/audio_general_headset_port_exists" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+
+ <Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:orientation="horizontal">
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:id="@+id/info_text"
- android:text="@string/audio_frequency_mic_instructions"/>
+ android:id="@+id/audio_general_headset_no"
+ android:text="@string/audio_general_headset_no" />
- <ProgressBar
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:id="@+id/audio_frequency_mic_progress_bar"/>
- </LinearLayout>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/audio_general_headset_yes"
+ android:text="@string/audio_general_headset_yes" />
- <Button
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:id="@+id/audio_frequency_mic_speakers_ready_btn"
- android:text="@string/audio_frequency_mic_speakers_ready_btn"/>
+ </LinearLayout>
- <TextView
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:id="@+id/info_text"
+ android:text="@string/audio_frequency_mic_instructions" />
+
+ <ProgressBar
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:id="@+id/audio_frequency_mic_progress_bar" />
+ </LinearLayout>
+
+ <Button
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/audio_frequency_mic_speakers_ready_btn"
+ android:text="@string/audio_frequency_mic_speakers_ready_btn" />
+
+ <TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="vertical"
android:gravity="bottom"
android:id="@+id/audio_frequency_mic_speakers_ready_status"
- android:text="@string/audio_frequency_mic_speakers_ready_status"/>
+ android:text="@string/audio_frequency_mic_speakers_ready_status" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
- android:id="@+id/audio_frequency_mic_layout_test1">
+ android:id="@+id/audio_frequency_mic_layout_test1"
+ >
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/audio_frequency_mic_instructions2"
- android:id="@+id/audio_frequency_mic_instructions2"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/audio_frequency_mic_instructions2"
+ android:id="@+id/audio_frequency_mic_instructions2" />
- <Button
+ <Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/audio_frequency_mic_test1_btn"
- android:id="@+id/audio_frequency_mic_test1_btn"/>
+ android:id="@+id/audio_frequency_mic_test1_btn" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/audio_frequency_mic_results_text"
- android:id="@+id/audio_frequency_mic_results1_text"/>
- </LinearLayout>
+ android:id="@+id/audio_frequency_mic_results1_text" />
+ </LinearLayout>
- <LinearLayout
+ <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
- android:id="@+id/audio_frequency_mic_layout_test2a">
+ android:id="@+id/audio_frequency_mic_layout_test2a"
+ >
- <Button
+ <Button
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/audio_frequency_mic_mic_ready_btn"
+ android:text="@string/audio_frequency_mic_mic_ready_btn" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/audio_frequency_mic_usb_status"
+ android:id="@+id/audio_frequency_mic_usb_status" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:orientation="vertical"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:id="@+id/audio_frequency_mic_mic_ready_btn"
- android:text="@string/audio_frequency_mic_mic_ready_btn"/>
+ android:layout_height="match_parent"
+ android:id="@+id/audio_frequency_mic_layout_test2b"
+ >
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/audio_frequency_mic_usb_status"
- android:id="@+id/audio_frequency_mic_usb_status"/>
- </LinearLayout>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/audio_frequency_mic_test2_btn"
+ android:id="@+id/audio_frequency_mic_test2_btn" />
- <LinearLayout
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:id="@+id/audio_frequency_mic_layout_test2b">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/audio_frequency_mic_results_text"
+ android:id="@+id/audio_frequency_mic_results_text" />
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/audio_frequency_mic_test2_btn"
- android:id="@+id/audio_frequency_mic_test2_btn"/>
+ </LinearLayout>
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/audio_frequency_mic_results_text"
- android:id="@+id/audio_frequency_mic_results_text"/>
-
+ <include layout="@layout/pass_fail_buttons" />
</LinearLayout>
-
- <include layout="@layout/pass_fail_buttons"/>
- </LinearLayout>
- </ScrollView>
+ </ScrollView>
</LinearLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/audio_routingnotifications_test.xml b/apps/CtsVerifier/res/layout/audio_input_routingnotifications_test.xml
similarity index 64%
copy from apps/CtsVerifier/res/layout/audio_routingnotifications_test.xml
copy to apps/CtsVerifier/res/layout/audio_input_routingnotifications_test.xml
index cef30d6..ca7dd19 100644
--- a/apps/CtsVerifier/res/layout/audio_routingnotifications_test.xml
+++ b/apps/CtsVerifier/res/layout/audio_input_routingnotifications_test.xml
@@ -26,40 +26,7 @@
android:scrollbars="vertical"
android:gravity="bottom"
android:id="@+id/info_text"
- android:text="@string/audio_dev_routingnotification_instructions" />
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:id="@+id/audioTrackRoutingLayout">
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/audio_routingnotification_playHeader"/>
-
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:id="@+id/audio_routingnotification_audioTrack_change"/>
-
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/audio_routingnotification_playBtn"
- android:text="@string/audio_routingnotification_playBtn"/>
-
- <Button
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:id="@+id/audio_routingnotification_playStopBtn"
- android:text="@string/audio_routingnotification_playStopBtn"/>
- </LinearLayout>
- </LinearLayout>
+ android:text="@string/audio_input_routingnotification_instructions" />
<LinearLayout
android:layout_width="match_parent"
diff --git a/apps/CtsVerifier/res/layout/audio_loopback_activity.xml b/apps/CtsVerifier/res/layout/audio_loopback_activity.xml
index 626ac4f..815f2bc 100644
--- a/apps/CtsVerifier/res/layout/audio_loopback_activity.xml
+++ b/apps/CtsVerifier/res/layout/audio_loopback_activity.xml
@@ -13,71 +13,120 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:padding="10dip"
- android:orientation="vertical">
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="10dip"
+ android:orientation="vertical"
+>
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:scrollbars="vertical"
- android:gravity="bottom"
- android:id="@+id/info_text"
- android:text="@string/audio_loopback_instructions" />
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
- <Button
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:id="@+id/audio_loopback_plug_ready_btn"
- android:text="@string/audio_loopback_plug_ready_btn"/>
-
- <LinearLayout
- android:orientation="vertical"
+ <ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:id="@+id/audio_loopback_layout">
+ android:id="@+id/scrollView"
+ >
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/audio_loopback_instructions2"
- android:id="@+id/audio_loopback_instructions2"/>
-
- <SeekBar
+ <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:id="@+id/audio_loopback_level_seekbar"/>
+ android:orientation="vertical"
+ >
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/audio_loopback_level_text"
- android:id="@+id/audio_loopback_level_text"/>
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:scrollbars="vertical"
+ android:gravity="bottom"
+ android:id="@+id/audio_general_headset_port_exists"
+ android:text="@string/audio_general_headset_port_exists" />
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/audio_loopback_test_btn"
- android:id="@+id/audio_loopback_test_btn"/>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
- <ProgressBar
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/audio_loopback_progress_bar"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/audio_general_headset_no"
+ android:text="@string/audio_general_headset_no" />
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/audio_loopback_results_text"
- android:id="@+id/audio_loopback_results_text"/>
- </LinearLayout>
- </LinearLayout>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/audio_general_headset_yes"
+ android:text="@string/audio_general_headset_yes" />
- <include layout="@layout/pass_fail_buttons" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:id="@+id/audio_loopback_headset_port"
+ >
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:scrollbars="vertical"
+ android:gravity="bottom"
+ android:id="@+id/info_text"
+ android:text="@string/audio_loopback_instructions" />
+
+ <Button
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/audio_loopback_plug_ready_btn"
+ android:text="@string/audio_loopback_plug_ready_btn" />
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/audio_loopback_layout"
+ >
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/audio_loopback_instructions2"
+ android:id="@+id/audio_loopback_instructions2" />
+
+ <SeekBar
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/audio_loopback_level_seekbar" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/audio_loopback_level_text"
+ android:id="@+id/audio_loopback_level_text" />
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/audio_loopback_test_btn"
+ android:id="@+id/audio_loopback_test_btn" />
+
+ <ProgressBar
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/audio_loopback_progress_bar" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/audio_loopback_results_text"
+ android:id="@+id/audio_loopback_results_text" />
+ </LinearLayout>
+
+ </LinearLayout>
+ <include layout="@layout/pass_fail_buttons" />
+ </LinearLayout>
+ </ScrollView>
</LinearLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/audio_routingnotifications_test.xml b/apps/CtsVerifier/res/layout/audio_output_routingnotifications_test.xml
similarity index 64%
rename from apps/CtsVerifier/res/layout/audio_routingnotifications_test.xml
rename to apps/CtsVerifier/res/layout/audio_output_routingnotifications_test.xml
index cef30d6..b321000 100644
--- a/apps/CtsVerifier/res/layout/audio_routingnotifications_test.xml
+++ b/apps/CtsVerifier/res/layout/audio_output_routingnotifications_test.xml
@@ -26,7 +26,7 @@
android:scrollbars="vertical"
android:gravity="bottom"
android:id="@+id/info_text"
- android:text="@string/audio_dev_routingnotification_instructions" />
+ android:text="@string/audio_output_routingnotification_instructions" />
<LinearLayout
android:layout_width="match_parent"
@@ -61,39 +61,6 @@
</LinearLayout>
</LinearLayout>
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:id="@+id/audioRecordRoutingLayout">
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/audio_routingnotification_recHeader"/>
-
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:id="@+id/audio_routingnotification_audioRecord_change"/>
-
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/audio_routingnotification_recordBtn"
- android:text="@string/audio_routingnotification_recBtn"/>
-
- <Button
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:id="@+id/audio_routingnotification_recordStopBtn"
- android:text="@string/audio_routingnotification_recStopBtn"/>
- </LinearLayout>
- </LinearLayout>
-
<include layout="@layout/pass_fail_buttons" />
</LinearLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/keychain_main.xml b/apps/CtsVerifier/res/layout/keychain_main.xml
index 01eb255..3f695cd 100644
--- a/apps/CtsVerifier/res/layout/keychain_main.xml
+++ b/apps/CtsVerifier/res/layout/keychain_main.xml
@@ -24,22 +24,36 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
- android:padding="10dip" >
+ android:padding="10dip">
- <TextView
- android:id="@+id/test_instruction"
- style="@style/InstructionsFont"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_weight="0" />
-
- <TextView
- android:id="@+id/test_log"
+ <ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
- android:layout_gravity="bottom"
- android:orientation="vertical" />
+ android:fillViewport="true">
+
+ <LinearLayout
+ android:id="@+id/test_messages"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/test_instruction"
+ style="@style/InstructionsFont"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="0" />
+
+ <TextView
+ android:id="@+id/test_log"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:orientation="vertical" />
+
+ </LinearLayout>
+ </ScrollView>
<LinearLayout
android:id="@+id/action_buttons"
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index c5469a7..d8a96eb 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -1277,7 +1277,25 @@
<string name="provisioning_byod_no_video_capture_resolver">No video capture app present. Skip test.</string>
<string name="provisioning_byod_no_audio_capture_resolver">No audio capture app present. Skip test.</string>
<string name="provisioning_byod_capture_media_error">Error while capturing media from managed profile.</string>
+ <string name="provisioning_byod_capture_image_error">Error while capturing image from managed profile.</string>
+ <string name="provisioning_byod_auth_bound_key">Autentication-boud keys</string>
+ <string name="provisioning_byod_auth_bound_key_info">
+ This test verifies keystore cryptographic keys can be bound to device credentials.
+ These keys should only be available if there was a recent enough authentication.
+ </string>
+ <string name="provisioning_byod_auth_bound_key_instruction">
+ This test verifies keystore cryptographic keys can be bound to device lockscreen challenge or fingerprints (if available).
+ These keys should only be available if there was a recent enough authentication. \n
+
+ 1. Press "Set up" to open Security settings. Create a lockscreen password and if available, enroll a fingerprint.\n
+ 2. Go through the list of tests.\n
+ 3. Mark the overall test pass or fail.\n
+ 4. Once the set of tests are completed, remove the lockscreen challenge.
+ </string>
+ <string name="provisioning_byod_auth_bound_key_set_up">Set up</string>
+ <string name="provisioning_byod_lockscreen_bound_key">Lockscreen-bound key test</string>
+ <string name="provisioning_byod_fingerprint_bound_key">Fingerprint-bound key test</string>
<!-- Strings for DeskClock -->
<string name="deskclock_tests">Alarms and Timers Tests</string>
<string name="deskclock_tests_info">
@@ -1436,12 +1454,13 @@
<string name="provisioning_byod_profile_visible">Profile-aware accounts settings</string>
<string name="provisioning_byod_admin_visible">Profile-aware device administrator settings</string>
<string name="provisioning_byod_workapps_visible">Badged work apps visible in Launcher</string>
- <string name="provisioning_byod_cross_profile">Open app cross profiles</string>
- <string name="provisioning_byod_cross_profile_app_personal">
- You selected the CTS Verifier option.
- </string>
+ <string name="provisioning_byod_cross_profile_from_personal">Open app cross profiles from the personal side</string>
+ <string name="provisioning_byod_cross_profile_from_work">Open app cross profiles from the work side</string>
+ <string name="provisioning_app_linking">App links from the work side</string>
+ <string name="provisioning_byod_cross_profile_app_personal">You selected the personal option.</string>
<string name="provisioning_byod_cross_profile_app_work">You selected the Work option.</string>
- <string name="provisioning_byod_cross_profile_instruction">
+ <string name="provisioning_byod_cross_profile_app_ctsverifier"> You selected the ctsverifier option </string>
+ <string name="provisioning_byod_cross_profile_from_personal_instruction">
Please press the Go button to start an action.\n
\n
You should be asked to choose either \"CTS Verifier\" or \"Work\" to complete the action.
@@ -1449,16 +1468,39 @@
\n
Verify that you are prompted with the above choices and both options work as intended. Then mark this test accordingly.
</string>
+ <string name="provisioning_byod_cross_profile_from_work_instruction">
+ Please press the Go button to start an action.\n
+ \n
+ You should be asked to choose either \"CTS Verifier\" or \"Personal\" to complete the action.
+ Pressing either should bring up a page stating your choice.\n
+ \n
+ Verify that you are prompted with the above choices and both options work as intended. Then mark this test accordingly.
+ </string>
+ <string name="provisioning_byod_app_linking_instruction">
+ Please press the Go button to start an action.\n
+ \n
+ You should be asked to choose either \"CTS Verifier\" or \"Personal\" to complete the action.\n
+ - If you choose \"CTS Verifier\", you should see a page stating your chose \"CTS Verifier\".\n
+ - If you choose \"Personal\", you should be presented with another dialog between \"CTS Verifier\"
+ and some other apps. In this case, you should choose \"CTS verifier\".\n
+ You should then see a page stating you chose \"Personal\".\n
+ \n
+ Verify that you are prompted with the above choices and both options work as intended. Then mark this test accordingly.
+ </string>
<string name="provisioning_byod_keyguard_disabled_features">Keyguard disabled features</string>
<string name="provisioning_byod_keyguard_disabled_features_info">
This test exercises Keyguard Disabled Features. Follow instructions above.
</string>
<string name="provisioning_byod_keyguard_disabled_features_instruction">
- Please press the \"Prepare test\" button to disable trust agents.\n
+ Please go to Settings > Security > Device administrators and set
+ \"CTS Verifier - AfW Admin\" as active admin.\n
+ After that please press the \"Prepare test\" button to disable trust agents.\n
Then please press through the following verification steps.\n
- Note: Device password will be set to \"testpassword\". After leaving the screen device password be cleared.
+ Note: Device password will be set to \"testpassword\". After leaving the screen device
+ password and active admin status will be cleared.
</string>
<string name="provisioning_byod_keyguard_disabled_features_prepare_button">Prepare test</string>
+ <string name="provisioning_byod_keyguard_disabled_features_not_admin">CtsVerifier is not active admin. Please follow instructions.</string>
<string name="provisioning_byod_disable_trust_agents">Disable trust agents</string>
<string name="provisioning_byod_disable_trust_agents_instruction">
Please press the Go button to go to Settings > Security. Then go to Trusted agents and\n
@@ -1525,6 +1567,10 @@
- Both Personal and Work categories exist.\n
- \"Remove work profile\" exists under the Work category.\n
\n
+ Furthermore, hit the action overflow button (3 dots) and verify that:\n
+ - There are two auto-sync options present, one for personal and one for work data.\n
+ - De-selecting either option prompts a warning dialog.\n
+ \n
Use the Back button to return to this page.
</string>
<string name="provisioning_byod_admin_visible_instruction">
@@ -1632,6 +1678,7 @@
<string name="provisioning_byod_send_share_intent">Send share intent</string>
<string name="provisioning_byod_cannot_resolve_beam_activity">Cannot find beam activity</string>
+ <string name="test_failed_cannot_start_intent">Cannot start the given intent.</string>
<string name="provisioning_byod_no_activity">Cannot communicate with activity in the work profile.</string>
<string name="provisioning_byod_delete_profile">Initiate deletion of work profile.</string>
<string name="provisioning_byod_profile_deleted">Work profile deleted.</string>
@@ -1867,7 +1914,6 @@
Do you see the programs named \"Dummy Program\" and their descriptions
"Dummy Program Description" in the EPG?
</string>
- <string name="tv_input_discover_test_yes">Yes</string>
<string name="tv_parental_control_test">TV app parental controls test</string>
<string name="tv_parental_control_test_info">
@@ -1956,6 +2002,24 @@
The playback position should be moved to the next position.
</string>
+ <string name="tv_app_link_test">TV app app-link test</string>
+ <string name="tv_app_link_test_info">
+ Verify that the bundled TV app supports linking to channel apps. If TV input service provides
+ links for its specific channels, TV app should show the links in a proper format.
+ </string>
+ <string name="tv_app_link_test_select_app_link">
+ Select the \"Launch TV app\" button, then check if you can see a menu with \"Cts App-Link Text\"
+ text in red background. If you see the link, select it to follow the link.
+ </string>
+ <string name="tv_app_link_test_verify_link_clicked">
+ The app-link must have been clicked and the activity should be changed correctly.
+ </string>
+ <string name="tv_input_link_test_verify_link_interface">
+ Do you see the app-link card similar to the image on the left?\n
+ 1) You should see the poster art image, but the color could be differ.\n
+ 2) You should see the text \"Cts App-Link Text\".\n
+ </string>
+
<string name="overlay_view_text">Overlay View Dummy Text</string>
<string name="fake_rating">Fake</string>
@@ -1976,25 +2040,34 @@
<string name="error_screen_pinning_did_not_exit">Screen was not unpinned.</string>
<string name="error_screen_pinning_couldnt_exit">Could not exit screen pinning through API.</string>
- <!-- Audio Devices Notifcations Test -->
- <string name="audio_devices_notifications_test">Audio Devices Notifications Test</string>
- <string name="audio_devices_notification_instructions">
+ <!-- Audio Devices Notifcations Tests -->
+ <string name="audio_out_devices_notifications_test">Audio Output Devices Notifications Test</string>
+ <string name="audio_out_devices_notification_instructions">
Click the "Clear Messages" button then connect and disconnect a wired headset.
Note if the appropriate notification messages appear below.
</string>
+ <string name="audio_in_devices_notifications_test">Audio Input Devices Notifications Test</string>
+ <string name="audio_in_devices_notification_instructions">
+ Click the "Clear Messages" button then connect and disconnect a microphone or wired headset.
+ Note if the appropriate notification messages appear below.
+ </string>
<string name="audio_dev_notification_clearmsgs">Clear Messages</string>
<string name="audio_dev_notification_connectMsg">CONNECT DETECTED</string>
<string name="audio_dev_notification_disconnectMsg">DISCONNECT DETECTED</string>
- <!-- Audio Routing Notifcations Test -->
- <string name="audio_routingnotifications_test">Audio Routing Notifications Test</string>
- <string name="audio_dev_routingnotification_instructions">
- Click on the "Play" button in the AudioTrack Routing Notifictions section below to
+ <string name="audio_input_routingnotifications_test">Audio Input Routing Notifications Test</string>
+ <string name="audio_input_routingnotification_instructions">
+ Click on the "Record" button in the AudioRecord Routing Notifications section below to
+ start recording. Insert a wired headset or microphone. Observe a message acknowledging the
+ rerouting event below. Remove the wired headset and observe the new routing message.
+ Click on the "Stop" button to stop recording.\n
+ </string>
+ <string name="audio_output_routingnotifications_test">Audio Output Routing Notifications Test</string>
+ <string name="audio_output_routingnotification_instructions">
+ Click on the "Play" button in the AudioTrack Routing Notifications section below to
start (silent) playback. Insert a wired headset. Observe a message acknowledging the
rerouting event below. Remove the wired headset and observe the new routing message.
Click on the "Stop" button to stop playback.\n
- Repeat the process with "Record" and "Stop" button in the AudioRecord Routing
- Notifications section below.
</string>
<string name="audio_routingnotification_playBtn">Play</string>
<string name="audio_routingnotification_playStopBtn">Stop</string>
@@ -2005,14 +2078,19 @@
<string name="audio_routingnotification_trackRoutingMsg">AudioTrack rerouting</string>
<string name="audio_routingnotification_recordRoutingMsg">AudioRecord rerouting</string>
+ <!-- Audio general text -->
+ <string name="audio_general_headset_port_exists">Does this device have a headset port?</string>
+ <string name="audio_general_headset_no">No</string>
+ <string name="audio_general_headset_yes">Yes</string>
+
<!-- Audio Loopback Latency Test -->
<string name="audio_loopback_test">Audio Loopback Latency Test</string>
<string name="audio_loopback_info">
- This test requires the Loopback Plug. Please connect a Loopback Plug on the headset
+ This test requires the Loopback Plug. Please connect a Loopback Plug into the headset
connector, and proceed with the instructions on the screen.
The system will measure the input-output audio latency by injecting a pulse on the output,
and computing the distance between replicas of the pulse.
- You can vary the Audio Level slider to ensure the pulse will feed back at adecuate levels.
+ You can vary the Audio Level slider to ensure the pulse will feed back at adequate levels.
Repeat until a confidence level >= 0.6 is achieved.
</string>
<string name="audio_loopback_instructions">
@@ -2047,7 +2125,7 @@
<string name="audio_frequency_speaker_test">Audio Frequency Speaker Test</string>
<string name="audio_frequency_speaker_info">
This test requires an external USB reference microphone. Please connect the USB microphone and proceed with the instructions on the screen.
- The system will measure frequency response of the left and right speakers (if there are two speakers), or twice the response of the mono speaker.
+ The system will measure frequency response of the left and right speakers (if there are two speakers), or the response of the mono speaker twice.
</string>
<string name="audio_frequency_speaker_instructions">
Please connect an USB reference microphone and press "USB Reference microphone ready"
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/DialogTestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/DialogTestListActivity.java
index 789effa..91b8d93 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/DialogTestListActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/DialogTestListActivity.java
@@ -23,6 +23,7 @@
import android.content.Intent;
import android.database.DataSetObserver;
import android.os.Bundle;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
@@ -41,6 +42,7 @@
* Instructions are shown on top of the screen and a test preparation button is provided.
*/
public abstract class DialogTestListActivity extends PassFailButtons.TestListActivity {
+ private final String TAG = "DialogTestListActivity";
private final int mLayoutId;
private final int mTitleStringId;
private final int mInfoStringId;
@@ -170,7 +172,13 @@
mCurrentTestPosition = position;
((DialogTestListItem)test).performTest(this);
} else {
- super.handleItemClick(l, v, position, id);
+ try {
+ super.handleItemClick(l, v, position, id);
+ } catch (ActivityNotFoundException e) {
+ Log.d(TAG, "handleItemClick() threw exception: ", e);
+ setTestResult(test, TestResult.TEST_RESULT_FAILED);
+ showToast(R.string.test_failed_cannot_start_intent);
+ }
}
}
@@ -196,7 +204,7 @@
// do nothing, override in subclass if needed
}
- protected void setTestResult(DialogTestListItem test, int result) {
+ protected void setTestResult(TestListAdapter.TestListItem test, int result) {
// Bundle result in an intent to feed into handleLaunchTestResult
Intent resultIntent = new Intent();
TestResult.addResultData(resultIntent, result, test.testName, /* testDetails */ null,
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyLineActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyLineActivity.java
index d3e2571..508fae0 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyLineActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyLineActivity.java
@@ -64,6 +64,9 @@
OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
Context mContext;
+ Button mHeadsetPortYes;
+ Button mHeadsetPortNo;
+
Button mLoopbackPlugReady;
LinearLayout mLinearLayout;
Button mTestButton;
@@ -116,6 +119,20 @@
Log.i(TAG, "audio loopback test");
startAudioTest();
break;
+ case R.id.audio_general_headset_yes:
+ Log.i(TAG, "User confirms Headset Port existence");
+ mLoopbackPlugReady.setEnabled(true);
+ recordHeasetPortFound(true);
+ mHeadsetPortYes.setEnabled(false);
+ mHeadsetPortNo.setEnabled(false);
+ break;
+ case R.id.audio_general_headset_no:
+ Log.i(TAG, "User denies Headset Port existence");
+ recordHeasetPortFound(false);
+ getPassButton().setEnabled(true);
+ mHeadsetPortYes.setEnabled(false);
+ mHeadsetPortNo.setEnabled(false);
+ break;
}
}
}
@@ -127,8 +144,14 @@
mContext = this;
+ mHeadsetPortYes = (Button)findViewById(R.id.audio_general_headset_yes);
+ mHeadsetPortYes.setOnClickListener(mBtnClickListener);
+ mHeadsetPortNo = (Button)findViewById(R.id.audio_general_headset_no);
+ mHeadsetPortNo.setOnClickListener(mBtnClickListener);
+
mLoopbackPlugReady = (Button)findViewById(R.id.audio_frequency_line_plug_ready_btn);
mLoopbackPlugReady.setOnClickListener(mBtnClickListener);
+ mLoopbackPlugReady.setEnabled(false);
mLinearLayout = (LinearLayout)findViewById(R.id.audio_frequency_line_layout);
mTestButton = (Button)findViewById(R.id.audio_frequency_line_test_btn);
mTestButton.setOnClickListener(mBtnClickListener);
@@ -479,6 +502,14 @@
Log.v(TAG, "Results Recorded");
}
+ private void recordHeasetPortFound(boolean found) {
+ getReportLog().addValue(
+ "User Reported Headset Port",
+ found ? 1.0 : 0,
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+ }
+
private void startRecording() {
synchronized (mRecordingLock) {
mIsRecording = true;
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyMicActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyMicActivity.java
index b37a721..03d84e1 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyMicActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyMicActivity.java
@@ -72,6 +72,9 @@
final OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
Context mContext;
+ Button mHeadsetPortYes;
+ Button mHeadsetPortNo;
+
Button mSpeakersReady; //user signal to have connected external speakers
Button mTest1Button; //execute test 1
Button mUsbMicReady; //user signal to have connected USB Microphone
@@ -137,6 +140,20 @@
case R.id.audio_frequency_mic_test2_btn:
startTest2();
break;
+ case R.id.audio_general_headset_yes:
+ Log.i(TAG, "User confirms Headset Port existence");
+ mSpeakersReady.setEnabled(true);
+ recordHeasetPortFound(true);
+ mHeadsetPortYes.setEnabled(false);
+ mHeadsetPortNo.setEnabled(false);
+ break;
+ case R.id.audio_general_headset_no:
+ Log.i(TAG, "User denies Headset Port existence");
+ recordHeasetPortFound(false);
+ getPassButton().setEnabled(true);
+ mHeadsetPortYes.setEnabled(false);
+ mHeadsetPortNo.setEnabled(false);
+ break;
}
}
}
@@ -146,10 +163,17 @@
super.onCreate(savedInstanceState);
setContentView(R.layout.audio_frequency_mic_activity);
mContext = this;
+
+ mHeadsetPortYes = (Button)findViewById(R.id.audio_general_headset_yes);
+ mHeadsetPortYes.setOnClickListener(mBtnClickListener);
+ mHeadsetPortNo = (Button)findViewById(R.id.audio_general_headset_no);
+ mHeadsetPortNo.setOnClickListener(mBtnClickListener);
+
mSpeakerReadyText = (TextView) findViewById(R.id.audio_frequency_mic_speakers_ready_status);
mSpeakersReady = (Button)findViewById(R.id.audio_frequency_mic_speakers_ready_btn);
mSpeakersReady.setOnClickListener(mBtnClickListener);
+ mSpeakersReady.setEnabled(false);
mTest1Button = (Button)findViewById(R.id.audio_frequency_mic_test1_btn);
mTest1Button.setOnClickListener(mBtnClickListener);
mTest1Result = (TextView)findViewById(R.id.audio_frequency_mic_results1_text);
@@ -644,6 +668,14 @@
Log.v(TAG, "Results Recorded");
}
+ private void recordHeasetPortFound(boolean found) {
+ getReportLog().addValue(
+ "User Reported Headset Port",
+ found ? 1.0 : 0,
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+ }
+
private void startRecording() {
synchronized (mRecordingLock) {
mIsRecording = true;
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioDeviceNotificationsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioInputDeviceNotificationsActivity.java
similarity index 86%
copy from apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioDeviceNotificationsActivity.java
copy to apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioInputDeviceNotificationsActivity.java
index 93e0507..3513774 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioDeviceNotificationsActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioInputDeviceNotificationsActivity.java
@@ -34,10 +34,10 @@
import android.widget.TextView;
/**
- * Tests Audio Device Connection events by prompting the user to insert/remove a wired headset
- * and noting the presence (or absence) of notifictions.
+ * Tests Audio Device Connection events for output by prompting the user to insert/remove a
+ * wired headset (or microphone) and noting the presence (or absence) of notifications.
*/
-public class AudioDeviceNotificationsActivity extends PassFailButtons.Activity {
+public class AudioInputDeviceNotificationsActivity extends PassFailButtons.Activity {
Context mContext;
TextView mConnectView;
@@ -71,6 +71,9 @@
mConnectView = (TextView)findViewById(R.id.audio_dev_notification_connect_msg);
mDisconnectView = (TextView)findViewById(R.id.audio_dev_notification_disconnect_msg);
+ ((TextView)findViewById(R.id.info_text)).setText(mContext.getResources().getString(
+ R.string.audio_in_devices_notification_instructions));
+
mClearMsgsBtn = (Button)findViewById(R.id.audio_dev_notification_connect_clearmsgs_btn);
mClearMsgsBtn.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioRoutingNotificationsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioInputRoutingNotificationsActivity.java
similarity index 61%
copy from apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioRoutingNotificationsActivity.java
copy to apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioInputRoutingNotificationsActivity.java
index b6a4255..cdc8199 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioRoutingNotificationsActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioInputRoutingNotificationsActivity.java
@@ -25,7 +25,6 @@
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
import android.media.AudioRecord;
-import android.media.AudioTrack;
import android.os.Bundle;
import android.os.Handler;
@@ -39,60 +38,34 @@
import android.widget.TextView;
/**
- * Tests AudioTrack and AudioRecord (re)Routing messages.
+ * Tests AudioRecord (re)Routing messages.
*/
-public class AudioRoutingNotificationsActivity extends PassFailButtons.Activity {
- private static final String TAG = "AudioRoutingNotificationsActivity";
+public class AudioInputRoutingNotificationsActivity extends PassFailButtons.Activity {
+ private static final String TAG = "AudioInputRoutingNotificationsActivity";
Context mContext;
- OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
-
- int mNumTrackNotifications = 0;
int mNumRecordNotifications = 0;
- TrivialPlayer mAudioPlayer = new TrivialPlayer();
+ OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
+
TrivialRecorder mAudioRecorder = new TrivialRecorder();
private class OnBtnClickListener implements OnClickListener {
@Override
public void onClick(View v) {
switch (v.getId()) {
- case R.id.audio_routingnotification_playBtn:
- Log.i(TAG, "audio_routingnotification_playBtn");
- mAudioPlayer.start();
- break;
-
- case R.id.audio_routingnotification_playStopBtn:
- Log.i(TAG, "audio_routingnotification_playStopBtn");
- mAudioPlayer.stop();
- break;
-
case R.id.audio_routingnotification_recordBtn:
+ mAudioRecorder.start();
break;
case R.id.audio_routingnotification_recordStopBtn:
+ mAudioRecorder.stop();
break;
}
}
}
- private class AudioTrackRoutingChangeListener implements AudioTrack.OnRoutingChangedListener {
- public void onRoutingChanged(AudioTrack audioTrack) {
- mNumTrackNotifications++;
- TextView textView =
- (TextView)findViewById(R.id.audio_routingnotification_audioTrack_change);
- String msg = mContext.getResources().getString(
- R.string.audio_routingnotification_trackRoutingMsg);
- AudioDeviceInfo routedDevice = audioTrack.getRoutedDevice();
- CharSequence deviceName = routedDevice != null ? routedDevice.getProductName() : "none";
- int deviceType = routedDevice != null ? routedDevice.getType() : -1;
- textView.setText(msg + " - " +
- deviceName + " [0x" + Integer.toHexString(deviceType) + "]" +
- " - " + mNumTrackNotifications);
- }
- }
-
private class AudioRecordRoutingChangeListener implements AudioRecord.OnRoutingChangedListener {
public void onRoutingChanged(AudioRecord audioRecord) {
mNumRecordNotifications++;
@@ -112,13 +85,9 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.audio_routingnotifications_test);
+ setContentView(R.layout.audio_input_routingnotifications_test);
Button btn;
- btn = (Button)findViewById(R.id.audio_routingnotification_playBtn);
- btn.setOnClickListener(mBtnClickListener);
- btn = (Button)findViewById(R.id.audio_routingnotification_playStopBtn);
- btn.setOnClickListener(mBtnClickListener);
btn = (Button)findViewById(R.id.audio_routingnotification_recordBtn);
btn.setOnClickListener(mBtnClickListener);
btn = (Button)findViewById(R.id.audio_routingnotification_recordStopBtn);
@@ -126,10 +95,6 @@
mContext = this;
- AudioTrack audioTrack = mAudioPlayer.getAudioTrack();
- audioTrack.addOnRoutingChangedListener(
- new AudioTrackRoutingChangeListener(), new Handler());
-
AudioRecord audioRecord = mAudioRecorder.getAudioRecord();
audioRecord.addOnRoutingChangedListener(
new AudioRecordRoutingChangeListener(), new Handler());
@@ -139,7 +104,6 @@
@Override
public void onBackPressed () {
- mAudioPlayer.shutDown();
mAudioRecorder.shutDown();
super.onBackPressed();
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackActivity.java
index e603a69..fbec57a 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackActivity.java
@@ -61,6 +61,9 @@
OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
Context mContext;
+ Button mHeadsetPortYes;
+ Button mHeadsetPortNo;
+
Button mLoopbackPlugReady;
TextView mAudioLevelText;
SeekBar mAudioLevelSeekbar;
@@ -83,7 +86,20 @@
Log.i(TAG, "audio loopback test");
startAudioTest();
break;
-
+ case R.id.audio_general_headset_yes:
+ Log.i(TAG, "User confirms Headset Port existence");
+ mLoopbackPlugReady.setEnabled(true);
+ recordHeasetPortFound(true);
+ mHeadsetPortYes.setEnabled(false);
+ mHeadsetPortNo.setEnabled(false);
+ break;
+ case R.id.audio_general_headset_no:
+ Log.i(TAG, "User denies Headset Port existence");
+ recordHeasetPortFound(false);
+ getPassButton().setEnabled(true);
+ mHeadsetPortYes.setEnabled(false);
+ mHeadsetPortNo.setEnabled(false);
+ break;
}
}
}
@@ -95,8 +111,14 @@
mContext = this;
+ mHeadsetPortYes = (Button)findViewById(R.id.audio_general_headset_yes);
+ mHeadsetPortYes.setOnClickListener(mBtnClickListener);
+ mHeadsetPortNo = (Button)findViewById(R.id.audio_general_headset_no);
+ mHeadsetPortNo.setOnClickListener(mBtnClickListener);
+
mLoopbackPlugReady = (Button)findViewById(R.id.audio_loopback_plug_ready_btn);
mLoopbackPlugReady.setOnClickListener(mBtnClickListener);
+ mLoopbackPlugReady.setEnabled(false);
mLinearLayout = (LinearLayout)findViewById(R.id.audio_loopback_layout);
mAudioLevelText = (TextView)findViewById(R.id.audio_loopback_level_text);
mAudioLevelSeekbar = (SeekBar)findViewById(R.id.audio_loopback_level_seekbar);
@@ -135,7 +157,7 @@
setPassFailButtonClickListeners();
getPassButton().setEnabled(false);
- setInfoResources(R.string.sample_test, R.string.audio_loopback_info, -1);
+ setInfoResources(R.string.audio_loopback_test, R.string.audio_loopback_info, -1);
}
/**
@@ -304,4 +326,12 @@
Log.v(TAG,"Results Recorded");
}
+
+ private void recordHeasetPortFound(boolean found) {
+ getReportLog().addValue(
+ "User Reported Headset Port",
+ found ? 1.0 : 0,
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+ }
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioDeviceNotificationsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioOutputDeviceNotificationsActivity.java
similarity index 86%
rename from apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioDeviceNotificationsActivity.java
rename to apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioOutputDeviceNotificationsActivity.java
index 93e0507..a64ddc4 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioDeviceNotificationsActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioOutputDeviceNotificationsActivity.java
@@ -34,10 +34,10 @@
import android.widget.TextView;
/**
- * Tests Audio Device Connection events by prompting the user to insert/remove a wired headset
- * and noting the presence (or absence) of notifictions.
+ * Tests Audio Device Connection events for output devices by prompting the user to
+ * insert/remove a wired headset and noting the presence (or absence) of notifications.
*/
-public class AudioDeviceNotificationsActivity extends PassFailButtons.Activity {
+public class AudioOutputDeviceNotificationsActivity extends PassFailButtons.Activity {
Context mContext;
TextView mConnectView;
@@ -71,6 +71,9 @@
mConnectView = (TextView)findViewById(R.id.audio_dev_notification_connect_msg);
mDisconnectView = (TextView)findViewById(R.id.audio_dev_notification_disconnect_msg);
+ ((TextView)findViewById(R.id.info_text)).setText(mContext.getResources().getString(
+ R.string.audio_out_devices_notification_instructions));
+
mClearMsgsBtn = (Button)findViewById(R.id.audio_dev_notification_connect_clearmsgs_btn);
mClearMsgsBtn.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioRoutingNotificationsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioOutputRoutingNotificationsActivity.java
similarity index 62%
rename from apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioRoutingNotificationsActivity.java
rename to apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioOutputRoutingNotificationsActivity.java
index b6a4255..bfc3d45 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioRoutingNotificationsActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioOutputRoutingNotificationsActivity.java
@@ -24,7 +24,6 @@
import android.media.AudioDeviceCallback;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
-import android.media.AudioRecord;
import android.media.AudioTrack;
import android.os.Bundle;
@@ -41,38 +40,28 @@
/**
* Tests AudioTrack and AudioRecord (re)Routing messages.
*/
-public class AudioRoutingNotificationsActivity extends PassFailButtons.Activity {
- private static final String TAG = "AudioRoutingNotificationsActivity";
+public class AudioOutputRoutingNotificationsActivity extends PassFailButtons.Activity {
+ private static final String TAG = "AudioOutputRoutingNotificationsActivity";
Context mContext;
OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
int mNumTrackNotifications = 0;
- int mNumRecordNotifications = 0;
TrivialPlayer mAudioPlayer = new TrivialPlayer();
- TrivialRecorder mAudioRecorder = new TrivialRecorder();
private class OnBtnClickListener implements OnClickListener {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.audio_routingnotification_playBtn:
- Log.i(TAG, "audio_routingnotification_playBtn");
mAudioPlayer.start();
break;
case R.id.audio_routingnotification_playStopBtn:
- Log.i(TAG, "audio_routingnotification_playStopBtn");
mAudioPlayer.stop();
break;
-
- case R.id.audio_routingnotification_recordBtn:
- break;
-
- case R.id.audio_routingnotification_recordStopBtn:
- break;
}
}
}
@@ -93,36 +82,16 @@
}
}
- private class AudioRecordRoutingChangeListener implements AudioRecord.OnRoutingChangedListener {
- public void onRoutingChanged(AudioRecord audioRecord) {
- mNumRecordNotifications++;
- TextView textView =
- (TextView)findViewById(R.id.audio_routingnotification_audioRecord_change);
- String msg = mContext.getResources().getString(
- R.string.audio_routingnotification_recordRoutingMsg);
- AudioDeviceInfo routedDevice = audioRecord.getRoutedDevice();
- CharSequence deviceName = routedDevice != null ? routedDevice.getProductName() : "none";
- int deviceType = routedDevice != null ? routedDevice.getType() : -1;
- textView.setText(msg + " - " +
- deviceName + " [0x" + Integer.toHexString(deviceType) + "]" +
- " - " + mNumRecordNotifications);
- }
- }
-
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.audio_routingnotifications_test);
+ setContentView(R.layout.audio_output_routingnotifications_test);
Button btn;
btn = (Button)findViewById(R.id.audio_routingnotification_playBtn);
btn.setOnClickListener(mBtnClickListener);
btn = (Button)findViewById(R.id.audio_routingnotification_playStopBtn);
btn.setOnClickListener(mBtnClickListener);
- btn = (Button)findViewById(R.id.audio_routingnotification_recordBtn);
- btn.setOnClickListener(mBtnClickListener);
- btn = (Button)findViewById(R.id.audio_routingnotification_recordStopBtn);
- btn.setOnClickListener(mBtnClickListener);
mContext = this;
@@ -130,17 +99,12 @@
audioTrack.addOnRoutingChangedListener(
new AudioTrackRoutingChangeListener(), new Handler());
- AudioRecord audioRecord = mAudioRecorder.getAudioRecord();
- audioRecord.addOnRoutingChangedListener(
- new AudioRecordRoutingChangeListener(), new Handler());
-
setPassFailButtonClickListeners();
}
@Override
public void onBackPressed () {
mAudioPlayer.shutDown();
- mAudioRecorder.shutDown();
super.onBackPressed();
}
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java
index 27f8c28..e3ff74b 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java
@@ -39,12 +39,14 @@
import android.media.Image;
import android.media.ImageReader;
import android.media.ImageWriter;
+import android.media.Image.Plane;
import android.net.Uri;
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Message;
+import android.os.SystemClock;
import android.os.Vibrator;
import android.util.Log;
import android.util.Rational;
@@ -56,6 +58,8 @@
import com.android.ex.camera2.blocking.BlockingStateCallback;
import com.android.ex.camera2.blocking.BlockingSessionCallback;
+import com.android.cts.verifier.camera.its.StatsImage;
+
import org.json.JSONArray;
import org.json.JSONObject;
@@ -70,6 +74,8 @@
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.util.ArrayList;
@@ -154,6 +160,9 @@
private AtomicInteger mCountYuv = new AtomicInteger();
private AtomicInteger mCountCapRes = new AtomicInteger();
private boolean mCaptureRawIsDng;
+ private boolean mCaptureRawIsStats;
+ private int mCaptureStatsGridWidth;
+ private int mCaptureStatsGridHeight;
private CaptureResult mCaptureResults[] = null;
private volatile ConditionVariable mInterlock3A = new ConditionVariable(true);
@@ -404,7 +413,7 @@
continue;
}
if (b.hasArray()) {
- mOpenSocket.getOutputStream().write(b.array());
+ mOpenSocket.getOutputStream().write(b.array(), 0, b.capacity());
} else {
byte[] barray = new byte[b.capacity()];
b.get(barray);
@@ -665,7 +674,16 @@
jsonSurface.put("height", readers[i].getHeight());
int format = readers[i].getImageFormat();
if (format == ImageFormat.RAW_SENSOR) {
- jsonSurface.put("format", "raw");
+ if (mCaptureRawIsStats) {
+ jsonSurface.put("format", "rawStats");
+ jsonSurface.put("width", readers[i].getWidth()/mCaptureStatsGridWidth);
+ jsonSurface.put("height",
+ readers[i].getHeight()/mCaptureStatsGridHeight);
+ } else if (mCaptureRawIsDng) {
+ jsonSurface.put("format", "dng");
+ } else {
+ jsonSurface.put("format", "raw");
+ }
} else if (format == ImageFormat.RAW10) {
jsonSurface.put("format", "raw10");
} else if (format == ImageFormat.RAW12) {
@@ -1068,6 +1086,12 @@
outputFormats[i] = ImageFormat.RAW_SENSOR;
sizes = ItsUtils.getRaw16OutputSizes(mCameraCharacteristics);
mCaptureRawIsDng = true;
+ } else if ("rawStats".equals(sformat)) {
+ outputFormats[i] = ImageFormat.RAW_SENSOR;
+ sizes = ItsUtils.getRaw16OutputSizes(mCameraCharacteristics);
+ mCaptureRawIsStats = true;
+ mCaptureStatsGridWidth = surfaceObj.optInt("gridWidth");
+ mCaptureStatsGridHeight = surfaceObj.optInt("gridHeight");
} else {
throw new ItsException("Unsupported format: " + sformat);
}
@@ -1086,6 +1110,14 @@
if (height <= 0) {
height = ItsUtils.getMaxSize(sizes).getHeight();
}
+ if (mCaptureStatsGridWidth <= 0) {
+ mCaptureStatsGridWidth = width;
+ }
+ if (mCaptureStatsGridHeight <= 0) {
+ mCaptureStatsGridHeight = height;
+ }
+
+ // TODO: Crop to the active array in the stats image analysis.
outputSizes[i] = new Size(width, height);
}
@@ -1122,6 +1154,7 @@
mCountRaw12.set(0);
mCountCapRes.set(0);
mCaptureRawIsDng = false;
+ mCaptureRawIsStats = false;
mCaptureResults = new CaptureResult[requests.size()];
JSONArray jsonOutputSpecs = ItsUtils.getOutputSpecs(params);
@@ -1235,6 +1268,7 @@
mCountRaw12.set(0);
mCountCapRes.set(0);
mCaptureRawIsDng = false;
+ mCaptureRawIsStats = false;
try {
// Parse the JSON to get the list of capture requests.
@@ -1427,8 +1461,28 @@
int count = mCountRawOrDng.getAndIncrement();
if (! mCaptureRawIsDng) {
byte[] img = ItsUtils.getDataFromImage(capture);
- ByteBuffer buf = ByteBuffer.wrap(img);
- mSocketRunnableObj.sendResponseCaptureBuffer("rawImage", buf);
+ if (! mCaptureRawIsStats) {
+ ByteBuffer buf = ByteBuffer.wrap(img);
+ mSocketRunnableObj.sendResponseCaptureBuffer("rawImage", buf);
+ } else {
+ // Compute the requested stats on the raw frame, and return the results
+ // in a new "stats image".
+ long startTimeMs = SystemClock.elapsedRealtime();
+ int w = capture.getWidth();
+ int h = capture.getHeight();
+ int gw = mCaptureStatsGridWidth;
+ int gh = mCaptureStatsGridHeight;
+ float[] stats = StatsImage.computeStatsImage(img, w, h, gw, gh);
+ long endTimeMs = SystemClock.elapsedRealtime();
+ Log.e(TAG, "Raw stats computation takes " + (endTimeMs - startTimeMs) + " ms");
+
+ ByteBuffer bBuf = ByteBuffer.allocateDirect(stats.length * 4);
+ bBuf.order(ByteOrder.nativeOrder());
+ FloatBuffer fBuf = bBuf.asFloatBuffer();
+ fBuf.put(stats);
+ fBuf.position(0);
+ mSocketRunnableObj.sendResponseCaptureBuffer("rawStatsImage", bBuf);
+ }
} else {
// Wait until the corresponding capture result is ready, up to a timeout.
long t0 = android.os.SystemClock.elapsedRealtime();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/StatsImage.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/StatsImage.java
new file mode 100644
index 0000000..037177c
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/StatsImage.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.camera.its;
+
+import android.util.Log;
+
+public class StatsImage {
+
+ static {
+ try {
+ System.loadLibrary("ctsverifier_jni");
+ } catch (UnsatisfiedLinkError e) {
+ Log.e("StatsImage", "Error loading cts verifier JNI library");
+ e.printStackTrace();
+ }
+ }
+
+ public native static float[] computeStatsImage(
+ byte[] img, int width, int height, int gridW, int gridH);
+
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureSummaryActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureSummaryActivity.java
index 51e0a62..3c108da 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureSummaryActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureSummaryActivity.java
@@ -232,6 +232,7 @@
public static final Feature[] ALL_MNC_FEATURES = {
new Feature(PackageManager.FEATURE_MIDI, false),
+ new Feature(PackageManager.FEATURE_AUDIO_PRO, false),
};
@Override
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/AuthenticationBoundKeyTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/AuthenticationBoundKeyTestActivity.java
new file mode 100644
index 0000000..073412d
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/AuthenticationBoundKeyTestActivity.java
@@ -0,0 +1,381 @@
+package com.android.cts.verifier.managedprovisioning;
+
+import android.Manifest;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.KeyguardManager;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.hardware.fingerprint.FingerprintManager;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.CountDownTimer;
+import android.provider.Settings;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyPermanentlyInvalidatedException;
+import android.security.keystore.KeyProperties;
+import android.security.keystore.UserNotAuthenticatedException;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Toast;
+
+import com.android.cts.verifier.ArrayTestListAdapter;
+import com.android.cts.verifier.DialogTestListActivity;
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.TestResult;
+
+import java.io.IOException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.KeyGenerator;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+
+/**
+ * Test device credential-bound keys in work profile.
+ * Currently there are two types, one is keys bound to lockscreen passwords which can be configured
+ * to remain available within a certain timeout after the latest successful user authentication.
+ * The other is keys bound to fingerprint authentication which require explicit fingerprint
+ * authentication before they can be accessed.
+ */
+public class AuthenticationBoundKeyTestActivity extends DialogTestListActivity {
+
+ public static final String ACTION_AUTH_BOUND_KEY_TEST =
+ "com.android.cts.verifier.managedprovisioning.action.AUTH_BOUND_KEY_TEST";
+
+ private static final int AUTHENTICATION_DURATION_SECONDS = 5;
+ private static final String LOCKSCREEN_KEY_NAME = "mp_lockscreen_key";
+ private static final String FINGERPRINT_KEY_NAME = "mp_fingerprint_key";
+ private static final byte[] SECRET_BYTE_ARRAY = new byte[] {1, 2, 3, 4, 5, 6};
+ private static final int CONFIRM_CREDENTIALS_REQUEST_CODE = 1;
+ private static final int FINGERPRINT_PERMISSION_REQUEST_CODE = 0;
+
+ private static final int LOCKSCREEN = 1;
+ private static final int FINGERPRINT = 2;
+
+ private static final String KEYSTORE_NAME = "AndroidKeyStore";
+ private static final String CIPHER_TRANSFORMATION = KeyProperties.KEY_ALGORITHM_AES + "/"
+ + KeyProperties.BLOCK_MODE_CBC + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7;
+
+
+ private KeyguardManager mKeyguardManager;
+ private FingerprintManager mFingerprintManager;
+ private boolean mFingerprintSupported;
+
+ private DialogTestListItem mLockScreenBoundKeyTest;
+ private DialogTestListItem mFingerprintBoundKeyTest;
+
+ private Cipher mFingerprintCipher;
+
+ public AuthenticationBoundKeyTestActivity() {
+ super(R.layout.provisioning_byod,
+ R.string.provisioning_byod_auth_bound_key,
+ R.string.provisioning_byod_auth_bound_key_info,
+ R.string.provisioning_byod_auth_bound_key_instruction);
+ }
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ mKeyguardManager = (KeyguardManager) getSystemService(KEYGUARD_SERVICE);
+ mFingerprintManager = (FingerprintManager) getSystemService(FINGERPRINT_SERVICE);
+ mFingerprintSupported = mFingerprintManager != null
+ && mFingerprintManager.isHardwareDetected();
+ // Need to have valid mFingerprintSupported value before calling super.onCreate() because
+ // mFingerprintSupported is used in setupTests() which gets called by super.onCreate().
+ super.onCreate(savedInstanceState);
+
+ mPrepareTestButton.setText(R.string.provisioning_byod_auth_bound_key_set_up);
+ mPrepareTestButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View arg0) {
+ startActivity(new Intent(Settings.ACTION_SECURITY_SETTINGS));
+ }
+ });
+ if (mFingerprintSupported) {
+ requestPermissions(new String[] {Manifest.permission.USE_FINGERPRINT},
+ FINGERPRINT_PERMISSION_REQUEST_CODE);
+ }
+ }
+
+ private class LockscreenCountDownTester extends CountDownTimer {
+
+ private Toast mToast;
+
+ public LockscreenCountDownTester() {
+ // Wait for AUTHENTICATION_DURATION_SECONDS so the key is evicted before the real test.
+ super(AUTHENTICATION_DURATION_SECONDS * 1000, 1000);
+ mToast = Toast.makeText(AuthenticationBoundKeyTestActivity.this, "", Toast.LENGTH_SHORT);
+ }
+
+ @Override
+ public void onFinish() {
+ mToast.cancel();
+ if (tryEncryptWithLockscreenKey()) {
+ showToast("Test failed. Key accessible without auth.");
+ setTestResult(mLockScreenBoundKeyTest, TestResult.TEST_RESULT_FAILED);
+ } else {
+ // Start the Confirm Credentials screen.
+ Intent intent = mKeyguardManager.createConfirmDeviceCredentialIntent(null, null);
+ if (intent != null) {
+ startActivityForResult(intent, CONFIRM_CREDENTIALS_REQUEST_CODE);
+ } else {
+ showToast("Test failed. No lockscreen password exists.");
+ setTestResult(mLockScreenBoundKeyTest, TestResult.TEST_RESULT_FAILED);
+ }
+ }
+ }
+
+ @Override
+ public void onTick(long millisUntilFinished) {
+ mToast.setText(String.format("Lockscreen challenge start in %d seconds..",
+ millisUntilFinished / 1000));
+ mToast.show();
+ }
+ }
+
+
+ @Override
+ protected void setupTests(ArrayTestListAdapter adapter) {
+ mLockScreenBoundKeyTest = new DialogTestListItem(this,
+ R.string.provisioning_byod_lockscreen_bound_key,
+ "BYOD_LockScreenBoundKeyTest") {
+
+ @Override
+ public void performTest(DialogTestListActivity activity) {
+ if (checkPreconditions()) {
+ createKey(LOCKSCREEN);
+ new LockscreenCountDownTester().start();
+ }
+ }
+ };
+ adapter.add(mLockScreenBoundKeyTest);
+
+ if (mFingerprintSupported) {
+ mFingerprintBoundKeyTest = new DialogTestListItem(this,
+ R.string.provisioning_byod_fingerprint_bound_key,
+ "BYOD_FingerprintBoundKeyTest") {
+
+ @Override
+ public void performTest(DialogTestListActivity activity) {
+ if (checkPreconditions()) {
+ createKey(FINGERPRINT);
+ mFingerprintCipher = initFingerprintEncryptionCipher();
+ if (tryEncryptWithFingerprintKey(mFingerprintCipher)) {
+ showToast("Test failed. Key accessible without auth.");
+ setTestResult(mFingerprintBoundKeyTest, TestResult.TEST_RESULT_FAILED);
+ } else {
+ new FingerprintAuthDialogFragment().show(getFragmentManager(),
+ "fingerprint_dialog");
+ }
+ }
+ }
+ };
+ adapter.add(mFingerprintBoundKeyTest);
+ }
+ }
+
+ private boolean checkPreconditions() {
+ if (!mKeyguardManager.isKeyguardSecure()) {
+ showToast("Please set a lockscreen password.");
+ return false;
+ } else if (mFingerprintSupported && !mFingerprintManager.hasEnrolledFingerprints()) {
+ showToast("Please enroll a fingerprint.");
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ private String getKeyName(int testType) {
+ return testType == LOCKSCREEN ? LOCKSCREEN_KEY_NAME : FINGERPRINT_KEY_NAME;
+ }
+ /**
+ * Creates a symmetric key in the Android Key Store which can only be used after the user has
+ * authenticated with device credentials.
+ */
+ private void createKey(int testType) {
+ try {
+ // Set the alias of the entry in Android KeyStore where the key will appear
+ // and the constrains (purposes) in the constructor of the Builder
+ KeyGenParameterSpec.Builder builder;
+ builder = new KeyGenParameterSpec.Builder(getKeyName(testType),
+ KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
+ .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
+ .setUserAuthenticationRequired(true)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7);
+ if (testType == LOCKSCREEN) {
+ // Require that the user unlocked the lockscreen in the last 5 seconds
+ builder.setUserAuthenticationValidityDurationSeconds(
+ AUTHENTICATION_DURATION_SECONDS);
+ }
+ KeyGenerator keyGenerator = KeyGenerator.getInstance(
+ KeyProperties.KEY_ALGORITHM_AES, KEYSTORE_NAME);
+ keyGenerator.init(builder.build());
+ keyGenerator.generateKey();
+ } catch (NoSuchAlgorithmException | NoSuchProviderException
+ | InvalidAlgorithmParameterException e) {
+ throw new RuntimeException("Failed to create a symmetric key", e);
+ }
+ }
+
+ private SecretKey loadSecretKey(int testType) {
+ try {
+ KeyStore keyStore = KeyStore.getInstance(KEYSTORE_NAME);
+ keyStore.load(null);
+ return (SecretKey) keyStore.getKey(getKeyName(testType), null);
+ } catch (UnrecoverableKeyException | CertificateException |KeyStoreException | IOException
+ | NoSuchAlgorithmException e) {
+ throw new RuntimeException("Failed to load a symmetric key", e);
+ }
+ }
+
+ private boolean tryEncryptWithLockscreenKey() {
+ try {
+ // Try encrypting something, it will only work if the user authenticated within
+ // the last AUTHENTICATION_DURATION_SECONDS seconds.
+ Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
+ cipher.init(Cipher.ENCRYPT_MODE, loadSecretKey(LOCKSCREEN));
+ cipher.doFinal(SECRET_BYTE_ARRAY);
+ return true;
+ } catch (UserNotAuthenticatedException e) {
+ // User is not authenticated, let's authenticate with device credentials.
+ return false;
+ } catch (KeyPermanentlyInvalidatedException e) {
+ // This happens if the lock screen has been disabled or reset after the key was
+ // generated.
+ createKey(LOCKSCREEN);
+ showToast("Set up lockscreen after test ran. Retry the test.");
+ return false;
+ } catch (IllegalBlockSizeException | BadPaddingException | InvalidKeyException
+ | NoSuchPaddingException | NoSuchAlgorithmException e) {
+ throw new RuntimeException("Encrypt with lockscreen-bound key failed", e);
+ }
+ }
+
+ private Cipher initFingerprintEncryptionCipher() {
+ try {
+ Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
+ cipher.init(Cipher.ENCRYPT_MODE, loadSecretKey(FINGERPRINT));
+ return cipher;
+ } catch (NoSuchPaddingException | NoSuchAlgorithmException e) {
+ return null;
+ } catch (KeyPermanentlyInvalidatedException e) {
+ // This happens if the lock screen has been disabled or reset after the key was
+ // generated after the key was generated.
+ createKey(FINGERPRINT);
+ showToast("Set up lockscreen after test ran. Retry the test.");
+ return null;
+ } catch (InvalidKeyException e) {
+ throw new RuntimeException("Init cipher with fingerprint-bound key failed", e);
+ }
+ }
+
+ private boolean tryEncryptWithFingerprintKey(Cipher cipher) {
+
+ try {
+ cipher.doFinal(SECRET_BYTE_ARRAY);
+ return true;
+ } catch (IllegalBlockSizeException e) {
+ // Cannot encrypt, key is unavailable
+ return false;
+ } catch (BadPaddingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ protected void handleActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case CONFIRM_CREDENTIALS_REQUEST_CODE:
+ if (resultCode == RESULT_OK) {
+ if (tryEncryptWithLockscreenKey()) {
+ setTestResult(mLockScreenBoundKeyTest, TestResult.TEST_RESULT_PASSED);
+ } else {
+ showToast("Test failed. Key not accessible after auth");
+ setTestResult(mLockScreenBoundKeyTest, TestResult.TEST_RESULT_FAILED);
+ }
+ } else {
+ showToast("Lockscreen challenge canceled.");
+ setTestResult(mLockScreenBoundKeyTest, TestResult.TEST_RESULT_FAILED);
+ }
+ break;
+ default:
+ super.handleActivityResult(requestCode, resultCode, data);
+ }
+ }
+
+ private void showToast(String message) {
+ Toast.makeText(this, message, Toast.LENGTH_LONG).show();
+ }
+
+ public class FingerprintAuthDialogFragment extends DialogFragment {
+
+ private CancellationSignal mCancellationSignal;
+ private FingerprintManagerCallback mFingerprintManagerCallback;
+ private boolean mSelfCancelled;
+
+ class FingerprintManagerCallback extends FingerprintManager.AuthenticationCallback {
+ @Override
+ public void onAuthenticationError(int errMsgId, CharSequence errString) {
+ if (!mSelfCancelled) {
+ showToast(errString.toString());
+ }
+ }
+
+ @Override
+ public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
+ showToast(helpString.toString());
+ }
+
+ @Override
+ public void onAuthenticationFailed() {
+ showToast(getString(R.string.sec_fp_auth_failed));
+ }
+
+ @Override
+ public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
+ if (tryEncryptWithFingerprintKey(mFingerprintCipher)) {
+ showToast("Test passed.");
+ setTestResult(mFingerprintBoundKeyTest, TestResult.TEST_RESULT_PASSED);
+ } else {
+ showToast("Test failed. Key not accessible after auth");
+ setTestResult(mFingerprintBoundKeyTest, TestResult.TEST_RESULT_FAILED);
+ }
+ FingerprintAuthDialogFragment.this.dismiss();
+ }
+ }
+
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ mCancellationSignal.cancel();
+ mSelfCancelled = true;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ mCancellationSignal = new CancellationSignal();
+ mSelfCancelled = false;
+ mFingerprintManagerCallback = new FingerprintManagerCallback();
+ mFingerprintManager.authenticate(new FingerprintManager.CryptoObject(mFingerprintCipher),
+ mCancellationSignal, 0, mFingerprintManagerCallback, null);
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ builder.setMessage(R.string.sec_fp_dialog_message);
+ return builder.create();
+ }
+
+ }
+
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
index 0200a4f..b129665 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
@@ -19,6 +19,7 @@
import android.app.admin.DevicePolicyManager;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
@@ -59,7 +60,9 @@
private DialogTestListItem mProfileAccountVisibleTest;
private DialogTestListItem mDeviceAdminVisibleTest;
private DialogTestListItem mWorkAppVisibleTest;
- private DialogTestListItem mCrossProfileIntentFiltersTest;
+ private DialogTestListItem mCrossProfileIntentFiltersTestFromPersonal;
+ private DialogTestListItem mCrossProfileIntentFiltersTestFromWork;
+ private DialogTestListItem mAppLinkingTest;
private DialogTestListItem mDisableNonMarketTest;
private DialogTestListItem mEnableNonMarketTest;
private DialogTestListItem mWorkNotificationBadgedTest;
@@ -78,6 +81,7 @@
private DialogTestListItem mCrossProfileAudioCaptureSupportTest;
private TestListItem mKeyguardDisabledFeaturesTest;
private DialogTestListItem mDisableNfcBeamTest;
+ private TestListItem mAuthenticationBoundKeyTest;
public ByodFlowTestActivity() {
super(R.layout.provisioning_byod,
@@ -264,20 +268,39 @@
R.string.provisioning_byod_print_settings_instruction,
new Intent(Settings.ACTION_PRINT_SETTINGS));
- Intent intent = new Intent(CrossProfileTestActivity.ACTION_CROSS_PROFILE);
+ Intent intent = new Intent(CrossProfileTestActivity.ACTION_CROSS_PROFILE_TO_WORK);
+ intent.putExtra(CrossProfileTestActivity.EXTRA_STARTED_FROM_WORK, false);
Intent chooser = Intent.createChooser(intent,
getResources().getString(R.string.provisioning_cross_profile_chooser));
- mCrossProfileIntentFiltersTest = new DialogTestListItem(this,
- R.string.provisioning_byod_cross_profile,
- "BYOD_CrossProfileIntentFiltersTest",
- R.string.provisioning_byod_cross_profile_instruction,
+ mCrossProfileIntentFiltersTestFromPersonal = new DialogTestListItem(this,
+ R.string.provisioning_byod_cross_profile_from_personal,
+ "BYOD_CrossProfileIntentFiltersTestFromPersonal",
+ R.string.provisioning_byod_cross_profile_from_personal_instruction,
chooser);
+ mCrossProfileIntentFiltersTestFromWork = new DialogTestListItem(this,
+ R.string.provisioning_byod_cross_profile_from_work,
+ "BYOD_CrossProfileIntentFiltersTestFromWork",
+ R.string.provisioning_byod_cross_profile_from_work_instruction,
+ new Intent(ByodHelperActivity.ACTION_TEST_CROSS_PROFILE_INTENTS_DIALOG));
+
+ mAppLinkingTest = new DialogTestListItem(this,
+ R.string.provisioning_app_linking,
+ "BYOD_AppLinking",
+ R.string.provisioning_byod_app_linking_instruction,
+ new Intent(ByodHelperActivity.ACTION_TEST_APP_LINKING_DIALOG));
+
mKeyguardDisabledFeaturesTest = TestListItem.newTest(this,
R.string.provisioning_byod_keyguard_disabled_features,
KeyguardDisabledFeaturesActivity.class.getName(),
new Intent(this, KeyguardDisabledFeaturesActivity.class), null);
+ mAuthenticationBoundKeyTest = TestListItem.newTest(this,
+ R.string.provisioning_byod_auth_bound_key,
+ AuthenticationBoundKeyTestActivity.class.getName(),
+ new Intent(AuthenticationBoundKeyTestActivity.ACTION_AUTH_BOUND_KEY_TEST),
+ null);
+
// Test for checking if the required intent filters are set during managed provisioning.
mIntentFiltersTest = new DialogTestListItem(this,
R.string.provisioning_byod_cross_profile_intent_filters,
@@ -287,7 +310,7 @@
checkIntentFilters();
}
};
-
+
Intent permissionCheckIntent = new Intent(
PermissionLockdownTestActivity.ACTION_MANAGED_PROFILE_CHECK_PERMISSION_LOCKDOWN);
mPermissionLockdownTest = new DialogTestListItem(this,
@@ -314,12 +337,15 @@
adapter.add(mDataUsageSettingsVisibleTest);
adapter.add(mPrintSettingsVisibleTest);
- adapter.add(mCrossProfileIntentFiltersTest);
+ adapter.add(mCrossProfileIntentFiltersTestFromPersonal);
+ adapter.add(mCrossProfileIntentFiltersTestFromWork);
+ adapter.add(mAppLinkingTest);
adapter.add(mDisableNonMarketTest);
adapter.add(mEnableNonMarketTest);
adapter.add(mIntentFiltersTest);
adapter.add(mPermissionLockdownTest);
adapter.add(mKeyguardDisabledFeaturesTest);
+ adapter.add(mAuthenticationBoundKeyTest);
if (canResolveIntent(ByodHelperActivity.getCaptureImageIntent())) {
// Capture image intent can be resolved in primary profile, so test.
@@ -495,7 +521,8 @@
ByodHelperActivity.class.getName(),
WorkNotificationTestActivity.class.getName(),
WorkStatusTestActivity.class.getName(),
- PermissionLockdownTestActivity.ACTIVITY_ALIAS
+ PermissionLockdownTestActivity.ACTIVITY_ALIAS,
+ AuthenticationBoundKeyTestActivity.class.getName()
};
for (String component : components) {
getPackageManager().setComponentEnabledSetting(new ComponentName(this, component),
@@ -503,4 +530,5 @@
PackageManager.DONT_KILL_APP);
}
}
+
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java
index 3fec414..9ea5061 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java
@@ -27,6 +27,7 @@
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
+import android.os.UserManager;
import android.provider.MediaStore;
import android.provider.Settings;
import android.support.v4.content.FileProvider;
@@ -84,6 +85,18 @@
public static final String ACTION_CHECK_INTENT_FILTERS =
"com.android.cts.verifier.managedprovisioning.action.CHECK_INTENT_FILTERS";
+ // Primary -> managed intent: will send a cross profile intent and check if the user sees an
+ // intent picker dialog and can open the apps.
+ public static final String ACTION_TEST_CROSS_PROFILE_INTENTS_DIALOG =
+ "com.android.cts.verifier.managedprovisioning.action.TEST_CROSS_PROFILE_INTENTS_DIALOG";
+
+ // Primary -> managed intent: will send an app link intent and check if the user sees a
+ // dialog and can open the apps. This test is extremely similar to
+ // ACTION_TEST_CROSS_PROFILE_INTENTS_DIALOG, but the intent used is a web intent, and there is
+ // some behavior which is specific to web intents.
+ public static final String ACTION_TEST_APP_LINKING_DIALOG =
+ "com.android.cts.verifier.managedprovisioning.action.TEST_APP_LINKING_DIALOG";
+
public static final int RESULT_FAILED = RESULT_FIRST_USER;
private static final int REQUEST_INSTALL_PACKAGE = 1;
@@ -160,6 +173,8 @@
IntentFiltersTestHelper.FLAG_INTENTS_FROM_MANAGED);
setResult(intentFiltersSetForManagedIntents? RESULT_OK : RESULT_FAILED, null);
} else if (action.equals(ACTION_CAPTURE_AND_CHECK_IMAGE)) {
+ // We need the camera permission to send the image capture intent.
+ grantCameraPermissionToSelf();
Intent captureImageIntent = getCaptureImageIntent();
mImageUri = getTempUri("image.jpg");
captureImageIntent.putExtra(MediaStore.EXTRA_OUTPUT, mImageUri);
@@ -172,6 +187,8 @@
}
return;
} else if (action.equals(ACTION_CAPTURE_AND_CHECK_VIDEO)) {
+ // We need the camera permission to send the video capture intent.
+ grantCameraPermissionToSelf();
Intent captureVideoIntent = getCaptureVideoIntent();
mVideoUri = getTempUri("video.mp4");
captureVideoIntent.putExtra(MediaStore.EXTRA_OUTPUT, mVideoUri);
@@ -207,6 +224,16 @@
startActivity(testNfcBeamIntent);
finish();
return;
+ } else if (action.equals(ACTION_TEST_CROSS_PROFILE_INTENTS_DIALOG)) {
+ sendIntentInsideChooser(new Intent(
+ CrossProfileTestActivity.ACTION_CROSS_PROFILE_TO_PERSONAL));
+ } else if (action.equals(ACTION_TEST_APP_LINKING_DIALOG)) {
+ mDevicePolicyManager.addUserRestriction(
+ DeviceAdminTestReceiver.getReceiverComponentName(),
+ UserManager.ALLOW_PARENT_PROFILE_APP_LINKING);
+ Intent toSend = new Intent(Intent.ACTION_VIEW);
+ toSend.setData(Uri.parse("http://com.android.cts.verifier"));
+ sendIntentInsideChooser(toSend);
}
// This activity has no UI and is only used to respond to CtsVerifier in the primary side.
finish();
@@ -336,6 +363,19 @@
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
+ private void grantCameraPermissionToSelf() {
+ mDevicePolicyManager.setPermissionGrantState(mAdminReceiverComponent, getPackageName(),
+ android.Manifest.permission.CAMERA,
+ DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED);
+ }
+
+ private void sendIntentInsideChooser(Intent toSend) {
+ toSend.putExtra(CrossProfileTestActivity.EXTRA_STARTED_FROM_WORK, true);
+ Intent chooser = Intent.createChooser(toSend,
+ getResources().getString(R.string.provisioning_cross_profile_chooser));
+ startActivity(chooser);
+ }
+
@Override
public void onDialogClose() {
finish();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodPresentMediaDialog.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodPresentMediaDialog.java
index b3f126b..15f5bc8 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodPresentMediaDialog.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodPresentMediaDialog.java
@@ -31,10 +31,14 @@
import android.widget.ImageView;
import android.widget.Toast;
import android.widget.VideoView;
-
+import android.view.Display;
+import android.graphics.BitmapFactory;
+import android.graphics.Point;
+import android.content.ContentResolver;
import com.android.cts.verifier.R;
import java.io.IOException;
+import java.io.InputStream;
/**
* This dialog shows/plays an image, video or audio uri.
@@ -46,6 +50,7 @@
private static final String KEY_IMAGE_URI = "image";
private static final String KEY_AUDIO_URI = "audio";
+ private Bitmap scaled = null;
/**
* Get a dialogFragment showing an image.
*/
@@ -79,6 +84,16 @@
return dialog;
}
+ private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight){
+ // Raw height and width of image
+ final int height = options.outHeight;
+ final int width = options.outWidth;
+ if(reqWidth <= 0 || reqHeight <= 0) {
+ return 1;
+ }
+ return Math.max(height/reqHeight, width/reqWidth) + 1;
+ }
+
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Dialog dialog = new Dialog(getActivity());
@@ -114,11 +129,36 @@
} else if (arguments.containsKey(KEY_IMAGE_URI)) {
// Show image UI.
dialog.setTitle(getString(R.string.provisioning_byod_verify_image_title));
-
- Uri uri = (Uri) getArguments().getParcelable(KEY_IMAGE_URI);
+ Uri uri = (Uri)getArguments().getParcelable(KEY_IMAGE_URI);
ImageView imageView = (ImageView) dialog.findViewById(R.id.imageView);
imageView.setVisibility(View.VISIBLE);
- imageView.setImageURI(uri);
+
+ try{
+ InputStream input = getActivity().getContentResolver().openInputStream(uri);
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ BitmapFactory.decodeStream(input, null, options);
+ //scale the picture
+ Display display = getActivity().getWindowManager().getDefaultDisplay();
+ Point size = new Point();
+ display.getSize(size);
+ int reqWidth = size.x;
+ int reqHeight = size.y;
+ options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
+
+ options.inJustDecodeBounds = false;
+ input.close();
+ input = getActivity().getContentResolver().openInputStream(uri);
+ scaled = BitmapFactory.decodeStream(input, null, options);
+ input.close();
+ imageView.setImageBitmap(scaled);
+ }catch(IOException e){
+ Log.e(TAG, "Cannot get image.", e);
+ Toast.makeText(getActivity(),R.string.provisioning_byod_capture_image_error,
+ Toast.LENGTH_SHORT).show();
+ getActivity().finish();
+ }
+
} else if (arguments.containsKey(KEY_AUDIO_URI)) {
// Show audio playback UI.
dialog.setTitle(getString(R.string.provisioning_byod_verify_audio_title));
@@ -161,6 +201,13 @@
((DialogCallback) getActivity()).onDialogClose();
}
+ @Override
+ public void onDestroyView() {
+ if(scaled!=null){
+ scaled.recycle();
+ }
+ super.onDestroyView();
+ }
public interface DialogCallback {
public abstract void onDialogClose();
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CrossProfileTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CrossProfileTestActivity.java
index 6c38e12..3f316f2 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CrossProfileTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CrossProfileTestActivity.java
@@ -35,7 +35,12 @@
*/
public class CrossProfileTestActivity extends Activity {
// Intent for app in both profiles
- public static final String ACTION_CROSS_PROFILE = "com.android.cts.verifier.managedprovisioning.CROSS_PROFILE";
+ public static final String ACTION_CROSS_PROFILE_TO_PERSONAL =
+ "com.android.cts.verifier.managedprovisioning.CROSS_PROFILE_TO_PERSONAL";
+ public static final String ACTION_CROSS_PROFILE_TO_WORK =
+ "com.android.cts.verifier.managedprovisioning.CROSS_PROFILE_TO_WORK";
+ public static final String EXTRA_STARTED_FROM_WORK
+ = "com.android.cts.verifier.managedprovisioning.STARTED_FROM_WORK";
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -45,9 +50,15 @@
// Check if we are running in the work or personal side, by testing if currently we are the
// profile owner or not.
- textView.setText(isProfileOwner() ? R.string.provisioning_byod_cross_profile_app_work
- : R.string.provisioning_byod_cross_profile_app_personal);
-
+ boolean inWorkProfile = isProfileOwner();
+ boolean startedFromWork = getIntent().getBooleanExtra(EXTRA_STARTED_FROM_WORK, false);
+ if (inWorkProfile && !startedFromWork) {
+ textView.setText(R.string.provisioning_byod_cross_profile_app_work);
+ } else if (!inWorkProfile && startedFromWork) {
+ textView.setText(R.string.provisioning_byod_cross_profile_app_personal);
+ } else { // started from the same side we're currently running in
+ textView.setText(R.string.provisioning_byod_cross_profile_app_ctsverifier);
+ }
findViewById(R.id.button_finish).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
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 008091b..ed6b5eb 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
@@ -64,7 +64,9 @@
filter.addAction(ByodHelperActivity.ACTION_KEYGUARD_DISABLED_FEATURES);
filter.addAction(ByodHelperActivity.ACTION_LOCKNOW);
filter.addAction(ByodHelperActivity.ACTION_TEST_NFC_BEAM);
- filter.addAction(CrossProfileTestActivity.ACTION_CROSS_PROFILE);
+ filter.addAction(ByodHelperActivity.ACTION_TEST_CROSS_PROFILE_INTENTS_DIALOG);
+ filter.addAction(ByodHelperActivity.ACTION_TEST_APP_LINKING_DIALOG);
+ filter.addAction(CrossProfileTestActivity.ACTION_CROSS_PROFILE_TO_WORK);
filter.addAction(WorkNotificationTestActivity.ACTION_WORK_NOTIFICATION);
filter.addAction(WorkNotificationTestActivity.ACTION_WORK_NOTIFICATION_ON_LOCKSCREEN);
filter.addAction(WorkNotificationTestActivity.ACTION_CLEAR_WORK_NOTIFICATION);
@@ -72,12 +74,14 @@
filter.addAction(WorkStatusTestActivity.ACTION_WORK_STATUS_ICON);
filter.addAction(
PermissionLockdownTestActivity.ACTION_MANAGED_PROFILE_CHECK_PERMISSION_LOCKDOWN);
+ filter.addAction(AuthenticationBoundKeyTestActivity.ACTION_AUTH_BOUND_KEY_TEST);
dpm.addCrossProfileIntentFilter(getWho(context), filter,
DevicePolicyManager.FLAG_MANAGED_CAN_ACCESS_PARENT);
// Work -> primary direction
filter = new IntentFilter();
filter.addAction(ByodHelperActivity.ACTION_PROFILE_OWNER_STATUS);
+ filter.addAction(CrossProfileTestActivity.ACTION_CROSS_PROFILE_TO_PERSONAL);
dpm.addCrossProfileIntentFilter(getWho(context), filter,
DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/IntentFiltersTestHelper.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/IntentFiltersTestHelper.java
index 579cbcc..eef6299 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/IntentFiltersTestHelper.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/IntentFiltersTestHelper.java
@@ -18,6 +18,7 @@
import android.app.Activity;
import android.app.admin.DevicePolicyManager;
+import android.app.DownloadManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -29,6 +30,7 @@
import android.net.Uri;
import android.nfc.cardemulation.CardEmulation;
import android.os.Bundle;
+import android.os.Environment;
import android.os.UserHandle;
import android.provider.AlarmClock;
import android.provider.CalendarContract.Events;
@@ -38,6 +40,7 @@
import android.util.Log;
import android.widget.Toast;
+import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
@@ -50,131 +53,83 @@
private static final String TAG = "IntentFiltersTestHelper";
// These are the intents which can be forwarded to the managed profile.
- private static final Intent[] forwardedIntentsFromPrimary = new Intent[] {
- new Intent(Intent.ACTION_SEND).setType("*/*"),
- new Intent(Intent.ACTION_SEND_MULTIPLE).setType("*/*")
- };
+ private static final ArrayList<Intent> forwardedIntentsFromPrimary =
+ new ArrayList<>(Arrays.asList(
+ new Intent(Intent.ACTION_SEND).setType("*/*"),
+ new Intent(Intent.ACTION_SEND_MULTIPLE).setType("*/*")
+ ));
// These are the intents which can be forwarded to the primary profile.
- private static final Intent[] forwardedIntentsFromManaged = new Intent[] {
- new Intent(AlarmClock.ACTION_SET_ALARM),
- new Intent(AlarmClock.ACTION_SET_TIMER),
- new Intent(AlarmClock.ACTION_SHOW_ALARMS),
- new Intent(MediaStore.ACTION_IMAGE_CAPTURE),
- new Intent(MediaStore.ACTION_VIDEO_CAPTURE),
- new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA),
- new Intent(MediaStore.INTENT_ACTION_VIDEO_CAMERA),
- new Intent(MediaStore.ACTION_IMAGE_CAPTURE_SECURE),
- new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE),
- new Intent(Intent.ACTION_SENDTO).setData(Uri.parse("sms:07700900100")),
- new Intent(Intent.ACTION_SENDTO).setData(Uri.parse("smsto:07700900100")),
- new Intent(Intent.ACTION_SENDTO).setData(Uri.parse("mms:07700900100")),
- new Intent(Intent.ACTION_SENDTO).setData(Uri.parse("mmsto:07700900100")),
- new Intent(Intent.ACTION_VIEW).setData(
- Uri.parse("sms:07700900100?body=Hello%20world")).addCategory(
- Intent.CATEGORY_BROWSABLE),
- new Intent(Intent.ACTION_VIEW).setData(
- Uri.parse("smsto:07700900100?body=Hello%20world")).addCategory(
- Intent.CATEGORY_BROWSABLE),
- new Intent(Intent.ACTION_VIEW).setData(
- Uri.parse("mms:07700900100?body=Hello%20world")).addCategory(
- Intent.CATEGORY_BROWSABLE),
- new Intent(Intent.ACTION_VIEW).setData(
- Uri.parse("mmsto:07700900100?body=Hello%20world")).addCategory(
- Intent.CATEGORY_BROWSABLE),
- new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS),
- new Intent(Settings.ACTION_AIRPLANE_MODE_SETTINGS),
- new Intent(Settings.ACTION_APN_SETTINGS),
- new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS),
- new Intent(Settings.ACTION_CAPTIONING_SETTINGS),
- new Intent(Settings.ACTION_DATA_ROAMING_SETTINGS),
- new Intent(Settings.ACTION_DATE_SETTINGS),
- new Intent(Settings.ACTION_DEVICE_INFO_SETTINGS),
- new Intent(Settings.ACTION_DISPLAY_SETTINGS),
- new Intent(Settings.ACTION_DREAM_SETTINGS),
- new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS),
- new Intent(Settings.ACTION_INPUT_METHOD_SUBTYPE_SETTINGS),
- new Intent(Settings.ACTION_LOCALE_SETTINGS),
- new Intent(Settings.ACTION_NETWORK_OPERATOR_SETTINGS),
- new Intent(Settings.ACTION_NFC_SETTINGS),
- new Intent(Settings.ACTION_NFCSHARING_SETTINGS),
- new Intent(Settings.ACTION_PRIVACY_SETTINGS),
- new Intent(Settings.ACTION_SETTINGS),
- new Intent(Settings.ACTION_SOUND_SETTINGS),
- new Intent(Settings.ACTION_WIRELESS_SETTINGS),
- new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD),
- new Intent("android.net.vpn.SETTINGS"),
- new Intent(CardEmulation.ACTION_CHANGE_DEFAULT),
- new Intent("android.settings.ACCOUNT_SYNC_SETTINGS"),
- new Intent(Settings.ACTION_BATTERY_SAVER_SETTINGS),
- new Intent(Settings.ACTION_HOME_SETTINGS),
- new Intent("android.settings.LICENSE"),
- new Intent("android.settings.NOTIFICATION_SETTINGS"),
- new Intent(Settings.ACTION_SHOW_REGULATORY_INFO),
- new Intent("android.settings.USER_SETTINGS"),
- new Intent("android.settings.ZEN_MODE_SETTINGS"),
- new Intent("com.android.settings.ACCESSIBILITY_COLOR_SPACE_SETTINGS"),
- new Intent("com.android.settings.STORAGE_USB_SETTINGS"),
- new Intent("com.android.settings.TTS_SETTINGS"),
- new Intent("com.android.settings.USER_DICTIONARY_EDIT"),
- new Intent(Intent.ACTION_CALL).setData(Uri.parse("tel:123")),
- new Intent("android.intent.action.CALL_EMERGENCY").setData(Uri.parse("tel:123")),
- new Intent("android.intent.action.CALL_PRIVILEGED").setData(Uri.parse("tel:123")),
- new Intent(Intent.ACTION_DIAL).setData(Uri.parse("tel:123")),
- new Intent(Intent.ACTION_VIEW).setData(Uri.parse("tel:123")).addCategory(
- Intent.CATEGORY_BROWSABLE),
- new Intent(Settings.ACTION_MEMORY_CARD_SETTINGS),
- new Intent(Settings.ACTION_NFC_PAYMENT_SETTINGS),
- new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS),
- new Intent(Settings.ACTION_INTERNAL_STORAGE_SETTINGS),
- new Intent(Settings.ACTION_SYNC_SETTINGS),
- new Intent(Settings.ACTION_ADD_ACCOUNT),
- new Intent(Intent.ACTION_GET_CONTENT).setType("*/*").addCategory(
- Intent.CATEGORY_OPENABLE),
- new Intent(Intent.ACTION_OPEN_DOCUMENT).setType("*/*").addCategory(
- Intent.CATEGORY_OPENABLE),
- new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)
- };
+ private static final ArrayList<Intent> forwardedIntentsFromManaged =
+ new ArrayList<>(Arrays.asList(
+ new Intent(AlarmClock.ACTION_SET_ALARM),
+ new Intent(AlarmClock.ACTION_SET_TIMER),
+ new Intent(AlarmClock.ACTION_SHOW_ALARMS),
+ new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS),
+ new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS),
+ new Intent(Settings.ACTION_CAPTIONING_SETTINGS),
+ new Intent(Settings.ACTION_DATE_SETTINGS),
+ new Intent(Settings.ACTION_DEVICE_INFO_SETTINGS),
+ new Intent(Settings.ACTION_DISPLAY_SETTINGS),
+ new Intent(Settings.ACTION_LOCALE_SETTINGS),
+ new Intent(Settings.ACTION_PRIVACY_SETTINGS),
+ new Intent(Settings.ACTION_SETTINGS),
+ new Intent(Settings.ACTION_WIRELESS_SETTINGS),
+ new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD),
+ new Intent("android.net.vpn.SETTINGS"),
+ new Intent("android.settings.ACCOUNT_SYNC_SETTINGS"),
+ new Intent(Settings.ACTION_BATTERY_SAVER_SETTINGS),
+ new Intent("android.settings.LICENSE"),
+ new Intent("android.settings.NOTIFICATION_SETTINGS"),
+ new Intent("android.settings.USER_SETTINGS"),
+ new Intent("android.settings.ZEN_MODE_SETTINGS"),
+ new Intent("com.android.settings.ACCESSIBILITY_COLOR_SPACE_SETTINGS"),
+ new Intent("com.android.settings.STORAGE_USB_SETTINGS"),
+ new Intent("com.android.settings.TTS_SETTINGS"),
+ new Intent("com.android.settings.USER_DICTIONARY_EDIT"),
+ new Intent(Settings.ACTION_INTERNAL_STORAGE_SETTINGS),
+ new Intent(Settings.ACTION_SYNC_SETTINGS),
+ new Intent(Settings.ACTION_ADD_ACCOUNT),
+ new Intent(Intent.ACTION_GET_CONTENT).setType("*/*").addCategory(
+ Intent.CATEGORY_OPENABLE),
+ new Intent(Intent.ACTION_OPEN_DOCUMENT).setType("*/*").addCategory(
+ Intent.CATEGORY_OPENABLE),
+ new Intent(Settings.ACTION_MANAGE_APPLICATIONS_SETTINGS),
+ new Intent(Settings.ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS),
+ new Intent(Settings.ACTION_APPLICATION_SETTINGS)
+ ));
// These are the intents which cannot be forwarded to the primary profile.
- private static final Intent[] notForwardedIntentsFromManaged = new Intent[] {
- new Intent(Intent.ACTION_INSERT).setData(
- Uri.parse("content://browser/bookmarks")),
- new Intent(Intent.ACTION_VIEW).setData(
- Uri.parse("http://www.example.com")).addCategory(
- Intent.CATEGORY_BROWSABLE),
- new Intent(Intent.ACTION_SENDTO).setData(
- Uri.parse("mailto:user@example.com")),
- new Intent(Intent.ACTION_VIEW).setData(
- Uri.parse("mailto:user@example.com")).addCategory(
- Intent.CATEGORY_BROWSABLE),
- new Intent(Intent.ACTION_VIEW).setData(
- Uri.parse("geo:0,0?q=BuckinghamPalace")),
- new Intent(Intent.ACTION_VIEW).setData(
- Uri.parse("http://example.com/oceans.mp4")).setType("video/mp4"),
- new Intent(Intent.ACTION_VIEW).setData(
- Uri.parse("http://www.example.com/horse.mp3")).setType("audio/*"),
- new Intent(MediaStore.INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH),
- new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION),
- new Intent(Intent.ACTION_VIEW).setData(
- Uri.parse("market://details?id=com.android.chrome")).addCategory(
- Intent.CATEGORY_BROWSABLE),
- new Intent(Intent.ACTION_WEB_SEARCH),
- new Intent(Settings.ACTION_SEARCH_SETTINGS),
- new Intent(Settings.ACTION_PRINT_SETTINGS),
- new Intent(Intent.ACTION_MANAGE_NETWORK_USAGE),
- new Intent(Settings.ACTION_MANAGE_APPLICATIONS_SETTINGS),
- new Intent(Settings.ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS),
- new Intent(Settings.ACTION_APPLICATION_SETTINGS),
- new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).setData(
- Uri.parse("package:com.android.chrome")),
- new Intent("android.settings.ACTION_OTHER_SOUND_SETTINGS"),
- new Intent(Settings.ACTION_WIFI_IP_SETTINGS),
- new Intent(Settings.ACTION_WIFI_SETTINGS),
- new Intent("android.settings.SHOW_INPUT_METHOD_PICKER"),
- new Intent(Intent.ACTION_INSERT).setData(Events.CONTENT_URI),
- new Intent(AudioEffect.ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL)
- };
+ private static final ArrayList<Intent> notForwardedIntentsFromManaged =
+ new ArrayList<>(Arrays.asList(
+ new Intent(Intent.ACTION_INSERT).setData(
+ Uri.parse("content://browser/bookmarks")),
+ new Intent(Intent.ACTION_VIEW).setData(
+ Uri.parse("http://www.example.com")).addCategory(
+ Intent.CATEGORY_BROWSABLE),
+ new Intent(Intent.ACTION_SENDTO).setData(
+ Uri.parse("mailto:user@example.com")),
+ new Intent(Intent.ACTION_VIEW).setData(
+ Uri.parse("mailto:user@example.com")).addCategory(
+ Intent.CATEGORY_BROWSABLE),
+ new Intent(Intent.ACTION_VIEW).setData(
+ Uri.parse("geo:0,0?q=BuckinghamPalace")),
+ new Intent(Intent.ACTION_VIEW).setData(
+ Uri.parse("http://example.com/oceans.mp4")).setType("video/mp4"),
+ new Intent(Intent.ACTION_VIEW).setData(
+ Uri.parse("http://www.example.com/horse.mp3")).setType("audio/*"),
+ new Intent(MediaStore.INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH),
+ new Intent(Intent.ACTION_VIEW).setData(
+ Uri.parse("market://details?id=com.android.chrome")).addCategory(
+ Intent.CATEGORY_BROWSABLE),
+ new Intent(Intent.ACTION_WEB_SEARCH),
+ new Intent(Settings.ACTION_SEARCH_SETTINGS),
+ new Intent(Intent.ACTION_MANAGE_NETWORK_USAGE),
+ new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).setData(
+ Uri.parse("package:com.android.chrome")),
+ new Intent(Intent.ACTION_INSERT).setData(Events.CONTENT_URI),
+ new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS)
+ ));
// This flag specifies we are dealing with intents fired from the primary profile.
public static final int FLAG_INTENTS_FROM_PRIMARY = 1;
@@ -185,6 +140,124 @@
IntentFiltersTestHelper(Context context) {
mContext = context;
+
+ addIntentsThatDependOnDeviceFeatures();
+ }
+
+ private void addIntentsThatDependOnDeviceFeatures() {
+ PackageManager pm = mContext.getPackageManager();
+
+ if (pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ forwardedIntentsFromManaged.addAll(Arrays.asList(
+ new Intent(Intent.ACTION_DIAL).setData(Uri.parse("tel:123")),
+ new Intent(Intent.ACTION_CALL).setData(Uri.parse("tel:123")),
+ new Intent("android.intent.action.CALL_EMERGENCY").setData(
+ Uri.parse("tel:123")),
+ new Intent("android.intent.action.CALL_PRIVILEGED").setData(
+ Uri.parse("tel:123")),
+ new Intent(Intent.ACTION_VIEW).setData(Uri.parse("tel:123")).addCategory(
+ Intent.CATEGORY_BROWSABLE),
+ new Intent(Settings.ACTION_NETWORK_OPERATOR_SETTINGS),
+ new Intent(Settings.ACTION_DATA_ROAMING_SETTINGS),
+ new Intent(Intent.ACTION_SENDTO).setData(Uri.parse("sms:07700900100")),
+ new Intent(Intent.ACTION_SENDTO).setData(Uri.parse("smsto:07700900100")),
+ new Intent(Intent.ACTION_SENDTO).setData(Uri.parse("mms:07700900100")),
+ new Intent(Intent.ACTION_SENDTO).setData(Uri.parse("mmsto:07700900100")),
+ new Intent(Intent.ACTION_VIEW).setData(
+ Uri.parse("sms:07700900100?body=Hello%20world")).addCategory(
+ Intent.CATEGORY_BROWSABLE),
+ new Intent(Intent.ACTION_VIEW).setData(
+ Uri.parse("smsto:07700900100?body=Hello%20world")).addCategory(
+ Intent.CATEGORY_BROWSABLE),
+ new Intent(Intent.ACTION_VIEW).setData(
+ Uri.parse("mms:07700900100?body=Hello%20world")).addCategory(
+ Intent.CATEGORY_BROWSABLE),
+ new Intent(Intent.ACTION_VIEW).setData(
+ Uri.parse("mmsto:07700900100?body=Hello%20world")).addCategory(
+ Intent.CATEGORY_BROWSABLE),
+ new Intent(Settings.ACTION_APN_SETTINGS)));
+ }
+
+ if (pm.hasSystemFeature(PackageManager.FEATURE_NFC)) {
+ forwardedIntentsFromManaged.addAll(Arrays.asList(
+ new Intent(Settings.ACTION_NFC_SETTINGS),
+ new Intent(Settings.ACTION_NFCSHARING_SETTINGS),
+ new Intent(Settings.ACTION_NFC_PAYMENT_SETTINGS)));
+ }
+
+ if (pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
+ forwardedIntentsFromManaged.add(
+ new Intent(CardEmulation.ACTION_CHANGE_DEFAULT));
+ }
+
+ if (pm.hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
+ forwardedIntentsFromManaged.addAll(Arrays.asList(
+ new Intent(MediaStore.ACTION_IMAGE_CAPTURE),
+ new Intent(MediaStore.ACTION_VIDEO_CAPTURE),
+ new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA),
+ new Intent(MediaStore.INTENT_ACTION_VIDEO_CAMERA),
+ new Intent(MediaStore.ACTION_IMAGE_CAPTURE_SECURE),
+ new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE)));
+ }
+
+ final String state = Environment.getExternalStorageState();
+ if (Environment.MEDIA_MOUNTED.equals(state)) {
+ forwardedIntentsFromManaged.add(
+ new Intent(Settings.ACTION_MEMORY_CARD_SETTINGS));
+ }
+
+ if (pm.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
+ forwardedIntentsFromManaged.addAll(Arrays.asList(
+ new Intent(Settings.ACTION_WIFI_IP_SETTINGS),
+ new Intent(Settings.ACTION_WIFI_SETTINGS)));
+ }
+
+ if (pm.hasSystemFeature(PackageManager.FEATURE_MICROPHONE)) {
+ forwardedIntentsFromManaged.addAll(Arrays.asList(
+ new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION),
+ new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)));
+ }
+
+ if (pm.hasSystemFeature(PackageManager.FEATURE_LOCATION)) {
+ forwardedIntentsFromManaged.add(
+ new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS));
+ }
+
+ if (pm.hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)) {
+ forwardedIntentsFromManaged.addAll(Arrays.asList(
+ new Intent(Settings.ACTION_SOUND_SETTINGS),
+ new Intent("android.settings.ACTION_OTHER_SOUND_SETTINGS")));
+ notForwardedIntentsFromManaged.add(
+ new Intent(AudioEffect.ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL));
+ }
+
+ if (pm.hasSystemFeature(PackageManager.FEATURE_HOME_SCREEN)) {
+ forwardedIntentsFromManaged.add(
+ new Intent(Settings.ACTION_HOME_SETTINGS));
+ }
+
+ if (pm.hasSystemFeature(PackageManager.FEATURE_INPUT_METHODS)) {
+ forwardedIntentsFromManaged.addAll(Arrays.asList(
+ new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS),
+ new Intent(Settings.ACTION_INPUT_METHOD_SUBTYPE_SETTINGS)));
+ notForwardedIntentsFromManaged.add(
+ new Intent("android.settings.SHOW_INPUT_METHOD_PICKER"));
+ }
+
+ if (!pm.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+ forwardedIntentsFromManaged.add(
+ new Intent(Settings.ACTION_DREAM_SETTINGS));
+ }
+
+ if (!pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
+ forwardedIntentsFromManaged.add(
+ new Intent(Settings.ACTION_AIRPLANE_MODE_SETTINGS));
+ }
+
+ if (pm.hasSystemFeature(PackageManager.FEATURE_PRINTING)) {
+ notForwardedIntentsFromManaged.add(
+ new Intent(Settings.ACTION_PRINT_SETTINGS));
+ }
}
public boolean checkCrossProfileIntentFilters(int flag) {
@@ -296,7 +369,7 @@
* @return {@code null} if all the intents are correctly handled
* otherwise, the first intent in the list which is not handled correctly.
*/
- private Intent checkForIntentsNotHandled(Intent[] intentList,
+ private Intent checkForIntentsNotHandled(ArrayList<Intent> intentList,
ActivityInfo expectedForwarderActivityInfo, boolean canResolve) {
for (Intent intent : intentList) {
if (canForwarderActivityHandleIntent(intent,
@@ -306,4 +379,4 @@
}
return null;
}
-}
\ No newline at end of file
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/KeyguardDisabledFeaturesActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/KeyguardDisabledFeaturesActivity.java
index 0fdc498..1a158f8 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/KeyguardDisabledFeaturesActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/KeyguardDisabledFeaturesActivity.java
@@ -18,6 +18,7 @@
import android.app.admin.DevicePolicyManager;
import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.hardware.fingerprint.FingerprintManager;
@@ -25,6 +26,7 @@
import android.provider.Settings;
import android.view.View;
import android.view.View.OnClickListener;
+import android.widget.Toast;
import com.android.cts.verifier.ArrayTestListAdapter;
import com.android.cts.verifier.DialogTestListActivity;
@@ -32,6 +34,8 @@
public class KeyguardDisabledFeaturesActivity extends DialogTestListActivity {
+ private DevicePolicyManager mDpm;
+
public KeyguardDisabledFeaturesActivity() {
super(R.layout.provisioning_byod,
R.string.provisioning_byod_keyguard_disabled_features,
@@ -43,12 +47,20 @@
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ mDpm = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
+
mPrepareTestButton.setText(
R.string.provisioning_byod_keyguard_disabled_features_prepare_button);
mPrepareTestButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
- resetPassword("testpassword");
+ if (!mDpm.isAdminActive(DeviceAdminTestReceiver.getReceiverComponentName())) {
+ Toast.makeText(KeyguardDisabledFeaturesActivity.this,
+ R.string.provisioning_byod_keyguard_disabled_features_not_admin,
+ Toast.LENGTH_SHORT).show();
+ return;
+ }
+ mDpm.resetPassword("testpassword", 0);
setKeyguardDisabledFeatures(DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS |
DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT |
DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
@@ -60,7 +72,11 @@
public void finish() {
// Pass and fail buttons are known to call finish() when clicked, and this is when we want to
// clear the password.
- resetPassword(null);
+ final ComponentName adminComponent = DeviceAdminTestReceiver.getReceiverComponentName();
+ if (mDpm.isAdminActive(adminComponent)) {
+ mDpm.resetPassword(null, 0);
+ mDpm.removeActiveAdmin(adminComponent);
+ }
super.finish();
}
@@ -71,16 +87,6 @@
startActivity(setKeyguardDisabledFeaturesIntent);
}
- /**
- * Reset device password
- * @param password password to reset to (may be null)
- */
- private void resetPassword(String password) {
- DevicePolicyManager dpm = (DevicePolicyManager)
- getSystemService(Context.DEVICE_POLICY_SERVICE);
- dpm.resetPassword(password, 0);
- }
-
@Override
protected void setupTests(ArrayTestListAdapter adapter) {
adapter.add(new DialogTestListItem(this, R.string.provisioning_byod_disable_trust_agents,
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/screenpinning/ScreenPinningTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/screenpinning/ScreenPinningTestActivity.java
index 8e72ebb..0728fb5 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/screenpinning/ScreenPinningTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/screenpinning/ScreenPinningTestActivity.java
@@ -31,6 +31,8 @@
private static final String TAG = "ScreenPinningTestActivity";
private static final String KEY_CURRENT_TEST = "keyCurrentTest";
+ private static final long TASK_MODE_CHECK_DELAY = 200;
+ private static final int MAX_TASK_MODE_CHECK_COUNT = 5;
private Test[] mTests;
private int mTestIndex;
@@ -203,10 +205,18 @@
return;
}
stopLockTask();
- if (!mActivityManager.isInLockTaskMode()) {
- succeed();
- } else {
- error(R.string.error_screen_pinning_couldnt_exit);
+ for (int retry = MAX_TASK_MODE_CHECK_COUNT; retry > 0; retry--) {
+ try {
+ Thread.sleep(TASK_MODE_CHECK_DELAY);
+ } catch (InterruptedException e) {
+ }
+ Log.d(TAG, "Check unpin ... " + retry);
+ if (!mActivityManager.isInLockTaskMode()) {
+ succeed();
+ break;
+ } else if (retry == 1) {
+ error(R.string.error_screen_pinning_couldnt_exit);
+ }
}
};
};
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/security/FingerprintBoundKeysTest.java b/apps/CtsVerifier/src/com/android/cts/verifier/security/FingerprintBoundKeysTest.java
index 70899c6..bca7a66 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/security/FingerprintBoundKeysTest.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/security/FingerprintBoundKeysTest.java
@@ -89,6 +89,22 @@
if (requestCode == FINGERPRINT_PERMISSION_REQUEST_CODE && state[0] == PackageManager.PERMISSION_GRANTED) {
mFingerprintManager = (FingerprintManager) getSystemService(Context.FINGERPRINT_SERVICE);
mKeyguardManager = (KeyguardManager) getSystemService(KeyguardManager.class);
+ Button startTestButton = (Button) findViewById(R.id.sec_start_test_button);
+
+ if (!mKeyguardManager.isKeyguardSecure()) {
+ // Show a message that the user hasn't set up a lock screen.
+ showToast( "Secure lock screen hasn't been set up.\n"
+ + "Go to 'Settings -> Security -> Screen lock' to set up a lock screen");
+ startTestButton.setEnabled(false);
+ return;
+ } else if (!mFingerprintManager.hasEnrolledFingerprints()) {
+ showToast("No fingerprints enrolled.\n"
+ + "Go to 'Settings -> Security -> Fingerprint' to set up a fingerprint");
+ startTestButton.setEnabled(false);
+ return;
+ }
+
+ createKey();
try {
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
@@ -107,7 +123,6 @@
throw new RuntimeException("Failed to init Cipher", e);
}
- Button startTestButton = (Button) findViewById(R.id.sec_start_test_button);
startTestButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
@@ -117,21 +132,7 @@
showAuthenticationScreen();
}
}
-
});
-
- if (!mKeyguardManager.isKeyguardSecure()) {
- // Show a message that the user hasn't set up a lock screen.
- showToast( "Secure lock screen hasn't been set up.\n"
- + "Go to 'Settings -> Security -> Screen lock' to set up a lock screen");
- startTestButton.setEnabled(false);
- } else if (!mFingerprintManager.hasEnrolledFingerprints()) {
- showToast("No fingerprints enrolled.\n"
- + "Go to 'Settings -> Security -> Fingerprint' to set up a fingerprint");
- startTestButton.setEnabled(false);
- } else {
- createKey();
- }
}
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVCameraPreview.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVCameraPreview.java
index a5b58f6..fa89b71 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVCameraPreview.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVCameraPreview.java
@@ -23,9 +23,9 @@
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
+import android.view.ViewGroup;
import java.io.IOException;
-import java.util.List;
/** Camera preview class */
public class RVCVCameraPreview extends SurfaceView implements SurfaceHolder.Callback {
@@ -34,15 +34,16 @@
private SurfaceHolder mHolder;
private Camera mCamera;
+ private float mAspect;
+ private int mRotation;
/**
* Constructor
* @param context Activity context
- * @param camera Camera object to be previewed
*/
- public RVCVCameraPreview(Context context, Camera camera) {
+ public RVCVCameraPreview(Context context) {
super(context);
- mCamera = camera;
+ mCamera = null;
initSurface();
}
@@ -55,8 +56,10 @@
super(context, attrs);
}
- public void init(Camera camera) {
+ public void init(Camera camera, float aspectRatio, int rotation) {
this.mCamera = camera;
+ mAspect = aspectRatio;
+ mRotation = rotation;
initSurface();
}
@@ -86,6 +89,20 @@
try {
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
+ int v_height = getHeight();
+ int v_width = getWidth();
+ ViewGroup.LayoutParams layout = getLayoutParams();
+ if ( (float)v_height/v_width >
+ mAspect) {
+ layout.height = (int)(v_width * mAspect);
+ layout.width = v_width;
+ }else {
+ layout.width = (int)(v_height / mAspect);
+ layout.height = v_height;
+ }
+ Log.d(TAG, String.format("Layout (%d, %d) -> (%d, %d)", v_width, v_height,
+ layout.width, layout.height));
+ setLayoutParams(layout);
} catch (IOException e) {
if (LOCAL_LOGD) Log.d(TAG, "Error when starting camera preview: " + e.getMessage());
}
@@ -111,8 +128,7 @@
// stop preview before making changes
mCamera.stopPreview();
- // the activity using this view is locked to this orientation, so hard code is fine
- mCamera.setDisplayOrientation(90);
+ mCamera.setDisplayOrientation(mRotation);
//do the same as if it is created again
surfaceCreated(holder);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVRecordActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVRecordActivity.java
index f90b27c..be5ec52 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVRecordActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVRecordActivity.java
@@ -46,6 +46,7 @@
import java.io.OutputStreamWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
+import java.util.List;
// ----------------------------------------------------------------------
@@ -67,7 +68,7 @@
private VideoRecorder mVideoRecorder;
private RVSensorLogger mRVSensorLogger;
private CoverageManager mCoverManager;
- private CameraPreviewer mPreviewer;
+ private CameraContext mCameraContext;
public static final int AXIS_NONE = 0;
public static final int AXIS_ALL = SensorManager.AXIS_X +
@@ -99,7 +100,7 @@
super.onPause();
mController.quit();
- mPreviewer.end();
+ mCameraContext.end();
endSoundPool();
}
@@ -128,8 +129,8 @@
*
*/
private void init() {
- mPreviewer = new CameraPreviewer();
- mPreviewer.init();
+ mCameraContext = new CameraContext();
+ mCameraContext.init();
mCoverManager = new CoverageManager();
mIndicatorView.setDataProvider(
@@ -140,7 +141,7 @@
initSoundPool();
mRVSensorLogger = new RVSensorLogger(this);
- mVideoRecorder = new VideoRecorder(mPreviewer.getCamera());
+ mVideoRecorder = new VideoRecorder(mCameraContext.getCamera(), mCameraContext.getProfile());
if (LOG_RAW_SENSORS) {
mRawSensorLogger = new RawSensorLogger(mRecordDir);
@@ -173,7 +174,8 @@
// X and Y
final String axisName = "YXZ";
- message("Manipulate the device in " + axisName.charAt(axis-1) + " axis (as illustrated) about the pattern.");
+ message("Manipulate the device in " + axisName.charAt(axis - 1) +
+ " axis (as illustrated) about the pattern.");
}
/**
@@ -250,20 +252,28 @@
* Start the sensor recording
*/
public void startRecordSensor() {
- mRVSensorLogger.init();
- if (LOG_RAW_SENSORS) {
- mRawSensorLogger.init();
- }
+ runOnUiThread(new Runnable() {
+ public void run() {
+ mRVSensorLogger.init();
+ if (LOG_RAW_SENSORS) {
+ mRawSensorLogger.init();
+ }
+ }
+ });
}
/**
* Stop the sensor recording
*/
public void stopRecordSensor() {
- mRVSensorLogger.end();
- if (LOG_RAW_SENSORS) {
- mRawSensorLogger.end();
- }
+ runOnUiThread(new Runnable() {
+ public void run() {
+ mRVSensorLogger.end();
+ if (LOG_RAW_SENSORS) {
+ mRawSensorLogger.end();
+ }
+ }
+ });
}
/**
@@ -365,20 +375,93 @@
/**
* Camera preview control class
*/
- class CameraPreviewer {
+ class CameraContext {
private Camera mCamera;
+ private CamcorderProfile mProfile;
+ private Camera.CameraInfo mCameraInfo;
- CameraPreviewer() {
+ private int [] mPreferredProfiles = {
+ CamcorderProfile.QUALITY_480P, // smaller -> faster
+ CamcorderProfile.QUALITY_720P,
+ CamcorderProfile.QUALITY_1080P,
+ CamcorderProfile.QUALITY_HIGH // existence guaranteed
+ };
+
+ CameraContext() {
try {
- mCamera = Camera.open(); // attempt to get a default Camera instance
+ mCamera = Camera.open(); // attempt to get a default Camera instance (0)
+ mProfile = null;
+ if (mCamera != null) {
+ mCameraInfo = new Camera.CameraInfo();
+ Camera.getCameraInfo(0, mCameraInfo);
+ setupCamera();
+ }
}
- catch (Exception e) {
+ catch (Exception e){
// Camera is not available (in use or does not exist)
Log.e(TAG, "Cannot obtain Camera!");
}
}
/**
+ * Find a preferred camera profile and set preview and picture size property accordingly.
+ */
+ void setupCamera() {
+ CamcorderProfile profile = null;
+ Camera.Parameters param = mCamera.getParameters();
+ List<Camera.Size> pre_sz = param.getSupportedPreviewSizes();
+ List<Camera.Size> pic_sz = param.getSupportedPictureSizes();
+
+ for (int i : mPreferredProfiles) {
+ if (CamcorderProfile.hasProfile(i)) {
+ profile = CamcorderProfile.get(i);
+
+ int valid = 0;
+ for (Camera.Size j : pre_sz) {
+ if (j.width == profile.videoFrameWidth &&
+ j.height == profile.videoFrameHeight) {
+ ++valid;
+ break;
+ }
+ }
+ for (Camera.Size j : pic_sz) {
+ if (j.width == profile.videoFrameWidth &&
+ j.height == profile.videoFrameHeight) {
+ ++valid;
+ break;
+ }
+ }
+ if (valid == 2) {
+ param.setPreviewSize(profile.videoFrameWidth, profile.videoFrameHeight);
+ param.setPictureSize(profile.videoFrameWidth, profile.videoFrameHeight);
+ mCamera.setParameters(param);
+ break;
+ } else {
+ profile = null;
+ }
+ }
+ }
+ if (profile != null) {
+ float fovW = param.getHorizontalViewAngle();
+ float fovH = param.getVerticalViewAngle();
+ writeVideoMetaInfo(profile.videoFrameWidth, profile.videoFrameHeight,
+ profile.videoFrameRate, fovW, fovH);
+ } else {
+ Log.e(TAG, "Cannot find a proper video profile");
+ }
+ mProfile = profile;
+
+ }
+
+
+ /**
+ * Get sensor information of the camera being used
+ */
+ public Camera.CameraInfo getCameraInfo() {
+ return mCameraInfo;
+ }
+
+ /**
* Get the camera to be previewed
* @return Reference to Camera used
*/
@@ -387,12 +470,20 @@
}
/**
+ * Get the camera profile to be used
+ * @return Reference to Camera profile
+ */
+ public CamcorderProfile getProfile() {
+ return mProfile;
+ }
+
+ /**
* Setup the camera
*/
public void init() {
if (mCamera != null) {
double alpha = mCamera.getParameters().getHorizontalViewAngle()*Math.PI/180.0;
- int width = 1920;
+ int width = mProfile.videoFrameWidth;
double fx = width/2/Math.tan(alpha/2.0);
if (LOCAL_LOGV) Log.v(TAG, "View angle="
@@ -400,7 +491,9 @@
RVCVCameraPreview cameraPreview =
(RVCVCameraPreview) findViewById(R.id.cam_preview);
- cameraPreview.init(mCamera);
+ cameraPreview.init(mCamera,
+ (float)mProfile.videoFrameWidth/mProfile.videoFrameHeight,
+ mCameraInfo.orientation);
} else {
message("Cannot open camera!");
finish();
@@ -466,26 +559,22 @@
class VideoRecorder
{
private MediaRecorder mRecorder;
+ private CamcorderProfile mProfile;
private Camera mCamera;
private boolean mRunning = false;
- private int [] mPreferredProfiles = { CamcorderProfile.QUALITY_480P, // smaller -> faster
- CamcorderProfile.QUALITY_720P,
- CamcorderProfile.QUALITY_1080P,
- CamcorderProfile.QUALITY_HIGH // existence guaranteed
- };
-
-
- VideoRecorder(Camera camera) {
+ VideoRecorder(Camera camera, CamcorderProfile profile){
mCamera = camera;
+ mProfile = profile;
}
/**
* Initialize and start recording
*/
public void init() {
- float fovW = mCamera.getParameters().getHorizontalViewAngle();
- float fovH = mCamera.getParameters().getVerticalViewAngle();
+ if (mCamera == null || mProfile ==null){
+ return;
+ }
mRecorder = new MediaRecorder();
mCamera.unlock();
@@ -494,17 +583,7 @@
mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
mRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
- CamcorderProfile profile = null;
- for (int i: mPreferredProfiles) {
- if (CamcorderProfile.hasProfile(i)) {
- profile = CamcorderProfile.get(i);
- mRecorder.setProfile(profile);
- break;
- }
- }
-
- writeVideoMetaInfo(profile.videoFrameWidth, profile.videoFrameHeight,
- profile.videoFrameRate, fovW, fovH);
+ mRecorder.setProfile(mProfile);
try {
mRecorder.setOutputFile(getVideoRecFilePath());
@@ -689,8 +768,20 @@
*/
public void init() {
mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
+ if (mSensorManager == null) {
+ Log.e(TAG,"SensorManager is null!");
+ }
mRVSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);
- mSensorManager.registerListener(this, mRVSensor, SENSOR_RATE);
+ if (mRVSensor != null) {
+ if (LOCAL_LOGV) Log.v(TAG, "Got RV Sensor");
+ }else {
+ Log.e(TAG, "Did not get RV sensor");
+ }
+ if(mSensorManager.registerListener(this, mRVSensor, SENSOR_RATE)) {
+ if (LOCAL_LOGV) Log.v(TAG,"Register listener successfull");
+ } else {
+ Log.e(TAG,"Register listener failed");
+ }
try {
mLogWriter= new OutputStreamWriter(
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckAnalyzer.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckAnalyzer.java
index 9afd1a9..3dc7270 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckAnalyzer.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckAnalyzer.java
@@ -15,12 +15,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
import android.media.MediaCodec;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.os.Debug;
import android.os.Environment;
+import android.os.PowerManager;
import android.util.JsonWriter;
import android.util.Log;
@@ -777,14 +777,16 @@
VideoMetaInfo meta = new VideoMetaInfo(new File(mPath, "videometa.json"));
int decimation = 1;
+ boolean use_timestamp = true;
+ // roughly determine if decimation is necessary
if (meta.fps > DECIMATION_FPS_TARGET) {
decimation = (int)(meta.fps / DECIMATION_FPS_TARGET);
meta.fps /=decimation;
}
VideoDecoderForOpenCV videoDecoder = new VideoDecoderForOpenCV(
- new File(mPath, "video.mp4"), decimation); // every 3 frame process 1 frame
+ new File(mPath, "video.mp4"), decimation);
Mat frame;
@@ -820,12 +822,17 @@
}
long startTime = System.nanoTime();
+ long [] ts = new long[1];
- while ((frame = videoDecoder.getFrame()) !=null) {
+ while ((frame = videoDecoder.getFrame(ts)) !=null) {
if (LOCAL_LOGV) {
Log.v(TAG, "got a frame " + i);
}
+ if (use_timestamp && ts[0] == -1) {
+ use_timestamp = false;
+ }
+
// has to be in front, as there are cases where execution
// will skip the later part of this while
i++;
@@ -873,8 +880,16 @@
// if error is reasonable, add it into the results
if (error < REPROJECTION_THREASHOLD) {
double [] rv = new double[3];
+ double timestamp;
+
rvec.get(0,0, rv);
- recs.add(new AttitudeRec((double) i / meta.fps, rodr2rpy(rv)));
+ if (use_timestamp) {
+ timestamp = (double)ts[0] / 1e6;
+ } else {
+ timestamp = (double) i / meta.fps;
+ }
+ if (LOCAL_LOGV) Log.v(TAG, String.format("Added frame %d ts = %f", i, timestamp));
+ recs.add(new AttitudeRec(timestamp, rodr2rpy(rv)));
}
if (OUTPUT_DEBUG_IMAGE) {
@@ -906,6 +921,8 @@
* One issue right now is that the glReadPixels is quite slow .. around 6.5ms for a 720p frame
*/
private class VideoDecoderForOpenCV implements Runnable {
+ static final String TAG = "VideoDecoderForOpenCV";
+
private MediaExtractor extractor=null;
private MediaCodec decoder=null;
private CtsMediaOutputSurface surface=null;
@@ -1031,7 +1048,7 @@
}
if (decoder == null) {
- Log.e("VideoDecoderForOpenCV", "Can't find video info!");
+ Log.e(TAG, "Can't find video info!");
return;
}
valid = true;
@@ -1060,6 +1077,7 @@
long timeoutUs = 10000;
int iframe = 0;
+ long frameTimestamp = 0;
while (!Thread.interrupted()) {
if (!isEOS) {
@@ -1076,8 +1094,12 @@
MediaCodec.BUFFER_FLAG_END_OF_STREAM);
isEOS = true;
} else {
- decoder.queueInputBuffer(inIndex, 0, sampleSize,
- extractor.getSampleTime(), 0);
+ frameTimestamp = extractor.getSampleTime();
+ decoder.queueInputBuffer(inIndex, 0, sampleSize, frameTimestamp, 0);
+ if (LOCAL_LOGD) {
+ Log.d(TAG, String.format("Frame %d sample time %f s",
+ iframe, (double)frameTimestamp/1e6));
+ }
extractor.advance();
}
}
@@ -1088,19 +1110,19 @@
switch (outIndex) {
case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
if (LOCAL_LOGD) {
- Log.d("VideoDecoderForOpenCV", "INFO_OUTPUT_BUFFERS_CHANGED");
+ Log.d(TAG, "INFO_OUTPUT_BUFFERS_CHANGED");
}
outputBuffers = decoder.getOutputBuffers();
break;
case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
outFormat = decoder.getOutputFormat();
if (LOCAL_LOGD) {
- Log.d("VideoDecoderForOpenCV", "New format " + outFormat);
+ Log.d(TAG, "New format " + outFormat);
}
break;
case MediaCodec.INFO_TRY_AGAIN_LATER:
if (LOCAL_LOGD) {
- Log.d("VideoDecoderForOpenCV", "dequeueOutputBuffer timed out!");
+ Log.d(TAG, "dequeueOutputBuffer timed out!");
}
break;
default:
@@ -1118,12 +1140,12 @@
if (doRender) {
surface.awaitNewImage();
surface.drawImage();
- if (LOCAL_LOGD) {
- Log.d("VideoDecoderForOpenCV", "Finish drawing a frame!");
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "Finish drawing a frame!");
}
if ((iframe++ % mDecimation) == 0) {
//Send the frame for processing
- mMatBuffer.put();
+ mMatBuffer.put(frameTimestamp);
}
}
break;
@@ -1149,8 +1171,8 @@
* Get next valid frame
* @return Frame in OpenCV mat
*/
- public Mat getFrame() {
- return mMatBuffer.get();
+ public Mat getFrame(long ts[]) {
+ return mMatBuffer.get(ts);
}
/**
@@ -1168,6 +1190,7 @@
private Mat mat;
private byte[] bytes;
private ByteBuffer buf;
+ private long timestamp;
private boolean full;
private int mWidth, mHeight;
@@ -1180,6 +1203,7 @@
mat = new Mat(height, width, CvType.CV_8UC4); //RGBA
buf = ByteBuffer.allocateDirect(width*height*4);
bytes = new byte[width*height*4];
+ timestamp = -1;
mValid = true;
full = false;
@@ -1190,7 +1214,7 @@
notifyAll();
}
- public synchronized Mat get() {
+ public synchronized Mat get(long ts[]) {
if (!mValid) return null;
while (full == false) {
@@ -1204,9 +1228,11 @@
mat.put(0,0, bytes);
full = false;
notifyAll();
+ ts[0] = timestamp;
return mat;
}
- public synchronized void put() {
+
+ public synchronized void put(long ts) {
while (full) {
try {
wait();
@@ -1219,6 +1245,7 @@
buf.get(bytes);
buf.rewind();
+ timestamp = ts;
full = true;
notifyAll();
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckTestActivity.java
index 9a74a0e..35c4d56 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckTestActivity.java
@@ -16,9 +16,10 @@
package com.android.cts.verifier.sensors;
-
+import android.content.Context;
import android.hardware.cts.helpers.SensorTestStateNotSupportedException;
import android.os.Bundle;
+import android.os.PowerManager;
import com.android.cts.verifier.sensors.base.SensorCtsVerifierTestActivity;
import com.android.cts.verifier.sensors.helpers.OpenCVLibrary;
@@ -82,7 +83,7 @@
while(retry-->0) {
try {
- Thread.sleep(100);
+ Thread.sleep(250);
} catch (InterruptedException e) {
//
}
@@ -146,7 +147,15 @@
// Analysis of recorded video and sensor data using RVCXAnalyzer
RVCVXCheckAnalyzer analyzer = new RVCVXCheckAnalyzer(mRecPath);
+
+ // acquire a partial wake lock just in case CPU fall asleep
+ PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
+ PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+ "RVCVXCheckAnalyzer");
+
+ wl.acquire();
mReport = analyzer.processDataSet();
+ wl.release();
playSound();
vibrate(500);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SignificantMotionTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SignificantMotionTestActivity.java
index f8f1a9a..2dfc7c8 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SignificantMotionTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SignificantMotionTestActivity.java
@@ -287,7 +287,9 @@
@Override
protected void activityCleanUp() {
- mScreenManipulator.turnScreenOff();
+ if (mScreenManipulator != null) {
+ mScreenManipulator.turnScreenOff();
+ }
LocalBroadcastManager.getInstance(this).unregisterReceiver(myBroadCastReceiver);
}
@@ -302,8 +304,9 @@
* It cannot be reused.
*/
private class TriggerVerifier extends TriggerEventListener {
- private volatile CountDownLatch mCountDownLatch = new CountDownLatch(1);
+ private volatile CountDownLatch mCountDownLatch;
private volatile TriggerEventRegistry mEventRegistry;
+ private volatile long mTimestampForTriggeredEvent = 0;
// TODO: refactor out if needed
private class TriggerEventRegistry {
@@ -329,10 +332,7 @@
}
public long getTimeStampForTriggerEvent() {
- if (mEventRegistry != null && mEventRegistry.triggerEvent != null) {
- return mEventRegistry.triggerEvent.timestamp;
- }
- return 0;
+ return mTimestampForTriggeredEvent;
}
public String verifyEventTriggered() throws Throwable {
@@ -388,9 +388,16 @@
}
private TriggerEventRegistry awaitForEvent() throws InterruptedException {
+ mCountDownLatch = new CountDownLatch(1);
mCountDownLatch.await(TRIGGER_MAX_DELAY_SECONDS, TimeUnit.SECONDS);
TriggerEventRegistry registry = mEventRegistry;
+ // Save the last timestamp when the event triggered.
+ if (mEventRegistry != null && mEventRegistry.triggerEvent != null) {
+ mTimestampForTriggeredEvent = mEventRegistry.triggerEvent.timestamp;
+ }
+
+ mEventRegistry = null;
playSound();
return registry != null ? registry : new TriggerEventRegistry(null, 0);
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/AppLinkTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/AppLinkTestActivity.java
new file mode 100644
index 0000000..43f293a
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/AppLinkTestActivity.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.tv;
+
+import android.content.Intent;
+import android.database.Cursor;
+import android.graphics.drawable.Drawable;
+import android.media.tv.TvContract;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.android.cts.verifier.R;
+
+/**
+ * Tests for verifying TV app behavior for TV app-link.
+ */
+public class AppLinkTestActivity extends TvAppVerifierActivity implements View.OnClickListener {
+ private static final long TIMEOUT_MS = 5l * 60l * 1000l; // 5 mins.
+
+ private boolean mSelectAppLinkItemPassed;
+ private View mSelectAppLinkItem;
+ private View mVerifyAppLinkIntentItem;
+ private View mVerifyAppLinkCardItem;
+
+ Runnable mSelectAppLinkFailCallback;
+
+ @Override
+ public void onClick(View v) {
+ final View postTarget = getPostTarget();
+
+ if (containsButton(mSelectAppLinkItem, v)) {
+ Intent tvAppIntent = null;
+ String[] projection = { TvContract.Channels._ID };
+ try (Cursor cursor = getContentResolver().query(
+ TvContract.buildChannelsUriForInput(MockTvInputService.getInputId(this)),
+ projection, null, null, null)) {
+ if (cursor != null && cursor.moveToNext()) {
+ tvAppIntent = new Intent(Intent.ACTION_VIEW,
+ TvContract.buildChannelUri(cursor.getLong(0)));
+ }
+ }
+ if (tvAppIntent == null) {
+ Toast.makeText(this, R.string.tv_channel_not_found, Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ mSelectAppLinkFailCallback = new Runnable() {
+ @Override
+ public void run() {
+ mSelectAppLinkItemPassed = false;
+ setPassState(mSelectAppLinkItem, false);
+ setPassState(mVerifyAppLinkIntentItem, false);
+ }
+ };
+ postTarget.postDelayed(mSelectAppLinkFailCallback, TIMEOUT_MS);
+ mSelectAppLinkItemPassed = true;
+ setPassState(mSelectAppLinkItem, true);
+
+ startActivity(tvAppIntent);
+ } else if (containsButton(mVerifyAppLinkCardItem, v)) {
+ setPassState(mVerifyAppLinkCardItem, true);
+ getPassButton().setEnabled(true);
+ }
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ if (mSelectAppLinkItemPassed
+ && TextUtils.equals(MockTvInputSetupActivity.APP_LINK_TEST_VALUE,
+ intent.getStringExtra(MockTvInputSetupActivity.APP_LINK_TEST_KEY))) {
+ getPostTarget().removeCallbacks(mSelectAppLinkFailCallback);
+ setPassState(mVerifyAppLinkIntentItem, true);
+ setButtonEnabled(mVerifyAppLinkCardItem, true);
+ }
+ }
+
+ @Override
+ protected void createTestItems() {
+ mSelectAppLinkItem = createUserItem(R.string.tv_app_link_test_select_app_link,
+ R.string.tv_launch_tv_app, this);
+ setButtonEnabled(mSelectAppLinkItem, true);
+ mVerifyAppLinkIntentItem = createAutoItem(
+ R.string.tv_app_link_test_verify_link_clicked);
+ mVerifyAppLinkCardItem = createUserItem(R.string.tv_input_link_test_verify_link_interface,
+ android.R.string.yes, this);
+ TextView instructions = (TextView) mVerifyAppLinkCardItem.findViewById(R.id.instructions);
+ Drawable image = getDrawable(R.drawable.app_link_img);
+ image.setBounds(0, 0, 317, 241);
+ instructions.setCompoundDrawablePadding(10);
+ instructions.setCompoundDrawables(image, null, null, null);
+ }
+
+ @Override
+ protected void setInfoResources() {
+ setInfoResources(R.string.tv_app_link_test, R.string.tv_app_link_test_info, -1);
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputSetupActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputSetupActivity.java
index c05b753..43ed7da 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputSetupActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputSetupActivity.java
@@ -17,9 +17,12 @@
package com.android.cts.verifier.tv;
import android.app.Activity;
+import android.content.ComponentName;
import android.content.ContentUris;
import android.content.ContentValues;
+import android.content.Intent;
import android.database.Cursor;
+import android.graphics.Color;
import android.media.tv.TvContract;
import android.media.tv.TvContract.Programs;
import android.media.tv.TvInputInfo;
@@ -28,6 +31,8 @@
import android.util.Pair;
import android.view.View;
+import com.android.cts.verifier.R;
+
import java.util.ArrayList;
public class MockTvInputSetupActivity extends Activity {
@@ -38,6 +43,10 @@
/* package-private */ static final String PROGRAM_TITLE = "Dummy Program";
/* package-private */ static final String PROGRAM_DESCRIPTION = "Dummy Program Description";
+
+ /* package-private */ static final String APP_LINK_TEST_KEY = "app_link_test_key";
+ /* package-private */ static final String APP_LINK_TEST_VALUE = "app_link_test_value";
+ private static final String APP_LINK_TEXT = "Cts App-Link Text";
private static final long PROGRAM_LENGTH_MILLIS = 60 * 60 * 1000;
private static final int PROGRAM_COUNT = 24;
@@ -69,6 +78,18 @@
values.put(TvContract.Channels.COLUMN_INPUT_ID, inputId);
values.put(TvContract.Channels.COLUMN_DISPLAY_NUMBER, CHANNEL_NUMBER);
values.put(TvContract.Channels.COLUMN_DISPLAY_NAME, CHANNEL_NAME);
+ values.put(TvContract.Channels.COLUMN_APP_LINK_TEXT, APP_LINK_TEXT);
+ values.put(TvContract.Channels.COLUMN_APP_LINK_COLOR, Color.RED);
+ values.put(TvContract.Channels.COLUMN_APP_LINK_ICON_URI,
+ "android.resource://" + getPackageName() + "/" + R.drawable.icon);
+ values.put(TvContract.Channels.COLUMN_APP_LINK_POSTER_ART_URI,
+ "android.resource://" + getPackageName() + "/" + R.raw.sns_texture);
+ Intent appLinkIntentUri = new Intent(this, AppLinkTestActivity.class);
+ appLinkIntentUri.putExtra(APP_LINK_TEST_KEY, APP_LINK_TEST_VALUE);
+ appLinkIntentUri.setFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
+ values.put(TvContract.Channels.COLUMN_APP_LINK_INTENT_URI,
+ appLinkIntentUri.toUri(Intent.URI_INTENT_SCHEME));
+
Uri channelUri = getContentResolver().insert(uri, values);
// If the channel's ID happens to be zero, we add another and delete the one.
if (ContentUris.parseId(channelUri) == 0) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvAppVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvAppVerifierActivity.java
index 12e9652..acab18e 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvAppVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvAppVerifierActivity.java
@@ -35,8 +35,6 @@
public abstract class TvAppVerifierActivity extends PassFailButtons.Activity {
private static final String TAG = "TvAppVerifierActivity";
- private static final long TIMEOUT_MS = 5l * 60l * 1000l; // 5 mins.
-
private LayoutInflater mInflater;
private ViewGroup mItemList;
private View mPostTarget;
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvInputDiscoveryTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvInputDiscoveryTestActivity.java
index 06f4f6f..e8e2cee 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvInputDiscoveryTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvInputDiscoveryTestActivity.java
@@ -25,8 +25,6 @@
import com.android.cts.verifier.R;
-import java.util.List;
-
/**
* Tests for verifying TV app behavior for third-party TV input apps.
*/
@@ -131,7 +129,7 @@
mGoToEpgItem = createUserItem(R.string.tv_input_discover_test_go_to_epg,
R.string.tv_launch_epg, this);
mVerifyEpgItem = createUserItem(R.string.tv_input_discover_test_verify_epg,
- R.string.tv_input_discover_test_yes, this);
+ android.R.string.yes, this);
}
@Override
diff --git a/apps/cts-usb-accessory/Android.mk b/apps/cts-usb-accessory/Android.mk
index 8d18da3..f3a7ebd 100644
--- a/apps/cts-usb-accessory/Android.mk
+++ b/apps/cts-usb-accessory/Android.mk
@@ -33,6 +33,7 @@
LOCAL_STATIC_LIBRARIES := libusbhost libcutils
LOCAL_LDLIBS += -lpthread
LOCAL_CFLAGS := -g -O0
+LOCAL_CXX_STL := none
include $(BUILD_HOST_EXECUTABLE)
diff --git a/build/device_info_package.mk b/build/device_info_package.mk
index 600f6a1..0aaa8aa 100644
--- a/build/device_info_package.mk
+++ b/build/device_info_package.mk
@@ -19,7 +19,7 @@
DEVICE_INFO_PACKAGE := com.android.compatibility.common.deviceinfo
DEVICE_INFO_INSTRUMENT := com.android.compatibility.common.deviceinfo.DeviceInfoInstrument
DEVICE_INFO_PERMISSIONS += android.permission.WRITE_EXTERNAL_STORAGE
-DEVICE_INFO_ACTIVITIES += $(DEVICE_INFO_PACKAGE).GenericDeviceInfo
+DEVICE_INFO_ACTIVITIES += $(DEVICE_INFO_PACKAGE).GenericDeviceInfo $(DEVICE_INFO_PACKAGE).PackageDeviceInfo
# Add the base device info
LOCAL_STATIC_JAVA_LIBRARIES += compatibility-device-info
diff --git a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/DeviceInfoActivity.java b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/DeviceInfoActivity.java
index 62e512c..f9de6eb 100644
--- a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/DeviceInfoActivity.java
+++ b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/DeviceInfoActivity.java
@@ -182,6 +182,17 @@
/**
* Start a new group of result.
*/
+ public void startGroup() {
+ try {
+ mJsonWriter.beginObject();
+ } catch (Exception e) {
+ error("Failed to begin JSON group: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Start a new group of result with specified name.
+ */
public void startGroup(String name) {
try {
mJsonWriter.name(name);
@@ -203,6 +214,41 @@
}
/**
+ * Start a new array of result.
+ */
+ public void startArray() {
+ try {
+ mJsonWriter.beginArray();
+ } catch (Exception e) {
+ error("Failed to begin JSON array: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Start a new array of result with specified name.
+ */
+ public void startArray(String name) {
+ checkName(name);
+ try {
+ mJsonWriter.name(name);
+ mJsonWriter.beginArray();
+ } catch (Exception e) {
+ error("Failed to begin JSON array: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Complete adding result to the last started array.
+ */
+ public void endArray() {
+ try {
+ mJsonWriter.endArray();
+ } catch (Exception e) {
+ error("Failed to end JSON group: " + e.getMessage());
+ }
+ }
+
+ /**
* Add a double value result.
*/
public void addResult(String name, double value) {
diff --git a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/DeviceInfoInstrument.java b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/DeviceInfoInstrument.java
index f3af0bc..2f80911 100644
--- a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/DeviceInfoInstrument.java
+++ b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/DeviceInfoInstrument.java
@@ -25,19 +25,37 @@
import android.text.TextUtils;
import android.util.Log;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
/**
* An instrumentation that runs all activities that extends DeviceInfoActivity.
*/
public class DeviceInfoInstrument extends Instrumentation {
private static final String LOG_TAG = "ExtendedDeviceInfo";
- private static final int DEVICE_INFO_ACTIVITY_REQUEST = 1;
+ private static final String COLLECTOR = "collector";
+ // List of collectors to run. If null or empty, all collectors will run.
+ private Set<String> mCollectorSet = new HashSet<String>();
+
+ // Results sent to the caller when this istrumentation completes.
private Bundle mBundle = new Bundle();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ if (savedInstanceState != null) {
+ String collectorList = savedInstanceState.getString(COLLECTOR);
+ if (!TextUtils.isEmpty(collectorList)) {
+ for (String collector : TextUtils.split(collectorList, ",")) {
+ if (!TextUtils.isEmpty(collector)) {
+ mCollectorSet.add(collector);
+ }
+ }
+ }
+ }
start();
}
@@ -47,13 +65,8 @@
Context context = getContext();
ActivityInfo[] activities = context.getPackageManager().getPackageInfo(
context.getPackageName(), PackageManager.GET_ACTIVITIES).activities;
-
for (ActivityInfo activityInfo : activities) {
- Class cls = Class.forName(activityInfo.name);
- if (cls != DeviceInfoActivity.class &&
- DeviceInfoActivity.class.isAssignableFrom(cls)) {
- runActivity(activityInfo.name);
- }
+ runActivity(activityInfo.name);
}
} catch (Exception e) {
Log.e(LOG_TAG, "Exception occurred while running activities.", e);
@@ -65,9 +78,40 @@
}
/**
+ * Returns true if the activity meets the criteria to run; otherwise, false.
+ */
+ private boolean isActivityRunnable(Class activityClass) {
+ // Don't run the base DeviceInfoActivity class.
+ if (DeviceInfoActivity.class == activityClass) {
+ return false;
+ }
+ // Don't run anything that doesn't extends DeviceInfoActivity.
+ if (!DeviceInfoActivity.class.isAssignableFrom(activityClass)) {
+ return false;
+ }
+ // Only run activity if mCollectorSet is empty or contains it.
+ if (mCollectorSet != null && mCollectorSet.size() > 0) {
+ return mCollectorSet.contains(activityClass.getName());
+ }
+ // Run anything that makes it here.
+ return true;
+ }
+
+ /**
* Runs a device info activity and return the file path where the results are written to.
*/
private void runActivity(String activityName) throws Exception {
+ Class activityClass = null;
+ try {
+ activityClass = Class.forName(activityName);
+ } catch (ClassNotFoundException e) {
+ return;
+ }
+
+ if (activityClass == null || !isActivityRunnable(activityClass)) {
+ return;
+ }
+
Intent intent = new Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setClassName(this.getContext(), activityName);
@@ -76,7 +120,7 @@
waitForIdleSync();
activity.waitForActivityToFinish();
- String className = Class.forName(activityName).getSimpleName();
+ String className = activityClass.getSimpleName();
String errorMessage = activity.getErrorMessage();
if (TextUtils.isEmpty(errorMessage)) {
mBundle.putString(className, activity.getResultFilePath());
diff --git a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PackageDeviceInfo.java b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PackageDeviceInfo.java
new file mode 100644
index 0000000..4d9ad46
--- /dev/null
+++ b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PackageDeviceInfo.java
@@ -0,0 +1,50 @@
+/*
+ * 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.compatibility.common.deviceinfo;
+
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+
+import com.android.compatibility.common.deviceinfo.DeviceInfoActivity;
+
+/**
+ * PackageDeviceInfo collector.
+ */
+public class PackageDeviceInfo extends DeviceInfoActivity {
+
+ private static final String PACKAGE = "package";
+ private static final String NAME = "name";
+ private static final String VERSION_NAME = "version_name";
+ private static final String SYSTEM_PRIV = "system_priv";
+ private static final String PRIV_APP_DIR = "/system/priv-app";
+
+ @Override
+ protected void collectDeviceInfo() {
+ PackageManager pm = this.getPackageManager();
+ startArray(PACKAGE);
+ for (PackageInfo pkg : pm.getInstalledPackages(0)) {
+ startGroup();
+ addResult(NAME, pkg.packageName);
+ addResult(VERSION_NAME, pkg.versionName);
+
+ String dir = pkg.applicationInfo.sourceDir;
+ addResult(SYSTEM_PRIV, dir != null && dir.startsWith(PRIV_APP_DIR));
+ endGroup();
+ }
+ endArray(); // Package
+ }
+}
diff --git a/common/device-side/device-info/tests/src/com/android/compatibility/common/deviceinfo/DeviceInfoActivityTest.java b/common/device-side/device-info/tests/src/com/android/compatibility/common/deviceinfo/DeviceInfoActivityTest.java
index 8a8a66c..d092f4e 100644
--- a/common/device-side/device-info/tests/src/com/android/compatibility/common/deviceinfo/DeviceInfoActivityTest.java
+++ b/common/device-side/device-info/tests/src/com/android/compatibility/common/deviceinfo/DeviceInfoActivityTest.java
@@ -25,15 +25,15 @@
/**
* Test for {@link DeviceInfoActivity}.
*/
-public class DeviceInfoActivityTest extends ActivityInstrumentationTestCase2<SampleDeviceInfo> {
+public class DeviceInfoActivityTest extends ActivityInstrumentationTestCase2<TestDeviceInfo> {
private static final String EXPECTED_FILE_PATH =
- "/storage/emulated/0/device-info-files/SampleDeviceInfo.deviceinfo.json";
+ "/storage/emulated/0/device-info-files/TestDeviceInfo.deviceinfo.json";
- private SampleDeviceInfo mActivity;
+ private TestDeviceInfo mActivity;
public DeviceInfoActivityTest() {
- super(SampleDeviceInfo.class);
+ super(TestDeviceInfo.class);
}
@Override
@@ -59,7 +59,7 @@
assertEquals("Incorrect file path", EXPECTED_FILE_PATH, resultFilePath);
// Check json file content
String jsonContent = readFile(resultFilePath);
- assertEquals("Incorrect json output", ExampleObjects.sampleDeviceInfoJson(), jsonContent);
+ assertEquals("Incorrect json output", ExampleObjects.testDeviceInfoJson(), jsonContent);
}
private String readFile(String filePath) throws IOException {
diff --git a/common/device-side/device-info/tests/src/com/android/compatibility/common/deviceinfo/ExampleObjects.java b/common/device-side/device-info/tests/src/com/android/compatibility/common/deviceinfo/ExampleObjects.java
index 92e7df1..570bdd5 100644
--- a/common/device-side/device-info/tests/src/com/android/compatibility/common/deviceinfo/ExampleObjects.java
+++ b/common/device-side/device-info/tests/src/com/android/compatibility/common/deviceinfo/ExampleObjects.java
@@ -20,53 +20,73 @@
*/
public final class ExampleObjects {
+ // Must match DeviceInfoActivity.MAX_STRING_VALUE_LENGTH and
+ // DeviceInfoActivity.MAX_ARRAY_LENGTH
private static final int MAX_LENGTH = 1000;
- private static final String SAMPLE_DEVICE_INFO_JSON = "{\n" +
- " \"foo\": {\n" +
- " \"foo_boolean\": true,\n" +
- " \"bar\": {\n" +
- " \"bar_string\": [\n" +
- " \"bar-string-1\",\n" +
- " \"bar-string-2\",\n" +
- " \"bar-string-3\"\n" +
- " ],\n" +
- " \"bar_boolean\": [\n" +
- " true,\n" +
- " false\n" +
- " ],\n" +
- " \"bar_double\": [\n" +
- " 1.7976931348623157E308,\n" +
- " 4.9E-324\n" +
- " ],\n" +
- " \"bar_int\": [\n" +
- " 2147483647,\n" +
- " -2147483648\n" +
- " ],\n" +
- " \"bar_long\": [\n" +
- " 9223372036854775807,\n" +
- " -9223372036854775808\n" +
+ private static final String TEST_DEVICE_INFO_JSON = "{\n" +
+ " \"test_boolean\": true,\n" +
+ " \"test_double\": 1.23456789,\n" +
+ " \"test_int\": 123456789,\n" +
+ " \"test_long\": 9223372036854775807,\n" +
+ " \"test_string\": \"test string\",\n" +
+ " \"test_strings\": [\n" +
+ " \"test string 1\",\n" +
+ " \"test string 2\",\n" +
+ " \"test string 3\"\n" +
+ " ],\n" +
+ " \"test_group\": {\n" +
+ " \"test_boolean\": false,\n" +
+ " \"test_double\": 9.87654321,\n" +
+ " \"test_int\": 987654321,\n" +
+ " \"test_long\": 9223372036854775807,\n" +
+ " \"test_string\": \"test group string\",\n" +
+ " \"test_strings\": [\n" +
+ " \"test group string 1\",\n" +
+ " \"test group string 2\",\n" +
+ " \"test group string 3\"\n" +
+ " ]\n" +
+ " },\n" +
+ " \"test_groups\": [\n" +
+ " {\n" +
+ " \"test_string\": \"test groups string 1\",\n" +
+ " \"test_strings\": [\n" +
+ " \"test groups string 1-1\",\n" +
+ " \"test groups string 1-2\",\n" +
+ " \"test groups string 1-3\"\n" +
" ]\n" +
" },\n" +
- " \"foo_double\": 1.7976931348623157E308,\n" +
- " \"foo_int\": 2147483647,\n" +
- " \"foo_long\": 9223372036854775807,\n" +
- " \"foo_string\": \"foo-string\",\n" +
- " \"long_string\": \"%s\",\n" +
- " \"long_int_array\": [\n%s" +
- " ]\n" +
- " }\n" +
+ " {\n" +
+ " \"test_string\": \"test groups string 2\",\n" +
+ " \"test_strings\": [\n" +
+ " \"test groups string 2-1\",\n" +
+ " \"test groups string 2-2\",\n" +
+ " \"test groups string 2-3\"\n" +
+ " ]\n" +
+ " },\n" +
+ " {\n" +
+ " \"test_string\": \"test groups string 3\",\n" +
+ " \"test_strings\": [\n" +
+ " \"test groups string 3-1\",\n" +
+ " \"test groups string 3-2\",\n" +
+ " \"test groups string 3-3\"\n" +
+ " ]\n" +
+ " }\n" +
+ " ],\n" +
+ " \"max_length_string\": \"%s\",\n" +
+ " \"max_num_ints\": [\n%s" +
+ " ]\n" +
"}\n";
- public static String sampleDeviceInfoJson() {
+ public static String testDeviceInfoJson() {
StringBuilder longStringSb = new StringBuilder();
StringBuilder longArraySb = new StringBuilder();
int lastNum = MAX_LENGTH - 1;
for (int i = 0; i < MAX_LENGTH; i++) {
longStringSb.append("a");
- longArraySb.append(String.format(" %d%s\n", i, ((i == lastNum)? "" : ",")));
+ longArraySb.append(String.format(" %d%s\n", i, ((i == lastNum)? "" : ",")));
}
- return String.format(SAMPLE_DEVICE_INFO_JSON,
+ return String.format(TEST_DEVICE_INFO_JSON,
longStringSb.toString(), longArraySb.toString());
}
}
\ No newline at end of file
diff --git a/common/device-side/device-info/tests/src/com/android/compatibility/common/deviceinfo/TestDeviceInfo.java b/common/device-side/device-info/tests/src/com/android/compatibility/common/deviceinfo/TestDeviceInfo.java
new file mode 100644
index 0000000..7f82942
--- /dev/null
+++ b/common/device-side/device-info/tests/src/com/android/compatibility/common/deviceinfo/TestDeviceInfo.java
@@ -0,0 +1,85 @@
+/*
+ * 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.compatibility.common.deviceinfo;
+
+import android.os.Bundle;
+
+import java.lang.StringBuilder;
+
+/**
+ * Collector for testing DeviceInfoActivity
+ */
+public class TestDeviceInfo extends DeviceInfoActivity {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ protected void collectDeviceInfo() {
+
+ // Test primitive results
+ addResult("test_boolean", true);
+ addResult("test_double", 1.23456789);
+ addResult("test_int", 123456789);
+ addResult("test_long", Long.MAX_VALUE);
+ addResult("test_string", "test string");
+ addArray("test_strings", new String[] {
+ "test string 1",
+ "test string 2",
+ "test string 3",
+ });
+
+ // Test group
+ startGroup("test_group");
+ addResult("test_boolean", false);
+ addResult("test_double", 9.87654321);
+ addResult("test_int", 987654321);
+ addResult("test_long", Long.MAX_VALUE);
+ addResult("test_string", "test group string");
+ addArray("test_strings", new String[] {
+ "test group string 1",
+ "test group string 2",
+ "test group string 3"
+ });
+ endGroup(); // test_group
+
+ // Test array of groups
+ startArray("test_groups");
+ for (int i = 1; i < 4; i++) {
+ startGroup();
+ addResult("test_string", "test groups string " + i);
+ addArray("test_strings", new String[] {
+ "test groups string " + i + "-1",
+ "test groups string " + i + "-2",
+ "test groups string " + i + "-3"
+ });
+ endGroup();
+ }
+ endArray(); // test_groups
+
+ // Test max
+ StringBuilder sb = new StringBuilder();
+ int[] arr = new int[1001];
+ for (int i = 0; i < 1001; i++) {
+ sb.append("a");
+ arr[i] = i;
+ }
+ addResult("max_length_string", sb.toString());
+ addArray("max_num_ints", arr);
+ }
+}
diff --git a/hostsidetests/appsecurity/src/com/android/cts/appsecurity/AdoptableHostTest.java b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/AdoptableHostTest.java
index 70414ca..2ae2e10 100644
--- a/hostsidetests/appsecurity/src/com/android/cts/appsecurity/AdoptableHostTest.java
+++ b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/AdoptableHostTest.java
@@ -294,7 +294,7 @@
private LocalVolumeInfo getAdoptionVolume() throws Exception {
String[] lines = null;
int attempt = 0;
- while (attempt++ < 5) {
+ while (attempt++ < 15) {
lines = getDevice().executeShellCommand("sm list-volumes private").split("\n");
for (String line : lines) {
final LocalVolumeInfo info = new LocalVolumeInfo(line.trim());
diff --git a/hostsidetests/atrace/AtraceTestApp/AndroidManifest.xml b/hostsidetests/atrace/AtraceTestApp/AndroidManifest.xml
index a86f7f0..c460300 100644
--- a/hostsidetests/atrace/AtraceTestApp/AndroidManifest.xml
+++ b/hostsidetests/atrace/AtraceTestApp/AndroidManifest.xml
@@ -19,7 +19,7 @@
A simple app with a tracing section to test that apps tracing signals are
emitted by atrace.
-->
- <application>
+ <application android:debuggable="true"> <!-- Debuggable to enable tracing -->
<activity android:name=".AtraceTestAppActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
diff --git a/hostsidetests/atrace/src/android/atrace/cts/AtraceHostTest.java b/hostsidetests/atrace/src/android/atrace/cts/AtraceHostTest.java
index 78da8ac..760723e 100644
--- a/hostsidetests/atrace/src/android/atrace/cts/AtraceHostTest.java
+++ b/hostsidetests/atrace/src/android/atrace/cts/AtraceHostTest.java
@@ -208,6 +208,7 @@
installResult);
// capture a launch of the app with async tracing
+ // content traced by 'view' tag tested below, 'sched' used to ensure tgid printed
String atraceArgs = "-a " + TEST_PKG + " -c -b 16000 view"; // TODO: zipping
getDevice().executeShellCommand("atrace --async_stop " + atraceArgs);
getDevice().executeShellCommand("atrace --async_start " + atraceArgs);
@@ -227,13 +228,15 @@
String traceData = atraceOutput.substring(dataStart + MARKER.length());
FtraceEntryCallback callback = new FtraceEntryCallback() {
- private int matches = 0;
- private int nextSectionIndex = 0;
- private int appPid = -1;
+ private int userSpaceMatches = 0;
+ private int beginMatches = 0;
+ private int nextSectionIndex = -1;
+ private int appTid = -1;
- // list of tags expected to be seen on app launch, in order.
+
+ private final String initialSection = "traceable-app-test-section";
+ // list of tags expected to be seen on app launch, in order, after the initial.
private final String[] requiredSectionList = {
- "traceable-app-test-section",
"inflate",
"Choreographer#doFrame",
"traversal",
@@ -251,34 +254,41 @@
return;
}
- matches++;
assertNotNull(truncatedThreadName);
assertTrue(tid > 0);
- if (TEST_PKG.endsWith(truncatedThreadName)) {
- matches++;
+ userSpaceMatches++;
- if (pid >= 0) {
- // verify pid, if present
- if (appPid == -1) {
- appPid = pid;
- } else {
- assertEquals(appPid, pid);
- }
- }
+ if (details == null || !details.startsWith("B|")) {
+ // not a begin event
+ return;
+ }
+ beginMatches++;
- if (nextSectionIndex < requiredSectionList.length
- && details != null
- && details.startsWith("B|")
- && details.endsWith("|" + requiredSectionList[nextSectionIndex])) {
- nextSectionIndex++;
- }
+ if (details.endsWith("|" + initialSection)) {
+ // initial section observed, start looking for others in order
+ assertEquals(nextSectionIndex, -1);
+ nextSectionIndex = 0;
+ appTid = tid;
+ return;
+ }
+
+ if (nextSectionIndex >= 0
+ && tid == appTid
+ && nextSectionIndex < requiredSectionList.length
+ && details.endsWith("|" + requiredSectionList[nextSectionIndex])) {
+ // found next required section in sequence
+ nextSectionIndex++;
}
}
@Override
public void onFinished() {
assertTrue("Unable to parse any userspace sections from atrace output",
- matches != 0);
+ userSpaceMatches != 0);
+ assertTrue("Unable to parse any section begin events from atrace output",
+ beginMatches != 0);
+ assertTrue("Unable to parse initial userspace sections from test app",
+ nextSectionIndex >= 0);
assertEquals("Didn't see required list of traced sections, in order",
requiredSectionList.length, nextSectionIndex);
}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/AndroidManifest.xml b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/AndroidManifest.xml
index ed920e9..fe07bcb 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/AndroidManifest.xml
@@ -33,6 +33,15 @@
<action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
</intent-filter>
</receiver>
+ <receiver
+ android:name="com.android.cts.deviceandprofileowner.PrimaryUserDeviceAdmin"
+ android:permission="android.permission.BIND_DEVICE_ADMIN">
+ <meta-data android:name="android.app.device_admin"
+ android:resource="@xml/primary_device_admin" />
+ <intent-filter>
+ <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+ </intent-filter>
+ </receiver>
<activity
android:name="com.android.cts.deviceandprofileowner.ApplicationRestrictionsActivity" />
<activity
@@ -62,6 +71,13 @@
android:resource="@xml/authenticator" />
</service>
+ <activity android:name=".UserRestrictionActivity" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
+
</application>
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/res/xml/primary_device_admin.xml b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/res/xml/primary_device_admin.xml
new file mode 100644
index 0000000..a6aff49
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/res/xml/primary_device_admin.xml
@@ -0,0 +1,20 @@
+<!-- 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.
+-->
+<device-admin xmlns:android="http://schemas.android.com/apk/res/android" android:visible="false">
+ <uses-policies>
+ <reset-password />
+ <limit-password />
+ </uses-policies>
+</device-admin>
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/DelegatedCertInstallerTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/DelegatedCertInstallerTest.java
similarity index 96%
rename from hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/DelegatedCertInstallerTest.java
rename to hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/DelegatedCertInstallerTest.java
index de859b4..3eb8c35 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/DelegatedCertInstallerTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/DelegatedCertInstallerTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.cts.managedprofile;
+package com.android.cts.deviceandprofileowner;
import android.app.KeyguardManager;
import android.app.admin.DevicePolicyManager;
@@ -40,14 +40,12 @@
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
-import static com.android.cts.managedprofile.BaseManagedProfileTest.ADMIN_RECEIVER_COMPONENT;
-
/**
* Exercise delegated cert installer APIs in {@link DevicePolicyManager} by setting the test app
* (CtsCertInstallerApp) as a delegated cert installer and then asking it to invoke various
* cert-related APIs. The expected certificate changes are validated both remotely and locally.
*/
-public class DelegatedCertInstallerTest extends AndroidTestCase {
+public class DelegatedCertInstallerTest extends BaseDeviceAdminTest {
private static final String CERT_INSTALLER_PACKAGE = "com.android.cts.certinstaller";
@@ -163,12 +161,12 @@
mReceivedException = null;
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_CERT_OPERATION_DONE);
- getContext().registerReceiver(receiver, filter);
+ mContext.registerReceiver(receiver, filter);
}
@Override
public void tearDown() throws Exception {
- getContext().unregisterReceiver(receiver);
+ mContext.unregisterReceiver(receiver);
mDpm.uninstallCaCert(ADMIN_RECEIVER_COMPONENT, TEST_CA.getBytes());
// Installed private key pair will be removed once the lockscreen password is cleared,
// which is done in the hostside test.
@@ -246,7 +244,7 @@
intent.setAction(ACTION_INSTALL_CERT);
intent.putExtra(EXTRA_CERT_DATA, cert);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- getContext().startActivity(intent);
+ mContext.startActivity(intent);
}
private void removeCaCert(byte[] cert) {
@@ -254,7 +252,7 @@
intent.setAction(ACTION_REMOVE_CERT);
intent.putExtra(EXTRA_CERT_DATA, cert);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- getContext().startActivity(intent);
+ mContext.startActivity(intent);
}
private void verifyCaCert(byte[] cert) {
@@ -262,7 +260,7 @@
intent.setAction(ACTION_VERIFY_CERT);
intent.putExtra(EXTRA_CERT_DATA, cert);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- getContext().startActivity(intent);
+ mContext.startActivity(intent);
}
private void assertResult(String testName, Boolean expectSuccess) throws InterruptedException {
@@ -288,6 +286,6 @@
intent.putExtra(EXTRA_KEY_DATA, key);
intent.putExtra(EXTRA_KEY_ALIAS, alias);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- getContext().startActivity(intent);
+ mContext.startActivity(intent);
}
}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PrimaryUserAdminHelper.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PrimaryUserAdminHelper.java
new file mode 100644
index 0000000..7fc0173
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PrimaryUserAdminHelper.java
@@ -0,0 +1,71 @@
+/*
+ * 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.deviceandprofileowner;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.test.AndroidTestCase;
+
+/**
+ * This test executes helper tasks as active device admin in the primary user. Current tasks are
+ * setting and clearing lockscreen password used by the host side delegated cert installer test.
+ */
+public class PrimaryUserAdminHelper extends AndroidTestCase {
+
+ private DevicePolicyManager mDpm;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mDpm = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+ }
+
+ /**
+ * Device admin can only be deactivated by itself and this test should be executed before the
+ * device admin package can be uninstalled.
+ */
+ public void testClearDeviceAdmin() throws Exception {
+ ComponentName cn = PrimaryUserDeviceAdmin.ADMIN_RECEIVER_COMPONENT;
+ if (mDpm.isAdminActive(cn)) {
+ mDpm.removeActiveAdmin(cn);
+ // Wait until device admin is not active (with 2 minutes timeout).
+ for (int i = 0; i < 2 * 60 && mDpm.isAdminActive(cn); i++) {
+ Thread.sleep(1000); // 1 second.
+ }
+ }
+ assertFalse(mDpm.isAdminActive(cn));
+ }
+
+ /**
+ * Set lockscreen password.
+ */
+ public void testSetPassword() {
+ // Enable credential storage by setting a nonempty password.
+ assertTrue(mDpm.resetPassword("test", 0));
+ }
+
+ /**
+ * Clear lockscreen password.
+ */
+ public void testClearPassword() {
+ mDpm.setPasswordQuality(PrimaryUserDeviceAdmin.ADMIN_RECEIVER_COMPONENT,
+ DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
+ mDpm.setPasswordMinimumLength(
+ PrimaryUserDeviceAdmin.ADMIN_RECEIVER_COMPONENT, 0);
+ assertTrue(mDpm.resetPassword("", 0));
+ }
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PrimaryUserDeviceAdmin.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PrimaryUserDeviceAdmin.java
new file mode 100644
index 0000000..f3c8ff6
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PrimaryUserDeviceAdmin.java
@@ -0,0 +1,29 @@
+/*
+ * 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.deviceandprofileowner;
+
+import android.app.admin.DeviceAdminReceiver;
+import android.content.ComponentName;
+
+/**
+ * A device admin class running in the primary user. Currently used by delegated cert installer
+ * test to set a lockscreen password which is prerequisite of installKeyPair().
+ */
+public class PrimaryUserDeviceAdmin extends DeviceAdminReceiver {
+ public static final ComponentName ADMIN_RECEIVER_COMPONENT = new ComponentName(
+ PrimaryUserDeviceAdmin.class.getPackage().getName(),
+ PrimaryUserDeviceAdmin.class.getName());
+}
\ No newline at end of file
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/UserRestrictionActivity.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/UserRestrictionActivity.java
new file mode 100644
index 0000000..fed1a79
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/UserRestrictionActivity.java
@@ -0,0 +1,81 @@
+/*
+ * 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.deviceandprofileowner;
+
+import android.app.Activity;
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Process;
+import android.util.Log;
+
+/**
+ * Simple activity that adds or clears a user restriction depending on the value of the extras.
+ */
+public class UserRestrictionActivity extends Activity {
+
+ private static final String TAG = UserRestrictionActivity.class.getName();
+
+ private static final String EXTRA_RESTRICTION_KEY = "extra-restriction-key";
+ private static final String EXTRA_COMMAND = "extra-command";
+
+ private static final String ADD_COMMAND = "add-restriction";
+ private static final String CLEAR_COMMAND = "clear-restriction";
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ handleIntent(getIntent());
+ }
+
+ // Overriding this method in case another intent is sent to this activity before finish()
+ @Override
+ public void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ handleIntent(intent);
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ // Calling finish() here because doing it in onCreate(), onStart() or onResume() makes
+ // "adb shell am start" timeout if using the -W option.
+ finish();
+ }
+
+ private void handleIntent(Intent intent) {
+ DevicePolicyManager dpm = (DevicePolicyManager)
+ getSystemService(Context.DEVICE_POLICY_SERVICE);
+ String restrictionKey = intent.getStringExtra(EXTRA_RESTRICTION_KEY);
+ String command = intent.getStringExtra(EXTRA_COMMAND);
+ Log.i(TAG, "Command: \"" + command + "\". Restriction: \"" + restrictionKey + "\"");
+
+ if (ADD_COMMAND.equals(command)) {
+ dpm.addUserRestriction(BaseDeviceAdminTest.ADMIN_RECEIVER_COMPONENT, restrictionKey);
+ Log.i(TAG, "Added user restriction " + restrictionKey
+ + " for user " + Process.myUserHandle());
+ } else if (CLEAR_COMMAND.equals(command)) {
+ dpm.clearUserRestriction(
+ BaseDeviceAdminTest.ADMIN_RECEIVER_COMPONENT, restrictionKey);
+ Log.i(TAG, "Cleared user restriction " + restrictionKey
+ + " for user " + Process.myUserHandle());
+ } else {
+ Log.e(TAG, "Invalid command: " + command);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/OwnerChangedBroadcastTest.java b/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/OwnerChangedBroadcastTest.java
index 5cad040..f305e86 100644
--- a/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/OwnerChangedBroadcastTest.java
+++ b/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/OwnerChangedBroadcastTest.java
@@ -61,7 +61,8 @@
// Otherwise, we'll wait until we receive it.
return;
}
- assertTrue(mPreferenceChanged.tryAcquire(40, TimeUnit.SECONDS));
+ // We're relying on background broadcast intents, which can take a long time.
+ assertTrue(mPreferenceChanged.tryAcquire(2, TimeUnit.MINUTES));
assertTrue(mPreferences.getBoolean(
BroadcastIntentReceiver.OWNER_CHANGED_BROADCAST_RECEIVED_KEY, false));
}
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/res/xml/primary_device_admin.xml b/hostsidetests/devicepolicy/app/ManagedProfile/res/xml/primary_device_admin.xml
index 0ec3d1e..4bac8a5 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/res/xml/primary_device_admin.xml
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/res/xml/primary_device_admin.xml
@@ -14,8 +14,6 @@
-->
<device-admin xmlns:android="http://schemas.android.com/apk/res/android" android:visible="false">
<uses-policies>
- <reset-password />
- <limit-password />
<disable-camera />
</uses-policies>
</device-admin>
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/PrimaryUserAdminHelper.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/PrimaryUserAdminHelper.java
index af0ac25..47f8716 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/PrimaryUserAdminHelper.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/PrimaryUserAdminHelper.java
@@ -21,8 +21,7 @@
import android.test.AndroidTestCase;
/**
- * This test executes helper tasks as active device admin in the primary user. Current tasks are
- * setting and clearing lockscreen password used by the host side delegated cert installer test.
+ * This test executes helper tasks as active device admin in the primary user.
*/
public class PrimaryUserAdminHelper extends AndroidTestCase {
@@ -49,23 +48,4 @@
}
assertFalse(mDpm.isAdminActive(cn));
}
-
- /**
- * Set lockscreen password.
- */
- public void testSetPassword() {
- // Enable credential storage by setting a nonempty password.
- assertTrue(mDpm.resetPassword("test", 0));
- }
-
- /**
- * Clear lockscreen password.
- */
- public void testClearPassword() {
- mDpm.setPasswordQuality(PrimaryUserDeviceAdmin.ADMIN_RECEIVER_COMPONENT,
- DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
- mDpm.setPasswordMinimumLength(
- PrimaryUserDeviceAdmin.ADMIN_RECEIVER_COMPONENT, 0);
- assertTrue(mDpm.resetPassword("", 0));
- }
}
diff --git a/hostsidetests/devicepolicy/app/PackageInstaller/Android.mk b/hostsidetests/devicepolicy/app/PackageInstaller/Android.mk
new file mode 100644
index 0000000..e68c884
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/PackageInstaller/Android.mk
@@ -0,0 +1,33 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := CtsPackageInstallerApp
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 ctstestrunner ub-uiautomator
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/devicepolicy/app/PackageInstaller/AndroidManifest.xml b/hostsidetests/devicepolicy/app/PackageInstaller/AndroidManifest.xml
new file mode 100644
index 0000000..aaa88f2
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/PackageInstaller/AndroidManifest.xml
@@ -0,0 +1,41 @@
+<?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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.cts.packageinstaller">
+
+ <uses-sdk android:minSdkVersion="21"/>
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+
+ <receiver
+ android:name=".ClearDeviceOwnerTest$BasicAdminReceiver"
+ 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>
+ </application>
+
+ <instrumentation
+ android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.cts.packageinstaller"
+ android:label="Package Installer CTS Tests" />
+
+</manifest>
diff --git a/hostsidetests/devicepolicy/app/PackageInstaller/res/xml/device_admin.xml b/hostsidetests/devicepolicy/app/PackageInstaller/res/xml/device_admin.xml
new file mode 100644
index 0000000..de4a9e1
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/PackageInstaller/res/xml/device_admin.xml
@@ -0,0 +1,3 @@
+<device-admin xmlns:android="http://schemas.android.com/apk/res/android" android:visible="false">
+ <uses-policies />
+</device-admin>
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/PackageInstallTest.java b/hostsidetests/devicepolicy/app/PackageInstaller/src/com/android/cts/packageinstaller/BasePackageInstallTest.java
similarity index 70%
rename from hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/PackageInstallTest.java
rename to hostsidetests/devicepolicy/app/PackageInstaller/src/com/android/cts/packageinstaller/BasePackageInstallTest.java
index 0eddbee..f994e9e 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/PackageInstallTest.java
+++ b/hostsidetests/devicepolicy/app/PackageInstaller/src/com/android/cts/packageinstaller/BasePackageInstallTest.java
@@ -13,17 +13,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.cts.deviceowner;
+package com.android.cts.packageinstaller;
import android.app.PendingIntent;
+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.content.IntentSender;
-import android.content.pm.PackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.support.test.uiautomator.UiDevice;
+import android.test.InstrumentationTestCase;
+
+import com.android.cts.packageinstaller.ClearDeviceOwnerTest.BasicAdminReceiver;
import java.io.File;
import java.io.FileInputStream;
@@ -31,34 +37,41 @@
import java.io.OutputStream;
/**
- * This class tests silent package install and uninstall by a device owner.
+ * Base test case for testing PackageInstaller.
*/
-public class PackageInstallTest extends BaseDeviceOwnerTest {
- private static final String TEST_APP_LOCATION = "/data/local/tmp/CtsSimpleApp.apk";
- private static final String TEST_APP_PKG = "com.android.cts.launcherapps.simpleapp";
- private static final int PACKAGE_INSTALLER_TIMEOUT_MS = 60000; // 60 seconds
+public class BasePackageInstallTest extends InstrumentationTestCase {
+ protected static final String TEST_APP_LOCATION = "/data/local/tmp/CtsSimpleApp.apk";
+ protected static final String TEST_APP_PKG = "com.android.cts.launcherapps.simpleapp";
+ protected static final int PACKAGE_INSTALLER_TIMEOUT_MS = 60000; // 60 seconds
private static final String ACTION_INSTALL_COMMIT =
"com.android.cts.deviceowner.INTENT_PACKAGE_INSTALL_COMMIT";
- private static final int PACKAGE_INSTALLER_STATUS_UNDEFINED = -1000;
+ protected static final int PACKAGE_INSTALLER_STATUS_UNDEFINED = -1000;
+ public static final String PACKAGE_NAME = SilentPackageInstallTest.class.getPackage().getName();
+ protected Context mContext;
+ protected UiDevice mDevice;
+ protected DevicePolicyManager mDevicePolicyManager;
private PackageManager mPackageManager;
private PackageInstaller mPackageInstaller;
private PackageInstaller.Session mSession;
- private boolean mCallbackReceived;
- private int mCallbackStatus;
+ protected boolean mCallbackReceived;
+ protected int mCallbackStatus;
+ protected Intent mCallbackIntent;
- private final Object mPackageInstallerTimeoutLock = new Object();
+ protected final Object mPackageInstallerTimeoutLock = new Object();
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- mContext.unregisterReceiver(this);
synchronized (mPackageInstallerTimeoutLock) {
mCallbackStatus = intent.getIntExtra(PackageInstaller.EXTRA_STATUS,
PACKAGE_INSTALLER_STATUS_UNDEFINED);
if (mCallbackStatus == PackageInstaller.STATUS_SUCCESS) {
+ mContext.unregisterReceiver(this);
assertEquals(TEST_APP_PKG, intent.getStringExtra(
PackageInstaller.EXTRA_PACKAGE_NAME));
+ } else if (mCallbackStatus == PackageInstaller.STATUS_PENDING_USER_ACTION) {
+ mCallbackIntent = (Intent) intent.getExtras().get(Intent.EXTRA_INTENT);
}
mCallbackReceived = true;
mPackageInstallerTimeoutLock.notify();
@@ -69,6 +82,10 @@
@Override
protected void setUp() throws Exception {
super.setUp();
+ mContext = getInstrumentation().getContext();
+ mDevice = UiDevice.getInstance(getInstrumentation());
+ mDevicePolicyManager = (DevicePolicyManager)
+ mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
mPackageManager = mContext.getPackageManager();
mPackageInstaller = mPackageManager.getPackageInstaller();
assertNotNull(mPackageInstaller);
@@ -79,7 +96,10 @@
@Override
protected void tearDown() throws Exception {
- mDevicePolicyManager.setUninstallBlocked(getWho(), TEST_APP_PKG, false);
+ if (mDevicePolicyManager.isDeviceOwnerApp(PACKAGE_NAME) ||
+ mDevicePolicyManager.isProfileOwnerApp(PACKAGE_NAME)) {
+ mDevicePolicyManager.setUninstallBlocked(getWho(), TEST_APP_PKG, false);
+ }
try {
mContext.unregisterReceiver(mBroadcastReceiver);
} catch (IllegalArgumentException e) {
@@ -91,33 +111,12 @@
super.tearDown();
}
- public void testSilentInstallUninstall() throws Exception {
- // install the app
- assertInstallPackage();
-
- // uninstall the app again
- assertTrue(tryUninstallPackage());
- assertFalse(isPackageInstalled(TEST_APP_PKG));
+ protected static ComponentName getWho() {
+ return new ComponentName(PACKAGE_NAME, BasicAdminReceiver.class.getName());
}
- public void testUninstallBlocked() throws Exception {
- // install the app
- assertInstallPackage();
-
- mDevicePolicyManager.setUninstallBlocked(getWho(), TEST_APP_PKG, true);
- assertTrue(mDevicePolicyManager.isUninstallBlocked(getWho(), TEST_APP_PKG));
- assertTrue(mDevicePolicyManager.isUninstallBlocked(null, TEST_APP_PKG));
- assertFalse(tryUninstallPackage());
- assertTrue(isPackageInstalled(TEST_APP_PKG));
-
- mDevicePolicyManager.setUninstallBlocked(getWho(), TEST_APP_PKG, false);
- assertFalse(mDevicePolicyManager.isUninstallBlocked(getWho(), TEST_APP_PKG));
- assertFalse(mDevicePolicyManager.isUninstallBlocked(null, TEST_APP_PKG));
- assertTrue(tryUninstallPackage());
+ protected void assertInstallPackage() throws Exception {
assertFalse(isPackageInstalled(TEST_APP_PKG));
- }
-
- private void assertInstallPackage() throws Exception {
synchronized (mPackageInstallerTimeoutLock) {
mCallbackReceived = false;
mCallbackStatus = PACKAGE_INSTALLER_STATUS_UNDEFINED;
@@ -134,7 +133,7 @@
assertTrue(isPackageInstalled(TEST_APP_PKG));
}
- private boolean tryUninstallPackage() throws Exception {
+ protected boolean tryUninstallPackage() throws Exception {
assertTrue(isPackageInstalled(TEST_APP_PKG));
synchronized (mPackageInstallerTimeoutLock) {
mCallbackReceived = false;
@@ -151,7 +150,7 @@
}
}
- private void installPackage(String packageLocation) throws Exception {
+ protected void installPackage(String packageLocation) throws Exception {
PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
int sessionId = mPackageInstaller.createSession(params);
@@ -184,11 +183,11 @@
mContext,
sessionId,
broadcastIntent,
- PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT);
+ PendingIntent.FLAG_UPDATE_CURRENT);
return pendingIntent.getIntentSender();
}
- private boolean isPackageInstalled(String packageName) {
+ protected boolean isPackageInstalled(String packageName) {
try {
PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0);
return pi != null;
diff --git a/hostsidetests/devicepolicy/app/PackageInstaller/src/com/android/cts/packageinstaller/ClearDeviceOwnerTest.java b/hostsidetests/devicepolicy/app/PackageInstaller/src/com/android/cts/packageinstaller/ClearDeviceOwnerTest.java
new file mode 100644
index 0000000..a06271b
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/PackageInstaller/src/com/android/cts/packageinstaller/ClearDeviceOwnerTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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.packageinstaller;
+
+import android.app.admin.DeviceAdminReceiver;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.test.InstrumentationTestCase;
+
+/**
+ * Base class for profile and device based tests.
+ *
+ * This class handles making sure that the test is the profile or device owner and that it has an
+ * active admin registered, so that all tests may assume these are done.
+ */
+public class ClearDeviceOwnerTest extends InstrumentationTestCase {
+
+ public static class BasicAdminReceiver extends DeviceAdminReceiver {
+ }
+
+ public static final String PACKAGE_NAME = BasicAdminReceiver.class.getPackage().getName();
+ public static final ComponentName ADMIN_RECEIVER_COMPONENT = new ComponentName(
+ PACKAGE_NAME, BasicAdminReceiver.class.getName());
+
+ private DevicePolicyManager mDevicePolicyManager;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mDevicePolicyManager = (DevicePolicyManager)
+ getInstrumentation().getContext().getSystemService(Context.DEVICE_POLICY_SERVICE);
+ assertNotNull(mDevicePolicyManager);
+
+ assertTrue(mDevicePolicyManager.isAdminActive(ADMIN_RECEIVER_COMPONENT));
+ assertTrue("App is not device owner", mDevicePolicyManager.isDeviceOwnerApp(PACKAGE_NAME));
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ removeActiveAdmin(ADMIN_RECEIVER_COMPONENT);
+ mDevicePolicyManager.clearDeviceOwnerApp(PACKAGE_NAME);
+ assertFalse(mDevicePolicyManager.isAdminActive(ADMIN_RECEIVER_COMPONENT));
+ assertFalse(mDevicePolicyManager.isDeviceOwnerApp(PACKAGE_NAME));
+
+ super.tearDown();
+ }
+
+ // This test clears the device owner and active admin on tearDown(). To be called from the host
+ // side test once a test case is finished.
+ public void testClearDeviceOwner() {
+ }
+
+ private void removeActiveAdmin(ComponentName cn) throws InterruptedException {
+ if (mDevicePolicyManager.isAdminActive(cn)) {
+ mDevicePolicyManager.removeActiveAdmin(cn);
+ for (int i = 0; i < 1000 && mDevicePolicyManager.isAdminActive(cn); i++) {
+ Thread.sleep(100);
+ }
+ }
+ }
+}
diff --git a/hostsidetests/devicepolicy/app/PackageInstaller/src/com/android/cts/packageinstaller/ManualPackageInstallTest.java b/hostsidetests/devicepolicy/app/PackageInstaller/src/com/android/cts/packageinstaller/ManualPackageInstallTest.java
new file mode 100644
index 0000000..96affae
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/PackageInstaller/src/com/android/cts/packageinstaller/ManualPackageInstallTest.java
@@ -0,0 +1,117 @@
+/*
+ * 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.packageinstaller;
+
+import android.content.Intent;
+import android.content.pm.PackageInstaller;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.BySelector;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+
+/**
+ * This class tests manual package install and uninstall by a device owner.
+ */
+public class ManualPackageInstallTest extends BasePackageInstallTest {
+ private static final int AUTOMATOR_WAIT_TIMEOUT = 5000;
+ private static final int INSTALL_WAIT_TIME = 5000;
+
+ private static final BySelector POPUP_BUTTON_SELECTOR = By
+ .clazz(android.widget.Button.class.getName())
+ .res("android:id/button1")
+ .pkg("com.google.android.packageinstaller");
+ private static final BySelector POPUP_TEXT_SELECTOR = By
+ .clazz(android.widget.TextView.class.getName())
+ .res("android:id/alertTitle")
+ .pkg("com.google.android.packageinstaller");
+ private static final BySelector INSTALL_BUTTON_SELECTOR = By
+ .clazz(android.widget.Button.class.getName())
+ .res("com.android.packageinstaller:id/ok_button")
+ .pkg("com.google.android.packageinstaller");
+
+ public void testManualInstallSucceeded() throws Exception {
+ assertInstallPackage();
+ }
+
+ public void testManualInstallBlocked() throws Exception {
+ synchronized (mPackageInstallerTimeoutLock) {
+ mCallbackReceived = false;
+ mCallbackStatus = PACKAGE_INSTALLER_STATUS_UNDEFINED;
+ }
+ // Calls the original installPackage which does not click through the install button.
+ super.installPackage(TEST_APP_LOCATION);
+ synchronized (mPackageInstallerTimeoutLock) {
+ try {
+ mPackageInstallerTimeoutLock.wait(PACKAGE_INSTALLER_TIMEOUT_MS);
+ } catch (InterruptedException e) {
+ }
+ assertTrue(mCallbackReceived);
+ assertEquals(PackageInstaller.STATUS_PENDING_USER_ACTION, mCallbackStatus);
+ }
+
+ mCallbackIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(mCallbackIntent);
+
+ automateDismissInstallBlockedDialog();
+
+ // Assuming installation is not synchronous, we should wait a while before checking.
+ Thread.sleep(INSTALL_WAIT_TIME);
+ assertFalse(isPackageInstalled(TEST_APP_PKG));
+ }
+
+ @Override
+ protected void installPackage(String packageLocation) throws Exception {
+ super.installPackage(packageLocation);
+
+ synchronized (mPackageInstallerTimeoutLock) {
+ try {
+ mPackageInstallerTimeoutLock.wait(PACKAGE_INSTALLER_TIMEOUT_MS);
+ } catch (InterruptedException e) {
+ }
+ assertTrue(mCallbackReceived);
+ assertEquals(PackageInstaller.STATUS_PENDING_USER_ACTION, mCallbackStatus);
+ }
+
+ // Use a receiver to listen for package install.
+ synchronized (mPackageInstallerTimeoutLock) {
+ mCallbackReceived = false;
+ mCallbackStatus = PACKAGE_INSTALLER_STATUS_UNDEFINED;
+ }
+
+ mCallbackIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(mCallbackIntent);
+
+ automateInstallClick();
+ }
+
+ private void automateInstallClick() {
+ mDevice.wait(Until.hasObject(INSTALL_BUTTON_SELECTOR), AUTOMATOR_WAIT_TIMEOUT);
+ UiObject2 button = mDevice.findObject(INSTALL_BUTTON_SELECTOR);
+ assertNotNull("Install button not found", button);
+ button.click();
+ }
+
+ private void automateDismissInstallBlockedDialog() {
+ mDevice.wait(Until.hasObject(POPUP_TEXT_SELECTOR), AUTOMATOR_WAIT_TIMEOUT);
+ UiObject2 text = mDevice.findObject(POPUP_TEXT_SELECTOR);
+ assertNotNull("Alert dialog not found", text);
+ // "OK" button only present in the dialog if it is blocked by policy.
+ UiObject2 button = mDevice.findObject(POPUP_BUTTON_SELECTOR);
+ assertNotNull("OK button not found", button);
+ button.click();
+ }
+}
diff --git a/hostsidetests/devicepolicy/app/PackageInstaller/src/com/android/cts/packageinstaller/SilentPackageInstallTest.java b/hostsidetests/devicepolicy/app/PackageInstaller/src/com/android/cts/packageinstaller/SilentPackageInstallTest.java
new file mode 100644
index 0000000..f1a80b9
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/PackageInstaller/src/com/android/cts/packageinstaller/SilentPackageInstallTest.java
@@ -0,0 +1,48 @@
+/*
+ * 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.packageinstaller;
+
+/**
+ * This class tests silent package install and uninstall by a device owner.
+ */
+public class SilentPackageInstallTest extends BasePackageInstallTest {
+ public void testSilentInstallUninstall() throws Exception {
+ // install the app
+ assertInstallPackage();
+
+ // uninstall the app again
+ assertTrue(tryUninstallPackage());
+ assertFalse(isPackageInstalled(TEST_APP_PKG));
+ }
+
+ public void testUninstallBlocked() throws Exception {
+ // install the app
+ assertInstallPackage();
+
+ mDevicePolicyManager.setUninstallBlocked(getWho(), TEST_APP_PKG, true);
+ assertTrue(mDevicePolicyManager.isUninstallBlocked(getWho(), TEST_APP_PKG));
+ assertTrue(mDevicePolicyManager.isUninstallBlocked(null, TEST_APP_PKG));
+ assertFalse(tryUninstallPackage());
+ assertTrue(isPackageInstalled(TEST_APP_PKG));
+
+ mDevicePolicyManager.setUninstallBlocked(getWho(), TEST_APP_PKG, false);
+ assertFalse(mDevicePolicyManager.isUninstallBlocked(getWho(), TEST_APP_PKG));
+ assertFalse(mDevicePolicyManager.isUninstallBlocked(null, TEST_APP_PKG));
+ assertTrue(tryUninstallPackage());
+ assertFalse(isPackageInstalled(TEST_APP_PKG));
+ }
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
index 650e963..4fc14e4 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
@@ -17,7 +17,6 @@
package com.android.cts.devicepolicy;
import com.android.cts.tradefed.build.CtsBuildHelper;
-import com.android.cts.util.AbiUtils;
import com.android.ddmlib.Log.LogLevel;
import com.android.ddmlib.testrunner.InstrumentationResultParser;
import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
@@ -363,4 +362,20 @@
CLog.logAndDisplay(LogLevel.INFO, "Output for command " + command + ": " + commandOutput);
return commandOutput.startsWith("Success:");
}
+
+ protected String getSettings(String namespace, String name, int userId)
+ throws DeviceNotAvailableException {
+ String command = "settings --user " + userId + " get " + namespace + " " + name;
+ String commandOutput = getDevice().executeShellCommand(command);
+ CLog.logAndDisplay(LogLevel.INFO, "Output for command " + command + ": " + commandOutput);
+ return commandOutput.replace("\n", "").replace("\r", "");
+ }
+
+ protected void putSettings(String namespace, String name, String value, int userId)
+ throws DeviceNotAvailableException {
+ String command = "settings --user " + userId + " put " + namespace + " " + name
+ + " " + value;
+ String commandOutput = getDevice().executeShellCommand(command);
+ CLog.logAndDisplay(LogLevel.INFO, "Output for command " + command + ": " + commandOutput);
+ }
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CustomDeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CustomDeviceOwnerTest.java
index 7cb8f3b..8d22638 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CustomDeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CustomDeviceOwnerTest.java
@@ -16,9 +16,7 @@
package com.android.cts.devicepolicy;
-import com.android.ddmlib.Log.LogLevel;
-import com.android.tradefed.log.LogUtil.CLog;
-
+import java.io.File;
import java.lang.Exception;
/**
@@ -49,6 +47,18 @@
private static final String INTENT_RECEIVER_PKG = "com.android.cts.intent.receiver";
private static final String INTENT_RECEIVER_APK = "CtsIntentReceiverApp.apk";
+ private static final String TEST_APP_APK = "CtsSimpleApp.apk";
+ private static final String TEST_APP_PKG = "com.android.cts.launcherapps.simpleapp";
+ private static final String TEST_APP_LOCATION = "/data/local/tmp/";
+
+ private static final String PACKAGE_INSTALLER_PKG = "com.android.cts.packageinstaller";
+ private static final String PACKAGE_INSTALLER_APK = "CtsPackageInstallerApp.apk";
+ private static final String PACKAGE_INSTALLER_ADMIN_COMPONENT =
+ PACKAGE_INSTALLER_PKG + "/" + ".ClearDeviceOwnerTest$BasicAdminReceiver";
+ private static final String PACKAGE_INSTALLER_CLEAR_DEVICE_OWNER_TEST_CLASS =
+ PACKAGE_INSTALLER_PKG + ".ClearDeviceOwnerTest";
+
+ @Override
public void tearDown() throws Exception {
if (mHasFeature) {
getDevice().uninstallPackage(DEVICE_OWNER_PKG);
@@ -117,4 +127,28 @@
"testRemoveAccounts", 0));
}
}
+
+ public void testSilentPackageInstall() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ final File apk = mCtsBuild.getTestApp(TEST_APP_APK);
+ try {
+ // Install the test and prepare the test apk.
+ installApp(PACKAGE_INSTALLER_APK);
+ assertTrue(setDeviceOwner(PACKAGE_INSTALLER_ADMIN_COMPONENT));
+
+ getDevice().uninstallPackage(TEST_APP_PKG);
+ assertTrue(getDevice().pushFile(apk, TEST_APP_LOCATION + apk.getName()));
+ assertTrue(runDeviceTests(PACKAGE_INSTALLER_PKG,
+ PACKAGE_INSTALLER_PKG + ".SilentPackageInstallTest"));
+ } finally {
+ assertTrue("Failed to remove device owner.", runDeviceTests(PACKAGE_INSTALLER_PKG,
+ PACKAGE_INSTALLER_CLEAR_DEVICE_OWNER_TEST_CLASS));
+ String command = "rm " + TEST_APP_LOCATION + apk.getName();
+ String commandOutput = getDevice().executeShellCommand(command);
+ getDevice().uninstallPackage(TEST_APP_PKG);
+ getDevice().uninstallPackage(PACKAGE_INSTALLER_PKG);
+ }
+ }
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
index 36dab2c..43e6730 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
@@ -16,6 +16,12 @@
package com.android.cts.devicepolicy;
+import com.android.ddmlib.Log.LogLevel;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.log.LogUtil.CLog;
+
+import java.io.File;
+
/**
* Set of tests for usecases that apply to profile and device owner.
* This class is the base class of MixedProfileOwnerTest and MixedDeviceOwnerTest and is abstract
@@ -34,8 +40,21 @@
private static final String SIMPLE_PRE_M_APP_PKG = "com.android.cts.launcherapps.simplepremapp";
private static final String SIMPLE_PRE_M_APP_APK = "CtsSimplePreMApp.apk";
+ private static final String CERT_INSTALLER_PKG = "com.android.cts.certinstaller";
+ private static final String CERT_INSTALLER_APK = "CtsCertInstallerApp.apk";
+
+ private static final String TEST_APP_APK = "CtsSimpleApp.apk";
+ private static final String TEST_APP_PKG = "com.android.cts.launcherapps.simpleapp";
+ private static final String TEST_APP_LOCATION = "/data/local/tmp/";
+
+ private static final String PACKAGE_INSTALLER_PKG = "com.android.cts.packageinstaller";
+ private static final String PACKAGE_INSTALLER_APK = "CtsPackageInstallerApp.apk";
+
protected static final int USER_OWNER = 0;
+ private static final String ADD_RESTRICTION_COMMAND = "add-restriction";
+ private static final String CLEAR_RESTRICTION_COMMAND = "clear-restriction";
+
// ID of the user all tests are run as. For device owner this will be 0, for profile owner it
// is the user id of the created profile.
protected int mUserId;
@@ -46,6 +65,7 @@
getDevice().uninstallPackage(DEVICE_ADMIN_PKG);
getDevice().uninstallPackage(PERMISSIONS_APP_PKG);
getDevice().uninstallPackage(SIMPLE_PRE_M_APP_PKG);
+ getDevice().uninstallPackage(CERT_INSTALLER_PKG);
}
super.tearDown();
}
@@ -159,13 +179,85 @@
executeDeviceTestClass(".ApplicationHiddenTest");
}
- // TODO: Remove AccountManagementTest from XTS after GTS is released for MNC.
public void testAccountManagement() throws Exception {
if (!mHasFeature) {
return;
}
executeDeviceTestClass(".AccountManagementTest");
+
+ // Send a home intent to dismiss an error dialog.
+ String command = "am start -a android.intent.action.MAIN -c android.intent.category.HOME";
+ CLog.logAndDisplay(LogLevel.INFO, "Output for command " + command + ": "
+ + getDevice().executeShellCommand(command));
+ }
+
+ public void testDelegatedCertInstaller() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ installAppAsUser(CERT_INSTALLER_APK, mUserId);
+ installAppAsUser(DEVICE_ADMIN_APK, USER_OWNER);
+ setDeviceAdmin(DEVICE_ADMIN_PKG + "/.PrimaryUserDeviceAdmin");
+
+ final String adminHelperClass = ".PrimaryUserAdminHelper";
+ try {
+ // Set a non-empty device lockscreen password, which is a precondition for installing
+ // private key pairs.
+ assertTrue("Set lockscreen password failed", runDeviceTestsAsUser(DEVICE_ADMIN_PKG,
+ adminHelperClass, "testSetPassword", 0 /* user 0 */));
+ assertTrue("DelegatedCertInstaller failed", runDeviceTestsAsUser(DEVICE_ADMIN_PKG,
+ ".DelegatedCertInstallerTest", mUserId));
+ } finally {
+ // Reset lockscreen password and remove device admin.
+ assertTrue("Clear lockscreen password failed", runDeviceTestsAsUser(DEVICE_ADMIN_PKG,
+ adminHelperClass, "testClearPassword", 0 /* user 0 */));
+ assertTrue("Clear device admin failed", runDeviceTestsAsUser(DEVICE_ADMIN_PKG,
+ adminHelperClass, "testClearDeviceAdmin", 0 /* user 0 */));
+ }
+ }
+
+ public void testPackageInstallUserRestrictions() throws Exception {
+ // UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES
+ final String DISALLOW_INSTALL_UNKNOWN_SOURCES = "no_install_unknown_sources";
+ final String UNKNOWN_SOURCES_SETTING = "install_non_market_apps";
+ final String SECURE_SETTING_CATEGORY = "secure";
+ final File apk = mCtsBuild.getTestApp(TEST_APP_APK);
+ String unknownSourceSetting = null;
+ try {
+ // Install the test and prepare the test apk.
+ installApp(PACKAGE_INSTALLER_APK);
+ assertTrue(getDevice().pushFile(apk, TEST_APP_LOCATION + apk.getName()));
+
+ // Add restrictions and test if we can install the apk.
+ getDevice().uninstallPackage(TEST_APP_PKG);
+ changeUserRestrictionForUser(DISALLOW_INSTALL_UNKNOWN_SOURCES,
+ ADD_RESTRICTION_COMMAND, mUserId);
+ assertTrue(runDeviceTestsAsUser(PACKAGE_INSTALLER_PKG, ".ManualPackageInstallTest",
+ "testManualInstallBlocked", mUserId));
+
+ // Clear restrictions and test if we can install the apk.
+ changeUserRestrictionForUser(DISALLOW_INSTALL_UNKNOWN_SOURCES,
+ CLEAR_RESTRICTION_COMMAND, mUserId);
+
+ // Enable Unknown sources in Settings.
+ unknownSourceSetting =
+ getSettings(SECURE_SETTING_CATEGORY, UNKNOWN_SOURCES_SETTING, mUserId);
+ putSettings(SECURE_SETTING_CATEGORY, UNKNOWN_SOURCES_SETTING, "1", mUserId);
+ assertEquals("1",
+ getSettings(SECURE_SETTING_CATEGORY, UNKNOWN_SOURCES_SETTING, mUserId));
+ assertTrue(runDeviceTestsAsUser(PACKAGE_INSTALLER_PKG, ".ManualPackageInstallTest",
+ "testManualInstallSucceeded", mUserId));
+ } finally {
+ String command = "rm " + TEST_APP_LOCATION + apk.getName();
+ getDevice().executeShellCommand(command);
+ getDevice().uninstallPackage(TEST_APP_PKG);
+ getDevice().uninstallPackage(PACKAGE_INSTALLER_APK);
+ if (unknownSourceSetting != null) {
+ putSettings(SECURE_SETTING_CATEGORY, UNKNOWN_SOURCES_SETTING, unknownSourceSetting,
+ mUserId);
+ }
+ }
}
protected void executeDeviceTestClass(String className) throws Exception {
@@ -175,4 +267,18 @@
protected void executeDeviceTestMethod(String className, String testName) throws Exception {
assertTrue(runDeviceTestsAsUser(DEVICE_ADMIN_PKG, className, testName, mUserId));
}
+
+ private void changeUserRestrictionForUser(String key, String command, int userId)
+ throws DeviceNotAvailableException {
+ String adbCommand = "am start -W --user " + userId
+ + " -c android.intent.category.DEFAULT "
+ + " --es extra-command " + command
+ + " --es extra-restriction-key " + key
+ + " " + DEVICE_ADMIN_PKG + "/.UserRestrictionActivity";
+ String commandOutput = getDevice().executeShellCommand(adbCommand);
+ CLog.logAndDisplay(LogLevel.INFO,
+ "Output for command " + adbCommand + ": " + commandOutput);
+ assertTrue("Command was expected to succeed " + commandOutput,
+ commandOutput.contains("Status: ok"));
+ }
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
index 4f267d1..96ca469 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
@@ -16,12 +16,6 @@
package com.android.cts.devicepolicy;
-import com.android.ddmlib.Log.LogLevel;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.log.LogUtil.CLog;
-
-import java.io.File;
-
/**
* Set of tests for Device Owner use cases.
*/
@@ -35,10 +29,6 @@
private static final String MANAGED_PROFILE_ADMIN =
MANAGED_PROFILE_PKG + ".BaseManagedProfileTest$BasicAdminReceiver";
- private static final String TEST_APP_APK = "CtsSimpleApp.apk";
- private static final String TEST_APP_PKG = "com.android.cts.launcherapps.simpleapp";
- private static final String TEST_APP_LOCATION = "/data/local/tmp/";
-
private static final String INTENT_RECEIVER_PKG = "com.android.cts.intent.receiver";
private static final String INTENT_RECEIVER_APK = "CtsIntentReceiverApp.apk";
@@ -93,19 +83,6 @@
}
}
- public void testPackageInstall() throws Exception {
- final File apk = mCtsBuild.getTestApp(TEST_APP_APK);
- try {
- getDevice().uninstallPackage(TEST_APP_PKG);
- assertTrue(getDevice().pushFile(apk, TEST_APP_LOCATION + apk.getName()));
- executeDeviceOwnerTest("PackageInstallTest");
- } finally {
- String command = "rm " + TEST_APP_LOCATION + apk.getName();
- String commandOutput = getDevice().executeShellCommand(command);
- getDevice().uninstallPackage(TEST_APP_PKG);
- }
- }
-
public void testSystemUpdatePolicy() throws Exception {
executeDeviceOwnerTest("SystemUpdatePolicyTest");
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsMultiUserTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsMultiUserTest.java
index 8da189f..1d5dd11 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsMultiUserTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsMultiUserTest.java
@@ -25,11 +25,13 @@
* apps.
*/
public class LauncherAppsMultiUserTest extends BaseLauncherAppsTest {
+ private static final String FEATURE_LIVE_TV = "android.software.live_tv";
private int mSecondaryUserId;
private int mSecondaryUserSerialNumber;
private boolean mMultiUserSupported;
+ private boolean mHasLiveTvFeature;
@Override
protected void setUp() throws Exception {
@@ -37,6 +39,7 @@
// We need multi user to be supported in order to create a secondary user
// and api level 21 to support LauncherApps
mMultiUserSupported = getMaxNumberOfUsersSupported() > 1 && getDevice().getApiLevel() >= 21;
+ mHasLiveTvFeature = hasDeviceFeature(FEATURE_LIVE_TV);
if (mMultiUserSupported) {
removeTestUsers();
@@ -58,7 +61,7 @@
}
public void testGetActivitiesForNonProfileFails() throws Exception {
- if (!mMultiUserSupported) {
+ if (!mMultiUserSupported || mHasLiveTvFeature) {
return;
}
installApp(SIMPLE_APP_APK);
@@ -73,7 +76,7 @@
}
public void testNoLauncherCallbackPackageAddedSecondaryUser() throws Exception {
- if (!mMultiUserSupported) {
+ if (!mMultiUserSupported || mHasLiveTvFeature) {
return;
}
startCallbackService();
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
index acc5b26..214131f 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
@@ -42,9 +42,6 @@
private static final String INTENT_RECEIVER_PKG = "com.android.cts.intent.receiver";
private static final String INTENT_RECEIVER_APK = "CtsIntentReceiverApp.apk";
- private static final String CERT_INSTALLER_PKG = "com.android.cts.certinstaller";
- private static final String CERT_INSTALLER_APK = "CtsCertInstallerApp.apk";
-
private static final String WIFI_CONFIG_CREATOR_PKG = "com.android.cts.wificonfigcreator";
private static final String WIFI_CONFIG_CREATOR_APK = "CtsWifiConfigCreator.apk";
@@ -94,7 +91,6 @@
getDevice().uninstallPackage(MANAGED_PROFILE_PKG);
getDevice().uninstallPackage(INTENT_SENDER_PKG);
getDevice().uninstallPackage(INTENT_RECEIVER_PKG);
- getDevice().uninstallPackage(CERT_INSTALLER_PKG);
}
super.tearDown();
}
@@ -525,28 +521,6 @@
"testSetBluetoothContactSharingDisabled_setterAndGetter", mUserId));
}
- public void testDelegatedCertInstaller() throws Exception {
- if (!mHasFeature) {
- return;
- }
- installApp(CERT_INSTALLER_APK);
- setDeviceAdmin(MANAGED_PROFILE_PKG + "/.PrimaryUserDeviceAdmin");
-
- final String adminHelperClass = ".PrimaryUserAdminHelper";
- try {
- assertTrue("Set lockscreen password failed", runDeviceTestsAsUser(MANAGED_PROFILE_PKG,
- adminHelperClass, "testSetPassword", 0 /* user 0 */));
- assertTrue("DelegatedCertInstaller failed", runDeviceTestsAsUser(MANAGED_PROFILE_PKG,
- ".DelegatedCertInstallerTest", mUserId));
- } finally {
- // Reset lockscreen password and remove device admin.
- assertTrue("Clear lockscreen password failed", runDeviceTestsAsUser(MANAGED_PROFILE_PKG,
- adminHelperClass, "testClearPassword", 0 /* user 0 */));
- assertTrue("Clear device admin failed", runDeviceTestsAsUser(MANAGED_PROFILE_PKG,
- adminHelperClass, "testClearDeviceAdmin", 0 /* user 0 */));
- }
- }
-
public void testCannotSetProfileOwnerAgain() throws Exception {
if (!mHasFeature) {
return;
diff --git a/hostsidetests/dumpsys/FramestatsTestApp/Android.mk b/hostsidetests/dumpsys/FramestatsTestApp/Android.mk
new file mode 100644
index 0000000..1104523
--- /dev/null
+++ b/hostsidetests/dumpsys/FramestatsTestApp/Android.mk
@@ -0,0 +1,28 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
+LOCAL_PACKAGE_NAME := CtsFramestatsTestApp
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/dumpsys/FramestatsTestApp/AndroidManifest.xml b/hostsidetests/dumpsys/FramestatsTestApp/AndroidManifest.xml
new file mode 100644
index 0000000..3a9f902
--- /dev/null
+++ b/hostsidetests/dumpsys/FramestatsTestApp/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.cts.framestatstestapp">
+ <!--
+ A simple app that draws at least one frame. Used by framestats
+ test.
+ -->
+ <application>
+ <activity android:name=".FramestatsTestAppActivity">
+ <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>
+ </application>
+</manifest>
diff --git a/hostsidetests/dumpsys/FramestatsTestApp/src/com/android/cts/framestatstestapp/FramestatsTestAppActivity.java b/hostsidetests/dumpsys/FramestatsTestApp/src/com/android/cts/framestatstestapp/FramestatsTestAppActivity.java
new file mode 100644
index 0000000..7370508
--- /dev/null
+++ b/hostsidetests/dumpsys/FramestatsTestApp/src/com/android/cts/framestatstestapp/FramestatsTestAppActivity.java
@@ -0,0 +1,31 @@
+/*
+ * 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.framestatstestapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.Trace;
+import android.view.View;
+
+public class FramestatsTestAppActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ View v = new View(this);
+ v.setBackgroundColor(0xFF00FF00);
+ setContentView(v);
+ }
+}
diff --git a/hostsidetests/dumpsys/src/android/dumpsys/cts/DumpsysHostTest.java b/hostsidetests/dumpsys/src/android/dumpsys/cts/DumpsysHostTest.java
index a787cdd..0daae03 100644
--- a/hostsidetests/dumpsys/src/android/dumpsys/cts/DumpsysHostTest.java
+++ b/hostsidetests/dumpsys/src/android/dumpsys/cts/DumpsysHostTest.java
@@ -16,11 +16,15 @@
package android.dumpsys.cts;
-import com.android.ddmlib.Log;
+import com.android.cts.tradefed.build.CtsBuildHelper;
+import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.IBuildReceiver;
import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
import java.io.StringReader;
import java.util.HashSet;
import java.util.Set;
@@ -28,8 +32,10 @@
/**
* Test to check the format of the dumps of various services (currently only procstats is tested).
*/
-public class DumpsysHostTest extends DeviceTestCase {
+public class DumpsysHostTest extends DeviceTestCase implements IBuildReceiver {
private static final String TAG = "DumpsysHostTest";
+ private static final String TEST_APK = "CtsFramestatsTestApp.apk";
+ private static final String TEST_PKG = "com.android.cts.framestatstestapp";
/**
* A reference to the device under test.
@@ -815,17 +821,38 @@
*/
public void testGfxinfoFramestats() throws Exception {
final String MARKER = "---PROFILEDATA---";
- final int TIMESTAMP_COUNT = 14;
- String frameinfo = mDevice.executeShellCommand("dumpsys gfxinfo com.android.systemui framestats");
- assertNotNull(frameinfo);
- assertTrue(frameinfo.length() > 0);
- int profileStart = frameinfo.indexOf(MARKER);
- int profileEnd = frameinfo.indexOf(MARKER, profileStart + 1);
- assertTrue(profileStart >= 0);
- assertTrue(profileEnd > profileStart);
- String profileData = frameinfo.substring(profileStart + MARKER.length(), profileEnd);
- assertTrue(profileData.length() > 0);
+ try {
+ // cleanup test apps that might be installed from previous partial test run
+ getDevice().uninstallPackage(TEST_PKG);
+
+ // install the test app
+ File testAppFile = mCtsBuild.getTestApp(TEST_APK);
+ String installResult = getDevice().installPackage(testAppFile, false);
+ assertNull(
+ String.format("failed to install atrace test app. Reason: %s", installResult),
+ installResult);
+
+ getDevice().executeShellCommand("am start -W " + TEST_PKG);
+
+ String frameinfo = mDevice.executeShellCommand("dumpsys gfxinfo " +
+ TEST_PKG + " framestats");
+ assertNotNull(frameinfo);
+ assertTrue(frameinfo.length() > 0);
+ int profileStart = frameinfo.indexOf(MARKER);
+ int profileEnd = frameinfo.indexOf(MARKER, profileStart + 1);
+ assertTrue(profileStart >= 0);
+ assertTrue(profileEnd > profileStart);
+ String profileData = frameinfo.substring(profileStart + MARKER.length(), profileEnd);
+ assertTrue(profileData.length() > 0);
+ validateProfileData(profileData);
+ } finally {
+ getDevice().uninstallPackage(TEST_PKG);
+ }
+ }
+
+ private void validateProfileData(String profileData) throws IOException {
+ final int TIMESTAMP_COUNT = 14;
boolean foundAtLeastOneRow = false;
try (BufferedReader reader = new BufferedReader(
new StringReader(profileData))) {
@@ -872,6 +899,16 @@
assertTrue(foundAtLeastOneRow);
}
+ private CtsBuildHelper mCtsBuild;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setBuild(IBuildInfo buildInfo) {
+ mCtsBuild = CtsBuildHelper.createBuildHelper(buildInfo);
+ }
+
private static long assertInteger(String input) {
try {
return Long.parseLong(input);
diff --git a/hostsidetests/security/src/android/cts/security/SELinuxHostTest.java b/hostsidetests/security/src/android/cts/security/SELinuxHostTest.java
index a0d3167..abb1ac4 100644
--- a/hostsidetests/security/src/android/cts/security/SELinuxHostTest.java
+++ b/hostsidetests/security/src/android/cts/security/SELinuxHostTest.java
@@ -589,9 +589,9 @@
assertDomainN("u:r:zygote:s0", "zygote", "zygote64");
}
- /* drm server is always present */
+ /* Checks drmserver for devices that require it */
public void testDrmServerDomain() throws DeviceNotAvailableException {
- assertDomainOne("u:r:drmserver:s0", "/system/bin/drmserver");
+ assertDomainZeroOrOne("u:r:drmserver:s0", "/system/bin/drmserver");
}
/* Media server is always running */
diff --git a/hostsidetests/theme/Android.mk b/hostsidetests/theme/Android.mk
index 71027c7..188bf7a 100644
--- a/hostsidetests/theme/Android.mk
+++ b/hostsidetests/theme/Android.mk
@@ -29,6 +29,8 @@
LOCAL_CTS_TEST_PACKAGE := android.host.theme
+LOCAL_SDK_VERSION := current
+
include $(BUILD_CTS_HOST_JAVA_LIBRARY)
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/hostsidetests/theme/README b/hostsidetests/theme/README
new file mode 100644
index 0000000..bce711a
--- /dev/null
+++ b/hostsidetests/theme/README
@@ -0,0 +1,73 @@
+* 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.
+
+
+INTRODUCTION
+
+The Android theme tests ensure that the Holo and Material themes have not been
+modified. They consist of API-specific sets of reference images representing
+specific themes and widgets that must be identical across devices. To pass the
+theme tests, a device must be able to generate images that are identical to the
+reference images.
+
+NOTE: Reference images should only be updated by the CTS test maintainers. Any
+ modifications to the reference images will invalidate the test results.
+
+
+INSTRUCTIONS
+
+I. Generating reference images (CTS maintainers only)
+
+Reference images are typically only generated for new API revisions. To
+generate a new set of reference images, do the following:
+
+ 1. Connect one device for each DPI bucket (ldpi, xxxhdpi, etc.) that you wish
+ to generate references images for. Confirm that all devices are connected
+ with:
+
+ adb devices
+
+ 2. Image generation occurs on all devices in parallel. Resulting sets of
+ reference images are saved in assets/<api>/<dpi>.zip and will overwrite
+ any existing sets. Image generation may be started using:
+
+ .cts/hostsidetests/theme/generate_images.sh
+
+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.
+
+
+II. Building theme tests
+
+1. If you have not already built the CTS tests, run an initial make:
+
+ make cts -j32
+
+2. Subsequent changes to the theme tests, including changes to the reference
+ images, may be built using mmm:
+
+ mmm cts/hostsidetests/theme -j32
+
+
+III. Running theme tests
+
+1. Connect the device that you wish to test. Confirm that is is connected with:
+
+ adb devices
+
+2. Run the theme tests using cts-tradefed:
+
+ cts-tradefed run cts -c android.theme.cts.ThemeHostTest
+
+3. Wait for the tests to complete. This should take less than five minutes.
diff --git a/hostsidetests/theme/android_device.py b/hostsidetests/theme/android_device.py
new file mode 100644
index 0000000..97b5fdd
--- /dev/null
+++ b/hostsidetests/theme/android_device.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python
+#
+# 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.
+#
+
+import os
+import re
+import sys
+import threading
+import subprocess
+import time
+
+# class for running android device from python
+# it will fork the device processor
+class androidDevice(object):
+ def __init__(self, adbDevice):
+ self._adbDevice = adbDevice
+
+ def runAdbCommand(self, cmd):
+ self.waitForAdbDevice()
+ adbCmd = "adb -s %s %s" %(self._adbDevice, cmd)
+ adbProcess = subprocess.Popen(adbCmd.split(" "), bufsize = -1, stdout = subprocess.PIPE)
+ return adbProcess.communicate()
+
+ def runShellCommand(self, cmd):
+ return self.runAdbCommand("shell " + cmd)
+
+ def waitForAdbDevice(self):
+ os.system("adb -s %s wait-for-device" %self._adbDevice)
+
+ def waitForBootComplete(self, timeout = 240):
+ boot_complete = False
+ attempts = 0
+ wait_period = 5
+ while not boot_complete and (attempts*wait_period) < timeout:
+ (output, err) = self.runShellCommand("getprop dev.bootcomplete")
+ output = output.strip()
+ if output == "1":
+ boot_complete = True
+ else:
+ time.sleep(wait_period)
+ attempts += 1
+ if not boot_complete:
+ print "***boot not complete within timeout. will proceed to the next step"
+ return boot_complete
+
+ def installApk(self, apkPath):
+ (out, err) = self.runAdbCommand("install -r -d -g " + apkPath)
+ result = out.split()
+ return (out, err, "Success" in result)
+
+ def uninstallApk(self, package):
+ (out, err) = self.runAdbCommand("uninstall " + package)
+ result = out.split()
+ return "Success" in result
+
+ def runInstrumentationTest(self, option):
+ return self.runShellCommand("am instrument -w " + option)
+
+ def isProcessAlive(self, processName):
+ (out, err) = self.runShellCommand("ps")
+ names = out.split()
+ # very lazy implementation as it does not filter out things like uid
+ # should work mostly unless processName is too simple to overlap with
+ # uid. So only use name like com.android.xyz
+ return processName in names
+
+ def getDensity(self):
+ if "emulator" in self._adbDevice:
+ return int(self.runShellCommand("getprop qemu.sf.lcd_density")[0])
+ else:
+ return int(self.runShellCommand("getprop ro.sf.lcd_density")[0])
+
+ def getSdkLevel(self):
+ return int(self.runShellCommand("getprop ro.build.version.sdk")[0])
+
+ def getOrientation(self):
+ return int(self.runShellCommand("dumpsys | grep SurfaceOrientation")[0].split()[1])
+
+def runAdbDevices():
+ devices = subprocess.check_output(["adb", "devices"])
+ devices = devices.split('\n')[1:]
+
+ deviceSerial = []
+
+ for device in devices:
+ if device is not "":
+ info = device.split('\t')
+ if info[1] == "device":
+ deviceSerial.append(info[0])
+
+ return deviceSerial
+
+if __name__ == '__main__':
+ main(sys.argv)
diff --git a/hostsidetests/theme/app/Android.mk b/hostsidetests/theme/app/Android.mk
index 70623cb..1be2983 100644
--- a/hostsidetests/theme/app/Android.mk
+++ b/hostsidetests/theme/app/Android.mk
@@ -26,6 +26,8 @@
LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
LOCAL_SRC_FILES := $(call all-java-files-under, src)
#Flags to tell the Android Asset Packaging Tool not to strip for some densities
diff --git a/hostsidetests/theme/app/AndroidManifest.xml b/hostsidetests/theme/app/AndroidManifest.xml
index 81a4d9d..4e81ab0 100755
--- a/hostsidetests/theme/app/AndroidManifest.xml
+++ b/hostsidetests/theme/app/AndroidManifest.xml
@@ -22,9 +22,10 @@
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
<application>
<uses-library android:name="android.test.runner" />
- <activity android:name=".HoloDeviceActivity">
+ <activity android:name=".ThemeDeviceActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
@@ -37,6 +38,13 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
+ <activity android:name=".GenerateImagesActivity"
+ android:exported="true" />
</application>
+ <!-- self-instrumenting test package. -->
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.theme.app"
+ android:label="Generates Theme reference images"/>
+
</manifest>
diff --git a/hostsidetests/theme/app/res/layout/holo_test.xml b/hostsidetests/theme/app/res/layout/theme_test.xml
similarity index 100%
rename from hostsidetests/theme/app/res/layout/holo_test.xml
rename to hostsidetests/theme/app/res/layout/theme_test.xml
diff --git a/hostsidetests/theme/app/res/values/strings.xml b/hostsidetests/theme/app/res/values/strings.xml
index a69a2e0..d9d6602 100644
--- a/hostsidetests/theme/app/res/values/strings.xml
+++ b/hostsidetests/theme/app/res/values/strings.xml
@@ -14,8 +14,6 @@
limitations under the License.
-->
<resources>
- <string name="holo_test_utilities">Holo Test Utilities</string>
-
<string name="display_info">Display Info</string>
<string name="display_info_text">Density DPI: %1$d\nDensity Bucket: %2$s\nWidth DP: %3$d\nHeight DP: %4$d</string>
diff --git a/hostsidetests/theme/app/src/android/theme/app/DisplayInfoActivity.java b/hostsidetests/theme/app/src/android/theme/app/DisplayInfoActivity.java
index 5255698..530675d 100644
--- a/hostsidetests/theme/app/src/android/theme/app/DisplayInfoActivity.java
+++ b/hostsidetests/theme/app/src/android/theme/app/DisplayInfoActivity.java
@@ -25,7 +25,8 @@
import android.widget.TextView;
/**
- * An activity to display information about the device, including density bucket and dimensions.
+ * An activity to display information about the device, including density
+ * bucket and dimensions.
*/
public class DisplayInfoActivity extends Activity {
diff --git a/hostsidetests/theme/app/src/android/theme/app/GenerateBitmapTask.java b/hostsidetests/theme/app/src/android/theme/app/GenerateBitmapTask.java
new file mode 100644
index 0000000..05b6dcf
--- /dev/null
+++ b/hostsidetests/theme/app/src/android/theme/app/GenerateBitmapTask.java
@@ -0,0 +1,97 @@
+/*
+ * 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.theme.app;
+
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.CompressFormat;
+import android.graphics.Canvas;
+import android.os.AsyncTask;
+import android.os.Environment;
+import android.util.Log;
+import android.view.View;
+
+import java.io.File;
+import java.io.FileOutputStream;
+
+/**
+ * A task which gets the UI element to render to a bitmap and then saves that
+ * as a PNG asynchronously.
+ */
+class GenerateBitmapTask extends AsyncTask<Void, Void, Boolean> {
+ private static final String TAG = "GenerateBitmapTask";
+
+ private final View mView;
+ private final File mOutDir;
+
+ private Bitmap mBitmap;
+
+ protected final String mName;
+
+ public GenerateBitmapTask(View view, File outDir, String name) {
+ mView = view;
+ mOutDir = outDir;
+ mName = name;
+ }
+
+ @Override
+ protected void onPreExecute() {
+ if (mView.getWidth() == 0 || mView.getHeight() == 0) {
+ Log.e(TAG, "Unable to draw view due to incorrect size: " + mName);
+ return;
+ }
+
+ mBitmap = Bitmap.createBitmap(mView.getWidth(), mView.getHeight(),
+ Bitmap.Config.ARGB_8888);
+
+ final Canvas canvas = new Canvas(mBitmap);
+ mView.draw(canvas);
+ }
+
+ @Override
+ protected Boolean doInBackground(Void... ignored) {
+ final Bitmap bitmap = mBitmap;
+ if (bitmap == null) {
+ return false;
+ }
+
+ final File file = new File(mOutDir, mName + ".png");
+ if (file.exists() && !file.canWrite()) {
+ Log.e(TAG, "Unable to write file: " + file.getAbsolutePath());
+ return false;
+ }
+
+ boolean success = false;
+ try {
+ FileOutputStream stream = null;
+ try {
+ stream = new FileOutputStream(file);
+ success = bitmap.compress(CompressFormat.PNG, 100, stream);
+ } finally {
+ if (stream != null) {
+ stream.flush();
+ stream.close();
+ }
+ }
+ } catch (Exception e) {
+ Log.e(TAG, e.getMessage());
+ } finally {
+ bitmap.recycle();
+ }
+
+ return success;
+ }
+}
diff --git a/hostsidetests/theme/app/src/android/theme/app/GenerateImagesActivity.java b/hostsidetests/theme/app/src/android/theme/app/GenerateImagesActivity.java
new file mode 100644
index 0000000..e7f5aa2
--- /dev/null
+++ b/hostsidetests/theme/app/src/android/theme/app/GenerateImagesActivity.java
@@ -0,0 +1,200 @@
+/*
+ * 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.theme.app;
+
+import android.Manifest.permission;
+import android.app.Activity;
+import android.app.KeyguardManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Build.VERSION;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.Handler;
+import android.util.Log;
+import android.view.WindowManager.LayoutParams;
+
+import java.io.File;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * Generates images by iterating through all themes and launching instances of
+ * {@link ThemeDeviceActivity}.
+ */
+public class GenerateImagesActivity extends Activity {
+ private static final String TAG = "GenerateImagesActivity";
+
+ private static final String OUT_DIR = "cts-theme-assets";
+ private static final int REQUEST_CODE = 1;
+
+ private final CountDownLatch mLatch = new CountDownLatch(1);
+
+ private File mOutputDir;
+ private int mCurrentTheme;
+ private String mFinishReason;
+ private boolean mFinishSuccess;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ getWindow().addFlags(LayoutParams.FLAG_KEEP_SCREEN_ON
+ | LayoutParams.FLAG_TURN_SCREEN_ON
+ | LayoutParams.FLAG_DISMISS_KEYGUARD);
+
+ mOutputDir = new File(Environment.getExternalStorageDirectory(), OUT_DIR);
+ ThemeTestUtils.deleteDirectory(mOutputDir);
+ mOutputDir.mkdirs();
+
+ if (!mOutputDir.exists()) {
+ finish("Failed to create output directory " + mOutputDir.getAbsolutePath(), false);
+ return;
+ }
+
+ final boolean canDisableKeyguard = checkCallingOrSelfPermission(
+ permission.DISABLE_KEYGUARD) == PackageManager.PERMISSION_GRANTED;
+ if (!canDisableKeyguard) {
+ finish("Not granted permission to disable keyguard", false);
+ return;
+ }
+
+ new KeyguardCheck(this) {
+ @Override
+ public void onSuccess() {
+ generateNextImage();
+ }
+
+ @Override
+ public void onFailure() {
+ finish("Device is locked", false);
+ }
+ }.start();
+ }
+
+ private void finish(String reason, boolean success) {
+ mFinishSuccess = success;
+ mFinishReason = reason;
+
+ Log.i(TAG, (success ? "OKAY" : "FAIL") + ":" + reason);
+ finish();
+ }
+
+ public boolean isFinishSuccess() {
+ return mFinishSuccess;
+ }
+
+ public String getFinishReason() {
+ return mFinishReason;
+ }
+
+ static abstract class KeyguardCheck implements Runnable {
+ private static final int MAX_RETRIES = 3;
+ private static final int RETRY_DELAY = 500;
+
+ private final Handler mHandler;
+ private final KeyguardManager mKeyguard;
+
+ private int mRetries;
+
+ public KeyguardCheck(Context context) {
+ mHandler = new Handler(context.getMainLooper());
+ mKeyguard = (KeyguardManager) context.getSystemService(KEYGUARD_SERVICE);
+ }
+
+ public void start() {
+ mRetries = 0;
+
+ mHandler.removeCallbacks(this);
+ mHandler.post(this);
+ }
+
+ public void cancel() {
+ mHandler.removeCallbacks(this);
+ }
+
+ @Override
+ public void run() {
+ if (!mKeyguard.isKeyguardLocked()) {
+ onSuccess();
+ } else if (mRetries < MAX_RETRIES) {
+ mRetries++;
+ mHandler.postDelayed(this, RETRY_DELAY);
+ } else {
+ onFailure();
+ }
+
+ }
+
+ public abstract void onSuccess();
+ public abstract void onFailure();
+ }
+
+ public File getOutputDir() {
+ return mOutputDir;
+ }
+
+ /**
+ * Starts the activity to generate the next image.
+ */
+ private boolean generateNextImage() {
+ final ThemeDeviceActivity.Theme theme = ThemeDeviceActivity.THEMES[mCurrentTheme];
+ if (theme.apiLevel > VERSION.SDK_INT) {
+ Log.v(TAG, "Skipping theme \"" + theme.name
+ + "\" (requires API " + theme.apiLevel + ")");
+ return false;
+ }
+
+ Log.v(TAG, "Generating images for theme \"" + theme.name + "\"...");
+
+ final Intent intent = new Intent(this, ThemeDeviceActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ intent.putExtra(ThemeDeviceActivity.EXTRA_THEME, mCurrentTheme);
+ intent.putExtra(ThemeDeviceActivity.EXTRA_OUTPUT_DIR, mOutputDir.getAbsolutePath());
+ startActivityForResult(intent, REQUEST_CODE);
+ return true;
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (resultCode != RESULT_OK) {
+ Log.i(TAG, "FAIL:Failed to generate images for theme " + mCurrentTheme);
+ finish();
+ return;
+ }
+
+ // Keep trying themes until one works.
+ boolean success = false;
+ while (++mCurrentTheme < ThemeDeviceActivity.THEMES.length && !success) {
+ success = generateNextImage();
+ }
+
+ // If we ran out of themes, we're done.
+ if (!success) {
+ finish("Image generation complete!", true);
+ }
+ }
+
+ public void finish() {
+ mLatch.countDown();
+ super.finish();
+ }
+
+ public void waitForCompletion() throws InterruptedException {
+ mLatch.await();
+ }
+}
diff --git a/hostsidetests/theme/app/src/android/theme/app/HoloDeviceActivity.java b/hostsidetests/theme/app/src/android/theme/app/HoloDeviceActivity.java
deleted file mode 100644
index 8ae9fc8..0000000
--- a/hostsidetests/theme/app/src/android/theme/app/HoloDeviceActivity.java
+++ /dev/null
@@ -1,331 +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.theme.app;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.CompressFormat;
-import android.graphics.Canvas;
-import android.os.AsyncTask;
-import android.os.Environment;
-import android.os.Bundle;
-import android.os.Handler;
-import android.theme.app.modifiers.DatePickerModifier;
-import android.theme.app.modifiers.ProgressBarModifier;
-import android.theme.app.modifiers.SearchViewModifier;
-import android.theme.app.modifiers.TimePickerModifier;
-import android.theme.app.modifiers.ViewCheckedModifier;
-import android.theme.app.modifiers.ViewPressedModifier;
-import android.theme.app.R;
-import android.theme.app.ReferenceViewGroup;
-import android.util.Log;
-import android.view.View;
-import android.widget.CheckBox;
-import android.widget.DatePicker;
-import android.widget.LinearLayout;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.lang.Override;
-
-/**
- * A activity which display various UI elements with Holo theme.
- */
-public class HoloDeviceActivity extends Activity {
-
- public static final String EXTRA_THEME = "holo_theme_extra";
-
- private static final String TAG = HoloDeviceActivity.class.getSimpleName();
-
- /**
- * The duration of the CalendarView adjustement to settle to its final position.
- */
- private static final long CALENDAR_VIEW_ADJUSTMENT_DURATION = 540;
-
- private Theme mTheme;
-
- private ReferenceViewGroup mViewGroup;
-
- private int mLayoutIndex;
-
- @Override
- protected void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- mTheme = THEMES[getIntent().getIntExtra(EXTRA_THEME, 0)];
- setTheme(mTheme.mId);
- setContentView(R.layout.holo_test);
- mViewGroup = (ReferenceViewGroup) findViewById(R.id.reference_view_group);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- setNextLayout();
- }
-
- @Override
- protected void onPause() {
- if (!isFinishing()) {
- // The Activity got paused for some reasons, for finish it as the host won't move on to
- // the next theme otherwise.
- Log.w(TAG, "onPause called without a call to finish().");
- finish();
- }
- super.onPause();
- }
-
- @Override
- protected void onDestroy() {
- if (mLayoutIndex != LAYOUTS.length) {
- Log.w(TAG, "Not all layouts got rendered: " + mLayoutIndex);
- }
- Log.i(TAG, "OKAY:" + mTheme.mName);
- super.onDestroy();
- }
-
- /**
- * Sets the next layout in the UI.
- */
- private void setNextLayout() {
- if (mLayoutIndex >= LAYOUTS.length) {
- finish();
- return;
- }
- final Layout layout = LAYOUTS[mLayoutIndex++];
- final String layoutName = String.format("%s_%s", mTheme.mName, layout.mName);
-
- mViewGroup.removeAllViews();
- final View view = getLayoutInflater().inflate(layout.mId, mViewGroup, false);
- if (layout.mModifier != null) {
- layout.mModifier.modifyView(view);
- }
- mViewGroup.addView(view);
- view.setFocusable(false);
-
- final Runnable generateBitmapRunnable = new Runnable() {
- @Override
- public void run() {
- new GenerateBitmapTask(view, layoutName).execute();
- }
- };
-
- if (view instanceof DatePicker) {
- // DatePicker uses a CalendarView that has a non-configurable adjustment duration of
- // 540ms
- view.postDelayed(generateBitmapRunnable, CALENDAR_VIEW_ADJUSTMENT_DURATION);
- } else {
- view.post(generateBitmapRunnable);
- }
- }
-
- /**
- * A task which gets the UI element to render to a bitmap and then saves that as a png
- * asynchronously
- */
- private class GenerateBitmapTask extends AsyncTask<Void, Void, Boolean> {
-
- private final View mView;
-
- private final String mName;
-
- public GenerateBitmapTask(final View view, final String name) {
- super();
- mView = view;
- mName = name;
- }
-
- @Override
- protected Boolean doInBackground(Void... ignored) {
- if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
- Log.i(TAG, "External storage for saving bitmaps is not mounted");
- return false;
- }
- if (mView.getWidth() == 0 || mView.getHeight() == 0) {
- Log.w(TAG, "Unable to draw View due to incorrect size: " + mName);
- return false;
- }
-
- final Bitmap bitmap = Bitmap.createBitmap(
- mView.getWidth(), mView.getHeight(), Bitmap.Config.ARGB_8888);
- final Canvas canvas = new Canvas(bitmap);
-
- mView.draw(canvas);
- final File dir = new File(Environment.getExternalStorageDirectory(), "cts-holo-assets");
- dir.mkdirs();
- boolean success = false;
- try {
- final File file = new File(dir, mName + ".png");
- FileOutputStream stream = null;
- try {
- stream = new FileOutputStream(file);
- success = bitmap.compress(CompressFormat.PNG, 100, stream);
- } finally {
- if (stream != null) {
- stream.close();
- }
- }
- } catch (Exception e) {
- Log.e(TAG, e.getMessage());
- } finally {
- bitmap.recycle();
- }
- return success;
- }
-
- @Override
- protected void onPostExecute(Boolean success) {
- setNextLayout();
- }
- }
-
- /**
- * A class to encapsulate information about a holo theme.
- */
- private static class Theme {
-
- public final int mId;
-
- public final String mName;
-
- private Theme(int id, String name) {
- mId = id;
- mName = name;
- }
- }
-
- private static final Theme[] THEMES = {
- new Theme(android.R.style.Theme_Holo,
- "holo"),
- new Theme(android.R.style.Theme_Holo_Dialog,
- "holo_dialog"),
- new Theme(android.R.style.Theme_Holo_Dialog_MinWidth,
- "holo_dialog_minwidth"),
- new Theme(android.R.style.Theme_Holo_Dialog_NoActionBar,
- "holo_dialog_noactionbar"),
- new Theme(android.R.style.Theme_Holo_Dialog_NoActionBar_MinWidth,
- "holo_dialog_noactionbar_minwidth"),
- new Theme(android.R.style.Theme_Holo_DialogWhenLarge,
- "holo_dialogwhenlarge"),
- new Theme(android.R.style.Theme_Holo_DialogWhenLarge_NoActionBar,
- "holo_dialogwhenlarge_noactionbar"),
- new Theme(android.R.style.Theme_Holo_InputMethod,
- "holo_inputmethod"),
- new Theme(android.R.style.Theme_Holo_Light,
- "holo_light"),
- new Theme(android.R.style.Theme_Holo_Light_DarkActionBar,
- "holo_light_darkactionbar"),
- new Theme(android.R.style.Theme_Holo_Light_Dialog,
- "holo_light_dialog"),
- new Theme(android.R.style.Theme_Holo_Light_Dialog_MinWidth,
- "holo_light_dialog_minwidth"),
- new Theme(android.R.style.Theme_Holo_Light_Dialog_NoActionBar,
- "holo_light_dialog_noactionbar"),
- new Theme(android.R.style.Theme_Holo_Light_Dialog_NoActionBar_MinWidth,
- "holo_light_dialog_noactionbar_minwidth"),
- new Theme(android.R.style.Theme_Holo_Light_DialogWhenLarge,
- "holo_light_dialogwhenlarge"),
- new Theme(android.R.style.Theme_Holo_Light_DialogWhenLarge_NoActionBar,
- "holo_light_dialogwhenlarge_noactionbar"),
- new Theme(android.R.style.Theme_Holo_Light_NoActionBar,
- "holo_light_noactionbar"),
- new Theme(android.R.style.Theme_Holo_Light_NoActionBar_Fullscreen,
- "holo_light_noactionbar_fullscreen"),
- new Theme(android.R.style.Theme_Holo_Light_Panel,
- "holo_light_panel"),
- new Theme(android.R.style.Theme_Holo_NoActionBar,
- "holo_noactionbar"),
- new Theme(android.R.style.Theme_Holo_NoActionBar_Fullscreen,
- "holo_noactionbar_fullscreen"),
- new Theme(android.R.style.Theme_Holo_Panel,
- "holo_panel"),
- new Theme(android.R.style.Theme_Holo_Wallpaper,
- "holo_wallpaper"),
- new Theme(android.R.style.Theme_Holo_Wallpaper_NoTitleBar,
- "holo_wallpaper_notitlebar")
- };
-
- /**
- * A class to encapsulate information about a holo layout.
- */
- private static class Layout {
-
- public final int mId;
-
- public final String mName;
-
- public final LayoutModifier mModifier;
-
- private Layout(int id, String name, LayoutModifier modifier) {
- mId = id;
- mName = name;
- mModifier = modifier;
- }
- }
-
- private static final Layout[] LAYOUTS = {
- new Layout(R.layout.button, "button", null),
- new Layout(R.layout.button, "button_pressed", new ViewPressedModifier()),
- new Layout(R.layout.checkbox, "checkbox", null),
- new Layout(R.layout.checkbox, "checkbox_checked", new ViewCheckedModifier()),
- new Layout(R.layout.chronometer, "chronometer", null),
- new Layout(R.layout.color_blue_bright, "color_blue_bright", null),
- new Layout(R.layout.color_blue_dark, "color_blue_dark", null),
- new Layout(R.layout.color_blue_light, "color_blue_light", null),
- new Layout(R.layout.color_green_dark, "color_green_dark", null),
- new Layout(R.layout.color_green_light, "color_green_light", null),
- new Layout(R.layout.color_orange_dark, "color_orange_dark", null),
- new Layout(R.layout.color_orange_light, "color_orange_light", null),
- new Layout(R.layout.color_purple, "color_purple", null),
- new Layout(R.layout.color_red_dark, "color_red_dark", null),
- new Layout(R.layout.color_red_light, "color_red_light", null),
- new Layout(R.layout.datepicker, "datepicker", new DatePickerModifier()),
- new Layout(R.layout.display_info, "display_info", null),
- new Layout(R.layout.edittext, "edittext", null),
- new Layout(R.layout.progressbar_horizontal_0, "progressbar_horizontal_0", null),
- new Layout(R.layout.progressbar_horizontal_100, "progressbar_horizontal_100", null),
- new Layout(R.layout.progressbar_horizontal_50, "progressbar_horizontal_50", null),
- new Layout(R.layout.progressbar_large, "progressbar_large", new ProgressBarModifier()),
- new Layout(R.layout.progressbar_small, "progressbar_small", new ProgressBarModifier()),
- new Layout(R.layout.progressbar, "progressbar", new ProgressBarModifier()),
- new Layout(R.layout.radiobutton_checked, "radiobutton_checked", null),
- new Layout(R.layout.radiobutton, "radiobutton", null),
- new Layout(R.layout.radiogroup_horizontal, "radiogroup_horizontal", null),
- new Layout(R.layout.radiogroup_vertical, "radiogroup_vertical", null),
- new Layout(R.layout.ratingbar_0, "ratingbar_0", null),
- new Layout(R.layout.ratingbar_2point5, "ratingbar_2point5", null),
- new Layout(R.layout.ratingbar_5, "ratingbar_5", null),
- new Layout(R.layout.ratingbar_0, "ratingbar_0_pressed", new ViewPressedModifier()),
- new Layout(R.layout.ratingbar_2point5, "ratingbar_2point5_pressed", new ViewPressedModifier()),
- new Layout(R.layout.ratingbar_5, "ratingbar_5_pressed", new ViewPressedModifier()),
- new Layout(R.layout.searchview, "searchview_query", new SearchViewModifier(SearchViewModifier.QUERY)),
- new Layout(R.layout.searchview, "searchview_query_hint", new SearchViewModifier(SearchViewModifier.QUERY_HINT)),
- new Layout(R.layout.seekbar_0, "seekbar_0", null),
- new Layout(R.layout.seekbar_100, "seekbar_100", null),
- new Layout(R.layout.seekbar_50, "seekbar_50", null),
- new Layout(R.layout.spinner, "spinner", null),
- new Layout(R.layout.switch_button_checked, "switch_button_checked", null),
- new Layout(R.layout.switch_button, "switch_button", null),
- new Layout(R.layout.textview, "textview", null),
- new Layout(R.layout.timepicker, "timepicker", new TimePickerModifier()),
- new Layout(R.layout.togglebutton_checked, "togglebutton_checked", null),
- new Layout(R.layout.togglebutton, "togglebutton", null),
- new Layout(R.layout.zoomcontrols, "zoomcontrols", null),
- };
-}
diff --git a/hostsidetests/theme/app/src/android/theme/app/ReferenceImagesTest.java b/hostsidetests/theme/app/src/android/theme/app/ReferenceImagesTest.java
new file mode 100644
index 0000000..7569252
--- /dev/null
+++ b/hostsidetests/theme/app/src/android/theme/app/ReferenceImagesTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.theme.app;
+
+import android.test.ActivityInstrumentationTestCase2;
+
+import java.io.File;
+
+/**
+ * Activity test case used to instrument generation of reference images.
+ */
+public class ReferenceImagesTest extends ActivityInstrumentationTestCase2<GenerateImagesActivity> {
+
+ public ReferenceImagesTest() {
+ super(GenerateImagesActivity.class);
+ }
+
+ public void testGenerateReferenceImages() throws Exception {
+ setActivityInitialTouchMode(true);
+
+ final GenerateImagesActivity activity = getActivity();
+ activity.waitForCompletion();
+
+ assertTrue(activity.getFinishReason(), activity.isFinishSuccess());
+
+ final File outputDir = activity.getOutputDir();
+ final File outputZip = new File(outputDir.getParentFile(), outputDir.getName() + ".zip");
+ if (outputZip.exists()) {
+ // Remove any old test results.
+ outputZip.delete();
+ }
+
+ ThemeTestUtils.compressDirectory(outputDir, outputZip);
+ ThemeTestUtils.deleteDirectory(outputDir);
+
+ assertTrue("Generated reference image ZIP", outputZip.exists());
+ }
+}
diff --git a/hostsidetests/theme/app/src/android/theme/app/ThemeDeviceActivity.java b/hostsidetests/theme/app/src/android/theme/app/ThemeDeviceActivity.java
new file mode 100644
index 0000000..d8b1f30
--- /dev/null
+++ b/hostsidetests/theme/app/src/android/theme/app/ThemeDeviceActivity.java
@@ -0,0 +1,414 @@
+/*
+ * 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.theme.app;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Bundle;
+import android.theme.app.modifiers.DatePickerModifier;
+import android.theme.app.modifiers.ProgressBarModifier;
+import android.theme.app.modifiers.SearchViewModifier;
+import android.theme.app.modifiers.TimePickerModifier;
+import android.theme.app.modifiers.ViewCheckedModifier;
+import android.theme.app.modifiers.ViewPressedModifier;
+import android.util.Log;
+import android.view.View;
+import android.view.WindowManager.LayoutParams;
+import android.widget.DatePicker;
+
+import java.io.File;
+import java.lang.Override;
+
+/**
+ * A activity which display various UI elements with non-modifiable themes.
+ */
+public class ThemeDeviceActivity extends Activity {
+ public static final String EXTRA_THEME = "theme";
+ public static final String EXTRA_OUTPUT_DIR = "outputDir";
+
+ private static final String TAG = "ThemeDeviceActivity";
+
+ /**
+ * The duration of the CalendarView adjustment to settle to its final
+ * position.
+ */
+ private static final long CALENDAR_VIEW_ADJUSTMENT_DURATION = 540;
+
+ private Theme mTheme;
+ private ReferenceViewGroup mViewGroup;
+ private File mOutputDir;
+ private int mLayoutIndex;
+ private boolean mIsRunning;
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ final Intent intent = getIntent();
+ final int themeIndex = intent.getIntExtra(EXTRA_THEME, -1);
+ if (themeIndex < 0) {
+ Log.e(TAG, "No theme specified");
+ finish();
+ }
+
+ final String outputDir = intent.getStringExtra(EXTRA_OUTPUT_DIR);
+ if (outputDir == null) {
+ Log.e(TAG, "No output directory specified");
+ finish();
+ }
+
+ mOutputDir = new File(outputDir);
+ mTheme = THEMES[themeIndex];
+
+ setTheme(mTheme.id);
+ setContentView(R.layout.theme_test);
+
+ mViewGroup = (ReferenceViewGroup) findViewById(R.id.reference_view_group);
+
+ getWindow().addFlags(LayoutParams.FLAG_KEEP_SCREEN_ON
+ | LayoutParams.FLAG_TURN_SCREEN_ON
+ | LayoutParams.FLAG_DISMISS_KEYGUARD );
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ mIsRunning = true;
+
+ setNextLayout();
+ }
+
+ @Override
+ protected void onPause() {
+ mIsRunning = false;
+
+ if (!isFinishing()) {
+ // The activity paused for some reason, likely a system crash
+ // dialog. Finish it so we can move to the next theme.
+ Log.w(TAG, "onPause() called without a call to finish()", new RuntimeException());
+ finish();
+ }
+
+ super.onPause();
+ }
+
+ @Override
+ protected void onDestroy() {
+ if (mLayoutIndex < LAYOUTS.length) {
+ Log.e(TAG, "Not all layouts got rendered: " + mLayoutIndex);
+ setResult(RESULT_CANCELED);
+ }
+
+ super.onDestroy();
+ }
+
+ /**
+ * Sets the next layout in the UI.
+ */
+ private void setNextLayout() {
+ if (mLayoutIndex >= LAYOUTS.length) {
+ setResult(RESULT_OK);
+ finish();
+ return;
+ }
+
+ mViewGroup.removeAllViews();
+
+ final Layout layout = LAYOUTS[mLayoutIndex++];
+ final String layoutName = String.format("%s_%s", mTheme.name, layout.name);
+ final View view = getLayoutInflater().inflate(layout.id, mViewGroup, false);
+ if (layout.modifier != null) {
+ layout.modifier.modifyView(view);
+ }
+
+ mViewGroup.addView(view);
+ view.setFocusable(false);
+
+ Log.v(TAG, "Rendering layout " + layoutName
+ + " (" + mLayoutIndex + "/" + LAYOUTS.length + ")");
+
+ final Runnable generateBitmapRunnable = new Runnable() {
+ @Override
+ public void run() {
+ new BitmapTask(view, layoutName).execute();
+ }
+ };
+
+ if (view instanceof DatePicker) {
+ // The Holo-styled DatePicker uses a CalendarView that has a
+ // non-configurable adjustment duration of 540ms.
+ view.postDelayed(generateBitmapRunnable, CALENDAR_VIEW_ADJUSTMENT_DURATION);
+ } else {
+ view.post(generateBitmapRunnable);
+ }
+ }
+
+ private class BitmapTask extends GenerateBitmapTask {
+ public BitmapTask(View view, String name) {
+ super(view, mOutputDir, name);
+ }
+
+ @Override
+ protected void onPostExecute(Boolean success) {
+ if (success && mIsRunning) {
+ setNextLayout();
+ } else {
+ Log.e(TAG, "Failed to render view to bitmap: " + mName + " (activity running? "
+ + mIsRunning + ")");
+ finish();
+ }
+ }
+ }
+
+ /**
+ * A class to encapsulate information about a theme.
+ */
+ static class Theme {
+ public final int id;
+ public final int apiLevel;
+ public final String name;
+
+ private Theme(int id, int apiLevel, String name) {
+ this.id = id;
+ this.apiLevel = apiLevel;
+ this.name = name;
+ }
+ }
+
+ // List of themes to verify.
+ static final Theme[] THEMES = {
+ // Holo
+ new Theme(android.R.style.Theme_Holo,
+ Build.VERSION_CODES.HONEYCOMB, "holo"),
+ new Theme(android.R.style.Theme_Holo_Dialog,
+ Build.VERSION_CODES.HONEYCOMB, "holo_dialog"),
+ new Theme(android.R.style.Theme_Holo_Dialog_MinWidth,
+ Build.VERSION_CODES.HONEYCOMB, "holo_dialog_minwidth"),
+ new Theme(android.R.style.Theme_Holo_Dialog_NoActionBar,
+ Build.VERSION_CODES.HONEYCOMB, "holo_dialog_noactionbar"),
+ new Theme(android.R.style.Theme_Holo_Dialog_NoActionBar_MinWidth,
+ Build.VERSION_CODES.HONEYCOMB, "holo_dialog_noactionbar_minwidth"),
+ new Theme(android.R.style.Theme_Holo_DialogWhenLarge,
+ Build.VERSION_CODES.HONEYCOMB, "holo_dialogwhenlarge"),
+ new Theme(android.R.style.Theme_Holo_DialogWhenLarge_NoActionBar,
+ Build.VERSION_CODES.HONEYCOMB, "holo_dialogwhenlarge_noactionbar"),
+ new Theme(android.R.style.Theme_Holo_InputMethod,
+ Build.VERSION_CODES.HONEYCOMB, "holo_inputmethod"),
+ new Theme(android.R.style.Theme_Holo_NoActionBar,
+ Build.VERSION_CODES.HONEYCOMB, "holo_noactionbar"),
+ new Theme(android.R.style.Theme_Holo_NoActionBar_Fullscreen,
+ Build.VERSION_CODES.HONEYCOMB, "holo_noactionbar_fullscreen"),
+ new Theme(android.R.style.Theme_Holo_NoActionBar_Overscan,
+ Build.VERSION_CODES.JELLY_BEAN_MR2, "holo_noactionbar_overscan"),
+ new Theme(android.R.style.Theme_Holo_NoActionBar_TranslucentDecor,
+ Build.VERSION_CODES.KITKAT, "holo_noactionbar_translucentdecor"),
+ new Theme(android.R.style.Theme_Holo_Panel,
+ Build.VERSION_CODES.HONEYCOMB, "holo_panel"),
+ new Theme(android.R.style.Theme_Holo_Wallpaper,
+ Build.VERSION_CODES.HONEYCOMB, "holo_wallpaper"),
+ new Theme(android.R.style.Theme_Holo_Wallpaper_NoTitleBar,
+ Build.VERSION_CODES.HONEYCOMB, "holo_wallpaper_notitlebar"),
+
+ // Holo Light
+ new Theme(android.R.style.Theme_Holo_Light,
+ Build.VERSION_CODES.HONEYCOMB, "holo_light"),
+ new Theme(android.R.style.Theme_Holo_Light_DarkActionBar,
+ Build.VERSION_CODES.ICE_CREAM_SANDWICH, "holo_light_darkactionbar"),
+ new Theme(android.R.style.Theme_Holo_Light_Dialog,
+ Build.VERSION_CODES.HONEYCOMB, "holo_light_dialog"),
+ new Theme(android.R.style.Theme_Holo_Light_Dialog_MinWidth,
+ Build.VERSION_CODES.HONEYCOMB, "holo_light_dialog_minwidth"),
+ new Theme(android.R.style.Theme_Holo_Light_Dialog_NoActionBar,
+ Build.VERSION_CODES.HONEYCOMB, "holo_light_dialog_noactionbar"),
+ new Theme(android.R.style.Theme_Holo_Light_Dialog_NoActionBar_MinWidth,
+ Build.VERSION_CODES.HONEYCOMB, "holo_light_dialog_noactionbar_minwidth"),
+ new Theme(android.R.style.Theme_Holo_Light_DialogWhenLarge,
+ Build.VERSION_CODES.HONEYCOMB, "holo_light_dialogwhenlarge"),
+ new Theme(android.R.style.Theme_Holo_Light_DialogWhenLarge_NoActionBar,
+ Build.VERSION_CODES.HONEYCOMB, "holo_light_dialogwhenlarge_noactionbar"),
+ new Theme(android.R.style.Theme_Holo_Light_NoActionBar,
+ Build.VERSION_CODES.HONEYCOMB_MR2, "holo_light_noactionbar"),
+ new Theme(android.R.style.Theme_Holo_Light_NoActionBar_Fullscreen,
+ Build.VERSION_CODES.HONEYCOMB_MR2, "holo_light_noactionbar_fullscreen"),
+ new Theme(android.R.style.Theme_Holo_Light_NoActionBar_Overscan,
+ Build.VERSION_CODES.JELLY_BEAN_MR2, "holo_light_noactionbar_overscan"),
+ new Theme(android.R.style.Theme_Holo_Light_NoActionBar_TranslucentDecor,
+ Build.VERSION_CODES.KITKAT, "holo_light_noactionbar_translucentdecor"),
+ new Theme(android.R.style.Theme_Holo_Light_Panel,
+ Build.VERSION_CODES.HONEYCOMB, "holo_light_panel"),
+
+ // Material
+ new Theme(android.R.style.Theme_Material,
+ Build.VERSION_CODES.LOLLIPOP, "material"),
+ new Theme(android.R.style.Theme_Material_Dialog,
+ Build.VERSION_CODES.LOLLIPOP, "material_dialog"),
+ new Theme(android.R.style.Theme_Material_Dialog_Alert,
+ Build.VERSION_CODES.LOLLIPOP, "material_dialog_alert"),
+ new Theme(android.R.style.Theme_Material_Dialog_MinWidth,
+ Build.VERSION_CODES.LOLLIPOP, "material_dialog_minwidth"),
+ new Theme(android.R.style.Theme_Material_Dialog_NoActionBar,
+ Build.VERSION_CODES.LOLLIPOP, "material_dialog_noactionbar"),
+ new Theme(android.R.style.Theme_Material_Dialog_NoActionBar_MinWidth,
+ Build.VERSION_CODES.LOLLIPOP, "material_dialog_noactionbar_minwidth"),
+ new Theme(android.R.style.Theme_Material_Dialog_Presentation,
+ Build.VERSION_CODES.LOLLIPOP, "material_dialog_presentation"),
+ new Theme(android.R.style.Theme_Material_DialogWhenLarge,
+ Build.VERSION_CODES.LOLLIPOP, "material_dialogwhenlarge"),
+ new Theme(android.R.style.Theme_Material_DialogWhenLarge_NoActionBar,
+ Build.VERSION_CODES.LOLLIPOP, "material_dialogwhenlarge_noactionbar"),
+ new Theme(android.R.style.Theme_Material_InputMethod,
+ Build.VERSION_CODES.LOLLIPOP, "material_inputmethod"),
+ new Theme(android.R.style.Theme_Material_NoActionBar,
+ Build.VERSION_CODES.LOLLIPOP, "material_noactionbar"),
+ new Theme(android.R.style.Theme_Material_NoActionBar_Fullscreen,
+ Build.VERSION_CODES.LOLLIPOP, "material_noactionbar_fullscreen"),
+ new Theme(android.R.style.Theme_Material_NoActionBar_Overscan,
+ Build.VERSION_CODES.LOLLIPOP, "material_noactionbar_overscan"),
+ new Theme(android.R.style.Theme_Material_NoActionBar_TranslucentDecor,
+ Build.VERSION_CODES.LOLLIPOP, "material_noactionbar_translucentdecor"),
+ new Theme(android.R.style.Theme_Material_Panel,
+ Build.VERSION_CODES.LOLLIPOP, "material_panel"),
+ new Theme(android.R.style.Theme_Material_Settings,
+ Build.VERSION_CODES.LOLLIPOP, "material_settings"),
+ new Theme(android.R.style.Theme_Material_Voice,
+ Build.VERSION_CODES.LOLLIPOP, "material_voice"),
+ new Theme(android.R.style.Theme_Material_Wallpaper,
+ Build.VERSION_CODES.LOLLIPOP, "material_wallpaper"),
+ new Theme(android.R.style.Theme_Material_Wallpaper_NoTitleBar,
+ Build.VERSION_CODES.LOLLIPOP, "material_wallpaper_notitlebar"),
+
+ // Material Light
+ new Theme(android.R.style.Theme_Material_Light,
+ Build.VERSION_CODES.LOLLIPOP, "material_light"),
+ new Theme(android.R.style.Theme_Material_Light_DarkActionBar,
+ Build.VERSION_CODES.LOLLIPOP, "material_light_darkactionbar"),
+ new Theme(android.R.style.Theme_Material_Light_Dialog,
+ Build.VERSION_CODES.LOLLIPOP, "material_light_dialog"),
+ new Theme(android.R.style.Theme_Material_Light_Dialog_Alert,
+ Build.VERSION_CODES.LOLLIPOP, "material_light_dialog_alert"),
+ new Theme(android.R.style.Theme_Material_Light_Dialog_MinWidth,
+ Build.VERSION_CODES.LOLLIPOP, "material_light_dialog_minwidth"),
+ new Theme(android.R.style.Theme_Material_Light_Dialog_NoActionBar,
+ Build.VERSION_CODES.LOLLIPOP, "material_light_dialog_noactionbar"),
+ new Theme(android.R.style.Theme_Material_Light_Dialog_NoActionBar_MinWidth,
+ Build.VERSION_CODES.LOLLIPOP, "material_light_dialog_noactionbar_minwidth"),
+ new Theme(android.R.style.Theme_Material_Light_Dialog_Presentation,
+ Build.VERSION_CODES.LOLLIPOP, "material_light_dialog_presentation"),
+ new Theme(android.R.style.Theme_Material_Light_DialogWhenLarge,
+ Build.VERSION_CODES.LOLLIPOP, "material_light_dialogwhenlarge"),
+ new Theme(android.R.style.Theme_Material_Light_DialogWhenLarge_NoActionBar,
+ Build.VERSION_CODES.LOLLIPOP, "material_light_dialogwhenlarge_noactionbar"),
+ new Theme(android.R.style.Theme_Material_Light_LightStatusBar,
+ Build.VERSION_CODES.M, "material_light_lightstatusbar"),
+ new Theme(android.R.style.Theme_Material_Light_NoActionBar,
+ Build.VERSION_CODES.LOLLIPOP, "material_light_noactionbar"),
+ new Theme(android.R.style.Theme_Material_Light_NoActionBar_Fullscreen,
+ Build.VERSION_CODES.LOLLIPOP, "material_light_noactionbar_fullscreen"),
+ new Theme(android.R.style.Theme_Material_Light_NoActionBar_Overscan,
+ Build.VERSION_CODES.LOLLIPOP, "material_light_noactionbar_overscan"),
+ new Theme(android.R.style.Theme_Material_Light_NoActionBar_TranslucentDecor,
+ Build.VERSION_CODES.LOLLIPOP, "material_light_noactionbar_translucentdecor"),
+ new Theme(android.R.style.Theme_Material_Light_Panel,
+ Build.VERSION_CODES.LOLLIPOP, "material_light_panel"),
+ new Theme(android.R.style.Theme_Material_Light_Voice,
+ Build.VERSION_CODES.LOLLIPOP, "material_light_voice")
+ };
+
+ /**
+ * A class to encapsulate information about a layout.
+ */
+ private static class Layout {
+ public final int id;
+ public final String name;
+ public final LayoutModifier modifier;
+
+ private Layout(int id, String name) {
+ this(id, name, null);
+ }
+
+ private Layout(int id, String name, LayoutModifier modifier) {
+ this.id = id;
+ this.name = name;
+ this.modifier = modifier;
+ }
+ }
+
+ // List of layouts to verify for each theme.
+ private static final Layout[] LAYOUTS = {
+ new Layout(R.layout.button, "button"),
+ new Layout(R.layout.button, "button_pressed",
+ new ViewPressedModifier()),
+ new Layout(R.layout.checkbox, "checkbox"),
+ new Layout(R.layout.checkbox, "checkbox_checked",
+ new ViewCheckedModifier()),
+ new Layout(R.layout.chronometer, "chronometer"),
+ new Layout(R.layout.color_blue_bright, "color_blue_bright"),
+ new Layout(R.layout.color_blue_dark, "color_blue_dark"),
+ new Layout(R.layout.color_blue_light, "color_blue_light"),
+ new Layout(R.layout.color_green_dark, "color_green_dark"),
+ new Layout(R.layout.color_green_light, "color_green_light"),
+ new Layout(R.layout.color_orange_dark, "color_orange_dark"),
+ new Layout(R.layout.color_orange_light, "color_orange_light"),
+ new Layout(R.layout.color_purple, "color_purple"),
+ new Layout(R.layout.color_red_dark, "color_red_dark"),
+ new Layout(R.layout.color_red_light, "color_red_light"),
+ new Layout(R.layout.datepicker, "datepicker",
+ new DatePickerModifier()),
+ new Layout(R.layout.display_info, "display_info"),
+ new Layout(R.layout.edittext, "edittext"),
+ new Layout(R.layout.progressbar_horizontal_0, "progressbar_horizontal_0"),
+ new Layout(R.layout.progressbar_horizontal_100, "progressbar_horizontal_100"),
+ new Layout(R.layout.progressbar_horizontal_50, "progressbar_horizontal_50"),
+ new Layout(R.layout.progressbar_large, "progressbar_large",
+ new ProgressBarModifier()),
+ new Layout(R.layout.progressbar_small, "progressbar_small",
+ new ProgressBarModifier()),
+ new Layout(R.layout.progressbar, "progressbar",
+ new ProgressBarModifier()),
+ new Layout(R.layout.radiobutton_checked, "radiobutton_checked"),
+ new Layout(R.layout.radiobutton, "radiobutton"),
+ new Layout(R.layout.radiogroup_horizontal, "radiogroup_horizontal"),
+ new Layout(R.layout.radiogroup_vertical, "radiogroup_vertical"),
+ new Layout(R.layout.ratingbar_0, "ratingbar_0"),
+ new Layout(R.layout.ratingbar_2point5, "ratingbar_2point5"),
+ new Layout(R.layout.ratingbar_5, "ratingbar_5"),
+ new Layout(R.layout.ratingbar_0, "ratingbar_0_pressed",
+ new ViewPressedModifier()),
+ new Layout(R.layout.ratingbar_2point5, "ratingbar_2point5_pressed",
+ new ViewPressedModifier()),
+ new Layout(R.layout.ratingbar_5, "ratingbar_5_pressed",
+ new ViewPressedModifier()),
+ new Layout(R.layout.searchview, "searchview_query",
+ new SearchViewModifier(SearchViewModifier.QUERY)),
+ new Layout(R.layout.searchview, "searchview_query_hint",
+ new SearchViewModifier(SearchViewModifier.QUERY_HINT)),
+ new Layout(R.layout.seekbar_0, "seekbar_0"),
+ new Layout(R.layout.seekbar_100, "seekbar_100"),
+ new Layout(R.layout.seekbar_50, "seekbar_50"),
+ new Layout(R.layout.spinner, "spinner"),
+ new Layout(R.layout.switch_button_checked, "switch_button_checked"),
+ new Layout(R.layout.switch_button, "switch_button"),
+ new Layout(R.layout.textview, "textview"),
+ new Layout(R.layout.timepicker, "timepicker",
+ new TimePickerModifier()),
+ new Layout(R.layout.togglebutton_checked, "togglebutton_checked"),
+ new Layout(R.layout.togglebutton, "togglebutton"),
+ new Layout(R.layout.zoomcontrols, "zoomcontrols"),
+ };
+}
diff --git a/hostsidetests/theme/app/src/android/theme/app/ThemeTestUtils.java b/hostsidetests/theme/app/src/android/theme/app/ThemeTestUtils.java
new file mode 100644
index 0000000..4daca6c
--- /dev/null
+++ b/hostsidetests/theme/app/src/android/theme/app/ThemeTestUtils.java
@@ -0,0 +1,80 @@
+/*
+ * 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.theme.app;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+public class ThemeTestUtils {
+
+ /**
+ * Compresses the contents of a directory to a ZIP file.
+ *
+ * @param dir the directory to compress
+ * @return {@code true} on success, {@code false} on failure
+ */
+ public static boolean compressDirectory(File dir, File file) throws IOException {
+ if (dir == null || !dir.exists() || file == null || file.exists()) {
+ return false;
+ }
+
+ final File[] srcFiles = dir.listFiles();
+ if (srcFiles.length == 0) {
+ return false;
+ }
+
+ final ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(file));
+ final byte[] data = new byte[4096];
+ for (int i = 0; i < srcFiles.length; i++) {
+ final FileInputStream fileIn = new FileInputStream(srcFiles[i]);
+ final ZipEntry entry = new ZipEntry(srcFiles[i].getName());
+ zipOut.putNextEntry(entry);
+
+ int count;
+ while ((count = fileIn.read(data, 0, data.length)) != -1) {
+ zipOut.write(data, 0, count);
+ zipOut.flush();
+ }
+
+ zipOut.closeEntry();
+ fileIn.close();
+ }
+
+ zipOut.close();
+ return true;
+ }
+
+ /**
+ * Recursively deletes a directory and its contents.
+ *
+ * @param dir the directory to delete
+ * @return {@code true} on success, {@code false} on failure
+ */
+ public static boolean deleteDirectory(File dir) {
+ final File files[] = dir.listFiles();
+ if (files != null) {
+ for (File file : files) {
+ deleteDirectory(file);
+ }
+ }
+ return dir.delete();
+ }
+}
diff --git a/hostsidetests/theme/assets/23/400dpi.zip b/hostsidetests/theme/assets/23/400dpi.zip
new file mode 100644
index 0000000..be0891f
--- /dev/null
+++ b/hostsidetests/theme/assets/23/400dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/23/560dpi.zip b/hostsidetests/theme/assets/23/560dpi.zip
new file mode 100644
index 0000000..cf0a559
--- /dev/null
+++ b/hostsidetests/theme/assets/23/560dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/23/hdpi.zip b/hostsidetests/theme/assets/23/hdpi.zip
new file mode 100644
index 0000000..80c12a7
--- /dev/null
+++ b/hostsidetests/theme/assets/23/hdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/23/ldpi.zip b/hostsidetests/theme/assets/23/ldpi.zip
new file mode 100644
index 0000000..937914a
--- /dev/null
+++ b/hostsidetests/theme/assets/23/ldpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/23/mdpi.zip b/hostsidetests/theme/assets/23/mdpi.zip
new file mode 100644
index 0000000..f842676
--- /dev/null
+++ b/hostsidetests/theme/assets/23/mdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/23/tvdpi.zip b/hostsidetests/theme/assets/23/tvdpi.zip
new file mode 100644
index 0000000..77386e5
--- /dev/null
+++ b/hostsidetests/theme/assets/23/tvdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/23/xhdpi.zip b/hostsidetests/theme/assets/23/xhdpi.zip
new file mode 100644
index 0000000..a8310d5
--- /dev/null
+++ b/hostsidetests/theme/assets/23/xhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/23/xxhdpi.zip b/hostsidetests/theme/assets/23/xxhdpi.zip
new file mode 100644
index 0000000..f88711f
--- /dev/null
+++ b/hostsidetests/theme/assets/23/xxhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/generate_images.sh b/hostsidetests/theme/generate_images.sh
new file mode 100755
index 0000000..9bcc3e5
--- /dev/null
+++ b/hostsidetests/theme/generate_images.sh
@@ -0,0 +1,55 @@
+#!/bin/sh
+# 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.
+
+# This script is used to generate reference images for the CTS theme tests.
+# See the accompanying README file for more information.
+
+# retry <command> <tries> <message> <delay>
+function retry {
+ RETRY="0"
+ while true; do
+ if (("$RETRY" >= "$2")); then
+ echo $OUTPUT
+ exit
+ fi
+
+ OUTPUT=`$1 |& grep error`
+
+ if [ -z "$OUTPUT" ]; then
+ break
+ fi
+
+ echo $3
+ sleep $4
+ RETRY=$[$RETRY + 1]
+ done
+}
+
+themeApkPath="$ANDROID_HOST_OUT/cts/android-cts/repository/testcases/CtsThemeDeviceApp.apk"
+outDir="$ANDROID_BUILD_TOP/cts/hostsidetests/theme/assets"
+exe="$ANDROID_BUILD_TOP/cts/hostsidetests/theme/run_theme_capture_device.py"
+
+if [ -z "$ANDROID_BUILD_TOP" ]; then
+ echo "Missing environment variables. Did you run build/envsetup.sh and lunch?"
+ exit
+fi
+
+if [ ! -e "$themeApkPath" ]; then
+ echo "Couldn't find test APK. Did you run make cts?"
+ exit
+fi
+
+adb devices
+python $exe $themeApkPath $outDir
diff --git a/hostsidetests/theme/run_theme_capture_device.py b/hostsidetests/theme/run_theme_capture_device.py
new file mode 100755
index 0000000..23171db
--- /dev/null
+++ b/hostsidetests/theme/run_theme_capture_device.py
@@ -0,0 +1,170 @@
+#!/usr/bin/env python
+#
+# 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.
+#
+
+import os
+import sys
+import threading
+import time
+import Queue
+sys.path.append(sys.path[0])
+from android_device import *
+
+CTS_THEME_dict = {
+ 120 : "ldpi",
+ 160 : "mdpi",
+ 213 : "tvdpi",
+ 240 : "hdpi",
+ 320 : "xhdpi",
+ 400 : "400dpi",
+ 480 : "xxhdpi",
+ 560 : "560dpi",
+ 640 : "xxxhdpi",
+}
+
+OUT_FILE = "/sdcard/cts-theme-assets.zip"
+
+# pass a function with number of instances to be executed in parallel
+# each thread continues until config q is empty.
+def executeParallel(tasks, setup, q, numberThreads):
+ class ParallelExecutor(threading.Thread):
+ def __init__(self, tasks, q):
+ threading.Thread.__init__(self)
+ self._q = q
+ self._tasks = tasks
+ self._setup = setup
+ self._result = 0
+
+ def run(self):
+ try:
+ while True:
+ config = q.get(block=True, timeout=2)
+ for t in self._tasks:
+ try:
+ if t(self._setup, config):
+ self._result += 1
+ except KeyboardInterrupt:
+ raise
+ except:
+ print "Failed to execute thread:", sys.exc_info()[0]
+ q.task_done()
+ except KeyboardInterrupt:
+ raise
+ except Queue.Empty:
+ pass
+
+ def getResult(self):
+ return self._result
+
+ result = 0;
+ threads = []
+ for i in range(numberThreads):
+ t = ParallelExecutor(tasks, q)
+ t.start()
+ threads.append(t)
+ for t in threads:
+ t.join()
+ result += t.getResult()
+ return result;
+
+def printAdbResult(device, out, err):
+ print "device: " + device
+ if out is not None:
+ print "out:\n" + out
+ if err is not None:
+ print "err:\n" + err
+
+def getResDir(outPath, resName):
+ resDir = outPath + "/" + resName
+ return resDir
+
+def doCapturing(setup, deviceSerial):
+ (themeApkPath, outPath) = setup
+
+ print "Found device: " + deviceSerial
+ device = androidDevice(deviceSerial)
+
+ # outPath = outPath + "/%d/" % (device.getSdkLevel()) + deviceSerial
+ outPath = outPath + "/%d" % (device.getSdkLevel())
+ density = device.getDensity()
+ resName = CTS_THEME_dict[density]
+
+ device.uninstallApk("android.theme.app")
+
+ (out, err, success) = device.installApk(themeApkPath)
+ if not success:
+ print "Failed to install APK on " + deviceSerial
+ printAdbResult(device, out, err)
+ return False
+
+ print "Generating images on " + deviceSerial + "..."
+ try:
+ (out, err) = device.runInstrumentationTest("android.theme.app/android.support.test.runner.AndroidJUnitRunner")
+ except KeyboardInterrupt:
+ raise
+ except:
+ (out, err) = device.runInstrumentationTest("android.theme.app/android.test.InstrumentationTestRunner")
+
+ # Detect test failure and abort.
+ if "FAILURES!!!" in out.split():
+ printAdbResult(deviceSerial, out, err)
+ return False
+
+ # Make sure that the run is complete by checking the process itself
+ print "Waiting for " + deviceSerial + "..."
+ waitTime = 0
+ while device.isProcessAlive("android.theme.app"):
+ time.sleep(1)
+ waitTime = waitTime + 1
+ if waitTime > 180:
+ print "Timed out"
+ break
+
+ time.sleep(10)
+ resDir = getResDir(outPath, resName)
+
+ print "Pulling images from " + deviceSerial + " to " + resDir + ".zip"
+ device.runAdbCommand("pull " + OUT_FILE + " " + resDir + ".zip")
+ device.runAdbCommand("shell rm -rf " + OUT_FILE)
+ return True
+
+def main(argv):
+ if len(argv) < 3:
+ print "run_theme_capture_device.py themeApkPath outDir"
+ sys.exit(1)
+ themeApkPath = argv[1]
+ outPath = os.path.abspath(argv[2])
+ os.system("mkdir -p " + outPath)
+
+ tasks = []
+ tasks.append(doCapturing)
+
+ devices = runAdbDevices();
+ numberThreads = len(devices)
+
+ configQ = Queue.Queue()
+ for device in devices:
+ configQ.put(device)
+ setup = (themeApkPath, outPath)
+ result = executeParallel(tasks, setup, configQ, numberThreads)
+
+ if result > 0:
+ print 'Generated reference images for %(count)d devices' % {"count": result}
+ else:
+ print 'Failed to generate reference images'
+
+if __name__ == '__main__':
+ main(sys.argv)
diff --git a/hostsidetests/theme/src/android/theme/cts/ComparisonTask.java b/hostsidetests/theme/src/android/theme/cts/ComparisonTask.java
old mode 100644
new mode 100755
index ba880d7..0e11111
--- a/hostsidetests/theme/src/android/theme/cts/ComparisonTask.java
+++ b/hostsidetests/theme/src/android/theme/cts/ComparisonTask.java
@@ -23,6 +23,7 @@
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
+import java.io.IOException;
import java.lang.String;
import java.util.concurrent.Callable;
@@ -32,59 +33,67 @@
* Compares the images generated by the device with the reference images.
*/
public class ComparisonTask implements Callable<Boolean> {
-
- private static final String TAG = ComparisonTask.class.getSimpleName();
+ private static final String TAG = "ComparisonTask";
private static final int IMAGE_THRESHOLD = 2;
- private static final String STORAGE_PATH_DEVICE = "/sdcard/cts-holo-assets/%s.png";
-
private final ITestDevice mDevice;
+ private final File mExpected;
+ private final File mActual;
- private final File mReference;
-
- private final String mName;
-
- public ComparisonTask(ITestDevice device, File reference, String name) {
+ public ComparisonTask(ITestDevice device, File expected, File actual) {
mDevice = device;
- mReference = reference;
- mName = name;
+ mExpected = expected;
+ mActual = actual;
}
public Boolean call() {
boolean success = false;
- File generated = null;
+
try {
- generated = File.createTempFile("gen_" + mName, ".png");
-
- final String remoteGenerated = String.format(STORAGE_PATH_DEVICE, mName);
- if (!mDevice.doesFileExist(remoteGenerated)) {
- Log.logAndDisplay(LogLevel.ERROR, TAG, "File " + remoteGenerated + " have not been saved on device");
- return false;
- }
- mDevice.pullFile(remoteGenerated, generated);
-
- final BufferedImage ref = ImageIO.read(mReference);
- final BufferedImage gen = ImageIO.read(generated);
- if (compare(ref, gen, IMAGE_THRESHOLD)) {
+ final BufferedImage expected = ImageIO.read(mExpected);
+ final BufferedImage actual = ImageIO.read(mActual);
+ if (compare(expected, actual, IMAGE_THRESHOLD)) {
success = true;
} else {
- File diff = File.createTempFile("diff_" + mName, ".png");
- createDiff(ref, gen, diff);
+ final File diff = File.createTempFile("diff_" + mExpected.getName(), ".png");
+ createDiff(expected, actual, diff);
Log.logAndDisplay(LogLevel.INFO, TAG, "Diff created: " + diff.getPath());
}
- } catch (Exception e) {
- Log.logAndDisplay(LogLevel.ERROR, TAG, String.format(STORAGE_PATH_DEVICE, mName));
+ } catch (IOException e) {
Log.logAndDisplay(LogLevel.ERROR, TAG, e.toString());
e.printStackTrace();
- } finally {
- if (generated != null) {
- generated.delete();
- }
}
+
return success;
}
+ /**
+ * Verifies that the pixels of reference and generated images are similar
+ * within a specified threshold.
+ *
+ * @param expected expected image
+ * @param actual actual image
+ * @param threshold maximum difference per channel
+ * @return {@code true} if the images are similar, false otherwise
+ */
+ private static int getAlphaScaledBlue(final int color) {
+ return (color & 0x000000FF) * getAlpha(color) / 255;
+ }
+
+ private static int getAlphaScaledGreen(final int color) {
+ return ((color & 0x0000FF00) >> 8) * getAlpha(color) / 255;
+ }
+
+ private static int getAlphaScaledRed(final int color) {
+ return ((color & 0x00FF0000) >> 16) * getAlpha(color) / 255;
+ }
+
+ private static int getAlpha(final int color) {
+ // use logical shift for keeping an unsigned value
+ return (color & 0xFF000000) >>> 24;
+ }
+
private static boolean compare(BufferedImage reference, BufferedImage generated, int threshold) {
final int w = generated.getWidth();
final int h = generated.getHeight();
@@ -96,15 +105,14 @@
for (int j = 0; j < h; j++) {
final int p1 = reference.getRGB(i, j);
final int p2 = generated.getRGB(i, j);
- final int dr = (p1 & 0x000000FF) - (p2 & 0x000000FF);
- final int dg = ((p1 & 0x0000FF00) - (p2 & 0x0000FF00)) >> 8;
- final int db = ((p1 & 0x00FF0000) - (p2 & 0x00FF0000)) >> 16;
- final int da = ((p1 & 0xFF000000) - (p2 & 0xFF000000)) >> 24;
+
+ final int dr = getAlphaScaledRed(p1) - getAlphaScaledRed(p2);
+ final int dg = getAlphaScaledGreen(p1) - getAlphaScaledGreen(p2);
+ final int db = getAlphaScaledBlue(p1) - getAlphaScaledBlue(p2);
if (Math.abs(db) > threshold ||
Math.abs(dg) > threshold ||
- Math.abs(dr) > threshold ||
- Math.abs(da) > threshold) {
+ Math.abs(dr) > threshold) {
return false;
}
}
@@ -112,45 +120,49 @@
return true;
}
- private static void createDiff(BufferedImage image1, BufferedImage image2, File out)
- throws Exception {
- final int w1 = image1.getWidth();
- final int h1 = image1.getHeight();
- final int w2 = image2.getWidth();
- final int h2 = image2.getHeight();
+ private static void createDiff(BufferedImage expected, BufferedImage actual, File out)
+ throws IOException {
+ final int w1 = expected.getWidth();
+ final int h1 = expected.getHeight();
+ final int w2 = actual.getWidth();
+ final int h2 = actual.getHeight();
final int width = Math.max(w1, w2);
final int height = Math.max(h1, h2);
+
// The diff will contain image1, image2 and the difference between the two.
- final BufferedImage diff = new BufferedImage(width * 3, height, BufferedImage.TYPE_INT_ARGB);
+ final BufferedImage diff = new BufferedImage(
+ width * 3, height, BufferedImage.TYPE_INT_ARGB);
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
final boolean inBounds1 = i < w1 && j < h1;
final boolean inBounds2 = i < w2 && j < h2;
- int color1 = Color.WHITE.getRGB();
- int color2 = Color.WHITE.getRGB();
- int color3;
+ int colorExpected = Color.WHITE.getRGB();
+ int colorActual = Color.WHITE.getRGB();
+ int colorDiff;
if (inBounds1 && inBounds2) {
- color1 = image1.getRGB(i, j);
- color2 = image2.getRGB(i, j);
- color3 = color1 == color2 ? color1 : Color.RED.getRGB();
+ colorExpected = expected.getRGB(i, j);
+ colorActual = actual.getRGB(i, j);
+ colorDiff = colorExpected == colorActual ? colorExpected : Color.RED.getRGB();
} else if (inBounds1 && !inBounds2) {
- color1 = image1.getRGB(i, j);
- color3 = Color.BLUE.getRGB();
+ colorExpected = expected.getRGB(i, j);
+ colorDiff = Color.BLUE.getRGB();
} else if (!inBounds1 && inBounds2) {
- color2 = image2.getRGB(i, j);
- color3 = Color.GREEN.getRGB();
+ colorActual = actual.getRGB(i, j);
+ colorDiff = Color.GREEN.getRGB();
} else {
- color3 = Color.MAGENTA.getRGB();
+ colorDiff = Color.MAGENTA.getRGB();
}
+
int x = i;
- diff.setRGB(x, j, color1);
+ diff.setRGB(x, j, colorExpected);
x += width;
- diff.setRGB(x, j, color2);
+ diff.setRGB(x, j, colorActual);
x += width;
- diff.setRGB(x, j, color3);
+ diff.setRGB(x, j, colorDiff);
}
}
+
ImageIO.write(diff, "png", out);
}
diff --git a/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java b/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java
index 8326b1f..b4bb748 100644
--- a/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java
+++ b/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java
@@ -21,8 +21,8 @@
import com.android.cts.util.TimeoutReq;
import com.android.ddmlib.Log;
import com.android.ddmlib.Log.LogLevel;
-import com.android.ddmlib.IShellOutputReceiver;
import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.testtype.DeviceTestCase;
import com.android.tradefed.testtype.IAbi;
@@ -30,145 +30,44 @@
import com.android.tradefed.testtype.IBuildReceiver;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileOutputStream;
+import java.io.IOException;
import java.io.InputStream;
import java.lang.String;
-import java.util.ArrayList;
import java.util.HashMap;
import java.util.Scanner;
-import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
/**
- * Test to check the Holo theme has not been changed.
+ * Test to check non-modifiable themes have not been changed.
*/
public class ThemeHostTest extends DeviceTestCase implements IAbiReceiver, IBuildReceiver {
+ private static final String LOG_TAG = "ThemeHostTest";
+ private static final String APK_NAME = "CtsThemeDeviceApp";
+ private static final String APP_PACKAGE_NAME = "android.theme.app";
- private static final String TAG = ThemeHostTest.class.getSimpleName();
-
- private static final int ADB_TIMEOUT = 60 * 60 * 1000;//60mins in ms
-
- /** The package name of the APK. */
- private static final String PACKAGE = "android.theme.app";
-
- /** The file name of the APK. */
- private static final String APK = "CtsThemeDeviceApp.apk";
+ private static final String GENERATED_ASSETS_ZIP = "/sdcard/cts-theme-assets.zip";
/** The class name of the main activity in the APK. */
- private static final String CLASS = "HoloDeviceActivity";
+ private static final String CLASS = "GenerateImagesActivity";
/** The command to launch the main activity. */
private static final String START_CMD = String.format(
- "am start -W -a android.intent.action.MAIN -n %s/%s.%s", PACKAGE, PACKAGE, CLASS);
+ "am start -W -a android.intent.action.MAIN -n %s/%s.%s", APP_PACKAGE_NAME,
+ APP_PACKAGE_NAME, CLASS);
- private static final String CLEAR_GENERATED_CMD = "rm -rf /sdcard/cts-holo-assets/*.png";
-
- private static final String STOP_CMD = String.format("am force-stop %s", PACKAGE);
-
+ private static final String CLEAR_GENERATED_CMD = "rm -rf %s/*.png";
+ private static final String STOP_CMD = String.format("am force-stop %s", APP_PACKAGE_NAME);
private static final String HARDWARE_TYPE_CMD = "dumpsys | grep android.hardware.type";
-
private static final String DENSITY_PROP_DEVICE = "ro.sf.lcd_density";
-
private static final String DENSITY_PROP_EMULATOR = "qemu.sf.lcd_density";
- // Intent extras
- protected final static String INTENT_STRING_EXTRA = " --es %s %s";
-
- protected final static String INTENT_BOOLEAN_EXTRA = " --ez %s %b";
-
- protected final static String INTENT_INTEGER_EXTRA = " --ei %s %d";
-
- // Intent extra keys
- private static final String EXTRA_THEME = "holo_theme_extra";
-
- private static final String[] THEMES = {
- "holo",
- "holo_dialog",
- "holo_dialog_minwidth",
- "holo_dialog_noactionbar",
- "holo_dialog_noactionbar_minwidth",
- "holo_dialogwhenlarge",
- "holo_dialogwhenlarge_noactionbar",
- "holo_inputmethod",
- "holo_light",
- "holo_light_darkactionbar",
- "holo_light_dialog",
- "holo_light_dialog_minwidth",
- "holo_light_dialog_noactionbar",
- "holo_light_dialog_noactionbar_minwidth",
- "holo_light_dialogwhenlarge",
- "holo_light_dialogwhenlarge_noactionbar",
- "holo_light_noactionbar",
- "holo_light_noactionbar_fullscreen",
- "holo_light_panel",
- "holo_noactionbar",
- "holo_noactionbar_fullscreen",
- "holo_panel",
- "holo_wallpaper",
- "holo_wallpaper_notitlebar",
- };
-
- private final int NUM_THEMES = THEMES.length;
-
- private static final String[] LAYOUTS = {
- "button",
- "button_pressed",
- "checkbox",
- "checkbox_checked",
- "chronometer",
- "color_blue_bright",
- "color_blue_dark",
- "color_blue_light",
- "color_green_dark",
- "color_green_light",
- "color_orange_dark",
- "color_orange_light",
- "color_purple",
- "color_red_dark",
- "color_red_light",
- "datepicker",
- "display_info",
- "edittext",
- "progressbar_horizontal_0",
- "progressbar_horizontal_100",
- "progressbar_horizontal_50",
- "progressbar_large",
- "progressbar_small",
- "progressbar",
- "radiobutton_checked",
- "radiobutton",
- "radiogroup_horizontal",
- "radiogroup_vertical",
- "ratingbar_0",
- "ratingbar_2point5",
- "ratingbar_5",
- "ratingbar_0_pressed",
- "ratingbar_2point5_pressed",
- "ratingbar_5_pressed",
- "searchview_query",
- "searchview_query_hint",
- "seekbar_0",
- "seekbar_100",
- "seekbar_50",
- "spinner",
- "switch_button_checked",
- "switch_button",
- "textview",
- "timepicker",
- "togglebutton_checked",
- "togglebutton",
- "zoomcontrols",
- };
-
- private final int NUM_LAYOUTS = LAYOUTS.length;
-
- private final HashMap<String, File> mReferences = new HashMap<String, File>();
+ private final HashMap<String, File> mReferences = new HashMap<>();
/** The ABI to use. */
private IAbi mAbi;
@@ -197,32 +96,21 @@
@Override
protected void setUp() throws Exception {
super.setUp();
- // Get the device, this gives a handle to run commands and install APKs.
+
mDevice = getDevice();
- // Remove any previously installed versions of this APK.
- mDevice.uninstallPackage(PACKAGE);
+ mDevice.uninstallPackage(APP_PACKAGE_NAME);
+
// Get the APK from the build.
- File app = mBuild.getTestApp(APK);
- // Get the ABI flag.
- String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
- // Install the APK on the device.
+ final File app = mBuild.getTestApp(String.format("%s.apk", APK_NAME));
+ final String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
+
mDevice.installPackage(app, false, options);
- // Remove previously generated images.
- mDevice.executeShellCommand(CLEAR_GENERATED_CMD);
- final String densityProp;
- if (mDevice.getSerialNumber().startsWith("emulator-")) {
- densityProp = DENSITY_PROP_EMULATOR;
- } else {
- densityProp = DENSITY_PROP_DEVICE;
- }
+ final String density = getDensityBucketForDevice(mDevice);
+ final String zipFile = String.format("/%s.zip", density);
+ Log.logAndDisplay(LogLevel.INFO, LOG_TAG, "Loading resources from " + zipFile);
- final String zip = String.format("/%s.zip",
- getDensityBucket(Integer.parseInt(mDevice.getProperty(densityProp))));
- Log.logAndDisplay(LogLevel.INFO, TAG, "Loading resources from " + zip);
-
-
- final InputStream zipStream = this.getClass().getResourceAsStream(zip);
+ final InputStream zipStream = ThemeHostTest.class.getResourceAsStream(zipFile);
if (zipStream != null) {
final ZipInputStream in = new ZipInputStream(zipStream);
try {
@@ -232,21 +120,28 @@
final String name = ze.getName();
final File tmp = File.createTempFile("ref_" + name, ".png");
final FileOutputStream out = new FileOutputStream(tmp);
+
int count;
while ((count = in.read(buffer)) != -1) {
out.write(buffer, 0, count);
}
+
out.flush();
out.close();
mReferences.put(name, tmp);
}
+ } catch (IOException e) {
+ Log.logAndDisplay(LogLevel.ERROR, LOG_TAG, "Failed to unzip assets: " + zipFile);
} finally {
in.close();
}
+ } else {
+ Log.logAndDisplay(LogLevel.ERROR, LOG_TAG, "Failed to get resource: " + zipFile);
}
- mExecutionService = Executors.newFixedThreadPool(2);// 2 worker threads
- mCompletionService = new ExecutorCompletionService<Boolean>(mExecutionService);
+ final int numCores = Runtime.getRuntime().availableProcessors();
+ mExecutionService = Executors.newFixedThreadPool(numCores * 2);
+ mCompletionService = new ExecutorCompletionService<>(mExecutionService);
}
@Override
@@ -255,86 +150,148 @@
for (File ref : mReferences.values()) {
ref.delete();
}
+
mExecutionService.shutdown();
+
// Remove the APK.
- mDevice.uninstallPackage(PACKAGE);
+ mDevice.uninstallPackage(APP_PACKAGE_NAME);
+
// Remove generated images.
mDevice.executeShellCommand(CLEAR_GENERATED_CMD);
+
super.tearDown();
}
@TimeoutReq(minutes = 60)
- public void testHoloThemes() throws Exception {
- if (checkHardwareTypeSkipTest(
- mDevice.executeShellCommand(HARDWARE_TYPE_CMD).trim())) {
- Log.logAndDisplay(LogLevel.INFO, TAG, "Skipped HoloThemes test for watch and TV");
+ public void testThemes() throws Exception {
+ if (checkHardwareTypeSkipTest(mDevice.executeShellCommand(HARDWARE_TYPE_CMD).trim())) {
+ Log.logAndDisplay(LogLevel.INFO, LOG_TAG, "Skipped themes test for watch");
return;
}
if (mReferences.isEmpty()) {
- Log.logAndDisplay(LogLevel.INFO, TAG,
- "Skipped HoloThemes test due to no reference images");
+ Log.logAndDisplay(LogLevel.INFO, LOG_TAG, "Skipped themes test due to no reference images");
return;
}
+ Log.logAndDisplay(LogLevel.INFO, LOG_TAG, "Generating device images...");
+
+ assertTrue("Aborted image generation", generateDeviceImages());
+
+ // Pull ZIP file from remote device.
+ final File localZip = File.createTempFile("generated", ".zip");
+ mDevice.pullFile(GENERATED_ASSETS_ZIP, localZip);
+
int numTasks = 0;
- for (int i = 0; i < NUM_THEMES; i++) {
- final String themeName = THEMES[i];
- runCapture(i, themeName);
- for (int j = 0; j < NUM_LAYOUTS; j++) {
- final String name = String.format("%s_%s", themeName, LAYOUTS[j]);
- final File ref = mReferences.get(name + ".png");
- if (!ref.exists()) {
- Log.logAndDisplay(LogLevel.INFO, TAG,
- "Skipping theme test due to missing reference for reference image " +
- name);
- continue;
+
+ Log.logAndDisplay(LogLevel.INFO, LOG_TAG, "Extracting generated images...");
+
+ // Extract generated images to temporary files.
+ final byte[] data = new byte[4096];
+ final ZipInputStream zipInput = new ZipInputStream(new FileInputStream(localZip));
+ ZipEntry entry;
+ while ((entry = zipInput.getNextEntry()) != null) {
+ final String name = entry.getName();
+ final File expected = mReferences.get(name);
+ if (expected != null && expected.exists()) {
+ final File actual = File.createTempFile("actual_" + name, ".png");
+ final FileOutputStream pngOutput = new FileOutputStream(actual);
+
+ int count;
+ while ((count = zipInput.read(data, 0, data.length)) != -1) {
+ pngOutput.write(data, 0, count);
}
- mCompletionService.submit(new ComparisonTask(mDevice, ref, name));
+
+ pngOutput.flush();
+ pngOutput.close();
+
+ mCompletionService.submit(new ComparisonTask(mDevice, expected, actual));
numTasks++;
+ } else {
+ Log.logAndDisplay(LogLevel.INFO, LOG_TAG, "Missing reference image for " + name);
}
+
+ zipInput.closeEntry();
}
+
+ zipInput.close();
+
+ Log.logAndDisplay(LogLevel.INFO, LOG_TAG, "Waiting for comparison tasks...");
+
int failures = 0;
- for (int i = 0; i < numTasks; i++) {
+ for (int i = numTasks; i > 0; i--) {
failures += mCompletionService.take().get() ? 0 : 1;
}
+
assertTrue(failures + " failures in theme test", failures == 0);
+
+ Log.logAndDisplay(LogLevel.INFO, LOG_TAG, "Finished!");
}
- private void runCapture(int themeId, String themeName) throws Exception {
- final StringBuilder sb = new StringBuilder(START_CMD);
- sb.append(String.format(INTENT_INTEGER_EXTRA, EXTRA_THEME, themeId));
- final String startCommand = sb.toString();
+ private boolean generateDeviceImages() throws Exception {
// Clear logcat
mDevice.executeAdbCommand("logcat", "-c");
+
// Stop any existing instances
mDevice.executeShellCommand(STOP_CMD);
- // Start activity
- mDevice.executeShellCommand(startCommand);
+ // Start activity
+ mDevice.executeShellCommand(START_CMD);
+
+ Log.logAndDisplay(LogLevel.VERBOSE, LOG_TAG, "Starting image generation...");
+
+ boolean aborted = false;
boolean waiting = true;
do {
// Dump logcat.
final String logs = mDevice.executeAdbCommand(
"logcat", "-v", "brief", "-d", CLASS + ":I", "*:S");
+
// Search for string.
final Scanner in = new Scanner(logs);
while (in.hasNextLine()) {
final String line = in.nextLine();
if (line.startsWith("I/" + CLASS)) {
final String[] lineSplit = line.split(":");
- final String s = lineSplit[1].trim();
- final String themeNameGenerated = lineSplit[2].trim();
- if (s.equals("OKAY") && themeNameGenerated.equals(themeName)) {
- waiting = false;
+ if (lineSplit.length >= 3) {
+ final String cmd = lineSplit[1].trim();
+ final String arg = lineSplit[2].trim();
+ switch (cmd) {
+ case "FAIL":
+ Log.logAndDisplay(LogLevel.WARN, LOG_TAG, line);
+ Log.logAndDisplay(LogLevel.WARN, LOG_TAG, "Aborting! Check host logs for details.");
+ aborted = true;
+ // fall-through
+ case "OKAY":
+ waiting = false;
+ break;
+ }
}
}
}
in.close();
- } while (waiting);
+ } while (waiting && !aborted);
+
+ Log.logAndDisplay(LogLevel.VERBOSE, LOG_TAG, "Image generation completed!");
+
+ return !aborted;
}
- private static String getDensityBucket(int density) {
+ private static String getDensityBucketForDevice(ITestDevice device) {
+ final String densityProp;
+ if (device.getSerialNumber().startsWith("emulator-")) {
+ densityProp = DENSITY_PROP_EMULATOR;
+ } else {
+ densityProp = DENSITY_PROP_DEVICE;
+ }
+
+ final int density;
+ try {
+ density = Integer.parseInt(device.getProperty(densityProp));
+ } catch (DeviceNotAvailableException e) {
+ return "unknown";
+ }
+
switch (density) {
case 120:
return "ldpi";
@@ -363,9 +320,7 @@
if (hardwareTypeString.contains("android.hardware.type.watch")) {
return true;
}
- if (hardwareTypeString.contains("android.hardware.type.television")) {
- return true;
- }
+
return false;
}
}
diff --git a/libs/deviceutil/src/android/cts/util/MediaUtils.java b/libs/deviceutil/src/android/cts/util/MediaUtils.java
index 7e2bfe3..b3ebbad 100755
--- a/libs/deviceutil/src/android/cts/util/MediaUtils.java
+++ b/libs/deviceutil/src/android/cts/util/MediaUtils.java
@@ -330,13 +330,34 @@
ex.release();
}
}
- return false;
+ return true;
}
public static boolean checkCodecsForPath(Context context, String path) {
return check(hasCodecsForPath(context, path), "no decoder found");
}
+ public static boolean hasCodecForDomain(boolean encoder, String domain) {
+ for (MediaCodecInfo info : sMCL.getCodecInfos()) {
+ if (encoder != info.isEncoder()) {
+ continue;
+ }
+
+ for (String type : info.getSupportedTypes()) {
+ if (type.toLowerCase().startsWith(domain.toLowerCase() + "/")) {
+ Log.i(TAG, "found codec " + info.getName() + " for mime " + type);
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public static boolean checkCodecForDomain(boolean encoder, String domain) {
+ return check(hasCodecForDomain(encoder, domain),
+ "no " + domain + (encoder ? " encoder" : " decoder") + " found");
+ }
+
private static boolean hasCodecForMime(boolean encoder, String mime) {
for (MediaCodecInfo info : sMCL.getCodecInfos()) {
if (encoder != info.isEncoder()) {
diff --git a/suite/cts/deviceTests/opengl/jni/graphics/Renderer.cpp b/suite/cts/deviceTests/opengl/jni/graphics/Renderer.cpp
index b8e2acf..16504fd 100644
--- a/suite/cts/deviceTests/opengl/jni/graphics/Renderer.cpp
+++ b/suite/cts/deviceTests/opengl/jni/graphics/Renderer.cpp
@@ -22,6 +22,14 @@
// Used to center the grid on the screen.
#define CENTER_GRID(x) ((((x) * 2.0 + 1) - OFFSCREEN_GRID_SIZE) / OFFSCREEN_GRID_SIZE)
+// Leave a good error message if something fails.
+#define EGL_RESULT_CHECK(X) do { \
+ EGLint error = eglGetError(); \
+ if (!(X) || error != EGL_SUCCESS) { \
+ ALOGE("EGL error '%d' at %s:%d", error, __FILE__, __LINE__);\
+ return false; \
+ } \
+ } while (0)
static const int FBO_NUM_VERTICES = 6;
@@ -66,7 +74,7 @@
EGL_NONE };
static const EGLint configAttribs[] = {
- EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
@@ -78,53 +86,60 @@
static const int FBO_SIZE = 128;
-Renderer::Renderer(ANativeWindow* window, bool offscreen, int workload) :
- mOffscreen(offscreen), mWindow(window), mEglDisplay(EGL_NO_DISPLAY),
- mEglSurface(EGL_NO_SURFACE), mEglContext(EGL_NO_CONTEXT), mWorkload(workload) {
+Renderer::Renderer(EGLNativeWindowType window, bool offscreen) :
+ mOffscreen(offscreen), mEglDisplay(EGL_NO_DISPLAY), mEglSurface(EGL_NO_SURFACE),
+ mEglContext(EGL_NO_CONTEXT), mWindow(window) {
}
-bool Renderer::setUp() {
+bool Renderer::eglSetUp() {
SCOPED_TRACE();
mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
- if (EGL_NO_DISPLAY == mEglDisplay || EGL_SUCCESS != eglGetError()) {
- return false;
- }
+ EGL_RESULT_CHECK(mEglDisplay != EGL_NO_DISPLAY);
EGLint major;
EGLint minor;
- if (!eglInitialize(mEglDisplay, &major, &minor) || EGL_SUCCESS != eglGetError()) {
- return false;
- }
+ EGL_RESULT_CHECK(eglInitialize(mEglDisplay, &major, &minor));
EGLint numConfigs = 0;
- if (!eglChooseConfig(mEglDisplay, configAttribs, &mGlConfig, 1, &numConfigs)
- || EGL_SUCCESS != eglGetError()) {
- return false;
- }
+ EGL_RESULT_CHECK(eglChooseConfig(mEglDisplay, configAttribs, &mGlConfig, 1, &numConfigs)
+ && (numConfigs > 0));
mEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig, mWindow, NULL);
- if (EGL_NO_SURFACE == mEglSurface || EGL_SUCCESS != eglGetError()) {
- return false;
- }
+ EGL_RESULT_CHECK(mEglSurface != EGL_NO_SURFACE);
mEglContext = eglCreateContext(mEglDisplay, mGlConfig, EGL_NO_CONTEXT, contextAttribs);
- if (EGL_NO_CONTEXT == mEglContext || EGL_SUCCESS != eglGetError()) {
- return false;
+ EGL_RESULT_CHECK(mEglContext != EGL_NO_CONTEXT);
+
+ EGL_RESULT_CHECK(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext));
+ EGL_RESULT_CHECK(eglQuerySurface(mEglDisplay, mEglSurface, EGL_WIDTH, &mWidth));
+ EGL_RESULT_CHECK(eglQuerySurface(mEglDisplay, mEglSurface, EGL_HEIGHT, &mHeight));
+
+ return true;
+}
+
+void Renderer::eglTearDown() {
+ SCOPED_TRACE();
+ eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+
+ if (mEglContext != EGL_NO_CONTEXT) {
+ eglDestroyContext(mEglDisplay, mEglContext);
+ mEglContext = EGL_NO_CONTEXT;
}
- if (!eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)
- || EGL_SUCCESS != eglGetError()) {
- return false;
+ if (mEglSurface != EGL_NO_SURFACE) {
+ mEglSurface = EGL_NO_SURFACE;
}
- if (!eglQuerySurface(mEglDisplay, mEglSurface, EGL_WIDTH, &mWidth)
- || EGL_SUCCESS != eglGetError()) {
- return false;
+ if (mEglDisplay != EGL_NO_DISPLAY) {
+ eglTerminate(mEglDisplay);
+ mEglDisplay = EGL_NO_DISPLAY;
}
- if (!eglQuerySurface(mEglDisplay, mEglSurface, EGL_HEIGHT, &mHeight)
- || EGL_SUCCESS != eglGetError()) {
- return false;
- }
+}
+
+bool Renderer::setUp(int /*workload*/) {
+ SCOPED_TRACE();
+
+ EGL_RESULT_CHECK(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext));
if (mOffscreen) {
mFboWidth = FBO_SIZE;
@@ -178,6 +193,7 @@
ALOGE("GLError %d in setUp", err);
return false;
}
+
return true;
}
@@ -202,29 +218,14 @@
ALOGE("GLError %d in tearDown", err);
return false;
}
- if (mEglContext != EGL_NO_CONTEXT) {
- eglDestroyContext(mEglDisplay, mEglContext);
- mEglContext = EGL_NO_CONTEXT;
- }
- if (mEglSurface != EGL_NO_SURFACE) {
- eglDestroySurface(mEglDisplay, mEglSurface);
- mEglSurface = EGL_NO_SURFACE;
- }
- if (mEglDisplay != EGL_NO_DISPLAY) {
- eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
- eglTerminate(mEglDisplay);
- mEglDisplay = EGL_NO_DISPLAY;
- }
- return EGL_SUCCESS == eglGetError();
+ return true;
}
bool Renderer::draw() {
SCOPED_TRACE();
- if (!eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)
- || EGL_SUCCESS != eglGetError()) {
- return false;
- }
+
+ EGL_RESULT_CHECK(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext));
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(0, 0, mWidth, mHeight);
@@ -287,5 +288,6 @@
return false;
}
- return eglSwapBuffers(mEglDisplay, mEglSurface);
+ EGL_RESULT_CHECK(eglSwapBuffers(mEglDisplay, mEglSurface));
+ return true;
}
diff --git a/suite/cts/deviceTests/opengl/jni/graphics/Renderer.h b/suite/cts/deviceTests/opengl/jni/graphics/Renderer.h
index caa1634..3c62a26 100644
--- a/suite/cts/deviceTests/opengl/jni/graphics/Renderer.h
+++ b/suite/cts/deviceTests/opengl/jni/graphics/Renderer.h
@@ -14,17 +14,18 @@
#ifndef RENDERER_H
#define RENDERER_H
-#include <android/native_window.h>
-
#include <EGL/egl.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
class Renderer {
public:
- Renderer(ANativeWindow* window, bool offscreen, int workload);
- virtual bool setUp();
+ Renderer(EGLNativeWindowType window, bool offscreen);
+ virtual bool setUp(int workload);
virtual bool tearDown();
+ bool eglSetUp();
+ void eglTearDown();
+
bool draw();
virtual void drawWorkload() = 0;
virtual ~Renderer() {};
@@ -32,7 +33,6 @@
static const int OFFSCREEN_GRID_SIZE = 10;
bool mOffscreen;
protected:
- ANativeWindow* mWindow;
EGLDisplay mEglDisplay;
EGLSurface mEglSurface;
EGLContext mEglContext;
@@ -40,7 +40,6 @@
GLuint mProgramId;
EGLint mWidth;
EGLint mHeight;
- int mWorkload;
int mFboWidth;// Frame buffer width
int mFboHeight;// Frame buffer height
GLuint mFboId;// Frame buffer id
@@ -52,5 +51,7 @@
GLuint mFboYOffsetUniformHandle;// Frame buffer y offset uniform handle
GLuint mFboPositionHandle;// Frame buffer position handle
GLuint mFboTexCoordHandle;// Frame buffer texture coordinate handle
+private:
+ EGLNativeWindowType mWindow;
};
#endif
diff --git a/suite/cts/deviceTests/opengl/jni/primitive/GLPrimitive.cpp b/suite/cts/deviceTests/opengl/jni/primitive/GLPrimitive.cpp
index 9d39af9..856da1e 100644
--- a/suite/cts/deviceTests/opengl/jni/primitive/GLPrimitive.cpp
+++ b/suite/cts/deviceTests/opengl/jni/primitive/GLPrimitive.cpp
@@ -28,16 +28,24 @@
// Holds the current benchmark's renderer.
Renderer* gRenderer = NULL;
+ANativeWindow* gNativeWindow = NULL;
+
+enum {
+ FULL_PIPELINE_BENCHMARK = 0,
+ PIXEL_OUTPUT_BENCHMARK = 1,
+ SHADER_PERF_BENCHMARK = 2,
+ CONTEXT_SWITCH_BENCHMARK = 3
+};
extern "C" JNIEXPORT jboolean JNICALL
Java_com_android_cts_opengl_primitive_GLPrimitiveActivity_startBenchmark(
- JNIEnv* env, jclass clazz, jint numFrames, jdoubleArray frameTimes) {
+ JNIEnv* env, jclass /*clazz*/, jint workload, jint numFrames, jdoubleArray frameTimes) {
if (gRenderer == NULL) {
return false;
}
// Sets up the renderer.
- bool success = gRenderer->setUp();
+ bool success = gRenderer->setUp(workload);
// Records the start time.
double start = GLUtils::currentTimeMillis();
@@ -60,41 +68,56 @@
double times[] = {start, end};
env->SetDoubleArrayRegion(frameTimes, 0, 2, times);
- // Tears down and deletes the renderer.
success = gRenderer->tearDown() && success;
- delete gRenderer;
- gRenderer = NULL;
return success;
}
// The following functions create the renderers for the various benchmarks.
extern "C" JNIEXPORT void JNICALL
-Java_com_android_cts_opengl_primitive_GLPrimitiveActivity_setupFullPipelineBenchmark(
- JNIEnv* env, jclass clazz, jobject surface, jboolean offscreen, jint workload) {
- gRenderer = new FullPipelineRenderer(
- ANativeWindow_fromSurface(env, surface), offscreen, workload);
-}
-
-extern "C" JNIEXPORT void JNICALL
-Java_com_android_cts_opengl_primitive_GLPrimitiveActivity_setupPixelOutputBenchmark(
- JNIEnv* env, jclass clazz, jobject surface, jboolean offscreen, jint workload) {
- gRenderer = new PixelOutputRenderer(
- ANativeWindow_fromSurface(env, surface), offscreen, workload);
-}
-
-extern "C" JNIEXPORT void JNICALL
-Java_com_android_cts_opengl_primitive_GLPrimitiveActivity_setupShaderPerfBenchmark(
- JNIEnv* env, jclass clazz, jobject surface, jboolean offscreen, jint workload) {
- gRenderer = new ShaderPerfRenderer(
- ANativeWindow_fromSurface(env, surface), offscreen, workload);
-}
-
-extern "C" JNIEXPORT void JNICALL
-Java_com_android_cts_opengl_primitive_GLPrimitiveActivity_setupContextSwitchBenchmark(
- JNIEnv* env, jclass clazz, jobject surface, jboolean offscreen, jint workload) {
- if (workload <= 8) {
- // This test uses 8 iterations, so workload can't be more than 8.
- gRenderer = new ContextSwitchRenderer(
- ANativeWindow_fromSurface(env, surface), offscreen, workload);
+Java_com_android_cts_opengl_primitive_GLPrimitiveActivity_setupBenchmark(
+ JNIEnv* env, jclass /*clazz*/, jobject surface, jint benchmark,
+ jboolean offscreen) {
+ gNativeWindow = ANativeWindow_fromSurface(env, surface);
+ switch (benchmark) {
+ case FULL_PIPELINE_BENCHMARK:
+ gRenderer = new FullPipelineRenderer(gNativeWindow, offscreen);
+ break;
+ case PIXEL_OUTPUT_BENCHMARK:
+ gRenderer = new PixelOutputRenderer(gNativeWindow, offscreen);
+ break;
+ case SHADER_PERF_BENCHMARK:
+ gRenderer = new ShaderPerfRenderer(gNativeWindow, offscreen);
+ break;
+ case CONTEXT_SWITCH_BENCHMARK:
+ gRenderer = new ContextSwitchRenderer(gNativeWindow, offscreen);
+ break;
+ default:
+ ALOGE("Unknown benchmark '%d'", benchmark);
+ ANativeWindow_release(gNativeWindow);
+ gNativeWindow = NULL;
+ return;
}
+
+ // Set up call will log error conditions
+ if (!gRenderer->eglSetUp()) {
+ delete gRenderer;
+ gRenderer = NULL;
+
+ ANativeWindow_release(gNativeWindow);
+ gNativeWindow = NULL;
+ }
+}
+
+extern "C" JNIEXPORT void JNICALL
+Java_com_android_cts_opengl_primitive_GLPrimitiveActivity_tearDownBenchmark(
+ JNIEnv* /*env*/, jclass /*clazz*/) {
+ if (gRenderer == NULL) {
+ return;
+ }
+ gRenderer->eglTearDown();
+ delete gRenderer;
+ gRenderer = NULL;
+
+ ANativeWindow_release(gNativeWindow);
+ gNativeWindow = NULL;
}
diff --git a/suite/cts/deviceTests/opengl/jni/primitive/contextswitch/ContextSwitchRenderer.cpp b/suite/cts/deviceTests/opengl/jni/primitive/contextswitch/ContextSwitchRenderer.cpp
index 7fd4093..1127d88 100644
--- a/suite/cts/deviceTests/opengl/jni/primitive/contextswitch/ContextSwitchRenderer.cpp
+++ b/suite/cts/deviceTests/opengl/jni/primitive/contextswitch/ContextSwitchRenderer.cpp
@@ -74,13 +74,14 @@
" gl_FragColor = texture2D(u_Texture, v_TexCoord);"
"}";
-ContextSwitchRenderer::ContextSwitchRenderer(ANativeWindow* window, bool offscreen, int workload) :
- Renderer(window, offscreen, workload), mContexts(NULL) {
+ContextSwitchRenderer::ContextSwitchRenderer(ANativeWindow* window, bool offscreen) :
+ Renderer(window, offscreen), mContexts(NULL), mWorkload(0) {
}
-bool ContextSwitchRenderer::setUp() {
+bool ContextSwitchRenderer::setUp(int workload) {
SCOPED_TRACE();
- if (!Renderer::setUp()) {
+ mWorkload = workload;
+ if (!Renderer::setUp(workload)) {
return false;
}
@@ -137,7 +138,7 @@
bool ContextSwitchRenderer::tearDown() {
SCOPED_TRACE();
if (mContexts) {
- // Destroy the contexts, the main one will be handled by Renderer::tearDown().
+ // Destroy the contexts, the main one will be handled by Renderer::eglTearDown().
for (int i = 0; i < NUM_WORKER_CONTEXTS; i++) {
if (mOffscreen) {
if (mFboIds[i] != 0) {
@@ -146,6 +147,7 @@
mFboIds[i] = 0;
}
}
+ eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroyContext(mEglDisplay, mContexts[i]);
}
delete[] mContexts;
@@ -163,6 +165,11 @@
void ContextSwitchRenderer::drawWorkload() {
SCOPED_TRACE();
+
+ if (mWorkload > 8) {
+ return; // This test does not support higher workloads.
+ }
+
// Set the background clear color to black.
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
@@ -216,6 +223,7 @@
}
}
+ eglWaitSyncKHR(mEglDisplay, fence, 0);
eglDestroySyncKHR(mEglDisplay, fence);
// Switch back to the main context.
diff --git a/suite/cts/deviceTests/opengl/jni/primitive/contextswitch/ContextSwitchRenderer.h b/suite/cts/deviceTests/opengl/jni/primitive/contextswitch/ContextSwitchRenderer.h
index 51a4376..ae320ff 100644
--- a/suite/cts/deviceTests/opengl/jni/primitive/contextswitch/ContextSwitchRenderer.h
+++ b/suite/cts/deviceTests/opengl/jni/primitive/contextswitch/ContextSwitchRenderer.h
@@ -18,9 +18,9 @@
class ContextSwitchRenderer: public Renderer {
public:
- ContextSwitchRenderer(ANativeWindow* window, bool offscreen, int workload);
+ ContextSwitchRenderer(ANativeWindow* window, bool offscreen);
virtual ~ContextSwitchRenderer() {};
- bool setUp();
+ bool setUp(int workload);
bool tearDown();
void drawWorkload();
private:
@@ -31,6 +31,7 @@
GLuint mTranslateUniformHandle;
GLuint mPositionHandle;
GLuint mTexCoordHandle;
+ int mWorkload;
};
#endif
diff --git a/suite/cts/deviceTests/opengl/jni/primitive/fullpipeline/FullPipelineRenderer.cpp b/suite/cts/deviceTests/opengl/jni/primitive/fullpipeline/FullPipelineRenderer.cpp
index 97462b5..0f75f81 100644
--- a/suite/cts/deviceTests/opengl/jni/primitive/fullpipeline/FullPipelineRenderer.cpp
+++ b/suite/cts/deviceTests/opengl/jni/primitive/fullpipeline/FullPipelineRenderer.cpp
@@ -91,15 +91,15 @@
" gl_FragColor = (diffuse * texture2D(u_Texture, v_TexCoordinate));\n"
"}";
-FullPipelineRenderer::FullPipelineRenderer(ANativeWindow* window, bool offscreen, int workload) :
- Renderer(window, offscreen, workload), mProgram(NULL), mSceneGraph(NULL),
+FullPipelineRenderer::FullPipelineRenderer(ANativeWindow* window, bool offscreen) :
+ Renderer(window, offscreen), mProgram(NULL), mSceneGraph(NULL),
mModelMatrix(NULL), mViewMatrix(NULL), mProjectionMatrix(NULL), mMesh(NULL),
mTextureId(0) {
}
-bool FullPipelineRenderer::setUp() {
+bool FullPipelineRenderer::setUp(int workload) {
SCOPED_TRACE();
- if (!Renderer::setUp()) {
+ if (!Renderer::setUp(workload)) {
return false;
}
@@ -147,7 +147,7 @@
return false;
}
- float count = mWorkload * mWorkload;
+ float count = workload * workload;
float middle = count / 2.0f;
float scale = 2.0f / count;
diff --git a/suite/cts/deviceTests/opengl/jni/primitive/fullpipeline/FullPipelineRenderer.h b/suite/cts/deviceTests/opengl/jni/primitive/fullpipeline/FullPipelineRenderer.h
index 84616b4..ce44760 100644
--- a/suite/cts/deviceTests/opengl/jni/primitive/fullpipeline/FullPipelineRenderer.h
+++ b/suite/cts/deviceTests/opengl/jni/primitive/fullpipeline/FullPipelineRenderer.h
@@ -22,9 +22,9 @@
class FullPipelineRenderer: public Renderer {
public:
- FullPipelineRenderer(ANativeWindow* window, bool offscreen, int workload);
+ FullPipelineRenderer(ANativeWindow* window, bool offscreen);
virtual ~FullPipelineRenderer() {};
- bool setUp();
+ bool setUp(int workload);
bool tearDown();
void drawWorkload();
private:
diff --git a/suite/cts/deviceTests/opengl/jni/primitive/pixeloutput/PixelOutputRenderer.cpp b/suite/cts/deviceTests/opengl/jni/primitive/pixeloutput/PixelOutputRenderer.cpp
index 287ebfb..3a3b9d1 100644
--- a/suite/cts/deviceTests/opengl/jni/primitive/pixeloutput/PixelOutputRenderer.cpp
+++ b/suite/cts/deviceTests/opengl/jni/primitive/pixeloutput/PixelOutputRenderer.cpp
@@ -50,13 +50,14 @@
" gl_FragColor = texture2D(u_Texture, v_TexCoord);"
"}";
-PixelOutputRenderer::PixelOutputRenderer(ANativeWindow* window, bool offscreen, int workload) :
- Renderer(window, offscreen, workload) {
+PixelOutputRenderer::PixelOutputRenderer(ANativeWindow* window, bool offscreen) :
+ Renderer(window, offscreen), mWorkload(0) {
}
-bool PixelOutputRenderer::setUp() {
+bool PixelOutputRenderer::setUp(int workload) {
SCOPED_TRACE();
- if (!Renderer::setUp()) {
+ mWorkload = workload;
+ if (!Renderer::setUp(workload)) {
return false;
}
@@ -80,6 +81,11 @@
bool PixelOutputRenderer::tearDown() {
SCOPED_TRACE();
+ if (mProgramId != 0)
+ {
+ glDeleteProgram(mProgramId);
+ mProgramId = 0;
+ }
if (mTextureId != 0) {
glDeleteTextures(1, &mTextureId);
mTextureId = 0;
diff --git a/suite/cts/deviceTests/opengl/jni/primitive/pixeloutput/PixelOutputRenderer.h b/suite/cts/deviceTests/opengl/jni/primitive/pixeloutput/PixelOutputRenderer.h
index e6b5692..816da6a 100644
--- a/suite/cts/deviceTests/opengl/jni/primitive/pixeloutput/PixelOutputRenderer.h
+++ b/suite/cts/deviceTests/opengl/jni/primitive/pixeloutput/PixelOutputRenderer.h
@@ -18,9 +18,9 @@
class PixelOutputRenderer: public Renderer {
public:
- PixelOutputRenderer(ANativeWindow* window, bool offscreen, int workload);
+ PixelOutputRenderer(ANativeWindow* window, bool offscreen);
virtual ~PixelOutputRenderer() {};
- bool setUp();
+ bool setUp(int workload);
bool tearDown();
void drawWorkload();
private:
@@ -28,6 +28,7 @@
GLuint mTextureUniformHandle;
GLuint mPositionHandle;
GLuint mTexCoordHandle;
+ int mWorkload;
};
#endif
diff --git a/suite/cts/deviceTests/opengl/jni/primitive/shaderperf/ShaderPerfRenderer.cpp b/suite/cts/deviceTests/opengl/jni/primitive/shaderperf/ShaderPerfRenderer.cpp
index 1cbc839..a02f4fe 100644
--- a/suite/cts/deviceTests/opengl/jni/primitive/shaderperf/ShaderPerfRenderer.cpp
+++ b/suite/cts/deviceTests/opengl/jni/primitive/shaderperf/ShaderPerfRenderer.cpp
@@ -91,13 +91,13 @@
return destAddr - destStart;
}
-ShaderPerfRenderer::ShaderPerfRenderer(ANativeWindow* window, bool offscreen, int workload) :
- Renderer(window, offscreen, workload) {
+ShaderPerfRenderer::ShaderPerfRenderer(ANativeWindow* window, bool offscreen) :
+ Renderer(window, offscreen) {
}
-bool ShaderPerfRenderer::setUp() {
+bool ShaderPerfRenderer::setUp(int workload) {
SCOPED_TRACE();
- if (!Renderer::setUp()) {
+ if (!Renderer::setUp(workload)) {
return false;
}
@@ -106,7 +106,7 @@
// Add the first part.
int index = charCopy(SP_FRAGMENT_1, spFragment, 0);
// Add the count, overwriting the '\0' added by charCopy.
- spFragment[index - 1] = (char) (((int) '0') + mWorkload);
+ spFragment[index - 1] = (char) (((int) '0') + workload);
// Add the second part.
index += charCopy(SP_FRAGMENT_2, spFragment, index);
// Create program.
diff --git a/suite/cts/deviceTests/opengl/jni/primitive/shaderperf/ShaderPerfRenderer.h b/suite/cts/deviceTests/opengl/jni/primitive/shaderperf/ShaderPerfRenderer.h
index 52fac43..c804202 100644
--- a/suite/cts/deviceTests/opengl/jni/primitive/shaderperf/ShaderPerfRenderer.h
+++ b/suite/cts/deviceTests/opengl/jni/primitive/shaderperf/ShaderPerfRenderer.h
@@ -18,9 +18,9 @@
class ShaderPerfRenderer: public Renderer {
public:
- ShaderPerfRenderer(ANativeWindow* window, bool offscreen, int workload);
+ ShaderPerfRenderer(ANativeWindow* window, bool offscreen);
virtual ~ShaderPerfRenderer() {};
- bool setUp();
+ bool setUp(int workload);
void drawWorkload();
private:
GLuint mTextureId;
diff --git a/suite/cts/deviceTests/opengl/jni/reference/GLReference.cpp b/suite/cts/deviceTests/opengl/jni/reference/GLReference.cpp
index 1857848..dc0b4e2 100644
--- a/suite/cts/deviceTests/opengl/jni/reference/GLReference.cpp
+++ b/suite/cts/deviceTests/opengl/jni/reference/GLReference.cpp
@@ -23,7 +23,7 @@
extern "C" JNIEXPORT jboolean JNICALL
Java_com_android_cts_opengl_reference_GLGameActivity_startBenchmark(
- JNIEnv* env, jclass clazz, jobject assetManager, jobject surface, jint numFrames,
+ JNIEnv* env, jclass /*clazz*/, jobject assetManager, jobject surface, jint numFrames,
jdoubleArray setUpTimes, jdoubleArray updateTimes, jdoubleArray renderTimes) {
GLUtils::setEnvAndAssetManager(env, assetManager);
@@ -32,9 +32,10 @@
return false;
}
- ReferenceRenderer* renderer = new ReferenceRenderer(ANativeWindow_fromSurface(env, surface));
-
- bool success = renderer->setUp();
+ ANativeWindow* nativeWindow = ANativeWindow_fromSurface(env, surface);
+ ReferenceRenderer* renderer = new ReferenceRenderer(nativeWindow);
+ bool success = renderer->eglSetUp();
+ success = renderer->setUp(0) && success;
env->SetDoubleArrayRegion(
setUpTimes, 0, ReferenceRenderer::NUM_SETUP_TIMES, renderer->mSetUpTimes);
@@ -54,7 +55,11 @@
env->SetDoubleArrayRegion(renderTimes, 0, numFrames, renders);
success = renderer->tearDown() && success;
+ renderer->eglTearDown();
delete renderer;
renderer = NULL;
+
+ ANativeWindow_release(nativeWindow);
+
return success;
}
diff --git a/suite/cts/deviceTests/opengl/jni/reference/ReferenceRenderer.cpp b/suite/cts/deviceTests/opengl/jni/reference/ReferenceRenderer.cpp
index 8f7703e..3b12ee1 100644
--- a/suite/cts/deviceTests/opengl/jni/reference/ReferenceRenderer.cpp
+++ b/suite/cts/deviceTests/opengl/jni/reference/ReferenceRenderer.cpp
@@ -22,10 +22,10 @@
#include <Trace.h>
ReferenceRenderer::ReferenceRenderer(ANativeWindow* window) :
- Renderer(window, false, 0) {
+ Renderer(window, false) {
}
-bool ReferenceRenderer::setUp() {
+bool ReferenceRenderer::setUp(int workload) {
SCOPED_TRACE();
// Reset the times.
for (int i = 0; i < NUM_SETUP_TIMES; i++) {
@@ -33,7 +33,7 @@
}
// Set up OpenGLES.
double start = GLUtils::currentTimeMillis();
- if (!Renderer::setUp()) {
+ if (!Renderer::setUp(workload)) {
return false;
}
mSetUpTimes[0] = GLUtils::currentTimeMillis() - start;
diff --git a/suite/cts/deviceTests/opengl/jni/reference/ReferenceRenderer.h b/suite/cts/deviceTests/opengl/jni/reference/ReferenceRenderer.h
index d10297a..f5c4b65 100644
--- a/suite/cts/deviceTests/opengl/jni/reference/ReferenceRenderer.h
+++ b/suite/cts/deviceTests/opengl/jni/reference/ReferenceRenderer.h
@@ -23,7 +23,7 @@
public:
ReferenceRenderer(ANativeWindow* window);
virtual ~ReferenceRenderer() {};
- bool setUp();
+ bool setUp(int workload);
bool tearDown();
bool update(int frame);
void drawWorkload();
diff --git a/suite/cts/deviceTests/opengl/src/com/android/cts/opengl/primitive/GLPrimitiveActivity.java b/suite/cts/deviceTests/opengl/src/com/android/cts/opengl/primitive/GLPrimitiveActivity.java
index 5dc9b88..6defdb7 100644
--- a/suite/cts/deviceTests/opengl/src/com/android/cts/opengl/primitive/GLPrimitiveActivity.java
+++ b/suite/cts/deviceTests/opengl/src/com/android/cts/opengl/primitive/GLPrimitiveActivity.java
@@ -102,19 +102,12 @@
}
}
- private static native void setupFullPipelineBenchmark(
- Surface surface, boolean offscreen, int workload);
+ private static native boolean setupBenchmark(
+ Surface surface, int benchmark, boolean offscreen);
- private static native void setupPixelOutputBenchmark(
- Surface surface, boolean offscreen, int workload);
+ private static native boolean startBenchmark(int workload, int numFrames, double[] frameTimes);
- private static native void setupShaderPerfBenchmark(
- Surface surface, boolean offscreen, int workload);
-
- private static native void setupContextSwitchBenchmark(
- Surface surface, boolean offscreen, int workload);
-
- private static native boolean startBenchmark(int numFrames, double[] frameTimes);
+ private static native void tearDownBenchmark();
/**
* This thread runs the benchmarks, freeing the UI thread.
@@ -138,36 +131,29 @@
watchDog = new WatchDog(mTimeout, this);
// Used to record the start and end time of the iteration.
double[] times = new double[2];
- for (int i = 0; i < mNumIterations && success; i++) {
- // The workload to use for this iteration.
- int workload = i + 1;
+ try {
// Setup the benchmark.
- switch (mBenchmark) {
- case FullPipeline:
- setupFullPipelineBenchmark(mSurface, mOffscreen, workload);
- break;
- case PixelOutput:
- setupPixelOutputBenchmark(mSurface, mOffscreen, workload);
- break;
- case ShaderPerf:
- setupShaderPerfBenchmark(mSurface, mOffscreen, workload);
- break;
- case ContextSwitch:
- setupContextSwitchBenchmark(mSurface, mOffscreen, workload);
- break;
- }
- watchDog.start();
- // Start benchmark.
- success = startBenchmark(mNumFrames, times);
- watchDog.stop();
-
- if (!success) {
- setException(new Exception("Benchmark failed to run"));
- } else {
- // Calculate FPS.
- mFpsValues[i] = mNumFrames * 1000.0f / (times[1] - times[0]);
+ setupBenchmark(mSurface, mBenchmark.ordinal(), mOffscreen);
+ for (int i = 0; i < mNumIterations && success; i++) {
+ // The workload to use for this iteration.
+ int workload = i + 1;
+ watchDog.start();
+ // Start benchmark.
+ success = startBenchmark(workload, mNumFrames, times);
+ watchDog.stop();
+ if (!success) {
+ setException(new Exception("Benchmark failed to run"));
+ } else {
+ // Calculate FPS.
+ mFpsValues[i] = mNumFrames * 1000.0f / (times[1] - times[0]);
+ }
}
}
+ finally
+ {
+ tearDownBenchmark();
+ }
+
complete();
Log.i(TAG, mBenchmark + " Benchmark Completed");
}
diff --git a/tests/expectations/knownfailures.txt b/tests/expectations/knownfailures.txt
index 754e38a..2cb99c4 100644
--- a/tests/expectations/knownfailures.txt
+++ b/tests/expectations/knownfailures.txt
@@ -1,5 +1,14 @@
[
{
+ 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"
@@ -15,6 +24,21 @@
bug: 17595050
},
{
+ description: "test fails on devices with no telephony",
+ names: [
+ "android.calllog.cts.CallLogBackupTest#testSingleCallBackup"
+ ],
+ bug: 23776099
+},
+{
+ 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",
@@ -39,6 +63,21 @@
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",
@@ -119,6 +158,21 @@
bug: 17508787
},
{
+ description: "This test should be outside of official CTS suite until it is verified for all Nexus devices",
+ names: [
+ "com.android.cts.devicepolicy.MixedDeviceOwnerTest#testPackageInstallUserRestrictions",
+ "com.android.cts.devicepolicy.MixedProfileOwnerTest#testPackageInstallUserRestrictions"
+ ],
+ bug: 18928535
+},
+{
+ description: "Test is not yet properly implemented",
+ names: [
+ "android.voicesettings.cts.ZenModeTest#testAll"
+ ],
+ bug: 23238984
+},
+{
description: "These tests fail on some devices.",
names: [
"android.uirendering.cts.testclasses.ExactCanvasTests#testBlueRect",
@@ -208,6 +262,13 @@
bug: 23008511
},
{
+ description: "Light status bar CTS coming in late",
+ names: [
+ "com.android.cts.systemui.LightStatusBarTests#testLightStatusBarIcons"
+ ],
+ bug: 23427621
+},
+{
description: "known failures",
names: [
"android.hardware.cts.SensorBatchingTests#testAccelerometer_50hz_batching",
@@ -234,5 +295,70 @@
"android.hardware.cts.SingleSensorTests#testOrientation_5hz"
],
bug: 22922206
+},
+{
+ description: "tests are not yet ready",
+ names: [
+ "com.android.cts.app.os.OsHostTests#testNonExportedActivities"
+ ],
+ bug: 23779168
+},
+{
+ description: "New assist tests that do not yet have a track record.",
+ names: [
+ "android.assist.cts.AssistantContentViewTest",
+ "android.assist.cts.ExtraAssistDataTest",
+ "android.assist.cts.FocusChangeTest",
+ "android.assist.cts.LargeViewHierarchyTest",
+ "android.assist.cts.ScreenshotTest",
+ "android.assist.cts.TextViewTest",
+ "android.assist.cts.WebViewTest"
+ ],
+ bug: 21668302
+},
+{
+ description: "ConnectivityConstraintTest job scheduler not working.",
+ names: [
+ "android.jobscheduler.cts.ConnectivityConstraintTest#testConnectivityConstraintExecutes_withWifi",
+ "android.jobscheduler.cts.ConnectivityConstraintTest#testUnmeteredConstraintExecutes_withWifi"
+ ],
+ bug: 21262226
+},
+{
+ description: "ConnectivityConstraintTest times out.",
+ names: [
+ "android.jobscheduler.cts.TimingConstraintsTest#testJobParameters_unexpiredDeadline"
+ ],
+ bug: 23144425
+},
+{
+ description: "Telephony returning wrong value.",
+ names: [
+ "android.telephony.cts.CellInfoTest#testCellInfo"
+ ],
+ bug: 23979591
+},
+{
+ description: "Video encoding tests are timing out.",
+ names: [
+ "android.media.cts.VideoEncoderTest#testGoogH264FlexArbitraryW",
+ "android.media.cts.VideoEncoderTest#testGoogH264SurfArbitraryW"
+ ],
+ bug: 23827982
+},
+{
+ description: "tests not yet ready",
+ names: [
+ "android.telecom.cts.OutgoingCallTest#testStartCallWithSpeakerphoneFalse_SpeakerphoneOffInCall",
+ "android.telecom.cts.OutgoingCallTest#testStartCallWithSpeakerphoneNotProvided_SpeakerphoneOffByDefault"
+ ],
+ bug: 24067587
+},
+{
+ description: "protected broadcast not working",
+ names: [
+ "android.permission2.cts.ProtectedBroadcastsTest#testSendProtectedBroadcasts"
+ ],
+ bug: 23192492
}
]
diff --git a/tests/leanbackjank/src/android/cts/leanbackjank/CtsDeviceLeanback.java b/tests/leanbackjank/src/android/cts/leanbackjank/CtsDeviceLeanback.java
index a86a707..241535e 100644
--- a/tests/leanbackjank/src/android/cts/leanbackjank/CtsDeviceLeanback.java
+++ b/tests/leanbackjank/src/android/cts/leanbackjank/CtsDeviceLeanback.java
@@ -16,6 +16,7 @@
import android.content.ComponentName;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.cts.jank.leanback.IntentKeys;
import android.os.SystemClock;
import android.support.test.jank.GfxMonitor;
@@ -40,9 +41,30 @@
private final static String JAVA_PACKAGE = "android.cts.jank.leanback.ui";
private final static String CLASS = JAVA_PACKAGE + ".MainActivity";
+ private boolean shouldSkip() {
+ PackageManager packageManager =
+ getInstrumentation().getTargetContext().getPackageManager();
+ if (!packageManager.hasSystemFeature(
+ PackageManager.FEATURE_LEANBACK)) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ protected void runTest() throws Throwable {
+ if (shouldSkip()) {
+ return;
+ }
+ super.runTest();
+ }
+
@Override
protected void setUp() throws Exception {
super.setUp();
+ if (shouldSkip()) {
+ return;
+ }
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setComponent(new ComponentName(APP_PACKAGE, CLASS));
diff --git a/tests/tests/netlegacy22/Android.mk b/tests/netlegacy22.api/Android.mk
similarity index 100%
rename from tests/tests/netlegacy22/Android.mk
rename to tests/netlegacy22.api/Android.mk
diff --git a/tests/tests/netlegacy22/AndroidManifest.xml b/tests/netlegacy22.api/AndroidManifest.xml
similarity index 92%
rename from tests/tests/netlegacy22/AndroidManifest.xml
rename to tests/netlegacy22.api/AndroidManifest.xml
index d243e45..f13805c 100644
--- a/tests/tests/netlegacy22/AndroidManifest.xml
+++ b/tests/netlegacy22.api/AndroidManifest.xml
@@ -16,7 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.cts.net.legacy22">
+ package="com.android.cts.netlegacy22.api">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
@@ -30,7 +30,7 @@
</application>
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.cts.net.legacy22"
+ android:targetPackage="com.android.cts.netlegacy22.api"
android:label="CTS tests of legacy android.net APIs as of API 22">
<meta-data android:name="listener"
android:value="com.android.cts.runner.CtsTestRunListener" />
diff --git a/tests/tests/netlegacy22/src/android/net/cts/legacy/api22/ConnectivityManagerLegacyTest.java b/tests/netlegacy22.api/src/android/net/cts/legacy/api22/ConnectivityManagerLegacyTest.java
similarity index 84%
rename from tests/tests/netlegacy22/src/android/net/cts/legacy/api22/ConnectivityManagerLegacyTest.java
rename to tests/netlegacy22.api/src/android/net/cts/legacy/api22/ConnectivityManagerLegacyTest.java
index 8a9002b..1836f06 100644
--- a/tests/tests/netlegacy22/src/android/net/cts/legacy/api22/ConnectivityManagerLegacyTest.java
+++ b/tests/netlegacy22.api/src/android/net/cts/legacy/api22/ConnectivityManagerLegacyTest.java
@@ -85,24 +85,58 @@
((addr[1] & 0xff) << 8) | (addr[0] & 0xff);
}
- private void checkSourceAddress(String addrString, int type) throws Exception {
- DatagramSocket d = new DatagramSocket();
- d.connect(InetAddress.getByName(addrString), 7);
- InetAddress localAddress = d.getLocalAddress();
-
+ // Returns a list of all the IP addresses for all the networks of a given legacy type. We can't
+ // just fetch the IP addresses for that type because there is no public getLinkProperties API
+ // that takes a legacy type.
+ private List<InetAddress> getIpAddresses(int type) {
+ ArrayList<InetAddress> addresses = new ArrayList<>();
Network[] networks = mCm.getAllNetworks();
for (int i = 0; i < networks.length; i++) {
NetworkInfo ni = mCm.getNetworkInfo(networks[i]);
if (ni != null && ni.getType() == type) {
+ // This does not include IP addresses on stacked interfaces (e.g., 464xlat), because
+ // there is no public API that will return them.
LinkProperties lp = mCm.getLinkProperties(networks[i]);
for (LinkAddress address : lp.getLinkAddresses()) {
- if (address.getAddress().equals(localAddress)) {
- return;
- }
+ addresses.add(address.getAddress());
}
}
}
- fail("Local address " + localAddress + " not assigned to any network of type " + type);
+ return addresses;
+ }
+
+ private boolean hasIPv4(int type) {
+ for (InetAddress address : getIpAddresses(type)) {
+ if (address instanceof Inet4Address) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void checkSourceAddress(String addrString, int type) throws Exception {
+ // The public requestRouteToHost API only supports IPv4, but it will not return failure if
+ // the network does not have an IPv4 address. So don't check that it's working unless we
+ // know that the network has an IPv4 address. Note that it's possible that the network will
+ // have an IPv4 address but we don't know about it, because the IPv4 address might be on a
+ // stacked interface and we wouldn't be able to see it.
+ if (!hasIPv4(type)) {
+ Log.d(TAG, "Not checking source address on network type " + type + ", no IPv4 address");
+ return;
+ }
+
+ DatagramSocket d = new DatagramSocket();
+ d.connect(InetAddress.getByName(addrString), 7);
+ InetAddress localAddress = d.getLocalAddress();
+ String localAddrString = localAddress.getHostAddress();
+
+ Log.d(TAG, "Got source address " + localAddrString + " for destination " + addrString);
+
+ assertTrue(
+ "Local address " + localAddress + " not assigned to any network of type " + type,
+ getIpAddresses(type).contains(localAddress));
+
+ Log.d(TAG, "Source address " + localAddress + " found on network type " + type);
}
/** Test that hipri can be brought up when Wifi is enabled. */
@@ -127,7 +161,6 @@
assertTrue("Couldn't requestRouteToHost using HIPRI.",
mCm.requestRouteToHost(TYPE_MOBILE_HIPRI, ipv4AddrToInt(HOST_ADDRESS1)));
- try { Thread.sleep(1000); } catch(Exception e) {}
checkSourceAddress(HOST_ADDRESS1, TYPE_MOBILE);
checkSourceAddress(HOST_ADDRESS2, TYPE_WIFI);
diff --git a/tests/netlegacy22.permission/Android.mk b/tests/netlegacy22.permission/Android.mk
new file mode 100644
index 0000000..fff9d85
--- /dev/null
+++ b/tests/netlegacy22.permission/Android.mk
@@ -0,0 +1,32 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# don't include this package in any target
+LOCAL_MODULE_TAGS := tests
+# and when built explicitly put it in the data partition
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsNetTestCasesLegacyPermission22
+
+LOCAL_SDK_VERSION := 22
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/netlegacy22.permission/AndroidManifest.xml b/tests/netlegacy22.permission/AndroidManifest.xml
new file mode 100644
index 0000000..cd1d2ba
--- /dev/null
+++ b/tests/netlegacy22.permission/AndroidManifest.xml
@@ -0,0 +1,51 @@
+<?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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.cts.netlegacy22.permission">
+
+ <uses-permission android:name="android.permission.INJECT_EVENTS" />
+ <application>
+ <uses-library android:name="android.test.runner" />
+ <activity android:name="android.permission.cts.PermissionStubActivity"
+ android:label="PermissionStubActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST"/>
+ </intent-filter>
+ </activity>
+ </application>
+
+ <!--
+ The CTS stubs package cannot be used as the target application here,
+ since that requires many permissions to be set. Instead, specify this
+ package itself as the target and include any stub activities needed.
+
+ This test package uses the default InstrumentationTestRunner, because
+ the InstrumentationCtsTestRunner is only available in the stubs
+ package. That runner cannot be added to this package either, since it
+ relies on hidden APIs.
+ -->
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.cts.netlegacy22.permission"
+ android:label="CTS tests of legacy android.net permissions as of API 22">
+ <meta-data android:name="listener"
+ android:value="com.android.cts.runner.CtsTestRunListener" />
+ </instrumentation>
+
+</manifest>
+
diff --git a/tests/netlegacy22.permission/src/android/net/cts/legacy/api22/permission/ConnectivityManagerPermissionTest.java b/tests/netlegacy22.permission/src/android/net/cts/legacy/api22/permission/ConnectivityManagerPermissionTest.java
new file mode 100644
index 0000000..0e59288
--- /dev/null
+++ b/tests/netlegacy22.permission/src/android/net/cts/legacy/api22/permission/ConnectivityManagerPermissionTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.cts.legacy.api22.permission;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+/**
+* Test that protected android.net.ConnectivityManager methods cannot be called without
+* permissions
+*/
+public class ConnectivityManagerPermissionTest extends AndroidTestCase {
+
+ private ConnectivityManager mConnectivityManager = null;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mConnectivityManager = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+ assertNotNull(mConnectivityManager);
+ }
+
+ /**
+ * Verify that calling {@link ConnectivityManager#requestRouteToHost(int, int)}
+ * requires permissions.
+ * <p>Tests Permission:
+ * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+ */
+ @SmallTest
+ public void testRequestRouteToHost() {
+ try {
+ mConnectivityManager.requestRouteToHost(ConnectivityManager.TYPE_MOBILE, 1);
+ fail("Was able to call requestRouteToHost");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+}
diff --git a/tests/netlegacy22.permission/src/android/net/cts/legacy/api22/permission/NoNetworkStatePermissionTest.java b/tests/netlegacy22.permission/src/android/net/cts/legacy/api22/permission/NoNetworkStatePermissionTest.java
new file mode 100644
index 0000000..8547261
--- /dev/null
+++ b/tests/netlegacy22.permission/src/android/net/cts/legacy/api22/permission/NoNetworkStatePermissionTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.cts.legacy.api22.permission;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import java.net.InetAddress;
+
+/**
+ * Verify ConnectivityManager related methods without specific network state permissions.
+ */
+public class NoNetworkStatePermissionTest extends AndroidTestCase {
+ private ConnectivityManager mConnectivityManager;
+ private static final int TEST_NETWORK_TYPE = ConnectivityManager.TYPE_MOBILE;
+ private static final String TEST_FEATURE = "enableHIPRI";
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mConnectivityManager = (ConnectivityManager) mContext.getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ assertNotNull(mConnectivityManager);
+ }
+
+ /**
+ * Verify that ConnectivityManager#startUsingNetworkFeature() requires permissions.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+ */
+ @SmallTest
+ public void testStartUsingNetworkFeature() {
+ try {
+ mConnectivityManager.startUsingNetworkFeature(TEST_NETWORK_TYPE, TEST_FEATURE);
+ fail("ConnectivityManager.startUsingNetworkFeature didn't throw SecurityException as"
+ + " expected");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ /**
+ * Verify that ConnectivityManager#requestRouteToHost() requires permissions.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
+ */
+ @SmallTest
+ public void testRequestRouteToHost() {
+ try {
+ mConnectivityManager.requestRouteToHost(TEST_NETWORK_TYPE, 0xffffffff);
+ fail("ConnectivityManager.requestRouteToHost didn't throw SecurityException as"
+ + " expected");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+}
diff --git a/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java b/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
index 44ab12e..f5b29cf 100644
--- a/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
+++ b/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
@@ -24,8 +24,10 @@
import android.app.Service;
import android.app.UiAutomation;
import android.content.Intent;
+import android.content.res.Configuration;
import android.test.suitebuilder.annotation.MediumTest;
import android.text.TextUtils;
+import android.util.Log;
import android.view.accessibility.AccessibilityEvent;
import android.widget.Button;
import android.widget.EditText;
@@ -44,6 +46,8 @@
public class AccessibilityEndToEndTest extends
AccessibilityActivityTestCase<AccessibilityEndToEndActivity> {
+ private static final String LOG_TAG = "AccessibilityEndToEndTest";
+
/**
* Creates a new instance for testing {@link AccessibilityEndToEndActivity}.
*/
@@ -309,6 +313,14 @@
@MediumTest
@SuppressWarnings("deprecation")
public void testTypeNotificationStateChangedAccessibilityEvent() throws Throwable {
+ // No notification UI on televisions.
+ if((getActivity().getResources().getConfiguration().uiMode
+ & Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_TELEVISION) {
+ Log.i(LOG_TAG, "Skipping: testTypeNotificationStateChangedAccessibilityEvent" +
+ " - No notification UI on televisions.");
+ return;
+ }
+
String message = getActivity().getString(R.string.notification_message);
// create the notification to send
diff --git a/tests/tests/alarmclock/AndroidTest.xml b/tests/tests/alarmclock/AndroidTest.xml
index 1cdd7f4..aafdb61 100644
--- a/tests/tests/alarmclock/AndroidTest.xml
+++ b/tests/tests/alarmclock/AndroidTest.xml
@@ -17,7 +17,5 @@
<option name="cts-apk-installer:test-file-name" value="CtsAlarmClockService.apk" />
<option name="run-command:run-command"
value="settings put secure voice_interaction_service android.alarmclock.service/.MainInteractionService" />
- <option name="run-command:teardown-command"
- value="settings put secure voice_interaction_service com.google.android.googlequicksearchbox/com.google.android.voiceinteraction.GsaVoiceInteractionService" />
<option name="cts-apk-installer:test-file-name" value="CtsAlarmClockTestCases.apk" />
</configuration>
diff --git a/tests/tests/alarmclock/src/android/alarmclock/cts/AlarmClockTestBase.java b/tests/tests/alarmclock/src/android/alarmclock/cts/AlarmClockTestBase.java
index 0089e69..0d434f2 100644
--- a/tests/tests/alarmclock/src/android/alarmclock/cts/AlarmClockTestBase.java
+++ b/tests/tests/alarmclock/src/android/alarmclock/cts/AlarmClockTestBase.java
@@ -23,6 +23,8 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.provider.AlarmClock;
import android.os.Bundle;
import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
@@ -68,8 +70,40 @@
new IntentFilter(Utils.BROADCAST_INTENT + testCaseType.toString()));
}
+ private boolean isIntentSupported(TestcaseType testCaseType) {
+ Intent intent;
+ switch (testCaseType) {
+ case DISMISS_ALARM:
+ intent = new Intent(AlarmClock.ACTION_DISMISS_ALARM);
+ break;
+
+ case SET_ALARM:
+ case SET_ALARM_FOR_DISMISSAL:
+ intent = new Intent(AlarmClock.ACTION_SET_ALARM);
+ break;
+
+ case SNOOZE_ALARM:
+ intent = new Intent(AlarmClock.ACTION_SNOOZE_ALARM);
+ break;
+
+ default:
+ // shouldn't happen
+ return false;
+ }
+ final PackageManager manager = mContext.getPackageManager();
+ assertNotNull(manager);
+ if (manager.resolveActivity(intent, 0) == null) {
+ Log.i(TAG, "No Voice Activity found for the intent: " + intent.getAction());
+ return false;
+ }
+ return true;
+ }
+
protected String runTest(TestcaseType testCaseType) throws Exception {
Log.i(TAG, "Begin Testing: " + testCaseType);
+ // Make sure the corresponding intent is supported by the platform, before testing.
+ if (!isIntentSupported(testCaseType)) return Utils.COMPLETION_RESULT;
+
if (!startTestActivity(testCaseType)) {
fail("test activity start failed for testcase = " + testCaseType);
return "";
diff --git a/tests/tests/app.usage/src/android/app/usage/cts/NetworkUsageStatsTest.java b/tests/tests/app.usage/src/android/app/usage/cts/NetworkUsageStatsTest.java
index b6fef4a..a889c02 100644
--- a/tests/tests/app.usage/src/android/app/usage/cts/NetworkUsageStatsTest.java
+++ b/tests/tests/app.usage/src/android/app/usage/cts/NetworkUsageStatsTest.java
@@ -20,6 +20,7 @@
import android.app.usage.NetworkStatsManager;
import android.app.usage.NetworkStats;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
@@ -39,7 +40,7 @@
import java.net.URL;
import java.text.MessageFormat;
import java.util.Scanner;
-import javax.net.ssl.HttpsURLConnection;
+import java.net.HttpURLConnection;
import libcore.io.IoUtils;
import libcore.io.Streams;
@@ -56,14 +57,21 @@
ConnectivityManager.TYPE_MOBILE,
};
- // Order corresponds to sNetworkTypesToTest
- private static final int[] sTransportTypesToTest = new int[] {
- NetworkCapabilities.TRANSPORT_WIFI,
- NetworkCapabilities.TRANSPORT_CELLULAR,
+ private static final String[] sSystemFeaturesToTest = new String[] {
+ PackageManager.FEATURE_WIFI,
+ PackageManager.FEATURE_TELEPHONY,
+ };
+
+ private static final String[] sFeatureNotConnectedCause = new String[] {
+ " Please make sure you are connected to a WiFi access point.",
+ " Please make sure you have added a SIM card with data plan to your phone, have enabled " +
+ "data over cellular and in case of dual SIM devices, have selected the right SIM " +
+ "for data connection."
};
private NetworkStatsManager mNsm;
private ConnectivityManager mCm;
+ private PackageManager mPm;
private long mStartTime;
private long mEndTime;
@@ -71,62 +79,50 @@
private String mWriteSettingsMode;
private String mUsageStatsMode;
- private void exerciseRemoteHost(int transportType) throws Exception {
+ private void exerciseRemoteHost(Network network) throws Exception {
final int timeout = 15000;
- mCm.requestNetwork(new NetworkRequest.Builder()
- .addTransportType(transportType)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
- .build(), new ConnectivityManager.NetworkCallback() {
- @Override
- public void onAvailable(Network network) {
- NetworkInfo networkInfo = mCm.getNetworkInfo(network);
- if (networkInfo == null) {
- Log.w(LOG_TAG, "Network info is null");
- } else {
- Log.w(LOG_TAG, "Network: " + networkInfo.toString());
- }
- InputStreamReader in = null;
- HttpsURLConnection urlc = null;
- String originalKeepAlive = System.getProperty("http.keepAlive");
- System.setProperty("http.keepAlive", "false");
- try {
- urlc = (HttpsURLConnection) network.openConnection(new URL(
- "https://www.google.com"));
- urlc.setConnectTimeout(timeout);
- urlc.setUseCaches(false);
- urlc.connect();
- boolean ping = urlc.getResponseCode() == 200;
- if (ping) {
- in = new InputStreamReader(
- (InputStream) urlc.getContent());
-
- mBytesRead = 0;
- while (in.read() != -1) ++mBytesRead;
- }
- } catch (Exception e) {
- Log.i(LOG_TAG, "Badness during exercising remote server: " + e);
- } finally {
- if (in != null) {
- try {
- in.close();
- } catch (IOException e) {
- // don't care
- }
- }
- if (urlc != null) {
- urlc.disconnect();
- }
- if (originalKeepAlive == null) {
- System.clearProperty("http.keepAlive");
- } else {
- System.setProperty("http.keepAlive", originalKeepAlive);
- }
- }
- }
- });
+ NetworkInfo networkInfo = mCm.getNetworkInfo(network);
+ if (networkInfo == null) {
+ Log.w(LOG_TAG, "Network info is null");
+ } else {
+ Log.w(LOG_TAG, "Network: " + networkInfo.toString());
+ }
+ InputStreamReader in = null;
+ HttpURLConnection urlc = null;
+ String originalKeepAlive = System.getProperty("http.keepAlive");
+ System.setProperty("http.keepAlive", "false");
try {
- Thread.sleep(timeout);
- } catch (InterruptedException e) {
+ urlc = (HttpURLConnection) network.openConnection(new URL(
+ "http://www.265.com/"));
+ urlc.setConnectTimeout(timeout);
+ urlc.setUseCaches(false);
+ urlc.connect();
+ boolean ping = urlc.getResponseCode() == 200;
+ if (ping) {
+ in = new InputStreamReader(
+ (InputStream) urlc.getContent());
+
+ mBytesRead = 0;
+ while (in.read() != -1) ++mBytesRead;
+ }
+ } catch (Exception e) {
+ Log.i(LOG_TAG, "Badness during exercising remote server: " + e);
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException e) {
+ // don't care
+ }
+ }
+ if (urlc != null) {
+ urlc.disconnect();
+ }
+ if (originalKeepAlive == null) {
+ System.clearProperty("http.keepAlive");
+ } else {
+ System.setProperty("http.keepAlive", originalKeepAlive);
+ }
}
}
@@ -139,6 +135,8 @@
mCm = (ConnectivityManager) getInstrumentation().getContext()
.getSystemService(Context.CONNECTIVITY_SERVICE);
+ mPm = getInstrumentation().getContext().getPackageManager();
+
mWriteSettingsMode = getAppOpsMode(AppOpsManager.OPSTR_WRITE_SETTINGS);
setAppOpsMode(AppOpsManager.OPSTR_WRITE_SETTINGS, "allow");
mUsageStatsMode = getAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS);
@@ -192,14 +190,26 @@
private boolean shouldTestThisNetworkType(int networkTypeIndex, long tolerance)
throws Exception {
- NetworkInfo networkInfo = mCm.getNetworkInfo(sNetworkTypesToTest[networkTypeIndex]);
- if (networkInfo == null || !networkInfo.isAvailable()) {
- return false;
+ Network[] networks = mCm.getAllNetworks();
+ for (Network network : networks) {
+ NetworkInfo networkInfo = mCm.getNetworkInfo(network);
+ if (networkInfo != null && networkInfo.isConnected() &&
+ networkInfo.getType() == sNetworkTypesToTest[networkTypeIndex]) {
+ NetworkCapabilities capabilities = mCm.getNetworkCapabilities(network);
+ if (capabilities != null && capabilities.hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
+ mStartTime = System.currentTimeMillis() - tolerance;
+ exerciseRemoteHost(network);
+ mEndTime = System.currentTimeMillis() + tolerance;
+ return true;
+ }
+ }
}
- mStartTime = System.currentTimeMillis() - tolerance;
- exerciseRemoteHost(sTransportTypesToTest[networkTypeIndex]);
- mEndTime = System.currentTimeMillis() + tolerance;
- return true;
+ assertFalse (sSystemFeaturesToTest[networkTypeIndex] + " is a reported system feature, " +
+ "however no corresponding connected network interface was found. " +
+ sFeatureNotConnectedCause[networkTypeIndex],
+ mPm.hasSystemFeature(sSystemFeaturesToTest[networkTypeIndex]));
+ return false;
}
private String getSubscriberId(int networkType) {
diff --git a/tests/tests/app/src/android/app/cts/ActivityManagerMemoryClassTest.java b/tests/tests/app/src/android/app/cts/ActivityManagerMemoryClassTest.java
index dfa278a..c8ef253 100644
--- a/tests/tests/app/src/android/app/cts/ActivityManagerMemoryClassTest.java
+++ b/tests/tests/app/src/android/app/cts/ActivityManagerMemoryClassTest.java
@@ -19,6 +19,7 @@
import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.test.ActivityInstrumentationTestCase2;
import android.util.DisplayMetrics;
@@ -40,6 +41,8 @@
}
public static class ExpectedMemorySizesClass {
+ private static final Map<Integer, Integer> expectedMemorySizeForWatch
+ = new HashMap<Integer, Integer>();
private static final Map<Integer, Integer> expectedMemorySizeForSmallNormalScreen
= new HashMap<Integer, Integer>();
private static final Map<Integer, Integer> expectedMemorySizeForLargeScreen
@@ -48,6 +51,21 @@
= new HashMap<Integer, Integer>();
static {
+ expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_LOW, 32);
+ expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_MEDIUM, 32);
+ expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_TV, 32);
+ expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_HIGH, 36);
+ expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_280, 36);
+ expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_XHIGH, 48);
+ expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_360, 48);
+ expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_400, 56);
+ expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_420, 64);
+ expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_XXHIGH, 88);
+ expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_560, 112);
+ expectedMemorySizeForWatch.put(DisplayMetrics.DENSITY_XXXHIGH, 154);
+ }
+
+ static {
expectedMemorySizeForSmallNormalScreen.put(DisplayMetrics.DENSITY_LOW, 32);
expectedMemorySizeForSmallNormalScreen.put(DisplayMetrics.DENSITY_MEDIUM, 32);
expectedMemorySizeForSmallNormalScreen.put(DisplayMetrics.DENSITY_TV, 48);
@@ -92,7 +110,15 @@
expectedMemorySizeForXLargeScreen.put(DisplayMetrics.DENSITY_XXXHIGH, 768);
}
- public static Integer getExpectedMemorySize(int screenSize, int screenDensity) {
+ public static Integer getExpectedMemorySize(
+ int screenSize,
+ int screenDensity,
+ boolean isWatch) {
+
+ if (isWatch) {
+ return expectedMemorySizeForWatch.get(screenDensity);
+ }
+
switch (screenSize) {
case Configuration.SCREENLAYOUT_SIZE_SMALL:
case Configuration.SCREENLAYOUT_SIZE_NORMAL:
@@ -141,8 +167,11 @@
}
private void assertMemoryForScreenDensity(int memoryClass, int screenDensity, int screenSize) {
- int expectedMinimumMemory = ExpectedMemorySizesClass.getExpectedMemorySize(screenSize,
- screenDensity);
+ Context context = getInstrumentation().getTargetContext();
+ boolean isWatch =
+ context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH);
+ int expectedMinimumMemory =
+ ExpectedMemorySizesClass.getExpectedMemorySize(screenSize, screenDensity, isWatch);
assertTrue("Expected to have at least " + expectedMinimumMemory
+ "mb of memory for screen density " + screenDensity,
diff --git a/tests/tests/app/src/android/app/cts/NotificationManagerTest.java b/tests/tests/app/src/android/app/cts/NotificationManagerTest.java
index 5781442..fbb3060 100644
--- a/tests/tests/app/src/android/app/cts/NotificationManagerTest.java
+++ b/tests/tests/app/src/android/app/cts/NotificationManagerTest.java
@@ -74,9 +74,8 @@
sendNotification(id, R.drawable.black);
mNotificationManager.cancel(id);
- StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications();
- for (StatusBarNotification sbn : sbns) {
- assertFalse("canceled notification was still alive, id=" + id, sbn.getId() == id);
+ if (!checkNotificationExistence(id, /*shouldExist=*/ false)) {
+ fail("canceled notification was still alive, id=" + id);
}
}
@@ -116,10 +115,31 @@
.build();
mNotificationManager.notify(id, notification);
- StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications();
- for (StatusBarNotification sbn : sbns) {
- if (sbn.getId() == id) return;
+
+ if (!checkNotificationExistence(id, /*shouldExist=*/ true)) {
+ fail("couldn't find posted notification id=" + id);
}
- fail("couldn't find posted notification id=" + id);
+ }
+
+ private boolean checkNotificationExistence(int id, boolean shouldExist) {
+ // notification is a bit asynchronous so it may take a few ms to appear in getActiveNotifications()
+ // we will check for it for up to 200ms before giving up
+ boolean found = false;
+ for (int tries=3; tries-->0;) {
+ final StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications();
+ for (StatusBarNotification sbn : sbns) {
+ if (sbn.getId() == id) {
+ found = true;
+ break;
+ }
+ }
+ if (found == shouldExist) break;
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException ex) {
+ // pass
+ }
+ }
+ return found == shouldExist;
}
}
diff --git a/tests/tests/assist/AndroidManifest.xml b/tests/tests/assist/AndroidManifest.xml
index 97bd874..a81ced2 100644
--- a/tests/tests/assist/AndroidManifest.xml
+++ b/tests/tests/assist/AndroidManifest.xml
@@ -20,16 +20,22 @@
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name="android.permission.BIND_VOICE_INTERACTION" />
+ <uses-permission android:name="android.permission.INTERNET" />
<application>
<uses-library android:name="android.test.runner" />
- <activity android:name="android.assist.TestStartActivity"
- android:label="Assist Target">
+ <activity android:name="android.assist.cts.TestStartActivity"
+ android:label="Assist Test Start Activity">
<intent-filter>
<action android:name="android.intent.action.TEST_START_ACTIVITY_ASSIST_STRUCTURE" />
<action android:name="android.intent.action.TEST_START_ACTIVITY_DISABLE_CONTEXT" />
<action android:name="android.intent.action.TEST_START_ACTIVITY_FLAG_SECURE" />
<action android:name="android.intent.action.TEST_START_ACTIVITY_LIFECYCLE" />
+ <action android:name="android.intent.action.TEST_START_ACTIVITY_SCREENSHOT" />
+ <action android:name="android.intent.action.TEST_START_ACTIVITY_EXTRA_ASSIST" />
+ <action android:name="android.intent.action.TEST_START_ACTIVITY_TEXTVIEW" />
+ <action android:name="android.intent.action.TEST_START_ACTIVITY_LARGE_VIEW_HIERARCHY" />
+ <action android:name="android.intent.action.TEST_START_ACTIVITY_WEBVIEW" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
diff --git a/tests/tests/assist/AndroidTest.xml b/tests/tests/assist/AndroidTest.xml
index f7c56ad..329692d 100644
--- a/tests/tests/assist/AndroidTest.xml
+++ b/tests/tests/assist/AndroidTest.xml
@@ -19,7 +19,5 @@
<option name="cts-apk-installer:test-file-name" value="CtsAssistApp.apk" />
<option name="run-command:run-command"
value="settings put secure voice_interaction_service android.assist.service/.MainInteractionService" />
- <option name="run-command:teardown-command"
- value="settings put secure voice_interaction_service com.google.android.googlequicksearchbox/com.google.android.voiceinteraction.GsaVoiceInteractionService" />
<option name="cts-apk-installer:test-file-name" value="CtsAssistTestCases.apk" />
</configuration>
diff --git a/tests/tests/assist/common/src/android/assist/common/Utils.java b/tests/tests/assist/common/src/android/assist/common/Utils.java
index 8d555da..54416b4 100644
--- a/tests/tests/assist/common/src/android/assist/common/Utils.java
+++ b/tests/tests/assist/common/src/android/assist/common/Utils.java
@@ -19,6 +19,8 @@
import android.content.ComponentName;
import android.os.Bundle;
+import org.json.JSONException;
+import org.json.JSONObject;
import java.util.ArrayList;
public class Utils {
@@ -29,13 +31,24 @@
public static final String BROADCAST_ASSIST_DATA_INTENT = ACTION_PREFIX + "ASSIST_DATA";
public static final String BROADCAST_INTENT_START_ASSIST = ACTION_PREFIX + "START_ASSIST";
public static final String ASSIST_RECEIVER_REGISTERED = ACTION_PREFIX + "ASSIST_READY";
+
+ public static final String ACTION_INVALIDATE = "invalidate_action";
+ public static final String GET_CONTENT_VIEW_HEIGHT = ACTION_PREFIX + "GET_CONTENT_VIEW_HEIGHT";
+ public static final String BROADCAST_CONTENT_VIEW_HEIGHT = ACTION_PREFIX + "VIEW_HEIGHT";
+ public static final String SCROLL_TEXTVIEW_ACTION = ACTION_PREFIX + "TEXTVIEW_SCROLL";
+ public static final String SCROLL_SCROLLVIEW_ACTION = ACTION_PREFIX + "SCROLLVIEW_SCROLL";
public static final String TEST_ERROR = "Error In Test:";
public static final String ASSIST_STRUCTURE_KEY = "assist_structure";
public static final String ASSIST_CONTENT_KEY = "assist_content";
public static final String ASSIST_BUNDLE_KEY = "assist_bundle";
public static final String ASSIST_SCREENSHOT_KEY = "assist_screenshot";
-
+ public static final String SCREENSHOT_COLOR_KEY = "set_screenshot_color";
+ public static final String COMPARE_SCREENSHOT_KEY = "compare_screenshot";
+ public static final String DISPLAY_WIDTH_KEY = "display_width";
+ public static final String DISPLAY_HEIGHT_KEY = "dislay_height";
+ public static final String SCROLL_X_POSITION = "scroll_x_position";
+ public static final String SCROLL_Y_POSITION = "scroll_y_position";
/** Lifecycle Test intent constants */
public static final String LIFECYCLE_PREFIX = ACTION_PREFIX + "lifecycle_";
@@ -44,34 +57,103 @@
public static final String LIFECYCLE_ONSTOP = LIFECYCLE_PREFIX + "onstop";
public static final String LIFECYCLE_ONDESTROY = LIFECYCLE_PREFIX + "ondestroy";
+ /** Focus Change Test intent constants */
+ public static final String GAINED_FOCUS = ACTION_PREFIX + "focus_changed";
+ public static final String LOST_FOCUS = ACTION_PREFIX + "lost_focus";
+
/** Flag Secure Test intent constants */
public static final String FLAG_SECURE_HASRESUMED = ACTION_PREFIX + "flag_secure_hasResumed";
+ public static final String APP_3P_HASRESUMED = ACTION_PREFIX + "app_3p_hasResumed";
+ public static final String TEST_ACTIVITY_LOADED = ACTION_PREFIX + "test_activity_hasResumed";
/** Two second timeout for getting back assist context */
- public static final int TIMEOUT_MS = 2 * 1000; // TODO(awlee): what is the timeout
+ public static final int TIMEOUT_MS = 2 * 1000;
+ /** Four second timeout for an activity to resume */
public static final int ACTIVITY_ONRESUME_TIMEOUT_MS = 4000;
+
public static final String EXTRA_REGISTER_RECEIVER = "register_receiver";
+ /** Extras for passing the Assistant's ContentView's dimensions*/
+ public static final String EXTRA_CONTENT_VIEW_HEIGHT = "extra_content_view_height";
+ public static final String EXTRA_CONTENT_VIEW_WIDTH = "extra_content_view_width";
+ public static final String EXTRA_DISPLAY_POINT = "extra_display_point";
+
/** Test name suffixes */
public static final String ASSIST_STRUCTURE = "ASSIST_STRUCTURE";
public static final String DISABLE_CONTEXT = "DISABLE_CONTEXT";
public static final String FLAG_SECURE = "FLAG_SECURE";
public static final String LIFECYCLE = "LIFECYCLE";
+ public static final String SCREENSHOT = "SCREENSHOT";
+ public static final String EXTRA_ASSIST = "EXTRA_ASSIST";
+ public static final String VERIFY_CONTENT_VIEW = "VERIFY_CONTENT_VIEW";
+ public static final String TEXTVIEW = "TEXTVIEW";
+ public static final String LARGE_VIEW_HIERARCHY = "LARGE_VIEW_HIERARCHY";
+ public static final String WEBVIEW = "WEBVIEW";
+ public static final String FOCUS_CHANGE = "FOCUS_CHANGE";
/** Session intent constants */
public static final String HIDE_SESSION = "android.intent.action.hide_session";
- /**
- * The shim activity that starts the service associated with each test.
- */
+ /** Stub html view to load into WebView */
+ public static final String WEBVIEW_HTML_GREETING = "Hello WebView!";
+ public static final String WEBVIEW_HTML = "<html><body><div><p>" + WEBVIEW_HTML_GREETING
+ + "</p></div></body></html>";
+
+ /** Extra data to add to assist data and assist content */
+ private static Bundle EXTRA_ASSIST_BUNDLE;
+ private static String STRUCTURED_JSON;
+
+ public static final String getStructuredJSON() throws Exception {
+ if (STRUCTURED_JSON == null) {
+ STRUCTURED_JSON = new JSONObject()
+ .put("@type", "MusicRecording")
+ .put("@id", "https://example/music/recording")
+ .put("url", "android-app://com.example/https/music/album")
+ .put("name", "Album Title")
+ .put("hello", "hi there")
+ .put("knownNull", null)
+ .put("unicode value", "\ud800\udc35")
+ .put("empty string", "")
+ .put("LongString",
+ "lkasdjfalsdkfjalsdjfalskj9i9234jl1w23j4o123j412l3j421l3kj412l3kj1l3k4j32")
+ .put("\ud800\udc35", "any-value")
+ .put("key with spaces", "any-value")
+ .toString();
+ }
+ return STRUCTURED_JSON;
+ }
+
+ public static final Bundle getExtraAssistBundle() {
+ if (EXTRA_ASSIST_BUNDLE == null) {
+ EXTRA_ASSIST_BUNDLE = new Bundle();
+ addExtraAssistDataToBundle(EXTRA_ASSIST_BUNDLE);
+ }
+ return EXTRA_ASSIST_BUNDLE;
+ }
+
+ public static void addExtraAssistDataToBundle(Bundle data) {
+ data.putString("hello", "there");
+ data.putBoolean("isthis_true_or_false", true);
+ data.putInt("number", 123);
+ }
+
+ /** The shim activity that starts the service associated with each test. */
public static final String getTestActivity(String testCaseType) {
switch (testCaseType) {
- case ASSIST_STRUCTURE:
- return "service.AssistStructureActivity";
case DISABLE_CONTEXT:
+ // doesn't need to wait for activity to resume
+ // can be activated on top of any non-secure activity.
return "service.DisableContextActivity";
+ case ASSIST_STRUCTURE:
case FLAG_SECURE:
case LIFECYCLE:
+ case SCREENSHOT:
+ case EXTRA_ASSIST:
+ case VERIFY_CONTENT_VIEW:
+ case TEXTVIEW:
+ case LARGE_VIEW_HIERARCHY:
+ case WEBVIEW:
+ case FOCUS_CHANGE:
return "service.DelayedAssistantActivity";
default:
return "";
@@ -84,6 +166,9 @@
public static final ComponentName getTestAppComponent(String testCaseType) {
switch (testCaseType) {
case ASSIST_STRUCTURE:
+ case LARGE_VIEW_HIERARCHY:
+ return new ComponentName(
+ "android.assist.testapp", "android.assist.testapp.TestApp");
case DISABLE_CONTEXT:
return new ComponentName(
"android.assist.testapp", "android.assist.testapp.TestApp");
@@ -93,11 +178,39 @@
case LIFECYCLE:
return new ComponentName(
"android.assist.testapp", "android.assist.testapp.LifecycleActivity");
+ case SCREENSHOT:
+ return new ComponentName(
+ "android.assist.testapp", "android.assist.testapp.ScreenshotActivity");
+ case EXTRA_ASSIST:
+ return new ComponentName(
+ "android.assist.testapp", "android.assist.testapp.ExtraAssistDataActivity");
+ case TEXTVIEW:
+ return new ComponentName(
+ "android.assist.testapp", "android.assist.testapp.TextViewActivity");
+ case WEBVIEW:
+ return new ComponentName(
+ "android.assist.testapp", "android.assist.testapp.WebViewActivity");
+ case FOCUS_CHANGE:
+ return new ComponentName(
+ "android.assist.testapp", "android.assist.testapp.FocusChangeActivity");
default:
return new ComponentName("","");
}
}
+ /**
+ * Returns the amount of time to wait for assist data.
+ */
+ public static final int getAssistDataTimeout(String testCaseType) {
+ switch (testCaseType) {
+ case SCREENSHOT:
+ // needs to wait for 3p activity to resume before receiving assist data.
+ return TIMEOUT_MS + ACTIVITY_ONRESUME_TIMEOUT_MS;
+ default:
+ return TIMEOUT_MS;
+ }
+ }
+
public static final String toBundleString(Bundle bundle) {
if (bundle == null) {
return "*** Bundle is null ****";
diff --git a/tests/tests/assist/res/layout/multiple_text_views.xml b/tests/tests/assist/res/layout/multiple_text_views.xml
new file mode 100644
index 0000000..455d5e3
--- /dev/null
+++ b/tests/tests/assist/res/layout/multiple_text_views.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:scrollbars="vertical"
+ android:text="@string/text_too_large_to_fit" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:scrollbars="vertical"
+ android:text="@string/text_too_large_to_fit" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:scrollbars="vertical"
+ android:text="@string/text_too_large_to_fit" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:scrollbars="vertical"
+ android:text="@string/text_too_large_to_fit" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:scrollbars="vertical"
+ android:text="@string/text_too_large_to_fit" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:scrollbars="vertical"
+ android:text="@string/text_too_large_to_fit" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:scrollbars="vertical"
+ android:text="@string/text_too_large_to_fit" />
+
+ <ScrollView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:scrollbars="vertical"
+ android:text="@string/text_too_large_to_fit" />
+ </ScrollView>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/tests/assist/res/layout/screenshot_activity.xml b/tests/tests/assist/res/layout/screenshot_activity.xml
new file mode 100644
index 0000000..05051dc
--- /dev/null
+++ b/tests/tests/assist/res/layout/screenshot_activity.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/screenshot_activity"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+</RelativeLayout>
\ No newline at end of file
diff --git a/tests/tests/assist/res/layout/test_app.xml b/tests/tests/assist/res/layout/test_app.xml
new file mode 100644
index 0000000..3fbfd6d
--- /dev/null
+++ b/tests/tests/assist/res/layout/test_app.xml
@@ -0,0 +1,47 @@
+<?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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/welcome" />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="350dp"
+ android:orientation="vertical"
+ android:layout_gravity="bottom">
+ <FrameLayout
+ android:id="@+id/card1"
+ android:layout_width="match_parent"
+ android:layout_height="150dp"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
+ android:layout_marginTop="16dp"
+ android:elevation="3dp">
+ </FrameLayout>
+ <View
+ android:id="@+id/card2"
+ android:layout_width="match_parent"
+ android:layout_height="200dp"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
+ android:layout_marginTop="16dp"
+ android:elevation="3dp"/>
+ </LinearLayout>
+</RelativeLayout>
\ No newline at end of file
diff --git a/tests/tests/assist/res/layout/text_view.xml b/tests/tests/assist/res/layout/text_view.xml
new file mode 100644
index 0000000..9964ab6
--- /dev/null
+++ b/tests/tests/assist/res/layout/text_view.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ <TextView
+ android:id="@+id/text_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:focusable="false"
+ android:focusableInTouchMode="false"
+ android:scrollbars="vertical"
+ android:clickable="true"
+ android:text="@string/text_too_large_to_fit" />
+
+ <ScrollView
+ android:id="@+id/scroll_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:scrollbars="vertical"
+ android:text="@string/text_too_large_to_fit" />
+ </ScrollView>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/tests/assist/res/layout/webview.xml b/tests/tests/assist/res/layout/webview.xml
new file mode 100644
index 0000000..bdb8082
--- /dev/null
+++ b/tests/tests/assist/res/layout/webview.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ <WebView
+ android:id="@+id/webview"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ </WebView>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/tests/assist/res/values/strings.xml b/tests/tests/assist/res/values/strings.xml
new file mode 100644
index 0000000..8170d70
--- /dev/null
+++ b/tests/tests/assist/res/values/strings.xml
@@ -0,0 +1,284 @@
+<?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.
+-->
+<resources>
+ <string name="welcome">Hello there!</string>
+ <string name="testAppTitle">Assist Structure Test Activity</string>
+ <string name="screenshotActivityTitle">Screenshot Test Activity</string>
+ <string name="textViewActivityTitle">TextView Test Activity</string>
+ <string name="webViewActivityTitle">WebView Test Activity</string>
+ <string name="text_too_large_to_fit">❤ ☀ ☆ ☂ ☻ ♞ ☯ ☭ ☢ € →Hello هتاف للترحيب שלום
+ përshëndetje Добры дзень 您好 হ্যালো здравей მიესალმები Χαίρετε હેલો नमस्ते Nnọọ こんにちは ಹಲೋ
+ Сәлеметсіз бе ជំរាបសួរ 안녕하세요 ສະບາຍດີ ഹലോ हॅलो Сайн байна уу नमस्ते سلامהעלא ہیلو
+ မင်္ဂလာပါ ਸਤ ਸ੍ਰੀ ਅਕਾਲ Здравствуйте здраво ආයුබෝවන් ஹலோ హలో สวัสดี Pẹlẹ o
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+ convallis nunc et vestibulum. Sed et consequat quam, blandit varius tortor. Curabitur
+ accumsan nulla lectus, placerat condimentum odio elementum vel. Nulla erat ex, accumsan ut
+ enim sagittis, scelerisque efficitur ante. Nullam quis orci nec magna maximus malesuada ac
+ id sem. Nam sagittis erat risus, a accumsan neque congue sit amet. Nullam risus velit,
+ faucibus eget scelerisque et, maximus eget arcu. Sed porta sed libero ac imperdiet.
+
+ Nulla sem lectus, ullamcorper id dui vel, rutrum interdum augue. Proin aliquam nisi vitae
+ hendrerit tempor. Mauris porttitor velit et egestas feugiat. Vivamus eu dapibus libero,
+ quis fringilla urna. Suspendisse non turpis dui. Vivamus facilisis diam vitae est auctor
+ luctus. Etiam quis lectus viverra, interdum turpis eu, aliquam sem. Nulla vulputate lacinia
+ nisi a dictum. Cras faucibus vitae tortor at ullamcorper. Quisque sit amet sapien maximus,
+ ornare nisi non, imperdiet magna. Vestibulum tempor metus ac mi ultrices dapibus.
+
+ Suspendisse potenti. Mauris pellentesque lacinia tristique. Pellentesque vel dui quis sem
+ lacinia imperdiet feugiat vitae sem. Proin a arcu magna. Sed quis augue eu mi accumsan
+ pellentesque pretium in leo. Duis euismod purus mauris, ac tempor erat auctor non. Quisque
+ bibendum est pulvinar ex dapibus, ac tincidunt nibh tempus. Mauris sodales sem id purus
+ commodo iaculis. Pellentesque a quam dapibus, vehicula lectus at, tincidunt arcu. In
+ placerat porttitor urna quis consequat. Nullam feugiat nisl sed urna hendrerit, sed
+ elementum massa iaculis. Fusce sit amet turpis hendrerit, varius lorem sed, luctus mi.
+ Phasellus sit amet ex orci. Duis scelerisque nisl quis efficitur maximus. Curabitur vitae
+ accumsan nunc, eget varius nisi.
+
+ Fusce efficitur malesuada luctus. Aliquam dapibus tortor sit amet purus semper, sit amet
+ pretium lorem feugiat. Maecenas gravida sed arcu et placerat. Nulla facilisi. Cras placerat
+ rutrum mi, in rutrum mauris maximus at. Mauris eu suscipit ante. Nullam pharetra egestas
+ diam a viverra. Donec sem turpis, tempor malesuada est vel, blandit accumsan magna. In
+ iaculis velit in efficitur hendrerit. Nulla facilisi. Curabitur eget ligula lorem. Sed sit
+ amet dolor ut ligula malesuada condimentum. Phasellus molestie augue eget libero commodo,
+ vel blandit ex blandit.
+
+ Morbi cursus tortor ante, et tempus nisi tempus et. Suspendisse quis gravida diam. Aliquam
+ efficitur dolor sit amet sollicitudin varius. Etiam libero purus, ornare nec nulla vel,
+ ullamcorper blandit nisl. Sed vel consequat diam, id placerat sem. Donec quis elementum
+ urna. In posuere bibendum nunc, in condimentum justo blandit ac. Quisque enim lorem,
+ gravida at purus at, sollicitudin imperdiet erat. Ut consectetur rutrum ante, et pretium
+ odio iaculis a. Nullam a nibh vulputate, volutpat lectus eu, pellentesque felis. Nam
+ vehicula suscipit diam nec convallis. Quisque congue maximus sem, sit amet hendrerit leo
+ tempor et.
+
+ Nam eu consequat dui. Sed semper dignissim mattis. Integer tortor eros, tempor in lectus a,
+ lobortis aliquam dolor. Phasellus at sagittis magna. Nulla eleifend orci ac urna auctor,
+ sit amet luctus urna vulputate. Nulla venenatis venenatis erat ac finibus. Etiam
+ ullamcorper elementum suscipit. Morbi nec velit non mauris porta finibus. Nullam in
+ sagittis odio. Praesent eget nisl ut mauris vestibulum feugiat. Sed vulputate at elit et
+ cursus. Praesent viverra erat blandit nunc egestas, vel feugiat ex condimentum. Class
+ aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.
+
+ Nulla fermentum mattis urna, non gravida eros vestibulum et. Fusce porttitor augue turpis,
+ sit amet aliquam augue sodales non. Nunc neque odio, sagittis sed gravida euismod, commodo
+ at libero. Donec porttitor pulvinar neque vitae lobortis. Aliquam accumsan velit nec sapien
+ placerat egestas. Aliquam at tincidunt massa, et dignissim justo. Donec sapien ante, rutrum
+ et tristique a, commodo a massa.
+
+ Nunc placerat lobortis magna, et molestie lacus semper porta. Lorem ipsum dolor sit amet,
+ consectetur adipiscing elit. Phasellus ac ligula dui. Duis ultrices viverra eros fermentum
+ finibus. Integer ultrices, felis in accumsan volutpat, mi ligula hendrerit nunc, nec
+ accumsan mauris tellus sit amet metus. Ut pharetra enim et sapien sollicitudin, nec
+ ultricies urna pharetra. Morbi non tortor nec dui feugiat rutrum. Aliquam malesuada sodales
+ risus, sed congue nunc accumsan vitae. Etiam nunc magna, tempus non suscipit eu, feugiat ut
+ nibh. Maecenas et libero ut nisl pellentesque tempor nec vel quam. Etiam sem ligula,
+ ullamcorper non dolor a, viverra placerat nulla. Nullam dictum commodo dui, sed ultrices
+ enim sagittis eget. Morbi non consectetur lectus. In gravida, augue vitae pulvinar
+ molestie, ligula orci vulputate ex, at bibendum urna felis nec nibh. Sed nisl nunc, iaculis
+ at augue venenatis, fringilla accumsan velit. Curabitur nec augue porttitor, rutrum nisi
+ vitae, elementum orci.
+
+ Vestibulum eu tortor iaculis, dignissim velit quis, rhoncus dolor. Donec et tincidunt
+ nulla. Duis faucibus auctor erat ac ultricies. In a fermentum mi. Fusce vitae mi id sem
+ interdum tincidunt. Nulla hendrerit orci turpis, in maximus elit mollis eget. Aliquam erat
+ volutpat. Phasellus mattis est nibh, ut scelerisque ligula egestas eu. Ut molestie orci a
+ malesuada tempor. Sed tempus arcu id orci gravida faucibus. Vivamus ac lacinia neque, at
+ vehicula magna.
+
+ Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae;
+ Nullam aliquam, justo scelerisque egestas sodales, purus odio posuere arcu, ac ultrices
+ nunc felis non massa. Aliquam vulputate ipsum sed aliquet auctor. In pulvinar, eros sit
+ amet ultricies tristique, lacus ipsum scelerisque eros, nec vestibulum est lectus quis
+ lorem. Pellentesque ac augue ut eros mattis viverra vitae ut lacus. Phasellus imperdiet
+ efficitur elit eget tincidunt. Donec sodales metus at dolor pulvinar, at gravida nibh
+ facilisis. Sed nec tellus luctus, cursus lacus sed, euismod orci. Maecenas sit amet leo
+ orci. Nulla non leo non mi eleifend consequat sit amet vitae dui. Sed gravida gravida
+ justo, tincidunt ultrices justo semper vitae. Fusce at nisi nisl. Morbi molestie quis justo
+ a convallis. Curabitur massa lacus, feugiat quis mauris ac, malesuada viverra est.
+
+ Phasellus bibendum faucibus velit, ac scelerisque velit tincidunt eu. Curabitur quis
+ suscipit erat, ac feugiat odio. Nullam et sapien et nibh maximus posuere. Vivamus faucibus
+ justo eget dictum sollicitudin. Etiam at leo eget elit facilisis lobortis. Maecenas
+ bibendum tortor non erat pretium dignissim. Fusce id imperdiet augue. Suspendisse dignissim
+ magna vel odio viverra varius. Maecenas suscipit ante et lorem sodales vehicula. Quisque
+ vel magna id sem suscipit iaculis. Etiam in elementum risus.
+
+ Suspendisse odio nisi, pharetra et purus sit amet, placerat lobortis diam. Phasellus enim
+ nunc, posuere sed porta in, ornare eu ipsum. Phasellus imperdiet porta neque, vitae dapibus
+ tellus feugiat eget. Nulla sodales leo ac efficitur luctus. Vivamus eget ipsum quis ante
+ pulvinar blandit. Vestibulum a justo convallis justo elementum viverra ut sit amet nisl.
+ Suspendisse eget augue fermentum, sagittis risus a, rhoncus elit. Vestibulum in maximus
+ tortor, non vestibulum libero. Nunc accumsan neque a nisl dapibus, id laoreet neque congue.
+ Pellentesque sapien odio, fringilla non nulla nec, varius placerat diam. Aliquam
+ consectetur neque eu ipsum posuere, nec dignissim sem faucibus. Donec sit amet tempor
+ sapien. Nam at libero vel lorem dapibus ultrices a vel augue. Nunc facilisis justo ante,
+ mollis tristique velit aliquet quis. Mauris consectetur odio at urna bibendum aliquam.
+
+ Nullam lectus orci, hendrerit ut ultrices in, dapibus pellentesque nibh. Aliquam arcu
+ metus, lobortis vel dignissim id, tempus ut ante. Integer vitae ante augue. In hac habitasse
+ platea dictumst. Vestibulum in tellus ante. Cras nisi tellus, congue ac velit quis, rhoncus
+ ornare ligula. Sed facilisis gravida pellentesque. Praesent id ultrices orci, ac ultricies
+ arcu. Donec at ante quis augue faucibus congue. Donec mattis quam dui, ut vestibulum orci
+ tempor mattis. Phasellus in quam id tortor varius ullamcorper ac ac ante. Proin cursus
+ accumsan sem, vel finibus lectus eleifend ut. Donec efficitur feugiat diam id ultricies. In
+ quis euismod nisi. Vestibulum eget viverra sapien. Donec scelerisque nec elit vel viverra.
+
+ Sed mi urna, rutrum quis augue vel, aliquet placerat diam. Proin faucibus in odio et
+ consequat. Proin ut ex in mauris eleifend efficitur. Praesent ullamcorper sollicitudin urna,
+ sed mollis elit hendrerit non. Duis leo lorem, efficitur eu auctor sit amet, scelerisque
+ scelerisque magna. Mauris eget massa auctor, viverra arcu a, elementum nibh. Sed
+ pellentesque, nulla sed condimentum posuere, tortor metus congue sem, nec placerat neque
+ magna vitae purus.
+
+ Etiam at risus vitae sapien aliquam condimentum. Vestibulum id libero placerat purus
+ vehicula consectetur. Pellentesque sapien sapien, posuere at pulvinar at, ultrices sed est.
+ Maecenas nec condimentum ante. Aenean volutpat, ex condimentum hendrerit hendrerit, quam
+ nisl pharetra nibh, vitae bibendum nisl odio vel lacus. Morbi mi tellus, bibendum id mauris
+ eu, facilisis volutpat turpis. Maecenas rutrum convallis felis. Quisque eget feugiat felis.
+ Duis pellentesque iaculis massa ut facilisis. Donec nec commodo magna. Integer aliquet orci
+ a odio eleifend elementum. Quisque molestie, urna ut molestie eleifend, odio neque maximus
+ enim, eget viverra metus lectus eget quam. Fusce nec urna ac neque bibendum aliquam vel quis
+ dui. Fusce ac quam consequat, feugiat leo vitae, auctor felis.
+
+ Sed ac metus mauris. Sed sed velit ut tortor aliquam vestibulum at eu arcu. Etiam eu
+ posuere lacus. Maecenas id lacus quis sem mollis sodales. Quisque justo sapien, vulputate ac
+ mi ut, congue vestibulum orci. Donec euismod erat rutrum, laoreet urna sed, accumsan purus.
+ Donec eu quam a sapien condimentum accumsan. Sed at tellus lorem. Curabitur bibendum, arcu
+ sit amet finibus sodales, mi sem finibus sem, eget scelerisque tellus neque vel urna.
+ Suspendisse eu augue nec erat suscipit luctus sed non metus.
+
+ Suspendisse porttitor ex ipsum. Pellentesque tristique eros sed pharetra porttitor.
+ Quisque ut elit vehicula, aliquet est nec, faucibus tellus. Donec ex augue, congue eu
+ dignissim maximus, vestibulum at purus. Nulla quam enim, laoreet sit amet molestie vel,
+ dapibus nec tortor. Sed interdum massa ac orci gravida, vel viverra lacus lacinia. Donec
+ nisl lacus, fermentum at faucibus ac, consequat ut nibh. Praesent laoreet est augue, vitae
+ maximus dui efficitur sit amet. Cras ipsum tellus, tincidunt at volutpat non, tincidunt ut
+ elit. Morbi commodo sagittis gravida. Pellentesque sed ante massa. Phasellus a turpis non
+ turpis cursus consequat sed nec tortor. Proin et augue elit.
+
+ Duis finibus sem commodo rutrum pulvinar. In sollicitudin ante magna, vel facilisis
+ tellus fringilla vel. Nam purus ex, tincidunt eget varius at, euismod nec elit. Curabitur
+ consequat nulla vel nisi iaculis, ut mattis odio congue. Nulla et mollis tortor, a maximus
+ justo. Donec semper eros sed nunc rhoncus condimentum. Donec nibh purus, interdum non lectus
+ id, porta convallis eros.
+
+ Sed hendrerit, dui non sagittis sollicitudin, enim ex imperdiet sapien, et maximus lorem
+ dolor a enim. Nulla risus nisl, vestibulum at ornare posuere, congue in felis. Duis sagittis
+ id diam a varius. Donec viverra eu orci sodales commodo. Cras metus tortor, sodales vitae
+ auctor non, scelerisque a ante. Quisque sodales nisi libero, ut lobortis enim suscipit ut.
+ Cras mi ipsum, maximus non bibendum sit amet, pharetra quis ipsum. Vestibulum venenatis, odi
+ at hendrerit pretium, tellus diam auctor justo, non posuere quam mauris id nisl. Nam dolor
+ nibh, molestie et lectus et, scelerisque porta elit. Vestibulum viverra condimentum auctor.
+ In eros tortor, convallis sed quam eu, ultrices malesuada purus. Integer lorem quam,
+ ultricies at est consectetur, sagittis porttitor eros. Proin non risus vitae lacus
+ consectetur malesuada non pulvinar justo. Aliquam mollis nisi nunc, sit amet vulputate metus
+ sollicitudin vel.
+
+ Quisque auctor varius fermentum. Praesent mollis justo sit amet est consectetur, in
+ volutpat tellus mollis. Aenean at bibendum eros, at finibus nibh. Phasellus nec mi sem.
+ Mauris pellentesque dui sit amet lobortis aliquam. Ut nec massa at urna aliquam gravida vel
+ in magna. Donec consectetur sapien magna, a auctor sapien placerat eu.
+
+ Pellentesque aliquet ante sed lacus gravida rutrum. Maecenas euismod varius felis, nec
+ tempus metus tempus et. Nam convallis augue a massa scelerisque, vel pharetra dolor
+ scelerisque. Fusce porttitor mi a magna rutrum condimentum. Aliquam fermentum at turpis at
+ auctor. Nulla ut suscipit dui. Donec rutrum viverra aliquam. Donec elementum nisl sapien, ac
+ blandit risus porta facilisis. Proin tellus dolor, ornare vel magna sit amet, maximus
+ volutpat felis. Aenean euismod aliquet purus, at finibus nunc elementum eu. Ut faucibus
+ ullamcorper consectetur. Aenean egestas arcu id mauris elementum, at sollicitudin est
+ congue. Sed a odio mattis, sollicitudin erat ut, tristique dolor. Aliquam luctus risus quis
+ tellus semper, a vestibulum nulla viverra.
+
+ Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos
+ himenaeos. Vestibulum sit amet nisi felis. Praesent condimentum consequat lacus pulvinar
+ imperdiet. Praesent vel condimentum quam. Maecenas eu aliquet odio. Vestibulum sed nulla
+ mattis lacus porta bibendum a ac eros. Nunc porttitor sagittis laoreet. Duis porta eros at
+ congue tristique. Pellentesque quis fringilla neque, a hendrerit tellus. Pellentesque ac
+ nibh ac tellus pulvinar porttitor et in est. Integer blandit lorem libero, eu pulvinar
+ tellus posuere eget. Vivamus pretium nulla ligula, ut dapibus massa fringilla in.
+ Suspendisse consectetur sem non elit porta, id pellentesque erat dapibus. Quisque ex mi,
+ tempus et hendrerit nec, gravida quis odio. Ut at mi in leo scelerisque venenatis.
+
+ Ut sed tellus in risus tincidunt tempor ut at arcu. Maecenas ut convallis justo. In
+ rutrum urna eu massa rhoncus, eget condimentum augue vehicula. Nullam eget placerat erat.
+ Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.
+ Aenean at volutpat orci, a lobortis dolor. Sed consequat facilisis interdum. Fusce libero
+ neque, fringilla in congue a, vehicula rutrum ipsum. Nam ornare placerat vestibulum. Proin
+ nec orci velit. Pellentesque scelerisque gravida diam, ut tristique libero tempus eu. Nam
+ semper lacus nec nulla volutpat imperdiet non eget tortor. Sed et pellentesque ligula.
+
+ Aenean a dolor dolor. Curabitur ut placerat lacus, sit amet aliquet orci. Aliquam erat
+ volutpat. Cras mollis sit amet lectus ornare pretium. Vestibulum fringilla orci vel est
+ iaculis, at mattis quam condimentum. Vivamus semper elit consectetur lectus gravida, in
+ sollicitudin mi fringilla. Donec eget lorem in orci blandit aliquam. Pellentesque libero
+ tellus, dignissim id augue et, vulputate viverra nisl. Cum sociis natoque penatibus et
+ magnis dis parturient montes, nascetur ridiculus mus. Donec ac vulputate metus, eu suscipit
+ sem. Donec placerat, nulla at sodales hendrerit, orci tortor vestibulum purus, non pharetra
+ nulla purus posuere arcu.
+
+ Quisque feugiat elit sem, ac interdum diam pharetra nec. Curabitur sem libero, vulputate
+ eu libero vitae, volutpat facilisis ligula. Aenean maximus erat laoreet, interdum ante in,
+ ultrices nisi. Nullam nec efficitur sapien. Integer feugiat et tortor ac bibendum. Donec a
+ scelerisque tortor. Cras quis viverra diam, vitae viverra ipsum. Aliquam ultrices neque sem,
+ congue sodales elit tempus sit amet. Pellentesque habitant morbi tristique senectus et netus
+ et malesuada fames ac turpis egestas. Sed dignissim ipsum eget diam rhoncus, ut finibus
+ nulla pretium. Morbi suscipit nibh vel nisl posuere molestie. Maecenas posuere turpis et
+ rutrum consectetur. Morbi venenatis arcu non gravida vulputate. Vivamus auctor tellus
+ ullamcorper ligula vestibulum cursus. Nunc gravida sit amet nisl quis facilisis.
+
+ Praesent ut justo vestibulum, accumsan mi et, feugiat purus. Nullam pulvinar iaculis
+ pharetra. Aliquam pulvinar risus sit amet elit suscipit tincidunt. In hac habitasse platea
+ dictumst. Etiam eget velit ac magna lacinia efficitur. Vestibulum ante ipsum primis in
+ faucibus orci luctus et ultrices posuere cubilia Curae; Cras volutpat tempus sollicitudin.
+ Ut et ante elit.
+
+ Sed ac tortor justo. Fusce sed metus libero. Duis sagittis tortor ac ante sollicitudin,
+ nec efficitur nibh euismod. Donec porttitor cursus quam, in aliquam lorem feugiat ut.
+ Aliquam tempor lacus eu feugiat feugiat. Nunc pulvinar, libero at auctor commodo, metus diam
+ commodo lorem, in feugiat elit ex non ligula. Quisque at vestibulum sapien, nec facilisis
+ neque. Aenean luctus, arcu ut rhoncus luctus, est massa rhoncus mauris, nec luctus urna sem
+ quis massa. Nam elit felis, congue et ligula eget, ultricies tincidunt erat. Vivamus
+ eleifend no dui ac dictum. In nulla justo, pulvinar ut tristique sed, congue et orci.
+
+ Quisque imperdiet mi lectus, ac scelerisque augue posuere ut. Duis non pulvinar ipsum,
+ finibus risus. Donec ullamcorper nisl at sodales lobortis. Mauris neque leo, vestibulum sit
+ amet placerat vel, aliquet vel sapien. Morbi massa tellus, scelerisque quis nisl in, feugiat
+ posuere augue. Aenean congue sem ut ipsum vulputate rhoncus vitae at eros. Maecenas in velit
+ orci pellentesque lobortis ac at felis. Vestibulum odio quam, lacinia dapibus ornare eu,
+ vulputate a eros. Curabitur eleifend ornare tellus, non sollicitudin justo viverra in. Sed
+ sodales neque et lacus semper, in pharetra est consequat. Nunc vehicula volutpat lectus, sit
+ amet scelerisque nisi. Aenean sollicitudin, sem at ultricies efficitur, eros metus
+ nisl, et fringilla felis lacus non orci. Praesent eros libero, finibus in purus id,
+ tempor ipsum. Donec suscipit libero velit. Aliquam quis diam pharetra, cursus ipsum in,
+ gravida metus. Maecenas ultrices ligula a ullamcorper scelerisque.
+
+ Donec tincidunt felis turpis, id venenatis neque convallis in. Proin euismod ligula nec
+ urna vulputate, sed elementum mauris ultrices. Ut efficitur sem vel mi vestibulum placerat.
+ Sed fermentum lacus nec metus dictum, a commodo quam fermentum. Sed vel vulputate magna.
+ Integer convallis nisi sit amet mi lobortis pellentesque. In egestas porttitor elit eu
+ scelerisque. Suspendisse eleifend vel enim quis tincidunt. Sed placerat risus et pretium
+ porttitor. Nam justo mi, cursus eu pellentesque vel, bibendum ut nisl. Nulla condimentum
+ lorem, non sagittis lorem volutpat vitae. Mauris nec libero lorem. Vestibulum lacus ex,
+ vulputate non massa vitae, pellentesque vestibulum dolor.
+
+ Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae;
+ Suspendisse vitae erat nisl. Vestibulum elit ante, semper et semper sit amet, fringilla
+ sapien. Morbi ac nisi sit amet turpis tincidunt mattis ac eget nisl. Nunc a venenatis quam,
+ facilisis maximus odio. Aliquam erat volutpat. Maecenas leo enim, ornare a magna quis,
+ venenatis ornare nulla. Aliquam venenatis nibh et elit tincidunt, ut egestas lorem finibus.
+ Integer iaculis dolor sed enim blandit vestibulum. Nullam vel libero ultricies, sagittis
+ tortor non, molestie eros.</string>
+</resources>
diff --git a/tests/tests/assist/service/AndroidManifest.xml b/tests/tests/assist/service/AndroidManifest.xml
index cdbeef0..354d771 100644
--- a/tests/tests/assist/service/AndroidManifest.xml
+++ b/tests/tests/assist/service/AndroidManifest.xml
@@ -18,6 +18,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.assist.service">
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+
<application>
<uses-library android:name="android.test.runner" />
<service android:name=".MainInteractionService"
@@ -31,14 +33,7 @@
<action android:name="android.service.voice.VoiceInteractionService" />
</intent-filter>
</service>
- <activity android:name=".AssistStructureActivity" >
- <intent-filter>
- <action android:name="android.intent.action.START_TEST_ASSIST_STRUCTURE" />
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- </activity>
- <activity android:name=".DisableContextActivity"
- android:label="Disabled Context Activity">
+ <activity android:name=".DisableContextActivity" >
<intent-filter>
<action android:name="android.intent.action.START_TEST_DISABLE_CONTEXT" />
<category android:name="android.intent.category.DEFAULT" />
@@ -47,8 +42,13 @@
<activity android:name=".DelayedAssistantActivity"
android:label="Delay Assistant Start Activity">
<intent-filter>
+ <action android:name="android.intent.action.START_TEST_ASSIST_STRUCTURE" />
<action android:name="android.intent.action.START_TEST_LIFECYCLE" />
<action android:name="android.intent.action.START_TEST_FLAG_SECURE" />
+ <action android:name="android.intent.action.START_TEST_SCREENSHOT" />
+ <action android:name="android.intent.action.START_TEST_EXTRA_ASSIST" />
+ <action android:name="android.intent.action.START_TEST_TEXTVIEW" />
+ <action android:name="android.intent.action.START_TEST_LARGE_VIEW_HIERARCHY" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
diff --git a/tests/tests/assist/service/res/layout/assist_layer.xml b/tests/tests/assist/service/res/layout/assist_layer.xml
index 49f35c9..3677208 100644
--- a/tests/tests/assist/service/res/layout/assist_layer.xml
+++ b/tests/tests/assist/service/res/layout/assist_layer.xml
@@ -1,9 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/assist_layer"
- android:layout_width="match_parent"
- android:background="@color/assist_layer_background"
- android:layout_height="match_parent">
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/assist_layer"
+ android:layout_width="match_parent"
+ android:background="@color/assist_layer_background"
+ android:layout_height="match_parent">
<TextView
android:layout_centerInParent="true"
android:text="@string/test_assistant_text"
diff --git a/tests/tests/assist/service/src/android/voiceinteraction/service/AssistStructureActivity.java b/tests/tests/assist/service/src/android/voiceinteraction/service/AssistStructureActivity.java
deleted file mode 100644
index 784d63b..0000000
--- a/tests/tests/assist/service/src/android/voiceinteraction/service/AssistStructureActivity.java
+++ /dev/null
@@ -1,42 +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.assist.service;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.content.ComponentName;
-import android.os.Bundle;
-import android.util.Log;
-
-public class AssistStructureActivity extends Activity {
- static final String TAG = "VoiceInteractionMain";
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- }
-
- @Override
- public void onStart() {
- super.onStart();
- Intent intent = new Intent();
- intent.setComponent(new ComponentName(this, MainInteractionService.class));
- Log.i(TAG, "Starting service.");
- finish();
- startService(intent);
- }
-}
diff --git a/tests/tests/assist/service/src/android/voiceinteraction/service/DelayedAssistantActivity.java b/tests/tests/assist/service/src/android/voiceinteraction/service/DelayedAssistantActivity.java
index 31d1694..ddf43bd 100644
--- a/tests/tests/assist/service/src/android/voiceinteraction/service/DelayedAssistantActivity.java
+++ b/tests/tests/assist/service/src/android/voiceinteraction/service/DelayedAssistantActivity.java
@@ -24,13 +24,19 @@
import android.util.Log;
public class DelayedAssistantActivity extends Activity {
- static final String TAG = "DelatyedAssistantActivity";
+ static final String TAG = "DelayedAssistantActivity";
+ @Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = new Intent();
intent.setComponent(new ComponentName(this, MainInteractionService.class));
intent.putExtra(Utils.EXTRA_REGISTER_RECEIVER, true);
+ intent.putExtra(Utils.TESTCASE_TYPE, getIntent().getStringExtra(Utils.TESTCASE_TYPE));
+ intent.putExtra(Utils.DISPLAY_WIDTH_KEY,
+ getIntent().getIntExtra(Utils.DISPLAY_WIDTH_KEY, 0));
+ intent.putExtra(Utils.DISPLAY_HEIGHT_KEY,
+ getIntent().getIntExtra(Utils.DISPLAY_HEIGHT_KEY, 0));
finish();
ComponentName serviceName = startService(intent);
Log.i(TAG, "Started service: " + serviceName);
diff --git a/tests/tests/assist/service/src/android/voiceinteraction/service/DisableContextActivity.java b/tests/tests/assist/service/src/android/voiceinteraction/service/DisableContextActivity.java
index 52ba7ac..2fd540b 100644
--- a/tests/tests/assist/service/src/android/voiceinteraction/service/DisableContextActivity.java
+++ b/tests/tests/assist/service/src/android/voiceinteraction/service/DisableContextActivity.java
@@ -22,9 +22,6 @@
import android.os.Bundle;
import android.util.Log;
-/**
- * TODO(awlee): Change context on/off settings and test
- */
public class DisableContextActivity extends Activity {
static final String TAG = "DisableContextActivity";
@@ -38,8 +35,8 @@
super.onStart();
Intent intent = new Intent();
intent.setComponent(new ComponentName(this, MainInteractionService.class));
+ Log.i(TAG, "Starting service.");
finish();
- ComponentName serviceName = startService(intent);
- Log.i(TAG, "Started service: " + serviceName);
+ startService(intent);
}
}
diff --git a/tests/tests/assist/service/src/android/voiceinteraction/service/MainInteractionService.java b/tests/tests/assist/service/src/android/voiceinteraction/service/MainInteractionService.java
index 7530933..916d676 100644
--- a/tests/tests/assist/service/src/android/voiceinteraction/service/MainInteractionService.java
+++ b/tests/tests/assist/service/src/android/voiceinteraction/service/MainInteractionService.java
@@ -30,11 +30,17 @@
import android.service.voice.VoiceInteractionSession;
import android.util.Log;
+import java.lang.Exception;
+import java.lang.Override;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
public class MainInteractionService extends VoiceInteractionService {
static final String TAG = "MainInteractionService";
private Intent mIntent;
private boolean mReady = false;
- private BroadcastReceiver mBroadcastReceiver;
+ private BroadcastReceiver mBroadcastReceiver, mResumeReceiver;
+ private CountDownLatch mResumeLatch;
@Override
public void onReady() {
@@ -51,27 +57,37 @@
}
private void maybeStart() {
- if (mIntent == null || !mReady) {
+ if (mIntent == null || !mReady) {
Log.wtf(TAG, "Can't start session because either intent is null or onReady() "
+ "has not been called yet. mIntent = " + mIntent + ", mReady = " + mReady);
} else {
if (isActiveService(this, new ComponentName(this, getClass()))) {
if (mIntent.getBooleanExtra(Utils.EXTRA_REGISTER_RECEIVER, false)) {
- Log.i(TAG, "Registering receiver to start session later");
+ mResumeLatch = new CountDownLatch(1);
if (mBroadcastReceiver == null) {
mBroadcastReceiver = new MainInteractionServiceBroadcastReceiver();
- registerReceiver(mBroadcastReceiver,
- new IntentFilter(Utils.BROADCAST_INTENT_START_ASSIST));
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Utils.BROADCAST_INTENT_START_ASSIST);
+ registerReceiver(mBroadcastReceiver, filter);
+ Log.i(TAG, "Registered receiver to start session later");
+
+ IntentFilter resumeFilter = new IntentFilter(Utils.APP_3P_HASRESUMED);
+ mResumeReceiver = new MainServiceAppResumeReceiver();
+ registerReceiver(mResumeReceiver, resumeFilter);
+ Log.i(TAG, "Registered receiver for resuming activity");
}
sendBroadcast(new Intent(Utils.ASSIST_RECEIVER_REGISTERED));
- } else {
- Log.i(TAG, "Yay! about to start session");
- showSession(new Bundle(), VoiceInteractionSession.SHOW_WITH_ASSIST |
- VoiceInteractionSession.SHOW_WITH_SCREENSHOT);
- }
+ } else {
+ Log.i(TAG, "Yay! about to start session");
+ Bundle bundle = new Bundle();
+ bundle.putString(Utils.TESTCASE_TYPE,
+ mIntent.getStringExtra(Utils.TESTCASE_TYPE));
+ showSession(bundle, VoiceInteractionSession.SHOW_WITH_ASSIST |
+ VoiceInteractionSession.SHOW_WITH_SCREENSHOT);
+ }
} else {
Log.wtf(TAG, "**** Not starting MainInteractionService because" +
- " it is not set as the current voice interaction service");
+ " it is not set as the current voice interaction service");
}
}
}
@@ -79,8 +95,51 @@
private class MainInteractionServiceBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
+ Log.i(MainInteractionService.TAG, "Recieved broadcast to start session now.");
if (intent.getAction().equals(Utils.BROADCAST_INTENT_START_ASSIST)) {
- showSession(new Bundle(), SHOW_WITH_ASSIST | SHOW_WITH_SCREENSHOT);
+ String testCaseName = intent.getStringExtra(Utils.TESTCASE_TYPE);
+ Log.i(MainInteractionService.TAG, "trying to start 3p activity: " + testCaseName);
+ Bundle extras = intent.getExtras();
+ if (extras == null) {
+ extras = new Bundle();
+ }
+ if (testCaseName.equals(Utils.SCREENSHOT)) {
+ try {
+ // extra info to pass along to 3p activity.
+
+ Intent intent3p = new Intent();
+ Log.i(TAG, "starting the 3p app again");
+ intent3p.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent3p.setAction("android.intent.action.TEST_APP_" + testCaseName);
+ intent3p.setComponent(Utils.getTestAppComponent(testCaseName));
+ intent3p.putExtras(extras);
+ startActivity(intent3p);
+ if (!MainInteractionService.this.mResumeLatch
+ .await(Utils.ACTIVITY_ONRESUME_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ Log.i(TAG, "waited for 3p activity to resume");
+ }
+ } catch (Exception e) {
+ Log.i(TAG, "failed so reload 3p app: " + e.toString());
+ }
+ }
+ extras.putString(Utils.TESTCASE_TYPE, mIntent.getStringExtra(Utils.TESTCASE_TYPE));
+ MainInteractionService.this.showSession(
+ extras, SHOW_WITH_ASSIST | SHOW_WITH_SCREENSHOT);
+ }
+ }
+ }
+
+ private class MainServiceAppResumeReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(Utils.APP_3P_HASRESUMED)) {
+ Log.i(MainInteractionService.TAG,
+ "3p activity has resumed in this new receiver");
+ if (MainInteractionService.this.mResumeLatch != null) {
+ MainInteractionService.this.mResumeLatch.countDown();
+ } else {
+ Log.i(TAG, "mResumeLatch was null");
+ }
}
}
}
@@ -91,4 +150,4 @@
unregisterReceiver(mBroadcastReceiver);
}
}
-}
+}
\ No newline at end of file
diff --git a/tests/tests/assist/service/src/android/voiceinteraction/service/MainInteractionSession.java b/tests/tests/assist/service/src/android/voiceinteraction/service/MainInteractionSession.java
index f297b3e..7bca9be 100644
--- a/tests/tests/assist/service/src/android/voiceinteraction/service/MainInteractionSession.java
+++ b/tests/tests/assist/service/src/android/voiceinteraction/service/MainInteractionSession.java
@@ -24,15 +24,24 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Bitmap;
+import android.graphics.Color;
+
+import android.graphics.Point;
import android.os.Bundle;
import android.service.voice.VoiceInteractionSession;
+import android.util.DisplayMetrics;
import android.util.Log;
+import android.view.Display;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.Display;
+import android.view.ViewTreeObserver;
import java.io.ByteArrayOutputStream;
+import java.util.Date;
import android.assist.common.Utils;
+import android.view.WindowManager;
public class MainInteractionSession extends VoiceInteractionSession {
static final String TAG = "MainInteractionSession";
@@ -43,7 +52,13 @@
private boolean hasReceivedAssistData = false;
private boolean hasReceivedScreenshot = false;
+ private int mCurColor;
+ private int mDisplayHeight;
+ private int mDisplayWidth;
+ private Bitmap mScreenshot;
private BroadcastReceiver mReceiver;
+ private String mTestName;
+ private View mContentView;
MainInteractionSession(Context context) {
super(context);
@@ -78,13 +93,30 @@
@Override
public void onShow(Bundle args, int showFlags) {
- // set some content view.
- // TODO: check that the view takes up the whole screen.
- // check that interactor mode is for assist
if ((showFlags & SHOW_WITH_ASSIST) == 0) {
return;
}
+ mTestName = args.getString(Utils.TESTCASE_TYPE, "");
+ mCurColor = args.getInt(Utils.SCREENSHOT_COLOR_KEY);
+ mDisplayHeight = args.getInt(Utils.DISPLAY_HEIGHT_KEY);
+ mDisplayWidth = args.getInt(Utils.DISPLAY_WIDTH_KEY);
super.onShow(args, showFlags);
+ mContentView.getViewTreeObserver().addOnPreDrawListener(
+ new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ mContentView.getViewTreeObserver().removeOnPreDrawListener(this);
+ Display d = mContentView.getDisplay();
+ Point displayPoint = new Point();
+ d.getRealSize(displayPoint);
+ Intent intent = new Intent(Utils.BROADCAST_CONTENT_VIEW_HEIGHT);
+ intent.putExtra(Utils.EXTRA_CONTENT_VIEW_HEIGHT, mContentView.getHeight());
+ intent.putExtra(Utils.EXTRA_CONTENT_VIEW_WIDTH, mContentView.getWidth());
+ intent.putExtra(Utils.EXTRA_DISPLAY_POINT, displayPoint);
+ mContext.sendBroadcast(intent);
+ return true;
+ }
+ });
}
@Override
@@ -92,7 +124,7 @@
/*@Nullable*/ AssistContent content) {
Log.i(TAG, "onHandleAssist");
Log.i(TAG,
- String.format("Bundle: %s, Structure: %s, Content: %s", data, structure, content));
+ String.format("Bundle: %s, Structure: %s, Content: %s", data, structure, content));
super.onHandleAssist(data, structure, content);
// send to test to verify that this is accurate.
@@ -107,17 +139,52 @@
public void onHandleScreenshot(/*@Nullable*/ Bitmap screenshot) {
Log.i(TAG, String.format("onHandleScreenshot - Screenshot: %s", screenshot));
super.onHandleScreenshot(screenshot);
- ByteArrayOutputStream bs = new ByteArrayOutputStream();
+
if (screenshot != null) {
- screenshot.compress(Bitmap.CompressFormat.PNG, 50, bs);
- mAssistData.putByteArray(Utils.ASSIST_SCREENSHOT_KEY, bs.toByteArray());
+ mAssistData.putBoolean(Utils.ASSIST_SCREENSHOT_KEY, true);
+
+ if (mTestName.equals(Utils.SCREENSHOT)) {
+ boolean screenshotMatches = compareScreenshot(screenshot, mCurColor);
+ Log.i(TAG, "this is a screenshot test. Matches? " + screenshotMatches);
+ mAssistData.putBoolean(
+ Utils.COMPARE_SCREENSHOT_KEY, screenshotMatches);
+ }
} else {
- mAssistData.putByteArray(Utils.ASSIST_SCREENSHOT_KEY, null);
+ mAssistData.putBoolean(Utils.ASSIST_SCREENSHOT_KEY, false);
}
hasReceivedScreenshot = true;
maybeBroadcastResults();
}
+ private boolean compareScreenshot(Bitmap screenshot, int color) {
+ Point size = new Point(mDisplayWidth, mDisplayHeight);
+
+ if (screenshot.getWidth() != size.x || screenshot.getHeight() != size.y) {
+ Log.i(TAG, "width or height didn't match: " + size + " vs " + screenshot.getWidth()
+ + "," + screenshot.getHeight());
+ return false;
+ }
+ int[] pixels = new int[size.x * size.y];
+ screenshot.getPixels(pixels, 0, size.x, 0, 0, size.x, size.y);
+
+ int expectedColor = 0;
+ int wrongColor = 0;
+ for (int pixel : pixels) {
+ if (pixel == color) {
+ expectedColor += 1;
+ } else {
+ wrongColor += 1;
+ }
+ }
+
+ double colorRatio = (double) expectedColor / (expectedColor + wrongColor);
+ Log.i(TAG, "the ratio is " + colorRatio);
+ if (colorRatio < 0.6) {
+ return false;
+ }
+ return true;
+ }
+
private void maybeBroadcastResults() {
if (!hasReceivedAssistData) {
Log.i(TAG, "waiting for assist data before broadcasting results");
@@ -141,13 +208,7 @@
if (f == null) {
Log.wtf(TAG, "layout inflater was null");
}
- return f.inflate(R.layout.assist_layer,null);
- }
-
- class DoneReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- Log.i(TAG, "Done_broadcast " + intent.getAction());
- }
+ mContentView = f.inflate(R.layout.assist_layer,null);
+ return mContentView;
}
}
diff --git a/tests/tests/assist/src/android/assist/TestStartActivity.java b/tests/tests/assist/src/android/assist/TestStartActivity.java
deleted file mode 100644
index df9b534..0000000
--- a/tests/tests/assist/src/android/assist/TestStartActivity.java
+++ /dev/null
@@ -1,84 +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.assist;
-
-import android.assist.common.Utils;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.content.ComponentName;
-import android.os.Bundle;
-import android.util.Log;
-
-public class TestStartActivity extends Activity {
- static final String TAG = "TestStartActivity";
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- Log.i(TAG, " in onCreate");
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- Log.i(TAG, " in onResume");
- }
-
- public void startTest(String testCaseName) {
- Log.i(TAG, "Starting test activity for TestCaseType = " + testCaseName);
- Intent intent = new Intent();
- intent.putExtra(Utils.TESTCASE_TYPE, testCaseName);
- intent.setAction("android.intent.action.START_TEST_" + testCaseName);
- intent.setComponent(new ComponentName("android.assist.service",
- "android.assist." + Utils.getTestActivity(testCaseName)));
- startActivity(intent);
- }
-
- public void start3pApp(String testCaseName) {
- Intent intent = new Intent();
- intent.setAction("android.intent.action.TEST_APP_" + testCaseName);
- intent.setComponent(Utils.getTestAppComponent(testCaseName));
- startActivity(intent);
- }
-
- @Override protected void onPause() {
- Log.i(TAG, " in onPause");
- super.onPause();
- }
-
- @Override protected void onStart() {
- super.onStart();
- Log.i(TAG, " in onStart");
- }
-
- @Override protected void onRestart() {
- super.onRestart();
- Log.i(TAG, " in onRestart");
- }
-
- @Override protected void onStop() {
- Log.i(TAG, " in onStop");
- super.onStop();
- }
-
- @Override
- protected void onDestroy() {
- Log.i(TAG, " in onDestroy");
- super.onDestroy();
- }
-}
diff --git a/tests/tests/assist/src/android/assist/cts/AssistStructureTest.java b/tests/tests/assist/src/android/assist/cts/AssistStructureTest.java
index 763ecef..4c1b1f3 100644
--- a/tests/tests/assist/src/android/assist/cts/AssistStructureTest.java
+++ b/tests/tests/assist/src/android/assist/cts/AssistStructureTest.java
@@ -18,7 +18,15 @@
import android.assist.common.Utils;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.provider.Settings;
+import android.util.Log;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
/**
@@ -26,10 +34,13 @@
*/
public class AssistStructureTest extends AssistTestBase {
- static final String TAG = "AssistStructureTest";
-
+ private static final String TAG = "AssistStructureTest";
private static final String TEST_CASE_TYPE = Utils.ASSIST_STRUCTURE;
+ private BroadcastReceiver mReceiver;
+ private CountDownLatch mHasResumedLatch = new CountDownLatch(1);
+ private CountDownLatch mReadyLatch = new CountDownLatch(1);
+
public AssistStructureTest() {
super();
}
@@ -37,13 +48,63 @@
@Override
protected void setUp() throws Exception {
super.setUp();
+ setUpAndRegisterReceiver();
startTestActivity(TEST_CASE_TYPE);
- waitForBroadcast();
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ super.tearDown();
+ if (mReceiver != null) {
+ mContext.unregisterReceiver(mReceiver);
+ mReceiver = null;
+ }
+ }
+
+ private void setUpAndRegisterReceiver() {
+ if (mReceiver != null) {
+ mContext.unregisterReceiver(mReceiver);
+ }
+ mReceiver = new AssistStructureTestBroadcastReceiver();
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Utils.APP_3P_HASRESUMED);
+ filter.addAction(Utils.ASSIST_RECEIVER_REGISTERED);
+ mContext.registerReceiver(mReceiver, filter);
+ }
+
+ private void waitForOnResume() throws Exception {
+ Log.i(TAG, "waiting for onResume() before continuing");
+ if (!mHasResumedLatch.await(Utils.ACTIVITY_ONRESUME_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ fail("Activity failed to resume in " + Utils.ACTIVITY_ONRESUME_TIMEOUT_MS + "msec");
+ }
}
public void testAssistStructure() throws Exception {
+ mTestActivity.start3pApp(TEST_CASE_TYPE);
+ mTestActivity.startTest(TEST_CASE_TYPE);
+ waitForAssistantToBeReady(mReadyLatch);
+ waitForOnResume();
+ startSession();
+ waitForContext();
verifyAssistDataNullness(false, false, false, false);
+
verifyAssistStructure(Utils.getTestAppComponent(TEST_CASE_TYPE),
- false /*FLAG_SECURE set*/);
+ false /*FLAG_SECURE set*/);
}
-}
\ No newline at end of file
+
+ private class AssistStructureTestBroadcastReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(Utils.APP_3P_HASRESUMED)) {
+ if (mHasResumedLatch != null) {
+ mHasResumedLatch.countDown();
+ }
+ } else if (action.equals(Utils.ASSIST_RECEIVER_REGISTERED)) {
+ if (mReadyLatch != null) {
+ mReadyLatch.countDown();
+ }
+ }
+ }
+ }
+}
diff --git a/tests/tests/assist/src/android/assist/cts/AssistTest.java b/tests/tests/assist/src/android/assist/cts/AssistTest.java
deleted file mode 100644
index 5241c4e..0000000
--- a/tests/tests/assist/src/android/assist/cts/AssistTest.java
+++ /dev/null
@@ -1,46 +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.assist.cts;
-
-import android.assist.TestStartActivity;
-
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Bundle;
-import android.test.ActivityInstrumentationTestCase2;
-import android.util.Log;
-
-import junit.framework.Assert;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import android.assist.common.Utils;
-
-public class AssistTest extends ActivityInstrumentationTestCase2<TestStartActivity> {
- static final String TAG = "AssistTest";
- private static final int TIMEOUT_MS = 2 * 1000;
-
- public AssistTest() {
- super(TestStartActivity.class);
- }
-}
diff --git a/tests/tests/assist/src/android/assist/cts/AssistTestBase.java b/tests/tests/assist/src/android/assist/cts/AssistTestBase.java
index a7e7087..46fb8d9 100644
--- a/tests/tests/assist/src/android/assist/cts/AssistTestBase.java
+++ b/tests/tests/assist/src/android/assist/cts/AssistTestBase.java
@@ -16,40 +16,60 @@
package android.assist.cts;
-import android.assist.TestStartActivity;
+import android.assist.cts.TestStartActivity;
import android.assist.common.Utils;
import android.app.assist.AssistContent;
import android.app.assist.AssistStructure;
+import android.app.assist.AssistStructure.ViewNode;
+import android.app.assist.AssistStructure.WindowNode;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.cts.util.SystemUtil;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
+import android.graphics.Color;
+import android.graphics.Point;
+import android.graphics.Rect;
import android.os.Bundle;
import android.provider.Settings;
import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
+import android.view.Display;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.webkit.WebView;
+import android.widget.EditText;
+import android.widget.TextView;
+import java.lang.Math;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class AssistTestBase extends ActivityInstrumentationTestCase2<TestStartActivity> {
- static final String TAG = "AssistTestBase";
+ private static final String TAG = "AssistTestBase";
protected TestStartActivity mTestActivity;
protected AssistContent mAssistContent;
protected AssistStructure mAssistStructure;
- protected Bitmap mScreenshot;
+ protected boolean mScreenshot;
+ protected Bitmap mAppScreenshot;
protected BroadcastReceiver mReceiver;
protected Bundle mAssistBundle;
protected Context mContext;
- protected CountDownLatch mLatch, mAssistantReadyLatch;
+ protected CountDownLatch mLatch, mScreenshotLatch, mHasResumedLatch;
+ protected boolean mScreenshotMatches;
+ private Point mDisplaySize;
private String mTestName;
+ private View mView;
public AssistTestBase() {
super(TestStartActivity.class);
@@ -58,29 +78,49 @@
@Override
protected void setUp() throws Exception {
super.setUp();
- mAssistantReadyLatch = new CountDownLatch(1);
mContext = getInstrumentation().getTargetContext();
SystemUtil.runShellCommand(getInstrumentation(),
- "settings put secure assist_structure_enabled 1");
+ "settings put secure assist_structure_enabled 1");
SystemUtil.runShellCommand(getInstrumentation(),
- "settings put secure assist_screenshot_enabled 1");
+ "settings put secure assist_screenshot_enabled 1");
logContextAndScreenshotSetting();
+
+ // reset old values
+ mScreenshotMatches = false;
+ mScreenshot = false;
+ mAssistStructure = null;
+ mAssistContent = null;
+ mAssistBundle = null;
+
+ if (mReceiver != null) {
+ mContext.unregisterReceiver(mReceiver);
+ }
+ mReceiver = new TestResultsReceiver();
+ mContext.registerReceiver(mReceiver,
+ new IntentFilter(Utils.BROADCAST_ASSIST_DATA_INTENT));
}
@Override
protected void tearDown() throws Exception {
- mContext.unregisterReceiver(mReceiver);
mTestActivity.finish();
- super.tearDown();
mContext.sendBroadcast(new Intent(Utils.HIDE_SESSION));
+ if (mReceiver != null) {
+ mContext.unregisterReceiver(mReceiver);
+ mReceiver = null;
+ }
+ super.tearDown();
}
+ /**
+ * Starts the shim service activity
+ */
protected void startTestActivity(String testName) {
Intent intent = new Intent();
mTestName = testName;
intent.setAction("android.intent.action.TEST_START_ACTIVITY_" + testName);
intent.setComponent(new ComponentName(getInstrumentation().getContext(),
- TestStartActivity.class));
+ TestStartActivity.class));
+ intent.putExtra(Utils.TESTCASE_TYPE, testName);
setActivityIntent(intent);
mTestActivity = getActivity();
}
@@ -88,9 +128,9 @@
/**
* Called when waiting for Assistant's Broadcast Receiver to be setup
*/
- public void waitForAssistantToBeReady() throws Exception {
+ public void waitForAssistantToBeReady(CountDownLatch latch) throws Exception {
Log.i(TAG, "waiting for assistant to be ready before continuing");
- if (!mAssistantReadyLatch.await(Utils.TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ if (!latch.await(Utils.TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
fail("Assistant was not ready before timeout of: " + Utils.TIMEOUT_MS + "msec");
}
}
@@ -99,11 +139,33 @@
* Send broadcast to MainInteractionService to start a session
*/
protected void startSession() {
- mContext.sendBroadcast(new Intent(Utils.BROADCAST_INTENT_START_ASSIST));
+ startSession(mTestName, new Bundle());
+ }
+
+ protected void startSession(String testName, Bundle extras) {
+ Intent intent = new Intent(Utils.BROADCAST_INTENT_START_ASSIST);
+ Log.i(TAG, "passed in class test name is: " + testName);
+ intent.putExtra(Utils.TESTCASE_TYPE, testName);
+ addDimensionsToIntent(intent);
+ intent.putExtras(extras);
+ mContext.sendBroadcast(intent);
}
/**
- * Called after startTestActivity
+ * Calculate display dimensions (including navbar) to pass along in the given intent.
+ */
+ private void addDimensionsToIntent(Intent intent) {
+ if (mDisplaySize == null) {
+ Display display = mTestActivity.getWindowManager().getDefaultDisplay();
+ mDisplaySize = new Point();
+ display.getRealSize(mDisplaySize);
+ }
+ intent.putExtra(Utils.DISPLAY_WIDTH_KEY, mDisplaySize.x);
+ intent.putExtra(Utils.DISPLAY_HEIGHT_KEY, mDisplaySize.y);
+ }
+
+ /**
+ * Called after startTestActivity. Includes check for receiving context.
*/
protected boolean waitForBroadcast() throws Exception {
mTestActivity.start3pApp(mTestName);
@@ -113,22 +175,24 @@
protected boolean waitForContext() throws Exception {
mLatch = new CountDownLatch(1);
+
if (mReceiver != null) {
mContext.unregisterReceiver(mReceiver);
}
mReceiver = new TestResultsReceiver();
mContext.registerReceiver(mReceiver,
- new IntentFilter(Utils.BROADCAST_ASSIST_DATA_INTENT));
+ new IntentFilter(Utils.BROADCAST_ASSIST_DATA_INTENT));
- if (!mLatch.await(Utils.TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
- fail("Failed to receive broadcast in " + Utils.TIMEOUT_MS + "msec");
- return false;
+ if (!mLatch.await(Utils.getAssistDataTimeout(mTestName), TimeUnit.MILLISECONDS)) {
+ fail("Fail to receive broadcast in " + Utils.getAssistDataTimeout(mTestName) + "msec");
}
+ Log.i(TAG, "Received broadcast with all information.");
return true;
}
/**
* Checks that the nullness of values are what we expect.
+ *
* @param isBundleNull True if assistBundle should be null.
* @param isStructureNull True if assistStructure should be null.
* @param isContentNull True if assistContent should be null.
@@ -139,27 +203,44 @@
if ((mAssistContent == null) != isContentNull) {
fail(String.format("Should %s have been null - AssistContent: %s",
- isContentNull? "":"not", mAssistContent));
+ isContentNull ? "" : "not", mAssistContent));
}
if ((mAssistStructure == null) != isStructureNull) {
fail(String.format("Should %s have been null - AssistStructure: %s",
- isStructureNull ? "" : "not", mAssistStructure));
+ isStructureNull ? "" : "not", mAssistStructure));
}
if ((mAssistBundle == null) != isBundleNull) {
fail(String.format("Should %s have been null - AssistBundle: %s",
- isBundleNull? "":"not", mAssistBundle));
+ isBundleNull ? "" : "not", mAssistBundle));
}
- if ((mScreenshot == null) != isScreenshotNull) {
+ if (mScreenshot == isScreenshotNull) {
fail(String.format("Should %s have been null - Screenshot: %s",
- isScreenshotNull? "":"not", mScreenshot));
+ isScreenshotNull ? "":"not", mScreenshot));
}
}
/**
- * Traverses and compares the view heirarchy of the backgroundApp and the view we expect.
+ * Sends a broadcast with the specified scroll positions to the test app.
+ */
+ protected void scrollTestApp(int scrollX, int scrollY, boolean scrollTextView,
+ boolean scrollScrollView) {
+ mTestActivity.scrollText(scrollX, scrollY, scrollTextView, scrollScrollView);
+ Intent intent = null;
+ if (scrollTextView) {
+ intent = new Intent(Utils.SCROLL_TEXTVIEW_ACTION);
+ } else if (scrollScrollView) {
+ intent = new Intent(Utils.SCROLL_SCROLLVIEW_ACTION);
+ }
+ intent.putExtra(Utils.SCROLL_X_POSITION, scrollX);
+ intent.putExtra(Utils.SCROLL_Y_POSITION, scrollY);
+ mContext.sendBroadcast(intent);
+ }
+
+ /**
+ * Verifies the view hierarchy of the backgroundApp matches the assist structure.
*
* @param backgroundApp ComponentName of app the assistant is invoked upon
* @param isSecureWindow Denotes whether the activity has FLAG_SECURE set
@@ -167,28 +248,212 @@
protected void verifyAssistStructure(ComponentName backgroundApp, boolean isSecureWindow) {
// Check component name matches
assertEquals(backgroundApp.flattenToString(),
- mAssistStructure.getActivityComponent().flattenToString());
+ mAssistStructure.getActivityComponent().flattenToString());
- int numWindows = mAssistStructure.getWindowNodeCount();
- assertEquals(1, numWindows);
- for (int i = 0; i < numWindows; i++) {
- AssistStructure.ViewNode node = mAssistStructure.getWindowNodeAt(i).getRootViewNode();
- // TODO: Actually traverse the view heirarchy and verify it matches what we expect
- // If isSecureWindow, will not have any children.
- }
+ Log.i(TAG, "Traversing down structure for: " + backgroundApp.flattenToString());
+ mView = mTestActivity.findViewById(android.R.id.content).getRootView();
+ verifyHierarchy(mAssistStructure, isSecureWindow);
}
protected void logContextAndScreenshotSetting() {
Log.i(TAG, "Context is: " + Settings.Secure.getString(
- mContext.getContentResolver(), "assist_structure_enabled"));
+ mContext.getContentResolver(), "assist_structure_enabled"));
Log.i(TAG, "Screenshot is: " + Settings.Secure.getString(
- mContext.getContentResolver(), "assist_screenshot_enabled"));
+ mContext.getContentResolver(), "assist_screenshot_enabled"));
+ }
+
+ /**
+ * Recursively traverse and compare properties in the View hierarchy with the Assist Structure.
+ */
+ public void verifyHierarchy(AssistStructure structure, boolean isSecureWindow) {
+ Log.i(TAG, "verifyHierarchy");
+ Window mWindow = mTestActivity.getWindow();
+
+ int numWindows = structure.getWindowNodeCount();
+ // TODO: multiple windows?
+ assertEquals("Number of windows don't match", 1, numWindows);
+
+ for (int i = 0; i < numWindows; i++) {
+ AssistStructure.WindowNode windowNode = structure.getWindowNodeAt(i);
+ Log.i(TAG, "Title: " + windowNode.getTitle());
+ // verify top level window bounds are as big as the screen and pinned to 0,0
+ assertEquals("Window left position wrong: was " + windowNode.getLeft(),
+ windowNode.getLeft(), 0);
+ assertEquals("Window top position wrong: was " + windowNode.getTop(),
+ windowNode.getTop(), 0);
+
+ traverseViewAndStructure(
+ mView,
+ windowNode.getRootViewNode(),
+ isSecureWindow);
+ }
+ }
+
+ private void traverseViewAndStructure(View parentView, ViewNode parentNode,
+ boolean isSecureWindow) {
+ ViewGroup parentGroup;
+
+ if (parentView == null && parentNode == null) {
+ Log.i(TAG, "Views are null, done traversing this branch.");
+ return;
+ } else if (parentNode == null || parentView == null) {
+ fail(String.format("Views don't match. View: %s, Node: %s", parentView, parentNode));
+ }
+
+ // Debugging
+ Log.i(TAG, "parentView is of type: " + parentView.getClass().getName());
+ if (parentView instanceof ViewGroup) {
+ for (int childInt = 0; childInt < ((ViewGroup) parentView).getChildCount();
+ childInt++) {
+ Log.i(TAG,
+ "viewchild" + childInt + " is of type: "
+ + ((ViewGroup) parentView).getChildAt(childInt).getClass().getName());
+ }
+ }
+ String parentViewId = null;
+ if (parentView.getId() > 0) {
+ parentViewId = mTestActivity.getResources().getResourceEntryName(parentView.getId());
+ Log.i(TAG, "View ID: " + parentViewId);
+ }
+
+ Log.i(TAG, "parentNode is of type: " + parentNode.getClassName());
+ for (int nodeInt = 0; nodeInt < parentNode.getChildCount(); nodeInt++) {
+ Log.i(TAG,
+ "nodechild" + nodeInt + " is of type: "
+ + parentNode.getChildAt(nodeInt).getClassName());
+ }
+ Log.i(TAG, "Node ID: " + parentNode.getIdEntry());
+
+ assertEquals("IDs do not match", parentViewId, parentNode.getIdEntry());
+
+ int numViewChildren = 0;
+ int numNodeChildren = 0;
+ if (parentView instanceof ViewGroup) {
+ numViewChildren = ((ViewGroup) parentView).getChildCount();
+ }
+ numNodeChildren = parentNode.getChildCount();
+
+ if (isSecureWindow) {
+ assertTrue("ViewNode property isAssistBlocked is false", parentNode.isAssistBlocked());
+ assertEquals("Secure window should only traverse root node.", 0, numNodeChildren);
+ isSecureWindow = false;
+ } else if (parentNode.getClassName().equals("android.webkit.WebView")) {
+ // WebView will also appear to have no children while the node does, traverse node
+ assertTrue("AssistStructure returned a WebView where the view wasn't one",
+ parentView instanceof WebView);
+
+ boolean textInWebView = false;
+
+ for (int i = numNodeChildren - 1; i >= 0; i--) {
+ textInWebView |= traverseWebViewForText(parentNode.getChildAt(i));
+ }
+ assertTrue("Did not find expected strings inside WebView", textInWebView);
+ } else {
+ assertEquals("Number of children did not match.", numViewChildren, numNodeChildren);
+
+ verifyViewProperties(parentView, parentNode);
+
+ if (parentView instanceof ViewGroup) {
+ parentGroup = (ViewGroup) parentView;
+
+ // TODO: set a max recursion level
+ for (int i = numNodeChildren - 1; i >= 0; i--) {
+ View childView = parentGroup.getChildAt(i);
+ ViewNode childNode = parentNode.getChildAt(i);
+
+ // if isSecureWindow, should not have reached this point.
+ assertFalse(isSecureWindow);
+ traverseViewAndStructure(childView, childNode, isSecureWindow);
+ }
+ }
+ }
+ }
+
+ /**
+ * Return true if the expected strings are found in the WebView, else fail.
+ */
+ private boolean traverseWebViewForText(ViewNode parentNode) {
+ boolean textFound = false;
+ if (parentNode.getText() != null
+ && parentNode.getText().toString().equals(Utils.WEBVIEW_HTML_GREETING)) {
+ return true;
+ }
+ for (int i = parentNode.getChildCount() - 1; i >= 0; i--) {
+ textFound |= traverseWebViewForText(parentNode.getChildAt(i));
+ }
+ return textFound;
+ }
+
+ /**
+ * Compare view properties of the view hierarchy with that reported in the assist structure.
+ */
+ private void verifyViewProperties(View parentView, ViewNode parentNode) {
+ assertEquals("Left positions do not match.", parentView.getLeft(), parentNode.getLeft());
+ assertEquals("Top positions do not match.", parentView.getTop(), parentNode.getTop());
+
+ int viewId = parentView.getId();
+
+ if (viewId > 0) {
+ if (parentNode.getIdEntry() != null) {
+ assertEquals("View IDs do not match.",
+ mTestActivity.getResources().getResourceEntryName(viewId),
+ parentNode.getIdEntry());
+ }
+ } else {
+ assertNull("View Node should not have an ID.", parentNode.getIdEntry());
+ }
+
+ Log.i(TAG, "parent text: " + parentNode.getText());
+ if (parentView instanceof TextView) {
+ Log.i(TAG, "view text: " + ((TextView) parentView).getText());
+ }
+
+
+ assertEquals("Scroll X does not match.", parentView.getScrollX(), parentNode.getScrollX());
+ assertEquals("Scroll Y does not match.", parentView.getScrollY(), parentNode.getScrollY());
+ assertEquals("Heights do not match.", parentView.getHeight(), parentNode.getHeight());
+ assertEquals("Widths do not match.", parentView.getWidth(), parentNode.getWidth());
+
+ if (parentView instanceof TextView) {
+ if (parentView instanceof EditText) {
+ assertEquals("Text selection start does not match",
+ ((EditText)parentView).getSelectionStart(), parentNode.getTextSelectionStart());
+ assertEquals("Text selection end does not match",
+ ((EditText)parentView).getSelectionEnd(), parentNode.getTextSelectionEnd());
+ }
+ TextView textView = (TextView) parentView;
+ assertEquals(textView.getTextSize(), parentNode.getTextSize());
+ String viewString = textView.getText().toString();
+ String nodeString = parentNode.getText().toString();
+
+ if (parentNode.getScrollX() == 0 && parentNode.getScrollY() == 0) {
+ Log.i(TAG, "Verifying text within TextView at the beginning");
+ Log.i(TAG, "view string: " + viewString);
+ Log.i(TAG, "node string: " + nodeString);
+ assertTrue("String length is unexpected: original string - " + viewString.length() +
+ ", string in AssistData - " + nodeString.length(),
+ viewString.length() >= nodeString.length());
+ assertTrue("Expected a longer string to be shown. expected: "
+ + Math.min(viewString.length(), 30) + " was: " + nodeString
+ .length(),
+ nodeString.length() >= Math.min(viewString.length(), 30));
+ for (int x = 0; x < parentNode.getText().length(); x++) {
+ assertEquals("Char not equal at index: " + x,
+ ((TextView) parentView).getText().toString().charAt(x),
+ parentNode.getText().charAt(x));
+ }
+ } else if (parentNode.getScrollX() == parentView.getWidth()) {
+
+ }
+ } else {
+ assertNull(parentNode.getText());
+ }
}
class TestResultsReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equalsIgnoreCase(Utils.BROADCAST_ASSIST_DATA_INTENT)) { // not necessary?
+ if (intent.getAction().equalsIgnoreCase(Utils.BROADCAST_ASSIST_DATA_INTENT)) {
Log.i(TAG, "Received broadcast with assist data.");
Bundle assistData = intent.getExtras();
AssistTestBase.this.mAssistBundle = assistData.getBundle(Utils.ASSIST_BUNDLE_KEY);
@@ -197,17 +462,20 @@
AssistTestBase.this.mAssistContent = assistData.getParcelable(
Utils.ASSIST_CONTENT_KEY);
- byte[] bitmapArray = assistData.getByteArray(Utils.ASSIST_SCREENSHOT_KEY);
- if (bitmapArray != null) {
- AssistTestBase.this.mScreenshot = BitmapFactory.decodeByteArray(
- bitmapArray, 0, bitmapArray.length);
- } else {
- AssistTestBase.this.mScreenshot = null;
- }
+ AssistTestBase.this.mScreenshot =
+ assistData.getBoolean(Utils.ASSIST_SCREENSHOT_KEY, false);
+
+ AssistTestBase.this.mScreenshotMatches = assistData.getBoolean(
+ Utils.COMPARE_SCREENSHOT_KEY, false);
if (mLatch != null) {
+ Log.i(AssistTestBase.TAG, "counting down latch. received assist data.");
mLatch.countDown();
}
+ } else if (intent.getAction().equals(Utils.APP_3P_HASRESUMED)) {
+ if (mHasResumedLatch != null) {
+ mHasResumedLatch.countDown();
+ }
}
}
}
diff --git a/tests/tests/assist/src/android/assist/cts/AssistantContentViewTest.java b/tests/tests/assist/src/android/assist/cts/AssistantContentViewTest.java
new file mode 100644
index 0000000..557134b
--- /dev/null
+++ b/tests/tests/assist/src/android/assist/cts/AssistantContentViewTest.java
@@ -0,0 +1,113 @@
+/*
+ * 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.assist.cts;
+
+import android.assist.cts.TestStartActivity;
+import android.assist.common.Utils;
+
+import android.app.Activity;
+import android.app.assist.AssistContent;
+import android.app.assist.AssistStructure;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.cts.util.SystemUtil;
+import android.graphics.Point;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import java.lang.Override;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/** Test verifying the Content View of the Assistant */
+public class AssistantContentViewTest extends AssistTestBase {
+ private static final String TAG = "ContentViewTest";
+ private BroadcastReceiver mReceiver;
+ private CountDownLatch mContentViewLatch, mReadyLatch;
+ private Intent mIntent;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ mContentViewLatch = new CountDownLatch(1);
+ mReadyLatch = new CountDownLatch(1);
+ setUpAndRegisterReceiver();
+ startTestActivity(Utils.VERIFY_CONTENT_VIEW);
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ super.tearDown();
+ if (mReceiver != null) {
+ mContext.unregisterReceiver(mReceiver);
+ mReceiver = null;
+ }
+ }
+
+ private void setUpAndRegisterReceiver() {
+ if (mReceiver != null) {
+ mContext.unregisterReceiver(mReceiver);
+ }
+ mReceiver = new AssistantContentViewReceiver();
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Utils.BROADCAST_CONTENT_VIEW_HEIGHT);
+ filter.addAction(Utils.ASSIST_RECEIVER_REGISTERED);
+ mContext.registerReceiver(mReceiver, filter);
+
+ }
+
+ private void waitForContentView() throws Exception {
+ Log.i(TAG, "waiting for the Assistant's Content View before continuing");
+ if (!mContentViewLatch.await(Utils.TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ fail("failed to receive content view in " + Utils.TIMEOUT_MS + "msec");
+ }
+ }
+
+ public void testAssistantContentViewDimens() throws Exception {
+ mTestActivity.startTest(Utils.VERIFY_CONTENT_VIEW);
+ waitForAssistantToBeReady(mReadyLatch);
+ startSession();
+ waitForContentView();
+ int height = mIntent.getIntExtra(Utils.EXTRA_CONTENT_VIEW_HEIGHT, 0);
+ int width = mIntent.getIntExtra(Utils.EXTRA_CONTENT_VIEW_WIDTH, 0);
+ Point displayPoint = (Point) mIntent.getParcelableExtra(Utils.EXTRA_DISPLAY_POINT);
+ assertEquals(displayPoint.y, height);
+ assertEquals(displayPoint.x, width);
+ }
+
+ private class AssistantContentViewReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(Utils.BROADCAST_CONTENT_VIEW_HEIGHT)) {
+ mIntent = intent;
+ if (mContentViewLatch != null) {
+ mContentViewLatch.countDown();
+ }
+ } else if (action.equals(Utils.ASSIST_RECEIVER_REGISTERED)) {
+ if (mReadyLatch != null) {
+ mReadyLatch.countDown();
+ }
+ }
+ }
+ }
+}
diff --git a/tests/tests/assist/src/android/assist/cts/DisableContextTest.java b/tests/tests/assist/src/android/assist/cts/DisableContextTest.java
index 9b29407..5c8aa19 100644
--- a/tests/tests/assist/src/android/assist/cts/DisableContextTest.java
+++ b/tests/tests/assist/src/android/assist/cts/DisableContextTest.java
@@ -16,7 +16,6 @@
package android.assist.cts;
-import android.assist.TestStartActivity;
import android.assist.common.Utils;
import android.app.Activity;
@@ -65,6 +64,7 @@
public void testContextAndScreenshotOff() throws Exception {
// Both settings off
+ Log.i(TAG, "DisableContext: Screenshot OFF, Context OFF");
SystemUtil.runShellCommand(getInstrumentation(),
"settings put secure assist_structure_enabled 0");
SystemUtil.runShellCommand(getInstrumentation(),
@@ -76,6 +76,7 @@
verifyAssistDataNullness(true, true, true, true);
// Screenshot off, context on
+ Log.i(TAG, "DisableContext: Screenshot OFF, Context ON");
SystemUtil.runShellCommand(getInstrumentation(),
"settings put secure assist_structure_enabled 1");
SystemUtil.runShellCommand(getInstrumentation(),
@@ -87,6 +88,7 @@
verifyAssistDataNullness(false, false, false, true);
// Context off, screenshot on
+ Log.i(TAG, "DisableContext: Screenshot ON, Context OFF");
SystemUtil.runShellCommand(getInstrumentation(),
"settings put secure assist_structure_enabled 0");
SystemUtil.runShellCommand(getInstrumentation(),
diff --git a/tests/tests/assist/src/android/assist/cts/ExtraAssistDataTest.java b/tests/tests/assist/src/android/assist/cts/ExtraAssistDataTest.java
new file mode 100644
index 0000000..28b2af2
--- /dev/null
+++ b/tests/tests/assist/src/android/assist/cts/ExtraAssistDataTest.java
@@ -0,0 +1,115 @@
+/*
+ * 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.assist.cts;
+
+import android.assist.common.Utils;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.util.Log;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class ExtraAssistDataTest extends AssistTestBase {
+ private static final String TAG = "ExtraAssistDataTest";
+ private static final String TEST_CASE_TYPE = Utils.EXTRA_ASSIST;
+
+ private BroadcastReceiver mReceiver;
+ private CountDownLatch mHasResumedLatch = new CountDownLatch(1);
+ private CountDownLatch mReadyLatch = new CountDownLatch(1);
+
+ public ExtraAssistDataTest() {
+ super();
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ setUpAndRegisterReceiver();
+ startTestActivity(TEST_CASE_TYPE);
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ super.tearDown();
+ if (mReceiver != null) {
+ mContext.unregisterReceiver(mReceiver);
+ mReceiver = null;
+ }
+ }
+
+ private void setUpAndRegisterReceiver() {
+ if (mReceiver != null) {
+ mContext.unregisterReceiver(mReceiver);
+ }
+ mReceiver = new ExtraAssistDataReceiver();
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Utils.APP_3P_HASRESUMED);
+ filter.addAction(Utils.ASSIST_RECEIVER_REGISTERED);
+ mContext.registerReceiver(mReceiver, filter);
+ }
+
+ public void testAssistContentAndAssistData() throws Exception {
+ mTestActivity.startTest(TEST_CASE_TYPE);
+ waitForAssistantToBeReady(mReadyLatch);
+ mTestActivity.start3pApp(TEST_CASE_TYPE);
+ waitForOnResume();
+ startSession();
+ waitForContext();
+ verifyAssistDataNullness(false, false, false, false);
+
+ Log.i(TAG, "assist bundle is: " + Utils.toBundleString(mAssistBundle));
+
+ // tests that the assist content's structured data is the expected
+ assertEquals("AssistContent structured data did not match data in onProvideAssistContent",
+ Utils.getStructuredJSON(), mAssistContent.getStructuredData());
+ // tests the assist data. EXTRA_ASSIST_CONTEXT is what's expected.
+ Bundle extraExpectedBundle = Utils.getExtraAssistBundle();
+ Bundle extraAssistBundle = mAssistBundle.getBundle(Intent.EXTRA_ASSIST_CONTEXT);
+ for (String key : extraExpectedBundle.keySet()) {
+ assertTrue("Assist bundle does not contain expected extra context key: " + key,
+ extraAssistBundle.containsKey(key));
+ assertEquals("Extra assist context bundle values do not match for key: " + key,
+ extraExpectedBundle.get(key), extraAssistBundle.get(key));
+ }
+ }
+
+ private void waitForOnResume() throws Exception {
+ Log.i(TAG, "waiting for onResume() before continuing");
+ if (!mHasResumedLatch.await(Utils.ACTIVITY_ONRESUME_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ fail("Activity failed to resume in " + Utils.ACTIVITY_ONRESUME_TIMEOUT_MS + "msec");
+ }
+ }
+
+ private class ExtraAssistDataReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(Utils.APP_3P_HASRESUMED)) {
+ if (mHasResumedLatch != null) {
+ mHasResumedLatch.countDown();
+ }
+ } else if (action.equals(Utils.ASSIST_RECEIVER_REGISTERED)) {
+ if (mReadyLatch != null) {
+ mReadyLatch.countDown();
+ }
+ }
+ }
+ }
+}
diff --git a/tests/tests/assist/src/android/assist/cts/FlagSecureTest.java b/tests/tests/assist/src/android/assist/cts/FlagSecureTest.java
index 40bf7a7..35f95a4 100644
--- a/tests/tests/assist/src/android/assist/cts/FlagSecureTest.java
+++ b/tests/tests/assist/src/android/assist/cts/FlagSecureTest.java
@@ -31,14 +31,13 @@
* invoked on an app with FLAG_SECURE set.
*/
public class FlagSecureTest extends AssistTestBase {
-
static final String TAG = "FlagSecureTest";
private static final String TEST_CASE_TYPE = Utils.FLAG_SECURE;
private BroadcastReceiver mReceiver;
private CountDownLatch mHasResumedLatch = new CountDownLatch(1);
-
+ private CountDownLatch mReadyLatch = new CountDownLatch(1);
public FlagSecureTest() {
super();
}
@@ -78,8 +77,13 @@
}
public void testSecureActivity() throws Exception {
+ mTestActivity.startTest(TEST_CASE_TYPE);
+ waitForAssistantToBeReady(mReadyLatch);
+ mTestActivity.start3pApp(TEST_CASE_TYPE);
+ waitForOnResume();
+ startSession();
+ waitForContext();
verifyAssistDataNullness(false, false, false, false);
-
// verify that we have only the root window and not its children.
verifyAssistStructure(Utils.getTestAppComponent(TEST_CASE_TYPE), true);
}
@@ -89,9 +93,13 @@
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(Utils.FLAG_SECURE_HASRESUMED)) {
- mHasResumedLatch.countDown();
+ if (mHasResumedLatch != null) {
+ mHasResumedLatch.countDown();
+ }
} else if (action.equals(Utils.ASSIST_RECEIVER_REGISTERED)) {
- mAssistantReadyLatch.countDown();
+ if (mReadyLatch != null) {
+ mReadyLatch.countDown();
+ }
}
}
}
diff --git a/tests/tests/assist/src/android/assist/cts/FocusChangeTest.java b/tests/tests/assist/src/android/assist/cts/FocusChangeTest.java
new file mode 100644
index 0000000..f6b90b9
--- /dev/null
+++ b/tests/tests/assist/src/android/assist/cts/FocusChangeTest.java
@@ -0,0 +1,112 @@
+/*
+ * 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.assist.cts;
+
+import android.assist.cts.TestStartActivity;
+import android.assist.common.Utils;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import java.lang.Override;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/** Test that triggering the Assistant causes the underlying Activity to lose focus **/
+
+public class FocusChangeTest extends AssistTestBase {
+ private static final String TAG = "FocusChangeTest";
+ private static final String TEST_CASE_TYPE = Utils.FOCUS_CHANGE;
+
+ private BroadcastReceiver mReceiver;
+ private CountDownLatch mHasGainedFocusLatch = new CountDownLatch(1);
+ private CountDownLatch mHasLostFocusLatch = new CountDownLatch(1);
+ private CountDownLatch mReadyLatch = new CountDownLatch(1);
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ setUpAndRegisterReceiver();
+ startTestActivity(TEST_CASE_TYPE);
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ super.tearDown();
+ if (mReceiver != null) {
+ mContext.unregisterReceiver(mReceiver);
+ mReceiver = null;
+ }
+ }
+
+ private void setUpAndRegisterReceiver() {
+ if (mReceiver != null) {
+ mContext.unregisterReceiver(mReceiver);
+ }
+ mReceiver = new FocusChangeTestReceiver();
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Utils.GAINED_FOCUS);
+ filter.addAction(Utils.LOST_FOCUS);
+ filter.addAction(Utils.ASSIST_RECEIVER_REGISTERED);
+ mContext.registerReceiver(mReceiver, filter);
+ }
+
+ private void waitToGainFocus() throws Exception {
+ Log.i(TAG, "Waiting for the underlying activity to gain focus before continuing.");
+ if (!mHasGainedFocusLatch.await(Utils.TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ fail("Activity failed to gain focus in " + Utils.TIMEOUT_MS + "msec.");
+ }
+ }
+
+ private void waitToLoseFocus() throws Exception {
+ Log.i(TAG, "Waiting for the underlying activity to lose focus.");
+ if (!mHasLostFocusLatch.await(Utils.TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ fail("Activity maintained focus despite the Assistant Firing"
+ + Utils.TIMEOUT_MS + "msec.");
+ }
+ }
+
+ public void testLayerCausesUnderlyingActivityToLoseFocus() throws Exception {
+ mTestActivity.startTest(Utils.FOCUS_CHANGE);
+ waitForAssistantToBeReady(mReadyLatch);
+ mTestActivity.start3pApp(Utils.FOCUS_CHANGE);
+ waitToGainFocus();
+ startSession();
+ waitToLoseFocus();
+ }
+
+ private class FocusChangeTestReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(Utils.GAINED_FOCUS) && mHasGainedFocusLatch != null) {
+ mHasGainedFocusLatch.countDown();
+ } else if (action.equals(Utils.LOST_FOCUS) && mHasLostFocusLatch != null) {
+ mHasLostFocusLatch.countDown();
+ } else if (action.equals(Utils.ASSIST_RECEIVER_REGISTERED)) {
+ if (mReadyLatch != null) {
+ mReadyLatch.countDown();
+ }
+ }
+ }
+ }
+}
diff --git a/tests/tests/assist/src/android/assist/cts/LargeViewHierarchyTest.java b/tests/tests/assist/src/android/assist/cts/LargeViewHierarchyTest.java
new file mode 100644
index 0000000..bc2ab80
--- /dev/null
+++ b/tests/tests/assist/src/android/assist/cts/LargeViewHierarchyTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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.assist.cts;
+
+import android.assist.common.Utils;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.provider.Settings;
+import android.util.Log;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Test that the AssistStructure returned is properly formatted.
+ */
+
+public class LargeViewHierarchyTest extends AssistTestBase {
+ private static final String TAG = "LargeViewHierarchyTest";
+ private static final String TEST_CASE_TYPE = Utils.LARGE_VIEW_HIERARCHY;
+
+ private BroadcastReceiver mReceiver;
+ private CountDownLatch mHasResumedLatch = new CountDownLatch(1);
+ private CountDownLatch mReadyLatch = new CountDownLatch(1);
+
+ public LargeViewHierarchyTest() {
+ super();
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ setUpAndRegisterReceiver();
+ startTestActivity(TEST_CASE_TYPE);
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ super.tearDown();
+ if (mReceiver != null) {
+ mContext.unregisterReceiver(mReceiver);
+ mReceiver = null;
+ }
+ }
+
+ private void setUpAndRegisterReceiver() {
+ if (mReceiver != null) {
+ mContext.unregisterReceiver(mReceiver);
+ }
+ mReceiver = new LargeViewTestBroadcastReceiver();
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Utils.APP_3P_HASRESUMED);
+ filter.addAction(Utils.ASSIST_RECEIVER_REGISTERED);
+ mContext.registerReceiver(mReceiver, filter);
+ }
+
+ private void waitForOnResume() throws Exception {
+ Log.i(TAG, "waiting for onResume() before continuing");
+ if (!mHasResumedLatch.await(Utils.ACTIVITY_ONRESUME_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ fail("Activity failed to resume in " + Utils.ACTIVITY_ONRESUME_TIMEOUT_MS + "msec");
+ }
+ }
+
+ public void testTextView() throws Exception {
+ mTestActivity.start3pApp(TEST_CASE_TYPE);
+ mTestActivity.startTest(TEST_CASE_TYPE);
+ waitForAssistantToBeReady(mReadyLatch);
+ waitForOnResume();
+ startSession();
+ waitForContext();
+ verifyAssistDataNullness(false, false, false, false);
+
+ verifyAssistStructure(Utils.getTestAppComponent(TEST_CASE_TYPE),
+ false /*FLAG_SECURE set*/);
+ }
+
+ private class LargeViewTestBroadcastReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(Utils.APP_3P_HASRESUMED) && mHasResumedLatch != null) {
+ mHasResumedLatch.countDown();
+ } else if (action.equals(Utils.ASSIST_RECEIVER_REGISTERED) && mReadyLatch != null) {
+ mReadyLatch.countDown();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/tests/assist/src/android/assist/cts/LifecycleTest.java b/tests/tests/assist/src/android/assist/cts/LifecycleTest.java
index 19a1be5..c886b74 100644
--- a/tests/tests/assist/src/android/assist/cts/LifecycleTest.java
+++ b/tests/tests/assist/src/android/assist/cts/LifecycleTest.java
@@ -16,7 +16,7 @@
package android.assist.cts;
-import android.assist.TestStartActivity;
+import android.assist.cts.TestStartActivity;
import android.assist.common.Utils;
import android.app.Activity;
@@ -46,15 +46,18 @@
private static final String action_onStop = Utils.LIFECYCLE_ONSTOP;
private static final String action_onDestroy = Utils.LIFECYCLE_ONDESTROY;
+ private static final String TEST_CASE_TYPE = Utils.LIFECYCLE;
+
private BroadcastReceiver mLifecycleTestBroadcastReceiver;
private CountDownLatch mHasResumedLatch = new CountDownLatch(1);
private CountDownLatch mActivityLifecycleLatch = new CountDownLatch(1);
+ private CountDownLatch mReadyLatch = new CountDownLatch(1);
@Override
public void setUp() throws Exception {
super.setUp();
setUpAndRegisterReceiver();
- startTestActivity(Utils.LIFECYCLE);
+ startTestActivity(TEST_CASE_TYPE);
}
@Override
@@ -96,7 +99,7 @@
public void testLayerDoesNotTriggerLifecycleMethods() throws Exception {
mTestActivity.startTest(Utils.LIFECYCLE);
- waitForAssistantToBeReady();
+ waitForAssistantToBeReady(mReadyLatch);
mTestActivity.start3pApp(Utils.LIFECYCLE);
waitForOnResume();
startSession();
@@ -108,16 +111,18 @@
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
- if (action.equals(action_hasResumed)) {
+ if (action.equals(action_hasResumed) && mHasResumedLatch != null) {
mHasResumedLatch.countDown();
- } else if (action.equals(action_onPause)) {
+ } else if (action.equals(action_onPause) && mActivityLifecycleLatch != null) {
mActivityLifecycleLatch.countDown();
- } else if (action.equals(action_onStop)) {
+ } else if (action.equals(action_onStop) && mActivityLifecycleLatch != null) {
mActivityLifecycleLatch.countDown();
- } else if (action.equals(action_onDestroy)) {
+ } else if (action.equals(action_onDestroy) && mActivityLifecycleLatch != null) {
mActivityLifecycleLatch.countDown();
} else if (action.equals(Utils.ASSIST_RECEIVER_REGISTERED)) {
- mAssistantReadyLatch.countDown();
+ if (mReadyLatch != null) {
+ mReadyLatch.countDown();
+ }
}
}
}
diff --git a/tests/tests/assist/src/android/assist/cts/ScreenshotTest.java b/tests/tests/assist/src/android/assist/cts/ScreenshotTest.java
new file mode 100644
index 0000000..45082ae
--- /dev/null
+++ b/tests/tests/assist/src/android/assist/cts/ScreenshotTest.java
@@ -0,0 +1,134 @@
+/*
+ * 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.assist.cts;
+
+import android.assist.common.Utils;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.DatabaseUtils;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.util.Log;
+
+import junit.framework.Test;
+
+import java.lang.Exception;
+import java.lang.Override;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class ScreenshotTest extends AssistTestBase {
+ static final String TAG = "ScreenshotTest";
+
+ private static final String TEST_CASE_TYPE = Utils.SCREENSHOT;
+
+ private BroadcastReceiver mScreenshotActivityReceiver;
+ private CountDownLatch mHasResumedLatch, mReadyLatch;
+
+ public ScreenshotTest() {
+ super();
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mReadyLatch = new CountDownLatch(1);
+ // set up receiver
+ mScreenshotActivityReceiver = new ScreenshotTestReceiver();
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Utils.ASSIST_RECEIVER_REGISTERED);
+ filter.addAction(Utils.APP_3P_HASRESUMED);
+ mContext.registerReceiver(mScreenshotActivityReceiver, filter);
+
+ // start test start activity
+ startTestActivity(TEST_CASE_TYPE);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ if (mScreenshotActivityReceiver != null) {
+ mContext.unregisterReceiver(mScreenshotActivityReceiver);
+ }
+ super.tearDown();
+ }
+
+ public void testRedScreenshot() throws Exception {
+ Log.i(TAG, "Starting screenshot test");
+ mTestActivity.startTest(TEST_CASE_TYPE);
+ Log.i(TAG, "start waitForAssistantToBeReady()");
+ waitForAssistantToBeReady(mReadyLatch);
+
+ waitForActivityResumeAndAssist(Color.RED);
+ verifyAssistDataNullness(false, false, false, false);
+ assertTrue(mScreenshotMatches);
+ }
+
+ public void testGreenScreenshot() throws Exception {
+ Log.i(TAG, "Starting screenshot test");
+ mTestActivity.startTest(TEST_CASE_TYPE);
+ Log.i(TAG, "start waitForAssistantToBeReady()");
+ waitForAssistantToBeReady(mReadyLatch);
+
+ waitForActivityResumeAndAssist(Color.GREEN);
+ verifyAssistDataNullness(false, false, false, false);
+ assertTrue(mScreenshotMatches);
+ }
+
+ public void testBlueScreenshot() throws Exception {
+ Log.i(TAG, "Starting screenshot test");
+ mTestActivity.startTest(TEST_CASE_TYPE);
+ Log.i(TAG, "start waitForAssistantToBeReady()");
+ waitForAssistantToBeReady(mReadyLatch);
+
+ waitForActivityResumeAndAssist(Color.BLUE);
+ verifyAssistDataNullness(false, false, false, false);
+ assertTrue(mScreenshotMatches);
+ }
+
+ private void waitForActivityResumeAndAssist(int color) throws Exception {
+ mHasResumedLatch = new CountDownLatch(1);
+ Bundle extras = new Bundle();
+ extras.putInt(Utils.SCREENSHOT_COLOR_KEY, color);
+ startSession(TEST_CASE_TYPE, extras);
+ Log.i(TAG, "waiting for onResume() before continuing.");
+ if (!mHasResumedLatch.await(Utils.ACTIVITY_ONRESUME_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ fail("activity failed to resume in " + Utils.ACTIVITY_ONRESUME_TIMEOUT_MS + "msec");
+ }
+ waitForContext();
+ }
+
+ private class ScreenshotTestReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ Log.i(ScreenshotTest.TAG, "Got some broadcast: " + action);
+ if (action.equals(Utils.ASSIST_RECEIVER_REGISTERED)) {
+ Log.i(ScreenshotTest.TAG, "Received assist receiver is registered.");
+ if (mReadyLatch != null) {
+ mReadyLatch.countDown();
+ }
+ } else if (action.equals(Utils.APP_3P_HASRESUMED)) {
+ if (mHasResumedLatch != null) {
+ mHasResumedLatch.countDown();
+ }
+ }
+ }
+ }
+}
diff --git a/tests/tests/assist/src/android/assist/cts/TestStartActivity.java b/tests/tests/assist/src/android/assist/cts/TestStartActivity.java
new file mode 100644
index 0000000..e54e774
--- /dev/null
+++ b/tests/tests/assist/src/android/assist/cts/TestStartActivity.java
@@ -0,0 +1,165 @@
+/*
+ * 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.assist.cts;
+
+import android.assist.common.Utils;
+
+import android.app.Activity;
+import android.app.assist.AssistStructure;
+import android.app.assist.AssistStructure.ViewNode;
+import android.content.Intent;
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.widget.ScrollView;
+import android.widget.TextView;
+
+import java.lang.Override;
+
+public class TestStartActivity extends Activity {
+ static final String TAG = "TestStartActivity";
+
+ private ScrollView mScrollView;
+ private TextView mTextView;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Log.i(TAG, " in onCreate");
+ // Set the respective view we want compared with the test activity
+ String testName = getIntent().getStringExtra(Utils.TESTCASE_TYPE);
+ switch (testName) {
+ case Utils.ASSIST_STRUCTURE:
+ setContentView(R.layout.test_app);
+ setTitle(R.string.testAppTitle);
+ return;
+ case Utils.TEXTVIEW:
+ setContentView(R.layout.text_view);
+ mTextView = (TextView) findViewById(R.id.text_view);
+ mScrollView = (ScrollView) findViewById(R.id.scroll_view);
+ setTitle(R.string.textViewActivityTitle);
+ return;
+ case Utils.LARGE_VIEW_HIERARCHY:
+ setContentView(R.layout.multiple_text_views);
+ setTitle(R.string.testAppTitle);
+ return;
+ case Utils.WEBVIEW:
+ if (getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_WEBVIEW)) {
+ setContentView(R.layout.webview);
+ setTitle(R.string.webViewActivityTitle);
+ WebView webview = (WebView) findViewById(R.id.webview);
+ webview.setWebViewClient(new WebViewClient() {
+ @Override
+ public void onPageFinished(WebView view, String url) {
+ sendBroadcast(new Intent(Utils.TEST_ACTIVITY_LOADED));
+ }
+ });
+ webview.loadData(Utils.WEBVIEW_HTML, "text/html", "UTF-8");
+ }
+ return;
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ Log.i(TAG, " in onResume");
+ }
+
+ public void startTest(String testCaseName) {
+ Log.i(TAG, "Starting test activity for TestCaseType = " + testCaseName);
+ Intent intent = new Intent();
+ intent.putExtra(Utils.TESTCASE_TYPE, testCaseName);
+ intent.setAction("android.intent.action.START_TEST_" + testCaseName);
+ intent.setComponent(new ComponentName("android.assist.service",
+ "android.assist." + Utils.getTestActivity(testCaseName)));
+ startActivity(intent);
+ }
+
+ public void start3pApp(String testCaseName) {
+ Intent intent = new Intent();
+ intent.putExtra(Utils.TESTCASE_TYPE, testCaseName);
+ intent.setAction("android.intent.action.TEST_APP_" + testCaseName);
+ intent.setComponent(Utils.getTestAppComponent(testCaseName));
+ startActivity(intent);
+ }
+
+ public void start3pAppWithColor(String testCaseName, int color) {
+ Intent intent = new Intent();
+ intent.setAction("android.intent.action.TEST_APP_" + testCaseName);
+ intent.putExtra(Utils.SCREENSHOT_COLOR_KEY, color);
+ intent.setComponent(Utils.getTestAppComponent(testCaseName));
+ startActivity(intent);
+ }
+
+ @Override
+ protected void onPause() {
+ Log.i(TAG, " in onPause");
+ super.onPause();
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ Log.i(TAG, " in onStart");
+ }
+
+ @Override protected void onRestart() {
+ super.onRestart();
+ Log.i(TAG, " in onRestart");
+ }
+
+ @Override
+ protected void onStop() {
+ Log.i(TAG, " in onStop");
+ super.onStop();
+ }
+
+ @Override
+ protected void onDestroy() {
+ Log.i(TAG, " in onDestroy");
+ super.onDestroy();
+ }
+
+ public void scrollText(int scrollX, int scrollY, boolean scrollTextView,
+ boolean scrollScrollView) {
+ if (scrollTextView) {
+ if (scrollX < 0 || scrollY < 0) {
+ scrollX = mTextView.getWidth();
+ scrollY = mTextView.getLayout().getLineTop(mTextView.getLineCount()) - mTextView.getHeight();
+ }
+ Log.i(TAG, "Scrolling text view to " + scrollX + ", " + scrollY);
+ mTextView.scrollTo(scrollX, scrollY);
+ } else if (scrollScrollView) {
+ if (scrollX < 0 || scrollY < 0) {
+ Log.i(TAG, "Scrolling scroll view to bottom right");
+ mScrollView.fullScroll(View.FOCUS_DOWN);
+ mScrollView.fullScroll(View.FOCUS_RIGHT);
+ } else {
+ Log.i(TAG, "Scrolling scroll view to " + scrollX + ", " + scrollY);
+ mScrollView.scrollTo(scrollX, scrollY);
+ }
+ }
+ }
+}
diff --git a/tests/tests/assist/src/android/assist/cts/TextViewTest.java b/tests/tests/assist/src/android/assist/cts/TextViewTest.java
new file mode 100644
index 0000000..e4390bc
--- /dev/null
+++ b/tests/tests/assist/src/android/assist/cts/TextViewTest.java
@@ -0,0 +1,133 @@
+/*
+ * 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.assist.cts;
+
+import android.assist.common.Utils;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.provider.Settings;
+import android.util.Log;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Test that the AssistStructure returned is properly formatted.
+ */
+
+public class TextViewTest extends AssistTestBase {
+ private static final String TAG = "TextViewTest";
+ private static final String TEST_CASE_TYPE = Utils.TEXTVIEW;
+
+ private BroadcastReceiver mReceiver;
+ private CountDownLatch mHasResumedLatch = new CountDownLatch(1);
+ private CountDownLatch mReadyLatch = new CountDownLatch(1);
+
+ public TextViewTest() {
+ super();
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ setUpAndRegisterReceiver();
+ startTestActivity(TEST_CASE_TYPE);
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ super.tearDown();
+ if (mReceiver != null) {
+ mContext.unregisterReceiver(mReceiver);
+ mReceiver = null;
+ }
+ }
+
+ private void setUpAndRegisterReceiver() {
+ if (mReceiver != null) {
+ mContext.unregisterReceiver(mReceiver);
+ }
+ mReceiver = new TextViewTestBroadcastReceiver();
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Utils.APP_3P_HASRESUMED);
+ filter.addAction(Utils.ASSIST_RECEIVER_REGISTERED);
+ mContext.registerReceiver(mReceiver, filter);
+ }
+
+ private void waitForOnResume() throws Exception {
+ Log.i(TAG, "waiting for onResume() before continuing");
+ if (!mHasResumedLatch.await(Utils.ACTIVITY_ONRESUME_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ fail("Activity failed to resume in " + Utils.ACTIVITY_ONRESUME_TIMEOUT_MS + "msec");
+ }
+ }
+
+ public void testTextView() throws Exception {
+ mTestActivity.start3pApp(TEST_CASE_TYPE);
+ scrollTestApp(0, 0, true, false);
+
+ // Verify that the text view contains the right text
+ mTestActivity.startTest(TEST_CASE_TYPE);
+ waitForAssistantToBeReady(mReadyLatch);
+ waitForOnResume();
+ startSession();
+ waitForContext();
+ verifyAssistDataNullness(false, false, false, false);
+
+ verifyAssistStructure(Utils.getTestAppComponent(TEST_CASE_TYPE),
+ false /*FLAG_SECURE set*/);
+
+ // Verify that the scroll position of the text view is accurate after scrolling.
+ scrollTestApp(10, 50, true /* scrollTextView */, false /* scrollScrollView */);
+ waitForOnResume();
+ startSession();
+ waitForContext();
+ verifyAssistStructure(Utils.getTestAppComponent(TEST_CASE_TYPE), false);
+
+ scrollTestApp(-1, -1, true, false);
+ waitForOnResume();
+ startSession();
+ waitForContext();
+ verifyAssistStructure(Utils.getTestAppComponent(TEST_CASE_TYPE), false);
+
+ scrollTestApp(0, 0, true, true);
+ waitForOnResume();
+ startSession();
+ waitForContext();
+ verifyAssistStructure(Utils.getTestAppComponent(TEST_CASE_TYPE), false);
+
+ scrollTestApp(10, 50, false, true);
+ waitForOnResume();
+ startSession();
+ waitForContext();
+ verifyAssistStructure(Utils.getTestAppComponent(TEST_CASE_TYPE), false);
+ }
+
+ private class TextViewTestBroadcastReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(Utils.APP_3P_HASRESUMED) && mHasResumedLatch != null) {
+ mHasResumedLatch.countDown();
+ } else if (action.equals(Utils.ASSIST_RECEIVER_REGISTERED) && mReadyLatch != null) {
+ mReadyLatch.countDown();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/tests/assist/src/android/assist/cts/WebViewTest.java b/tests/tests/assist/src/android/assist/cts/WebViewTest.java
new file mode 100644
index 0000000..e367e22
--- /dev/null
+++ b/tests/tests/assist/src/android/assist/cts/WebViewTest.java
@@ -0,0 +1,121 @@
+/*
+ * 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.assist.cts;
+
+import android.assist.common.Utils;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.provider.Settings;
+import android.util.Log;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Test that the AssistStructure returned is properly formatted.
+ */
+
+public class WebViewTest extends AssistTestBase {
+ private static final String TAG = "WebViewTest";
+ private static final String TEST_CASE_TYPE = Utils.WEBVIEW;
+
+ private boolean mWebViewSupported;
+ private BroadcastReceiver mReceiver;
+ private CountDownLatch mHasResumedLatch = new CountDownLatch(1);
+ private CountDownLatch mTestWebViewLatch = new CountDownLatch(1);
+ private CountDownLatch mReadyLatch = new CountDownLatch(1);
+
+ public WebViewTest() {
+ super();
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ setUpAndRegisterReceiver();
+ startTestActivity(TEST_CASE_TYPE);
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ super.tearDown();
+ if (mReceiver != null) {
+ mContext.unregisterReceiver(mReceiver);
+ mReceiver = null;
+ }
+ }
+
+ private void setUpAndRegisterReceiver() {
+ if (mReceiver != null) {
+ mContext.unregisterReceiver(mReceiver);
+ }
+ mReceiver = new WebViewTestBroadcastReceiver();
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Utils.APP_3P_HASRESUMED);
+ filter.addAction(Utils.ASSIST_RECEIVER_REGISTERED);
+ filter.addAction(Utils.TEST_ACTIVITY_LOADED);
+ mContext.registerReceiver(mReceiver, filter);
+ }
+
+ private void waitForOnResume() throws Exception {
+ Log.i(TAG, "waiting for onResume() before continuing");
+ if (!mHasResumedLatch.await(Utils.ACTIVITY_ONRESUME_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ fail("Activity failed to resume in " + Utils.ACTIVITY_ONRESUME_TIMEOUT_MS + "msec");
+ }
+ }
+
+ private void waitForTestActivity() throws Exception {
+ Log.i(TAG, "waiting for webview in test activity to load");
+ if (!mTestWebViewLatch.await(Utils.ACTIVITY_ONRESUME_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ // wait for webView to load completely.
+ }
+ }
+
+ public void testWebView() throws Exception {
+ if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WEBVIEW)) {
+ return;
+ }
+ mTestActivity.start3pApp(TEST_CASE_TYPE);
+ mTestActivity.startTest(TEST_CASE_TYPE);
+ waitForAssistantToBeReady(mReadyLatch);
+ waitForOnResume();
+ waitForTestActivity();
+ startSession();
+ waitForContext();
+ verifyAssistDataNullness(false, false, false, false);
+ verifyAssistStructure(Utils.getTestAppComponent(TEST_CASE_TYPE),
+ false /*FLAG_SECURE set*/);
+ }
+
+ private class WebViewTestBroadcastReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(Utils.APP_3P_HASRESUMED) && mHasResumedLatch != null) {
+ mHasResumedLatch.countDown();
+ } else if (action.equals(Utils.ASSIST_RECEIVER_REGISTERED) && mReadyLatch != null) {
+ mReadyLatch.countDown();
+ } else if (action.equals(Utils.TEST_ACTIVITY_LOADED) && mTestWebViewLatch != null) {
+ mTestWebViewLatch.countDown();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/tests/assist/testapp/AndroidManifest.xml b/tests/tests/assist/testapp/AndroidManifest.xml
index 8d6169c..fa08f55 100644
--- a/tests/tests/assist/testapp/AndroidManifest.xml
+++ b/tests/tests/assist/testapp/AndroidManifest.xml
@@ -18,33 +18,79 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.assist.testapp">
+ <uses-permission android:name="android.permission.INTERNET" />
+
<application>
<uses-library android:name="android.test.runner" />
- <activity android:name="TestApp"
- android:label="Assist Test App"
- android:theme="@android:style/Theme.Material.Light">
+ <activity android:name=".TestApp"
+ android:label="Assist Structure Test Activity">
<intent-filter>
<action android:name="android.intent.action.TEST_APP_ASSIST_STRUCTURE" />
+ <action android:name="android.intent.action.TEST_APP_LARGE_VIEWHIERARCHY" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.VOICE" />
</intent-filter>
</activity>
- <activity android:name="SecureActivity"
- android:label="Secure Test App"
- android:theme="@android:style/Theme.Material.Light">
+ <activity android:name=".DisableContextActivity"
+ android:label="Disable Context Test Activity">
+ <intent-filter>
+ <action android:name="android.intent.action.TEST_APP_DISABLE_CONTEXT" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+ <activity android:name=".SecureActivity"
+ android:label="Secure Test Activity">
<intent-filter>
<action android:name="android.intent.action.TEST_APP_FLAG_SECURE" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.VOICE" />
</intent-filter>
</activity>
- <activity android:name="LifecycleActivity"
- android:label="Life Cycle Check Activity"
- android:theme="@android:style/Theme.Material.Light">
+ <activity android:name=".LifecycleActivity"
+ android:label="Life Cycle Check Activity">
<intent-filter>
<action android:name="android.intent.action.TEST_APP_LIFECYCLE" />
<category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.VOICE" />
+ </intent-filter>
+ </activity>
+ <activity android:name=".ScreenshotActivity"
+ android:label="Screenshot Test Activity">
+ <intent-filter>
+ <action android:name="android.intent.action.TEST_APP_SCREENSHOT" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.VOICE" />
+ </intent-filter>
+ </activity>
+ <activity android:name=".ExtraAssistDataActivity"
+ android:label="Extra Assist Test Activity">
+ <intent-filter>
+ <action android:name="android.intent.action.TEST_APP_EXTRA_ASSIST" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.VOICE" />
+ </intent-filter>
+ </activity>
+ <activity android:name=".TextViewActivity"
+ android:label="TextView Test Activity">
+ <intent-filter>
+ <action android:name="android.intent.action.TEST_APP_TEXTVIEW" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+ <activity android:name=".WebViewActivity"
+ android:label="WebView Test Activity">
+ <intent-filter>
+ <action android:name="android.intent.action.TEST_APP_WEBVIEW" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+ <activity android:name=".FocusChangeActivity"
+ android:label="Focus Change Test Activity">
+ <intent-filter>
+ <action android:name="android.intent.action.TEST_APP_FOCUS_CHANGE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.VOICE" />
</intent-filter>
</activity>
</application>
diff --git a/tests/tests/assist/testapp/res/layout/multiple_text_views.xml b/tests/tests/assist/testapp/res/layout/multiple_text_views.xml
new file mode 100644
index 0000000..455d5e3
--- /dev/null
+++ b/tests/tests/assist/testapp/res/layout/multiple_text_views.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:scrollbars="vertical"
+ android:text="@string/text_too_large_to_fit" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:scrollbars="vertical"
+ android:text="@string/text_too_large_to_fit" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:scrollbars="vertical"
+ android:text="@string/text_too_large_to_fit" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:scrollbars="vertical"
+ android:text="@string/text_too_large_to_fit" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:scrollbars="vertical"
+ android:text="@string/text_too_large_to_fit" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:scrollbars="vertical"
+ android:text="@string/text_too_large_to_fit" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:scrollbars="vertical"
+ android:text="@string/text_too_large_to_fit" />
+
+ <ScrollView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:scrollbars="vertical"
+ android:text="@string/text_too_large_to_fit" />
+ </ScrollView>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/tests/assist/testapp/res/layout/screenshot_activity.xml b/tests/tests/assist/testapp/res/layout/screenshot_activity.xml
new file mode 100644
index 0000000..05051dc
--- /dev/null
+++ b/tests/tests/assist/testapp/res/layout/screenshot_activity.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/screenshot_activity"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+</RelativeLayout>
\ No newline at end of file
diff --git a/tests/tests/assist/testapp/res/layout/secure_app.xml b/tests/tests/assist/testapp/res/layout/secure_app.xml
index 9169a37..3b72ad6 100644
--- a/tests/tests/assist/testapp/res/layout/secure_app.xml
+++ b/tests/tests/assist/testapp/res/layout/secure_app.xml
@@ -14,12 +14,12 @@
limitations under the License.
-->
<RelativeLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
+ 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">
<TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/welcome" />
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/welcome" />
</RelativeLayout>
\ No newline at end of file
diff --git a/tests/tests/assist/testapp/res/layout/test_app.xml b/tests/tests/assist/testapp/res/layout/test_app.xml
index 9169a37..3fbfd6d 100644
--- a/tests/tests/assist/testapp/res/layout/test_app.xml
+++ b/tests/tests/assist/testapp/res/layout/test_app.xml
@@ -13,13 +13,35 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<RelativeLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
<TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/welcome" />
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/welcome" />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="350dp"
+ android:orientation="vertical"
+ android:layout_gravity="bottom">
+ <FrameLayout
+ android:id="@+id/card1"
+ android:layout_width="match_parent"
+ android:layout_height="150dp"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
+ android:layout_marginTop="16dp"
+ android:elevation="3dp">
+ </FrameLayout>
+ <View
+ android:id="@+id/card2"
+ android:layout_width="match_parent"
+ android:layout_height="200dp"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
+ android:layout_marginTop="16dp"
+ android:elevation="3dp"/>
+ </LinearLayout>
</RelativeLayout>
\ No newline at end of file
diff --git a/tests/tests/assist/testapp/res/layout/text_view.xml b/tests/tests/assist/testapp/res/layout/text_view.xml
new file mode 100644
index 0000000..0f0f427
--- /dev/null
+++ b/tests/tests/assist/testapp/res/layout/text_view.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ <TextView
+ android:id="@+id/text_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:focusable="false"
+ android:focusableInTouchMode="false"
+ android:scrollbars="vertical"
+ android:clickable="true"
+ android:text="@string/text_too_large_to_fit" />
+
+ <ScrollView
+ android:id="@+id/scroll_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:scrollbars="vertical"
+ android:text="@string/text_too_large_to_fit" />
+ </ScrollView>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/tests/assist/testapp/res/layout/webview.xml b/tests/tests/assist/testapp/res/layout/webview.xml
new file mode 100644
index 0000000..bdb8082
--- /dev/null
+++ b/tests/tests/assist/testapp/res/layout/webview.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ <WebView
+ android:id="@+id/webview"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ </WebView>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/tests/assist/testapp/res/values/strings.xml b/tests/tests/assist/testapp/res/values/strings.xml
index a245b36..670024b 100644
--- a/tests/tests/assist/testapp/res/values/strings.xml
+++ b/tests/tests/assist/testapp/res/values/strings.xml
@@ -1,4 +1,284 @@
<?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.
+-->
<resources>
+
+ <string name="app_name">Memegen</string>
+ <string name="hello_world">Hello world!</string>
+ <string name="action_settings">Settings</string>
<string name="welcome">Hello there!</string>
-</resources>
\ No newline at end of file
+ <string name="text_too_large_to_fit">❤ ☀ ☆ ☂ ☻ ♞ ☯ ☭ ☢ € →Hello هتاف للترحيب שלום
+ përshëndetje Добры дзень 您好 হ্যালো здравей მიესალმები Χαίρετε હેલો नमस्ते Nnọọ こんにちは ಹಲೋ
+ Сәлеметсіз бе ជំរាបសួរ 안녕하세요 ສະບາຍດີ ഹലോ हॅलो Сайн байна уу नमस्ते سلامהעלא ہیلو
+ မင်္ဂလာပါ ਸਤ ਸ੍ਰੀ ਅਕਾਲ Здравствуйте здраво ආයුබෝවන් ஹலோ హలో สวัสดี Pẹlẹ o
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+ convallis nunc et vestibulum. Sed et consequat quam, blandit varius tortor. Curabitur
+ accumsan nulla lectus, placerat condimentum odio elementum vel. Nulla erat ex, accumsan ut
+ enim sagittis, scelerisque efficitur ante. Nullam quis orci nec magna maximus malesuada ac
+ id sem. Nam sagittis erat risus, a accumsan neque congue sit amet. Nullam risus velit,
+ faucibus eget scelerisque et, maximus eget arcu. Sed porta sed libero ac imperdiet.
+
+ Nulla sem lectus, ullamcorper id dui vel, rutrum interdum augue. Proin aliquam nisi vitae
+ hendrerit tempor. Mauris porttitor velit et egestas feugiat. Vivamus eu dapibus libero,
+ quis fringilla urna. Suspendisse non turpis dui. Vivamus facilisis diam vitae est auctor
+ luctus. Etiam quis lectus viverra, interdum turpis eu, aliquam sem. Nulla vulputate lacinia
+ nisi a dictum. Cras faucibus vitae tortor at ullamcorper. Quisque sit amet sapien maximus,
+ ornare nisi non, imperdiet magna. Vestibulum tempor metus ac mi ultrices dapibus.
+
+ Suspendisse potenti. Mauris pellentesque lacinia tristique. Pellentesque vel dui quis sem
+ lacinia imperdiet feugiat vitae sem. Proin a arcu magna. Sed quis augue eu mi accumsan
+ pellentesque pretium in leo. Duis euismod purus mauris, ac tempor erat auctor non. Quisque
+ bibendum est pulvinar ex dapibus, ac tincidunt nibh tempus. Mauris sodales sem id purus
+ commodo iaculis. Pellentesque a quam dapibus, vehicula lectus at, tincidunt arcu. In
+ placerat porttitor urna quis consequat. Nullam feugiat nisl sed urna hendrerit, sed
+ elementum massa iaculis. Fusce sit amet turpis hendrerit, varius lorem sed, luctus mi.
+ Phasellus sit amet ex orci. Duis scelerisque nisl quis efficitur maximus. Curabitur vitae
+ accumsan nunc, eget varius nisi.
+
+ Fusce efficitur malesuada luctus. Aliquam dapibus tortor sit amet purus semper, sit amet
+ pretium lorem feugiat. Maecenas gravida sed arcu et placerat. Nulla facilisi. Cras placerat
+ rutrum mi, in rutrum mauris maximus at. Mauris eu suscipit ante. Nullam pharetra egestas
+ diam a viverra. Donec sem turpis, tempor malesuada est vel, blandit accumsan magna. In
+ iaculis velit in efficitur hendrerit. Nulla facilisi. Curabitur eget ligula lorem. Sed sit
+ amet dolor ut ligula malesuada condimentum. Phasellus molestie augue eget libero commodo,
+ vel blandit ex blandit.
+
+ Morbi cursus tortor ante, et tempus nisi tempus et. Suspendisse quis gravida diam. Aliquam
+ efficitur dolor sit amet sollicitudin varius. Etiam libero purus, ornare nec nulla vel,
+ ullamcorper blandit nisl. Sed vel consequat diam, id placerat sem. Donec quis elementum
+ urna. In posuere bibendum nunc, in condimentum justo blandit ac. Quisque enim lorem,
+ gravida at purus at, sollicitudin imperdiet erat. Ut consectetur rutrum ante, et pretium
+ odio iaculis a. Nullam a nibh vulputate, volutpat lectus eu, pellentesque felis. Nam
+ vehicula suscipit diam nec convallis. Quisque congue maximus sem, sit amet hendrerit leo
+ tempor et.
+
+ Nam eu consequat dui. Sed semper dignissim mattis. Integer tortor eros, tempor in lectus a,
+ lobortis aliquam dolor. Phasellus at sagittis magna. Nulla eleifend orci ac urna auctor,
+ sit amet luctus urna vulputate. Nulla venenatis venenatis erat ac finibus. Etiam
+ ullamcorper elementum suscipit. Morbi nec velit non mauris porta finibus. Nullam in
+ sagittis odio. Praesent eget nisl ut mauris vestibulum feugiat. Sed vulputate at elit et
+ cursus. Praesent viverra erat blandit nunc egestas, vel feugiat ex condimentum. Class
+ aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.
+
+ Nulla fermentum mattis urna, non gravida eros vestibulum et. Fusce porttitor augue turpis,
+ sit amet aliquam augue sodales non. Nunc neque odio, sagittis sed gravida euismod, commodo
+ at libero. Donec porttitor pulvinar neque vitae lobortis. Aliquam accumsan velit nec sapien
+ placerat egestas. Aliquam at tincidunt massa, et dignissim justo. Donec sapien ante, rutrum
+ et tristique a, commodo a massa.
+
+ Nunc placerat lobortis magna, et molestie lacus semper porta. Lorem ipsum dolor sit amet,
+ consectetur adipiscing elit. Phasellus ac ligula dui. Duis ultrices viverra eros fermentum
+ finibus. Integer ultrices, felis in accumsan volutpat, mi ligula hendrerit nunc, nec
+ accumsan mauris tellus sit amet metus. Ut pharetra enim et sapien sollicitudin, nec
+ ultricies urna pharetra. Morbi non tortor nec dui feugiat rutrum. Aliquam malesuada sodales
+ risus, sed congue nunc accumsan vitae. Etiam nunc magna, tempus non suscipit eu, feugiat ut
+ nibh. Maecenas et libero ut nisl pellentesque tempor nec vel quam. Etiam sem ligula,
+ ullamcorper non dolor a, viverra placerat nulla. Nullam dictum commodo dui, sed ultrices
+ enim sagittis eget. Morbi non consectetur lectus. In gravida, augue vitae pulvinar
+ molestie, ligula orci vulputate ex, at bibendum urna felis nec nibh. Sed nisl nunc, iaculis
+ at augue venenatis, fringilla accumsan velit. Curabitur nec augue porttitor, rutrum nisi
+ vitae, elementum orci.
+
+ Vestibulum eu tortor iaculis, dignissim velit quis, rhoncus dolor. Donec et tincidunt
+ nulla. Duis faucibus auctor erat ac ultricies. In a fermentum mi. Fusce vitae mi id sem
+ interdum tincidunt. Nulla hendrerit orci turpis, in maximus elit mollis eget. Aliquam erat
+ volutpat. Phasellus mattis est nibh, ut scelerisque ligula egestas eu. Ut molestie orci a
+ malesuada tempor. Sed tempus arcu id orci gravida faucibus. Vivamus ac lacinia neque, at
+ vehicula magna.
+
+ Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae;
+ Nullam aliquam, justo scelerisque egestas sodales, purus odio posuere arcu, ac ultrices
+ nunc felis non massa. Aliquam vulputate ipsum sed aliquet auctor. In pulvinar, eros sit
+ amet ultricies tristique, lacus ipsum scelerisque eros, nec vestibulum est lectus quis
+ lorem. Pellentesque ac augue ut eros mattis viverra vitae ut lacus. Phasellus imperdiet
+ efficitur elit eget tincidunt. Donec sodales metus at dolor pulvinar, at gravida nibh
+ facilisis. Sed nec tellus luctus, cursus lacus sed, euismod orci. Maecenas sit amet leo
+ orci. Nulla non leo non mi eleifend consequat sit amet vitae dui. Sed gravida gravida
+ justo, tincidunt ultrices justo semper vitae. Fusce at nisi nisl. Morbi molestie quis justo
+ a convallis. Curabitur massa lacus, feugiat quis mauris ac, malesuada viverra est.
+
+ Phasellus bibendum faucibus velit, ac scelerisque velit tincidunt eu. Curabitur quis
+ suscipit erat, ac feugiat odio. Nullam et sapien et nibh maximus posuere. Vivamus faucibus
+ justo eget dictum sollicitudin. Etiam at leo eget elit facilisis lobortis. Maecenas
+ bibendum tortor non erat pretium dignissim. Fusce id imperdiet augue. Suspendisse dignissim
+ magna vel odio viverra varius. Maecenas suscipit ante et lorem sodales vehicula. Quisque
+ vel magna id sem suscipit iaculis. Etiam in elementum risus.
+
+ Suspendisse odio nisi, pharetra et purus sit amet, placerat lobortis diam. Phasellus enim
+ nunc, posuere sed porta in, ornare eu ipsum. Phasellus imperdiet porta neque, vitae dapibus
+ tellus feugiat eget. Nulla sodales leo ac efficitur luctus. Vivamus eget ipsum quis ante
+ pulvinar blandit. Vestibulum a justo convallis justo elementum viverra ut sit amet nisl.
+ Suspendisse eget augue fermentum, sagittis risus a, rhoncus elit. Vestibulum in maximus
+ tortor, non vestibulum libero. Nunc accumsan neque a nisl dapibus, id laoreet neque congue.
+ Pellentesque sapien odio, fringilla non nulla nec, varius placerat diam. Aliquam
+ consectetur neque eu ipsum posuere, nec dignissim sem faucibus. Donec sit amet tempor
+ sapien. Nam at libero vel lorem dapibus ultrices a vel augue. Nunc facilisis justo ante,
+ mollis tristique velit aliquet quis. Mauris consectetur odio at urna bibendum aliquam.
+
+ Nullam lectus orci, hendrerit ut ultrices in, dapibus pellentesque nibh. Aliquam arcu
+ metus, lobortis vel dignissim id, tempus ut ante. Integer vitae ante augue. In hac habitasse
+ platea dictumst. Vestibulum in tellus ante. Cras nisi tellus, congue ac velit quis, rhoncus
+ ornare ligula. Sed facilisis gravida pellentesque. Praesent id ultrices orci, ac ultricies
+ arcu. Donec at ante quis augue faucibus congue. Donec mattis quam dui, ut vestibulum orci
+ tempor mattis. Phasellus in quam id tortor varius ullamcorper ac ac ante. Proin cursus
+ accumsan sem, vel finibus lectus eleifend ut. Donec efficitur feugiat diam id ultricies. In
+ quis euismod nisi. Vestibulum eget viverra sapien. Donec scelerisque nec elit vel viverra.
+
+ Sed mi urna, rutrum quis augue vel, aliquet placerat diam. Proin faucibus in odio et
+ consequat. Proin ut ex in mauris eleifend efficitur. Praesent ullamcorper sollicitudin urna,
+ sed mollis elit hendrerit non. Duis leo lorem, efficitur eu auctor sit amet, scelerisque
+ scelerisque magna. Mauris eget massa auctor, viverra arcu a, elementum nibh. Sed
+ pellentesque, nulla sed condimentum posuere, tortor metus congue sem, nec placerat neque
+ magna vitae purus.
+
+ Etiam at risus vitae sapien aliquam condimentum. Vestibulum id libero placerat purus
+ vehicula consectetur. Pellentesque sapien sapien, posuere at pulvinar at, ultrices sed est.
+ Maecenas nec condimentum ante. Aenean volutpat, ex condimentum hendrerit hendrerit, quam
+ nisl pharetra nibh, vitae bibendum nisl odio vel lacus. Morbi mi tellus, bibendum id mauris
+ eu, facilisis volutpat turpis. Maecenas rutrum convallis felis. Quisque eget feugiat felis.
+ Duis pellentesque iaculis massa ut facilisis. Donec nec commodo magna. Integer aliquet orci
+ a odio eleifend elementum. Quisque molestie, urna ut molestie eleifend, odio neque maximus
+ enim, eget viverra metus lectus eget quam. Fusce nec urna ac neque bibendum aliquam vel quis
+ dui. Fusce ac quam consequat, feugiat leo vitae, auctor felis.
+
+ Sed ac metus mauris. Sed sed velit ut tortor aliquam vestibulum at eu arcu. Etiam eu
+ posuere lacus. Maecenas id lacus quis sem mollis sodales. Quisque justo sapien, vulputate ac
+ mi ut, congue vestibulum orci. Donec euismod erat rutrum, laoreet urna sed, accumsan purus.
+ Donec eu quam a sapien condimentum accumsan. Sed at tellus lorem. Curabitur bibendum, arcu
+ sit amet finibus sodales, mi sem finibus sem, eget scelerisque tellus neque vel urna.
+ Suspendisse eu augue nec erat suscipit luctus sed non metus.
+
+ Suspendisse porttitor ex ipsum. Pellentesque tristique eros sed pharetra porttitor.
+ Quisque ut elit vehicula, aliquet est nec, faucibus tellus. Donec ex augue, congue eu
+ dignissim maximus, vestibulum at purus. Nulla quam enim, laoreet sit amet molestie vel,
+ dapibus nec tortor. Sed interdum massa ac orci gravida, vel viverra lacus lacinia. Donec
+ nisl lacus, fermentum at faucibus ac, consequat ut nibh. Praesent laoreet est augue, vitae
+ maximus dui efficitur sit amet. Cras ipsum tellus, tincidunt at volutpat non, tincidunt ut
+ elit. Morbi commodo sagittis gravida. Pellentesque sed ante massa. Phasellus a turpis non
+ turpis cursus consequat sed nec tortor. Proin et augue elit.
+
+ Duis finibus sem commodo rutrum pulvinar. In sollicitudin ante magna, vel facilisis
+ tellus fringilla vel. Nam purus ex, tincidunt eget varius at, euismod nec elit. Curabitur
+ consequat nulla vel nisi iaculis, ut mattis odio congue. Nulla et mollis tortor, a maximus
+ justo. Donec semper eros sed nunc rhoncus condimentum. Donec nibh purus, interdum non lectus
+ id, porta convallis eros.
+
+ Sed hendrerit, dui non sagittis sollicitudin, enim ex imperdiet sapien, et maximus lorem
+ dolor a enim. Nulla risus nisl, vestibulum at ornare posuere, congue in felis. Duis sagittis
+ id diam a varius. Donec viverra eu orci sodales commodo. Cras metus tortor, sodales vitae
+ auctor non, scelerisque a ante. Quisque sodales nisi libero, ut lobortis enim suscipit ut.
+ Cras mi ipsum, maximus non bibendum sit amet, pharetra quis ipsum. Vestibulum venenatis, odi
+ at hendrerit pretium, tellus diam auctor justo, non posuere quam mauris id nisl. Nam dolor
+ nibh, molestie et lectus et, scelerisque porta elit. Vestibulum viverra condimentum auctor.
+ In eros tortor, convallis sed quam eu, ultrices malesuada purus. Integer lorem quam,
+ ultricies at est consectetur, sagittis porttitor eros. Proin non risus vitae lacus
+ consectetur malesuada non pulvinar justo. Aliquam mollis nisi nunc, sit amet vulputate metus
+ sollicitudin vel.
+
+ Quisque auctor varius fermentum. Praesent mollis justo sit amet est consectetur, in
+ volutpat tellus mollis. Aenean at bibendum eros, at finibus nibh. Phasellus nec mi sem.
+ Mauris pellentesque dui sit amet lobortis aliquam. Ut nec massa at urna aliquam gravida vel
+ in magna. Donec consectetur sapien magna, a auctor sapien placerat eu.
+
+ Pellentesque aliquet ante sed lacus gravida rutrum. Maecenas euismod varius felis, nec
+ tempus metus tempus et. Nam convallis augue a massa scelerisque, vel pharetra dolor
+ scelerisque. Fusce porttitor mi a magna rutrum condimentum. Aliquam fermentum at turpis at
+ auctor. Nulla ut suscipit dui. Donec rutrum viverra aliquam. Donec elementum nisl sapien, ac
+ blandit risus porta facilisis. Proin tellus dolor, ornare vel magna sit amet, maximus
+ volutpat felis. Aenean euismod aliquet purus, at finibus nunc elementum eu. Ut faucibus
+ ullamcorper consectetur. Aenean egestas arcu id mauris elementum, at sollicitudin est
+ congue. Sed a odio mattis, sollicitudin erat ut, tristique dolor. Aliquam luctus risus quis
+ tellus semper, a vestibulum nulla viverra.
+
+ Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos
+ himenaeos. Vestibulum sit amet nisi felis. Praesent condimentum consequat lacus pulvinar
+ imperdiet. Praesent vel condimentum quam. Maecenas eu aliquet odio. Vestibulum sed nulla
+ mattis lacus porta bibendum a ac eros. Nunc porttitor sagittis laoreet. Duis porta eros at
+ congue tristique. Pellentesque quis fringilla neque, a hendrerit tellus. Pellentesque ac
+ nibh ac tellus pulvinar porttitor et in est. Integer blandit lorem libero, eu pulvinar
+ tellus posuere eget. Vivamus pretium nulla ligula, ut dapibus massa fringilla in.
+ Suspendisse consectetur sem non elit porta, id pellentesque erat dapibus. Quisque ex mi,
+ tempus et hendrerit nec, gravida quis odio. Ut at mi in leo scelerisque venenatis.
+
+ Ut sed tellus in risus tincidunt tempor ut at arcu. Maecenas ut convallis justo. In
+ rutrum urna eu massa rhoncus, eget condimentum augue vehicula. Nullam eget placerat erat.
+ Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.
+ Aenean at volutpat orci, a lobortis dolor. Sed consequat facilisis interdum. Fusce libero
+ neque, fringilla in congue a, vehicula rutrum ipsum. Nam ornare placerat vestibulum. Proin
+ nec orci velit. Pellentesque scelerisque gravida diam, ut tristique libero tempus eu. Nam
+ semper lacus nec nulla volutpat imperdiet non eget tortor. Sed et pellentesque ligula.
+
+ Aenean a dolor dolor. Curabitur ut placerat lacus, sit amet aliquet orci. Aliquam erat
+ volutpat. Cras mollis sit amet lectus ornare pretium. Vestibulum fringilla orci vel est
+ iaculis, at mattis quam condimentum. Vivamus semper elit consectetur lectus gravida, in
+ sollicitudin mi fringilla. Donec eget lorem in orci blandit aliquam. Pellentesque libero
+ tellus, dignissim id augue et, vulputate viverra nisl. Cum sociis natoque penatibus et
+ magnis dis parturient montes, nascetur ridiculus mus. Donec ac vulputate metus, eu suscipit
+ sem. Donec placerat, nulla at sodales hendrerit, orci tortor vestibulum purus, non pharetra
+ nulla purus posuere arcu.
+
+ Quisque feugiat elit sem, ac interdum diam pharetra nec. Curabitur sem libero, vulputate
+ eu libero vitae, volutpat facilisis ligula. Aenean maximus erat laoreet, interdum ante in,
+ ultrices nisi. Nullam nec efficitur sapien. Integer feugiat et tortor ac bibendum. Donec a
+ scelerisque tortor. Cras quis viverra diam, vitae viverra ipsum. Aliquam ultrices neque sem,
+ congue sodales elit tempus sit amet. Pellentesque habitant morbi tristique senectus et netus
+ et malesuada fames ac turpis egestas. Sed dignissim ipsum eget diam rhoncus, ut finibus
+ nulla pretium. Morbi suscipit nibh vel nisl posuere molestie. Maecenas posuere turpis et
+ rutrum consectetur. Morbi venenatis arcu non gravida vulputate. Vivamus auctor tellus
+ ullamcorper ligula vestibulum cursus. Nunc gravida sit amet nisl quis facilisis.
+
+ Praesent ut justo vestibulum, accumsan mi et, feugiat purus. Nullam pulvinar iaculis
+ pharetra. Aliquam pulvinar risus sit amet elit suscipit tincidunt. In hac habitasse platea
+ dictumst. Etiam eget velit ac magna lacinia efficitur. Vestibulum ante ipsum primis in
+ faucibus orci luctus et ultrices posuere cubilia Curae; Cras volutpat tempus sollicitudin.
+ Ut et ante elit.
+
+ Sed ac tortor justo. Fusce sed metus libero. Duis sagittis tortor ac ante sollicitudin,
+ nec efficitur nibh euismod. Donec porttitor cursus quam, in aliquam lorem feugiat ut.
+ Aliquam tempor lacus eu feugiat feugiat. Nunc pulvinar, libero at auctor commodo, metus diam
+ commodo lorem, in feugiat elit ex non ligula. Quisque at vestibulum sapien, nec facilisis
+ neque. Aenean luctus, arcu ut rhoncus luctus, est massa rhoncus mauris, nec luctus urna sem
+ quis massa. Nam elit felis, congue et ligula eget, ultricies tincidunt erat. Vivamus
+ eleifend no dui ac dictum. In nulla justo, pulvinar ut tristique sed, congue et orci.
+
+ Quisque imperdiet mi lectus, ac scelerisque augue posuere ut. Duis non pulvinar ipsum,
+ finibus risus. Donec ullamcorper nisl at sodales lobortis. Mauris neque leo, vestibulum sit
+ amet placerat vel, aliquet vel sapien. Morbi massa tellus, scelerisque quis nisl in, feugiat
+ posuere augue. Aenean congue sem ut ipsum vulputate rhoncus vitae at eros. Maecenas in velit
+ orci pellentesque lobortis ac at felis. Vestibulum odio quam, lacinia dapibus ornare eu,
+ vulputate a eros. Curabitur eleifend ornare tellus, non sollicitudin justo viverra in. Sed
+ sodales neque et lacus semper, in pharetra est consequat. Nunc vehicula volutpat lectus, sit
+ amet scelerisque nisi. Aenean sollicitudin, sem at ultricies efficitur, eros metus
+ nisl, et fringilla felis lacus non orci. Praesent eros libero, finibus in purus id,
+ tempor ipsum. Donec suscipit libero velit. Aliquam quis diam pharetra, cursus ipsum in,
+ gravida metus. Maecenas ultrices ligula a ullamcorper scelerisque.
+
+ Donec tincidunt felis turpis, id venenatis neque convallis in. Proin euismod ligula nec
+ urna vulputate, sed elementum mauris ultrices. Ut efficitur sem vel mi vestibulum placerat.
+ Sed fermentum lacus nec metus dictum, a commodo quam fermentum. Sed vel vulputate magna.
+ Integer convallis nisi sit amet mi lobortis pellentesque. In egestas porttitor elit eu
+ scelerisque. Suspendisse eleifend vel enim quis tincidunt. Sed placerat risus et pretium
+ porttitor. Nam justo mi, cursus eu pellentesque vel, bibendum ut nisl. Nulla condimentum
+ lorem, non sagittis lorem volutpat vitae. Mauris nec libero lorem. Vestibulum lacus ex,
+ vulputate non massa vitae, pellentesque vestibulum dolor.
+
+ Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae;
+ Suspendisse vitae erat nisl. Vestibulum elit ante, semper et semper sit amet, fringilla
+ sapien. Morbi ac nisi sit amet turpis tincidunt mattis ac eget nisl. Nunc a venenatis quam,
+ facilisis maximus odio. Aliquam erat volutpat. Maecenas leo enim, ornare a magna quis,
+ venenatis ornare nulla. Aliquam venenatis nibh et elit tincidunt, ut egestas lorem finibus.
+ Integer iaculis dolor sed enim blandit vestibulum. Nullam vel libero ultricies, sagittis
+ tortor non, molestie eros.</string>
+</resources>
diff --git a/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/ExtraAssistDataActivity.java b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/ExtraAssistDataActivity.java
new file mode 100644
index 0000000..57d34f8
--- /dev/null
+++ b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/ExtraAssistDataActivity.java
@@ -0,0 +1,65 @@
+/*
+ * 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.assist.testapp;
+
+import android.app.Activity;
+import android.app.assist.AssistContent;
+import android.assist.common.Utils;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.lang.Override;
+
+/**
+ * Test the onProvideAssistData and onProvideAssistContent methods activities may override to
+ * provide extra information to the assistant. Verify that the data passed from the activity matches
+ * the data received in {@link android.service.voice.VoiceInteractionSession}.
+ */
+public class ExtraAssistDataActivity extends Activity {
+ private static final String TAG = "ExtraAssistDataActivity";
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ public void onProvideAssistData(Bundle data) {
+ super.onProvideAssistData(data);
+ Log.i(TAG, "onProvideAssistData");
+ Utils.addExtraAssistDataToBundle(data);
+ }
+
+ @Override
+ public void onProvideAssistContent(AssistContent outContent) {
+ super.onProvideAssistContent(outContent);
+ Log.i(TAG, "onProvideAssistContent");
+ try {
+ outContent.setStructuredData(Utils.getStructuredJSON());
+ } catch (Exception e) {
+ Log.i(TAG, "Failed to get Structured JSON to put into the AssistContent.");
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ sendBroadcast(new Intent(Utils.APP_3P_HASRESUMED));
+ }
+}
\ No newline at end of file
diff --git a/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/FocusChangeActivity.java b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/FocusChangeActivity.java
new file mode 100644
index 0000000..4ab24ed
--- /dev/null
+++ b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/FocusChangeActivity.java
@@ -0,0 +1,39 @@
+/*
+ * 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.assist.testapp;
+
+import android.app.Activity;
+import android.assist.common.Utils;
+import android.content.Intent;
+import android.util.Log;
+
+public class FocusChangeActivity extends Activity {
+ private static final String TAG = "FocusChangeActivity";
+ private boolean mGainedFocus = false;
+
+ @Override
+ public void onWindowFocusChanged(boolean hasFocus) {
+ if (hasFocus && !mGainedFocus) {
+ mGainedFocus = true;
+ Log.i(TAG, "gained focus");
+ sendBroadcast(new Intent(Utils.GAINED_FOCUS));
+ } else if (!hasFocus && mGainedFocus) {
+ Log.i(TAG, "lost focus");
+ sendBroadcast(new Intent(Utils.LOST_FOCUS));
+ }
+ }
+}
diff --git a/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/LifecycleActivity.java b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/LifecycleActivity.java
index 4e1dc80..af10f99 100644
--- a/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/LifecycleActivity.java
+++ b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/LifecycleActivity.java
@@ -39,22 +39,22 @@
@Override
protected void onPause() {
- super.onPause();
Log.i(TAG, "activity was paused");
sendBroadcast(new Intent("android.intent.action.lifecycle_onpause"));
+ super.onPause();
}
@Override
protected void onStop() {
- super.onStop();
Log.i(TAG, "activity was stopped");
sendBroadcast(new Intent("android.intent.action.lifecycle_onstop"));
+ super.onStop();
}
@Override
protected void onDestroy() {
- super.onDestroy();
Log.i(TAG, "activity was destroyed");
sendBroadcast(new Intent("android.intent.action.lifecycle_ondestroy"));
+ super.onDestroy();
}
}
diff --git a/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/ScreenshotActivity.java b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/ScreenshotActivity.java
new file mode 100644
index 0000000..581af2e
--- /dev/null
+++ b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/ScreenshotActivity.java
@@ -0,0 +1,63 @@
+/*
+ * 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.assist.testapp;
+
+import android.assist.common.Utils;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+
+import java.lang.Override;
+
+public class ScreenshotActivity extends Activity {
+ static final String TAG = "ScreenshotActivity";
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Log.i(TAG, "ScreenshotActivity created");
+ setContentView(R.layout.screenshot_activity);
+ }
+
+ @Override
+ public void onResume() {
+ Log.i(TAG, " in onResume");
+ super.onResume();
+ int backgroundColor = getIntent().getIntExtra(Utils.SCREENSHOT_COLOR_KEY, Color.WHITE);
+ View view = findViewById(R.id.screenshot_activity);
+ view.setBackgroundColor(backgroundColor);
+ view.requestLayout();
+
+ // Tell service activity is in foreground.
+ Intent intent = new Intent(Utils.APP_3P_HASRESUMED);
+ sendBroadcast(intent);
+ Log.i(TAG, "Resumed broadcast sent.");
+ }
+
+ @Override
+ public void onPause() {
+ Log.i(TAG, "onPause");
+ finish();
+ super.onPause();
+ }
+}
diff --git a/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/SecureActivity.java b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/SecureActivity.java
index d9b2ff2..708061e 100644
--- a/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/SecureActivity.java
+++ b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/SecureActivity.java
@@ -16,11 +16,16 @@
package android.assist.testapp;
+import android.assist.common.Utils;
+
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.WindowManager;
public class SecureActivity extends Activity {
@@ -37,7 +42,15 @@
@Override
protected void onResume() {
super.onResume();
- Log.i(TAG, "Activity has resumed");
- sendBroadcast(new Intent("android.intent.action.flag_secure_hasResumed"));
+ Log.i(TAG, "Activity has resumed");
+ final View layout = findViewById(android.R.id.content);
+ ViewTreeObserver vto = layout.getViewTreeObserver();
+ vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ layout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ sendBroadcast(new Intent(Utils.FLAG_SECURE_HASRESUMED));
+ }
+ });
}
}
diff --git a/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/TestApp.java b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/TestApp.java
index 85a9342..e0f83cc 100644
--- a/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/TestApp.java
+++ b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/TestApp.java
@@ -16,25 +16,59 @@
package android.assist.testapp;
+import android.assist.common.Utils;
+
import android.app.Activity;
+import android.content.Intent;
+import android.graphics.Bitmap;
import android.os.Bundle;
import android.util.Log;
+import android.view.View;
+import android.view.View.MeasureSpec;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+
+import java.io.ByteArrayOutputStream;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+
+import java.lang.Override;
public class TestApp extends Activity {
static final String TAG = "TestApp";
- Bundle mTestinfo = new Bundle();
- Bundle mTotalInfo = new Bundle();
+ private String mTestCaseName;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG, "TestApp created");
- getLayoutInflater().inflate(R.layout.test_app, null);
+ mTestCaseName = getIntent().getStringExtra(Utils.TESTCASE_TYPE);
+ switch (mTestCaseName) {
+ case Utils.LARGE_VIEW_HIERARCHY:
+ setContentView(R.layout.multiple_text_views);
+ return;
+ default:
+ setContentView(R.layout.test_app);
+ }
}
@Override
public void onResume() {
super.onResume();
+ Log.i(TAG, "TestApp has resumed");
+ final View layout = findViewById(android.R.id.content);
+ ViewTreeObserver vto = layout.getViewTreeObserver();
+ vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ layout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ sendBroadcast(new Intent(Utils.APP_3P_HASRESUMED));
+ }
+ });
}
}
\ No newline at end of file
diff --git a/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/TextViewActivity.java b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/TextViewActivity.java
new file mode 100644
index 0000000..9e57e9b
--- /dev/null
+++ b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/TextViewActivity.java
@@ -0,0 +1,114 @@
+/*
+ * 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.assist.testapp;
+
+import android.assist.common.Utils;
+
+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.text.method.ScrollingMovementMethod;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+import android.widget.ScrollView;
+import android.widget.TextView;
+
+import java.lang.Override;
+
+public class TextViewActivity extends Activity {
+ static final String TAG = "TextViewActivity";
+
+ private BroadcastReceiver mReceiver;
+ private TextView mTextView;
+ private ScrollView mScrollView;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Log.i(TAG, "TextViewActivity created");
+ setContentView(R.layout.text_view);
+ mScrollView = (ScrollView) findViewById(R.id.scroll_view);
+ mTextView = (TextView) findViewById(R.id.text_view);
+ mTextView.setMovementMethod(new ScrollingMovementMethod());
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ Log.i(TAG, "TextViewActivity has resumed");
+
+ mReceiver = new ScrollReceiver();
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Utils.SCROLL_TEXTVIEW_ACTION);
+ filter.addAction(Utils.SCROLL_SCROLLVIEW_ACTION);
+ registerReceiver(mReceiver, filter);
+
+ final View layout = findViewById(android.R.id.content);
+ ViewTreeObserver vto = layout.getViewTreeObserver();
+ vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ layout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ sendBroadcast(new Intent(Utils.APP_3P_HASRESUMED));
+ }
+ });
+ }
+
+ @Override
+ public void onPause() {
+ if (mReceiver != null) {
+ unregisterReceiver(mReceiver);
+ }
+ super.onPause();
+ }
+
+ class ScrollReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ int scrollX, scrollY;
+ scrollX = intent.getIntExtra(Utils.SCROLL_X_POSITION, 0);
+ scrollY = intent.getIntExtra(Utils.SCROLL_Y_POSITION, 0);
+ if (intent.getAction().equals(Utils.SCROLL_TEXTVIEW_ACTION)) {
+ Log.i(TAG, "Scrolling textview to (" + scrollX + "," + scrollY + ")");
+ if (scrollX < 0 || scrollY < 0) {
+ // Scroll to bottom as negative positions are not possible.
+ scrollX = mTextView.getWidth();
+ scrollY = mTextView.getLayout().getLineTop(mTextView.getLineCount())
+ - mTextView.getHeight();
+ }
+ TextViewActivity.this.mTextView.scrollTo(scrollX, scrollY);
+ } else if (intent.getAction().equals(Utils.SCROLL_SCROLLVIEW_ACTION)) {
+ Log.i(TAG, "Scrolling scrollview to (" + scrollX + "," + scrollY + ")");
+ if (scrollX < 0 || scrollY < 0) {
+ // Scroll to bottom
+ TextViewActivity.this.mScrollView.fullScroll(View.FOCUS_DOWN);
+ TextViewActivity.this.mScrollView.fullScroll(View.FOCUS_RIGHT);
+ } else {
+ TextViewActivity.this.mScrollView.scrollTo(scrollX, scrollY);
+ }
+ }
+ Log.i(TAG, "the max height of this textview is: " + mTextView.getHeight());
+ Log.i(TAG, "the max line count of this text view is: " + mTextView.getMaxLines());
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/WebViewActivity.java b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/WebViewActivity.java
new file mode 100644
index 0000000..59f96cb
--- /dev/null
+++ b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/WebViewActivity.java
@@ -0,0 +1,53 @@
+/*
+ * 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.assist.testapp;
+
+import android.assist.common.Utils;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+
+import java.lang.Override;
+
+public class WebViewActivity extends Activity {
+ static final String TAG = "WebViewActivity";
+
+ private String mTestCaseName;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Log.i(TAG, "TestApp created");
+ mTestCaseName = getIntent().getStringExtra(Utils.TESTCASE_TYPE);
+ setContentView(R.layout.webview);
+ WebView webview = (WebView) findViewById(R.id.webview);
+ webview.setWebViewClient(new WebViewClient() {
+ @Override
+ public void onPageFinished(WebView view, String url){
+ sendBroadcast(new Intent(Utils.APP_3P_HASRESUMED));
+ }
+ });
+ webview.loadData(Utils.WEBVIEW_HTML, "text/html", "UTF-8");
+ //webview.loadUrl(
+ // "https://android-developers.blogspot.com/2015/08/m-developer-preview-3-final-sdk.html");
+ }
+}
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeScanTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeScanTest.java
index a79df42..bc819c1 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeScanTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeScanTest.java
@@ -41,6 +41,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
@@ -58,6 +59,7 @@
private static final int SCAN_DURATION_MILLIS = 5000;
private static final int BATCH_SCAN_REPORT_DELAY_MILLIS = 20000;
+ private CountDownLatch mFlushBatchScanLatch;
private BluetoothAdapter mBluetoothAdapter;
private BluetoothLeScanner mScanner;
@@ -215,8 +217,14 @@
batchScanCallback);
sleep(SCAN_DURATION_MILLIS);
mScanner.flushPendingScanResults(batchScanCallback);
- sleep(1000);
+ mFlushBatchScanLatch = new CountDownLatch(1);
List<ScanResult> results = batchScanCallback.getBatchScanResults();
+ try {
+ mFlushBatchScanLatch.await(5, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ // Nothing to do.
+ Log.e(TAG, "interrupted!");
+ }
assertTrue(!results.isEmpty());
long scanEndMillis = SystemClock.elapsedRealtime();
mScanner.stopScan(batchScanCallback);
@@ -249,7 +257,12 @@
@Override
public void onBatchScanResults(List<ScanResult> results) {
- mBatchScanResults = results;
+ // In case onBatchScanResults are called due to buffer full, we want to collect all
+ // scan results.
+ mBatchScanResults.addAll(results);
+ if (mFlushBatchScanLatch != null) {
+ mFlushBatchScanLatch.countDown();
+ }
}
// Clear regular and batch scan results.
diff --git a/tests/tests/content/AndroidManifest.xml b/tests/tests/content/AndroidManifest.xml
index a20befb..bffb1f7 100644
--- a/tests/tests/content/AndroidManifest.xml
+++ b/tests/tests/content/AndroidManifest.xml
@@ -117,6 +117,14 @@
</intent-filter>
</receiver>
+ <!-- Receiver that will be explicitly disabled at runtime -->
+ <receiver android:name="android.content.cts.MockReceiverDisableable"
+ android:enabled="true">
+ <intent-filter android:priority="1">
+ <action android:name="android.content.cts.BroadcastReceiverTest.BROADCAST_DISABLED" />
+ </intent-filter>
+ </receiver>
+
<activity android:name="android.content.cts.AvailableIntentsActivity"
android:label="AvailableIntentsActivity">
<intent-filter>
diff --git a/tests/tests/content/src/android/content/cts/BroadcastReceiverTest.java b/tests/tests/content/src/android/content/cts/BroadcastReceiverTest.java
index 526087b..43d6cfe 100644
--- a/tests/tests/content/src/android/content/cts/BroadcastReceiverTest.java
+++ b/tests/tests/content/src/android/content/cts/BroadcastReceiverTest.java
@@ -24,6 +24,7 @@
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.test.ActivityInstrumentationTestCase2;
@@ -45,10 +46,16 @@
"android.content.cts.BroadcastReceiverTest.BROADCAST_MOCKTEST";
private static final String ACTION_BROADCAST_TESTABORT =
"android.content.cts.BroadcastReceiverTest.BROADCAST_TESTABORT";
+ private static final String ACTION_BROADCAST_DISABLED =
+ "android.content.cts.BroadcastReceiverTest.BROADCAST_DISABLED";
private static final long SEND_BROADCAST_TIMEOUT = 5000;
private static final long START_SERVICE_TIMEOUT = 3000;
+ private static final ComponentName DISABLEABLE_RECEIVER =
+ new ComponentName("com.android.cts.content",
+ "android.content.cts.MockReceiverDisableable");
+
public BroadcastReceiverTest() {
super("com.android.cts.content", MockActivity.class);
}
@@ -127,6 +134,26 @@
}
}
+ private class MockReceiverInternalVerifyUncalled extends MockReceiverInternal {
+ final int mExpectedInitialCode;
+
+ public MockReceiverInternalVerifyUncalled(int initialCode) {
+ mExpectedInitialCode = initialCode;
+ }
+
+ @Override
+ public synchronized void onReceive(Context context, Intent intent) {
+ // only update to the expected final values if we're still in the
+ // initial conditions. The intermediate receiver would have
+ // updated the result code if it [inappropriately] ran.
+ if (getResultCode() == mExpectedInitialCode) {
+ setResultCode(RESULT_INTERNAL_FINAL_CODE);
+ }
+
+ super.onReceive(context, intent);
+ }
+ }
+
public void testOnReceive () throws InterruptedException {
final MockActivity activity = getActivity();
@@ -202,6 +229,26 @@
resultExtras.getString(MockReceiverAbort.RESULT_EXTRAS_ABORT_KEY));
}
+ public void testDisabledBroadcastReceiver() throws Exception {
+ final Context context = getInstrumentation().getContext();
+ PackageManager pm = context.getPackageManager();
+
+ MockReceiverInternalVerifyUncalled lastReceiver =
+ new MockReceiverInternalVerifyUncalled(RESULT_INITIAL_CODE);
+ assertEquals(0, lastReceiver.getResultCode());
+
+ pm.setComponentEnabledSetting(DISABLEABLE_RECEIVER,
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+ PackageManager.DONT_KILL_APP);
+
+ context.sendOrderedBroadcast(
+ new Intent(ACTION_BROADCAST_DISABLED), null, lastReceiver,
+ null, RESULT_INITIAL_CODE, RESULT_INITIAL_DATA, new Bundle());
+ lastReceiver.waitForReceiver(SEND_BROADCAST_TIMEOUT);
+
+ assertEquals(RESULT_INTERNAL_FINAL_CODE, lastReceiver.getResultCode());
+ }
+
public void testPeekService() throws InterruptedException {
final MockActivity activity = getActivity();
diff --git a/tests/tests/content/src/android/content/cts/MockReceiverDisableable.java b/tests/tests/content/src/android/content/cts/MockReceiverDisableable.java
new file mode 100644
index 0000000..9ee73ed
--- /dev/null
+++ b/tests/tests/content/src/android/content/cts/MockReceiverDisableable.java
@@ -0,0 +1,31 @@
+/*
+ * 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.content.cts;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+
+public class MockReceiverDisableable extends BroadcastReceiver {
+ public static final int RESULT_CODE = 99;
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ setResultCode(RESULT_CODE);
+ }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java
index 0fef3d7..dd4e3e3 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java
@@ -69,7 +69,8 @@
private static final long EXPOSURE_TIME_BOUNDARY_50HZ_NS = 10000000L; // 10ms
private static final long EXPOSURE_TIME_BOUNDARY_60HZ_NS = 8333333L; // 8.3ms, Approximation.
private static final long EXPOSURE_TIME_ERROR_MARGIN_NS = 100000L; // 100us, Approximation.
- private static final int SENSITIVITY_ERROR_MARGIN = 10; // 10
+ private static final float EXPOSURE_TIME_ERROR_MARGIN_RATE = 0.03f; // 3%, Approximation.
+ private static final float SENSITIVITY_ERROR_MARGIN_RATE = 0.03f; // 3%, Approximation.
private static final int DEFAULT_NUM_EXPOSURE_TIME_STEPS = 3;
private static final int DEFAULT_NUM_SENSITIVITY_STEPS = 16;
private static final int DEFAULT_SENSITIVITY_STEP_SIZE = 100;
@@ -107,6 +108,12 @@
private final int INDEX_ALGORITHM_AWB = 1;
private final int INDEX_ALGORITHM_AF = 2;
+ private enum TorchSeqState {
+ RAMPING_UP,
+ FIRED,
+ RAMPING_DOWN
+ }
+
@Override
protected void setUp() throws Exception {
super.setUp();
@@ -963,16 +970,39 @@
waitForNumResults(listener, flashModeTorchRequests - NUM_FLASH_REQUESTS_TESTED);
// Verify the results
+ TorchSeqState state = TorchSeqState.RAMPING_UP;
for (int i = 0; i < NUM_FLASH_REQUESTS_TESTED; i++) {
result = listener.getCaptureResultForRequest(torchRequest,
NUM_RESULTS_WAIT_TIMEOUT);
-
- // Result mode must be TORCH, state must be FIRED
- mCollector.expectEquals("Flash mode result must be TORCH",
+ int flashMode = result.get(CaptureResult.FLASH_MODE);
+ int flashState = result.get(CaptureResult.FLASH_STATE);
+ // Result mode must be TORCH
+ mCollector.expectEquals("Flash mode result " + i + " must be TORCH",
CaptureResult.FLASH_MODE_TORCH, result.get(CaptureResult.FLASH_MODE));
- mCollector.expectEquals("Flash state result must be FIRED",
- CaptureResult.FLASH_STATE_FIRED, result.get(CaptureResult.FLASH_STATE));
+ if (state == TorchSeqState.RAMPING_UP &&
+ flashState == CaptureResult.FLASH_STATE_FIRED) {
+ state = TorchSeqState.FIRED;
+ } else if (state == TorchSeqState.FIRED &&
+ flashState == CaptureResult.FLASH_STATE_PARTIAL) {
+ state = TorchSeqState.RAMPING_DOWN;
+ }
+
+ if (i == 0 && mStaticInfo.isPerFrameControlSupported()) {
+ mCollector.expectTrue(
+ "Per frame control device must enter FIRED state on first torch request",
+ state == TorchSeqState.FIRED);
+ }
+
+ if (state == TorchSeqState.FIRED) {
+ mCollector.expectEquals("Flash state result " + i + " must be FIRED",
+ CaptureResult.FLASH_STATE_FIRED, result.get(CaptureResult.FLASH_STATE));
+ } else {
+ mCollector.expectEquals("Flash state result " + i + " must be PARTIAL",
+ CaptureResult.FLASH_STATE_PARTIAL, result.get(CaptureResult.FLASH_STATE));
+ }
}
+ mCollector.expectTrue("Torch state FIRED never seen",
+ state == TorchSeqState.FIRED || state == TorchSeqState.RAMPING_DOWN);
// Test flash OFF mode control
requestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);
@@ -1995,12 +2025,12 @@
private long[] getExposureTimeTestValues() {
long[] testValues = new long[DEFAULT_NUM_EXPOSURE_TIME_STEPS + 1];
long maxExpTime = mStaticInfo.getExposureMaximumOrDefault(DEFAULT_EXP_TIME_NS);
- long minxExpTime = mStaticInfo.getExposureMinimumOrDefault(DEFAULT_EXP_TIME_NS);
+ long minExpTime = mStaticInfo.getExposureMinimumOrDefault(DEFAULT_EXP_TIME_NS);
- long range = maxExpTime - minxExpTime;
+ long range = maxExpTime - minExpTime;
double stepSize = range / (double)DEFAULT_NUM_EXPOSURE_TIME_STEPS;
for (int i = 0; i < testValues.length; i++) {
- testValues[i] = minxExpTime + (long)(stepSize * i);
+ testValues[i] = maxExpTime - (long)(stepSize * i);
testValues[i] = mStaticInfo.getExposureClampToRange(testValues[i]);
}
@@ -2049,7 +2079,7 @@
}
int[] testValues = new int[numSteps + 1];
for (int i = 0; i < testValues.length; i++) {
- testValues[i] = minSensitivity + stepSize * i;
+ testValues[i] = maxSensitivity - stepSize * i;
testValues[i] = mStaticInfo.getSensitivityClampToRange(testValues[i]);
}
@@ -2066,10 +2096,12 @@
*/
private void validateExposureTime(long request, long result) {
long expTimeDelta = request - result;
+ long expTimeErrorMargin = (long)(Math.max(EXPOSURE_TIME_ERROR_MARGIN_NS, request
+ * EXPOSURE_TIME_ERROR_MARGIN_RATE));
// First, round down not up, second, need close enough.
mCollector.expectTrue("Exposture time is invalid for AE manaul control test, request: "
+ request + " result: " + result,
- expTimeDelta < EXPOSURE_TIME_ERROR_MARGIN_NS && expTimeDelta >= 0);
+ expTimeDelta < expTimeErrorMargin && expTimeDelta >= 0);
}
/**
@@ -2079,11 +2111,12 @@
* @param result Result sensitivity
*/
private void validateSensitivity(int request, int result) {
- int sensitivityDelta = request - result;
+ float sensitivityDelta = (float)(request - result);
+ float sensitivityErrorMargin = request * SENSITIVITY_ERROR_MARGIN_RATE;
// First, round down not up, second, need close enough.
mCollector.expectTrue("Sensitivity is invalid for AE manaul control test, request: "
+ request + " result: " + result,
- sensitivityDelta < SENSITIVITY_ERROR_MARGIN && sensitivityDelta >= 0);
+ sensitivityDelta < sensitivityErrorMargin && sensitivityDelta >= 0);
}
/**
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/PerformanceTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/PerformanceTest.java
index b14c551..39eb1dc 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/PerformanceTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/PerformanceTest.java
@@ -66,7 +66,7 @@
private static final int NUM_MAX_IMAGES = 4;
private static final int NUM_RESULTS_WAIT = 30;
private static final int[] REPROCESS_FORMATS = {ImageFormat.YUV_420_888, ImageFormat.PRIVATE};
- private final int MAX_REPROCESS_IMAGES = 10;
+ private final int MAX_REPROCESS_IMAGES = 6;
private final int MAX_JPEG_IMAGES = MAX_REPROCESS_IMAGES;
private final int MAX_INPUT_IMAGES = MAX_REPROCESS_IMAGES;
// ZSL queue depth should be bigger than the max simultaneous reprocessing capture request
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
index a6cf613..de0e3d5 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
@@ -14,6 +14,7 @@
import static android.hardware.camera2.cts.CameraTestUtils.*;
import static com.android.ex.camera2.blocking.BlockingSessionCallback.*;
+import android.cts.util.MediaUtils;
import android.graphics.ImageFormat;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraCaptureSession;
@@ -151,6 +152,9 @@
* </p>
*/
public void testRecordingFromPersistentSurface() throws Exception {
+ if (!MediaUtils.checkCodecForDomain(true /* encoder */, "video")) {
+ return; // skipped
+ }
mPersistentSurface = MediaCodec.createPersistentInputSurface();
assertNotNull("Failed to create persistent input surface!", mPersistentSurface);
@@ -375,7 +379,7 @@
prepareRecording(size, videoFramerate, captureRate);
// prepare preview surface by using video size.
- updatePreviewSurfaceWithVideoSize(size);
+ updatePreviewSurfaceWithVideo(size, captureRate);
// Start recording
SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
@@ -440,7 +444,7 @@
prepareRecording(size, VIDEO_FRAME_RATE, captureRate);
// prepare preview surface by using video size.
- updatePreviewSurfaceWithVideoSize(size);
+ updatePreviewSurfaceWithVideo(size, captureRate);
// Start recording
SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
@@ -631,7 +635,7 @@
prepareRecordingWithProfile(profile);
// prepare preview surface by using video size.
- updatePreviewSurfaceWithVideoSize(videoSz);
+ updatePreviewSurfaceWithVideo(videoSz, profile.videoFrameRate);
// Start recording
SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
@@ -681,7 +685,7 @@
prepareRecording(sz, VIDEO_FRAME_RATE, VIDEO_FRAME_RATE);
// prepare preview surface by using video size.
- updatePreviewSurfaceWithVideoSize(sz);
+ updatePreviewSurfaceWithVideo(sz, VIDEO_FRAME_RATE);
// Start recording
SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
@@ -821,13 +825,21 @@
getAvailableMinFrameDurationsForFormatChecked(ImageFormat.JPEG);
for (int i = mOrderedStillSizes.size() - 2; i >= 0; i--) {
Size candidateSize = mOrderedStillSizes.get(i);
- Long jpegFrameDuration = minFrameDurationMap.get(candidateSize);
- assertTrue("Cannot find minimum frame duration for jpeg size " + candidateSize,
- jpegFrameDuration != null);
- if (candidateSize.getWidth() <= videoSz.getWidth() &&
- candidateSize.getHeight() <= videoSz.getHeight() &&
- jpegFrameDuration <= videoFrameDuration) {
- videoSnapshotSz = candidateSize;
+ if (mStaticInfo.isHardwareLevelLegacy()) {
+ // Legacy level doesn't report min frame duration
+ if (candidateSize.getWidth() <= videoSz.getWidth() &&
+ candidateSize.getHeight() <= videoSz.getHeight()) {
+ videoSnapshotSz = candidateSize;
+ }
+ } else {
+ Long jpegFrameDuration = minFrameDurationMap.get(candidateSize);
+ assertTrue("Cannot find minimum frame duration for jpeg size " + candidateSize,
+ jpegFrameDuration != null);
+ if (candidateSize.getWidth() <= videoSz.getWidth() &&
+ candidateSize.getHeight() <= videoSz.getHeight() &&
+ jpegFrameDuration <= videoFrameDuration) {
+ videoSnapshotSz = candidateSize;
+ }
}
}
@@ -893,7 +905,7 @@
CameraDevice.TEMPLATE_VIDEO_SNAPSHOT);
// prepare preview surface by using video size.
- updatePreviewSurfaceWithVideoSize(videoSz);
+ updatePreviewSurfaceWithVideo(videoSz, profile.videoFrameRate);
prepareVideoSnapshot(videoSnapshotRequestBuilder, imageListener);
CaptureRequest request = videoSnapshotRequestBuilder.build();
@@ -993,20 +1005,44 @@
* <p>Preview size will be capped with max preview size.</p>
*
* @param videoSize The video size used for preview.
+ * @param videoFrameRate The video frame rate
+ *
*/
- private void updatePreviewSurfaceWithVideoSize(Size videoSize) {
+ private void updatePreviewSurfaceWithVideo(Size videoSize, int videoFrameRate) {
if (mOrderedPreviewSizes == null) {
throw new IllegalStateException("supported preview size list is not initialized yet");
}
+ final float FRAME_DURATION_TOLERANCE = 0.01f;
+ long videoFrameDuration = (long) (1e9 / videoFrameRate *
+ (1.0 + FRAME_DURATION_TOLERANCE));
+ HashMap<Size, Long> minFrameDurationMap = mStaticInfo.
+ getAvailableMinFrameDurationsForFormatChecked(ImageFormat.PRIVATE);
Size maxPreviewSize = mOrderedPreviewSizes.get(0);
- Size previewSize = videoSize;
+ Size previewSize = null;
if (videoSize.getWidth() > maxPreviewSize.getWidth() ||
videoSize.getHeight() > maxPreviewSize.getHeight()) {
- Log.w(TAG, "Overwrite preview size from " + videoSize.toString() +
- " to " + maxPreviewSize.toString());
- previewSize = maxPreviewSize;
+ for (Size s : mOrderedPreviewSizes) {
+ Long frameDuration = minFrameDurationMap.get(s);
+ if (mStaticInfo.isHardwareLevelLegacy()) {
+ // Legacy doesn't report min frame duration
+ frameDuration = new Long(0);
+ }
+ assertTrue("Cannot find minimum frame duration for private size" + s,
+ frameDuration != null);
+ if (frameDuration <= videoFrameDuration &&
+ s.getWidth() <= videoSize.getWidth() &&
+ s.getHeight() <= videoSize.getHeight()) {
+ Log.w(TAG, "Overwrite preview size from " + videoSize.toString() +
+ " to " + s.toString());
+ previewSize = s;
+ break;
+ // If all preview size doesn't work then we fallback to video size
+ }
+ }
}
-
+ if (previewSize == null) {
+ previewSize = videoSize;
+ }
updatePreviewSurface(previewSize);
}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/ReprocessCaptureTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/ReprocessCaptureTest.java
index 8a60dc2..dd49c8d 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/ReprocessCaptureTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/ReprocessCaptureTest.java
@@ -1211,6 +1211,9 @@
throw new IllegalArgumentException("isReprocessCaptures must have at least 1 capture.");
}
+ boolean hasReprocessRequest = false;
+ boolean hasRegularRequest = false;
+
TotalCaptureResult[] results = new TotalCaptureResult[isReprocessCaptures.length];
for (int i = 0; i < isReprocessCaptures.length; i++) {
// submit a capture and get the result if this entry is a reprocess capture.
@@ -1219,6 +1222,9 @@
/*inputResult*/null);
mImageWriter.queueInputImage(
mFirstImageReaderListener.getImage(CAPTURE_TIMEOUT_MS));
+ hasReprocessRequest = true;
+ } else {
+ hasRegularRequest = true;
}
}
@@ -1232,7 +1238,24 @@
ImageResultHolder[] holders = new ImageResultHolder[isReprocessCaptures.length];
for (int i = 0; i < isReprocessCaptures.length; i++) {
Image image = getReprocessOutputImageReaderListener().getImage(CAPTURE_TIMEOUT_MS);
- holders[i] = new ImageResultHolder(image, finalResults[i]);
+ if (hasReprocessRequest && hasRegularRequest) {
+ // If there are mixed requests, images and results may not be in the same order.
+ for (int j = 0; j < finalResults.length; j++) {
+ if (finalResults[j] != null &&
+ finalResults[j].get(CaptureResult.SENSOR_TIMESTAMP) ==
+ image.getTimestamp()) {
+ holders[i] = new ImageResultHolder(image, finalResults[j]);
+ finalResults[j] = null;
+ break;
+ }
+ }
+
+ assertNotNull("Cannot find a result matching output image's timestamp: " +
+ image.getTimestamp(), holders[i]);
+ } else {
+ // If no mixed requests, images and results should be in the same order.
+ holders[i] = new ImageResultHolder(image, finalResults[i]);
+ }
}
return holders;
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java
index 8ccc931..ddae8eb 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java
@@ -1351,8 +1351,8 @@
Log.i(TAG, String.format("Testing Camera %s, config %s",
cameraId, MaxStreamSizes.configToString(config)));
- // Timeout is relaxed by 500ms for LEGACY devices to reduce false positive rate in CTS
- final int TIMEOUT_FOR_RESULT_MS = (mStaticInfo.isHardwareLevelLegacy()) ? 1500 : 1000;
+ // Timeout is relaxed by 1 second for LEGACY devices to reduce false positive rate in CTS
+ final int TIMEOUT_FOR_RESULT_MS = (mStaticInfo.isHardwareLevelLegacy()) ? 2000 : 1000;
final int MIN_RESULT_COUNT = 3;
// Set up outputs
diff --git a/tests/tests/hardware/src/android/hardware/cts/LowRamDeviceTest.java b/tests/tests/hardware/src/android/hardware/cts/LowRamDeviceTest.java
index 68efef0..a2ddb4d 100755
--- a/tests/tests/hardware/src/android/hardware/cts/LowRamDeviceTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/LowRamDeviceTest.java
@@ -105,7 +105,7 @@
if (supports64Bit) {
assertMinMemoryMb(1824);
} else {
- assertMinMemoryMb(1344);
+ assertMinMemoryMb(1099);
}
} else if (greaterThanDpi(density, DENSITY_400, screenSize,
SCREENLAYOUT_SIZE_NORMAL, SCREENLAYOUT_SIZE_SMALL) ||
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorParameterRangeTest.java b/tests/tests/hardware/src/android/hardware/cts/SensorParameterRangeTest.java
index 4750b09..22f092a 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorParameterRangeTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorParameterRangeTest.java
@@ -35,21 +35,21 @@
*/
public class SensorParameterRangeTest extends SensorTestCase {
- private static final double ACCELEROMETER_MAX_RANGE = 8 * 9.81; // 8G
- private static final int ACCELEROMETER_MIN_FREQUENCY = 5;
+ private static final double ACCELEROMETER_MAX_RANGE = 8 * 9.80; // 8G minus a slop
+ private static final double ACCELEROMETER_MIN_FREQUENCY = 12.50;
private static final int ACCELEROMETER_MAX_FREQUENCY = 200;
- private static final double GYRO_MAX_RANGE = 1000/57.295; // 1000 degrees per sec.
- private static final int GYRO_MIN_FREQUENCY = 5;
- private static final int GYRO_MAX_FREQUENCY = 200;
+ private static final double GYRO_MAX_RANGE = 1000/57.295 - 1.0; // 1000 degrees per sec minus a slop
+ private static final double GYRO_MIN_FREQUENCY = 12.50;
+ private static final double GYRO_MAX_FREQUENCY = 200.0;
private static final int MAGNETOMETER_MAX_RANGE = 900; // micro telsa
- private static final int MAGNETOMETER_MIN_FREQUENCY = 5;
- private static final int MAGNETOMETER_MAX_FREQUENCY = 50;
+ private static final double MAGNETOMETER_MIN_FREQUENCY = 5.0;
+ private static final double MAGNETOMETER_MAX_FREQUENCY = 50.0;
- private static final int PRESSURE_MAX_RANGE = 1100; // hecto-pascal
- private static final int PRESSURE_MIN_FREQUENCY = 1;
- private static final int PRESSURE_MAX_FREQUENCY = 10;
+ private static final double PRESSURE_MAX_RANGE = 1100.0; // hecto-pascal
+ private static final double PRESSURE_MIN_FREQUENCY = 1.0;
+ private static final double PRESSURE_MAX_FREQUENCY = 10.0;
private boolean mHasHifiSensors;
private SensorManager mSensorManager;
@@ -94,7 +94,7 @@
}
private void checkSensorRangeAndFrequency(
- Sensor sensor, double maxRange, int minFrequency, int maxFrequency) {
+ Sensor sensor, double maxRange, double minFrequency, double maxFrequency) {
if (!mHasHifiSensors) return;
assertTrue(String.format("%s Range actual=%.2f expected=%.2f %s",
sensor.getName(), sensor.getMaximumRange(), maxRange,
@@ -102,14 +102,14 @@
sensor.getMaximumRange() >= maxRange);
double actualMinFrequency = SensorCtsHelper.getFrequency(sensor.getMaxDelay(),
TimeUnit.MICROSECONDS);
- assertTrue(String.format("%s Min Frequency actual=%.2f expected=%dHz",
+ assertTrue(String.format("%s Min Frequency actual=%.2f expected=%.2fHz",
sensor.getName(), actualMinFrequency, minFrequency), actualMinFrequency <=
- minFrequency);
+ minFrequency + 0.1);
double actualMaxFrequency = SensorCtsHelper.getFrequency(sensor.getMinDelay(),
TimeUnit.MICROSECONDS);
- assertTrue(String.format("%s Max Frequency actual=%.2f expected=%dHz",
+ assertTrue(String.format("%s Max Frequency actual=%.2f expected=%.2fHz",
sensor.getName(), actualMaxFrequency, maxFrequency), actualMaxFrequency >=
- maxFrequency);
+ maxFrequency - 0.1);
}
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorTest.java b/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
index 2c3c6f4..cffafdc 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
@@ -88,7 +88,7 @@
}
@Override
- protected void tearDown(){
+ protected void tearDown() {
if (mSensorManager != null) {
// SensorManager will check listener and status, so just unregister listener
mSensorManager.unregisterListener(mNullSensorEventListener);
@@ -256,7 +256,8 @@
if (mTriggerSensor == null) {
throw new SensorNotSupportedException(Sensor.TYPE_ACCELEROMETER);
}
- boolean result = mSensorManager.requestTriggerSensor(mNullTriggerEventListener, mTriggerSensor);
+ boolean result =
+ mSensorManager.requestTriggerSensor(mNullTriggerEventListener, mTriggerSensor);
assertFalse(result);
}
@@ -265,7 +266,8 @@
if (mTriggerSensor == null) {
throw new SensorNotSupportedException(Sensor.TYPE_ACCELEROMETER);
}
- boolean result = mSensorManager.cancelTriggerSensor(mNullTriggerEventListener, mTriggerSensor);
+ boolean result =
+ mSensorManager.cancelTriggerSensor(mNullTriggerEventListener, mTriggerSensor);
assertFalse(result);
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
index 55465ac..9cb3710 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
@@ -38,21 +38,32 @@
private SensorCtsHelper() {}
/**
- * Get the value of the 95th percentile using nearest rank algorithm.
+ * Get percentiles using nearest rank algorithm.
*
- * @throws IllegalArgumentException if the collection is null or empty
+ * @param percentiles List of percentiles interested. Its range is 0 to 1, instead of in %.
+ * The value will be internally bounded.
+ *
+ * @throws IllegalArgumentException if the collection or percentiles is null or empty
*/
- public static <TValue extends Comparable<? super TValue>> TValue get95PercentileValue(
- Collection<TValue> collection) {
+ public static <TValue extends Comparable<? super TValue>> List<TValue> getPercentileValue(
+ Collection<TValue> collection, float[] percentiles) {
validateCollection(collection);
+ if(percentiles == null || percentiles.length == 0) {
+ throw new IllegalStateException("percentiles cannot be null or empty");
+ }
List<TValue> arrayCopy = new ArrayList<TValue>(collection);
Collections.sort(arrayCopy);
- // zero-based array index
- int arrayIndex = (int) Math.round(arrayCopy.size() * 0.95 + .5) - 1;
-
- return arrayCopy.get(arrayIndex);
+ List<TValue> percentileValues = new ArrayList<TValue>();
+ for (float p : percentiles) {
+ // zero-based array index
+ int arrayIndex = (int) Math.round(arrayCopy.size() * p - .5f);
+ // bound the index to avoid out of range error
+ arrayIndex = Math.min(Math.max(arrayIndex, 0), arrayCopy.size() - 1);
+ percentileValues.add(arrayCopy.get(arrayIndex));
+ }
+ return percentileValues;
}
/**
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEnvironment.java b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEnvironment.java
index 6156d3d..47c8313 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEnvironment.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEnvironment.java
@@ -223,7 +223,7 @@
if (mSamplingPeriodUs == SensorManager.SENSOR_DELAY_FASTEST) {
return "fastest";
}
- return String.format("%.0fhz", getFrequencyHz());
+ return String.format("%.2fhz", getFrequencyHz());
}
/**
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java
index 3e70e75..e8df1ab 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java
@@ -172,8 +172,7 @@
*/
public List<TestSensorEvent> getCollectedEvents() {
synchronized (mCollectedEvents){
- List<TestSensorEvent> collectedEventsList = (List)mCollectedEvents.clone();
- return Collections.unmodifiableList(collectedEventsList);
+ return Collections.unmodifiableList((List<TestSensorEvent>) mCollectedEvents.clone());
}
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FrequencyVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FrequencyVerification.java
index cf34f28..2f4777b 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FrequencyVerification.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FrequencyVerification.java
@@ -128,9 +128,9 @@
stats.addValue(SensorStats.FREQUENCY_KEY, measuredFrequencyHz);
stats.addValue(PASSED_KEY, !failed);
String resultString = String.format(
- "Requested \"%s\" at %.2fHz (expecting between %.2fHz and %.2fHz, measured %.2fHz)",
+ "Requested \"%s\" at %s (expecting between %.2fHz and %.2fHz, measured %.2fHz)",
environment.getSensor().getName(),
- environment.getFrequencyHz(),
+ environment.getFrequencyString(),
mLowerThresholdHz,
mUpperThresholdHz,
measuredFrequencyHz);
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/JitterVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/JitterVerification.java
index d246ec5..9d36f37 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/JitterVerification.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/JitterVerification.java
@@ -16,8 +16,6 @@
package android.hardware.cts.helpers.sensorverification;
-import junit.framework.Assert;
-
import android.content.Context;
import android.content.pm.PackageManager;
@@ -34,6 +32,7 @@
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
+import junit.framework.Assert;
/**
* A {@link ISensorVerification} which verifies that the sensor jitter is in an acceptable range.
@@ -43,15 +42,22 @@
// sensorType: threshold (% of expected period)
private static final SparseIntArray DEFAULTS = new SparseIntArray(12);
- // Max allowed jitter (in percentage).
+ // Max allowed jitter in +/- sense (in percentage).
private static final int GRACE_FACTOR = 2;
private static final int THRESHOLD_PERCENT_FOR_HIFI_SENSORS = 1 * GRACE_FACTOR;
+
+ // Margin sample intervals that considered outliers, lower and higher margin is discarded
+ // before verification
+ private static final float OUTLIER_MARGIN = 0.025f; //2.5%
+
static {
// Use a method so that the @deprecation warning can be set for that method only
setDefaults();
}
- private final int mThresholdAsPercentage;
+ private final float mOutlierMargin;
+ private final long mThresholdNs;
+ private final long mExpectedPeriodNs; // for error message only
private final List<Long> mTimestamps = new LinkedList<Long>();
/**
@@ -59,8 +65,10 @@
*
* @param thresholdAsPercentage the acceptable margin of error as a percentage
*/
- public JitterVerification(int thresholdAsPercentage) {
- mThresholdAsPercentage = thresholdAsPercentage;
+ public JitterVerification(float outlierMargin, long thresholdNs, long expectedPeriodNs) {
+ mExpectedPeriodNs = expectedPeriodNs;
+ mOutlierMargin = outlierMargin;
+ mThresholdNs = thresholdNs;
}
/**
@@ -71,16 +79,20 @@
*/
public static JitterVerification getDefault(TestSensorEnvironment environment) {
int sensorType = environment.getSensor().getType();
- int threshold = DEFAULTS.get(sensorType, -1);
- if (threshold == -1) {
+
+ int thresholdPercent = DEFAULTS.get(sensorType, -1);
+ if (thresholdPercent == -1) {
return null;
}
boolean hasHifiSensors = environment.getContext().getPackageManager().hasSystemFeature(
PackageManager.FEATURE_HIFI_SENSORS);
if (hasHifiSensors) {
- threshold = THRESHOLD_PERCENT_FOR_HIFI_SENSORS;
+ thresholdPercent = THRESHOLD_PERCENT_FOR_HIFI_SENSORS;
}
- return new JitterVerification(threshold);
+
+ long expectedPeriodNs = (long) environment.getExpectedSamplingPeriodUs() * 1000;
+ long jitterThresholdNs = expectedPeriodNs * thresholdPercent * 2 / 100; // *2 is for +/-
+ return new JitterVerification(OUTLIER_MARGIN, jitterThresholdNs, expectedPeriodNs);
}
/**
@@ -99,24 +111,33 @@
return;
}
- List<Double> jitters = getJitterValues();
- double jitter95PercentileNs = SensorCtsHelper.get95PercentileValue(jitters);
- long firstTimestamp = mTimestamps.get(0);
- long lastTimestamp = mTimestamps.get(timestampsCount - 1);
- long measuredPeriodNs = (lastTimestamp - firstTimestamp) / (timestampsCount - 1);
- double jitter95PercentilePercent = (jitter95PercentileNs * 100.0) / measuredPeriodNs;
- stats.addValue(SensorStats.JITTER_95_PERCENTILE_PERCENT_KEY, jitter95PercentilePercent);
+ List<Long> deltas = getDeltaValues();
+ float percentiles[] = new float[2];
+ percentiles[0] = mOutlierMargin;
+ percentiles[1] = 1 - percentiles[0];
- boolean success = (jitter95PercentilePercent < mThresholdAsPercentage);
+ List<Long> percentileValues = SensorCtsHelper.getPercentileValue(deltas, percentiles);
+ double normalizedRange =
+ (double)(percentileValues.get(1) - percentileValues.get(0)) / mThresholdNs;
+
+ double percentageJitter =
+ (double)(percentileValues.get(1) - percentileValues.get(0)) /
+ mExpectedPeriodNs / 2 * 100; //one side variation comparing to sample time
+
+ stats.addValue(SensorStats.JITTER_95_PERCENTILE_PERCENT_KEY, percentageJitter);
+
+ boolean success = normalizedRange <= 1.0;
stats.addValue(PASSED_KEY, success);
if (!success) {
String message = String.format(
- "Jitter out of range: measured period=%dns, jitter(95th percentile)=%.2f%%"
- + " (expected < %d%%)",
- measuredPeriodNs,
- jitter95PercentilePercent,
- mThresholdAsPercentage);
+ "Jitter out of range: requested period = %dns, " +
+ "jitter min, max, range (95th percentile) = (%dns, %dns, %dns), " +
+ "jitter expected range <= %dns",
+ mExpectedPeriodNs,
+ percentileValues.get(0), percentileValues.get(1),
+ percentileValues.get(1) - percentileValues.get(0),
+ mThresholdNs);
Assert.fail(message);
}
}
@@ -126,7 +147,7 @@
*/
@Override
public JitterVerification clone() {
- return new JitterVerification(mThresholdAsPercentage);
+ return new JitterVerification(mOutlierMargin, mThresholdNs, mExpectedPeriodNs);
}
/**
@@ -138,19 +159,14 @@
}
/**
- * Get the list of all jitter values. Exposed for unit testing.
+ * Get the list of delta values. Exposed for unit testing.
*/
- List<Double> getJitterValues() {
+ List<Long> getDeltaValues() {
List<Long> deltas = new ArrayList<Long>(mTimestamps.size() - 1);
for (int i = 1; i < mTimestamps.size(); i++) {
deltas.add(mTimestamps.get(i) - mTimestamps.get(i - 1));
}
- double deltaMean = StatisticsUtils.getMean(deltas);
- List<Double> jitters = new ArrayList<Double>(deltas.size());
- for (long delta : deltas) {
- jitters.add(Math.abs(delta - deltaMean));
- }
- return jitters;
+ return deltas;
}
@SuppressWarnings("deprecation")
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/JitterVerificationTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/JitterVerificationTest.java
index 50e288c..d8e1586 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/JitterVerificationTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/JitterVerificationTest.java
@@ -27,11 +27,10 @@
import java.util.List;
/**
- * Tests for {@link EventOrderingVerification}.
+ * Tests for {@link JitterVerification}.
*/
public class JitterVerificationTest extends TestCase {
-
public void testVerify() {
final int SAMPLE_SIZE = 100;
// for unit testing the verification, only the parameter 'sensorMightHaveMoreListeners' is
@@ -67,54 +66,58 @@
} catch (AssertionError e) {
// Expected;
}
- verifyStats(stats, false, 47.34);
+ verifyStats(stats, false, 25); // 500 us range (250 us in single-sided sense)
+ // divide by 1ms requested sample time x 100%
}
- public void testCalculateJitter() {
+ public void testCalculateDelta() {
long[] timestamps = new long[]{0, 1, 2, 3, 4};
JitterVerification verification = getVerification(1, timestamps);
- List<Double> jitterValues = verification.getJitterValues();
- assertEquals(4, jitterValues.size());
- assertEquals(0.0, jitterValues.get(0));
- assertEquals(0.0, jitterValues.get(1));
- assertEquals(0.0, jitterValues.get(2));
- assertEquals(0.0, jitterValues.get(3));
+ List<Long> deltaValues = verification.getDeltaValues();
+ assertEquals(4, deltaValues.size());
+ assertEquals(1, deltaValues.get(0).longValue());
+ assertEquals(1, deltaValues.get(1).longValue());
+ assertEquals(1, deltaValues.get(2).longValue());
+ assertEquals(1, deltaValues.get(3).longValue());
timestamps = new long[]{0, 0, 2, 4, 4};
verification = getVerification(1, timestamps);
- jitterValues = verification.getJitterValues();
- assertEquals(4, jitterValues.size());
- assertEquals(1.0, jitterValues.get(0));
- assertEquals(1.0, jitterValues.get(1));
- assertEquals(1.0, jitterValues.get(2));
- assertEquals(1.0, jitterValues.get(3));
+ deltaValues = verification.getDeltaValues();
+ assertEquals(4, deltaValues.size());
+ assertEquals(0, deltaValues.get(0).longValue());
+ assertEquals(2, deltaValues.get(1).longValue());
+ assertEquals(2, deltaValues.get(2).longValue());
+ assertEquals(0, deltaValues.get(3).longValue());
timestamps = new long[]{0, 1, 4, 9, 16};
verification = getVerification(1, timestamps);
- jitterValues = verification.getJitterValues();
- assertEquals(4, jitterValues.size());
- assertEquals(4, jitterValues.size());
- assertEquals(3.0, jitterValues.get(0));
- assertEquals(1.0, jitterValues.get(1));
- assertEquals(1.0, jitterValues.get(2));
- assertEquals(3.0, jitterValues.get(3));
+ deltaValues = verification.getDeltaValues();
+ assertEquals(4, deltaValues.size());
+ assertEquals(1, deltaValues.get(0).longValue());
+ assertEquals(3, deltaValues.get(1).longValue());
+ assertEquals(5, deltaValues.get(2).longValue());
+ assertEquals(7, deltaValues.get(3).longValue());
}
- private static JitterVerification getVerification(int threshold, long ... timestamps) {
+ private static JitterVerification getVerification(int marginPercent, long ... timestamps) {
Collection<TestSensorEvent> events = new ArrayList<>(timestamps.length);
for (long timestamp : timestamps) {
events.add(new TestSensorEvent(null, timestamp, 0, null));
}
- JitterVerification verification = new JitterVerification(threshold);
+ long samplePeriodNs = 1000*1000; //1000Hz
+ long jitterThresholdNs = 20*1000; // 2% of that
+
+ JitterVerification verification =
+ new JitterVerification(marginPercent/100.0f, jitterThresholdNs, samplePeriodNs);
verification.addSensorEvents(events);
return verification;
}
- private void verifyStats(SensorStats stats, boolean passed, double jitter95) {
+ private void verifyStats(SensorStats stats, boolean passed, double percentageJitter) {
assertEquals(passed, stats.getValue(JitterVerification.PASSED_KEY));
assertEquals(
- jitter95,
+ percentageJitter,
(Double) stats.getValue(SensorStats.JITTER_95_PERCENTILE_PERCENT_KEY),
- 0.1);
+ 0.01);
}
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/StandardDeviationVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/StandardDeviationVerification.java
index 1e1c950..1b66e6a 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/StandardDeviationVerification.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/StandardDeviationVerification.java
@@ -24,8 +24,10 @@
import android.hardware.cts.helpers.SensorStats;
import android.hardware.cts.helpers.TestSensorEnvironment;
import android.hardware.cts.helpers.TestSensorEvent;
+import android.hardware.cts.helpers.SensorCtsHelper;
import android.util.Log;
+import java.util.concurrent.TimeUnit;
import java.util.HashMap;
import java.util.Map;
@@ -66,18 +68,37 @@
*/
public static StandardDeviationVerification getDefault(TestSensorEnvironment environment) {
int sensorType = environment.getSensor().getType();
- float mGraceFactorAccelGyro = 2.0f;
- float mGraceFactorMagPressure = 4.0f;
- float mMaxBandWidth = (float) environment.getFrequencyHz();
- float mAccelNoise = (float)(mGraceFactorAccelGyro * Math.sqrt(mMaxBandWidth) * (9.81 * 0.0004));
- float mGyroNoise = (float)(mGraceFactorAccelGyro * Math.sqrt(mMaxBandWidth) * (Math.PI/180.0 * 0.014));
- float mMagNoise = (float)((mGraceFactorMagPressure) * 0.5); // Allow extra grace for mag
- float mPressureNoise = (float)(mGraceFactorMagPressure * 0.02 * (float)Math.sqrt(mMaxBandWidth)); // Allow extra grace for pressure
+ float graceFactorAccelGyro = 2.0f;
+ float graceFactorMagPressure = 4.0f;
+ float currOperatingFreq = (float) environment.getFrequencyHz();
+ float maxBandWidth = (float)SensorCtsHelper.getFrequency(
+ environment.getSensor().getMinDelay(), TimeUnit.MICROSECONDS);
+ float minBandWidth = (float) SensorCtsHelper.getFrequency(
+ environment.getSensor().getMaxDelay(), TimeUnit.MICROSECONDS);
+
+ if (Float.isInfinite(currOperatingFreq)) {
+ currOperatingFreq = maxBandWidth;
+ }
+
+ if (currOperatingFreq > maxBandWidth && !Float.isInfinite(maxBandWidth)) {
+ currOperatingFreq = maxBandWidth;
+ }
+
+ if (currOperatingFreq < minBandWidth && !Float.isInfinite(minBandWidth)) {
+ currOperatingFreq = minBandWidth;
+ }
+
+ float mAccelNoise = (float)(graceFactorAccelGyro * Math.sqrt(currOperatingFreq) *
+ (9.81 * 0.0004));
+ float mGyroNoise = (float)(graceFactorAccelGyro * Math.sqrt(currOperatingFreq) *
+ (Math.PI/180.0 * 0.014));
+ float mMagNoise = (float)((graceFactorMagPressure) * 0.5); // Allow extra grace for mag
+ float mPressureNoise = (float)(graceFactorMagPressure * 0.02 *
+ (float)Math.sqrt(currOperatingFreq)); // Allow extra grace for pressure
if (!DEFAULTS.containsKey(sensorType)) {
return null;
}
-
boolean hasHifiSensors = environment.getContext().getPackageManager().hasSystemFeature(
PackageManager.FEATURE_HIFI_SENSORS);
@@ -132,9 +153,9 @@
if (stdDevs[i] > mThreshold[i]) {
failed = true;
}
- stddevSb.append(String.format("%.3f", stdDevs[i]));
+ stddevSb.append(String.format("%.6f", stdDevs[i]));
if (i != stdDevs.length - 1) stddevSb.append(", ");
- expectedSb.append(String.format("<%.3f", mThreshold[i]));
+ expectedSb.append(String.format("<%.6f", mThreshold[i]));
if (i != stdDevs.length - 1) expectedSb.append(", ");
}
if (stdDevs.length > 1) {
diff --git a/tests/tests/hardware/src/android/hardware/multiprocess/camera/cts/CameraEvictionTest.java b/tests/tests/hardware/src/android/hardware/multiprocess/camera/cts/CameraEvictionTest.java
index aa34de3..d1ca19a 100644
--- a/tests/tests/hardware/src/android/hardware/multiprocess/camera/cts/CameraEvictionTest.java
+++ b/tests/tests/hardware/src/android/hardware/multiprocess/camera/cts/CameraEvictionTest.java
@@ -16,6 +16,7 @@
package android.hardware.multiprocess.camera.cts;
+import android.app.Activity;
import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
@@ -112,7 +113,8 @@
super.setUp();
mCompleted = false;
- mContext = getActivity();
+ getActivity();
+ mContext = getInstrumentation().getTargetContext();
System.setProperty("dexmaker.dexcache", mContext.getCacheDir().toString());
mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
mErrorServiceConnection = new ErrorLoggingService.ErrorServiceConnection(mContext);
@@ -232,6 +234,7 @@
assertTrue("Remote camera service exited early", timeoutExceptionHit);
android.os.Process.killProcess(mProcessPid);
mProcessPid = -1;
+ forceCtsActivityToTop();
}
/**
@@ -337,6 +340,19 @@
assertTrue("Remote camera service exited early", timeoutExceptionHit);
android.os.Process.killProcess(mProcessPid);
mProcessPid = -1;
+ forceCtsActivityToTop();
+ }
+
+ /**
+ * Ensure the CTS activity becomes foreground again instead of launcher.
+ */
+ private void forceCtsActivityToTop() throws InterruptedException {
+ Thread.sleep(WAIT_TIME);
+ Activity a = getActivity();
+ Intent activityIntent = new Intent(a, CameraCtsActivity.class);
+ activityIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ a.startActivity(activityIntent);
+ Thread.sleep(WAIT_TIME);
}
/**
@@ -389,15 +405,15 @@
public void startRemoteProcess(java.lang.Class<?> klass, String processName)
throws InterruptedException {
// Ensure no running activity process with same name
- String cameraActivityName = mContext.getPackageName() + ":" + processName;
+ Activity a = getActivity();
+ String cameraActivityName = a.getPackageName() + ":" + processName;
List<ActivityManager.RunningAppProcessInfo> list =
mActivityManager.getRunningAppProcesses();
assertEquals(-1, getPid(cameraActivityName, list));
// Start activity in a new top foreground process
- Intent activityIntent = new Intent(mContext, klass);
- activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- mContext.startActivity(activityIntent);
+ Intent activityIntent = new Intent(a, klass);
+ a.startActivity(activityIntent);
Thread.sleep(WAIT_TIME);
// Fail if activity isn't running
diff --git a/tests/tests/media/res/raw/heap_oob_flac.mp3 b/tests/tests/media/res/raw/heap_oob_flac.mp3
new file mode 100644
index 0000000..ae542d0
--- /dev/null
+++ b/tests/tests/media/res/raw/heap_oob_flac.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/on_input_buffer_filled_sigsegv.mp4 b/tests/tests/media/res/raw/on_input_buffer_filled_sigsegv.mp4
new file mode 100644
index 0000000..110c0d6
--- /dev/null
+++ b/tests/tests/media/res/raw/on_input_buffer_filled_sigsegv.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/test1m1shighstereo.mp3 b/tests/tests/media/res/raw/test1m1shighstereo.mp3
new file mode 100644
index 0000000..2a97077
--- /dev/null
+++ b/tests/tests/media/res/raw/test1m1shighstereo.mp3
Binary files differ
diff --git a/tests/tests/media/src/android/media/cts/AudioTrackTest.java b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
index 4c03183..9ae6b64 100644
--- a/tests/tests/media/src/android/media/cts/AudioTrackTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
@@ -1646,6 +1646,7 @@
Thread.sleep(WAIT_MSEC); // wait for the data to drain.
// -------- tear down --------------
track.release();
+ Thread.sleep(WAIT_MSEC); // wait for release to complete
frequency += 50; // increment test tone frequency
}
}
diff --git a/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java b/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java
index 9da229c..8650d20 100644
--- a/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java
+++ b/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java
@@ -20,6 +20,8 @@
import android.content.res.AssetFileDescriptor;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
+import android.media.MediaCodecInfo.CodecCapabilities;
+import android.media.MediaCodecInfo.CodecProfileLevel;
import android.media.MediaCodecList;
import android.media.MediaExtractor;
import android.media.MediaFormat;
@@ -1141,4 +1143,87 @@
private static String getMimeTypeFor(MediaFormat format) {
return format.getString(MediaFormat.KEY_MIME);
}
+
+ /**
+ * Returns the first codec capable of encoding the specified MIME type, or null if no match was
+ * found.
+ */
+ private static MediaCodecInfo selectCodec(String mimeType) {
+ int numCodecs = MediaCodecList.getCodecCount();
+ for (int i = 0; i < numCodecs; i++) {
+ MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
+
+ if (!codecInfo.isEncoder()) {
+ continue;
+ }
+
+ String[] types = codecInfo.getSupportedTypes();
+ for (int j = 0; j < types.length; j++) {
+ if (types[j].equalsIgnoreCase(mimeType)) {
+ return codecInfo;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Checks whether the given resolution is supported by the AVC codec.
+ */
+ private static boolean isAvcSupportedSize(int width, int height) {
+ MediaCodecInfo mediaCodecInfo = selectCodec(OUTPUT_VIDEO_MIME_TYPE);
+ CodecCapabilities cap = mediaCodecInfo.getCapabilitiesForType(OUTPUT_VIDEO_MIME_TYPE);
+ if (cap == null) { // not supported
+ return false;
+ }
+ int highestLevel = 0;
+ for (CodecProfileLevel lvl : cap.profileLevels) {
+ if (lvl.level > highestLevel) {
+ highestLevel = lvl.level;
+ }
+ }
+ int maxW = 0;
+ int maxH = 0;
+ int bitRate = 0;
+ int fps = 0; // frame rate for the max resolution
+ switch(highestLevel) {
+ // Do not support Level 1 to 2.
+ case CodecProfileLevel.AVCLevel1:
+ case CodecProfileLevel.AVCLevel11:
+ case CodecProfileLevel.AVCLevel12:
+ case CodecProfileLevel.AVCLevel13:
+ case CodecProfileLevel.AVCLevel1b:
+ case CodecProfileLevel.AVCLevel2:
+ return false;
+ case CodecProfileLevel.AVCLevel21:
+ maxW = 352;
+ maxH = 576;
+ break;
+ case CodecProfileLevel.AVCLevel22:
+ maxW = 720;
+ maxH = 480;
+ break;
+ case CodecProfileLevel.AVCLevel3:
+ maxW = 720;
+ maxH = 480;
+ break;
+ case CodecProfileLevel.AVCLevel31:
+ maxW = 1280;
+ maxH = 720;
+ break;
+ case CodecProfileLevel.AVCLevel32:
+ maxW = 1280;
+ maxH = 720;
+ break;
+ case CodecProfileLevel.AVCLevel4: // only try up to 1080p
+ default:
+ maxW = 1920;
+ maxH = 1080;
+ break;
+ }
+ if(maxW*maxH < width*height)
+ return false;
+ else
+ return true;
+ }
}
diff --git a/tests/tests/media/src/android/media/cts/JetPlayerTest.java b/tests/tests/media/src/android/media/cts/JetPlayerTest.java
index fc03bcc..4df3555 100644
--- a/tests/tests/media/src/android/media/cts/JetPlayerTest.java
+++ b/tests/tests/media/src/android/media/cts/JetPlayerTest.java
@@ -55,6 +55,10 @@
@Override
protected void tearDown() throws Exception {
+ // Prevent tests from failing with EAS_ERROR_FILE_ALREADY_OPEN
+ // after a previous test fails.
+ mJetPlayer.closeJetFile();
+
File jetFile = new File(mJetFile);
if (jetFile.exists()) {
jetFile.delete();
@@ -63,31 +67,32 @@
}
public void testLoadJetFromPath() throws Throwable {
- mJetPlayer.clearQueue();
+ assertTrue(mJetPlayer.clearQueue());
prepareFile();
mJetPlayer.setEventListener(mOnJetEventListener);
- mJetPlayer.loadJetFile(mJetFile);
+ assertTrue(mJetPlayer.loadJetFile(mJetFile));
runJet();
}
public void testLoadJetFromFd() throws Throwable {
- mJetPlayer.clearQueue();
+ assertTrue(mJetPlayer.clearQueue());
mJetPlayer.setEventListener(mOnJetEventListener, mHandler);
- mJetPlayer.loadJetFile(mContext.getResources().openRawResourceFd(R.raw.test_jet));
+ assertTrue(mJetPlayer.loadJetFile(mContext.getResources().openRawResourceFd(R.raw.test_jet)));
runJet();
}
public void testQueueJetSegmentMuteArray() throws Throwable {
- mJetPlayer.clearQueue();
+ assertTrue(mJetPlayer.clearQueue());
mJetPlayer.setEventListener(mOnJetEventListener, mHandler);
- mJetPlayer.loadJetFile(mContext.getResources().openRawResourceFd(R.raw.test_jet));
+ assertTrue(mJetPlayer.loadJetFile(mContext.getResources().openRawResourceFd(R.raw.test_jet)));
byte userID = 0;
int segmentNum = 3;
int libNum = -1;
int repeatCount = 0;
int transpose = 0;
boolean[] muteFlags = new boolean[32];
- assertTrue(mJetPlayer.queueJetSegmentMuteArray(segmentNum, libNum, repeatCount, transpose,
+ assertTrue(mJetPlayer.queueJetSegmentMuteArray(segmentNum, libNum,
+ repeatCount, transpose,
muteFlags, userID));
assertTrue(mJetPlayer.play());
for (int i = 0; i < muteFlags.length; i++) {
@@ -96,7 +101,8 @@
muteFlags[8] = false;
muteFlags[9] = false;
muteFlags[10] = false;
- assertTrue(mJetPlayer.queueJetSegmentMuteArray(segmentNum, libNum, repeatCount, transpose,
+ assertTrue(mJetPlayer.queueJetSegmentMuteArray(segmentNum, libNum,
+ repeatCount, transpose,
muteFlags, userID));
Thread.sleep(20000);
assertTrue(mJetPlayer.pause());
@@ -112,16 +118,19 @@
int repeatCount = 1;
int transpose = 0;
int muteFlags = 0;
- mJetPlayer.queueJetSegment(segmentNum, libNum, repeatCount, transpose, muteFlags, userID);
+ assertTrue(mJetPlayer.queueJetSegment(segmentNum, libNum, repeatCount,
+ transpose, muteFlags, userID));
segmentNum = 6;
repeatCount = 1;
transpose = -1;
- mJetPlayer.queueJetSegment(segmentNum, libNum, repeatCount, transpose, muteFlags, userID);
+ assertTrue(mJetPlayer.queueJetSegment(segmentNum, libNum, repeatCount,
+ transpose, muteFlags, userID));
segmentNum = 7;
transpose = 0;
- mJetPlayer.queueJetSegment(segmentNum, libNum, repeatCount, transpose, muteFlags, userID);
+ assertTrue(mJetPlayer.queueJetSegment(segmentNum, libNum, repeatCount,
+ transpose, muteFlags, userID));
for (int i = 0; i < 7; i++) {
assertTrue(mJetPlayer.triggerClip(i));
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java b/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
index 813af0f..71cbd61 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
@@ -600,6 +600,29 @@
return actualMax;
}
+ private boolean knownTypes(String type) {
+ return (type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AAC ) ||
+ type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AC3 ) ||
+ type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AMR_NB ) ||
+ type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AMR_WB ) ||
+ type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_EAC3 ) ||
+ type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_FLAC ) ||
+ type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_G711_ALAW) ||
+ type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_G711_MLAW) ||
+ type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_MPEG ) ||
+ type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_MSGSM ) ||
+ type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_OPUS ) ||
+ type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_RAW ) ||
+ type.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_VORBIS ) ||
+ type.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_AVC ) ||
+ type.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_H263 ) ||
+ type.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_HEVC ) ||
+ type.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_MPEG2 ) ||
+ type.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_MPEG4 ) ||
+ type.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_VP8 ) ||
+ type.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_VP9 ));
+ }
+
public void testGetMaxSupportedInstances() {
final int MAX_INSTANCES = 32;
StringBuilder xmlOverrides = new StringBuilder();
@@ -610,6 +633,10 @@
String[] types = info.getSupportedTypes();
for (int j = 0; j < types.length; ++j) {
+ if (!knownTypes(types[j])) {
+ Log.d(TAG, "skipping unknown type " + types[j]);
+ continue;
+ }
Log.d(TAG, "calling getCapabilitiesForType " + types[j]);
CodecCapabilities caps = info.getCapabilitiesForType(types[j]);
int max = caps.getMaxSupportedInstances();
diff --git a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
index 86f0313..75a5a13 100644
--- a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
@@ -21,6 +21,7 @@
import android.content.pm.PackageManager;
import android.content.res.AssetFileDescriptor;
import android.cts.util.MediaUtils;
+import android.hardware.Camera;
import android.media.AudioManager;
import android.media.MediaCodec;
import android.media.MediaDataSource;
@@ -49,6 +50,7 @@
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
+import java.util.List;
import java.util.StringTokenizer;
import java.util.UUID;
import java.util.Vector;
@@ -95,6 +97,49 @@
}
}
+ public void testonInputBufferFilledSigsegv() throws Exception {
+ testIfMediaServerDied(R.raw.on_input_buffer_filled_sigsegv);
+ }
+
+ public void testFlacHeapOverflow() throws Exception {
+ testIfMediaServerDied(R.raw.heap_oob_flac);
+ }
+
+ private void testIfMediaServerDied(int res) throws Exception {
+ mMediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
+ @Override
+ public boolean onError(MediaPlayer mp, int what, int extra) {
+ assertTrue(mp == mMediaPlayer);
+ assertTrue("mediaserver process died", what != MediaPlayer.MEDIA_ERROR_SERVER_DIED);
+ Log.w(LOG_TAG, "onError " + what);
+ return false;
+ }
+ });
+
+ mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
+ @Override
+ public void onCompletion(MediaPlayer mp) {
+ assertTrue(mp == mMediaPlayer);
+ mOnCompletionCalled.signal();
+ }
+ });
+
+ AssetFileDescriptor afd = mResources.openRawResourceFd(res);
+ mMediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
+ afd.close();
+ try {
+ mMediaPlayer.prepare();
+ mMediaPlayer.start();
+ if (!mOnCompletionCalled.waitForSignal(5000)) {
+ Log.w(LOG_TAG, "testIfMediaServerDied: Timed out waiting for Error/Completion");
+ }
+ } catch (Exception e) {
+ Log.w(LOG_TAG, "playback failed", e);
+ } finally {
+ mMediaPlayer.release();
+ }
+ }
+
// Bug 13652927
public void testVorbisCrash() throws Exception {
MediaPlayer mp = mMediaPlayer;
@@ -700,7 +745,7 @@
public void testVideoSurfaceResetting() throws Exception {
final int tolerance = 150;
final int audioLatencyTolerance = 1000; /* covers audio path latency variability */
- final int seekPos = 5000;
+ final int seekPos = 4760; // This is the I-frame position
final CountDownLatch seekDone = new CountDownLatch(1);
@@ -722,7 +767,12 @@
mMediaPlayer.setDisplay(getActivity().getSurfaceHolder2());
int posAfter = mMediaPlayer.getCurrentPosition();
- assertEquals(posAfter, posBefore, tolerance);
+ /* temporarily disable timestamp checking because MediaPlayer now seeks to I-frame
+ * position, instead of requested position. setDisplay invovles a seek operation
+ * internally.
+ */
+ // TODO: uncomment out line below when MediaPlayer can seek to requested position.
+ // assertEquals(posAfter, posBefore, tolerance);
assertTrue(mMediaPlayer.isPlaying());
Thread.sleep(SLEEP_TIME);
@@ -736,7 +786,8 @@
posBefore = mMediaPlayer.getCurrentPosition();
mMediaPlayer.setDisplay(null);
posAfter = mMediaPlayer.getCurrentPosition();
- assertEquals(posAfter, posBefore, tolerance);
+ // TODO: uncomment out line below when MediaPlayer can seek to requested position.
+ // assertEquals(posAfter, posBefore, tolerance);
assertTrue(mMediaPlayer.isPlaying());
Thread.sleep(SLEEP_TIME);
@@ -745,7 +796,8 @@
mMediaPlayer.setDisplay(getActivity().getSurfaceHolder());
posAfter = mMediaPlayer.getCurrentPosition();
- assertEquals(posAfter, posBefore, tolerance);
+ // TODO: uncomment out line below when MediaPlayer can seek to requested position.
+ // assertEquals(posAfter, posBefore, tolerance);
assertTrue(mMediaPlayer.isPlaying());
Thread.sleep(SLEEP_TIME);
@@ -771,15 +823,34 @@
return getActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA);
}
+ private Camera mCamera;
private void testRecordedVideoPlaybackWithAngle(int angle) throws Exception {
- final int width = RECORDED_VIDEO_WIDTH;
- final int height = RECORDED_VIDEO_HEIGHT;
+ int width = RECORDED_VIDEO_WIDTH;
+ int height = RECORDED_VIDEO_HEIGHT;
final String file = RECORDED_FILE;
final long durationMs = RECORDED_DURATION_MS;
if (!hasCamera()) {
return;
}
+
+ boolean isSupported = false;
+ mCamera = Camera.open(0);
+ Camera.Parameters parameters = mCamera.getParameters();
+ List<Camera.Size> previewSizes = parameters.getSupportedPreviewSizes();
+ for (Camera.Size size : previewSizes)
+ {
+ if (size.width == width && size.height == height) {
+ isSupported = true;
+ break;
+ }
+ }
+ mCamera.release();
+ mCamera = null;
+ if (!isSupported) {
+ width = previewSizes.get(0).width;
+ height = previewSizes.get(0).height;
+ }
checkOrientation(angle);
recordVideo(width, height, angle, file, durationMs);
checkDisplayedVideoSize(width, height, angle, file);
@@ -1403,6 +1474,42 @@
return 1;
}
+ public void testPositionAtEnd() throws Throwable {
+ testPositionAtEnd(R.raw.test1m1shighstereo);
+ testPositionAtEnd(R.raw.loudsoftmp3);
+ testPositionAtEnd(R.raw.loudsoftmp3);
+ testPositionAtEnd(R.raw.loudsoftwav);
+ testPositionAtEnd(R.raw.loudsoftogg);
+ testPositionAtEnd(R.raw.loudsoftitunes);
+ testPositionAtEnd(R.raw.loudsoftfaac);
+ testPositionAtEnd(R.raw.loudsoftaac);
+ }
+
+ private void testPositionAtEnd(int res) throws Throwable {
+
+ loadResource(res);
+ mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+ mMediaPlayer.prepare();
+ int duration = mMediaPlayer.getDuration();
+ assertTrue("resource too short", duration > 6000);
+ mOnCompletionCalled.reset();
+ mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
+ @Override
+ public void onCompletion(MediaPlayer mp) {
+ mOnCompletionCalled.signal();
+ }
+ });
+ mMediaPlayer.seekTo(duration - 5000);
+ mMediaPlayer.start();
+ while (mMediaPlayer.isPlaying()) {
+ Log.i("@@@@", "position: " + mMediaPlayer.getCurrentPosition());
+ Thread.sleep(500);
+ }
+ Log.i("@@@@", "final position: " + mMediaPlayer.getCurrentPosition());
+ assertTrue(mMediaPlayer.getCurrentPosition() > duration - 1000);
+ mMediaPlayer.reset();
+ }
+
public void testCallback() throws Throwable {
final int mp4Duration = 8484;
diff --git a/tests/tests/media/src/android/media/cts/MediaRecorderTest.java b/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
index f7b6c91..4c90e56 100644
--- a/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
@@ -60,6 +60,8 @@
private static final int RECORDED_DUR_TOLERANCE_MS = 1000;
private static final int VIDEO_WIDTH = 176;
private static final int VIDEO_HEIGHT = 144;
+ private static int mVideoWidth = VIDEO_WIDTH;
+ private static int mVideoHeight = VIDEO_HEIGHT;
private static final int VIDEO_BIT_RATE_IN_BPS = 128000;
private static final double VIDEO_TIMELAPSE_CAPTURE_RATE_FPS = 1.0;
private static final int AUDIO_BIT_RATE_IN_BPS = 12200;
@@ -219,6 +221,7 @@
int durMs = timelapse? RECORD_TIME_LAPSE_MS: RECORD_TIME_MS;
for (int cameraId = 0; cameraId < nCamera; cameraId++) {
mCamera = Camera.open(cameraId);
+ setSupportedResolution(mCamera);
recordVideoUsingCamera(mCamera, OUTPUT_PATH, durMs, timelapse);
mCamera.release();
mCamera = null;
@@ -226,6 +229,21 @@
}
}
+ private void setSupportedResolution(Camera camera) {
+ Camera.Parameters parameters = camera.getParameters();
+ List<Camera.Size> previewSizes = parameters.getSupportedPreviewSizes();
+ for (Camera.Size size : previewSizes)
+ {
+ if (size.width == VIDEO_WIDTH && size.height == VIDEO_HEIGHT) {
+ mVideoWidth = VIDEO_WIDTH;
+ mVideoHeight = VIDEO_HEIGHT;
+ return;
+ }
+ }
+ mVideoWidth = previewSizes.get(0).width;
+ mVideoHeight = previewSizes.get(0).height;
+ }
+
private void recordVideoUsingCamera(
Camera camera, String fileName, int durMs, boolean timelapse) throws Exception {
// FIXME:
@@ -242,7 +260,7 @@
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
mMediaRecorder.setVideoFrameRate(frameRate);
- mMediaRecorder.setVideoSize(VIDEO_WIDTH, VIDEO_HEIGHT);
+ mMediaRecorder.setVideoSize(mVideoWidth, mVideoHeight);
mMediaRecorder.setPreviewDisplay(mActivity.getSurfaceHolder().getSurface());
mMediaRecorder.setOutputFile(fileName);
mMediaRecorder.setLocation(LATITUDE, LONGITUDE);
@@ -872,6 +890,12 @@
boolean success = false;
Surface surface = null;
int noOfFailure = 0;
+
+ if (!hasH264()) {
+ MediaUtils.skipTest("no codecs");
+ return true;
+ }
+
try {
if (persistent) {
surface = MediaCodec.createPersistentInputSurface();
diff --git a/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java b/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java
index 7497da2..ce61d76 100644
--- a/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java
+++ b/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java
@@ -312,6 +312,11 @@
}
public void testPlayHlsStreamWithTimedId3() throws Throwable {
+ if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
+ Log.d(TAG, "Device doesn't have video codec, skipping test");
+ return;
+ }
+
mServer = new CtsTestServer(mContext);
try {
// counter must be final if we want to access it inside onTimedMetaData;
diff --git a/tests/tests/media/src/android/media/cts/VideoEncoderTest.java b/tests/tests/media/src/android/media/cts/VideoEncoderTest.java
index e3d2f09..fb1521d 100644
--- a/tests/tests/media/src/android/media/cts/VideoEncoderTest.java
+++ b/tests/tests/media/src/android/media/cts/VideoEncoderTest.java
@@ -174,6 +174,7 @@
private boolean mSignaledDecoderEOS;
protected boolean mCompleted;
+ protected boolean mEncoderIsActive;
protected boolean mEncodeOutputFormatUpdated;
protected final Object mCondition = new Object();
@@ -322,6 +323,10 @@
mCompleted = true;
mCondition.notifyAll(); // condition is always satisfied
}
+ } else {
+ synchronized(mCondition) {
+ mEncoderIsActive = true;
+ }
}
}
}
@@ -395,6 +400,11 @@
break;
}
if (!haveBuffers()) {
+ if (mEncoderIsActive) {
+ mEncoderIsActive = false;
+ Log.d(TAG, "No more input but still getting output from encoder.");
+ continue;
+ }
fail("timed out after " + mBuffersToRender.size()
+ " decoder output and " + mEncInputBuffers.size()
+ " encoder input buffers");
@@ -557,7 +567,6 @@
implements SurfaceTexture.OnFrameAvailableListener {
private static final String TAG = "SurfaceVideoProcessor";
private boolean mFrameAvailable;
- private boolean mEncoderIsActive;
private boolean mGotDecoderEOS;
private boolean mSignaledEncoderEOS;
diff --git a/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
index 88dbd7c..9a99c22 100644
--- a/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -16,6 +16,9 @@
package android.net.cts;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -23,6 +26,7 @@
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkConfig;
@@ -467,4 +471,23 @@
mAvailableLatch.countDown();
}
}
+
+ /** Verify restricted networks cannot be requested. */
+ public void testRestrictedNetworks() {
+ // Verify we can request unrestricted networks:
+ NetworkRequest request = new NetworkRequest.Builder()
+ .addCapability(NET_CAPABILITY_INTERNET).build();
+ NetworkCallback callback = new NetworkCallback();
+ mCm.requestNetwork(request, callback);
+ mCm.unregisterNetworkCallback(callback);
+ // Verify we cannot request restricted networks:
+ request = new NetworkRequest.Builder().addCapability(NET_CAPABILITY_IMS).build();
+ callback = new NetworkCallback();
+ try {
+ mCm.requestNetwork(request, callback);
+ fail("No exception thrown when restricted network requested.");
+ } catch (SecurityException e) {
+ // Expected.
+ }
+ }
}
diff --git a/tests/tests/os/jni/seccomp_sample_program.cpp b/tests/tests/os/jni/seccomp_sample_program.cpp
index 3c90196..3bc7da4 100644
--- a/tests/tests/os/jni/seccomp_sample_program.cpp
+++ b/tests/tests/os/jni/seccomp_sample_program.cpp
@@ -826,7 +826,7 @@
{0x35, 0, 4, 0x76},
{0x35, 0, 2, 0x79},
{0x35, 0, 241, 0x7a},
- {0x35, 240, 239, 0x7b},
+ {0x35, 240, 239, 0x7c},
{0x35, 238, 239, 0x77},
{0x35, 0, 2, 0x72},
{0x35, 0, 236, 0x73},
diff --git a/tests/tests/os/src/android/os/cts/BuildVersionTest.java b/tests/tests/os/src/android/os/cts/BuildVersionTest.java
index 419f320..af60139 100644
--- a/tests/tests/os/src/android/os/cts/BuildVersionTest.java
+++ b/tests/tests/os/src/android/os/cts/BuildVersionTest.java
@@ -29,8 +29,8 @@
private static final String LOG_TAG = "BuildVersionTest";
private static final Set<String> EXPECTED_RELEASES =
- new HashSet<String>(Arrays.asList("5.1", "5.1.1"));
- private static final int EXPECTED_SDK = 22;
+ new HashSet<String>(Arrays.asList("6.0"));
+ private static final int EXPECTED_SDK = 23;
private static final String EXPECTED_BUILD_VARIANT = "user";
private static final String EXPECTED_TAG = "release-keys";
diff --git a/tests/tests/os/src/android/os/cts/CpuFeaturesTest.java b/tests/tests/os/src/android/os/cts/CpuFeaturesTest.java
index 0b389a4..d8e128b 100644
--- a/tests/tests/os/src/android/os/cts/CpuFeaturesTest.java
+++ b/tests/tests/os/src/android/os/cts/CpuFeaturesTest.java
@@ -82,8 +82,8 @@
}
private static final String[] armv8RequiredFeatures = {
- "wp", "half", "thumb", "fastmult", "vfp", "edsp", "neon",
- "vfpv3", "tlsi", "vfpv4", "idiva", "idivt" };
+ "half", "thumb", "fastmult", "vfp", "edsp", "neon",
+ "vfpv3", "vfpv4", "idiva", "idivt" };
private static void assertInCpuinfo(List<String> features,
String feature) {
diff --git a/tests/tests/permission/src/android/permission/cts/ConnectivityManagerPermissionTest.java b/tests/tests/permission/src/android/permission/cts/ConnectivityManagerPermissionTest.java
index 8714100..a95c96e 100644
--- a/tests/tests/permission/src/android/permission/cts/ConnectivityManagerPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/ConnectivityManagerPermissionTest.java
@@ -53,21 +53,4 @@
// expected
}
}
-
- /**
- * Verify that calling {@link ConnectivityManager#requestRouteToHost(int, int)}
- * requires permissions.
- * <p>Tests Permission:
- * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
- */
- @SmallTest
- public void testRequestRouteToHost() {
- try {
- mConnectivityManager.requestRouteToHost(ConnectivityManager.TYPE_MOBILE, 1);
- fail("Was able to call requestRouteToHost");
- } catch (SecurityException e) {
- // expected
- }
- }
}
-
diff --git a/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java b/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
index cf6a09d..0962bbd 100644
--- a/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
@@ -251,7 +251,6 @@
@MediumTest
public void testProcSelfOomAdjSane() {
File f = new File("/proc/self/oom_adj");
- assertTrue(f.canRead());
assertFalse(f.canWrite());
assertFalse(f.canExecute());
}
@@ -259,7 +258,6 @@
@MediumTest
public void testProcSelfOomScoreAdjSane() {
File f = new File("/proc/self/oom_score_adj");
- assertTrue(f.canRead());
assertFalse(f.canWrite());
assertFalse(f.canExecute());
}
diff --git a/tests/tests/permission/src/android/permission/cts/NoNetworkStatePermissionTest.java b/tests/tests/permission/src/android/permission/cts/NoNetworkStatePermissionTest.java
index 0eae9cc..35ebdc1 100644
--- a/tests/tests/permission/src/android/permission/cts/NoNetworkStatePermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/NoNetworkStatePermissionTest.java
@@ -86,38 +86,6 @@
}
}
- /**
- * Verify that ConnectivityManager#startUsingNetworkFeature() requires permissions.
- * <p>Requires Permission:
- * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
- */
- @SmallTest
- public void testStartUsingNetworkFeature() {
- try {
- mConnectivityManager.startUsingNetworkFeature(TEST_NETWORK_TYPE, TEST_FEATURE);
- fail("ConnectivityManager.startUsingNetworkFeature didn't throw SecurityException as"
- + " expected");
- } catch (SecurityException e) {
- // expected
- }
- }
-
- /**
- * Verify that ConnectivityManager#requestRouteToHost() requires permissions.
- * <p>Requires Permission:
- * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
- */
- @SmallTest
- public void testRequestRouteToHost() {
- try {
- mConnectivityManager.requestRouteToHost(TEST_NETWORK_TYPE, 0xffffffff);
- fail("ConnectivityManager.requestRouteToHost didn't throw SecurityException as"
- + " expected");
- } catch (SecurityException e) {
- // expected
- }
- }
-
@SmallTest
public void testSecurityExceptionFromDns() throws Exception {
try {
diff --git a/tests/tests/permission2/src/android/permission2/cts/ProtectedBroadcastsTest.java b/tests/tests/permission2/src/android/permission2/cts/ProtectedBroadcastsTest.java
index f7e5443..c260706 100644
--- a/tests/tests/permission2/src/android/permission2/cts/ProtectedBroadcastsTest.java
+++ b/tests/tests/permission2/src/android/permission2/cts/ProtectedBroadcastsTest.java
@@ -98,4 +98,4 @@
}
}
}
-}
+}
\ No newline at end of file
diff --git a/tests/tests/print/src/android/print/cts/BasePrintTest.java b/tests/tests/print/src/android/print/cts/BasePrintTest.java
index e75ec94..1378bdb 100644
--- a/tests/tests/print/src/android/print/cts/BasePrintTest.java
+++ b/tests/tests/print/src/android/print/cts/BasePrintTest.java
@@ -140,6 +140,11 @@
@Override
public void setUp() throws Exception {
+ super.setUp();
+ if (!supportsPrinting()) {
+ return;
+ }
+
// Make sure we start with a clean slate.
clearPrintSpoolerData();
enablePrintServices();
@@ -176,23 +181,26 @@
@Override
public void tearDown() throws Exception {
- // Done with the activity.
- getActivity().finish();
- enableImes();
+ if (supportsPrinting()) {
+ // Done with the activity.
+ getActivity().finish();
+ enableImes();
- // Restore the locale if needed.
- if (mOldLocale != null) {
- Resources resources = getInstrumentation().getTargetContext().getResources();
- DisplayMetrics displayMetrics = resources.getDisplayMetrics();
- Configuration newConfiguration = new Configuration(resources.getConfiguration());
- newConfiguration.locale = mOldLocale;
- mOldLocale = null;
- resources.updateConfiguration(newConfiguration, displayMetrics);
+ // Restore the locale if needed.
+ if (mOldLocale != null) {
+ Resources resources = getInstrumentation().getTargetContext().getResources();
+ DisplayMetrics displayMetrics = resources.getDisplayMetrics();
+ Configuration newConfiguration = new Configuration(resources.getConfiguration());
+ newConfiguration.locale = mOldLocale;
+ mOldLocale = null;
+ resources.updateConfiguration(newConfiguration, displayMetrics);
+ }
+
+ disablePrintServices();
+ // Make sure the spooler is cleaned.
+ clearPrintSpoolerData();
}
-
- disablePrintServices();
- // Make sure the spooler is cleaned.
- clearPrintSpoolerData();
+ super.tearDown();
}
protected void print(final PrintDocumentAdapter adapter) {
diff --git a/tests/tests/security/AndroidManifest.xml b/tests/tests/security/AndroidManifest.xml
index 25bc6ac..d8d8dfa 100644
--- a/tests/tests/security/AndroidManifest.xml
+++ b/tests/tests/security/AndroidManifest.xml
@@ -22,6 +22,7 @@
<uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<application>
<uses-library android:name="android.test.runner" />
diff --git a/tests/tests/security/jni/Android.mk b/tests/tests/security/jni/Android.mk
index 2d55fb6..327eefc 100644
--- a/tests/tests/security/jni/Android.mk
+++ b/tests/tests/security/jni/Android.mk
@@ -31,11 +31,15 @@
android_security_cts_SELinuxTest.cpp \
android_security_cts_MMapExecutableTest.cpp \
android_security_cts_AudioPolicyBinderTest.cpp \
- android_security_cts_EncryptionTest.cpp
+ android_security_cts_EncryptionTest.cpp \
+ android_security_cts_MediaPlayerInfoLeakTest.cpp \
+ android_security_cts_AudioEffectBinderTest.cpp \
+ android_security_cts_AudioFlingerBinderTest.cpp \
+ android_security_cts_StagefrightFoundationTest.cpp
LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
-LOCAL_SHARED_LIBRARIES := libnativehelper liblog libbinder libutils libmedia libselinux libdl libcutils libcrypto
+LOCAL_SHARED_LIBRARIES := libnativehelper liblog libbinder libutils libmedia libselinux libdl libcutils libcrypto libstagefright_foundation
LOCAL_C_INCLUDES += ndk/sources/cpufeatures
LOCAL_STATIC_LIBRARIES := cpufeatures
diff --git a/tests/tests/security/jni/CtsSecurityJniOnLoad.cpp b/tests/tests/security/jni/CtsSecurityJniOnLoad.cpp
index c60b866..b09821c 100644
--- a/tests/tests/security/jni/CtsSecurityJniOnLoad.cpp
+++ b/tests/tests/security/jni/CtsSecurityJniOnLoad.cpp
@@ -25,7 +25,11 @@
extern int register_android_security_cts_SELinuxTest(JNIEnv*);
extern int register_android_security_cts_MMapExecutableTest(JNIEnv* env);
extern int register_android_security_cts_AudioPolicyBinderTest(JNIEnv* env);
+extern int register_android_security_cts_AudioFlingerBinderTest(JNIEnv* env);
extern int register_android_security_cts_EncryptionTest(JNIEnv* env);
+extern int register_android_security_cts_MediaPlayerInfoLeakTest(JNIEnv* env);
+extern int register_android_security_cts_AudioEffectBinderTest(JNIEnv* env);
+extern int register_android_security_cts_StagefrightFoundationTest(JNIEnv* env);
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = NULL;
@@ -70,5 +74,21 @@
return JNI_ERR;
}
+ if (register_android_security_cts_MediaPlayerInfoLeakTest(env)) {
+ return JNI_ERR;
+ }
+
+ if (register_android_security_cts_AudioEffectBinderTest(env)) {
+ return JNI_ERR;
+ }
+
+ if (register_android_security_cts_AudioFlingerBinderTest(env)) {
+ return JNI_ERR;
+ }
+
+ if (register_android_security_cts_StagefrightFoundationTest(env)) {
+ return JNI_ERR;
+ }
+
return JNI_VERSION_1_4;
}
diff --git a/tests/tests/security/jni/android_security_cts_AudioEffectBinderTest.cpp b/tests/tests/security/jni/android_security_cts_AudioEffectBinderTest.cpp
new file mode 100644
index 0000000..4c27416
--- /dev/null
+++ b/tests/tests/security/jni/android_security_cts_AudioEffectBinderTest.cpp
@@ -0,0 +1,161 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "AudioEffectBinderTest-JNI"
+
+#include <jni.h>
+#include <media/AudioEffect.h>
+#include <media/IEffect.h>
+
+using namespace android;
+
+/*
+ * Native methods used by
+ * cts/tests/tests/security/src/android/security/cts/AudioEffectBinderTest.java
+ */
+
+struct EffectClient : public BnEffectClient {
+ EffectClient() { }
+ virtual void controlStatusChanged(bool controlGranted __unused) { }
+ virtual void enableStatusChanged(bool enabled __unused) { }
+ virtual void commandExecuted(uint32_t cmdCode __unused,
+ uint32_t cmdSize __unused,
+ void *pCmdData __unused,
+ uint32_t replySize __unused,
+ void *pReplyData __unused) { }
+};
+
+struct DeathRecipient : public IBinder::DeathRecipient {
+ DeathRecipient() : mDied(false) { }
+ virtual void binderDied(const wp<IBinder>& who __unused) { mDied = true; }
+ bool died() const { return mDied; }
+ bool mDied;
+};
+
+static bool isIEffectCommandSecure(IEffect *effect)
+{
+ // some magic constants here
+ const int COMMAND_SIZE = 1024 + 12; // different than reply size to get different heap frag
+ char cmdData[COMMAND_SIZE];
+ memset(cmdData, 0xde, sizeof(cmdData));
+
+ const int REPLY_DATA_SIZE = 256;
+ char replyData[REPLY_DATA_SIZE];
+ bool secure = true;
+ for (int k = 0; k < 10; ++k) {
+ Parcel data;
+ data.writeInterfaceToken(effect->getInterfaceDescriptor());
+ data.writeInt32(0); // 0 is EFFECT_CMD_INIT
+ data.writeInt32(sizeof(cmdData));
+ data.write(cmdData, sizeof(cmdData));
+ data.writeInt32(sizeof(replyData));
+
+ Parcel reply;
+ status_t status = effect->asBinder(effect)->transact(3, data, &reply); // 3 is COMMAND
+ ALOGV("transact status: %d", status);
+ if (status != NO_ERROR) {
+ ALOGW("invalid transaction status %d", status);
+ continue;
+ }
+
+ ALOGV("reply data avail %zu", reply.dataAvail());
+ status = reply.readInt32();
+ ALOGV("reply status %d", status);
+ if (status == NO_ERROR) {
+ continue;
+ }
+
+ int size = reply.readInt32();
+ ALOGV("reply size %d", size);
+ if (size != sizeof(replyData)) { // we expect 0 or full reply data if command failed
+ ALOGW_IF(size != 0, "invalid reply size: %d", size);
+ continue;
+ }
+
+ // Note that if reply.read() returns success, it should completely fill replyData.
+ status = reply.read(replyData, sizeof(replyData));
+ if (status != NO_ERROR) {
+ ALOGW("invalid reply read - ignoring");
+ continue;
+ }
+ unsigned int *out = (unsigned int *)replyData;
+ for (size_t index = 0; index < sizeof(replyData) / sizeof(*out); ++index) {
+ if (out[index] != 0) {
+ secure = false;
+ ALOGI("leaked data = %#08x", out[index]);
+ }
+ }
+ }
+ ALOGI("secure: %s", secure ? "YES" : "NO");
+ return secure;
+}
+
+static jboolean android_security_cts_AudioEffect_test_isCommandSecure()
+{
+ const sp<IAudioFlinger> &audioFlinger = AudioSystem::get_audio_flinger();
+ if (audioFlinger.get() == NULL) {
+ ALOGE("could not get audioflinger");
+ return JNI_FALSE;
+ }
+
+ static const effect_uuid_t EFFECT_UIID_EQUALIZER = // type
+ { 0x0bed4300, 0xddd6, 0x11db, 0x8f34, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b }};
+ sp<EffectClient> effectClient(new EffectClient());
+ effect_descriptor_t descriptor;
+ memset(&descriptor, 0, sizeof(descriptor));
+ descriptor.type = EFFECT_UIID_EQUALIZER;
+ descriptor.uuid = *EFFECT_UUID_NULL;
+ const int32_t priority = 0;
+ const int sessionId = AUDIO_SESSION_OUTPUT_MIX;
+ const audio_io_handle_t io = AUDIO_IO_HANDLE_NONE;
+ const String16 opPackageName("Exploitable");
+ status_t status;
+ int32_t id;
+ int enabled;
+ sp<IEffect> effect = audioFlinger->createEffect(&descriptor, effectClient,
+ priority, io, sessionId, opPackageName, &status, &id, &enabled);
+ if (effect.get() == NULL || status != NO_ERROR) {
+ ALOGW("could not create effect");
+ return JNI_TRUE;
+ }
+
+ sp<DeathRecipient> deathRecipient(new DeathRecipient());
+ IInterface::asBinder(effect)->linkToDeath(deathRecipient);
+
+ // check exploit
+ if (!isIEffectCommandSecure(effect.get())) {
+ ALOGE("not secure!");
+ return JNI_FALSE;
+ }
+
+ sleep(1); // wait to check death
+ if (deathRecipient->died()) {
+ ALOGE("effect binder died");
+ return JNI_FALSE;
+ }
+ return JNI_TRUE;
+}
+
+int register_android_security_cts_AudioEffectBinderTest(JNIEnv *env)
+{
+ static JNINativeMethod methods[] = {
+ { "native_test_isCommandSecure", "()Z",
+ (void *) android_security_cts_AudioEffect_test_isCommandSecure },
+ };
+
+ jclass clazz = env->FindClass("android/security/cts/AudioEffectBinderTest");
+ return env->RegisterNatives(clazz, methods, sizeof(methods) / sizeof(methods[0]));
+}
diff --git a/tests/tests/security/jni/android_security_cts_AudioFlingerBinderTest.cpp b/tests/tests/security/jni/android_security_cts_AudioFlingerBinderTest.cpp
new file mode 100644
index 0000000..fb80d6b
--- /dev/null
+++ b/tests/tests/security/jni/android_security_cts_AudioFlingerBinderTest.cpp
@@ -0,0 +1,310 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "AudioFlingerBinderTest-JNI"
+
+#include <jni.h>
+#include <binder/IServiceManager.h>
+#include <media/IAudioFlinger.h>
+#include <media/AudioSystem.h>
+#include <system/audio.h>
+#include <utils/Log.h>
+#include <utils/SystemClock.h>
+
+using namespace android;
+
+/*
+ * Native methods used by
+ * cts/tests/tests/security/src/android/security/cts/AudioFlingerBinderTest.java
+ */
+
+#define TEST_ARRAY_SIZE 10000
+#define MAX_ARRAY_SIZE 1024
+#define TEST_PATTERN 0x55
+
+class MyDeathClient: public IBinder::DeathRecipient
+{
+public:
+ MyDeathClient() :
+ mAfIsDead(false) {
+ }
+
+ bool afIsDead() const { return mAfIsDead; }
+
+ // DeathRecipient
+ virtual void binderDied(const wp<IBinder>& who __unused) { mAfIsDead = true; }
+
+private:
+ bool mAfIsDead;
+};
+
+
+static bool connectAudioFlinger(sp<IAudioFlinger>& af, sp<MyDeathClient> &dr)
+{
+ int64_t startTime = 0;
+ while (af == 0) {
+ sp<IBinder> binder = defaultServiceManager()->checkService(String16("media.audio_flinger"));
+ if (binder == 0) {
+ if (startTime == 0) {
+ startTime = uptimeMillis();
+ } else if ((uptimeMillis()-startTime) > 10000) {
+ ALOGE("timeout while getting audio flinger service");
+ return false;
+ }
+ sleep(1);
+ } else {
+ af = interface_cast<IAudioFlinger>(binder);
+ dr = new MyDeathClient();
+ binder->linkToDeath(dr);
+ }
+ }
+ return true;
+}
+
+/*
+ * Checks that AudioSystem::setMasterMute() does not crash mediaserver if a duplicated output
+ * is opened.
+ */
+jboolean android_security_cts_AudioFlinger_test_setMasterMute(JNIEnv* env __unused,
+ jobject thiz __unused)
+{
+ sp<IAudioFlinger> af;
+ sp<MyDeathClient> dr;
+
+ if (!connectAudioFlinger(af, dr)) {
+ return false;
+ }
+
+ // force opening of a duplicating output
+ status_t status = AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_REMOTE_SUBMIX,
+ AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
+ "0", "");
+ if (status != NO_ERROR) {
+ return false;
+ }
+
+ bool mute;
+ status = AudioSystem::getMasterMute(&mute);
+ if (status != NO_ERROR) {
+ return false;
+ }
+
+ AudioSystem::setMasterMute(!mute);
+
+ sleep(1);
+
+ // Check that mediaserver did not crash
+ if (dr->afIsDead()) {
+ return false;
+ }
+
+ AudioSystem::setMasterMute(mute);
+
+ AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_REMOTE_SUBMIX,
+ AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
+ "0", "");
+
+ AudioSystem::setMasterMute(false);
+
+ return true;
+}
+
+jboolean android_security_cts_AudioFlinger_test_setMasterVolume(JNIEnv* env __unused,
+ jobject thiz __unused)
+{
+ sp<IAudioFlinger> af;
+ sp<MyDeathClient> dr;
+
+ if (!connectAudioFlinger(af, dr)) {
+ return false;
+ }
+
+ // force opening of a duplicating output
+ status_t status = AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_REMOTE_SUBMIX,
+ AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
+ "0", "");
+ if (status != NO_ERROR) {
+ return false;
+ }
+
+ float vol;
+ status = AudioSystem::getMasterVolume(&vol);
+ if (status != NO_ERROR) {
+ return false;
+ }
+
+ AudioSystem::setMasterVolume(vol < 0.5 ? 1.0 : 0.0);
+
+ sleep(1);
+
+ // Check that mediaserver did not crash
+ if (dr->afIsDead()) {
+ return false;
+ }
+
+ AudioSystem::setMasterMute(vol);
+
+ AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_REMOTE_SUBMIX,
+ AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
+ "0", "");
+
+ return true;
+}
+
+jboolean android_security_cts_AudioFlinger_test_listAudioPorts(JNIEnv* env __unused,
+ jobject thiz __unused)
+{
+ sp<IAudioFlinger> af;
+ sp<MyDeathClient> dr;
+
+ if (!connectAudioFlinger(af, dr)) {
+ return false;
+ }
+
+ unsigned int num_ports = TEST_ARRAY_SIZE;
+ struct audio_port *ports =
+ (struct audio_port *)calloc(TEST_ARRAY_SIZE, sizeof(struct audio_port));
+
+ memset(ports, TEST_PATTERN, TEST_ARRAY_SIZE * sizeof(struct audio_port));
+
+ status_t status = af->listAudioPorts(&num_ports, ports);
+
+ sleep(1);
+
+ // Check that the memory content above the max allowed array size was not changed
+ char *ptr = (char *)(ports + MAX_ARRAY_SIZE);
+ for (size_t i = 0; i < TEST_ARRAY_SIZE - MAX_ARRAY_SIZE; i++) {
+ if (ptr[i * sizeof(struct audio_port)] != TEST_PATTERN) {
+ free(ports);
+ return false;
+ }
+ }
+
+ free(ports);
+
+ // Check that mediaserver did not crash
+ if (dr->afIsDead()) {
+ return false;
+ }
+
+ return true;
+}
+
+jboolean android_security_cts_AudioFlinger_test_listAudioPatches(JNIEnv* env __unused,
+ jobject thiz __unused)
+{
+ sp<IAudioFlinger> af;
+ sp<MyDeathClient> dr;
+
+ if (!connectAudioFlinger(af, dr)) {
+ return false;
+ }
+
+ unsigned int num_patches = TEST_ARRAY_SIZE;
+ struct audio_patch *patches =
+ (struct audio_patch *)calloc(TEST_ARRAY_SIZE, sizeof(struct audio_patch));
+
+ memset(patches, TEST_PATTERN, TEST_ARRAY_SIZE * sizeof(struct audio_patch));
+
+ status_t status = af->listAudioPatches(&num_patches, patches);
+
+ sleep(1);
+
+ // Check that the memory content above the max allowed array size was not changed
+ char *ptr = (char *)(patches + MAX_ARRAY_SIZE);
+ for (size_t i = 0; i < TEST_ARRAY_SIZE - MAX_ARRAY_SIZE; i++) {
+ if (ptr[i * sizeof(struct audio_patch)] != TEST_PATTERN) {
+ free(patches);
+ return false;
+ }
+ }
+
+ free(patches);
+
+ // Check that mediaserver did not crash
+ if (dr->afIsDead()) {
+ return false;
+ }
+
+ return true;
+}
+
+jboolean android_security_cts_AudioFlinger_test_createEffect(JNIEnv* env __unused,
+ jobject thiz __unused)
+{
+ sp<IAudioFlinger> af;
+ sp<MyDeathClient> dr;
+
+ if (!connectAudioFlinger(af, dr)) {
+ return false;
+ }
+
+ for (int j = 0; j < 10; ++j) {
+ Parcel data, reply;
+ data.writeInterfaceToken(af->getInterfaceDescriptor());
+ data.writeInt32((int32_t)j);
+ status_t status = af->asBinder(af)->transact(40, data, &reply); // 40 is CREATE_EFFECT
+ if (status != NO_ERROR) {
+ return false;
+ }
+
+ status = (status_t)reply.readInt32();
+ if (status == NO_ERROR) {
+ continue;
+ }
+
+ int id = reply.readInt32();
+ int enabled = reply.readInt32();
+ sp<IEffect> effect = interface_cast<IEffect>(reply.readStrongBinder());
+ effect_descriptor_t desc;
+ effect_descriptor_t descTarget;
+ memset(&desc, 0, sizeof(effect_descriptor_t));
+ memset(&descTarget, 0, sizeof(effect_descriptor_t));
+ reply.read(&desc, sizeof(effect_descriptor_t));
+ if (id != 0 || enabled != 0 || memcmp(&desc, &descTarget, sizeof(effect_descriptor_t))) {
+ return false;
+ }
+ }
+
+ sleep(1);
+
+ // Check that mediaserver did not crash
+ if (dr->afIsDead()) {
+ return false;
+ }
+
+ return true;
+}
+
+static JNINativeMethod gMethods[] = {
+ { "native_test_setMasterMute", "()Z",
+ (void *) android_security_cts_AudioFlinger_test_setMasterMute },
+ { "native_test_setMasterVolume", "()Z",
+ (void *) android_security_cts_AudioFlinger_test_setMasterVolume },
+ { "native_test_listAudioPorts", "()Z",
+ (void *) android_security_cts_AudioFlinger_test_listAudioPorts },
+ { "native_test_listAudioPatches", "()Z",
+ (void *) android_security_cts_AudioFlinger_test_listAudioPatches },
+ { "native_test_createEffect", "()Z",
+ (void *) android_security_cts_AudioFlinger_test_createEffect },
+};
+
+int register_android_security_cts_AudioFlingerBinderTest(JNIEnv* env)
+{
+ jclass clazz = env->FindClass("android/security/cts/AudioFlingerBinderTest");
+ return env->RegisterNatives(clazz, gMethods,
+ sizeof(gMethods) / sizeof(JNINativeMethod));
+}
diff --git a/tests/tests/security/jni/android_security_cts_AudioPolicyBinderTest.cpp b/tests/tests/security/jni/android_security_cts_AudioPolicyBinderTest.cpp
index 6807523..6011920 100644
--- a/tests/tests/security/jni/android_security_cts_AudioPolicyBinderTest.cpp
+++ b/tests/tests/security/jni/android_security_cts_AudioPolicyBinderTest.cpp
@@ -18,6 +18,7 @@
#include <jni.h>
#include <binder/IServiceManager.h>
+#include <binder/Parcel.h>
#include <media/IAudioPolicyService.h>
#include <media/AudioSystem.h>
#include <system/audio.h>
@@ -151,12 +152,12 @@
return false;
}
- status_t status = aps->isStreamActive((audio_stream_type_t)(-1), 0);
- if (status == NO_ERROR) {
+ bool status = aps->isStreamActive((audio_stream_type_t)(-1), 0);
+ if (status) {
return false;
}
status = aps->isStreamActive((audio_stream_type_t)AUDIO_STREAM_CNT, 0);
- if (status == NO_ERROR) {
+ if (status) {
return false;
}
return true;
@@ -186,6 +187,64 @@
return true;
}
+jint android_security_cts_AudioPolicy_test_getStreamVolumeLeak(JNIEnv* env __unused,
+ jobject thiz __unused)
+{
+ sp<IAudioPolicyService> aps;
+
+ if (!init(aps, NULL, NULL)) {
+ return -1;
+ }
+
+ // Keep synchronized with IAudioPolicyService.cpp!
+ enum {
+ GET_STREAM_VOLUME = 17,
+ };
+
+ Parcel data, reply;
+ status_t err;
+ data.writeInterfaceToken(aps->getInterfaceDescriptor());
+ data.writeInt32(-1); // stream type
+ data.writeInt32(-1); // device
+ IInterface::asBinder(aps)->transact(GET_STREAM_VOLUME, data, &reply);
+ int index = reply.readInt32();
+ err = reply.readInt32();
+
+ return index;
+}
+
+jboolean android_security_cts_AudioPolicy_test_startAudioSource(JNIEnv* env __unused,
+ jobject thiz __unused)
+{
+ sp<IAudioPolicyService> aps;
+
+ if (!init(aps, NULL, NULL)) {
+ return false;
+ }
+
+ // Keep synchronized with IAudioPolicyService.cpp!
+ enum {
+ START_AUDIO_SOURCE = 41,
+ };
+
+ for (int i = 0; i < 10; ++i) {
+ Parcel data, reply;
+ data.writeInterfaceToken(aps->getInterfaceDescriptor());
+ data.writeInt32(-i);
+ IInterface::asBinder(aps)->transact(START_AUDIO_SOURCE, data, &reply);
+ status_t err = (status_t)reply.readInt32();
+ if (err == NO_ERROR) {
+ continue;
+ }
+ audio_io_handle_t handle = (audio_io_handle_t)reply.readInt32();
+ if (handle != 0) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
static JNINativeMethod gMethods[] = {
{ "native_test_startOutput", "()Z",
(void *) android_security_cts_AudioPolicy_test_startOutput },
@@ -195,6 +254,10 @@
(void *) android_security_cts_AudioPolicy_test_isStreamActive },
{ "native_test_isStreamActiveRemotely", "()Z",
(void *) android_security_cts_AudioPolicy_test_isStreamActiveRemotely },
+ { "native_test_getStreamVolumeLeak", "()I",
+ (void *) android_security_cts_AudioPolicy_test_getStreamVolumeLeak },
+ { "native_test_startAudioSource", "()Z",
+ (void *) android_security_cts_AudioPolicy_test_startAudioSource },
};
int register_android_security_cts_AudioPolicyBinderTest(JNIEnv* env)
diff --git a/tests/tests/security/jni/android_security_cts_MediaPlayerInfoLeakTest.cpp b/tests/tests/security/jni/android_security_cts_MediaPlayerInfoLeakTest.cpp
new file mode 100644
index 0000000..0c61f25
--- /dev/null
+++ b/tests/tests/security/jni/android_security_cts_MediaPlayerInfoLeakTest.cpp
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "MediaPlayerInfoLeakTest-JNI"
+
+#include <jni.h>
+
+#include <binder/Parcel.h>
+#include <binder/IServiceManager.h>
+
+#include <media/IMediaPlayer.h>
+#include <media/IMediaPlayerService.h>
+#include <media/IMediaPlayerClient.h>
+
+#include <sys/stat.h>
+
+using namespace android;
+
+static status_t connectMediaPlayer(sp<IMediaPlayer>& iMP)
+{
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> mediaPlayerService = sm->checkService(String16("media.player"));
+
+ sp<IMediaPlayerService> iMPService = IMediaPlayerService::asInterface(mediaPlayerService);
+ sp<IMediaPlayerClient> client;
+ Parcel data, reply;
+ int dummyAudioSessionId = 1;
+ data.writeInterfaceToken(iMPService->getInterfaceDescriptor());
+ data.writeStrongBinder(IInterface::asBinder(client));
+ data.writeInt32(dummyAudioSessionId);
+
+ // Keep synchronized with IMediaPlayerService.cpp!
+ enum {
+ CREATE = IBinder::FIRST_CALL_TRANSACTION,
+ };
+ status_t err = IInterface::asBinder(iMPService)->transact(CREATE, data, &reply);
+ if (err == NO_ERROR) {
+ iMP = interface_cast<IMediaPlayer>(reply.readStrongBinder());
+ }
+ return err;
+}
+
+int testMediaPlayerInfoLeak(int command)
+{
+ sp<IMediaPlayer> iMP;
+ if (NO_ERROR != connectMediaPlayer(iMP)) {
+ return false;
+ }
+
+
+ Parcel data, reply;
+ data.writeInterfaceToken(iMP->getInterfaceDescriptor());
+ IInterface::asBinder(iMP)->transact(command, data, &reply);
+
+ int leak = reply.readInt32();
+ status_t err = reply.readInt32();
+ return leak;
+}
+
+jint android_security_cts_MediaPlayer_test_getCurrentPositionLeak(JNIEnv* env __unused,
+ jobject thiz __unused)
+{
+ // Keep synchronized with IMediaPlayer.cpp!
+ enum {
+ GET_CURRENT_POSITION = 16,
+ };
+ return testMediaPlayerInfoLeak(GET_CURRENT_POSITION);
+}
+
+jint android_security_cts_MediaPlayer_test_getDurationLeak(JNIEnv* env __unused,
+ jobject thiz __unused)
+{
+ // Keep synchronized with IMediaPlayer.cpp!
+ enum {
+ GET_DURATION = 17,
+ };
+ return testMediaPlayerInfoLeak(GET_DURATION);
+}
+
+static JNINativeMethod gMethods[] = {
+ { "native_test_getCurrentPositionLeak", "()I",
+ (void *) android_security_cts_MediaPlayer_test_getCurrentPositionLeak },
+ { "native_test_getDurationLeak", "()I",
+ (void *) android_security_cts_MediaPlayer_test_getDurationLeak },
+};
+
+int register_android_security_cts_MediaPlayerInfoLeakTest(JNIEnv* env)
+{
+ jclass clazz = env->FindClass("android/security/cts/MediaPlayerInfoLeakTest");
+ return env->RegisterNatives(clazz, gMethods,
+ sizeof(gMethods) / sizeof(JNINativeMethod));
+}
diff --git a/tests/tests/security/jni/android_security_cts_StagefrightFoundationTest.cpp b/tests/tests/security/jni/android_security_cts_StagefrightFoundationTest.cpp
new file mode 100644
index 0000000..d16bd38
--- /dev/null
+++ b/tests/tests/security/jni/android_security_cts_StagefrightFoundationTest.cpp
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "AudioEffectBinderTest-JNI"
+
+#include <cstdio>
+#include <jni.h>
+#include <binder/Parcel.h>
+#include <media/stagefright/foundation/AMessage.h>
+
+using namespace android;
+
+/*
+ * Native methods used by
+ * cts/tests/tests/security/src/android/security/cts/StagefrightFoundationTest.java
+ */
+
+static jboolean android_security_cts_StagefrightFoundation_test_aMessageFromParcel(
+ JNIEnv* env __unused, jobject thiz __unused)
+{
+ const int kMaxNumItems = 64;
+ const int kNumItems = kMaxNumItems + 1 + 1000;
+ char name[128];
+
+ Parcel data;
+ data.writeInt32(0); // what
+ data.writeInt32(kNumItems); // numItems
+ for (int i = 0; i < kMaxNumItems; ++i) {
+ snprintf(name, sizeof(name), "item-%d", i);
+ data.writeCString(name); // name
+ data.writeInt32(0); // kTypeInt32
+ data.writeInt32(i); // value
+ }
+ data.writeCString("evil"); // name
+ data.writeInt32(0); // kTypeInt32
+ data.writeInt32(0); // value
+ // NOTE: This could overwrite mNumItems!
+
+ for (int i = 0; i < 1000; ++i) {
+ snprintf(name, sizeof(name), "evil-%d", i);
+ data.writeCString(name); // name
+ data.writeInt32(0); // kTypeInt32
+ data.writeInt32(0); // value
+ }
+
+ data.setDataPosition(0);
+ sp<AMessage> msg = AMessage::FromParcel(data);
+
+ for (int i = 0; i < kMaxNumItems; ++i) {
+ snprintf(name, sizeof(name), "item-%d", i);
+ int32_t value;
+ if (!msg->findInt32(name, &value)) {
+ ALOGE("cannot find value for %s", name);
+ return JNI_FALSE;
+ }
+ if (value != i) {
+ ALOGE("value is changed: expected %d actual %d", i, value);
+ return JNI_FALSE;
+ }
+ }
+ return JNI_TRUE;
+}
+
+int register_android_security_cts_StagefrightFoundationTest(JNIEnv *env)
+{
+ static JNINativeMethod methods[] = {
+ { "native_test_aMessageFromParcel", "()Z",
+ (void *) android_security_cts_StagefrightFoundation_test_aMessageFromParcel},
+ };
+
+ jclass clazz = env->FindClass("android/security/cts/StagefrightFoundationTest");
+ return env->RegisterNatives(clazz, methods, sizeof(methods) / sizeof(methods[0]));
+}
diff --git a/tests/tests/security/src/android/security/cts/AudioEffectBinderTest.java b/tests/tests/security/src/android/security/cts/AudioEffectBinderTest.java
new file mode 100644
index 0000000..20d9615
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/AudioEffectBinderTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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.security.cts;
+
+import android.media.audiofx.AudioEffect;
+
+import java.util.UUID;
+
+import junit.framework.TestCase;
+
+public class AudioEffectBinderTest extends TestCase {
+
+ static {
+ System.loadLibrary("ctssecurity_jni");
+ }
+
+ /**
+ * Checks that IEffect::command() cannot leak data.
+ */
+ public void test_isCommandSecure() throws Exception {
+ if (isEffectTypeAvailable(AudioEffect.EFFECT_TYPE_EQUALIZER)) {
+ assertTrue(native_test_isCommandSecure());
+ }
+ }
+
+ /* see AudioEffect.isEffectTypeAvailable(), implements hidden function */
+ private static boolean isEffectTypeAvailable(UUID type) {
+ AudioEffect.Descriptor[] desc = AudioEffect.queryEffects();
+ if (desc == null) {
+ return false;
+ }
+
+ for (int i = 0; i < desc.length; i++) {
+ if (desc[i].type.equals(type)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static native boolean native_test_isCommandSecure();
+}
diff --git a/tests/tests/security/src/android/security/cts/AudioFlingerBinderTest.java b/tests/tests/security/src/android/security/cts/AudioFlingerBinderTest.java
new file mode 100644
index 0000000..202bd65
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/AudioFlingerBinderTest.java
@@ -0,0 +1,71 @@
+/*
+ * 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.security.cts;
+
+import junit.framework.TestCase;
+
+public class AudioFlingerBinderTest extends TestCase {
+
+ static {
+ System.loadLibrary("ctssecurity_jni");
+ }
+
+ /**
+ * Checks that AudioSystem::setMasterMute() does not crash mediaserver if a duplicated output
+ * is opened.
+ */
+ public void test_setMasterMute() throws Exception {
+ assertTrue(native_test_setMasterMute());
+ }
+
+ /**
+ * Checks that AudioSystem::setMasterVolume() does not crash mediaserver if a duplicated output
+ * is opened.
+ */
+ public void test_setMasterVolume() throws Exception {
+ assertTrue(native_test_setMasterVolume());
+ }
+
+ /**
+ * Checks that IAudioFlinger::listAudioPorts() does not cause a memory overflow when passed a
+ * large number of ports.
+ */
+ public void test_listAudioPorts() throws Exception {
+ assertTrue(native_test_listAudioPorts());
+ }
+
+ /**
+ * Checks that IAudioFlinger::listAudioPatches() does not cause a memory overflow when passed a
+ * large number of ports.
+ */
+ public void test_listAudioPatches() throws Exception {
+ assertTrue(native_test_listAudioPatches());
+ }
+
+ /**
+ * Checks that IAudioFlinger::createEffect() does not leak information on the server side.
+ */
+ public void test_createEffect() throws Exception {
+ assertTrue(native_test_createEffect());
+ }
+
+ private static native boolean native_test_setMasterMute();
+ private static native boolean native_test_setMasterVolume();
+ private static native boolean native_test_listAudioPorts();
+ private static native boolean native_test_listAudioPatches();
+ private static native boolean native_test_createEffect();
+}
diff --git a/tests/tests/security/src/android/security/cts/AudioPolicyBinderTest.java b/tests/tests/security/src/android/security/cts/AudioPolicyBinderTest.java
index b307247..82346a1 100644
--- a/tests/tests/security/src/android/security/cts/AudioPolicyBinderTest.java
+++ b/tests/tests/security/src/android/security/cts/AudioPolicyBinderTest.java
@@ -56,8 +56,27 @@
assertTrue(native_test_isStreamActiveRemotely());
}
+ /**
+ * Checks that IAudioPolicyService::getStreamVolumeIndex() does not leak information
+ * when called with an invalid stream/device type.
+ */
+ public void test_getStreamVolumeLeak() throws Exception {
+ int volume = native_test_getStreamVolumeLeak();
+ assertTrue(String.format("Leaked volume 0x%08X", volume), volume == 0);
+ }
+
+ /**
+ * Checks that IAudioPolicyService::startAudioSource() cannot leak information from
+ * server side.
+ */
+ public void test_startAudioSource() throws Exception {
+ assertTrue(native_test_startAudioSource());
+ }
+
private static native boolean native_test_startOutput();
private static native boolean native_test_stopOutput();
private static native boolean native_test_isStreamActive();
private static native boolean native_test_isStreamActiveRemotely();
+ private static native int native_test_getStreamVolumeLeak();
+ private static native boolean native_test_startAudioSource();
}
diff --git a/tests/tests/security/src/android/security/cts/ClonedSecureRandomTest.java b/tests/tests/security/src/android/security/cts/ClonedSecureRandomTest.java
index d188aab..19866de 100644
--- a/tests/tests/security/src/android/security/cts/ClonedSecureRandomTest.java
+++ b/tests/tests/security/src/android/security/cts/ClonedSecureRandomTest.java
@@ -26,7 +26,8 @@
import android.security.cts.activity.ISecureRandomService;
import android.security.cts.activity.SecureRandomService;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
+
+import com.android.cts.util.TimeoutReq;
import java.io.BufferedReader;
import java.io.EOFException;
@@ -37,7 +38,6 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
-@LargeTest
public class ClonedSecureRandomTest extends AndroidTestCase {
private static final int MAX_SHUTDOWN_TRIES = 50;
@@ -93,6 +93,7 @@
* sees two newly started processes with the same PID and compares their
* output.
*/
+ @TimeoutReq(minutes=15)
public void testCheckForDuplicateOutput() throws Exception {
assertEquals("Only supports up to " + MAX_PID + " because of memory requirements",
Integer.toString(MAX_PID), getFirstLineFromFile("/proc/sys/kernel/pid_max"));
diff --git a/tests/tests/security/src/android/security/cts/ListeningPortsTest.java b/tests/tests/security/src/android/security/cts/ListeningPortsTest.java
index 87e957d..54fa406 100644
--- a/tests/tests/security/src/android/security/cts/ListeningPortsTest.java
+++ b/tests/tests/security/src/android/security/cts/ListeningPortsTest.java
@@ -18,11 +18,14 @@
import android.content.pm.PackageManager;
import android.test.AndroidTestCase;
+import android.util.Log;
import junit.framework.AssertionFailedError;
import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
@@ -37,15 +40,31 @@
* is considered a security best practice.
*/
public class ListeningPortsTest extends AndroidTestCase {
+ private static final String TAG = "ListeningPortsTest";
+
+ private static final int CONN_TIMEOUT_IN_MS = 5000;
/** Ports that are allowed to be listening. */
private static final List<String> EXCEPTION_PATTERNS = new ArrayList<String>(6);
static {
// IPv4 exceptions
- EXCEPTION_PATTERNS.add("0.0.0.0:5555"); // emulator port
- EXCEPTION_PATTERNS.add("10.0.2.15:5555"); // net forwarding for emulator
- EXCEPTION_PATTERNS.add("127.0.0.1:5037"); // adb daemon "smart sockets"
+ // Patterns containing ":" are allowed address port combinations
+ // Pattterns contains " " are allowed address UID combinations
+ // Patterns containing both are allowed address, port, and UID combinations
+ EXCEPTION_PATTERNS.add("0.0.0.0:5555"); // emulator port
+ EXCEPTION_PATTERNS.add("0.0.0.0:9101"); // verified ports
+ EXCEPTION_PATTERNS.add("0.0.0.0:9551"); // verified ports
+ EXCEPTION_PATTERNS.add("0.0.0.0:9552"); // verified ports
+ EXCEPTION_PATTERNS.add("10.0.2.15:5555"); // net forwarding for emulator
+ EXCEPTION_PATTERNS.add("127.0.0.1:5037"); // adb daemon "smart sockets"
+ EXCEPTION_PATTERNS.add("0.0.0.0 1020"); // used by the cast receiver
+ EXCEPTION_PATTERNS.add("0.0.0.0 10000"); // used by the cast receiver
+ EXCEPTION_PATTERNS.add("127.0.0.1 10000"); // used by the cast receiver
+ EXCEPTION_PATTERNS.add(":: 1002"); // used by remote control
+ EXCEPTION_PATTERNS.add(":: 1020"); // used by remote control
+ //no current patterns involve address, port and UID combinations
+ //Example for when necessary: EXCEPTION_PATTERNS.add("0.0.0.0:5555 10000")
}
/**
@@ -185,10 +204,16 @@
List<ParsedProcEntry> entries = ParsedProcEntry.parse(procFilePath);
for (ParsedProcEntry entry : entries) {
String addrPort = entry.localAddress.getHostAddress() + ':' + entry.port;
+ String addrUid = entry.localAddress.getHostAddress() + ' ' + entry.uid;
+ String addrPortUid = addrPort + ' ' + entry.uid;
if (isPortListening(entry.state, isTcp)
- && !isException(addrPort)
- && (!entry.localAddress.isLoopbackAddress() ^ loopback)) {
+ && !(isException(addrPort) || isException(addrUid) || isException(addrPortUid))
+ && (!entry.localAddress.isLoopbackAddress() ^ loopback)) {
+ if (isTcp && !isTcpConnectable(entry.localAddress, entry.port)) {
+ continue;
+ }
+
errors += "\nFound port listening on addr="
+ entry.localAddress.getHostAddress() + ", port="
+ entry.port + ", UID=" + entry.uid
@@ -210,6 +235,33 @@
return Arrays.asList(packages).toString();
}
+ private boolean isTcpConnectable(InetAddress address, int port) {
+ Socket socket = new Socket();
+
+ try {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Trying to connect " + address + ":" + port);
+ }
+ socket.connect(new InetSocketAddress(address, port), CONN_TIMEOUT_IN_MS);
+ } catch (IOException ioe) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Unable to connect:" + ioe);
+ }
+ return false;
+ } finally {
+ try {
+ socket.close();
+ } catch (IOException closeError) {
+ Log.e(TAG, "Unable to close socket: " + closeError);
+ }
+ }
+
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, address + ":" + port + " is connectable.");
+ }
+ return true;
+ }
+
private static boolean isException(String localAddress) {
return isPatternMatch(EXCEPTION_PATTERNS, localAddress);
}
diff --git a/tests/tests/security/src/android/security/cts/MediaPlayerInfoLeakTest.java b/tests/tests/security/src/android/security/cts/MediaPlayerInfoLeakTest.java
new file mode 100644
index 0000000..e34fc05
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/MediaPlayerInfoLeakTest.java
@@ -0,0 +1,46 @@
+/*
+ * 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.security.cts;
+
+import junit.framework.TestCase;
+
+public class MediaPlayerInfoLeakTest extends TestCase {
+
+ static {
+ System.loadLibrary("ctssecurity_jni");
+ }
+
+
+ /**
+ * Checks that IMediaPlayer::getCurrentPosition() does not leak info in error case
+ */
+ public void test_getCurrentPositionLeak() throws Exception {
+ int pos = native_test_getCurrentPositionLeak();
+ assertTrue(String.format("Leaked pos 0x%08X", pos), pos == 0);
+ }
+
+ /**
+ * Checks that IMediaPlayer::getDuration() does not leak info in error case
+ */
+ public void test_getDurationLeak() throws Exception {
+ int dur = native_test_getDurationLeak();
+ assertTrue(String.format("Leaked dur 0x%08X", dur), dur == 0);
+ }
+
+ private static native int native_test_getCurrentPositionLeak();
+ private static native int native_test_getDurationLeak();
+}
diff --git a/tests/tests/security/src/android/security/cts/PutOverflowTest.java b/tests/tests/security/src/android/security/cts/PutOverflowTest.java
new file mode 100644
index 0000000..e0eb34c
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/PutOverflowTest.java
@@ -0,0 +1,38 @@
+/*
+ * 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.security.cts;
+
+import android.test.AndroidTestCase;
+import java.lang.reflect.Method;
+
+public class PutOverflowTest extends AndroidTestCase {
+ public void testCrash() throws Exception {
+ try {
+ Class<?> keystoreClass = Class.forName("android.security.KeyStore");
+ Method getInstance = keystoreClass.getMethod("getInstance");
+ Method put = keystoreClass.getMethod("put",
+ String.class, byte[].class, int.class, int.class);
+ Object keystore = getInstance.invoke(null);
+ byte[] buffer = new byte[65536];
+ Boolean result = (Boolean)put.invoke(keystore, "crashFile", buffer, -1, 0);
+ assertTrue("Fix for ANDROID-22802399 not present", result);
+ } catch (ReflectiveOperationException ignored) {
+ // Since this test requires reflection avoid causing undue failures if classes or
+ // methods were changed.
+ }
+ }
+}
diff --git a/tests/tests/security/src/android/security/cts/ServicePermissionsTest.java b/tests/tests/security/src/android/security/cts/ServicePermissionsTest.java
index 00cbfd8..fdc3058 100644
--- a/tests/tests/security/src/android/security/cts/ServicePermissionsTest.java
+++ b/tests/tests/security/src/android/security/cts/ServicePermissionsTest.java
@@ -17,6 +17,7 @@
package android.security.cts;
import android.os.IBinder;
+import android.os.DeadObjectException;
import android.os.TransactionTooLargeException;
import android.test.AndroidTestCase;
import android.util.Log;
@@ -108,7 +109,7 @@
// probably not checking for DUMP.
throw e;
}
- } catch (TransactionTooLargeException e) {
+ } catch (TransactionTooLargeException | DeadObjectException e) {
// SELinux likely prevented the dump - assume safe
continue;
} finally {
diff --git a/tests/tests/security/src/android/security/cts/StagefrightFoundationTest.java b/tests/tests/security/src/android/security/cts/StagefrightFoundationTest.java
new file mode 100644
index 0000000..9999d88
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/StagefrightFoundationTest.java
@@ -0,0 +1,35 @@
+/*
+ * 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.security.cts;
+
+import junit.framework.TestCase;
+
+public class StagefrightFoundationTest extends TestCase {
+
+ static {
+ System.loadLibrary("ctssecurity_jni");
+ }
+
+ /**
+ * Checks that IEffect::command() cannot leak data.
+ */
+ public void test_aMessageFromParcel() throws Exception {
+ assertTrue(native_test_aMessageFromParcel());
+ }
+
+ private static native boolean native_test_aMessageFromParcel();
+}
diff --git a/tests/tests/systemui/Android.mk b/tests/tests/systemui/Android.mk
new file mode 100644
index 0000000..1a15fd2
--- /dev/null
+++ b/tests/tests/systemui/Android.mk
@@ -0,0 +1,30 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# don't include this package in any target
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsSystemUiTestCases
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/systemui/AndroidManifest.xml b/tests/tests/systemui/AndroidManifest.xml
new file mode 100644
index 0000000..bf5df5b
--- /dev/null
+++ b/tests/tests/systemui/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?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
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.cts.systemui">
+ <uses-permission android:name="android.permission.INJECT_EVENTS" />
+ <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <application>
+ <activity android:name=".LightStatusBarActivity"
+ android:theme="@android:style/Theme.Material.NoActionBar"></activity>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.cts.systemui">
+ </instrumentation>
+
+</manifest>
+
diff --git a/tests/tests/systemui/src/com/android/cts/systemui/ColorUtils.java b/tests/tests/systemui/src/com/android/cts/systemui/ColorUtils.java
new file mode 100644
index 0000000..626a179
--- /dev/null
+++ b/tests/tests/systemui/src/com/android/cts/systemui/ColorUtils.java
@@ -0,0 +1,68 @@
+/*
+ * 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.systemui;
+
+/**
+ * Copies of non-public {@link android.graphics.Color} APIs
+ */
+public class ColorUtils {
+
+ public static float brightness(int argb) {
+ int r = (argb >> 16) & 0xFF;
+ int g = (argb >> 8) & 0xFF;
+ int b = argb & 0xFF;
+
+ int V = Math.max(b, Math.max(r, g));
+
+ return (V / 255.f);
+ }
+
+ public static float hue(int argb) {
+ int r = (argb >> 16) & 0xFF;
+ int g = (argb >> 8) & 0xFF;
+ int b = argb & 0xFF;
+
+ int V = Math.max(b, Math.max(r, g));
+ int temp = Math.min(b, Math.min(r, g));
+
+ float H;
+
+ if (V == temp) {
+ H = 0;
+ } else {
+ final float vtemp = (float) (V - temp);
+ final float cr = (V - r) / vtemp;
+ final float cg = (V - g) / vtemp;
+ final float cb = (V - b) / vtemp;
+
+ if (r == V) {
+ H = cb - cg;
+ } else if (g == V) {
+ H = 2 + cr - cb;
+ } else {
+ H = 4 + cg - cr;
+ }
+
+ H /= 6.f;
+ if (H < 0) {
+ H++;
+ }
+ }
+
+ return H;
+ }
+}
diff --git a/tests/tests/systemui/src/com/android/cts/systemui/LightStatusBarActivity.java b/tests/tests/systemui/src/com/android/cts/systemui/LightStatusBarActivity.java
new file mode 100644
index 0000000..3722320
--- /dev/null
+++ b/tests/tests/systemui/src/com/android/cts/systemui/LightStatusBarActivity.java
@@ -0,0 +1,58 @@
+/*
+ * 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.systemui;
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup.LayoutParams;
+
+
+/**
+ * An activity that exercises SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.
+ */
+public class LightStatusBarActivity extends Activity {
+
+ private View mContent;
+
+ public void onCreate(Bundle bundle){
+ super.onCreate(bundle);
+
+ mContent = new View(this);
+ mContent.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
+ LayoutParams.MATCH_PARENT));
+ setContentView(mContent);
+ }
+
+ public void setLightStatusBar(boolean lightStatusBar) {
+ int vis = getWindow().getDecorView().getSystemUiVisibility();
+ if (lightStatusBar) {
+ vis |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+ } else {
+ vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+ }
+ getWindow().getDecorView().setSystemUiVisibility(vis);
+ }
+
+ public int getTop() {
+ return mContent.getLocationOnScreen()[1];
+ }
+
+ public int getWidth() {
+ return mContent.getWidth();
+ }
+}
diff --git a/tests/tests/systemui/src/com/android/cts/systemui/LightStatusBarTests.java b/tests/tests/systemui/src/com/android/cts/systemui/LightStatusBarTests.java
new file mode 100644
index 0000000..b5bfd51
--- /dev/null
+++ b/tests/tests/systemui/src/com/android/cts/systemui/LightStatusBarTests.java
@@ -0,0 +1,235 @@
+/*
+ * 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.systemui;
+
+import android.app.ActivityManager;
+import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.support.test.InstrumentationRegistry;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/**
+ * Test for light status bar.
+ */
+public class LightStatusBarTests extends ActivityInstrumentationTestCase2<LightStatusBarActivity> {
+
+ public static final String TAG = "LightStatusBarTests";
+
+ public static final String DUMP_PATH = "/sdcard/lightstatustest.png";
+
+ public LightStatusBarTests() {
+ super(LightStatusBarActivity.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ // As the way to access Instrumentation is changed in the new runner, we need to inject it
+ // manually into ActivityInstrumentationTestCase2. ActivityInstrumentationTestCase2 will
+ // be marked as deprecated and replaced with ActivityTestRule.
+ injectInstrumentation(InstrumentationRegistry.getInstrumentation());
+ }
+
+ public void testLightStatusBarIcons() throws Throwable {
+ PackageManager pm = getInstrumentation().getContext().getPackageManager();
+ if (pm.hasSystemFeature(PackageManager.FEATURE_WATCH)
+ || pm.hasSystemFeature(PackageManager.FEATURE_TELEVISION)
+ || pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
+ // No status bar on TVs and watches.
+ return;
+ }
+
+ if (!ActivityManager.isHighEndGfx()) {
+ // non-highEndGfx devices don't do colored system bars.
+ return;
+ }
+
+ requestLightStatusBar(Color.RED /* background */);
+ Thread.sleep(1000);
+
+ Bitmap bitmap = takeStatusBarScreenshot();
+ Stats s = evaluateLightStatusBarBitmap(bitmap, Color.RED /* background */);
+ boolean success = false;
+
+ try {
+ assertMoreThan("Not enough background pixels", 0.3f,
+ (float) s.backgroundPixels / s.totalPixels(),
+ "Is the status bar background showing correctly (solid red)?");
+
+ assertMoreThan("Not enough pixels colored as in the spec", 0.1f,
+ (float) s.iconPixels / s.foregroundPixels(),
+ "Are the status bar icons colored according to the spec "
+ + "(60% black and 24% black)?");
+
+ assertLessThan("Too many lighter pixels lighter than the background", 0.05f,
+ (float) s.sameHueLightPixels / s.foregroundPixels(),
+ "Are the status bar icons dark?");
+
+ assertLessThan("Too many pixels with a changed hue", 0.05f,
+ (float) s.unexpectedHuePixels / s.foregroundPixels(),
+ "Are the status bar icons color-free?");
+
+ success = true;
+ } finally {
+ if (!success) {
+ Log.e(TAG, "Dumping failed bitmap to " + DUMP_PATH);
+ dumpBitmap(bitmap);
+ }
+ }
+ }
+
+ private void assertMoreThan(String what, float expected, float actual, String hint) {
+ if (!(actual > expected)) {
+ fail(what + ": expected more than " + expected * 100 + "%, but only got " + actual * 100
+ + "%; " + hint);
+ }
+ }
+
+ private void assertLessThan(String what, float expected, float actual, String hint) {
+ if (!(actual < expected)) {
+ fail(what + ": expected less than " + expected * 100 + "%, but got " + actual * 100
+ + "%; " + hint);
+ }
+ }
+
+ private void requestLightStatusBar(final int background) throws Throwable {
+ final LightStatusBarActivity activity = getActivity();
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ activity.getWindow().setStatusBarColor(background);
+ activity.setLightStatusBar(true);
+ }
+ });
+ }
+
+ private static class Stats {
+ int backgroundPixels;
+ int iconPixels;
+ int sameHueDarkPixels;
+ int sameHueLightPixels;
+ int unexpectedHuePixels;
+
+ int totalPixels() {
+ return backgroundPixels + iconPixels + sameHueDarkPixels
+ + sameHueLightPixels + unexpectedHuePixels;
+ }
+
+ int foregroundPixels() {
+ return iconPixels + sameHueDarkPixels
+ + sameHueLightPixels + unexpectedHuePixels;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{bg=%d, ic=%d, dark=%d, light=%d, bad=%d}",
+ backgroundPixels, iconPixels, sameHueDarkPixels, sameHueLightPixels,
+ unexpectedHuePixels);
+ }
+ }
+
+ private Stats evaluateLightStatusBarBitmap(Bitmap bitmap, int background) {
+ int iconColor = 0x99000000;
+ int iconPartialColor = 0x3d000000;
+
+ int mixedIconColor = mixSrcOver(background, iconColor);
+ int mixedIconPartialColor = mixSrcOver(background, iconPartialColor);
+
+ int[] pixels = new int[bitmap.getHeight() * bitmap.getWidth()];
+ bitmap.getPixels(pixels, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
+
+ Stats s = new Stats();
+ float eps = 0.005f;
+
+ for (int c : pixels) {
+ if (c == background) {
+ s.backgroundPixels++;
+ continue;
+ }
+
+ // What we expect the icons to be colored according to the spec.
+ if (c == mixedIconColor || c == mixedIconPartialColor) {
+ s.iconPixels++;
+ continue;
+ }
+
+ // Due to anti-aliasing, there will be deviations from the ideal icon color, but it
+ // should still be mostly the same hue.
+ float hueDiff = Math.abs(ColorUtils.hue(background) - ColorUtils.hue(c));
+ if (hueDiff < eps || hueDiff > 1 - eps) {
+ // .. it shouldn't be lighter than the original background though.
+ if (ColorUtils.brightness(c) > ColorUtils.brightness(background)) {
+ s.sameHueLightPixels++;
+ } else {
+ s.sameHueDarkPixels++;
+ }
+ continue;
+ }
+
+ s.unexpectedHuePixels++;
+ }
+
+ return s;
+ }
+
+ private void dumpBitmap(Bitmap bitmap) {
+ FileOutputStream fileStream = null;
+ try {
+ fileStream = new FileOutputStream(DUMP_PATH);
+ bitmap.compress(Bitmap.CompressFormat.PNG, 85, fileStream);
+ fileStream.flush();
+ } catch (Exception e) {
+ Log.e(TAG, "Dumping bitmap failed.", e);
+ } finally {
+ if (fileStream != null) {
+ try {
+ fileStream.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ private int mixSrcOver(int background, int foreground) {
+ int bgAlpha = Color.alpha(background);
+ int bgRed = Color.red(background);
+ int bgGreen = Color.green(background);
+ int bgBlue = Color.blue(background);
+
+ int fgAlpha = Color.alpha(foreground);
+ int fgRed = Color.red(foreground);
+ int fgGreen = Color.green(foreground);
+ int fgBlue = Color.blue(foreground);
+
+ return Color.argb(fgAlpha + (255 - fgAlpha) * bgAlpha / 255,
+ fgRed + (255 - fgAlpha) * bgRed / 255,
+ fgGreen + (255 - fgAlpha) * bgGreen / 255,
+ fgBlue + (255 - fgAlpha) * bgBlue / 255);
+ }
+
+ private Bitmap takeStatusBarScreenshot() {
+ Bitmap fullBitmap = getInstrumentation().getUiAutomation().takeScreenshot();
+ return Bitmap.createBitmap(fullBitmap, 0, 0,
+ getActivity().getWidth(), getActivity().getTop());
+ }
+}
diff --git a/tests/tests/telecom/src/android/telecom/cts/BaseRemoteTelecomTest.java b/tests/tests/telecom/src/android/telecom/cts/BaseRemoteTelecomTest.java
index 52c8317..3a0dbd3 100644
--- a/tests/tests/telecom/src/android/telecom/cts/BaseRemoteTelecomTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/BaseRemoteTelecomTest.java
@@ -51,16 +51,21 @@
@Override
protected void tearDown() throws Exception {
- tearDownConnectionServices(TEST_PHONE_ACCOUNT_HANDLE, TEST_REMOTE_PHONE_ACCOUNT_HANDLE);
+ if (mShouldTestTelecom) {
+ tearDownRemoteConnectionService(TEST_REMOTE_PHONE_ACCOUNT_HANDLE);
+ }
super.tearDown();
}
protected void setupConnectionServices(MockConnectionService connectionService,
- MockConnectionService remoteConnectionService, int flags)
- throws Exception {
+ MockConnectionService remoteConnectionService, int flags) throws Exception {
// Setup the primary connection service first
setupConnectionService(connectionService, flags);
+ setupRemoteConnectionService(remoteConnectionService, flags);
+ }
+ protected void setupRemoteConnectionService(MockConnectionService remoteConnectionService,
+ int flags) throws Exception {
if (remoteConnectionService != null) {
this.remoteConnectionService = remoteConnectionService;
} else {
@@ -76,19 +81,23 @@
TEST_REMOTE_PHONE_ACCOUNT_HANDLE,
REMOTE_ACCOUNT_LABEL,
TEST_REMOTE_PHONE_ACCOUNT_ADDRESS);
+ // Wait till the adb commands have executed and account is in Telecom database.
+ assertPhoneAccountRegistered(TEST_REMOTE_PHONE_ACCOUNT_HANDLE);
}
if ((flags & FLAG_ENABLE) != 0) {
TestUtils.enablePhoneAccount(getInstrumentation(), TEST_REMOTE_PHONE_ACCOUNT_HANDLE);
+ // Wait till the adb commands have executed and account is enabled in Telecom database.
+ assertPhoneAccountEnabled(TEST_REMOTE_PHONE_ACCOUNT_HANDLE);
}
}
- protected void tearDownConnectionServices(PhoneAccountHandle accountHandle,
- PhoneAccountHandle remoteAccountHandle) throws Exception {
- // Teardown the primary connection service first
- tearDownConnectionService(accountHandle);
-
+ protected void tearDownRemoteConnectionService(PhoneAccountHandle remoteAccountHandle)
+ throws Exception {
+ assertNumConnections(this.remoteConnectionService, 0);
mTelecomManager.unregisterPhoneAccount(remoteAccountHandle);
CtsRemoteConnectionService.tearDown();
+ //Telecom doesn't unbind the remote connection service at the end of all calls today.
+ //assertCtsRemoteConnectionServiceUnbound();
this.remoteConnectionService = null;
}
@@ -208,4 +217,22 @@
"Remote Conference should be in state " + state
);
}
+
+ void assertCtsRemoteConnectionServiceUnbound() {
+ waitUntilConditionIsTrueOrTimeout(
+ new Condition() {
+ @Override
+ public Object expected(){
+ return true;
+ }
+
+ @Override
+ public Object actual() {
+ return CtsRemoteConnectionService.isServiceUnbound();
+ }
+ },
+ WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+ "CtsRemoteConnectionService not yet unbound!"
+ );
+ }
}
diff --git a/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java b/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
index 31ca09b..d7fe239 100644
--- a/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
+++ b/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
@@ -110,13 +110,13 @@
TestUtils.setDefaultDialer(getInstrumentation(), mPreviousDefaultDialer);
}
tearDownConnectionService(TEST_PHONE_ACCOUNT_HANDLE);
+ assertMockInCallServiceUnbound();
}
super.tearDown();
}
protected PhoneAccount setupConnectionService(MockConnectionService connectionService,
- int flags)
- throws Exception {
+ int flags) throws Exception {
if (connectionService != null) {
this.connectionService = connectionService;
} else {
@@ -130,14 +130,18 @@
}
if ((flags & FLAG_ENABLE) != 0) {
TestUtils.enablePhoneAccount(getInstrumentation(), TEST_PHONE_ACCOUNT_HANDLE);
+ // Wait till the adb commands have executed and account is enabled in Telecom database.
+ assertPhoneAccountEnabled(TEST_PHONE_ACCOUNT_HANDLE);
}
return TEST_PHONE_ACCOUNT;
}
protected void tearDownConnectionService(PhoneAccountHandle accountHandle) throws Exception {
+ assertNumConnections(this.connectionService, 0);
mTelecomManager.unregisterPhoneAccount(accountHandle);
CtsConnectionService.tearDown();
+ assertCtsConnectionServiceUnbound();
this.connectionService = null;
}
@@ -513,6 +517,22 @@
}
+ void assertNumConnections(final MockConnectionService connService, final int numConnections) {
+ waitUntilConditionIsTrueOrTimeout(new Condition() {
+ @Override
+ public Object expected() {
+ return numConnections;
+ }
+ @Override
+ public Object actual() {
+ return connService.getAllConnections().size();
+ }
+ },
+ WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+ "ConnectionService should contain " + numConnections + " connections."
+ );
+ }
+
void assertMuteState(final InCallService incallService, final boolean isMuted) {
waitUntilConditionIsTrueOrTimeout(
new Condition() {
@@ -621,7 +641,7 @@
}
},
WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
- "Call should be in state " + state
+ "Call: " + call + " should be in state " + state
);
}
@@ -731,6 +751,79 @@
);
}
+ void assertPhoneAccountRegistered(final PhoneAccountHandle handle) {
+ waitUntilConditionIsTrueOrTimeout(
+ new Condition() {
+ @Override
+ public Object expected() {
+ return true;
+ }
+
+ @Override
+ public Object actual() {
+ return mTelecomManager.getPhoneAccount(handle) != null;
+ }
+ },
+ WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+ "Phone account registration failed for " + handle
+ );
+ }
+
+ void assertPhoneAccountEnabled(final PhoneAccountHandle handle) {
+ waitUntilConditionIsTrueOrTimeout(
+ new Condition() {
+ @Override
+ public Object expected() {
+ return true;
+ }
+
+ @Override
+ public Object actual() {
+ PhoneAccount phoneAccount = mTelecomManager.getPhoneAccount(handle);
+ return (phoneAccount != null && phoneAccount.isEnabled());
+ }
+ },
+ WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+ "Phone account enable failed for " + handle
+ );
+ }
+
+ void assertCtsConnectionServiceUnbound() {
+ waitUntilConditionIsTrueOrTimeout(
+ new Condition() {
+ @Override
+ public Object expected() {
+ return true;
+ }
+
+ @Override
+ public Object actual() {
+ return CtsConnectionService.isServiceUnbound();
+ }
+ },
+ WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+ "CtsConnectionService not yet unbound!"
+ );
+ }
+
+ void assertMockInCallServiceUnbound() {
+ waitUntilConditionIsTrueOrTimeout(
+ new Condition() {
+ @Override
+ public Object expected() {
+ return true;
+ }
+
+ @Override
+ public Object actual() {
+ return MockInCallService.isServiceUnbound();
+ }
+ },
+ WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+ "MockInCallService not yet unbound!"
+ );
+ }
+
void waitUntilConditionIsTrueOrTimeout(Condition condition, long timeout,
String description) {
final long start = System.currentTimeMillis();
diff --git a/tests/tests/telecom/src/android/telecom/cts/ConferenceTest.java b/tests/tests/telecom/src/android/telecom/cts/ConferenceTest.java
index 121d559..508870c 100644
--- a/tests/tests/telecom/src/android/telecom/cts/ConferenceTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/ConferenceTest.java
@@ -50,7 +50,6 @@
@Override
protected void setUp() throws Exception {
super.setUp();
- mContext = getInstrumentation().getContext();
if (mShouldTestTelecom) {
addOutgoingCalls();
addConferenceCall(mCall1, mCall2);
diff --git a/tests/tests/telecom/src/android/telecom/cts/ConnectionServiceTest.java b/tests/tests/telecom/src/android/telecom/cts/ConnectionServiceTest.java
index afaa3eb..836ca48 100644
--- a/tests/tests/telecom/src/android/telecom/cts/ConnectionServiceTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/ConnectionServiceTest.java
@@ -48,20 +48,13 @@
placeAndVerifyCall();
verifyConnectionForOutgoingCall();
+ // Add second connection (add existing connection)
final MockConnection connection = new MockConnection();
connection.setOnHold();
CtsConnectionService.addExistingConnectionToTelecom(TEST_PHONE_ACCOUNT_HANDLE, connection);
-
- try {
- if (!mInCallCallbacks.lock.tryAcquire(3, TimeUnit.SECONDS)) {
- fail("No call added to InCallService.");
- }
- } catch (InterruptedException e) {
- Log.i(TAG, "Test interrupted!");
- }
-
- final MockInCallService inCallService = mInCallCallbacks.getService();
- final Call call = inCallService.getLastCall();
+ assertNumCalls(mInCallCallbacks.getService(), 2);
+ mInCallCallbacks.lock.drainPermits();
+ final Call call = mInCallCallbacks.getService().getLastCall();
assertCallState(call, Call.STATE_HOLDING);
}
@@ -77,17 +70,22 @@
Collection<Connection> connections = CtsConnectionService.getAllConnectionsFromTelecom();
assertEquals(1, connections.size());
assertTrue(connections.contains(connection1));
+ // Need to move this to active since we reject the 3rd incoming call below if this is in
+ // dialing state (b/23428950).
+ connection1.setActive();
+ assertCallState(mInCallCallbacks.getService().getLastCall(), Call.STATE_ACTIVE);
// Add second connection (add existing connection)
final Connection connection2 = new MockConnection();
CtsConnectionService.addExistingConnectionToTelecom(TEST_PHONE_ACCOUNT_HANDLE, connection2);
-
+ assertNumCalls(mInCallCallbacks.getService(), 2);
+ mInCallCallbacks.lock.drainPermits();
connections = CtsConnectionService.getAllConnectionsFromTelecom();
assertEquals(2, connections.size());
assertTrue(connections.contains(connection2));
// Add third connection (incoming call)
- addAndVerifyNewIncomingCall(getTestNumber(), null);
+ addAndVerifyNewIncomingCall(createTestNumber(), null);
final Connection connection3 = verifyConnectionForIncomingCall();
connections = CtsConnectionService.getAllConnectionsFromTelecom();
assertEquals(3, connections.size());
diff --git a/tests/tests/telecom/src/android/telecom/cts/CtsConnectionService.java b/tests/tests/telecom/src/android/telecom/cts/CtsConnectionService.java
index 3c30e6b..d8d5773 100644
--- a/tests/tests/telecom/src/android/telecom/cts/CtsConnectionService.java
+++ b/tests/tests/telecom/src/android/telecom/cts/CtsConnectionService.java
@@ -16,6 +16,9 @@
package android.telecom.cts;
+import static org.junit.Assert.assertFalse;
+
+import android.content.Intent;
import android.telecom.Conference;
import android.telecom.Connection;
import android.telecom.ConnectionRequest;
@@ -23,6 +26,7 @@
import android.telecom.PhoneAccountHandle;
import android.telecom.RemoteConference;
import android.telecom.RemoteConnection;
+import android.util.Log;
import java.util.Collection;
@@ -43,10 +47,12 @@
*
*/
public class CtsConnectionService extends ConnectionService {
+ private static String LOG_TAG = "CtsConnectionService";
// This is the connection service implemented by the test
private static ConnectionService sConnectionService;
// This is the connection service registered with Telecom
private static ConnectionService sTelecomConnectionService;
+ private static boolean mIsServiceUnbound;
public CtsConnectionService() throws Exception {
super();
@@ -65,13 +71,14 @@
private static Object sLock = new Object();
public static void setUp(PhoneAccountHandle phoneAccountHandle,
- ConnectionService connectionService)
- throws Exception {
+ ConnectionService connectionService) throws Exception {
synchronized(sLock) {
if (sConnectionService != null) {
throw new Exception("Mock ConnectionService exists. Failed to call tearDown().");
}
sConnectionService = connectionService;
+ // Cant override the onBind method for ConnectionService, so reset it here.
+ mIsServiceUnbound = false;
}
}
@@ -178,4 +185,16 @@
}
}
}
+
+ @Override
+ public boolean onUnbind(Intent intent) {
+ Log.i(LOG_TAG, "Service unbounded");
+ assertFalse(mIsServiceUnbound);
+ mIsServiceUnbound = true;
+ return super.onUnbind(intent);
+ }
+
+ public static boolean isServiceUnbound() {
+ return mIsServiceUnbound;
+ }
}
diff --git a/tests/tests/telecom/src/android/telecom/cts/CtsRemoteConnectionService.java b/tests/tests/telecom/src/android/telecom/cts/CtsRemoteConnectionService.java
index 3738487..13b525f 100644
--- a/tests/tests/telecom/src/android/telecom/cts/CtsRemoteConnectionService.java
+++ b/tests/tests/telecom/src/android/telecom/cts/CtsRemoteConnectionService.java
@@ -16,11 +16,15 @@
package android.telecom.cts;
+import static org.junit.Assert.assertFalse;
+
+import android.content.Intent;
import android.telecom.Conference;
import android.telecom.Connection;
import android.telecom.ConnectionRequest;
import android.telecom.ConnectionService;
import android.telecom.PhoneAccountHandle;
+import android.util.Log;
/**
* This is the Remote ConnectionService for Telecom's CTS App. Since telecom requires that a
@@ -39,10 +43,12 @@
*
*/
public class CtsRemoteConnectionService extends ConnectionService {
+ private static String LOG_TAG = "CtsConnectionService";
// This is the connection service implemented by the test
private static ConnectionService sConnectionService;
// This is the connection service registered with Telecom
private static ConnectionService sTelecomConnectionService;
+ private static boolean mIsServiceUnbound;
public CtsRemoteConnectionService() throws Exception {
super();
@@ -64,13 +70,14 @@
private static Object sLock = new Object();
public static void setUp(PhoneAccountHandle phoneAccountHandle,
- ConnectionService connectionService)
- throws Exception {
+ ConnectionService connectionService) throws Exception {
synchronized(sLock) {
if (sConnectionService != null) {
throw new Exception("Mock ConnectionService exists. Failed to call tearDown().");
}
sConnectionService = connectionService;
+ // Cant override the onBind method for ConnectionService, so reset it here.
+ mIsServiceUnbound = false;
}
}
@@ -124,4 +131,16 @@
sTelecomConnectionService.addConference(conference);
}
}
+
+ @Override
+ public boolean onUnbind(Intent intent) {
+ Log.i(LOG_TAG, "Service unbounded");
+ assertFalse(mIsServiceUnbound);
+ mIsServiceUnbound = true;
+ return super.onUnbind(intent);
+ }
+
+ public static boolean isServiceUnbound() {
+ return mIsServiceUnbound;
+ }
}
diff --git a/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java b/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java
index 19d27af..02d2f15 100644
--- a/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java
@@ -188,7 +188,7 @@
return;
}
- addAndVerifyNewIncomingCall(getTestNumber(), null);
+ addAndVerifyNewIncomingCall(createTestNumber(), null);
final MockConnection connection = verifyConnectionForIncomingCall();
final MockInCallService inCallService = mInCallCallbacks.getService();
@@ -209,7 +209,7 @@
return;
}
- addAndVerifyNewIncomingCall(getTestNumber(), null);
+ addAndVerifyNewIncomingCall(createTestNumber(), null);
final MockConnection connection = verifyConnectionForIncomingCall();
final MockInCallService inCallService = mInCallCallbacks.getService();
@@ -232,7 +232,7 @@
return;
}
- addAndVerifyNewIncomingCall(getTestNumber(), null);
+ addAndVerifyNewIncomingCall(createTestNumber(), null);
final MockConnection connection = verifyConnectionForIncomingCall();
final MockInCallService inCallService = mInCallCallbacks.getService();
@@ -390,7 +390,7 @@
return;
}
- addAndVerifyNewIncomingCall(getTestNumber(), null);
+ addAndVerifyNewIncomingCall(createTestNumber(), null);
verifyConnectionForIncomingCall();
final MockInCallService inCallService = mInCallCallbacks.getService();
@@ -428,7 +428,7 @@
assertEquals("InCallService.getCalls() should return list with 1 call.", 1, calls.size());
assertEquals(call1, calls.get(0));
- addAndVerifyNewIncomingCall(getTestNumber(), null);
+ addAndVerifyNewIncomingCall(createTestNumber(), null);
verifyConnectionForIncomingCall();
final Call call2 = inCallService.getLastCall();
diff --git a/tests/tests/telecom/src/android/telecom/cts/MockConnection.java b/tests/tests/telecom/src/android/telecom/cts/MockConnection.java
index d556baa..9bb83a1 100644
--- a/tests/tests/telecom/src/android/telecom/cts/MockConnection.java
+++ b/tests/tests/telecom/src/android/telecom/cts/MockConnection.java
@@ -66,6 +66,7 @@
if (mRemoteConnection != null) {
mRemoteConnection.reject();
}
+ destroy();
}
@Override
@@ -90,18 +91,20 @@
public void onDisconnect() {
super.onDisconnect();
setDisconnected(new DisconnectCause(DisconnectCause.LOCAL));
- destroy();
if (mRemoteConnection != null) {
mRemoteConnection.disconnect();
}
+ destroy();
}
@Override
public void onAbort() {
super.onAbort();
+ setDisconnected(new DisconnectCause(DisconnectCause.UNKNOWN));
if (mRemoteConnection != null) {
mRemoteConnection.abort();
}
+ destroy();
}
@Override
diff --git a/tests/tests/telecom/src/android/telecom/cts/MockInCallService.java b/tests/tests/telecom/src/android/telecom/cts/MockInCallService.java
index 1856603..0f1f538 100644
--- a/tests/tests/telecom/src/android/telecom/cts/MockInCallService.java
+++ b/tests/tests/telecom/src/android/telecom/cts/MockInCallService.java
@@ -16,10 +16,14 @@
package android.telecom.cts;
+import static org.junit.Assert.assertFalse;
+
+import android.content.Intent;
import android.telecom.Call;
import android.telecom.CallAudioState;
import android.telecom.InCallService;
import android.util.ArrayMap;
+import android.util.Log;
import java.util.ArrayList;
import java.util.List;
@@ -27,6 +31,7 @@
import java.util.concurrent.Semaphore;
public class MockInCallService extends InCallService {
+ private static String LOG_TAG = "MockInCallService";
private ArrayList<Call> mCalls = new ArrayList<>();
private ArrayList<Call> mConferenceCalls = new ArrayList<>();
private static InCallServiceCallbacks sCallbacks;
@@ -34,6 +39,7 @@
new ArrayMap<Call, MockVideoCallCallback>();
private static final Object sLock = new Object();
+ private static boolean mIsServiceUnbound;
public static abstract class InCallServiceCallbacks {
private MockInCallService mService;
@@ -152,9 +158,11 @@
@Override
public android.os.IBinder onBind(android.content.Intent intent) {
+ Log.i(LOG_TAG, "Service bounded");
if (getCallbacks() != null) {
getCallbacks().setService(this);
}
+ mIsServiceUnbound = false;
return super.onBind(intent);
}
@@ -312,4 +320,16 @@
public MockVideoCallCallback getVideoCallCallback(Call call) {
return mVideoCallCallbacks.get(call);
}
+
+ @Override
+ public boolean onUnbind(Intent intent) {
+ Log.i(LOG_TAG, "Service unbounded");
+ assertFalse(mIsServiceUnbound);
+ mIsServiceUnbound = true;
+ return super.onUnbind(intent);
+ }
+
+ public static boolean isServiceUnbound() {
+ return mIsServiceUnbound;
+ }
}
diff --git a/tests/tests/telecom/src/android/telecom/cts/NumberDialingTest.java b/tests/tests/telecom/src/android/telecom/cts/NumberDialingTest.java
index 8ffcf48..e95a290 100644
--- a/tests/tests/telecom/src/android/telecom/cts/NumberDialingTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/NumberDialingTest.java
@@ -62,7 +62,5 @@
res.wait(CS_WAIT_MILLIS);
}
assertEquals(address, res[0]);
-
- tearDownConnectionService(account.getAccountHandle());
}
}
diff --git a/tests/tests/telecom/src/android/telecom/cts/OutgoingCallTest.java b/tests/tests/telecom/src/android/telecom/cts/OutgoingCallTest.java
index f2422c9..bba9a44 100644
--- a/tests/tests/telecom/src/android/telecom/cts/OutgoingCallTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/OutgoingCallTest.java
@@ -18,6 +18,8 @@
import static android.telecom.cts.TestUtils.shouldTestTelecom;
+import android.content.Context;
+import android.media.AudioManager;
import android.os.Bundle;
import android.telecom.CallAudioState;
import android.telecom.TelecomManager;
@@ -35,15 +37,13 @@
}
}
- // TODO: Need to send some commands to the UserManager via adb to do setup
+ /* TODO: Need to send some commands to the UserManager via adb to do setup
public void testDisallowOutgoingCallsForSecondaryUser() {
+ } */
- }
-
- // TODO: Need to figure out a way to mock emergency calls without adb root
+ /* TODO: Need to figure out a way to mock emergency calls without adb root
public void testOutgoingCallBroadcast_isSentForAllCalls() {
-
- }
+ } */
/**
* Verifies that providing the EXTRA_START_CALL_WITH_SPEAKERPHONE extra starts the call with
@@ -68,11 +68,15 @@
return;
}
+ AudioManager am = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ int expectedRoute = am.isWiredHeadsetOn() ?
+ CallAudioState.ROUTE_WIRED_HEADSET : CallAudioState.ROUTE_EARPIECE;
+
final Bundle extras = new Bundle();
extras.putBoolean(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, false);
placeAndVerifyCall(extras);
verifyConnectionForOutgoingCall();
- assertAudioRoute(mInCallCallbacks.getService(), CallAudioState.ROUTE_EARPIECE);
+ assertAudioRoute(mInCallCallbacks.getService(), expectedRoute);
}
public void testStartCallWithSpeakerphoneNotProvided_SpeakerphoneOffByDefault() {
@@ -80,8 +84,12 @@
return;
}
+ AudioManager am = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ int expectedRoute = am.isWiredHeadsetOn() ?
+ CallAudioState.ROUTE_WIRED_HEADSET : CallAudioState.ROUTE_EARPIECE;
+
placeAndVerifyCall();
verifyConnectionForOutgoingCall();
- assertAudioRoute(mInCallCallbacks.getService(), CallAudioState.ROUTE_EARPIECE);
+ assertAudioRoute(mInCallCallbacks.getService(), expectedRoute);
}
}
diff --git a/tests/tests/telecom/src/android/telecom/cts/RemoteConferenceTest.java b/tests/tests/telecom/src/android/telecom/cts/RemoteConferenceTest.java
index 3fc65ea..787966a 100644
--- a/tests/tests/telecom/src/android/telecom/cts/RemoteConferenceTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/RemoteConferenceTest.java
@@ -61,7 +61,6 @@
@Override
protected void setUp() throws Exception {
super.setUp();
- mContext = getInstrumentation().getContext();
if (mShouldTestTelecom) {
addRemoteConferenceCall();
verifyRemoteConferenceObject(mRemoteConferenceObject, mRemoteConference, mConference);
diff --git a/tests/tests/telecom/src/android/telecom/cts/RemoteConnectionTest.java b/tests/tests/telecom/src/android/telecom/cts/RemoteConnectionTest.java
index 79fb592..5b552a0 100644
--- a/tests/tests/telecom/src/android/telecom/cts/RemoteConnectionTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/RemoteConnectionTest.java
@@ -1210,7 +1210,6 @@
};
mRemoteConnectionObject.registerCallback(callback, handler);
mRemoteConnection.createMockVideoProvider();
- mRemoteConnection.setVideoProvider(mRemoteConnection.getMockVideoProvider());
callbackInvoker.waitForCount(1, WAIT_FOR_STATE_CHANGE_TIMEOUT_MS);
assertEquals(mRemoteConnectionObject, callbackInvoker.getArgs(0)[0]);
mRemoteConnectionObject.unregisterCallback(callback);
diff --git a/tests/tests/telecom/src/android/telecom/cts/TestUtils.java b/tests/tests/telecom/src/android/telecom/cts/TestUtils.java
index 962df59..4936209 100644
--- a/tests/tests/telecom/src/android/telecom/cts/TestUtils.java
+++ b/tests/tests/telecom/src/android/telecom/cts/TestUtils.java
@@ -63,7 +63,8 @@
return false;
}
final PackageManager pm = context.getPackageManager();
- return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
+ return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY) &&
+ pm.hasSystemFeature(PackageManager.FEATURE_CONNECTION_SERVICE);
}
public static String setDefaultDialer(Instrumentation instrumentation, String packageName)
diff --git a/tests/tests/telecom/src/android/telecom/cts/WiredHeadsetTest.java b/tests/tests/telecom/src/android/telecom/cts/WiredHeadsetTest.java
index 466a90b..e7130ba 100644
--- a/tests/tests/telecom/src/android/telecom/cts/WiredHeadsetTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/WiredHeadsetTest.java
@@ -35,21 +35,12 @@
}
}
- @Override
- protected void tearDown() throws Exception {
- if (mInCallCallbacks != null && mInCallCallbacks.getService() != null) {
- mInCallCallbacks.getService().disconnectLastCall();
- assertNumCalls(mInCallCallbacks.getService(), 0);
- }
- super.tearDown();
- }
-
public void testIncomingCallShortPress_acceptsCall() throws Exception {
if (!mShouldTestTelecom) {
return;
}
- addAndVerifyNewIncomingCall(getTestNumber(), null);
+ addAndVerifyNewIncomingCall(createTestNumber(), null);
final MockConnection connection = verifyConnectionForIncomingCall();
final Call call = mInCallCallbacks.getService().getLastCall();
@@ -66,7 +57,7 @@
return;
}
- addAndVerifyNewIncomingCall(getTestNumber(), null);
+ addAndVerifyNewIncomingCall(createTestNumber(), null);
final MockConnection connection = verifyConnectionForIncomingCall();
final Call call = mInCallCallbacks.getService().getLastCall();
@@ -119,16 +110,6 @@
assertConnectionState(connection, Connection.STATE_DISCONNECTED);
}
- public void testStartCallWithSpeakerphoneNotProvided_SpeakerphoneOffByDefault() {
- if (!mShouldTestTelecom) {
- return;
- }
-
- placeAndVerifyCall();
- verifyConnectionForOutgoingCall();
- assertAudioRoute(mInCallCallbacks.getService(), CallAudioState.ROUTE_EARPIECE);
- }
-
private void sendMediaButtonShortPress() throws Exception {
sendMediaButtonPress(false /* longPress */);
}
diff --git a/tests/tests/telephony/src/android/telephony/cts/MmsTest.java b/tests/tests/telephony/src/android/telephony/cts/MmsTest.java
index 176c50c..e15b45f 100644
--- a/tests/tests/telephony/src/android/telephony/cts/MmsTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/MmsTest.java
@@ -23,6 +23,7 @@
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;
@@ -84,6 +85,7 @@
private Random mRandom;
private SentReceiver mSentReceiver;
private TelephonyManager mTelephonyManager;
+ private PackageManager mPackageManager;
private static class SentReceiver extends BroadcastReceiver {
private final Object mLock;
@@ -164,15 +166,26 @@
mRandom = new Random();
mTelephonyManager =
(TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ mPackageManager = mContext.getPackageManager();
}
public void testSendMmsMessage() {
- if (!mTelephonyManager.isSmsCapable()) {
- Log.i(TAG, "testSendMmsMessage skipped: not SMS capable");
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ Log.i(TAG, "testSendMmsMessage skipped: no telephony available");
return;
}
Log.i(TAG, "testSendMmsMessage");
+ // Prime the MmsService so that MMS config is loaded
+ final SmsManager smsManager = SmsManager.getDefault();
+ smsManager.getAutoPersisting();
+ // MMS config is loaded asynchronously. Wait a bit so it will be loaded.
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+
final Context context = getContext();
// Register sent receiver
mSentReceiver = new SentReceiver();
@@ -193,7 +206,7 @@
// Send
final PendingIntent pendingIntent = PendingIntent.getBroadcast(
context, 0, new Intent(ACTION_MMS_SENT), 0);
- SmsManager.getDefault().sendMultimediaMessage(context,
+ smsManager.sendMultimediaMessage(context,
contentUri, null/*locationUrl*/, null/*configOverrides*/, pendingIntent);
assertTrue(mSentReceiver.waitForSuccess(SENT_TIMEOUT));
sendFile.delete();
diff --git a/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java b/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
index 41fe996..8b94d00 100644
--- a/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
@@ -225,7 +225,10 @@
break;
case TelephonyManager.PHONE_TYPE_NONE:
- if (mCm.getNetworkInfo(ConnectivityManager.TYPE_WIFI) != null) {
+ boolean nwSupported = mCm.isNetworkSupported(mCm.TYPE_WIFI);
+ PackageManager packageManager = getContext().getPackageManager();
+ // only check serial number & MAC address if device report wifi feature
+ if (packageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
assertSerialNumber();
assertMacAddress(getWifiMacAddress());
} else if (mCm.getNetworkInfo(ConnectivityManager.TYPE_BLUETOOTH) != null) {
diff --git a/tests/tests/text/src/android/text/format/cts/DateUtilsTest.java b/tests/tests/text/src/android/text/format/cts/DateUtilsTest.java
index 6eb09eb..9ab815f 100644
--- a/tests/tests/text/src/android/text/format/cts/DateUtilsTest.java
+++ b/tests/tests/text/src/android/text/format/cts/DateUtilsTest.java
@@ -94,20 +94,20 @@
final long ONE_SECOND_IN_MS = 1000;
assertEquals("0 minutes ago",
DateUtils.getRelativeTimeSpanString(mBaseTime - ONE_SECOND_IN_MS));
- assertEquals("in 0 minutes",
+ assertEquals("In 0 minutes",
DateUtils.getRelativeTimeSpanString(mBaseTime + ONE_SECOND_IN_MS));
final long ONE_MINUTE_IN_MS = 60 * ONE_SECOND_IN_MS;
assertEquals("1 minute ago", DateUtils.getRelativeTimeSpanString(0, ONE_MINUTE_IN_MS,
DateUtils.MINUTE_IN_MILLIS));
- assertEquals("in 1 minute", DateUtils.getRelativeTimeSpanString(ONE_MINUTE_IN_MS, 0,
+ assertEquals("In 1 minute", DateUtils.getRelativeTimeSpanString(ONE_MINUTE_IN_MS, 0,
DateUtils.MINUTE_IN_MILLIS));
final long ONE_HOUR_IN_MS = 60 * 60 * 1000;
final long TWO_HOURS_IN_MS = 2 * ONE_HOUR_IN_MS;
assertEquals("2 hours ago", DateUtils.getRelativeTimeSpanString(mBaseTime - TWO_HOURS_IN_MS,
mBaseTime, DateUtils.MINUTE_IN_MILLIS, DateUtils.FORMAT_NUMERIC_DATE));
- assertEquals("in 2 hours", DateUtils.getRelativeTimeSpanString(mBaseTime + TWO_HOURS_IN_MS,
+ assertEquals("In 2 hours", DateUtils.getRelativeTimeSpanString(mBaseTime + TWO_HOURS_IN_MS,
mBaseTime, DateUtils.MINUTE_IN_MILLIS, DateUtils.FORMAT_NUMERIC_DATE));
}
diff --git a/tests/tests/text/src/android/text/method/cts/ArrowKeyMovementMethodTest.java b/tests/tests/text/src/android/text/method/cts/ArrowKeyMovementMethodTest.java
index 10d08d0..482edb0 100644
--- a/tests/tests/text/src/android/text/method/cts/ArrowKeyMovementMethodTest.java
+++ b/tests/tests/text/src/android/text/method/cts/ArrowKeyMovementMethodTest.java
@@ -255,7 +255,7 @@
assertFalse(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
KeyEvent.KEYCODE_DPAD_UP, noMetaEvent));
- // |first line
+ // first lin|e
// second line
// last line
assertSelection(0);
diff --git a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
index 3c6028f..6514402 100755
--- a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
+++ b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
@@ -165,6 +165,9 @@
// Wait for things to settle.
getUiDevice().waitForIdle();
+ // Wait for Activity draw finish
+ getInstrumentation().waitForIdleSync();
+
// Clear the window animation stats to be with a clean slate.
uiAutomation.clearWindowAnimationFrameStats();
@@ -177,6 +180,9 @@
// Wait for things to settle.
getUiDevice().waitForIdle();
+ // Wait for Activity draw finish
+ getInstrumentation().waitForIdleSync();
+
// Get the frame stats.
WindowAnimationFrameStats stats = uiAutomation.getWindowAnimationFrameStats();
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/SamplePointVerifier.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/SamplePointVerifier.java
index cb62694..c97e020 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/SamplePointVerifier.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/SamplePointVerifier.java
@@ -28,13 +28,19 @@
*/
public class SamplePointVerifier extends BitmapVerifier {
private static final String TAG = "SamplePoint";
- private Point[] mTestPoints;
- private int[] mExpectedColors;
- private int mTolerance = 20;
+ private static final int DEFAULT_TOLERANCE = 20;
+ private final Point[] mTestPoints;
+ private final int[] mExpectedColors;
+ private final int mTolerance;
public SamplePointVerifier(Point[] testPoints, int[] expectedColors) {
+ this(testPoints, expectedColors, DEFAULT_TOLERANCE);
+ }
+
+ public SamplePointVerifier(Point[] testPoints, int[] expectedColors, int tolerance) {
mTestPoints = testPoints;
mExpectedColors = expectedColors;
+ mTolerance = tolerance;
}
@Override
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java
index 2726dac..38777a2 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java
@@ -16,6 +16,7 @@
package android.uirendering.cts.testclasses;
+import android.content.pm.PackageManager;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
@@ -163,6 +164,9 @@
@SmallTest
public void testWebViewClipWithCircle() {
+ if (!getActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_WEBVIEW)) {
+ return; // no WebView to run test on
+ }
createTest()
// golden client - draw a simple non-AA circle
.addCanvasClient(new CanvasClient() {
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ShadowTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ShadowTests.java
index 2061023..4582935 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ShadowTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ShadowTests.java
@@ -32,6 +32,7 @@
if (getActivity().getOnTv()) {
shadowColorValue = 0xBB;
}
+ // Use a higher threshold (30) than default value (20);
SamplePointVerifier verifier = new SamplePointVerifier(
new Point[] {
// view area
@@ -46,7 +47,8 @@
Color.WHITE,
Color.rgb(shadowColorValue, shadowColorValue, shadowColorValue),
Color.rgb(shadowColorValue, shadowColorValue, shadowColorValue),
- });
+ },
+ 30);
createTest()
.addLayout(R.layout.simple_shadow_layout, null, true/* HW only */)
.runWithVerifier(verifier);
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
index d585f5f..57c67bd 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
@@ -48,7 +48,7 @@
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN);
mHandler = new RenderSpecHandler();
int uiMode = getResources().getConfiguration().uiMode;
- mOnTv = (uiMode & Configuration.UI_MODE_TYPE_TELEVISION) == Configuration.UI_MODE_TYPE_TELEVISION;
+ mOnTv = (uiMode & Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_TELEVISION;
}
public boolean getOnTv() {
diff --git a/tests/tests/view/src/android/view/cts/MotionEventTest.java b/tests/tests/view/src/android/view/cts/MotionEventTest.java
index cdedca4..10ea33a 100644
--- a/tests/tests/view/src/android/view/cts/MotionEventTest.java
+++ b/tests/tests/view/src/android/view/cts/MotionEventTest.java
@@ -180,6 +180,40 @@
assertEquals(mMotionEvent2.getDeviceId(), motionEvent.getDeviceId());
}
+ public void testReadFromParcelWithInvalidPointerCountSize() {
+ Parcel parcel = Parcel.obtain();
+ mMotionEvent2.writeToParcel(parcel, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+
+ // Move to pointer id count.
+ parcel.setDataPosition(4);
+ parcel.writeInt(17);
+
+ parcel.setDataPosition(0);
+ try {
+ MotionEvent.CREATOR.createFromParcel(parcel);
+ fail("deserialized invalid parcel");
+ } catch (RuntimeException e) {
+ // Expected.
+ }
+ }
+
+ public void testReadFromParcelWithInvalidSampleSize() {
+ Parcel parcel = Parcel.obtain();
+ mMotionEvent2.writeToParcel(parcel, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+
+ // Move to sample count.
+ parcel.setDataPosition(2 * 4);
+ parcel.writeInt(0x000f0000);
+
+ parcel.setDataPosition(0);
+ try {
+ MotionEvent.CREATOR.createFromParcel(parcel);
+ fail("deserialized invalid parcel");
+ } catch (RuntimeException e) {
+ // Expected.
+ }
+ }
+
public void testToString() {
// make sure this method never throw exception.
mMotionEvent2.toString();
diff --git a/tests/tests/voiceinteraction/AndroidTest.xml b/tests/tests/voiceinteraction/AndroidTest.xml
index 374c216..fa1ab70 100644
--- a/tests/tests/voiceinteraction/AndroidTest.xml
+++ b/tests/tests/voiceinteraction/AndroidTest.xml
@@ -19,7 +19,5 @@
<option name="cts-apk-installer:test-file-name" value="CtsVoiceInteractionApp.apk" />
<option name="run-command:run-command"
value="settings put secure voice_interaction_service android.voiceinteraction.service/.MainInteractionService" />
- <option name="run-command:teardown-command"
- value="settings put secure voice_interaction_service com.google.android.googlequicksearchbox/com.google.android.voiceinteraction.GsaVoiceInteractionService" />
<option name="cts-apk-installer:test-file-name" value="CtsVoiceInteractionTestCases.apk" />
</configuration>
diff --git a/tests/tests/voiceinteraction/testapp/AndroidManifest.xml b/tests/tests/voiceinteraction/testapp/AndroidManifest.xml
index 1c9ee71..15069ec 100644
--- a/tests/tests/voiceinteraction/testapp/AndroidManifest.xml
+++ b/tests/tests/voiceinteraction/testapp/AndroidManifest.xml
@@ -23,7 +23,7 @@
<activity android:name="TestApp"
android:label="Voice Interaction Test App"
- android:theme="@android:style/Theme.Material.Light">
+ android:theme="@android:style/Theme.DeviceDefault">
<intent-filter>
<action android:name="android.intent.action.TEST_APP" />
<category android:name="android.intent.category.DEFAULT" />
diff --git a/tests/tests/voicesettings/AndroidTest.xml b/tests/tests/voicesettings/AndroidTest.xml
index 0a3974d..e3be691 100644
--- a/tests/tests/voicesettings/AndroidTest.xml
+++ b/tests/tests/voicesettings/AndroidTest.xml
@@ -17,7 +17,5 @@
<option name="cts-apk-installer:test-file-name" value="CtsVoiceSettingsService.apk" />
<option name="run-command:run-command"
value="settings put secure voice_interaction_service android.voicesettings.service/.MainInteractionService" />
- <option name="run-command:teardown-command"
- value="settings put secure voice_interaction_service com.google.android.googlequicksearchbox/com.google.android.voiceinteraction.GsaVoiceInteractionService" />
<option name="cts-apk-installer:test-file-name" value="CtsVoiceSettingsTestCases.apk" />
</configuration>
diff --git a/tests/tests/voicesettings/service/src/android/voicesettings/service/MainInteractionSession.java b/tests/tests/voicesettings/service/src/android/voicesettings/service/MainInteractionSession.java
index c2b7e18..aff1160 100644
--- a/tests/tests/voicesettings/service/src/android/voicesettings/service/MainInteractionSession.java
+++ b/tests/tests/voicesettings/service/src/android/voicesettings/service/MainInteractionSession.java
@@ -24,6 +24,7 @@
import static android.provider.Settings.ACTION_VOICE_CONTROL_BATTERY_SAVER_MODE;
import static android.provider.Settings.EXTRA_BATTERY_SAVER_MODE_ENABLED;
+import android.app.VoiceInteractor;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
@@ -138,9 +139,10 @@
@Override
public void onRequestCompleteVoice(CompleteVoiceRequest request) {
- CharSequence prompt = request.getVoicePrompt().getVoicePromptAt(0);
+ VoiceInteractor.Prompt prompt = request.getVoicePrompt();
+ CharSequence message = (prompt != null ? prompt.getVoicePromptAt(0) : "(none)");
Log.i(TAG, "in Session testcasetype = " + mTestType +
- ", onRequestCompleteVoice recvd. message = " + prompt);
+ ", onRequestCompleteVoice recvd. message = " + message);
AsyncTaskArg asyncTaskArg = new AsyncTaskArg().setRequest(request).setCompleteReq(true);
newTask().execute(asyncTaskArg);
}
diff --git a/tests/tests/voicesettings/src/android/voicesettings/cts/AirplaneModeTest.java b/tests/tests/voicesettings/src/android/voicesettings/cts/AirplaneModeTest.java
index 8abe396..9ce743e 100644
--- a/tests/tests/voicesettings/src/android/voicesettings/cts/AirplaneModeTest.java
+++ b/tests/tests/voicesettings/src/android/voicesettings/cts/AirplaneModeTest.java
@@ -16,6 +16,8 @@
package android.voicesettings.cts;
+import static android.provider.Settings.ACTION_VOICE_CONTROL_AIRPLANE_MODE;
+
import android.provider.Settings;
import android.provider.Settings.Global;
import android.util.Log;
@@ -33,9 +35,20 @@
}
public void testAll() throws Exception {
+ if (!isIntentSupported(ACTION_VOICE_CONTROL_AIRPLANE_MODE)) {
+ Log.e(TAG, "Voice intent for Airplane Mode NOT supported. existing the test");
+ return;
+ }
+ int mode;
+ try {
+ mode = getMode();
+ Log.i(TAG, "Before testing, AIRPLANE_MODE is set to: " + mode);
+ } catch (Settings.SettingNotFoundException e) {
+ // if the mode is not supported, don't run the test.
+ Log.i(TAG, "airplane mode is not found in Settings. Skipping AirplaneModeTest");
+ return;
+ }
startTestActivity("AIRPLANE_MODE");
- int mode = getMode();
- Log.i(TAG, "Before testing, AIRPLANE_MODE is set to: " + mode);
if (mode == AIRPLANE_MODE_IS_OFF) {
// mode is currently OFF.
// run a test to turn it on.
@@ -70,7 +83,7 @@
return true;
}
- private int getMode() throws Exception {
+ private int getMode() throws Settings.SettingNotFoundException {
return Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.AIRPLANE_MODE_ON);
}
diff --git a/tests/tests/voicesettings/src/android/voicesettings/cts/BatterySaverModeTest.java b/tests/tests/voicesettings/src/android/voicesettings/cts/BatterySaverModeTest.java
index 3d1357a..983e27b 100644
--- a/tests/tests/voicesettings/src/android/voicesettings/cts/BatterySaverModeTest.java
+++ b/tests/tests/voicesettings/src/android/voicesettings/cts/BatterySaverModeTest.java
@@ -16,6 +16,8 @@
package android.voicesettings.cts;
+import static android.provider.Settings.ACTION_VOICE_CONTROL_BATTERY_SAVER_MODE;
+
import android.content.Context;
import android.os.PowerManager;
import android.util.Log;
@@ -30,6 +32,10 @@
}
public void testAll() throws Exception {
+ if (!isIntentSupported(ACTION_VOICE_CONTROL_BATTERY_SAVER_MODE)) {
+ Log.e(TAG, "Voice intent for Battery Saver Mode NOT supported. existing the test");
+ return;
+ }
startTestActivity("BATTERYSAVER_MODE");
boolean modeIsOn = isModeOn();
Log.i(TAG, "Before testing, BATTERYSAVER_MODE is set to: " + modeIsOn);
diff --git a/tests/tests/voicesettings/src/android/voicesettings/cts/VoiceSettingsTestBase.java b/tests/tests/voicesettings/src/android/voicesettings/cts/VoiceSettingsTestBase.java
index 5386497..98b9c29 100644
--- a/tests/tests/voicesettings/src/android/voicesettings/cts/VoiceSettingsTestBase.java
+++ b/tests/tests/voicesettings/src/android/voicesettings/cts/VoiceSettingsTestBase.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
import android.os.Bundle;
import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
@@ -53,10 +54,23 @@
@Override
protected void tearDown() throws Exception {
- mContext.unregisterReceiver(mActivityDoneReceiver);
+ if (mActivityDoneReceiver != null) {
+ mContext.unregisterReceiver(mActivityDoneReceiver);
+ }
super.tearDown();
}
+ protected boolean isIntentSupported(String intentStr) {
+ Intent intent = new Intent(intentStr);
+ final PackageManager manager = mContext.getPackageManager();
+ assertNotNull(manager);
+ if (manager.resolveActivity(intent, 0) == null) {
+ Log.i(TAG, "No Voice Activity found for the intent: " + intentStr);
+ return false;
+ }
+ return true;
+ }
+
protected void startTestActivity(String intentSuffix) {
Intent intent = new Intent();
intent.setAction("android.intent.action.TEST_START_ACTIVITY_" + intentSuffix);
diff --git a/tests/tests/voicesettings/src/android/voicesettings/cts/ZenModeTest.java b/tests/tests/voicesettings/src/android/voicesettings/cts/ZenModeTest.java
index 8c2efbe..420da8f 100644
--- a/tests/tests/voicesettings/src/android/voicesettings/cts/ZenModeTest.java
+++ b/tests/tests/voicesettings/src/android/voicesettings/cts/ZenModeTest.java
@@ -16,6 +16,7 @@
package android.voicesettings.cts;
+import static android.provider.Settings.ACTION_VOICE_CONTROL_DO_NOT_DISTURB_MODE;
import static android.provider.Settings.EXTRA_DO_NOT_DISTURB_MODE_ENABLED;
import static android.provider.Settings.EXTRA_DO_NOT_DISTURB_MODE_MINUTES;
@@ -39,9 +40,20 @@
}
public void testAll() throws Exception {
+ if (!isIntentSupported(ACTION_VOICE_CONTROL_DO_NOT_DISTURB_MODE)) {
+ Log.e(TAG, "Voice intent for Zen Mode NOT supported. existing the test");
+ return;
+ }
+ int mode;
+ try {
+ mode = getMode();
+ Log.i(TAG, "Before testing, zen-mode is set to: " + mode);
+ } catch (Settings.SettingNotFoundException e) {
+ // if the mode is not supported, don't run the test.
+ Log.i(TAG, "zen_mode is not found in Settings. Skipping ZenModeTest");
+ return;
+ }
startTestActivity("ZEN_MODE");
- int mode = getMode();
- Log.i(TAG, "Before testing, zen-mode is set to: " + mode);
if (mode == ZEN_MODE_IS_OFF) {
// mode is currently OFF.
// run a test to turn it on.
@@ -85,7 +97,7 @@
return true;
}
- private int getMode() throws Exception {
+ private int getMode() throws Settings.SettingNotFoundException {
return Settings.Global.getInt(mContext.getContentResolver(), ZEN_MODE);
}
}
diff --git a/tests/tests/widget/src/android/widget/cts/AdapterViewTest.java b/tests/tests/widget/src/android/widget/cts/AdapterViewTest.java
index 6a2240e..ccbdd56 100644
--- a/tests/tests/widget/src/android/widget/cts/AdapterViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/AdapterViewTest.java
@@ -317,13 +317,8 @@
//expected
}
- try {
- assertEquals(AdapterView.INVALID_POSITION,
- mAdapterView.getPositionForView(new ImageView(mActivity)));
- fail("Should throw NullPointerException");
- } catch (NullPointerException e) {
- //expected
- }
+ assertEquals(AdapterView.INVALID_POSITION,
+ mAdapterView.getPositionForView(new ImageView(mActivity)));
}
public void testChangeFocusable() {
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildProvider.java b/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildProvider.java
index fa930df..782e6ab 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildProvider.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildProvider.java
@@ -31,7 +31,7 @@
@Option(name="cts-install-path", description="the path to the cts installation to use")
private String mCtsRootDirPath = System.getProperty("CTS_ROOT");
- public static final String CTS_BUILD_VERSION = "6.0_r0";
+ public static final String CTS_BUILD_VERSION = "6.0_r1";
public static final String CTS_PACKAGE = "com.android.cts.tradefed.testtype";
/**
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/device/DeviceInfoCollector.java b/tools/tradefed-host/src/com/android/cts/tradefed/device/DeviceInfoCollector.java
index fe096bd..61561a5 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/device/DeviceInfoCollector.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/device/DeviceInfoCollector.java
@@ -17,6 +17,7 @@
import com.android.cts.util.AbiUtils;
import com.android.cts.tradefed.result.CtsXmlResultReporter;
+import com.android.ddmlib.IDevice;
import com.android.ddmlib.Log;
import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.build.IFolderBuildInfo;
@@ -48,7 +49,7 @@
"com.android.compatibility.common.deviceinfo";
private static final String EXTENDED_INSTRUMENTATION_NAME =
"com.android.compatibility.common.deviceinfo.DeviceInfoInstrument";
- private static final String DEVICE_RESULT_DIR = "/sdcard/device-info-files";
+ private static final String DEVICE_INFO_FILES = "device-info-files";
public static final Set<String> IDS = new HashSet<String>();
public static final Set<String> EXTENDED_IDS = new HashSet<String>();
@@ -86,7 +87,8 @@
ITestInvocationListener listener, IBuildInfo buildInfo)
throws DeviceNotAvailableException {
// Clear files in device test result directory
- device.executeShellCommand(String.format("rm -rf %s", DEVICE_RESULT_DIR));
+ String deviceResultDir = getDeviceResultDir(device);
+ device.executeShellCommand(String.format("rm -rf %s", deviceResultDir));
runInstrumentation(device, abi, testApkDir, listener, EXTENDED_APK_NAME,
EXTENDED_APP_PACKAGE_NAME, EXTENDED_INSTRUMENTATION_NAME);
// Copy files in remote result directory to local directory
@@ -126,9 +128,16 @@
Log.e(LOG_TAG, "Local result directory is null or is not a directory");
return;
}
+
+ localResultDir = new File(localResultDir, DEVICE_INFO_FILES);
+ localResultDir.mkdirs();
+
+
+ String deviceResultDir = getDeviceResultDir(device);
+
// Pull files from device result directory to local result directory
String command = String.format("adb -s %s pull %s %s", device.getSerialNumber(),
- DEVICE_RESULT_DIR, localResultDir.getAbsolutePath());
+ deviceResultDir, localResultDir.getAbsolutePath());
if (!execute(command)) {
Log.e(LOG_TAG, String.format("Failed to run %s", command));
}
@@ -143,4 +152,14 @@
return false;
}
}
+
+ private static String getDeviceResultDir(ITestDevice device) {
+ String externalStorePath = device.getMountPoint(IDevice.MNT_EXTERNAL_STORAGE);
+ if (externalStorePath == null) {
+ Log.e(LOG_TAG, String.format(
+ "Failed to get external storage path on device %s", device.getSerialNumber()));
+ return null;
+ }
+ return String.format("%s/%s", externalStorePath, DEVICE_INFO_FILES);
+ }
}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/result/TestPackageResult.java b/tools/tradefed-host/src/com/android/cts/tradefed/result/TestPackageResult.java
index f5a3d02..45224f6 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/result/TestPackageResult.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/result/TestPackageResult.java
@@ -254,12 +254,12 @@
if (perfResult == null) {
perfResult = CtsHostStore.removeCtsResult(mDeviceSerial, mAbi, test.toString());
}
- if (perfResult != null) {
+ Test result = findTest(test);
+ if (perfResult != null && !result.getResult().equals(CtsTestStatus.FAIL)) {
// CTS result is passed in Summary++++Details format.
// Extract Summary and Details, and pass them.
Matcher m = mCtsLogPattern.matcher(perfResult);
if (m.find()) {
- Test result = findTest(test);
result.setResultStatus(CtsTestStatus.PASS);
result.setSummary(m.group(1));
result.setDetails(m.group(2));
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/DeqpTestRunner.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/DeqpTestRunner.java
index 43aaf98..6f4d42d 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/DeqpTestRunner.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/DeqpTestRunner.java
@@ -63,7 +63,7 @@
private static final BatchRunConfiguration DEFAULT_CONFIG =
new BatchRunConfiguration("rgba8888d24s8", "unspecified", "window");
- private static final int UNRESPOSIVE_CMD_TIMEOUT_MS = 60000; // one minute
+ private static final int UNRESPOSIVE_CMD_TIMEOUT_MS = 10*60*1000; // ten minutes
private final String mPackageName;
private final String mName;
@@ -427,6 +427,7 @@
if (!mGotTestResult) {
result.allInstancesPassed = false;
result.errorMessages.put(mRunConfig, INCOMPLETE_LOG_MESSAGE);
+ CLog.i("Test %s failed as it ended before receiving result.", mCurrentTestId);
}
if (mLogData && mCurrentTestLog != null && mCurrentTestLog.length() > 0) {
@@ -477,6 +478,7 @@
mPendingResults.get(mCurrentTestId)
.errorMessages.put(mRunConfig, codeError + ": " + details);
mGotTestResult = true;
+ CLog.e("Got invalid result code '%s' for test %s", code, mCurrentTestId);
}
}
@@ -950,6 +952,7 @@
private void killDeqpProcess() throws DeviceNotAvailableException,
ProcessKillFailureException {
for (Integer processId : getDeqpProcessPids()) {
+ CLog.i("Killing deqp device process with ID %d", processId);
mDevice.executeShellCommand(String.format("kill -9 %d", processId));
}
@@ -958,6 +961,7 @@
// check that processes actually died
if (getDeqpProcessPids().iterator().hasNext()) {
// a process is still alive, killing failed
+ CLog.w("Failed to kill all deqp processes on device");
throw new ProcessKillFailureException();
}
}
@@ -1336,15 +1340,19 @@
UNRESPOSIVE_CMD_TIMEOUT_MS, TimeUnit.MILLISECONDS);
} catch (TimeoutException ex) {
// Opening connection timed out
+ CLog.e("Opening connection timed out for command: '%s'", command);
throw new AdbComLinkOpenError("opening connection timed out", ex);
} catch (AdbCommandRejectedException ex) {
// Command rejected
+ CLog.e("Device rejected command: '%s'", command);
throw new AdbComLinkOpenError("command rejected", ex);
} catch (IOException ex) {
// shell command channel killed
+ CLog.e("Channel died for command: '%s'", command);
throw new AdbComLinkKilledError("command link killed", ex);
} catch (ShellCommandUnresponsiveException ex) {
// shell command halted
+ CLog.e("No output from command in %d ms: '%s'", UNRESPOSIVE_CMD_TIMEOUT_MS, command);
throw new AdbComLinkKilledError("command link hung", ex);
}
}
@@ -1460,11 +1468,14 @@
// interrupted, try to recover
if (interruptingError != null) {
if (interruptingError instanceof AdbComLinkOpenError) {
+ CLog.i("Recovering from comm link error");
mDeviceRecovery.recoverConnectionRefused();
} else if (interruptingError instanceof AdbComLinkKilledError) {
+ CLog.i("Recovering from comm link killed");
mDeviceRecovery.recoverComLinkKilled();
} else if (interruptingError instanceof RunInterruptedException) {
// external run interruption request. Terminate immediately.
+ CLog.i("Run termination requested. Throwing forward.");
throw (RunInterruptedException)interruptingError;
} else {
CLog.e(interruptingError);
@@ -1473,6 +1484,7 @@
// recoverXXX did not throw => recovery succeeded
} else if (!parser.wasSuccessful()) {
+ CLog.i("Parse not successful. Will attempt comm link recovery.");
mDeviceRecovery.recoverComLinkKilled();
// recoverXXX did not throw => recovery succeeded
}
@@ -1495,8 +1507,10 @@
// This is required so that a consistently crashing or non-existent tests will
// not cause futile (non-terminating) re-execution attempts.
if (mInstanceListerner.getCurrentTestId() != null) {
+ CLog.w("Test '%s' started, but not completed", onlyTest);
mInstanceListerner.abortTest(onlyTest, INCOMPLETE_LOG_MESSAGE);
} else {
+ CLog.w("Test '%s' could not start", onlyTest);
mInstanceListerner.abortTest(onlyTest, NOT_EXECUTABLE_LOG_MESSAGE);
}
} else if (wasTestExecuted) {
@@ -1827,6 +1841,7 @@
uninstallTestApk();
} else {
// Pass all tests if OpenGL ES version is not supported
+ CLog.i("Package %s not supported by the device. Tests trivially pass.", mPackageName);
fakePassTests(listener);
}
} catch (CapabilityQueryFailureException ex) {
diff --git a/tools/utils/buildCts.py b/tools/utils/buildCts.py
index 2b7e093..2eb5145 100755
--- a/tools/utils/buildCts.py
+++ b/tools/utils/buildCts.py
@@ -191,8 +191,6 @@
for package, test_list in small_tests.iteritems():
plan.Include(package+'$')
plan.Exclude(r'com\.android\.cts\.browserbench')
- for package, test_list in temporarily_known_failure_tests.iteritems():
- plan.ExcludeTests(package, test_list)
for package, test_list in flaky_tests.iteritems():
plan.ExcludeTests(package, test_list)
for package, test_list in releasekey_tests.iteritems():
@@ -273,9 +271,6 @@
plan.Exclude(package+'$')
for package, tests_list in new_test_packages.iteritems():
plan.Exclude(package+'$')
- for package, test_list in temporarily_known_failure_tests.iteritems():
- plan.Include(package+'$')
- plan.IncludeTests(package, test_list)
plan.Exclude(r'com\.drawelements\.')
plan.Exclude(r'android\.hardware$')
plan.Exclude(r'android\.media$')
@@ -286,6 +281,15 @@
plan.ExcludeTests(package, test_list)
for package, test_list in releasekey_tests.iteritems():
plan.ExcludeTests(package, test_list)
+ self.__WritePlan(plan, 'CTS-m-tests')
+
+
+ # CTS - sub plan for new test packages added for staging
+ plan = tools.TestPlan(packages)
+ plan.Exclude('.*')
+ for package, test_list in temporarily_known_failure_tests.iteritems():
+ plan.Include(package+'$')
+ plan.IncludeTests(package, test_list)
self.__WritePlan(plan, 'CTS-staging')
plan = tools.TestPlan(packages)
@@ -461,11 +465,47 @@
""" Construct a defaultdict that maps package name to a list of tests
that are known failures during dev cycle but expected to be fixed before launch """
return {
- 'android.bluetooth' : [
- 'android.bluetooth.cts.BluetoothLeScanTest#testBasicBleScan',
- 'android.bluetooth.cts.BluetoothLeScanTest#testBatchScan',
- 'android.bluetooth.cts.BluetoothLeScanTest#testOpportunisticScan',
- 'android.bluetooth.cts.BluetoothLeScanTest#testScanFilter',],
+ 'android.alarmclock' : [
+ 'android.alarmclock.cts.DismissAlarmTest#testAll',
+ 'android.alarmclock.cts.SetAlarmTest#testAll',
+ 'android.alarmclock.cts.SnoozeAlarmTest#testAll',
+ ],
+ 'android.assist' : [
+ 'android.assist.cts.AssistantContentViewTest',
+ 'android.assist.cts.ExtraAssistDataTest',
+ 'android.assist.cts.FocusChangeTest',
+ 'android.assist.cts.LargeViewHierarchyTest',
+ 'android.assist.cts.ScreenshotTest',
+ 'android.assist.cts.TextViewTest',
+ 'android.assist.cts.WebViewTest',
+ ],
+ 'android.calllog' : [
+ 'android.calllog.cts.CallLogBackupTest#testSingleCallBackup',
+ ],
+ 'android.dumpsys' : [
+ 'android.dumpsys.cts.DumpsysHostTest#testBatterystatsOutput',
+ 'android.dumpsys.cts.DumpsysHostTest#testGfxinfoFramestats',
+ ],
+ 'android.telecom' : [
+ 'android.telecom.cts.ExtendedInCallServiceTest#testAddNewOutgoingCallAndThenDisconnect',
+ 'android.telecom.cts.RemoteConferenceTest#testRemoteConferenceCallbacks_ConferenceableConnections',
+ ],
+ 'android.transition' : [
+ 'android.transition.cts.ChangeScrollTest#testChangeScroll',
+ ],
+ 'android.voicesettings' : [
+ 'android.voicesettings.cts.ZenModeTest#testAll',
+ ],
+ 'com.android.cts.systemui' : [
+ 'com.android.cts.systemui.LightStatusBarTests#testLightStatusBarIcons',
+ ],
+ 'com.android.cts.app.os' : [
+ 'com.android.cts.app.os.OsHostTests#testNonExportedActivities',
+ ],
+ 'com.android.cts.devicepolicy' : [
+ 'com.android.cts.devicepolicy.MixedDeviceOwnerTest#testPackageInstallUserRestrictions',
+ 'com.android.cts.devicepolicy.MixedProfileOwnerTest#testPackageInstallUserRestrictions',
+ ],
'' : []}
def LogGenerateDescription(name):