Merge "CTS: Add testConnectivityConstraintExecutes_withMobile to knownfailures" into marshmallow-cts-dev
diff --git a/CtsBuild.mk b/CtsBuild.mk
index c745885..ba158ce 100644
--- a/CtsBuild.mk
+++ b/CtsBuild.mk
@@ -40,6 +40,10 @@
$(foreach executable,$(1),$(CTS_TESTCASES_OUT)/$(executable))
endef
+define cts-get-deqp-api-test-xmls
+ $(foreach file,$(call find-files-in-subdirs, external/deqp/android/cts/master, 'com.drawelements.deqp.$(1).*xml', .),$(CTS_TESTCASES_OUT)/$(file))
+endef
+
define cts-get-deqp-test-xmls
- $(foreach api,$(1),$(CTS_TESTCASES_OUT)/com.drawelements.deqp.$(api).xml)
+ $(foreach api,$(1),$(call cts-get-deqp-api-test-xmls,$(api)))
endef
diff --git a/CtsTestCaseList.mk b/CtsTestCaseList.mk
index 4d30dda..7378d9e 100644
--- a/CtsTestCaseList.mk
+++ b/CtsTestCaseList.mk
@@ -100,7 +100,9 @@
CtsManagedProfileApp \
CtsMonkeyApp \
CtsMonkeyApp2 \
+ CtsPackageInstallerApp \
CtsPermissionApp \
+ CtsPreconditionsApp \
CtsSimpleApp \
CtsSimplePreMApp \
CtsSomeAccessibilityServices \
@@ -149,6 +151,7 @@
CtsBluetoothTestCases \
CtsCalendarcommon2TestCases \
CtsCallLogTestCases \
+ CtsCameraTestCases \
CtsContentTestCases \
CtsDatabaseTestCases \
CtsDisplayTestCases \
@@ -176,6 +179,7 @@
CtsNdefTestCases \
CtsNetTestCases \
CtsNetTestCasesLegacyApi22 \
+ CtsNetTestCasesLegacyPermission22 \
CtsOpenGLTestCases \
CtsOpenGlPerfTestCases \
CtsOsTestCases \
@@ -192,6 +196,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/scene0/test_metadata.py b/apps/CameraITS/tests/scene0/test_metadata.py
index 375a6af..44663bb 100644
--- a/apps/CameraITS/tests/scene0/test_metadata.py
+++ b/apps/CameraITS/tests/scene0/test_metadata.py
@@ -72,8 +72,6 @@
check('props.has_key("android.scaler.croppingType")')
check('props["android.scaler.croppingType"] is not None')
check('props["android.scaler.croppingType"] in [0,1]')
- if full:
- check('props["android.scaler.croppingType"] == 1')
assert(not failed)
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..a752519 100644
--- a/apps/CtsVerifier/Android.mk
+++ b/apps/CtsVerifier/Android.mk
@@ -41,8 +41,6 @@
LOCAL_PACKAGE_NAME := CtsVerifier
-LOCAL_AAPT_FLAGS += --version-name "6.0_r0 $(BUILD_NUMBER)"
-
LOCAL_JNI_SHARED_LIBRARIES := libctsverifier_jni libaudioloopback_jni
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
@@ -53,6 +51,32 @@
include $(BUILD_PACKAGE)
+# Build CTS verifier framework as a libary.
+
+include $(CLEAR_VARS)
+
+define java-files-in
+$(sort $(patsubst ./%,%, \
+ $(shell cd $(LOCAL_PATH) ; \
+ find -L $(1) -maxdepth 1 -name *.java -and -not -name ".*") \
+ ))
+endef
+
+LOCAL_MODULE := cts-verifier-framework
+LOCAL_AAPT_FLAGS := --auto-add-overlay --extra-packages android.support.v4
+LOCAL_SDK_VERSION := current
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_SRC_FILES := \
+ $(call java-files-in, src/com/android/cts/verifier) \
+ $(call java-files-in, src/com/android/cts/verifier/backup) \
+ $(call all-java-files-under, src/android) \
+ $(call all-Iaidl-files-under, src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 \
+ compatibility-common-util-devicesidelib_v2 \
+ compatibility-device-util_v2 \
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
# opencv library
include $(CLEAR_VARS)
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 217913c..a29e594 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -17,7 +17,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.cts.verifier"
- android:versionCode="5">
+ android:versionCode="5"
+ android:versionName="6.0_r2">
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="23"/>
@@ -109,6 +110,26 @@
<!-- A generic activity for intent based tests -->
<activity android:name=".IntentDrivenTestActivity"/>
+ <activity android:name=".admin.DeviceAdminKeyguardDisabledFeaturesActivity"
+ android:label="@string/da_kg_disabled_features_test"
+ android:configChanges="keyboardHidden|orientation|screenSize">
+ <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_device_admin" />
+ </activity>
+
+ <activity android:name=".admin.RedactedNotificationKeyguardDisabledFeaturesActivity"
+ android:label="@string/rn_kg_disabled_features_test"
+ android:configChanges="keyboardHidden|orientation|screenSize">
+ <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_device_admin" />
+ </activity>
+
<activity android:name=".admin.ScreenLockTestActivity"
android:label="@string/da_screen_lock_test"
android:configChanges="keyboardHidden|orientation|screenSize">
@@ -121,15 +142,6 @@
android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch" />
</activity>
- <receiver android:name=".admin.TestDeviceAdminReceiver"
- 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>
-
<activity android:name=".backup.BackupTestActivity" android:label="@string/backup_test">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -363,6 +375,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 +766,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 +775,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"
@@ -1380,6 +1391,21 @@
android:label="@string/device_owner_wifi_lockdown_test">
</activity>
+ <activity android:name=".managedprovisioning.VpnTestActivity"
+ android:label="@string/device_owner_vpn_test">
+ <intent-filter>
+ <action android:name="com.android.cts.verifier.managedprovisioning.VPN" />
+ <category android:name="android.intent.category.DEFAULT"></category>
+ </intent-filter>
+ </activity>
+
+ <service android:name=".managedprovisioning.VpnTestActivity$MyTestVpnService"
+ android:permission="android.permission.BIND_VPN_SERVICE">
+ <intent-filter>
+ <action android:name="android.net.VpnService"/>
+ </intent-filter>
+ </service>
+
<activity android:name=".managedprovisioning.PermissionLockdownTestActivity"
android:label="@string/device_profile_owner_permission_lockdown_test">
<intent-filter>
@@ -1397,6 +1423,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 +1457,12 @@
<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" />
+ <action android:name="com.android.cts.verifier.managedprovisioning.BYOD_SET_LOCATION_AND_CHECK" />
+ <action android:name="com.android.cts.verifier.managedprovisioning.NOTIFICATION" />
+ <action android:name="com.android.cts.verifier.managedprovisioning.LOCKSCREEN_NOTIFICATION" />
+ <action android:name="com.android.cts.verifier.managedprovisioning.CLEAR_NOTIFICATION" />
<category android:name="android.intent.category.DEFAULT"></category>
</intent-filter>
</activity>
@@ -1451,8 +1490,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>
@@ -1464,15 +1582,6 @@
</intent-filter>
</activity>
- <activity android:name=".managedprovisioning.WorkNotificationTestActivity">
- <intent-filter>
- <action android:name="com.android.cts.verifier.managedprovisioning.WORK_NOTIFICATION" />
- <action android:name="com.android.cts.verifier.managedprovisioning.LOCKSCREEN_NOTIFICATION" />
- <action android:name="com.android.cts.verifier.managedprovisioning.CLEAR_WORK_NOTIFICATION" />
- <category android:name="android.intent.category.DEFAULT"></category>
- </intent-filter>
- </activity>
-
<receiver android:name=".managedprovisioning.DeviceAdminTestReceiver"
android:label="@string/afw_device_admin"
android:permission="android.permission.BIND_DEVICE_ADMIN">
@@ -1570,6 +1679,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,30 +1730,46 @@
<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_out_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" />
- -->
+ <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"
android:label="@string/audio_loopback_test">
<intent-filter>
@@ -1641,6 +1778,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..ceedf1c 100644
--- a/apps/CtsVerifier/res/layout/audio_dev_notify.xml
+++ b/apps/CtsVerifier/res/layout/audio_dev_notify.xml
@@ -13,19 +13,51 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ 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:padding="10dip"
+ android:padding="20dp"
android:orientation="vertical">
- <TextView
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:scrollbars="vertical"
+ android:gravity="bottom"
+ android:id="@+id/audio_general_headset_port_exists"
+ android:text="@string/audio_general_headset_port_exists" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/audio_general_headset_no"
+ android:text="@string/audio_general_headset_no" />
+
+ <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_devices_notification_instructions" />
+ android:id="@+id/info_text"/>
<LinearLayout
android:layout_width="match_parent"
@@ -51,4 +83,5 @@
<include layout="@layout/pass_fail_buttons" />
-</LinearLayout>
\ No newline at end of file
+</LinearLayout>
+</ScrollView>
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 61%
copy from apps/CtsVerifier/res/layout/audio_routingnotifications_test.xml
copy to apps/CtsVerifier/res/layout/audio_input_routingnotifications_test.xml
index cef30d6..60a12ef 100644
--- a/apps/CtsVerifier/res/layout/audio_routingnotifications_test.xml
+++ b/apps/CtsVerifier/res/layout/audio_input_routingnotifications_test.xml
@@ -14,52 +14,51 @@
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ 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:padding="10dip"
+ android:padding = "20dp"
android:orientation="vertical">
- <TextView
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:scrollbars="vertical"
+ android:gravity="bottom"
+ android:id="@+id/audio_general_headset_port_exists"
+ android:text="@string/audio_general_headset_port_exists" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/audio_general_headset_no"
+ android:text="@string/audio_general_headset_no" />
+
+ <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_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"
@@ -96,4 +95,5 @@
<include layout="@layout/pass_fail_buttons" />
-</LinearLayout>
\ No newline at end of file
+</LinearLayout>
+</ScrollView>
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 61%
rename from apps/CtsVerifier/res/layout/audio_routingnotifications_test.xml
rename to apps/CtsVerifier/res/layout/audio_output_routingnotifications_test.xml
index cef30d6..d039691 100644
--- a/apps/CtsVerifier/res/layout/audio_routingnotifications_test.xml
+++ b/apps/CtsVerifier/res/layout/audio_output_routingnotifications_test.xml
@@ -14,19 +14,52 @@
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ 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:padding="10dip"
+ android:padding="20dp"
android:orientation="vertical">
- <TextView
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:scrollbars="vertical"
+ android:gravity="bottom"
+ android:id="@+id/audio_general_headset_port_exists"
+ android:text="@string/audio_general_headset_port_exists" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/audio_general_headset_no"
+ android:text="@string/audio_general_headset_no" />
+
+ <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_dev_routingnotification_instructions" />
+ android:text="@string/audio_output_routingnotification_instructions" />
<LinearLayout
android:layout_width="match_parent"
@@ -61,39 +94,7 @@
</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
+</LinearLayout>
+</ScrollView>
diff --git a/apps/CtsVerifier/res/layout/js_charging.xml b/apps/CtsVerifier/res/layout/js_charging.xml
index 8d9ed1d..2888714 100644
--- a/apps/CtsVerifier/res/layout/js_charging.xml
+++ b/apps/CtsVerifier/res/layout/js_charging.xml
@@ -29,30 +29,29 @@
android:text="@string/js_start_test_text"
android:onClick="startTest"
android:enabled="false"/>
-
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/js_padding"
- android:layout_marginBottom="@dimen/js_padding">
- <ImageView
- android:id="@+id/charging_off_test_image"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/fs_indeterminate"
- android:layout_marginRight="@dimen/js_padding"/>
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/js_charging_off_test"
- android:textSize="16dp"/>
- </LinearLayout>
<TextView
+ android:id="@+id/js_waiting_for_charging_text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/js_padding"
- android:text="@string/js_charging_description_2"
- android:textStyle="bold"/>
+ android:text="@string/js_charging_description_3"
+ android:textStyle="bold"
+ android:visibility="gone"/>
+ <com.android.cts.verifier.TimerProgressBar
+ android:id="@+id/js_waiting_for_charging_progress_bar"
+ style="?android:attr/progressBarStyleHorizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="@dimen/js_padding"
+ android:visibility="gone"/>
+ <TextView
+ android:id="@+id/js_problem_with_charger_text_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="@dimen/js_padding"
+ android:text="@string/js_charging_description_4"
+ android:textStyle="bold"
+ android:visibility="gone"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -70,6 +69,29 @@
android:text="@string/js_charging_on_test"
android:textSize="16dp"/>
</LinearLayout>
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="@dimen/js_padding"
+ android:text="@string/js_charging_description_2"
+ android:textStyle="bold"/>
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/js_padding"
+ android:layout_marginBottom="@dimen/js_padding">
+ <ImageView
+ android:id="@+id/charging_off_test_image"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/fs_indeterminate"
+ android:layout_marginRight="@dimen/js_padding"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/js_charging_off_test"
+ android:textSize="16dp"/>
+ </LinearLayout>
<include layout="@layout/pass_fail_buttons" />
</LinearLayout>
</ScrollView>
diff --git a/apps/CtsVerifier/res/layout/js_idle.xml b/apps/CtsVerifier/res/layout/js_idle.xml
index 4277173..5289b98 100644
--- a/apps/CtsVerifier/res/layout/js_idle.xml
+++ b/apps/CtsVerifier/res/layout/js_idle.xml
@@ -15,13 +15,6 @@
android:layout_height="wrap_content"
android:text="@string/js_test_description"
android:layout_margin="@dimen/js_padding"/>
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/js_idle_description_1"
- android:layout_margin="@dimen/js_padding"
- android:textStyle="bold"/>
-
<Button
android:id="@+id/js_idle_start_test_button"
android:layout_width="wrap_content"
@@ -30,6 +23,14 @@
android:text="@string/js_start_test_text"
android:onClick="startTest"
android:enabled="false"/>
+ <TextView
+ android:id="@+id/js_idle_continue_instruction_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/js_idle_continue_instruction"
+ android:layout_margin="@dimen/js_padding"
+ android:textStyle="bold"
+ android:visibility="gone"/>
<LinearLayout
android:layout_width="wrap_content"
diff --git a/apps/CtsVerifier/res/layout/keychain_main.xml b/apps/CtsVerifier/res/layout/keychain_main.xml
index 01eb2558..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/layout/vpn_test.xml b/apps/CtsVerifier/res/layout/vpn_test.xml
new file mode 100644
index 0000000..83c0c1b
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/vpn_test.xml
@@ -0,0 +1,36 @@
+<?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:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="320dp"
+ android:layout_weight="2">
+ <TextView
+ android:id="@+id/device_owner_vpn_info"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="10dip"
+ android:text="@string/device_owner_vpn_info_default"
+ android:textSize="18dip" />
+ </ScrollView>
+
+ <include layout="@layout/pass_fail_buttons" />
+
+</LinearLayout>
diff --git a/apps/CtsVerifier/res/layout/wifi_lockdown.xml b/apps/CtsVerifier/res/layout/wifi_lockdown.xml
index ae6ea0c..2ac337e 100644
--- a/apps/CtsVerifier/res/layout/wifi_lockdown.xml
+++ b/apps/CtsVerifier/res/layout/wifi_lockdown.xml
@@ -16,8 +16,7 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- >
+ android:layout_height="match_parent">
<ScrollView
android:layout_width="match_parent"
@@ -74,4 +73,4 @@
<include layout="@layout/pass_fail_buttons" />
-</LinearLayout>
\ No newline at end of file
+</LinearLayout>
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index c5469a7..6c2e333 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -124,6 +124,8 @@
settings that may specify a timeout.\n\nClick the \"Force Lock\" button to lock the screen.
Your screen should be locked and require the password to be entered.
</string>
+ <string name="da_kg_disabled_features_test">Keyguard Disabled Features Test</string>
+ <string name="rn_kg_disabled_features_test">Redacted Notifications Keyguard Disabled Features Test</string>
<string name="da_force_lock">Force Lock</string>
<string name="da_lock_success">It appears the screen was locked successfully!</string>
<string name="da_lock_error">It does not look like the screen was locked...</string>
@@ -339,8 +341,7 @@
<string name="hifi_ultrasound_test_default_false_string">false</string>
<string name="hifi_ultrasound_test_mic_no_support">
Device does not support near-ultrasound recording.\n
- All new phones and tablets MUST support near-ultrasound recording.\n
- Report FAIL if this is a new device, report PASS if this is an updating device.\n</string>
+ Please report PASS.\n</string>
<string name="hifi_ultrasound_test_spkr_no_support">
Device does not support near-ultrasound playback.\n
If this is your reference device, please use a different reference device.\n</string>
@@ -360,8 +361,7 @@
If this is your reference device, please use a different reference device.\n</string>
<string name="hifi_ultrasound_speaker_test_spkr_no_support">
Device does not support near-ultrasound playback.\n
- All new phones and tablets MUST support near-ultrasound playback.\n
- Report FAIL if this is a new device, report PASS if this is an updating device.\n</string>
+ Please report PASS.\n</string>
<string name="hifi_ultrasound_speaker_test_test_side">
Please wait for the result on the reference device then report here.</string>
<string name="hifi_ultrasound_speaker_test_reference_side">
@@ -1277,7 +1277,26 @@
<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>
+ <string name="provisioning_byod_vpn">Vpn test</string>
<!-- Strings for DeskClock -->
<string name="deskclock_tests">Alarms and Timers Tests</string>
<string name="deskclock_tests_info">
@@ -1406,8 +1425,29 @@
<string name="snsr_rotation_vector_set_final">Place the device back to the reference position.</string>
<string name="snsr_rotation_vector_verification">Angular deviation [%1$4.1f %2$4.1f %3$4.1f]. Current: %4$f deg. Max tolerated: %5$f.</string>
+ <!-- Strings for device admin tests -->
+ <string name="device_admin_notification">This is device admin notification</string>
+ <string name="device_admin_keyguard_disable_camera">Disable camera</string>
+ <string name="device_admin_keyguard_disable_camera_instruction">
+ Please press the Go button to lock the screen. Then try to open the camera
+ from the lower right corner of the screen. Expected result is you cannot
+ open the camera from lock screen and it will ask for password instead.
+ </string>
+ <string name="device_admin_disable_notifications">Disable notifications</string>
+ <string name="device_admin_disable_notifications_instruction">
+ Please press the Go button to lock the screen. Wait a few seconds to see
+ if a notification appears. Expected result is no notifications appear.
+ You should be able to see one after unlocking.
+ </string>
+ <string name="device_admin_disable_unredacted_notifications">Disable unredacted notifications</string>
+ <string name="device_admin_disable_unredacted_notifications_instruction">
+ Please press the Go button to lock the screen. Wait a few seconds to see
+ if a notification appears. Expected result is a notification appear with
+ its content hidden. You should be able to see the content after unlocking.
+ </string>
+
<!-- Strings common for BYOD and DO managed provisioning tests. -->
- <string name="afw_device_admin">CTS Verifier - AfW Admin</string>
+ <string name="afw_device_admin">CTS Verifier</string>
<!-- Strings for BYOD managed provisioning tests (ByodFlowTestActivity) -->
<string name="test_category_managed_provisioning">Managed Provisioning</string>
@@ -1436,12 +1476,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 +1490,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\" 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
@@ -1468,8 +1532,9 @@
<string name="provisioning_byod_fingerprint_disabled_in_settings">Fingerprint is disabled in Settings</string>
<string name="provisioning_byod_fingerprint_disabled_in_settings_instruction">
Please press the Go button to go to Settings > Security. Then go to Fingerprint and\n
- check if the screen is shown as disabled by the administrator.
- Then please press Back and mark the test as \"Pass\" or \"Fail\".
+ check if the disclaimer at the bottom of screen is altered to warn the users for\n
+ fingerprint being disabled in lock screen. Then please press Back and mark the \n
+ test as \"Pass\" or \"Fail\".
</string>
<string name="provisioning_byod_disable_fingerprint">Fingerprint disabled on keyguard</string>
<string name="provisioning_byod_disable_fingerprint_instruction">
@@ -1477,12 +1542,12 @@
Expected result is you cannot log in using your fingerprint.\n
After you log back in, please navigate back to CtsVerifier and mark the test as \"Pass\" or \"Fail\".
</string>
- <string name="provisioning_byod_disable_notifications">Notifications disabled on keyguard</string>
- <string name="provisioning_byod_disable_notifications_instruction">
+ <string name="provisioning_byod_disable_unredacted_notifications">Unredacted notifications disabled on keyguard</string>
+ <string name="provisioning_byod_disable_unredacted_notifications_instruction">
Please press the Go button to lock the screen. Wait a couple of seconds and look out for a
notification from CtsVerifier.\n
Expected result is the notification is shown as \"Contents hidden\", you can not see the contents
- (Which would read \"This is a work notification\").\n
+ (Which would read \"This is a notification\"). You should be seeing a work badge.\n
After you log back in, please navigate back to CtsVerifier and mark the test as \"Pass\" or \"Fail\".
</string>
<string name="provisioning_byod_work_notification">Work notification is badged</string>
@@ -1491,7 +1556,7 @@
\n
Verify that the notification is badged (see sample badge below). Then mark this test accordingly.
</string>
- <string name="provisioning_byod_work_notification_title">This is a work notification</string>
+ <string name="provisioning_byod_notification_title">This is a notification</string>
<string name="provisioning_byod_work_status_icon">Work status icon is displayed</string>
<string name="provisioning_byod_work_status_icon_instruction">
Verify that the current status bar does not have a work status icon (see sample icon below).
@@ -1525,6 +1590,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">
@@ -1532,7 +1601,7 @@
Navigate to Device administrators and confirm that:\n
\n
- Both Personal and Work categories exist.\n
- - \"CTS Verifier - AfW Admin\" exists under the Work category, and is activated.\n
+ - \"CTS Verifier\" exists under the Work category, and is activated.\n
\n
Use the Back button to return to this page.
</string>
@@ -1632,6 +1701,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>
@@ -1639,6 +1709,27 @@
<string name="provisioning_button_finish">Finish</string>
<string name="provisioning_cross_profile_chooser">Choose an app to complete action</string>
+ <string name="provisioning_byod_no_gps_location_feature">No GPS feature present. Skip test.</string>
+ <string name="provisioning_byod_location_mode_enable">Enable location</string>
+ <string name="provisioning_byod_location_mode_enable_toast_location_change">Location changed</string>
+ <string name="provisioning_byod_location_mode_enable_instruction">
+ This test verifies that the location updates can be enabled for the managed profile apps.\n
+ 1. Press the go button to go to the location settings page, set the location switch enabled.\n
+ 2. Move your position a little bit, verify that location updates toast comes up.\n
+ Please wait until the location updates or timeout toast message shows up before going back to the cts-verifier tests.\n
+ 3. Go back to the cts-verifier tests using the back button, then mark the test accordingly.\n
+ </string>
+
+ <string name="provisioning_byod_location_mode_disable">Disable location</string>
+ <string name="provisioning_byod_location_mode_time_out_toast">Timeout waiting for gps location change</string>
+ <string name="provisioning_byod_location_mode_disable_instruction">
+ This test verifies that the location updates can be disabled for the managed profile apps.\n
+ 1. Press the go button to go to the location settings page, set the location switch disabled.\n
+ 2. Move your position a little bit, verify that no location updates toast come up and that the timeout message show up after around 15 seconds.
+ Please wait until the timeout or location updates toast message shows up before going back to the cts-verifier tests.\n
+ 3. Go back to the cts-verifier tests using the back button, then mark the test accordingly.\n
+ </string>
+
<!-- Strings for DeviceOwnerProvisioningTest -->
<string name="provisioning_device_owner">Device Owner Provisioning</string>
<string name="device_owner_provisioning_tests">Device Owner provisioning tests</string>
@@ -1685,7 +1776,7 @@
<string name="device_owner_wifi_lockdown_info">
Please enter the SSID and auth method of an available WiFi Access Point and press the button to create a
WiFi configuration. This configuration can be seen on Settings > WiFi. The test cases
- are going use this config. Please go through test cases in order (from top to bottom).
+ are going to use this config. Please go through test cases in order (from top to bottom).
</string>
<string name="switch_wifi_lockdown_off_button">WiFi config lockdown off</string>
<string name="switch_wifi_lockdown_on_button">WiFi config lockdown on</string>
@@ -1777,8 +1868,8 @@
Please press the Go button to open the Security page in Settings.
Navigate to Device administrators and confirm that:\n
\n
- - \"CTS Verifier - AfW Admin\" exists and is activated.\n
- - \"CTS Verifier - AfW Admin\" cannot be disabled.\n
+ - \"CTS Verifier\" exists and is activated.\n
+ - \"CTS Verifier\" cannot be disabled.\n
\n
Use the Back button to return to this page.
</string>
@@ -1807,6 +1898,42 @@
<string name="device_owner_user_restriction_set">Set restriction</string>
<string name="device_owner_settings_go">Go</string>
+ <string name="device_owner_vpn_connection">
+ Vpn connection has been established.\n
+ This is not as expected.\n
+ Mark this test as failed.\n
+ </string>
+ <string name="device_owner_vpn_connection_close_failed">
+ Established vpn connection cannot be closed.\n
+ This is not as expected.\n
+ Mark this test as failed.\n
+ </string>
+ <string name="device_owner_no_vpn_connection">
+ Cannot establish a VPN connection.\n
+ This was expected.\n
+ Mark this test as passed.\n
+ </string>
+ <string name="device_owner_vpn_connection_canceled">
+ Cannot establish a VPN connection.\n
+ Connection canceled by user.\n
+ </string>
+ <string name="device_owner_vpn_test">Check VPN</string>
+ <string name="device_owner_vpn_info_default">Vpn test message</string>
+
+ <string name="device_owner_disallow_config_vpn">Disallow configuring VPN</string>
+ <string name="device_owner_disallow_config_vpn_info">
+ Please press the Set VPN restriction button to set the VPN restriction.
+ Perform tests in order. Mark test as passed if both test cases pass\n\n
+ 1. Press Go to open the Vpn settings page.\n
+ Confirm that:\n
+ - You cannot add a new VPN network.\n
+ - You cannot edit, add or remove any existing VPNs.\n\n
+ 2. Press Check VPN to check programmatic Vpn test.\n
+ - Check Vpn setup\n\n
+ Use the Back button to return to this page.
+ </string>
+ <string name="device_owner_user_vpn_restriction_set">Set VPN restriction</string>
+
<!-- Strings for JobScheduler Tests -->
<string name="js_test_description">This test is mostly automated, but requires some user interaction. You can pass this test once the list items below are checked.</string>
@@ -1814,15 +1941,20 @@
<string name="js_start_test_text">Start test</string>
<string name="js_idle_instructions">Verify the behaviour of the JobScheduler API for when the device is in idle mode. Simply follow the on-screen instructions.</string>
<string name="js_idle_description_1">Turn the screen off and then back on in order to begin.</string>
+ <string name="js_idle_continue_instruction">
+ Switch off screen and wait for it to turn on to continue.
+ </string>
<string name="js_idle_item_idle_off">Idle job does not execute when device is not idle.</string>
<string name="js_idle_item_idle_on">Idle job does execute when device is forced into idle.</string>
<string name="js_charging_test">Charging Constraints</string>
<string name="js_charging_instructions">Verify the behaviour of the JobScheduler API for when the device is on power and unplugged from power. Simply follow the on-screen instructions.</string>
- <string name="js_charging_description_1">Unplug the device in order to begin.</string>
+ <string name="js_charging_description_1">Plug in the charger if it isn\'t already plugged in.</string>
<string name="js_charging_off_test">Device not charging will not execute a job with a charging constraint.</string>
<string name="js_charging_on_test">Device when charging will execute a job with a charging constraint.</string>
- <string name="js_charging_description_2">After the above test has passed, plug the device back in to continue. If the above failed, you can simply fail this test.</string>
+ <string name="js_charging_description_2">After the above test has passed, remove the charger to continue. If the above failed, you can simply fail this test.</string>
+ <string name="js_charging_description_3">Device is plugged in. Please wait while it get\s into stable charging state.</string>
+ <string name="js_charging_description_4">There seems to be a problem with your charger. Pleasy try again.</string>
<string name="js_connectivity_test">Connectivity Constraints</string>
<string name="js_connectivity_instructions">Verify the behaviour of the JobScheduler API for when the device has no access to data connectivity. Simply follow the on-screen instructions.</string>
@@ -1867,7 +1999,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 +2087,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 +2125,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 +2163,23 @@
<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>
+ <string name="audio_general_deficiency_found">WARNING: Some results show potential deficiencies on the system.
+ Please consider addressing them for a future release.</string>
+ <string name="audio_general_test_passed">Test Successful</string>
+ <string name="audio_general_test_failed">Test Failed</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 +2214,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..167fd84 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;
@@ -96,11 +98,6 @@
*/
protected abstract void setupTests(ArrayTestListAdapter adapter);
- // Enable Pass Button when all tests passed.
- private void updatePassButton() {
- getPassButton().setEnabled(mAdapter.allTestsPassed());
- }
-
public class DefaultTestCallback implements DialogTestListItem.TestCallback {
final private DialogTestListItem mTest;
@@ -170,7 +167,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);
+ }
}
}
@@ -185,6 +188,7 @@
try {
startActivity(intent);
} catch (ActivityNotFoundException e) {
+ Log.w(TAG, "Cannot start activity.", e);
Toast.makeText(this, "Cannot start " + intent, Toast.LENGTH_LONG).show();
setTestResult(test, TestResult.TEST_RESULT_FAILED);
return false;
@@ -196,7 +200,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/PassFailButtons.java b/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java
index 5a08558..2c3d35d 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java
@@ -262,6 +262,10 @@
@Override
public ReportLog getReportLog() { return reportLog; }
+
+ public void updatePassButton() {
+ getPassButton().setEnabled(mAdapter.allTestsPassed());
+ }
}
private static <T extends android.app.Activity & PassFailActivity>
@@ -399,16 +403,14 @@
private static void setTestResultAndFinish(android.app.Activity activity, String testId,
String testDetails, ReportLog reportLog, View target) {
boolean passed;
- switch (target.getId()) {
- case R.id.pass_button:
- passed = true;
- break;
- case R.id.fail_button:
- passed = false;
- break;
- default:
- throw new IllegalArgumentException("Unknown id: " + target.getId());
+ if (target.getId() == R.id.pass_button) {
+ passed = true;
+ } else if (target.getId() == R.id.fail_button) {
+ passed = false;
+ } else {
+ throw new IllegalArgumentException("Unknown id: " + target.getId());
}
+
setTestResultAndFinishHelper(activity, testId, testDetails, passed, reportLog);
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/TestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/TestListActivity.java
index 976ff32..6a96961 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/TestListActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/TestListActivity.java
@@ -19,7 +19,9 @@
import android.Manifest;
import android.app.ListActivity;
import android.content.Intent;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
@@ -33,19 +35,6 @@
/** Top-level {@link ListActivity} for launching tests and managing results. */
public class TestListActivity extends AbstractTestListActivity implements View.OnClickListener {
-
- private static final String [] RUNTIME_PERMISSIONS = {
- Manifest.permission.ACCESS_FINE_LOCATION,
- Manifest.permission.BODY_SENSORS,
- Manifest.permission.READ_EXTERNAL_STORAGE,
- Manifest.permission.READ_PHONE_STATE,
- Manifest.permission.CALL_PHONE,
- Manifest.permission.WRITE_CONTACTS,
- Manifest.permission.CAMERA,
- Manifest.permission.WRITE_EXTERNAL_STORAGE,
- Manifest.permission.RECORD_AUDIO,
- Manifest.permission.READ_CONTACTS
- };
private static final int CTS_VERIFIER_PERMISSION_REQUEST = 1;
private static final String TAG = TestListActivity.class.getSimpleName();
@@ -59,15 +48,25 @@
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- for (String runtimePermission : RUNTIME_PERMISSIONS) {
- Log.v(TAG, "Checking permissions for: " + runtimePermission);
- if (checkSelfPermission(runtimePermission) != PackageManager.PERMISSION_GRANTED) {
- requestPermissions(RUNTIME_PERMISSIONS, CTS_VERIFIER_PERMISSION_REQUEST);
- return;
- }
+ try {
+ PackageInfo packageInfo = getPackageManager().getPackageInfo(
+ getApplicationInfo().packageName, PackageManager.GET_PERMISSIONS);
+ if (packageInfo.requestedPermissions != null) {
+ for (String permission : packageInfo.requestedPermissions) {
+ Log.v(TAG, "Checking permissions for: " + permission);
+ if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
+ requestPermissions(packageInfo.requestedPermissions,
+ CTS_VERIFIER_PERMISSION_REQUEST);
+ return;
+ }
+ }
+ }
+ createContinue();
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "Unable to load package's permissions", e);
+ Toast.makeText(this, R.string.runtime_permissions_error, Toast.LENGTH_SHORT).show();
}
- createContinue();
}
private void createContinue() {
@@ -137,21 +136,16 @@
}
private boolean handleMenuItemSelected(int id) {
- switch (id) {
- case R.id.clear:
- handleClearItemSelected();
- return true;
-
- case R.id.view:
- handleViewItemSelected();
- return true;
-
- case R.id.export:
- handleExportItemSelected();
- return true;
-
- default:
- return false;
+ if (id == R.id.clear) {
+ handleClearItemSelected();
+ } else if (id == R.id.view) {
+ handleViewItemSelected();
+ } else if (id == R.id.export) {
+ handleExportItemSelected();
+ } else {
+ return false;
}
+
+ return true;
}
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsReport.java b/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsReport.java
index 05c5e77..dc2502c 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsReport.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsReport.java
@@ -96,7 +96,6 @@
xml.startTag(null, VERIFIER_INFO_TAG);
xml.attribute(null, "version-name", Version.getVersionName(mContext));
xml.attribute(null, "version-code", Integer.toString(Version.getVersionCode(mContext)));
- xml.attribute(null, "build", Version.getBuildNumber(mContext));
xml.endTag(null, VERIFIER_INFO_TAG);
xml.startTag(null, DEVICE_INFO_TAG);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/TimerProgressBar.java b/apps/CtsVerifier/src/com/android/cts/verifier/TimerProgressBar.java
new file mode 100644
index 0000000..4e0f61e
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/TimerProgressBar.java
@@ -0,0 +1,119 @@
+/*
+ * 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;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.util.AttributeSet;
+import android.widget.ProgressBar;
+
+/**
+ * Can be used to show time outs for events. A progress bar will be displayed to the user.
+ * On calling start, it will start filling up.
+ */
+public class TimerProgressBar extends ProgressBar {
+ public TimerProgressBar(Context context) {
+ super(context);
+ setHandler(context);
+ }
+
+ public TimerProgressBar(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setHandler(context);
+ }
+
+ public TimerProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ setHandler(context);
+ }
+
+ @TargetApi(21)
+ public TimerProgressBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ setHandler(context);
+ }
+
+ private void setHandler(Context context) {
+ mHandler = new Handler(context.getMainLooper());
+ }
+
+ private Handler mHandler;
+ private TimerExpiredCallback mTimerExpiredCallback;
+ private long mStartTime;
+ private long mDuration;
+ private long mStepSize;
+ private boolean mForceComplete;
+
+ private Runnable mProgressCallback = new Runnable() {
+ @Override
+ public void run() {
+ if (mForceComplete) {
+ TimerProgressBar.this.setProgress(TimerProgressBar.this.getMax());
+ return;
+ }
+
+ long currentTime = SystemClock.elapsedRealtime();
+ int progress = (int) ((currentTime - mStartTime) / mStepSize);
+ progress = Math.min(progress, TimerProgressBar.this.getMax());
+ TimerProgressBar.this.setProgress(progress);
+
+ if (mStartTime + mDuration > currentTime) {
+ mHandler.postDelayed(this, mStepSize);
+ } else {
+ if (mTimerExpiredCallback != null) {
+ mTimerExpiredCallback.onTimerExpired();
+ }
+ }
+ }
+ };
+
+ public void start(long duration, long stepSize) {
+ start(duration, stepSize, null);
+ }
+
+ /**
+ * Start filling up the progress bar.
+ *
+ * @param duration Time in milliseconds the progress bar takes to fill up completely
+ * @param stepSize Time in milliseconds between consecutive updates to progress bar's progress
+ * @param callback Callback that should be executed after the progress bar is filled completely (i.e. timer expires)
+ */
+ public void start(long duration, long stepSize, TimerExpiredCallback callback) {
+ mDuration = duration;
+ mStepSize = stepSize;
+ mStartTime = SystemClock.elapsedRealtime();
+ mForceComplete = false;
+ mTimerExpiredCallback = callback;
+ this.setMax((int) (duration / stepSize));
+ this.setProgress(0);
+ mHandler.post(mProgressCallback);
+ }
+
+ /**
+ * Fill the progress bar completely. Timer expired callback won't be executed.
+ */
+ public void forceComplete() {
+ mForceComplete = true;
+ }
+
+ public interface TimerExpiredCallback {
+ void onTimerExpired();
+ }
+
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/Version.java b/apps/CtsVerifier/src/com/android/cts/verifier/Version.java
index dfe9508..e7b6121 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/Version.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/Version.java
@@ -24,21 +24,13 @@
class Version {
static String getVersionName(Context context) {
- return getVersionNameStrings(context)[0];
+ return getPackageInfo(context).versionName;
}
static int getVersionCode(Context context) {
return getPackageInfo(context).versionCode;
}
- static String getBuildNumber(Context context) {
- return getVersionNameStrings(context)[1];
- }
-
- static private String[] getVersionNameStrings(Context context) {
- return getPackageInfo(context).versionName.split(" ");
- }
-
static PackageInfo getPackageInfo(Context context) {
try {
PackageManager packageManager = context.getPackageManager();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/admin/DeviceAdminKeyguardDisabledFeaturesActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/admin/DeviceAdminKeyguardDisabledFeaturesActivity.java
new file mode 100644
index 0000000..2ad77f6
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/admin/DeviceAdminKeyguardDisabledFeaturesActivity.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.verifier.admin;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.fingerprint.FingerprintManager;
+import android.provider.Settings;
+
+import com.android.cts.verifier.ArrayTestListAdapter;
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.DialogTestListActivity;
+import com.android.cts.verifier.managedprovisioning.ByodHelperActivity;
+import com.android.cts.verifier.managedprovisioning.DeviceAdminTestReceiver;
+import com.android.cts.verifier.managedprovisioning.KeyguardDisabledFeaturesActivity;
+
+
+/**
+ * Tests for Device Admin keyguard disabled features.
+ */
+public class DeviceAdminKeyguardDisabledFeaturesActivity extends KeyguardDisabledFeaturesActivity {
+ @Override
+ protected int getKeyguardDisabledFeatures() {
+ return DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL;
+ }
+
+ @Override
+ protected void setKeyguardDisabledFeatures() {
+ int flags = getKeyguardDisabledFeatures();
+ mDpm.setKeyguardDisabledFeatures(getAdminComponent(), flags);
+ }
+
+ @Override
+ protected String getTestIdPrefix() {
+ return "DeviceAdmin_";
+ }
+
+ @Override
+ protected void setupTests(ArrayTestListAdapter adapter) {
+ setupFingerprintTests(adapter);
+ setupDisableTrustAgentsTest(adapter);
+ adapter.add(new DialogTestListItem(this, R.string.device_admin_keyguard_disable_camera,
+ getTestIdPrefix()+"KeyguardDisableCamera",
+ R.string.device_admin_keyguard_disable_camera_instruction,
+ new Intent(ByodHelperActivity.ACTION_LOCKNOW)));
+
+ adapter.add(new DialogTestListItem(this, R.string.device_admin_disable_notifications,
+ "DeviceAdmin_DisableNotifications",
+ R.string.device_admin_disable_notifications_instruction,
+ new Intent(ByodHelperActivity.ACTION_NOTIFICATION_ON_LOCKSCREEN)));
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/admin/PolicySerializationTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/admin/PolicySerializationTestActivity.java
index 1591df3..ee24868 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/admin/PolicySerializationTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/admin/PolicySerializationTestActivity.java
@@ -16,6 +16,7 @@
package com.android.cts.verifier.admin;
+import com.android.cts.verifier.managedprovisioning.DeviceAdminTestReceiver;
import com.android.cts.verifier.PassFailButtons;
import com.android.cts.verifier.R;
@@ -73,7 +74,7 @@
setPassFailButtonClickListeners();
mDevicePolicyManager = (DevicePolicyManager) getSystemService(DEVICE_POLICY_SERVICE);
- mAdmin = TestDeviceAdminReceiver.getComponent(this);
+ mAdmin = DeviceAdminTestReceiver.getReceiverComponentName();
mGeneratePolicyButton = findViewById(R.id.generate_policy_button);
mGeneratePolicyButton.setOnClickListener(new OnClickListener() {
@@ -136,7 +137,7 @@
private void applyPolicy() {
Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
- TestDeviceAdminReceiver.getComponent(this));
+ DeviceAdminTestReceiver.getReceiverComponentName());
startActivityForResult(intent, ADD_DEVICE_ADMIN_REQUEST_CODE);
}
@@ -152,7 +153,7 @@
private void handleAddDeviceAdminResult(int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
- ComponentName admin = TestDeviceAdminReceiver.getComponent(this);
+ ComponentName admin = DeviceAdminTestReceiver.getReceiverComponentName();
for (PolicyItem<?> item : mPolicyItems) {
item.applyExpectedValue(mDevicePolicyManager, admin);
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/admin/RedactedNotificationKeyguardDisabledFeaturesActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/admin/RedactedNotificationKeyguardDisabledFeaturesActivity.java
new file mode 100644
index 0000000..711fd8c
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/admin/RedactedNotificationKeyguardDisabledFeaturesActivity.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.verifier.admin;
+
+import android.app.admin.DevicePolicyManager;
+
+import android.content.Intent;
+
+
+import com.android.cts.verifier.ArrayTestListAdapter;
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.DialogTestListActivity;
+import com.android.cts.verifier.managedprovisioning.ByodHelperActivity;
+
+/**
+ * Tests for Device Admin keyguard redacted notification feature. This test is taken out from
+ * DeviceAdminKeyguardDisabledFeaturesActivity class, because KEYGUARD_DISABLE_SECURE_NOTIFICATIONS
+ * would mask KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS.
+ * */
+
+public class RedactedNotificationKeyguardDisabledFeaturesActivity
+ extends DeviceAdminKeyguardDisabledFeaturesActivity {
+ @Override
+ protected int getKeyguardDisabledFeatures() {
+ return DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS;
+ }
+
+ @Override
+ protected void setupTests(ArrayTestListAdapter adapter) {
+ adapter.add(new DialogTestListItem(this, R.string.device_admin_disable_unredacted_notifications,
+ "DeviceAdmin_DisableUnredactedNotifications",
+ R.string.device_admin_disable_unredacted_notifications_instruction,
+ new Intent(ByodHelperActivity.ACTION_NOTIFICATION_ON_LOCKSCREEN)));
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/admin/ScreenLockTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/admin/ScreenLockTestActivity.java
index 5520bb7..41217a6 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/admin/ScreenLockTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/admin/ScreenLockTestActivity.java
@@ -16,6 +16,7 @@
package com.android.cts.verifier.admin;
+import com.android.cts.verifier.managedprovisioning.DeviceAdminTestReceiver;
import com.android.cts.verifier.PassFailButtons;
import com.android.cts.verifier.R;
@@ -72,7 +73,7 @@
private void sendAddDeviceAdminIntent() {
Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
- TestDeviceAdminReceiver.getComponent(this));
+ DeviceAdminTestReceiver.getReceiverComponentName());
startActivityForResult(intent, ADD_DEVICE_ADMIN_REQUEST_CODE);
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/admin/TestDeviceAdminReceiver.java b/apps/CtsVerifier/src/com/android/cts/verifier/admin/TestDeviceAdminReceiver.java
deleted file mode 100644
index 5ecb36d..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/admin/TestDeviceAdminReceiver.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.verifier.admin;
-
-import android.app.admin.DeviceAdminReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-
-public class TestDeviceAdminReceiver extends DeviceAdminReceiver {
-
- public static ComponentName getComponent(Context context) {
- return new ComponentName(context, TestDeviceAdminReceiver.class);
- }
-}
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..edb3bf0 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyLineActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyLineActivity.java
@@ -22,8 +22,8 @@
import com.android.compatibility.common.util.ReportLog;
import com.android.compatibility.common.util.ResultType;
import com.android.compatibility.common.util.ResultUnit;
-import android.content.Context;
+import android.content.Context;
import android.media.AudioDeviceCallback;
import android.media.AudioDeviceInfo;
import android.media.AudioFormat;
@@ -31,17 +31,13 @@
import android.media.AudioTrack;
import android.media.AudioRecord;
import android.media.MediaRecorder;
-
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
-
import android.util.Log;
-
import android.view.View;
import android.view.View.OnClickListener;
-
import android.widget.Button;
import android.widget.TextView;
import android.widget.SeekBar;
@@ -64,6 +60,9 @@
OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
Context mContext;
+ Button mHeadsetPortYes;
+ Button mHeadsetPortNo;
+
Button mLoopbackPlugReady;
LinearLayout mLinearLayout;
Button mTestButton;
@@ -116,6 +115,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 +140,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);
@@ -368,9 +387,15 @@
Results resultsRight = new Results("Right");
computeResultsForVector(mFreqAverage1, resultsRight);
if (resultsLeft.testAll() && resultsRight.testAll()) {
- //enable button
- getPassButton().setEnabled(true);
+ String strSuccess = getResources().getString(R.string.audio_general_test_passed);
+ appendResultsToScreen(strSuccess);
+ } else {
+ String strFailed = getResources().getString(R.string.audio_general_test_failed);
+ appendResultsToScreen(strFailed + "\n");
+ String strWarning = getResources().getString(R.string.audio_general_deficiency_found);
+ appendResultsToScreen(strWarning);
}
+ getPassButton().setEnabled(true); //Everybody passes! (for now...)
}
private void computeResultsForVector(VectorAverage freqAverage,Results results) {
@@ -479,6 +504,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/AudioFrequencySpeakerActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencySpeakerActivity.java
index f9334b3..ba7b86d 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencySpeakerActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencySpeakerActivity.java
@@ -416,8 +416,15 @@
computeResultsForVector(mFreqAverageRight, resultsRight, false, bandSpecsArray);
if (resultsLeft.testAll() && resultsRight.testAll() && resultsBase.testAll()) {
//enable button
- getPassButton().setEnabled(true);
+ String strSuccess = getResources().getString(R.string.audio_general_test_passed);
+ appendResultsToScreen(strSuccess);
+ } else {
+ String strFailed = getResources().getString(R.string.audio_general_test_failed);
+ appendResultsToScreen(strFailed + "\n");
+ String strWarning = getResources().getString(R.string.audio_general_deficiency_found);
+ appendResultsToScreen(strWarning);
}
+ getPassButton().setEnabled(true); //Everybody passes! (for now...)
}
private void computeResultsForVector(VectorAverage freqAverage,Results results, boolean isBase,
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 82%
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..e253635 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioDeviceNotificationsActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioInputDeviceNotificationsActivity.java
@@ -16,7 +16,6 @@
package com.android.cts.verifier.audio;
-import com.android.cts.verifier.PassFailButtons;
import com.android.cts.verifier.R;
import android.content.Context;
@@ -34,10 +33,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 HeadsetHonorSystemActivity {
Context mContext;
TextView mConnectView;
@@ -62,6 +61,11 @@
}
@Override
+ protected void enableTestButtons(boolean enabled) {
+ // Nothing to do.
+ }
+
+ @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.audio_dev_notify);
@@ -71,6 +75,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) {
@@ -82,6 +89,9 @@
AudioManager audioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
audioManager.registerAudioDeviceCallback(new TestAudioDeviceCallback(), null);
+ // "Honor System" buttons
+ super.setup();
+
setPassFailButtonClickListeners();
}
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioInputRoutingNotificationsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioInputRoutingNotificationsActivity.java
new file mode 100644
index 0000000..eefa9e4
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioInputRoutingNotificationsActivity.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.audio;
+
+import com.android.cts.verifier.R;
+
+import android.content.Context;
+
+import android.media.AudioDeviceCallback;
+import android.media.AudioDeviceInfo;
+import android.media.AudioManager;
+import android.media.AudioRecord;
+
+import android.os.Bundle;
+import android.os.Handler;
+
+import android.util.Log;
+
+import android.view.View;
+import android.view.View.OnClickListener;
+
+import android.widget.Button;
+import android.widget.TextView;
+
+/**
+ * Tests AudioRecord (re)Routing messages.
+ */
+public class AudioInputRoutingNotificationsActivity extends HeadsetHonorSystemActivity {
+ private static final String TAG = "AudioInputRoutingNotificationsActivity";
+
+ Button recordBtn;
+ Button stopBtn;
+
+ Context mContext;
+
+ int mNumRecordNotifications = 0;
+
+ 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_recordBtn:
+ mAudioRecorder.start();
+ break;
+
+ case R.id.audio_routingnotification_recordStopBtn:
+ mAudioRecorder.stop();
+ break;
+ }
+ }
+ }
+
+ 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);
+ }
+ }
+
+ protected void enableTestButtons(boolean enabled) {
+ recordBtn.setEnabled(enabled);
+ stopBtn.setEnabled(enabled);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.audio_input_routingnotifications_test);
+
+ Button btn;
+ recordBtn = (Button)findViewById(R.id.audio_routingnotification_recordBtn);
+ recordBtn.setOnClickListener(mBtnClickListener);
+ stopBtn = (Button)findViewById(R.id.audio_routingnotification_recordStopBtn);
+ stopBtn.setOnClickListener(mBtnClickListener);
+
+ mContext = this;
+
+ AudioRecord audioRecord = mAudioRecorder.getAudioRecord();
+ audioRecord.addOnRoutingChangedListener(
+ new AudioRecordRoutingChangeListener(), new Handler());
+
+ // "Honor System" buttons
+ super.setup();
+
+ setPassFailButtonClickListeners();
+ }
+
+ @Override
+ public void onBackPressed () {
+ 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 82%
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..ad8ba68 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioDeviceNotificationsActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioOutputDeviceNotificationsActivity.java
@@ -16,7 +16,6 @@
package com.android.cts.verifier.audio;
-import com.android.cts.verifier.PassFailButtons;
import com.android.cts.verifier.R;
import android.content.Context;
@@ -34,10 +33,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 HeadsetHonorSystemActivity {
Context mContext;
TextView mConnectView;
@@ -62,6 +61,11 @@
}
@Override
+ protected void enableTestButtons(boolean enabled) {
+ // Nothing to do.
+ }
+
+ @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.audio_dev_notify);
@@ -71,6 +75,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) {
@@ -82,6 +89,9 @@
AudioManager audioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
audioManager.registerAudioDeviceCallback(new TestAudioDeviceCallback(), null);
+ // "Honor System" buttons
+ super.setup();
+
setPassFailButtonClickListeners();
}
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioOutputRoutingNotificationsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioOutputRoutingNotificationsActivity.java
new file mode 100644
index 0000000..a6d8846
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioOutputRoutingNotificationsActivity.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.audio;
+
+import com.android.cts.verifier.R;
+
+import android.content.Context;
+
+import android.media.AudioDeviceCallback;
+import android.media.AudioDeviceInfo;
+import android.media.AudioManager;
+import android.media.AudioTrack;
+
+import android.os.Bundle;
+import android.os.Handler;
+
+import android.util.Log;
+
+import android.view.View;
+import android.view.View.OnClickListener;
+
+import android.widget.Button;
+import android.widget.TextView;
+
+/**
+ * Tests AudioTrack and AudioRecord (re)Routing messages.
+ */
+public class AudioOutputRoutingNotificationsActivity extends HeadsetHonorSystemActivity {
+ private static final String TAG = "AudioOutputRoutingNotificationsActivity";
+
+ Context mContext;
+
+ Button playBtn;
+ Button stopBtn;
+
+ private OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
+
+ int mNumTrackNotifications = 0;
+
+ TrivialPlayer mAudioPlayer = new TrivialPlayer();
+
+ private class OnBtnClickListener implements OnClickListener {
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.audio_routingnotification_playBtn:
+ mAudioPlayer.start();
+ break;
+
+ case R.id.audio_routingnotification_playStopBtn:
+ mAudioPlayer.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);
+ }
+ }
+
+ @Override
+ protected void enableTestButtons(boolean enabled) {
+ playBtn.setEnabled(enabled);
+ stopBtn.setEnabled(enabled);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.audio_output_routingnotifications_test);
+
+ mContext = this;
+
+ playBtn = (Button)findViewById(R.id.audio_routingnotification_playBtn);
+ playBtn.setOnClickListener(mBtnClickListener);
+ stopBtn = (Button)findViewById(R.id.audio_routingnotification_playStopBtn);
+ stopBtn.setOnClickListener(mBtnClickListener);
+
+ AudioTrack audioTrack = mAudioPlayer.getAudioTrack();
+ audioTrack.addOnRoutingChangedListener(
+ new AudioTrackRoutingChangeListener(), new Handler());
+
+ // "Honor System" buttons
+ super.setup();
+
+ setPassFailButtonClickListeners();
+ }
+
+ @Override
+ public void onBackPressed () {
+ mAudioPlayer.shutDown();
+ super.onBackPressed();
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioRoutingNotificationsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioRoutingNotificationsActivity.java
deleted file mode 100644
index b6a4255..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioRoutingNotificationsActivity.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.verifier.audio;
-
-import com.android.cts.verifier.PassFailButtons;
-import com.android.cts.verifier.R;
-
-import android.content.Context;
-
-import android.media.AudioDeviceCallback;
-import android.media.AudioDeviceInfo;
-import android.media.AudioManager;
-import android.media.AudioRecord;
-import android.media.AudioTrack;
-
-import android.os.Bundle;
-import android.os.Handler;
-
-import android.util.Log;
-
-import android.view.View;
-import android.view.View.OnClickListener;
-
-import android.widget.Button;
-import android.widget.TextView;
-
-/**
- * Tests AudioTrack and AudioRecord (re)Routing messages.
- */
-public class AudioRoutingNotificationsActivity extends PassFailButtons.Activity {
- private static final String TAG = "AudioRoutingNotificationsActivity";
-
- 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;
- }
- }
- }
-
- 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++;
- 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);
-
- 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;
-
- AudioTrack audioTrack = mAudioPlayer.getAudioTrack();
- 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/audio/Correlation.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/Correlation.java
index 75b04eb..98d1365 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/Correlation.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/Correlation.java
@@ -30,6 +30,8 @@
public double mEstimatedLatencyMs = 0;
public double mEstimatedLatencyConfidence = 0.0;
+ private double mAmplitudeThreshold = 0.001; // 0.001 = -60 dB noise
+
public void init(int blockSize, int samplingRate) {
mBlockSize = blockSize;
mSamplingRate = samplingRate;
@@ -40,7 +42,7 @@
log("Started Auto Correlation for data with " + data.length + " points");
mSamplingRate = samplingRate;
- downsampleData(data, mDataDownsampled);
+ downsampleData(data, mDataDownsampled, mAmplitudeThreshold);
//correlation vector
autocorrelation(mDataDownsampled, mDataAutocorrelated);
@@ -95,7 +97,7 @@
return status;
}
- private boolean downsampleData(double [] data, double [] dataDownsampled) {
+ private boolean downsampleData(double [] data, double [] dataDownsampled, double threshold) {
boolean status = false;
// mDataDownsampled = new double[mBlockSize];
@@ -106,6 +108,8 @@
int N = data.length; //all samples available
double groupSize = (double) N / mBlockSize;
+ int ignored = 0;
+
int currentIndex = 0;
double nextGroup = groupSize;
for (int i = 0; i<N && currentIndex<mBlockSize; i++) {
@@ -118,9 +122,18 @@
if (currentIndex>=mBlockSize) {
break;
}
- dataDownsampled[currentIndex] += Math.abs(data[i]);
+
+ double value = Math.abs(data[i]);
+ if (value >= threshold) {
+ dataDownsampled[currentIndex] += value;
+ } else {
+ ignored++;
+ }
}
+ log(String.format(" Threshold: %.3f, ignored:%d/%d (%%.2f)", threshold, ignored, N,
+ (double) ignored/(double)N));
+
status = true;
return status;
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/HeadsetHonorSystemActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/HeadsetHonorSystemActivity.java
new file mode 100644
index 0000000..a82b994
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/HeadsetHonorSystemActivity.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.audio;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import com.android.compatibility.common.util.ReportLog;
+import com.android.compatibility.common.util.ResultType;
+import com.android.compatibility.common.util.ResultUnit;
+
+import android.content.Context;
+
+import android.os.Bundle;
+import android.os.Handler;
+
+import android.util.Log;
+
+import android.view.View;
+import android.view.View.OnClickListener;
+
+import android.widget.Button;
+//import android.widget.TextView;
+
+abstract class HeadsetHonorSystemActivity extends PassFailButtons.Activity {
+ private static final String TAG = "HeadsetHonorSystemActivity";
+
+ private OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
+
+ abstract protected void enableTestButtons(boolean enabled);
+
+ private void recordHeadsetPortFound(boolean found) {
+ getReportLog().addValue(
+ "User Reported Headset Port",
+ found ? 1.0 : 0,
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+ }
+
+ protected void setup() {
+ // The "Honor" system buttons
+ ((Button)findViewById(R.id.audio_general_headset_no)).
+ setOnClickListener(mBtnClickListener);
+ ((Button)findViewById(R.id.audio_general_headset_yes)).
+ setOnClickListener(mBtnClickListener);
+
+ enableTestButtons(false);
+ }
+
+ private class OnBtnClickListener implements OnClickListener {
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.audio_general_headset_no:
+ Log.i(TAG, "User denies Headset Port existence");
+ enableTestButtons(false);
+ recordHeadsetPortFound(false);
+ break;
+
+ case R.id.audio_general_headset_yes:
+ Log.i(TAG, "User confirms Headset Port existence");
+ enableTestButtons(true);
+ recordHeadsetPortFound(true);
+ break;
+ }
+ }
+ }
+
+}
\ No newline at end of file
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/deskclock/DeskClockTestsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/deskclock/DeskClockTestsActivity.java
index fcd88d1..aaea279 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/deskclock/DeskClockTestsActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/deskclock/DeskClockTestsActivity.java
@@ -165,13 +165,6 @@
}
}
- /**
- * Enable Pass Button when the all tests passed.
- */
- private void updatePassButton() {
- getPassButton().setEnabled(mAdapter.allTestsPassed());
- }
-
public static class DeskClockIntentFactory implements IntentFactory {
@Override
public Intent[] createIntents(String testId, int buttonText) {
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..1b8396d 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,8 @@
public static final Feature[] ALL_MNC_FEATURES = {
new Feature(PackageManager.FEATURE_MIDI, false),
+ new Feature(PackageManager.FEATURE_AUDIO_PRO, false),
+ new Feature(PackageManager.FEATURE_FINGERPRINT, false),
};
@Override
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/ChargingConstraintTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/ChargingConstraintTestActivity.java
index 2a94ace..4b70b894 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/ChargingConstraintTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/ChargingConstraintTestActivity.java
@@ -9,17 +9,21 @@
import android.content.IntentFilter;
import android.os.AsyncTask;
import android.os.Bundle;
+import android.os.BatteryManager;
import android.widget.Button;
import android.widget.ImageView;
+import android.widget.TextView;
+import android.view.View;
import com.android.cts.verifier.R;
+import com.android.cts.verifier.TimerProgressBar;
/**
* This activity runs the following tests:
- * - Ask the tester to unplug the phone, and verify that jobs with charging constraints will
- * not run.
* - Ask the tester to ensure the phone is plugged in, and verify that jobs with charging
* constraints are run.
+ * - Ask the tester to unplug the phone, and verify that jobs with charging constraints will
+ * not run.
*/
@TargetApi(21)
public class ChargingConstraintTestActivity extends ConstraintTestActivity {
@@ -29,6 +33,19 @@
private static final int OFF_CHARGING_JOB_ID =
ChargingConstraintTestActivity.class.hashCode() + 1;
+ // Time in milliseconds to wait after power is connected for the phone
+ // to get into charging mode.
+ private static final long WAIT_FOR_CHARGING_DURATION = 3 * 60 * 1000;
+
+ private static final int STATE_NOT_RUNNING = 0;
+ private static final int STATE_WAITING_TO_START_ON_CHARGING_TEST = 1;
+ private static final int STATE_ON_CHARGING_TEST_PASSED = 2;
+
+ private int mTestState;
+ TimerProgressBar mWaitingForChargingProgressBar;
+ TextView mWaitingForChargingTextView;
+ TextView mProblemWithChargerTextView;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -38,6 +55,20 @@
setPassFailButtonClickListeners();
setInfoResources(R.string.js_charging_test, R.string.js_charging_instructions, -1);
mStartButton = (Button) findViewById(R.id.js_charging_start_test_button);
+ mWaitingForChargingProgressBar = (TimerProgressBar) findViewById(
+ R.id.js_waiting_for_charging_progress_bar);
+ mWaitingForChargingTextView = (TextView) findViewById(
+ R.id.js_waiting_for_charging_text_view);
+ mProblemWithChargerTextView = (TextView) findViewById(
+ R.id.js_problem_with_charger_text_view);
+
+ if (isDevicePluggedIn()){
+ mStartButton.setEnabled(true);
+ }
+
+ hideWaitingForStableChargingViews();
+
+ mTestState = STATE_NOT_RUNNING;
mJobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
@@ -45,6 +76,8 @@
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_POWER_CONNECTED);
intentFilter.addAction(Intent.ACTION_POWER_DISCONNECTED);
+ intentFilter.addAction(BatteryManager.ACTION_CHARGING);
+ intentFilter.addAction(BatteryManager.ACTION_DISCHARGING);
registerReceiver(mChargingChangedReceiver, intentFilter);
}
@@ -55,22 +88,47 @@
unregisterReceiver(mChargingChangedReceiver);
}
+ private boolean isDevicePluggedIn() {
+ IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
+ Intent batteryStatus = registerReceiver(null, ifilter);
+ int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
+ // 0 indicates device is on battery power
+ return status != 0;
+ }
+
@Override
public void startTestImpl() {
- new TestDeviceUnpluggedConstraint().execute();
+ BatteryManager bm = (BatteryManager) getSystemService(BATTERY_SERVICE);
+ if (bm.isCharging()) {
+ new TestDevicePluggedInConstraint().execute();
+ } else if (isDevicePluggedIn()) {
+ mTestState = STATE_WAITING_TO_START_ON_CHARGING_TEST;
+ showWaitingForStableChargingViews();
+ }
}
private BroadcastReceiver mChargingChangedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- if (Intent.ACTION_POWER_DISCONNECTED.equals(intent.getAction())) {
- mDeviceUnpluggedTestPassed = false;
- mStartButton.setEnabled(true);
- } else if (Intent.ACTION_POWER_CONNECTED.equals(intent.getAction())) {
- mStartButton.setEnabled(false);
- if (mDeviceUnpluggedTestPassed) {
- continueTest();
+ if (BatteryManager.ACTION_CHARGING.equals(intent.getAction())) {
+ if (mTestState == STATE_WAITING_TO_START_ON_CHARGING_TEST) {
+ mWaitingForChargingProgressBar.forceComplete();
+ hideWaitingForStableChargingViews();
+ new TestDevicePluggedInConstraint().execute();
}
+ } else if (BatteryManager.ACTION_DISCHARGING.equals(intent.getAction())) {
+ if (mTestState == STATE_ON_CHARGING_TEST_PASSED) {
+ new TestDeviceUnpluggedConstraint().execute();
+ }
+ } else if (Intent.ACTION_POWER_CONNECTED.equals(intent.getAction())) {
+ if (mTestState == STATE_WAITING_TO_START_ON_CHARGING_TEST) {
+ showWaitingForStableChargingViews();
+ } else if (mTestState == STATE_NOT_RUNNING) {
+ mStartButton.setEnabled(true);
+ }
+ mStartButton.setEnabled(true);
+ } else if (Intent.ACTION_POWER_DISCONNECTED.equals(intent.getAction())) {
+ hideWaitingForStableChargingViews();
}
}
};
@@ -79,15 +137,6 @@
private boolean mDeviceUnpluggedTestPassed = false;
/**
- * After the first test has passed, and preconditions are met, this will kick off the second
- * test.
- * See {@link #startTest(android.view.View)}.
- */
- private void continueTest() {
- new TestDevicePluggedInConstraint().execute();
- }
-
- /**
* Test blocks and can't be run on the main thread.
*/
private void testChargingConstraintFails_notCharging() {
@@ -99,17 +148,12 @@
.build();
mJobScheduler.schedule(runOnCharge);
- // Send intent to kick off any jobs. This will be a no-op as the device is not plugged in;
- // the JobScheduler tracks charging state independently.
- sendBroadcastAndBlockForResult(EXPEDITE_STABLE_CHARGING);
-
boolean testPassed;
try {
testPassed = mTestEnvironment.awaitTimeout();
} catch (InterruptedException e) {
testPassed = false;
}
- mDeviceUnpluggedTestPassed = testPassed;
runOnUiThread(new ChargingConstraintTestResultRunner(OFF_CHARGING_JOB_ID, testPassed));
}
@@ -127,15 +171,14 @@
mTestEnvironment.setExpectedExecutions(1);
mJobScheduler.schedule(delayConstraintAndUnexpiredDeadline);
- // Force the JobScheduler to consider any jobs that have charging constraints.
- sendBroadcast(EXPEDITE_STABLE_CHARGING);
-
boolean testPassed;
try {
testPassed = mTestEnvironment.awaitExecution();
} catch (InterruptedException e) {
testPassed = false;
}
+
+ mTestState = testPassed ? STATE_ON_CHARGING_TEST_PASSED : STATE_NOT_RUNNING;
runOnUiThread(new ChargingConstraintTestResultRunner(ON_CHARGING_JOB_ID, testPassed));
}
@@ -144,11 +187,15 @@
@Override
protected Void doInBackground(Void... voids) {
testChargingConstraintFails_notCharging();
-
- // Do not call notifyTestCompleted here, as we're still waiting for the user to put
- // the device back on charge to continue with TestDevicePluggedInConstraint.
+ notifyTestCompleted();
return null;
}
+
+ @Override
+ protected void onPostExecute(Void res) {
+ mTestState = STATE_NOT_RUNNING;
+ mStartButton.setEnabled(true);
+ }
}
/** Run test for when the <bold>device is connected to power.</bold> */
@@ -156,8 +203,6 @@
@Override
protected Void doInBackground(Void... voids) {
testChargingConstraintExecutes_onCharging();
-
- notifyTestCompleted();
return null;
}
}
@@ -181,4 +226,25 @@
view.setImageResource(mTestPassed ? R.drawable.fs_good : R.drawable.fs_error);
}
}
+
+ private void showWaitingForStableChargingViews() {
+ mWaitingForChargingProgressBar.start(WAIT_FOR_CHARGING_DURATION, 1000,
+ new TimerProgressBar.TimerExpiredCallback(){
+ @Override
+ public void onTimerExpired() {
+ mProblemWithChargerTextView.setVisibility(View.VISIBLE);
+ }
+ }
+ );
+ mWaitingForChargingProgressBar.setVisibility(View.VISIBLE);
+ mWaitingForChargingTextView.setVisibility(View.VISIBLE);
+ mProblemWithChargerTextView.setVisibility(View.GONE);
+ }
+
+ private void hideWaitingForStableChargingViews() {
+ mWaitingForChargingProgressBar.forceComplete();
+ mWaitingForChargingProgressBar.setVisibility(View.GONE);
+ mWaitingForChargingTextView.setVisibility(View.GONE);
+ mProblemWithChargerTextView.setVisibility(View.GONE);
+ }
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/ConnectivityConstraintTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/ConnectivityConstraintTestActivity.java
index 8d10bda..aaf68e6 100755
--- a/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/ConnectivityConstraintTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/ConnectivityConstraintTestActivity.java
@@ -106,11 +106,6 @@
mJobScheduler.schedule(testJob1);
mJobScheduler.schedule(testJob2);
- /*
- // Send intent to kick off ready jobs that the JobScheduler might be lazily holding on to.
- sendBroadcastAndBlockForResult(EXPEDITE_STABLE_CHARGING);
- */
-
boolean testPassed;
try {
testPassed = mTestEnvironment.awaitExecution();
@@ -132,9 +127,6 @@
mJobScheduler.schedule(testJob);
- // Send intent to kick off ready jobs that the JobScheduler might be lazily holding on to.
- sendBroadcastAndBlockForResult(EXPEDITE_STABLE_CHARGING);
-
boolean testPassed;
try {
testPassed = mTestEnvironment.awaitTimeout();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/ConstraintTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/ConstraintTestActivity.java
index da0862a..0d8d13f 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/ConstraintTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/ConstraintTestActivity.java
@@ -18,13 +18,7 @@
@TargetApi(21)
public abstract class ConstraintTestActivity extends PassFailButtons.Activity {
- /**
- * Intent we use to force the job scheduler to consider any ready jobs that otherwise it may
- * have decided to be lazy about.
- */
- protected static final Intent EXPEDITE_STABLE_CHARGING =
- new Intent("com.android.server.task.controllers.BatteryController.ACTION_CHARGING_STABLE");
-
+
protected ComponentName mMockComponent;
protected MockJobService.TestEnvironment mTestEnvironment;
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/IdleConstraintTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/IdleConstraintTestActivity.java
index a8bd993..05c1a2e 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/IdleConstraintTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/IdleConstraintTestActivity.java
@@ -26,15 +26,17 @@
import android.content.IntentFilter;
import android.os.AsyncTask;
import android.os.Bundle;
+import android.os.PowerManager;
import android.util.Log;
+import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
+import android.widget.TextView;
/**
* Idle constraints:
- * The framework doesn't support turning idle mode off. Use the manual tester to ensure that
- * the device is not in idle mode (by turning the screen off and then back on) before running
- * the tests.
+ * The framework doesn't support turning the screen off. Use the manual tester to
+ * turn off the screen to run to run tests that require idle mode to be on.
*/
@TargetApi(21)
public class IdleConstraintTestActivity extends ConstraintTestActivity {
@@ -57,22 +59,35 @@
*/
private static final int IDLE_ON_JOB_ID = IdleConstraintTestActivity.class.hashCode() + 1;
+ private static final int IDLE_ON_TEST_STATE_NOT_IN_PROGRESS = 0;
+ private static final int IDLE_ON_TEST_STATE_WAITING_FOR_SCREEN_OFF = 1;
+
/**
- * Listens for idle mode off/on events, namely {@link #ACTION_EXPEDITE_IDLE_MODE} and
- * {@link Intent#ACTION_SCREEN_ON}.
- * On ACTION_EXPEDITE_IDLE_MODE, we will disable the {@link #mStartButton}, and on
- * ACTION_SCREEN_ON we enable it. This is to avoid the start button being clicked when the
- * device is in idle mode.
+ * mTestState stores the state of the tests. It is used to ensure that we only run
+ * the 'idle on' test if screen is turned off after the user has started tests.
*/
- private BroadcastReceiver mIdleChangedReceiver = new BroadcastReceiver() {
+ private int mTestState = IDLE_ON_TEST_STATE_NOT_IN_PROGRESS;
+
+ private PowerManager mPowerManager;
+ private TextView mContinueInstructionTextView;
+
+ /**
+ * Listens for screen off event. Starts an async task to force device into
+ * idle mode and run the 'idle on' test.
+ */
+ private BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())) {
- mStartButton.setEnabled(true);
- } else if (ACTION_EXPEDITE_IDLE_MODE.equals(intent.getAction())) {
- mStartButton.setEnabled(false);
+ if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
+ if (mTestState == IDLE_ON_TEST_STATE_WAITING_FOR_SCREEN_OFF) {
+ mContinueInstructionTextView.setVisibility(View.GONE);
+ PowerManager.WakeLock wl = mPowerManager.newWakeLock(
+ PowerManager.PARTIAL_WAKE_LOCK, TAG);
+ wl.acquire();
+ new TestIdleModeTaskIdle().execute(wl);
+ }
} else {
- Log.e(TAG, "Invalid broadcast received, was expecting SCREEN_ON");
+ Log.e(TAG, "Invalid broadcast received, was expecting SCREEN_OFF");
}
}
};
@@ -86,47 +101,88 @@
setPassFailButtonClickListeners();
setInfoResources(R.string.js_idle_test, R.string.js_idle_instructions, -1);
mStartButton = (Button) findViewById(R.id.js_idle_start_test_button);
+ mContinueInstructionTextView = (TextView) findViewById(
+ R.id.js_idle_continue_instruction_view);
- // Register receiver for idle off/on events.
+ // Register receiver for screen off event.
IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(Intent.ACTION_SCREEN_ON);
- intentFilter.addAction(ACTION_EXPEDITE_IDLE_MODE);
+ intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
- registerReceiver(mIdleChangedReceiver, intentFilter);
+ mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
+
+ registerReceiver(mScreenOffReceiver, intentFilter);
+
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ // Enable start button only if tests are not in progress.
+ if (mTestState == IDLE_ON_TEST_STATE_NOT_IN_PROGRESS) {
+ mStartButton.setEnabled(true);
+ mContinueInstructionTextView.setVisibility(View.GONE);
+ }
}
@Override
protected void onDestroy() {
super.onDestroy();
- unregisterReceiver(mIdleChangedReceiver);
+ unregisterReceiver(mScreenOffReceiver);
}
@Override
protected void startTestImpl() {
- new TestIdleModeTask().execute();
+ mStartButton.setEnabled(false);
+ new TestIdleModeTaskNotIdle().execute();
}
- /** Background task that will run the actual test. */
- private class TestIdleModeTask extends AsyncTask<Void, Void, Void> {
-
+ /** Background task that will run the 'not idle' test. */
+ private class TestIdleModeTaskNotIdle extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... voids) {
testIdleConstraintFails_notIdle();
+ return null;
+ }
+ @Override
+ protected void onPostExecute(Void result) {
+ mTestState = IDLE_ON_TEST_STATE_WAITING_FOR_SCREEN_OFF;
+ mContinueInstructionTextView.setVisibility(View.VISIBLE);
+ }
+ }
- // Send the {@link #ACTION_EXPEDITE_IDLE_MODE} broadcast as an ordered broadcast, this
- // function will block until all receivers have processed the broadcast.
+ /** Background task that will run the 'idle' test. */
+ private class TestIdleModeTaskIdle extends AsyncTask<PowerManager.WakeLock, Void, Void> {
+
+ private PowerManager.WakeLock mPartialWakeLock;
+
+ @Override
+ protected Void doInBackground(PowerManager.WakeLock... wakeLocks) {
+ mPartialWakeLock = wakeLocks[0];
+
if (!sendBroadcastAndBlockForResult(new Intent(ACTION_EXPEDITE_IDLE_MODE))) {
- // Fail the test if the broadcast wasn't processed.
runOnUiThread(new IdleTestResultRunner(IDLE_ON_JOB_ID, false));
+ } else {
+ testIdleConstraintExecutes_onIdle();
}
-
- testIdleConstraintExecutes_onIdle();
-
notifyTestCompleted();
return null;
}
+ @Override
+ protected void onPostExecute(Void result) {
+ // Reset test state
+ mTestState = IDLE_ON_TEST_STATE_NOT_IN_PROGRESS;
+
+ PowerManager.WakeLock fullWakeLock = mPowerManager.newWakeLock(
+ PowerManager.FULL_WAKE_LOCK
+ | PowerManager.ACQUIRE_CAUSES_WAKEUP
+ | PowerManager.ON_AFTER_RELEASE, TAG);
+ // Turn on screen and release both locks
+ fullWakeLock.acquire();
+ fullWakeLock.release();
+ mPartialWakeLock.release();
+ }
}
/**
@@ -154,6 +210,10 @@
runOnUiThread(new IdleTestResultRunner(IDLE_OFF_JOB_ID, testPassed));
}
+ /**
+ * Called after screen is switched off and device is forced into idle mode.
+ * Schedule a job with an idle constraint and verify that it executes.
+ */
private void testIdleConstraintExecutes_onIdle() {
mTestEnvironment.setUp();
mJobScheduler.cancelAll();
@@ -172,6 +232,7 @@
// We'll just indicate that it failed, not why.
testPassed = false;
}
+
runOnUiThread(new IdleTestResultRunner(IDLE_ON_JOB_ID, testPassed));
}
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..33e613f 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,10 @@
private DialogTestListItem mCrossProfileAudioCaptureSupportTest;
private TestListItem mKeyguardDisabledFeaturesTest;
private DialogTestListItem mDisableNfcBeamTest;
+ private TestListItem mAuthenticationBoundKeyTest;
+ private DialogTestListItem mEnableLocationModeTest;
+ private DialogTestListItem mDisableLocationModeTest;
+ private TestListItem mVpnTest;
public ByodFlowTestActivity() {
super(R.layout.provisioning_byod,
@@ -90,7 +97,7 @@
super.onCreate(savedInstanceState);
mAdminReceiverComponent = new ComponentName(this, DeviceAdminTestReceiver.class.getName());
- disableComponent();
+ enableComponent(PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
mPrepareTestButton.setText(R.string.provisioning_byod_start);
mPrepareTestButton.setOnClickListener(new OnClickListener() {
@Override
@@ -153,6 +160,7 @@
// Pass and fail buttons are known to call finish() when clicked, and this is when we want to
// clean up the provisioned profile.
requestDeleteProfileOwner();
+ enableComponent(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT);
super.finish();
}
@@ -182,7 +190,7 @@
R.string.provisioning_byod_work_notification,
"BYOD_WorkNotificationBadgedTest",
R.string.provisioning_byod_work_notification_instruction,
- new Intent(WorkNotificationTestActivity.ACTION_WORK_NOTIFICATION),
+ new Intent(ByodHelperActivity.ACTION_NOTIFICATION),
R.drawable.ic_corp_icon);
Intent workStatusIcon = new Intent(WorkStatusTestActivity.ACTION_WORK_STATUS_ICON);
@@ -264,20 +272,45 @@
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);
+
+ mVpnTest = TestListItem.newTest(this,
+ R.string.provisioning_byod_vpn,
+ VpnTestActivity.class.getName(),
+ new Intent(VpnTestActivity.ACTION_VPN),
+ 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 +320,7 @@
checkIntentFilters();
}
};
-
+
Intent permissionCheckIntent = new Intent(
PermissionLockdownTestActivity.ACTION_MANAGED_PROFILE_CHECK_PERMISSION_LOCKDOWN);
mPermissionLockdownTest = new DialogTestListItem(this,
@@ -314,13 +347,20 @@
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);
+ adapter.add(mVpnTest);
+ /* If there is an application that handles ACTION_IMAGE_CAPTURE, test that it handles it
+ * well.
+ */
if (canResolveIntent(ByodHelperActivity.getCaptureImageIntent())) {
// Capture image intent can be resolved in primary profile, so test.
mCrossProfileImageCaptureSupportTest = new DialogTestListItem(this,
@@ -336,6 +376,9 @@
.show();
}
+ /* If there is an application that handles ACTION_VIDEO_CAPTURE, test that it handles it
+ * well.
+ */
if (canResolveIntent(ByodHelperActivity.getCaptureVideoIntent())) {
// Capture video intent can be resolved in primary profile, so test.
mCrossProfileVideoCaptureSupportTest = new DialogTestListItem(this,
@@ -383,7 +426,9 @@
adapter.add(mDisableNfcBeamTest);
}
- /* TODO: reinstate when bug b/20131958 is fixed
+ /* If there is an application that handles RECORD_SOUND_ACTION, test that it handles it
+ * well.
+ */
if (canResolveIntent(ByodHelperActivity.getCaptureAudioIntent())) {
// Capture audio intent can be resolved in primary profile, so test.
mCrossProfileAudioCaptureSupportTest = new DialogTestListItem(this,
@@ -398,7 +443,27 @@
R.string.provisioning_byod_no_audio_capture_resolver, Toast.LENGTH_SHORT)
.show();
}
- */
+
+ if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_LOCATION_GPS)) {
+ mEnableLocationModeTest = new DialogTestListItem(this,
+ R.string.provisioning_byod_location_mode_enable,
+ "BYOD_LocationModeEnableTest",
+ R.string.provisioning_byod_location_mode_enable_instruction,
+ new Intent(ByodHelperActivity.ACTION_SET_LOCATION_AND_CHECK_UPDATES));
+ mDisableLocationModeTest = new DialogTestListItem(this,
+ R.string.provisioning_byod_location_mode_disable,
+ "BYOD_LocationModeDisableTest",
+ R.string.provisioning_byod_location_mode_disable_instruction,
+ new Intent(ByodHelperActivity.ACTION_SET_LOCATION_AND_CHECK_UPDATES));
+
+ adapter.add(mEnableLocationModeTest);
+ adapter.add(mDisableLocationModeTest);
+ } else {
+ // The system does not support GPS feature, so skip test.
+ Toast.makeText(ByodFlowTestActivity.this,
+ R.string.provisioning_byod_no_gps_location_feature, Toast.LENGTH_SHORT)
+ .show();
+ }
}
// Return whether the intent can be resolved in the current profile
@@ -409,11 +474,11 @@
@Override
protected void clearRemainingState(final DialogTestListItem test) {
super.clearRemainingState(test);
- if (WorkNotificationTestActivity.ACTION_WORK_NOTIFICATION.equals(
+ if (ByodHelperActivity.ACTION_NOTIFICATION.equals(
test.getManualTestIntent().getAction())) {
try {
startActivity(new Intent(
- WorkNotificationTestActivity.ACTION_CLEAR_WORK_NOTIFICATION));
+ ByodHelperActivity.ACTION_CLEAR_NOTIFICATION));
} catch (ActivityNotFoundException e) {
// User shouldn't run this test before work profile is set up.
}
@@ -488,19 +553,24 @@
intentFiltersSet ? TestResult.TEST_RESULT_PASSED : TestResult.TEST_RESULT_FAILED);
}
- private void disableComponent() {
- // Disable app components in the current profile, so only the counterpart in the other profile
- // can respond (via cross-profile intent filter)
+ /**
+ * Disable or enable app components in the current profile. When they are disabled only the
+ * counterpart in the other profile can respond (via cross-profile intent filter).
+ * @param enabledState {@link PackageManager#COMPONENT_ENABLED_STATE_DISABLED} or
+ * {@link PackageManager#COMPONENT_ENABLED_STATE_DEFAULT}
+ */
+ private void enableComponent(final int enabledState) {
final String[] components = {
ByodHelperActivity.class.getName(),
- WorkNotificationTestActivity.class.getName(),
WorkStatusTestActivity.class.getName(),
- PermissionLockdownTestActivity.ACTIVITY_ALIAS
+ PermissionLockdownTestActivity.ACTIVITY_ALIAS,
+ AuthenticationBoundKeyTestActivity.class.getName(),
+ VpnTestActivity.class.getName()
};
for (String component : components) {
getPackageManager().setComponentEnabledSetting(new ComponentName(this, component),
- PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
- PackageManager.DONT_KILL_APP);
+ enabledState, 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..ef7c952 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java
@@ -19,14 +19,22 @@
import android.app.Activity;
import android.app.admin.DevicePolicyManager;
import android.app.Dialog;
+import android.app.Notification;
+import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
import android.net.Uri;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.UserManager;
import android.provider.MediaStore;
import android.provider.Settings;
import android.support.v4.content.FileProvider;
@@ -50,7 +58,7 @@
*
* Note: We have to use a dummy activity because cross-profile intents only work for activities.
*/
-public class ByodHelperActivity extends Activity implements DialogCallback {
+public class ByodHelperActivity extends Activity implements DialogCallback, Handler.Callback {
static final String TAG = "ByodHelperActivity";
// Primary -> managed intent: query if the profile owner has been set up.
@@ -84,24 +92,69 @@
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";
+
+ // Primary -> managed intent: request to goto the location settings page and listen to updates.
+ public static final String ACTION_SET_LOCATION_AND_CHECK_UPDATES =
+ "com.android.cts.verifier.managedprovisioning.BYOD_SET_LOCATION_AND_CHECK";
+ public static final String ACTION_NOTIFICATION =
+ "com.android.cts.verifier.managedprovisioning.NOTIFICATION";
+ public static final String ACTION_NOTIFICATION_ON_LOCKSCREEN =
+ "com.android.cts.verifier.managedprovisioning.LOCKSCREEN_NOTIFICATION";
+ public static final String ACTION_CLEAR_NOTIFICATION =
+ "com.android.cts.verifier.managedprovisioning.CLEAR_NOTIFICATION";
+
public static final int RESULT_FAILED = RESULT_FIRST_USER;
private static final int REQUEST_INSTALL_PACKAGE = 1;
private static final int REQUEST_IMAGE_CAPTURE = 2;
private static final int REQUEST_VIDEO_CAPTURE = 3;
private static final int REQUEST_AUDIO_CAPTURE = 4;
+ private static final int REQUEST_LOCATION_UPDATE = 5;
private static final String ORIGINAL_SETTINGS_NAME = "original settings";
+
+ private static final int NOTIFICATION_ID = 7;
+
+ private NotificationManager mNotificationManager;
private Bundle mOriginalSettings;
+ private static final int MSG_TIMEOUT = 1;
+
+ private static final long MSG_TIMEOUT_MILLISEC = 15 * 1000;
+
private ComponentName mAdminReceiverComponent;
private DevicePolicyManager mDevicePolicyManager;
+ private LocationManager mLocationManager;
+ private Handler mHandler;
+ private boolean mIsLocationUpdated;
private Uri mImageUri;
private Uri mVideoUri;
private ArrayList<File> mTempFiles = new ArrayList<File>();
+ private void showNotification(int visibility) {
+ final Notification notification = new Notification.Builder(this)
+ .setSmallIcon(R.drawable.icon)
+ .setContentTitle(getString(R.string.provisioning_byod_notification_title))
+ .setVisibility(visibility)
+ .setAutoCancel(true)
+ .build();
+ mNotificationManager.notify(NOTIFICATION_ID, notification);
+ }
+
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -115,6 +168,10 @@
mAdminReceiverComponent = new ComponentName(this, DeviceAdminTestReceiver.class.getName());
mDevicePolicyManager = (DevicePolicyManager) getSystemService(
Context.DEVICE_POLICY_SERVICE);
+ mLocationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
+ mHandler = new Handler(this);
+ mIsLocationUpdated = false;
+ mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
Intent intent = getIntent();
String action = intent.getAction();
Log.d(TAG, "ByodHelperActivity.onCreate: " + action);
@@ -133,6 +190,8 @@
// Request to delete work profile.
} else if (action.equals(ACTION_REMOVE_PROFILE_OWNER)) {
if (isProfileOwner()) {
+ Log.d(TAG, "Clearing cross profile intents");
+ mDevicePolicyManager.clearCrossProfileIntentFilters(mAdminReceiverComponent);
mDevicePolicyManager.wipeData(0);
showToast(R.string.provisioning_byod_profile_deleted);
}
@@ -160,6 +219,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 +233,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 +270,41 @@
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);
+ } else if (action.equals(ACTION_SET_LOCATION_AND_CHECK_UPDATES)) {
+ // Grant the locaiton permission to the provile owner on cts-verifier.
+ // The permission state does not have to be reverted at the end since the profile onwer
+ // is going to be deleted when BYOD tests ends.
+ grantLocationPermissionToSelf();
+ Intent locationSettingsIntent = getLocationSettingsIntent();
+ if (locationSettingsIntent.resolveActivity(getPackageManager()) != null) {
+ startActivityForResult(locationSettingsIntent, REQUEST_LOCATION_UPDATE);
+ scheduleTimeout();
+ } else {
+ Log.e(TAG, "BYOD settings could not be resolved in managed profile");
+ finish();
+ }
+ mLocationManager.requestLocationUpdates(
+ LocationManager.GPS_PROVIDER, 0, 0, mLocationListener);
+ return;
+ } else if (action.equals(ACTION_NOTIFICATION)) {
+ showNotification(Notification.VISIBILITY_PUBLIC);
+ } else if (ACTION_NOTIFICATION_ON_LOCKSCREEN.equals(action)) {
+ DevicePolicyManager dpm = (DevicePolicyManager) getSystemService(
+ Context.DEVICE_POLICY_SERVICE);
+ dpm.lockNow();
+ showNotification(Notification.VISIBILITY_PRIVATE);
+ } else if (ACTION_CLEAR_NOTIFICATION.equals(action)) {
+ mNotificationManager.cancel(NOTIFICATION_ID);
}
// This activity has no UI and is only used to respond to CtsVerifier in the primary side.
finish();
@@ -262,6 +360,13 @@
}
break;
}
+ case REQUEST_LOCATION_UPDATE: {
+ Log.d(TAG, "BYOD exit location settings:OK");
+ mLocationManager.removeUpdates(mLocationListener);
+ mHandler.removeMessages(MSG_TIMEOUT);
+ finish();
+ break;
+ }
default: {
Log.wtf(TAG, "Unknown requestCode " + requestCode + "; data = " + data);
break;
@@ -287,6 +392,10 @@
return new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION);
}
+ public static Intent getLocationSettingsIntent() {
+ return new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
+ }
+
public static Intent createLockIntent() {
return new Intent(ACTION_LOCKNOW);
}
@@ -336,8 +445,62 @@
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);
+ }
+
+ private void grantLocationPermissionToSelf() {
+ mDevicePolicyManager.setPermissionGrantState(mAdminReceiverComponent, getPackageName(),
+ android.Manifest.permission.ACCESS_FINE_LOCATION,
+ DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED);
+ }
+
+ private final LocationListener mLocationListener = new LocationListener() {
+ @Override
+ public void onLocationChanged(Location location) {
+ if (mIsLocationUpdated) return;
+ showToast(R.string.provisioning_byod_location_mode_enable_toast_location_change);
+ mIsLocationUpdated = true;
+ }
+
+ @Override
+ public void onProviderDisabled(String provider) {
+ }
+
+ @Override
+ public void onProviderEnabled(String provider) {
+ }
+
+ @Override
+ public void onStatusChanged(String provider, int status, Bundle extras) {
+ }
+ };
+
+ private void scheduleTimeout() {
+ mHandler.removeMessages(MSG_TIMEOUT);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_TIMEOUT), MSG_TIMEOUT_MILLISEC);
+ }
+
@Override
public void onDialogClose() {
finish();
}
+
+ @Override
+ public boolean handleMessage(Message msg) {
+ if (msg.what == MSG_TIMEOUT) {
+ if (mIsLocationUpdated) return true;
+ showToast(R.string.provisioning_byod_location_mode_time_out_toast);
+ }
+ return true;
+ }
}
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..3f316f21 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..039cc09 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
@@ -22,6 +22,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.provider.Settings;
import android.util.Log;
/**
@@ -64,20 +65,26 @@
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(WorkNotificationTestActivity.ACTION_WORK_NOTIFICATION);
- filter.addAction(WorkNotificationTestActivity.ACTION_WORK_NOTIFICATION_ON_LOCKSCREEN);
- filter.addAction(WorkNotificationTestActivity.ACTION_CLEAR_WORK_NOTIFICATION);
+ filter.addAction(ByodHelperActivity.ACTION_TEST_CROSS_PROFILE_INTENTS_DIALOG);
+ filter.addAction(ByodHelperActivity.ACTION_TEST_APP_LINKING_DIALOG);
+ filter.addAction(ByodHelperActivity.ACTION_NOTIFICATION);
+ filter.addAction(ByodHelperActivity.ACTION_NOTIFICATION_ON_LOCKSCREEN);
+ filter.addAction(ByodHelperActivity.ACTION_CLEAR_NOTIFICATION);
+ filter.addAction(CrossProfileTestActivity.ACTION_CROSS_PROFILE_TO_WORK);
filter.addAction(WorkStatusTestActivity.ACTION_WORK_STATUS_TOAST);
filter.addAction(WorkStatusTestActivity.ACTION_WORK_STATUS_ICON);
filter.addAction(
PermissionLockdownTestActivity.ACTION_MANAGED_PROFILE_CHECK_PERMISSION_LOCKDOWN);
+ filter.addAction(AuthenticationBoundKeyTestActivity.ACTION_AUTH_BOUND_KEY_TEST);
+ filter.addAction(ByodHelperActivity.ACTION_SET_LOCATION_AND_CHECK_UPDATES);
+ filter.addAction(VpnTestActivity.ACTION_VPN);
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/DeviceOwnerNegativeTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerNegativeTestActivity.java
index c7e785c..3c0955d 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerNegativeTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerNegativeTestActivity.java
@@ -81,12 +81,5 @@
setTestListAdapter(adapter);
}
-
- /**
- * Enable Pass Button when the all tests passed.
- */
- private void updatePassButton() {
- getPassButton().setEnabled(mAdapter.allTestsPassed());
- }
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
index a6a5e5a..72361c1 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
@@ -75,6 +75,9 @@
PermissionLockdownTestActivity.class.getName();
private static final String DISALLOW_CONFIG_BT_ID = "DISALLOW_CONFIG_BT";
private static final String DISALLOW_CONFIG_WIFI_ID = "DISALLOW_CONFIG_WIFI";
+ private static final String DISALLOW_CONFIG_VPN_ID = "DISALLOW_CONFIG_VPN";
+ //TODO(rgl): This symbol should be available in android.provider.settings
+ private static final String ACTION_VPN_SETTINGS = "android.net.vpn.SETTINGS";
private static final String REMOVE_DEVICE_OWNER_TEST_ID = "REMOVE_DEVICE_OWNER";
@Override
@@ -123,13 +126,6 @@
super.finish();
}
- /**
- * Enable Pass Button when all tests passed.
- */
- private void updatePassButton() {
- getPassButton().setEnabled(mAdapter.allTestsPassed());
- }
-
private void addTestsToAdapter(final ArrayTestListAdapter adapter) {
adapter.add(createTestItem(this, CHECK_DEVICE_OWNER_TEST_ID,
R.string.device_owner_check_device_owner_test,
@@ -166,6 +162,22 @@
new Intent(Settings.ACTION_WIFI_SETTINGS))}));
}
+ // DISALLOW_CONFIG_VPN
+ adapter.add(createInteractiveTestItem(this, DISALLOW_CONFIG_VPN_ID,
+ R.string.device_owner_disallow_config_vpn,
+ R.string.device_owner_disallow_config_vpn_info,
+ new ButtonInfo[] {
+ new ButtonInfo(
+ R.string.device_owner_user_vpn_restriction_set,
+ createSetUserRestrictionIntent(
+ UserManager.DISALLOW_CONFIG_VPN)),
+ new ButtonInfo(
+ R.string.device_owner_settings_go,
+ new Intent(ACTION_VPN_SETTINGS)),
+ new ButtonInfo(
+ R.string.device_owner_vpn_test,
+ new Intent(this, VpnTestActivity.class))}));
+
// DISALLOW_CONFIG_BLUETOOTH
if (packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
adapter.add(createInteractiveTestItem(this, DISALLOW_CONFIG_BT_ID,
@@ -326,6 +338,7 @@
dpm.setKeyguardDisabled(admin, false);
dpm.clearUserRestriction(admin, UserManager.DISALLOW_CONFIG_BLUETOOTH);
dpm.clearUserRestriction(admin, UserManager.DISALLOW_CONFIG_WIFI);
+ dpm.clearUserRestriction(admin, UserManager.DISALLOW_CONFIG_VPN);
dpm.clearDeviceOwnerApp(getPackageName());
}
}
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..21c7331 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,81 @@
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.TTS_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(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 +138,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 +367,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 +377,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..1b4edcf 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 {
+ protected DevicePolicyManager mDpm;
+
public KeyguardDisabledFeaturesActivity() {
super(R.layout.provisioning_byod,
R.string.provisioning_byod_keyguard_disabled_features,
@@ -39,82 +43,110 @@
R.string.provisioning_byod_keyguard_disabled_features_instruction);
}
+ protected int getKeyguardDisabledFeatures() {
+ return DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS
+ | DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT
+ | DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS;
+ }
+
@Override
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");
- setKeyguardDisabledFeatures(DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS |
- DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT |
- DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
+ if (!mDpm.isAdminActive(getAdminComponent())) {
+ Toast.makeText(KeyguardDisabledFeaturesActivity.this,
+ R.string.provisioning_byod_keyguard_disabled_features_not_admin,
+ Toast.LENGTH_SHORT).show();
+ return;
+ }
+ setKeyguardDisabledFeatures();
+ mDpm.resetPassword("testpassword", 0);
}
});
}
+
+ protected ComponentName getAdminComponent() {
+ return DeviceAdminTestReceiver.getReceiverComponentName();
+ }
+
+ protected String getTestIdPrefix() {
+ return "BYOD_";
+ }
@Override
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 = getAdminComponent();
+ if (mDpm.isAdminActive(adminComponent)) {
+ mDpm.resetPassword(null, 0);
+ mDpm.removeActiveAdmin(adminComponent);
+ }
super.finish();
}
- private void setKeyguardDisabledFeatures(final int flags) {
- Intent setKeyguardDisabledFeaturesIntent =
- new Intent(ByodHelperActivity.ACTION_KEYGUARD_DISABLED_FEATURES)
- .putExtra(ByodHelperActivity.EXTRA_PARAMETER_1, flags);
+ protected void setKeyguardDisabledFeatures() {
+ int flags = getKeyguardDisabledFeatures();
+ Intent setKeyguardDisabledFeaturesIntent = new Intent(
+ ByodHelperActivity.ACTION_KEYGUARD_DISABLED_FEATURES)
+ .putExtra(ByodHelperActivity.EXTRA_PARAMETER_1, flags);
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) {
+
+ protected void setupDisableTrustAgentsTest(ArrayTestListAdapter adapter) {
adapter.add(new DialogTestListItem(this, R.string.provisioning_byod_disable_trust_agents,
- "BYOD_DisableTrustAgentsTest",
+ getTestIdPrefix() + "DisableTrustAgentsTest",
R.string.provisioning_byod_disable_trust_agents_instruction,
new Intent(Settings.ACTION_SECURITY_SETTINGS)));
+ }
+
+ protected void setupDisableUnredactedWorkNotification(ArrayTestListAdapter adapter) {
adapter.add(new DialogTestListItemWithIcon(this,
- R.string.provisioning_byod_disable_notifications,
- "BYOD_DisableUnredactedNotifications",
- R.string.provisioning_byod_disable_notifications_instruction,
- new Intent(WorkNotificationTestActivity.ACTION_WORK_NOTIFICATION_ON_LOCKSCREEN),
+ R.string.provisioning_byod_disable_unredacted_notifications,
+ getTestIdPrefix() + "DisableUnredactedNotifications",
+ R.string.provisioning_byod_disable_unredacted_notifications_instruction,
+ new Intent(ByodHelperActivity.ACTION_NOTIFICATION_ON_LOCKSCREEN),
R.drawable.ic_corp_icon));
+ }
+
+ protected void setupFingerprintTests(ArrayTestListAdapter adapter) {
FingerprintManager fpm = (FingerprintManager) getSystemService(Context.FINGERPRINT_SERVICE);
if (fpm.isHardwareDetected()) {
adapter.add(new DialogTestListItem(this,
R.string.provisioning_byod_fingerprint_disabled_in_settings,
- "BYOD_FingerprintDisabledInSettings",
+ getTestIdPrefix() + "FingerprintDisabledInSettings",
R.string.provisioning_byod_fingerprint_disabled_in_settings_instruction,
new Intent(Settings.ACTION_SECURITY_SETTINGS)));
adapter.add(new DialogTestListItem(this, R.string.provisioning_byod_disable_fingerprint,
- "BYOD_DisableFingerprint",
+ getTestIdPrefix() + "DisableFingerprint",
R.string.provisioning_byod_disable_fingerprint_instruction,
ByodHelperActivity.createLockIntent()));
}
}
@Override
+ protected void setupTests(ArrayTestListAdapter adapter) {
+ setupDisableTrustAgentsTest(adapter);
+ setupDisableUnredactedWorkNotification(adapter);
+ setupFingerprintTests(adapter);
+
+ }
+
+ @Override
protected void clearRemainingState(final DialogTestListItem test) {
super.clearRemainingState(test);
- if (WorkNotificationTestActivity.ACTION_WORK_NOTIFICATION_ON_LOCKSCREEN.equals(
+ if (ByodHelperActivity.ACTION_NOTIFICATION_ON_LOCKSCREEN.equals(
test.getManualTestIntent().getAction())) {
try {
startActivity(new Intent(
- WorkNotificationTestActivity.ACTION_CLEAR_WORK_NOTIFICATION));
+ ByodHelperActivity.ACTION_CLEAR_NOTIFICATION));
} catch (ActivityNotFoundException e) {
// User shouldn't run this test before work profile is set up.
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/VpnTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/VpnTestActivity.java
new file mode 100644
index 0000000..49c0c20
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/VpnTestActivity.java
@@ -0,0 +1,127 @@
+/*
+ * 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.managedprovisioning;
+
+import android.app.AlertDialog;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.net.VpnService;
+import android.net.VpnService.Builder;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.util.Log;
+import android.widget.TextView;
+import android.net.VpnService;
+import android.os.ParcelFileDescriptor;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import java.io.IOException;
+
+/**
+ * Activity to test Vpn configuration
+ */
+public class VpnTestActivity extends PassFailButtons.Activity {
+
+ public static final String ACTION_VPN = "com.android.cts.verifier.managedprovisioning.VPN";
+
+ public static class MyTestVpnService extends VpnService {
+ /*
+ * MyVpnTestService is just a stub. This class exists because the framework needs a class
+ * inside the app to refer back to, just using VpnService itself won't work.
+ */
+ }
+
+ private ParcelFileDescriptor descriptor = null;
+ private ComponentName mAdminReceiverComponent;
+ private DevicePolicyManager mDevicePolicyManager;
+ private UserManager mUserManager;
+ private static final String TAG = "DeviceOwnerPositiveTestActivity";
+ private static final int REQUEST_VPN_CODE = 1;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.vpn_test);
+ setPassFailButtonClickListeners();
+ mAdminReceiverComponent = new ComponentName(this, DeviceAdminTestReceiver.class.getName());
+ mDevicePolicyManager = (DevicePolicyManager) getSystemService(
+ Context.DEVICE_POLICY_SERVICE);
+ mDevicePolicyManager.addUserRestriction(mAdminReceiverComponent,
+ UserManager.DISALLOW_CONFIG_VPN);
+ mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);
+ testVpnEstablishFails();
+ }
+
+ @Override
+ public void finish() {
+ mDevicePolicyManager.clearUserRestriction(mAdminReceiverComponent,
+ UserManager.DISALLOW_CONFIG_VPN);
+ super.finish();
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int result, Intent data) {
+ if (requestCode == REQUEST_VPN_CODE && result == RESULT_OK) {
+ establishVpn();
+ } else {
+ // vpn connection canceled by user
+ Log.w(TAG, "Test failed, canceled by user");
+ populateInfo(R.string.device_owner_vpn_connection_canceled);
+ }
+ }
+
+ public void testVpnEstablishFails() {
+ Intent newIntent = VpnService.prepare(this);
+ if (newIntent != null) {
+ startActivityForResult(newIntent, REQUEST_VPN_CODE);
+ } else {
+ establishVpn();
+ }
+ }
+
+ public void establishVpn() {
+ MyTestVpnService service = new MyTestVpnService();
+ descriptor = service.new Builder().addAddress("8.8.8.8", 30).establish();
+ if (descriptor == null) {
+ // vpn connection not established, as expected, test case succeeds
+ Log.i(TAG, "Test succeeded: descriptor is null");
+ populateInfo(R.string.device_owner_no_vpn_connection);
+ return;
+ }
+ // vpn connection established, not expected, test case fails
+ Log.w(TAG, "vpn connection established, not expected, test case fails");
+ try {
+ descriptor.close();
+ populateInfo(R.string.device_owner_vpn_connection);
+ } catch (IOException e) {
+ Log.i(TAG, "Closing vpn connection failed. Caught exception: ", e);
+ populateInfo(R.string.device_owner_vpn_connection_close_failed);
+ }
+ }
+
+ private void populateInfo(int messageId) {
+ TextView vpnInfoTextView = (TextView) findViewById(R.id.device_owner_vpn_info);
+ vpnInfoTextView.setText(getString(messageId));
+ }
+
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/WifiLockdownTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/WifiLockdownTestActivity.java
index 4a292eb..3babb8f 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/WifiLockdownTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/WifiLockdownTestActivity.java
@@ -126,13 +126,6 @@
});
}
- /**
- * Enable Pass Button when all tests passed.
- */
- private void updatePassButton() {
- getPassButton().setEnabled(mAdapter.allTestsPassed());
- }
-
private void addTestsToAdapter(final ArrayTestListAdapter adapter) {
adapter.add(DeviceOwnerPositiveTestActivity.createInteractiveTestItem(this,
CONFIG_MODIFIABLE_WHEN_UNLOCKED_TEST_ID,
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/WorkNotificationTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/WorkNotificationTestActivity.java
deleted file mode 100644
index a912d2c..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/WorkNotificationTestActivity.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.verifier.managedprovisioning;
-
-import android.app.Activity;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.admin.DevicePolicyManager;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-
-import com.android.cts.verifier.R;
-
-/**
- * Test activity used to generate a notification.
- */
-public class WorkNotificationTestActivity extends Activity {
- public static final String ACTION_WORK_NOTIFICATION =
- "com.android.cts.verifier.managedprovisioning.WORK_NOTIFICATION";
- public static final String ACTION_WORK_NOTIFICATION_ON_LOCKSCREEN =
- "com.android.cts.verifier.managedprovisioning.LOCKSCREEN_NOTIFICATION";
- public static final String ACTION_CLEAR_WORK_NOTIFICATION =
- "com.android.cts.verifier.managedprovisioning.CLEAR_WORK_NOTIFICATION";
- private static final int NOTIFICATION_ID = 7;
- private NotificationManager mNotificationManager;
-
- private void showWorkNotification(int visibility) {
- final Notification notification = new Notification.Builder(this)
- .setSmallIcon(R.drawable.icon)
- .setContentTitle(getString(R.string.provisioning_byod_work_notification_title))
- .setVisibility(visibility)
- .setAutoCancel(true)
- .build();
- mNotificationManager.notify(NOTIFICATION_ID, notification);
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- final String action = getIntent().getAction();
- mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- if (ACTION_WORK_NOTIFICATION.equals(action)) {
- showWorkNotification(Notification.VISIBILITY_PUBLIC);
- } else if (ACTION_WORK_NOTIFICATION_ON_LOCKSCREEN.equals(action)) {
- DevicePolicyManager dpm = (DevicePolicyManager) getSystemService(
- Context.DEVICE_POLICY_SERVICE);
- dpm.lockNow();
- showWorkNotification(Notification.VISIBILITY_PRIVATE);
- } else if (ACTION_CLEAR_WORK_NOTIFICATION.equals(action)) {
- mNotificationManager.cancel(NOTIFICATION_ID);
- }
- finish();
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/p2p/P2pTestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/p2p/P2pTestListActivity.java
index 920c9ae..5985be6 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/p2p/P2pTestListActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/p2p/P2pTestListActivity.java
@@ -153,13 +153,6 @@
}
/**
- * Enable Pass Button when the all tests passed.
- */
- private void updatePassButton() {
- getPassButton().setEnabled(mAdapter.allTestsPassed());
- }
-
- /**
* Receive the WIFI_P2P_STATE_CHANGED_ACTION action.
*/
class P2pBroadcastReceiver extends BroadcastReceiver {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/p2p/RequesterTestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/p2p/RequesterTestListActivity.java
index e6f94af..dd53d33 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/p2p/RequesterTestListActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/p2p/RequesterTestListActivity.java
@@ -103,11 +103,4 @@
adapter.add(TestListItem.newTest(testcase.getTestName(), testcase.getTestId(),
intent, null));
}
-
- /**
- * Enable Pass Button when the all tests passed.
- */
- private void updatePassButton() {
- getPassButton().setEnabled(mAdapter.allTestsPassed());
- }
}
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/DeviceSuspendTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/DeviceSuspendTestActivity.java
index 83e3054..ab8546b 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/DeviceSuspendTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/DeviceSuspendTestActivity.java
@@ -186,37 +186,35 @@
}
public String runAPWakeUpWhenReportLatencyExpires(Sensor sensor) throws Throwable {
+
+ verifyBatchingSupport(sensor);
+
int fifoMaxEventCount = sensor.getFifoMaxEventCount();
- if (fifoMaxEventCount == 0) {
- throw new SensorTestStateNotSupportedException("Batching not supported.");
- }
- int maximumExpectedSamplingPeriodUs = sensor.getMaxDelay();
- if (maximumExpectedSamplingPeriodUs == 0) {
+ int samplingPeriodUs = sensor.getMaxDelay();
+ if (samplingPeriodUs == 0) {
// If maxDelay is not defined, set the value for 5 Hz.
- maximumExpectedSamplingPeriodUs = 200000;
- }
- int fifoBasedReportLatencyUs = fifoMaxEventCount * maximumExpectedSamplingPeriodUs;
-
- // Ensure that FIFO based report latency is at least 20 seconds, we need at least 10
- // seconds of time to allow the device to be in suspend state.
- if (fifoBasedReportLatencyUs < 20000000L) {
- throw new SensorTestStateNotSupportedException("FIFO too small to test reliably");
+ samplingPeriodUs = 200000;
}
- final int MAX_REPORT_LATENCY_US = 15000000; // 15 seconds
+ long fifoBasedReportLatencyUs = maxBatchingPeriod(sensor, samplingPeriodUs);
+ verifyBatchingPeriod(fifoBasedReportLatencyUs);
+
+ final long MAX_REPORT_LATENCY_US = TimeUnit.SECONDS.toMicros(15); // 15 seconds
TestSensorEnvironment environment = new TestSensorEnvironment(
this,
sensor,
false,
- maximumExpectedSamplingPeriodUs,
- MAX_REPORT_LATENCY_US,
+ samplingPeriodUs,
+ (int) MAX_REPORT_LATENCY_US,
true /*isDeviceSuspendTest*/);
TestSensorOperation op = TestSensorOperation.createOperation(environment,
mDeviceSuspendLock,
false);
- final int ALARM_WAKE_UP_DELAY_MS = MAX_REPORT_LATENCY_US/1000 +
- (int)TimeUnit.SECONDS.toMillis(10);
+ final long ALARM_WAKE_UP_DELAY_MS =
+ TimeUnit.MICROSECONDS.toMillis(MAX_REPORT_LATENCY_US) +
+ TimeUnit.SECONDS.toMillis(10);
+
op.addVerification(BatchArrivalVerification.getDefault(environment));
mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() + ALARM_WAKE_UP_DELAY_MS,
@@ -231,35 +229,37 @@
}
public String runAPWakeUpWhenFIFOFull(Sensor sensor) throws Throwable {
- int fifoMaxEventCount = sensor.getFifoMaxEventCount();
- if (fifoMaxEventCount == 0) {
- throw new SensorTestStateNotSupportedException("Batching not supported.");
- }
+ verifyBatchingSupport(sensor);
+
// Try to fill the FIFO at the fastest rate and check if the time is enough to run
// the manual test.
- int maximumExpectedSamplingPeriodUs = sensor.getMinDelay();
- int fifoBasedReportLatencyUs = fifoMaxEventCount * maximumExpectedSamplingPeriodUs;
+ int samplingPeriodUs = sensor.getMinDelay();
- final int MIN_LATENCY_US = (int)TimeUnit.SECONDS.toMicros(20);
+ long fifoBasedReportLatencyUs = maxBatchingPeriod(sensor, samplingPeriodUs);
+
+ final long MIN_LATENCY_US = TimeUnit.SECONDS.toMicros(20);
// Ensure that FIFO based report latency is at least 20 seconds, we need at least 10
// seconds of time to allow the device to be in suspend state.
if (fifoBasedReportLatencyUs < MIN_LATENCY_US) {
- maximumExpectedSamplingPeriodUs = MIN_LATENCY_US/fifoMaxEventCount;
+ int fifoMaxEventCount = sensor.getFifoMaxEventCount();
+ samplingPeriodUs = (int) MIN_LATENCY_US/fifoMaxEventCount;
fifoBasedReportLatencyUs = MIN_LATENCY_US;
}
final int MAX_REPORT_LATENCY_US = Integer.MAX_VALUE;
- final int ALARM_WAKE_UP_DELAY_MS = fifoBasedReportLatencyUs/1000 +
- (int)TimeUnit.SECONDS.toMillis(10);
+ final long ALARM_WAKE_UP_DELAY_MS =
+ TimeUnit.MICROSECONDS.toMillis(fifoBasedReportLatencyUs) +
+ TimeUnit.SECONDS.toMillis(10);
+
TestSensorEnvironment environment = new TestSensorEnvironment(
this,
sensor,
false,
- maximumExpectedSamplingPeriodUs,
- MAX_REPORT_LATENCY_US,
+ (int) samplingPeriodUs,
+ (int) MAX_REPORT_LATENCY_US,
true /*isDeviceSuspendTest*/);
- TestSensorOperation op = TestSensorOperation.createOperation(environment,
+ TestSensorOperation op = TestSensorOperation.createOperation(environment,
mDeviceSuspendLock,
true);
mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
@@ -277,33 +277,26 @@
public String runAPWakeUpByAlarmNonWakeSensor(Sensor sensor, int maxReportLatencyUs)
throws Throwable {
- int fifoMaxEventCount = sensor.getFifoMaxEventCount();
- if (fifoMaxEventCount == 0) {
- throw new SensorTestStateNotSupportedException("Batching not supported.");
- }
- int maximumExpectedSamplingPeriodUs = sensor.getMaxDelay();
- if (maximumExpectedSamplingPeriodUs == 0 ||
- maximumExpectedSamplingPeriodUs > 200000) {
- // If maxDelay is not defined, set the value for 5 Hz.
- maximumExpectedSamplingPeriodUs = 200000;
- }
- int fifoBasedReportLatencyUs = fifoMaxEventCount * maximumExpectedSamplingPeriodUs;
+ verifyBatchingSupport(sensor);
- // Ensure that FIFO based report latency is at least 20 seconds, we need at least 10
- // seconds of time to allow the device to be in suspend state.
- if (fifoBasedReportLatencyUs < 20000000L) {
- throw new SensorTestStateNotSupportedException("FIFO too small to test reliably");
+ int samplingPeriodUs = sensor.getMaxDelay();
+ if (samplingPeriodUs == 0 || samplingPeriodUs > 200000) {
+ // If maxDelay is not defined, set the value for 5 Hz.
+ samplingPeriodUs = 200000;
}
+ long fifoBasedReportLatencyUs = maxBatchingPeriod(sensor, samplingPeriodUs);
+ verifyBatchingPeriod(fifoBasedReportLatencyUs);
+
TestSensorEnvironment environment = new TestSensorEnvironment(
this,
sensor,
false,
- maximumExpectedSamplingPeriodUs,
+ (int) samplingPeriodUs,
maxReportLatencyUs,
true /*isDeviceSuspendTest*/);
- final int ALARM_WAKE_UP_DELAY_MS = 20000;
+ final long ALARM_WAKE_UP_DELAY_MS = 20000;
TestSensorOperation op = TestSensorOperation.createOperation(environment,
mDeviceSuspendLock,
true);
@@ -318,4 +311,27 @@
}
return null;
}
+
+ private void verifyBatchingSupport(Sensor sensor)
+ throws SensorTestStateNotSupportedException {
+ int fifoMaxEventCount = sensor.getFifoMaxEventCount();
+ if (fifoMaxEventCount == 0) {
+ throw new SensorTestStateNotSupportedException("Batching not supported.");
+ }
+ }
+
+ private void verifyBatchingPeriod(long periodUs)
+ throws SensorTestStateNotSupportedException {
+ // Ensure that FIFO based report latency is at least 20 seconds, we need at least 10
+ // seconds of time to allow the device to be in suspend state.
+ if (periodUs < TimeUnit.SECONDS.toMicros(20)) {
+ throw new SensorTestStateNotSupportedException("FIFO too small to test reliably");
+ }
+ }
+
+ private long maxBatchingPeriod (Sensor sensor, long samplePeriod) {
+ long fifoMaxEventCount = sensor.getFifoMaxEventCount();
+ return fifoMaxEventCount * samplePeriod;
+ }
+
}
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/streamquality/StreamingVideoActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/streamquality/StreamingVideoActivity.java
index 9684d97..00a52ae 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/streamquality/StreamingVideoActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/streamquality/StreamingVideoActivity.java
@@ -117,7 +117,7 @@
private static final Stream[] HTTP_STREAMS = {
new Stream("H263 Video, AMR Audio", "http_h263_amr",
- "http://redirector.c.play.google.com/"
+ "http://redirector.gvt1.com/"
+ "videoplayback?id=271de9756065677e"
+ "&itag=13&ip=0.0.0.0&ipbits=0&expire=19000000000"
+ "&sparams=ip,ipbits,expire,id,itag"
@@ -126,7 +126,7 @@
+ "&source=youtube"
+ "&key=ik0&user=android-device-test"),
new Stream("MPEG4 SP Video, AAC Audio", "http_mpeg4_aac",
- "http://redirector.c.play.google.com/"
+ "http://redirector.gvt1.com/"
+ "videoplayback?id=271de9756065677e"
+ "&itag=17&ip=0.0.0.0&ipbits=0&expire=19000000000"
+ "&sparams=ip,ipbits,expire,id,itag"
@@ -135,7 +135,7 @@
+ "&source=youtube"
+ "&key=ik0&user=android-device-test"),
new Stream("H264 Base Video, AAC Audio", "http_h264_aac",
- "http://redirector.c.play.google.com/"
+ "http://redirector.gvt1.com/"
+ "videoplayback?id=271de9756065677e"
+ "&itag=18&ip=0.0.0.0&ipbits=0&expire=19000000000"
+ "&sparams=ip,ipbits,expire,id,itag"
@@ -220,10 +220,6 @@
i, null));
}
- private void updatePassButton() {
- getPassButton().setEnabled(mAdapter.allTestsPassed());
- }
-
/** @returns the appropriate RTSP url, or null in case of failure */
private String lookupRtspUrl(int itag, String signature) {
String rtspLookupUri = String.format(RTSP_LOOKUP_URI_TEMPLATE, itag, signature);
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/build/test_deqp_package.mk b/build/test_deqp_package.mk
index 8fbae26..650875c 100644
--- a/build/test_deqp_package.mk
+++ b/build/test_deqp_package.mk
@@ -18,15 +18,14 @@
CTS_DEQP_CONFIG_PATH := $(call my-dir)
-cts_library_xml := $(CTS_TESTCASES_OUT)/com.drawelements.deqp.$(DEQP_API).xml
-$(cts_library_xml): MUSTPASS_XML_FILE := external/deqp/android/cts/master/com.drawelements.deqp.$(DEQP_API).xml
-$(cts_library_xml): PRIVATE_TEST_NAME := $(DEQP_TEST_NAME)
-$(cts_library_xml): PRIVATE_TEST_PACKAGE := com.drawelements.deqp.$(DEQP_API)
-$(cts_library_xml): PRIVATE_DUMMY_CASELIST := $(CTS_DEQP_CONFIG_PATH)/deqp_dummy_test_list
-$(cts_library_xml): external/deqp/android/cts/master/com.drawelements.deqp.$(DEQP_API).xml $(CTS_EXPECTATIONS) $(CTS_UNSUPPORTED_ABIS) $(CTS_XML_GENERATOR)
- $(hide) echo Generating test description for $(PRIVATE_TEST_NAME)
+cts_library_xmls:=$(foreach xml_file, $(call find-files-in-subdirs, external/deqp/android/cts/master, 'com.drawelements.deqp.$(DEQP_API).*xml', .), $(CTS_TESTCASES_OUT)/$(xml_file))
+$(cts_library_xmls) : PRIVATE_API := $(DEQP_API)
+$(cts_library_xmls) : PRIVATE_TEST_NAME := $(DEQP_TEST_NAME)
+$(cts_library_xmls) : PRIVATE_TEST_PACKAGE := com.drawelements.deqp.$(DEQP_API)
+$(cts_library_xmls) : PRIVATE_DUMMY_CASELIST := $(CTS_DEQP_CONFIG_PATH)/deqp_dummy_test_list
+$(cts_library_xmls) : $(CTS_TESTCASES_OUT)/%.xml: external/deqp/android/cts/master/%.xml $(CTS_EXPECTATIONS) $(CTS_UNSUPPORTED_ABIS) $(CTS_XML_GENERATOR)
+ $(hide) echo Generating test description $@
$(hide) mkdir -p $(CTS_TESTCASES_OUT)
-
# Query build ABIs by routing a dummy test list through xml generator and parse result
$(hide) $(eval supported_abi_attr := $(shell $(CTS_XML_GENERATOR) -t dummyTest \
-n dummyName \
@@ -36,8 +35,7 @@
-a $(CTS_TARGET_ARCH) \
< $(PRIVATE_DUMMY_CASELIST) \
| grep --only-matching -e " abis=\"[^\"]*\""))
-
# Patch xml caselist with supported abi
$(hide) $(SED_EXTENDED) -e 's:^(\s*)<Test ((.[^/]|[^/])*)(/?)>$$:\1<Test \2 $(supported_abi_attr)\4>:' \
- < $(MUSTPASS_XML_FILE) \
+ < $< \
> $@
diff --git a/build/test_host_java_library.mk b/build/test_host_java_library.mk
index 7fdefb5..b4c7e63 100644
--- a/build/test_host_java_library.mk
+++ b/build/test_host_java_library.mk
@@ -29,6 +29,11 @@
cts_src_dirs := $(addprefix -s , $(cts_src_dirs))
cts_library_xml := $(CTS_TESTCASES_OUT)/$(LOCAL_MODULE).xml
+ifeq ($(cts_runtime_hint),)
+$(cts_library_xml): PRIVATE_CTS_RUNTIME_HINT := "0"
+else
+$(cts_library_xml): PRIVATE_CTS_RUNTIME_HINT := $(cts_runtime_hint)
+endif
$(cts_library_xml): PRIVATE_SRC_DIRS := $(cts_src_dirs)
$(cts_library_xml): PRIVATE_TEST_PACKAGE := $(LOCAL_CTS_TEST_PACKAGE)
$(cts_library_xml): PRIVATE_LIBRARY := $(LOCAL_MODULE)
@@ -44,6 +49,7 @@
-j $(PRIVATE_JAR_PATH) \
-n $(PRIVATE_LIBRARY) \
-p $(PRIVATE_TEST_PACKAGE) \
+ -x "runtimeHint->$(PRIVATE_CTS_RUNTIME_HINT)" \
-e $(CTS_EXPECTATIONS) \
-b $(CTS_UNSUPPORTED_ABIS) \
-a $(CTS_TARGET_ARCH) \
diff --git a/build/test_package.mk b/build/test_package.mk
index c6b0865..13e582e 100644
--- a/build/test_package.mk
+++ b/build/test_package.mk
@@ -39,6 +39,11 @@
else
PRIVATE_CTS_TEST_PACKAGE_NAME_ := android.$(notdir $(LOCAL_PATH))
endif
+ifeq ($(cts_runtime_hint),)
+$(cts_package_xml): PRIVATE_CTS_RUNTIME_HINT := "0"
+else
+$(cts_package_xml): PRIVATE_CTS_RUNTIME_HINT := $(cts_runtime_hint)
+endif
$(cts_package_xml): PRIVATE_TEST_PACKAGE := $(PRIVATE_CTS_TEST_PACKAGE_NAME_)
$(cts_package_xml): PRIVATE_MANIFEST := $(LOCAL_PATH)/AndroidManifest.xml
$(cts_package_xml): PRIVATE_TEST_TYPE := $(if $(LOCAL_CTS_TEST_RUNNER),$(LOCAL_CTS_TEST_RUNNER),'')
@@ -56,9 +61,13 @@
-i "$(PRIVATE_INSTRUMENTATION)" \
-n $(PRIVATE_PACKAGE) \
-p $(PRIVATE_TEST_PACKAGE) \
+ -x "runtimeHint->$(PRIVATE_CTS_RUNTIME_HINT)" \
-e $(CTS_EXPECTATIONS) \
-b $(CTS_UNSUPPORTED_ABIS) \
-a $(CTS_TARGET_ARCH) \
-o $@
# Have the module name depend on the cts files; so the cts files get generated when you run mm/mmm/mma/mmma.
$(my_register_name) : $(cts_package_xml) $(cts_module_test_config)
+
+# Make sure we clear cts_runtime_hint, so other parents will use the default if they do not set cts_runtime_hint.
+cts_runtime_hint :=
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/GenericDeviceInfo.java b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/GenericDeviceInfo.java
index f6e7c57..decf6dd 100644
--- a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/GenericDeviceInfo.java
+++ b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/GenericDeviceInfo.java
@@ -68,6 +68,8 @@
public static final String BUILD_SERIAL = "build_serial";
public static final String BUILD_VERSION_RELEASE = "build_version_release";
public static final String BUILD_VERSION_SDK = "build_version_sdk";
+ public static final String BUILD_VERSION_BASE_OS = "build_version_base_os";
+ public static final String BUILD_VERSION_SECURITY_PATH = "build_version_security_patch";
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -93,5 +95,7 @@
addResult(BUILD_SERIAL, Build.SERIAL);
addResult(BUILD_VERSION_RELEASE, Build.VERSION.RELEASE);
addResult(BUILD_VERSION_SDK, Build.VERSION.SDK);
+ addResult(BUILD_VERSION_BASE_OS, Build.VERSION.BASE_OS);
+ addResult(BUILD_VERSION_SECURITY_PATH, Build.VERSION.SECURITY_PATCH);
}
-}
\ No newline at end of file
+}
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/appsecurity/src/com/android/cts/appsecurity/ExternalStorageHostTest.java b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/ExternalStorageHostTest.java
index cb67c63..9c22bdc 100644
--- a/hostsidetests/appsecurity/src/com/android/cts/appsecurity/ExternalStorageHostTest.java
+++ b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/ExternalStorageHostTest.java
@@ -226,7 +226,9 @@
}
private void wipePrimaryExternalStorage() throws DeviceNotAvailableException {
- getDevice().executeShellCommand("rm -rf /sdcard/*");
+ getDevice().executeShellCommand("rm -rf /sdcard/Android");
+ getDevice().executeShellCommand("rm -rf /sdcard/DCIM");
+ getDevice().executeShellCommand("rm -rf /sdcard/MUST_*");
}
private int[] createUsersForTest() throws DeviceNotAvailableException {
diff --git a/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/src/com/android/cts/writeexternalstorageapp/WriteExternalStorageTest.java b/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/src/com/android/cts/writeexternalstorageapp/WriteExternalStorageTest.java
index 5e6aa3e..3861ddf 100644
--- a/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/src/com/android/cts/writeexternalstorageapp/WriteExternalStorageTest.java
+++ b/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/src/com/android/cts/writeexternalstorageapp/WriteExternalStorageTest.java
@@ -154,11 +154,10 @@
* storage.
*/
public void testPrimaryOtherPackageWriteAccess() throws Exception {
- deleteContents(Environment.getExternalStorageDirectory());
-
final File ourCache = getContext().getExternalCacheDir();
final File otherCache = new File(ourCache.getAbsolutePath()
.replace(getContext().getPackageName(), PACKAGE_NONE));
+ deleteContents(otherCache);
assertTrue(otherCache.mkdirs());
assertDirReadWriteAccess(otherCache);
@@ -237,8 +236,9 @@
* Verify that .nomedia is created correctly.
*/
public void testVerifyNoMediaCreated() throws Exception {
- deleteContents(Environment.getExternalStorageDirectory());
-
+ for (File file : getAllPackageSpecificPaths(getContext())) {
+ deleteContents(file);
+ }
final List<File> paths = getAllPackageSpecificPaths(getContext());
// Require that .nomedia was created somewhere above each dir
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/Android.mk b/hostsidetests/devicepolicy/Android.mk
index 6708c3d..dc5e117 100644
--- a/hostsidetests/devicepolicy/Android.mk
+++ b/hostsidetests/devicepolicy/Android.mk
@@ -26,6 +26,8 @@
LOCAL_CTS_TEST_PACKAGE := android.adminhostside
+cts_runtime_hint := 70
+
include $(BUILD_CTS_HOST_JAVA_LIBRARY)
# Build the test APKs using their own makefiles
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/apps/CtsVerifier/res/xml/device_admin.xml b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/res/xml/primary_device_admin.xml
similarity index 75%
rename from apps/CtsVerifier/res/xml/device_admin.xml
rename to hostsidetests/devicepolicy/app/DeviceAndProfileOwner/res/xml/primary_device_admin.xml
index 49d705a..a6aff49 100644
--- a/apps/CtsVerifier/res/xml/device_admin.xml
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/res/xml/primary_device_admin.xml
@@ -1,4 +1,4 @@
-<!-- Copyright (C) 2011 The Android Open Source Project
+<!-- 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.
@@ -12,14 +12,9 @@
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">
+<device-admin xmlns:android="http://schemas.android.com/apk/res/android" android:visible="false">
<uses-policies>
- <limit-password />
- <watch-login />
- <reset-password />
- <force-lock />
- <wipe-data />
- <expire-password />
+ <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/DeviceOwner/src/com/android/cts/deviceowner/CaCertManagementTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/CaCertManagementTest.java
index 9127dab..51c575c 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/CaCertManagementTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/CaCertManagementTest.java
@@ -15,69 +15,143 @@
*/
package com.android.cts.deviceowner;
-import static com.android.cts.deviceowner.FakeKeys.FAKE_RSA_1;
-import static com.android.cts.deviceowner.FakeKeys.FAKE_DSA_1;
-
import java.io.ByteArrayInputStream;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
-import java.security.cert.Certificate;
+import java.util.Arrays;
import java.util.List;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+
+import static com.android.cts.deviceowner.FakeKeys.FAKE_DSA_1;
+import static com.android.cts.deviceowner.FakeKeys.FAKE_RSA_1;
+
public class CaCertManagementTest extends BaseDeviceOwnerTest {
+ /**
+ * Test: device admins should be able to list all installed certs.
+ *
+ * <p>The list of certificates must never be {@code null}.
+ */
public void testCanRetrieveListOfInstalledCaCerts() {
List<byte[]> caCerts = mDevicePolicyManager.getInstalledCaCerts(getWho());
assertNotNull(caCerts);
}
+ /**
+ * Test: a valid cert should be installable and also removable.
+ */
public void testCanInstallAndUninstallACaCert()
- throws CertificateException {
- assertFalse(hasCaCertInstalled(FAKE_RSA_1.caCertificate));
- assertFalse(hasCaCertInstalled(FAKE_DSA_1.caCertificate));
+ throws CertificateException, GeneralSecurityException {
+ assertUninstalled(FAKE_RSA_1.caCertificate);
+ assertUninstalled(FAKE_DSA_1.caCertificate);
+
assertTrue(mDevicePolicyManager.installCaCert(getWho(), FAKE_RSA_1.caCertificate));
- assertTrue(hasCaCertInstalled(FAKE_RSA_1.caCertificate));
- assertFalse(hasCaCertInstalled(FAKE_DSA_1.caCertificate));
+ assertInstalled(FAKE_RSA_1.caCertificate);
+ assertUninstalled(FAKE_DSA_1.caCertificate);
+
mDevicePolicyManager.uninstallCaCert(getWho(), FAKE_RSA_1.caCertificate);
- assertFalse(hasCaCertInstalled(FAKE_RSA_1.caCertificate));
- assertFalse(hasCaCertInstalled(FAKE_DSA_1.caCertificate));
+ assertUninstalled(FAKE_RSA_1.caCertificate);
+ assertUninstalled(FAKE_DSA_1.caCertificate);
}
- public void testUninstallationIsSelective() throws CertificateException {
+ /**
+ * Test: removing one certificate must not remove any others.
+ */
+ public void testUninstallationIsSelective()
+ throws CertificateException, GeneralSecurityException {
assertTrue(mDevicePolicyManager.installCaCert(getWho(), FAKE_RSA_1.caCertificate));
assertTrue(mDevicePolicyManager.installCaCert(getWho(), FAKE_DSA_1.caCertificate));
+
mDevicePolicyManager.uninstallCaCert(getWho(), FAKE_DSA_1.caCertificate);
- assertTrue(hasCaCertInstalled(FAKE_RSA_1.caCertificate));
- assertFalse(hasCaCertInstalled(FAKE_DSA_1.caCertificate));
+ assertInstalled(FAKE_RSA_1.caCertificate);
+ assertUninstalled(FAKE_DSA_1.caCertificate);
+
mDevicePolicyManager.uninstallCaCert(getWho(), FAKE_RSA_1.caCertificate);
}
- public void testCanUninstallAllUserCaCerts() throws CertificateException {
+ /**
+ * Test: uninstallAllUserCaCerts should be equivalent to calling uninstallCaCert on every
+ * supplementary installed certificate.
+ */
+ public void testCanUninstallAllUserCaCerts()
+ throws CertificateException, GeneralSecurityException {
assertTrue(mDevicePolicyManager.installCaCert(getWho(), FAKE_RSA_1.caCertificate));
assertTrue(mDevicePolicyManager.installCaCert(getWho(), FAKE_DSA_1.caCertificate));
+
mDevicePolicyManager.uninstallAllUserCaCerts(getWho());
- assertFalse(hasCaCertInstalled(FAKE_RSA_1.caCertificate));
- assertFalse(hasCaCertInstalled(FAKE_DSA_1.caCertificate));
+ assertUninstalled(FAKE_RSA_1.caCertificate);
+ assertUninstalled(FAKE_DSA_1.caCertificate);
}
- private boolean hasCaCertInstalled(byte [] caCert) throws CertificateException {
- boolean result = mDevicePolicyManager.hasCaCertInstalled(getWho(), caCert);
- assertEquals(result, containsCertificate(
- mDevicePolicyManager.getInstalledCaCerts(getWho()), caCert));
- return result;
+ private void assertInstalled(byte[] caBytes)
+ throws CertificateException, GeneralSecurityException {
+ Certificate caCert = readCertificate(caBytes);
+ assertTrue(isCaCertInstalledAndTrusted(caCert));
}
- private static boolean containsCertificate(List<byte[]> certificates, byte [] toMatch)
- throws CertificateException {
- Certificate certificateToMatch = readCertificate(toMatch);
- for (byte[] certBuffer : certificates) {
- Certificate cert = readCertificate(certBuffer);
- if (certificateToMatch.equals(cert)) {
- return true;
+ private void assertUninstalled(byte[] caBytes)
+ throws CertificateException, GeneralSecurityException {
+ Certificate caCert = readCertificate(caBytes);
+ assertFalse(isCaCertInstalledAndTrusted(caCert));
+ }
+
+ /**
+ * Whether a given cert, or one a lot like it, has been installed system-wide and is available
+ * to all apps.
+ *
+ * <p>A CA certificate is "installed" if it matches all of the following conditions:
+ * <ul>
+ * <li>{@link DevicePolicyManager#hasCaCertInstalled} returns {@code true}.</li>
+ * <li>{@link DevicePolicyManager#getInstalledCaCerts} lists a matching certificate (not
+ * necessarily exactly the same) in its response.</li>
+ * <li>Any new instances of {@link TrustManager} should report the certificate among their
+ * accepted issuer list -- older instances may keep the set of issuers they were created
+ * with until explicitly refreshed.</li>
+ *
+ * @return {@code true} if installed by all metrics, {@code false} if not installed by any
+ * metric. In any other case an {@link AssertionError} will be thrown.
+ */
+ private boolean isCaCertInstalledAndTrusted(Certificate caCert)
+ throws GeneralSecurityException, CertificateException {
+ boolean installed = mDevicePolicyManager.hasCaCertInstalled(getWho(), caCert.getEncoded());
+
+ boolean listed = false;
+ for (byte[] certBuffer : mDevicePolicyManager.getInstalledCaCerts(getWho())) {
+ if (caCert.equals(readCertificate(certBuffer))) {
+ listed = true;
}
}
- return false;
+
+ boolean trusted = false;
+ final TrustManagerFactory tmf =
+ TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+ tmf.init((KeyStore) null);
+ for (TrustManager trustManager : tmf.getTrustManagers()) {
+ if (trustManager instanceof X509TrustManager) {
+ final X509TrustManager tm = (X509TrustManager) trustManager;
+ if (Arrays.asList(tm.getAcceptedIssuers()).contains(caCert)) {
+ trusted = true;
+ }
+ }
+ }
+
+ // All three responses should match - if an installed certificate isn't trusted or (worse)
+ // a trusted certificate isn't even installed we should
+ assertEquals(installed, listed);
+ assertEquals(installed, trusted);
+ return installed;
}
+ /**
+ * Convert an encoded certificate back into a {@link Certificate}.
+ *
+ * Instantiates a fresh CertificateFactory every time for repeatability.
+ */
private static Certificate readCertificate(byte[] certBuffer) throws CertificateException {
final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
return certFactory.generateCertificate(new ByteArrayInputStream(certBuffer));
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/KeyManagementTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/KeyManagementTest.java
old mode 100644
new mode 100755
index 5774b0c..27fd36f
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/KeyManagementTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/KeyManagementTest.java
@@ -47,7 +47,7 @@
public class KeyManagementTest extends
ActivityInstrumentationTestCase2<KeyManagementActivity> {
- private static final int KEYCHAIN_TIMEOUT_MS = 8000;
+ private static final int KEYCHAIN_TIMEOUT_MS = 6 * 60 * 1000;
private DevicePolicyManager mDevicePolicyManager;
public KeyManagementTest() {
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..6dc47e6 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();
}
@@ -209,13 +205,9 @@
// Now there's only the browser in the managed profile left
assertAppLinkResult("testReceivedByBrowserActivityInManaged");
- changeVerificationStatus(USER_OWNER, INTENT_RECEIVER_PKG, "always");
- changeVerificationStatus(mUserId, INTENT_RECEIVER_PKG, "ask");
- // We've set the receiver in the primary user to always: only this one should receive the
- // intent.
- assertAppLinkResult("testReceivedByAppLinkActivityInPrimary");
changeVerificationStatus(mUserId, INTENT_RECEIVER_PKG, "always");
+ changeVerificationStatus(USER_OWNER, INTENT_RECEIVER_PKG, "always");
// We have one always in the primary user and one always in the managed profile: the managed
// profile one should have precedence.
assertAppLinkResult("testReceivedByAppLinkActivityInManaged");
@@ -525,28 +517,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..0a454b6 100755
--- a/libs/deviceutil/src/android/cts/util/MediaUtils.java
+++ b/libs/deviceutil/src/android/cts/util/MediaUtils.java
@@ -160,6 +160,14 @@
return null;
}
+ public static boolean canEncode(MediaFormat format) {
+ if (sMCL.findEncoderForFormat(format) == null) {
+ Log.i(TAG, "no encoder for " + format);
+ return false;
+ }
+ return true;
+ }
+
public static boolean canDecode(MediaFormat format) {
if (sMCL.findDecoderForFormat(format) == null) {
Log.i(TAG, "no decoder for " + format);
@@ -330,13 +338,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()) {
@@ -386,6 +415,10 @@
return canDecode(format);
}
+ public static boolean checkEncoderForFormat(MediaFormat format) {
+ return check(canEncode(format), "no encoder for " + format);
+ }
+
public static boolean checkDecoderForFormat(MediaFormat format) {
return check(canDecode(format), "no decoder for " + format);
}
diff --git a/suite/cts/deviceTests/filesystemperf/Android.mk b/suite/cts/deviceTests/filesystemperf/Android.mk
index 843d21a..7ee93de 100644
--- a/suite/cts/deviceTests/filesystemperf/Android.mk
+++ b/suite/cts/deviceTests/filesystemperf/Android.mk
@@ -26,5 +26,7 @@
LOCAL_SDK_VERSION := 16
+cts_runtime_hint := 28
+
include $(BUILD_CTS_PACKAGE)
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/suite/cts/deviceTests/tvproviderperf/src/com/android/cts/tvproviderperf/TvProviderPerfTest.java b/suite/cts/deviceTests/tvproviderperf/src/com/android/cts/tvproviderperf/TvProviderPerfTest.java
index f9daa3c..8eef86d 100644
--- a/suite/cts/deviceTests/tvproviderperf/src/com/android/cts/tvproviderperf/TvProviderPerfTest.java
+++ b/suite/cts/deviceTests/tvproviderperf/src/com/android/cts/tvproviderperf/TvProviderPerfTest.java
@@ -157,12 +157,12 @@
// Query a channel
try (final Cursor cursor = mContentResolver.query(Channels.CONTENT_URI,
projection, null, null, null)) {
- final Uri channelUri = TvContract.buildChannelUri(cursor.getLong(0));
applyBatchTimes = MeasureTime.measure(QUERY_RUNS, new MeasureRun() {
@Override
public void run(int i) {
assertTrue(cursor.moveToNext());
- try (Cursor c = mContentResolver.query(channelUri, null, null, null, null)) {
+ try (Cursor c = mContentResolver.query(TvContract.buildChannelUri(
+ cursor.getLong(0)), null, null, null, null)) {
while (c.moveToNext()) {
// Do nothing. Just iterate all the items.
}
@@ -321,12 +321,12 @@
// Query a program
try (final Cursor cursor = mContentResolver.query(Programs.CONTENT_URI,
projection, null, null, null)) {
- final Uri programUri = TvContract.buildProgramUri(cursor.getLong(0));
applyBatchTimes = MeasureTime.measure(QUERY_RUNS, new MeasureRun() {
@Override
public void run(int i) {
assertTrue(cursor.moveToNext());
- try (Cursor c = mContentResolver.query(programUri, null, null, null, null)) {
+ try (Cursor c = mContentResolver.query(TvContract.buildProgramUri(
+ cursor.getLong(0)), null, null, null, null)) {
while (c.moveToNext()) {
// Do nothing. Just iterate all the items.
}
diff --git a/suite/cts/deviceTests/videoperf/Android.mk b/suite/cts/deviceTests/videoperf/Android.mk
index a393683..b589475 100644
--- a/suite/cts/deviceTests/videoperf/Android.mk
+++ b/suite/cts/deviceTests/videoperf/Android.mk
@@ -33,5 +33,7 @@
LOCAL_SDK_VERSION := current
+cts_runtime_hint := 50
+
include $(BUILD_CTS_PACKAGE)
diff --git a/tests/camera/Android.mk b/tests/camera/Android.mk
new file mode 100644
index 0000000..4cae7c4
--- /dev/null
+++ b/tests/camera/Android.mk
@@ -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.
+
+LOCAL_PATH:= $(call my-dir)
+
+# CtsCameraTestCases package
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil \
+ ctstestrunner \
+ mockito-target \
+ android-ex-camera2
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsCameraTestCases
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+cts_runtime_hint := 120
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/camera/AndroidManifest.xml b/tests/camera/AndroidManifest.xml
new file mode 100644
index 0000000..2b39fca
--- /dev/null
+++ b/tests/camera/AndroidManifest.xml
@@ -0,0 +1,81 @@
+<?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="android.camera.cts">
+ <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+ <uses-permission android:name="android.permission.CAMERA" />
+ <uses-permission android:name="android.permission.RECORD_AUDIO" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.REORDER_TASKS" />
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+
+ <activity android:name="android.hardware.cts.CameraCtsActivity"
+ android:label="CameraCtsActivity"
+ android:screenOrientation="landscape"
+ android:configChanges="keyboardHidden|orientation|screenSize">
+ </activity>
+
+ <activity android:name="android.hardware.camera2.cts.Camera2SurfaceViewCtsActivity"
+ android:label="Camera2CtsActivity"
+ android:screenOrientation="landscape"
+ android:configChanges="keyboardHidden|orientation|screenSize">
+ </activity>
+
+ <activity android:name="android.hardware.camera2.cts.Camera2MultiViewCtsActivity"
+ android:label="Camera2MultiViewCtsActivity"
+ android:screenOrientation="landscape"
+ android:configChanges="keyboardHidden|orientation|screenSize">
+ </activity>
+
+ <activity android:name="android.hardware.cts.GLSurfaceViewCtsActivity"
+ android:label="GLSurfaceViewCtsActivity"/>
+
+ <service android:name="android.hardware.multiprocess.camera.cts.ErrorLoggingService"
+ android:label="ErrorLoggingService"
+ android:process=":errorLoggingServiceProcess"
+ android:exported="false">
+ </service>
+
+ <activity android:name="android.hardware.multiprocess.camera.cts.Camera1Activity"
+ android:label="RemoteCamera1Activity"
+ android:screenOrientation="landscape"
+ android:configChanges="keyboardHidden|orientation|screenSize"
+ android:process=":camera1ActivityProcess">
+ </activity>
+
+ <activity android:name="android.hardware.multiprocess.camera.cts.Camera2Activity"
+ android:label="RemoteCamera2Activity"
+ android:screenOrientation="landscape"
+ android:configChanges="keyboardHidden|orientation|screenSize"
+ android:process=":camera2ActivityProcess">
+ </activity>
+
+ </application>
+
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.camera.cts"
+ android:label="CTS tests of android camera">
+ <meta-data android:name="listener"
+ android:value="com.android.cts.runner.CtsTestRunListener" />
+ </instrumentation>
+
+</manifest>
+
diff --git a/tests/tests/hardware/res/layout/multi_view.xml b/tests/camera/res/layout/multi_view.xml
similarity index 100%
rename from tests/tests/hardware/res/layout/multi_view.xml
rename to tests/camera/res/layout/multi_view.xml
diff --git a/tests/tests/hardware/res/layout/surface_view.xml b/tests/camera/res/layout/surface_view.xml
similarity index 100%
rename from tests/tests/hardware/res/layout/surface_view.xml
rename to tests/camera/res/layout/surface_view.xml
diff --git a/tests/tests/hardware/res/layout/surface_view_2.xml b/tests/camera/res/layout/surface_view_2.xml
similarity index 100%
rename from tests/tests/hardware/res/layout/surface_view_2.xml
rename to tests/camera/res/layout/surface_view_2.xml
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/AllocationTest.java b/tests/camera/src/android/hardware/camera2/cts/AllocationTest.java
similarity index 98%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/AllocationTest.java
rename to tests/camera/src/android/hardware/camera2/cts/AllocationTest.java
index 229185d..0476477 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/AllocationTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/AllocationTest.java
@@ -97,7 +97,6 @@
assertNotNull("Can't connect to camera manager!", mCameraManager);
RenderScriptSingleton.setContext(context);
- // TODO: call clearContext
}
@Override
@@ -117,11 +116,7 @@
@Override
protected void tearDown() throws Exception {
MaybeNull.close(mCamera);
-
- // TODO: Clean up RenderScript context in a static test run finished method.
- // Or alternatively count the # of test methods that are in this test,
- // once we reach that count, it's time to call the last tear down
-
+ RenderScriptSingleton.clearContext();
mHandlerThread.quitSafely();
mHandler = null;
super.tearDown();
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/BurstCaptureRawTest.java b/tests/camera/src/android/hardware/camera2/cts/BurstCaptureRawTest.java
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/BurstCaptureRawTest.java
rename to tests/camera/src/android/hardware/camera2/cts/BurstCaptureRawTest.java
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/BurstCaptureTest.java b/tests/camera/src/android/hardware/camera2/cts/BurstCaptureTest.java
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/BurstCaptureTest.java
rename to tests/camera/src/android/hardware/camera2/cts/BurstCaptureTest.java
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/Camera2MultiViewCtsActivity.java b/tests/camera/src/android/hardware/camera2/cts/Camera2MultiViewCtsActivity.java
similarity index 98%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/Camera2MultiViewCtsActivity.java
rename to tests/camera/src/android/hardware/camera2/cts/Camera2MultiViewCtsActivity.java
index 16d2301..d6350fc 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/Camera2MultiViewCtsActivity.java
+++ b/tests/camera/src/android/hardware/camera2/cts/Camera2MultiViewCtsActivity.java
@@ -23,7 +23,7 @@
import android.view.TextureView;
import android.view.WindowManager;
-import com.android.cts.hardware.R;
+import android.camera.cts.R;
public class Camera2MultiViewCtsActivity extends Activity {
private final static String TAG = "Camera2MultiViewCtsActivity";
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/Camera2SurfaceViewCtsActivity.java b/tests/camera/src/android/hardware/camera2/cts/Camera2SurfaceViewCtsActivity.java
similarity index 98%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/Camera2SurfaceViewCtsActivity.java
rename to tests/camera/src/android/hardware/camera2/cts/Camera2SurfaceViewCtsActivity.java
index 8a217fd..6773a9b 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/Camera2SurfaceViewCtsActivity.java
+++ b/tests/camera/src/android/hardware/camera2/cts/Camera2SurfaceViewCtsActivity.java
@@ -23,7 +23,7 @@
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
-import com.android.cts.hardware.R;
+import android.camera.cts.R;
public class Camera2SurfaceViewCtsActivity extends Activity implements SurfaceHolder.Callback {
private final static String TAG = "Camera2SurfaceViewCtsActivity";
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraDeviceTest.java b/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/CameraDeviceTest.java
rename to tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraManagerTest.java b/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java
similarity index 98%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/CameraManagerTest.java
rename to tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java
index 77a0c8e..67c08fe 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraManagerTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java
@@ -149,7 +149,8 @@
assertNotNull("Can't get lens facing info", lensFacing);
if (lensFacing == CameraCharacteristics.LENS_FACING_FRONT) {
assertTrue("System doesn't have front camera feature",
- mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT));
+ mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT) ||
+ mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_EXTERNAL));
} else if (lensFacing == CameraCharacteristics.LENS_FACING_BACK) {
assertTrue("System doesn't have back camera feature",
mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA));
@@ -534,7 +535,7 @@
java.util.concurrent.TimeUnit.MILLISECONDS);
assertTrue(String.format("Received unavailability notice for wrong ID " +
"(expected %s, got %s)", id, candidateId),
- id == candidateId);
+ id.equals(candidateId));
assertTrue("Availability events received unexpectedly",
availableEventQueue.size() == 0);
@@ -549,7 +550,7 @@
java.util.concurrent.TimeUnit.MILLISECONDS);
assertTrue(String.format("Received availability notice for wrong ID " +
"(expected %s, got %s)", id, candidateId),
- id == candidateId);
+ id.equals(candidateId));
assertTrue("Unavailability events received unexpectedly",
unavailableEventQueue.size() == 0);
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java b/tests/camera/src/android/hardware/camera2/cts/CameraTestUtils.java
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
rename to tests/camera/src/android/hardware/camera2/cts/CameraTestUtils.java
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java b/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java
similarity index 97%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java
rename to tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java
index 0fef3d7..dd4e3e3 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java
+++ b/tests/camera/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/CaptureResultTest.java b/tests/camera/src/android/hardware/camera2/cts/CaptureResultTest.java
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/CaptureResultTest.java
rename to tests/camera/src/android/hardware/camera2/cts/CaptureResultTest.java
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/DngCreatorTest.java b/tests/camera/src/android/hardware/camera2/cts/DngCreatorTest.java
similarity index 99%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/DngCreatorTest.java
rename to tests/camera/src/android/hardware/camera2/cts/DngCreatorTest.java
index 0da0ce7..bddbd52 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/DngCreatorTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/DngCreatorTest.java
@@ -75,6 +75,8 @@
@Override
protected void tearDown() throws Exception {
+ RenderScriptSingleton.clearContext();
+
super.tearDown();
}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java b/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
similarity index 99%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
rename to tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
index 6d62067..28693c6 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
@@ -178,9 +178,10 @@
if (activeArraySize.getWidth() >= FULLHD.getWidth() &&
activeArraySize.getHeight() >= FULLHD.getHeight()) {
- assertArrayContains(String.format(
+ assertArrayContainsAnyOf(String.format(
"Required FULLHD size not found for format %x for: ID %s",
- ImageFormat.JPEG, mIds[counter]), jpegSizes, FULLHD);
+ ImageFormat.JPEG, mIds[counter]), jpegSizes,
+ new Size[] {FULLHD, FULLHD_ALT});
}
if (activeArraySize.getWidth() >= HD.getWidth() &&
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/FlashlightTest.java b/tests/camera/src/android/hardware/camera2/cts/FlashlightTest.java
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/FlashlightTest.java
rename to tests/camera/src/android/hardware/camera2/cts/FlashlightTest.java
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/ImageReaderTest.java b/tests/camera/src/android/hardware/camera2/cts/ImageReaderTest.java
similarity index 95%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/ImageReaderTest.java
rename to tests/camera/src/android/hardware/camera2/cts/ImageReaderTest.java
index f1115c4..9c783e0 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/ImageReaderTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/ImageReaderTest.java
@@ -41,8 +41,11 @@
import android.util.Size;
import android.view.Surface;
+import com.android.ex.camera2.blocking.BlockingSessionCallback;
+
import java.nio.ByteBuffer;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import static android.hardware.camera2.cts.CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS;
@@ -275,6 +278,8 @@
* resolution and format supported.
*/
public void testAllOutputYUVResolutions() throws Exception {
+ Integer[] sessionStates = {BlockingSessionCallback.SESSION_READY,
+ BlockingSessionCallback.SESSION_CONFIGURE_FAILED};
for (String id : mCameraIds) {
try {
Log.v(TAG, "Testing all YUV image resolutions for camera " + id);
@@ -309,6 +314,7 @@
", at least one JPEG output is required.", jpegSizes.length == 0);
Size maxJpegSize = CameraTestUtils.getMaxSize(jpegSizes);
+ Size maxPreviewSize = mOrderedPreviewSizes.get(0);
for (int format : supportedYUVFormats) {
Size[] targetCaptureSizes =
@@ -343,6 +349,27 @@
outputSurfaces.add(jpegSurface);
createSession(outputSurfaces);
+ int state = mCameraSessionListener.getStateWaiter().waitForAnyOfStates(
+ Arrays.asList(sessionStates),
+ CameraTestUtils.SESSION_CONFIGURE_TIMEOUT_MS);
+
+ if (state == BlockingSessionCallback.SESSION_CONFIGURE_FAILED) {
+ if (captureSz.getWidth() > maxPreviewSize.getWidth() ||
+ captureSz.getHeight() > maxPreviewSize.getHeight()) {
+ Log.v(TAG, "Skip testing {yuv:" + captureSz
+ + " ,jpeg:" + maxJpegSize + "} for camera "
+ + mCamera.getId() +
+ " because full size jpeg + yuv larger than "
+ + "max preview size (" + maxPreviewSize
+ + ") is not supported");
+ continue;
+ } else {
+ fail("Camera " + mCamera.getId() +
+ ":session configuration failed for {jpeg: " +
+ maxJpegSize + ", yuv: " + captureSz + "}");
+ }
+ }
+
// Warm up camera preview (mainly to give legacy devices time to do 3A).
CaptureRequest.Builder warmupRequest =
mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/ImageWriterTest.java b/tests/camera/src/android/hardware/camera2/cts/ImageWriterTest.java
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/ImageWriterTest.java
rename to tests/camera/src/android/hardware/camera2/cts/ImageWriterTest.java
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/MultiViewTest.java b/tests/camera/src/android/hardware/camera2/cts/MultiViewTest.java
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/MultiViewTest.java
rename to tests/camera/src/android/hardware/camera2/cts/MultiViewTest.java
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/PerformanceTest.java b/tests/camera/src/android/hardware/camera2/cts/PerformanceTest.java
similarity index 99%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/PerformanceTest.java
rename to tests/camera/src/android/hardware/camera2/cts/PerformanceTest.java
index b14c551..39eb1dc 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/PerformanceTest.java
+++ b/tests/camera/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/camera/src/android/hardware/camera2/cts/RecordingTest.java
similarity index 95%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
rename to tests/camera/src/android/hardware/camera2/cts/RecordingTest.java
index a6cf613..de0e3d5 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
+++ b/tests/camera/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/camera/src/android/hardware/camera2/cts/ReprocessCaptureTest.java
similarity index 97%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/ReprocessCaptureTest.java
rename to tests/camera/src/android/hardware/camera2/cts/ReprocessCaptureTest.java
index 8a60dc2..dd49c8d 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/ReprocessCaptureTest.java
+++ b/tests/camera/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/camera/src/android/hardware/camera2/cts/RobustnessTest.java
similarity index 99%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java
rename to tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
index 8ccc931..ddae8eb 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java
+++ b/tests/camera/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/camera2/cts/StaticMetadataCollectionTest.java b/tests/camera/src/android/hardware/camera2/cts/StaticMetadataCollectionTest.java
similarity index 99%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/StaticMetadataCollectionTest.java
rename to tests/camera/src/android/hardware/camera2/cts/StaticMetadataCollectionTest.java
index 283f09b..145c2d1 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/StaticMetadataCollectionTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/StaticMetadataCollectionTest.java
@@ -18,8 +18,8 @@
import android.content.pm.PackageManager;
import android.cts.util.DeviceReportLog;
-import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
import android.hardware.camera2.cts.helpers.CameraMetadataGetter;
+import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
import android.util.Log;
import com.android.cts.util.ResultType;
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/StaticMetadataTest.java b/tests/camera/src/android/hardware/camera2/cts/StaticMetadataTest.java
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/StaticMetadataTest.java
rename to tests/camera/src/android/hardware/camera2/cts/StaticMetadataTest.java
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/StillCaptureTest.java b/tests/camera/src/android/hardware/camera2/cts/StillCaptureTest.java
similarity index 98%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/StillCaptureTest.java
rename to tests/camera/src/android/hardware/camera2/cts/StillCaptureTest.java
index 5e931fe..e7978c6 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/StillCaptureTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/StillCaptureTest.java
@@ -983,6 +983,8 @@
CaptureRequest.Builder stillRequest =
mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
boolean canSetAeLock = mStaticInfo.isAeLockSupported();
+ boolean canReadSensorSettings = mStaticInfo.isCapabilitySupported(
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS);
if (canSetAeLock) {
stillRequest.set(CaptureRequest.CONTROL_AE_LOCK, true);
@@ -991,19 +993,20 @@
CaptureResult normalResult;
CaptureResult compensatedResult;
- // The following variables should only be read under the MANUAL_SENSOR capability guard:
+ boolean canReadExposureValueRange = mStaticInfo.areKeysAvailable(
+ CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE,
+ CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE);
+ boolean canVerifyExposureValue = canReadSensorSettings && canReadExposureValueRange;
long minExposureValue = -1;
- long maxExposureTimeUs = -1;
long maxExposureValuePreview = -1;
long maxExposureValueStill = -1;
- if (mStaticInfo.isCapabilitySupported(
- CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
+ if (canReadExposureValueRange) {
// Minimum exposure settings is mostly static while maximum exposure setting depends on
// frame rate range which in term depends on capture request.
minExposureValue = mStaticInfo.getSensitivityMinimumOrDefault() *
mStaticInfo.getExposureMinimumOrDefault() / 1000;
long maxSensitivity = mStaticInfo.getSensitivityMaximumOrDefault();
- maxExposureTimeUs = mStaticInfo.getExposureMaximumOrDefault() / 1000;
+ long maxExposureTimeUs = mStaticInfo.getExposureMaximumOrDefault() / 1000;
maxExposureValuePreview = getMaxExposureValue(previewRequest, maxExposureTimeUs,
maxSensitivity);
maxExposureValueStill = getMaxExposureValue(stillRequest, maxExposureTimeUs,
@@ -1024,8 +1027,7 @@
normalResult = resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
long normalExposureValue = -1;
- if (mStaticInfo.isCapabilitySupported(
- CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS)) {
+ if (canVerifyExposureValue) {
// get and check if current exposure value is valid
normalExposureValue = getExposureValue(normalResult);
mCollector.expectInRange("Exposure setting out of bound", normalExposureValue,
@@ -1072,8 +1074,7 @@
compensatedResult = resultListener.getCaptureResultForRequest(
request, WAIT_FOR_RESULT_TIMEOUT_MS);
- if (mStaticInfo.isCapabilitySupported(
- CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS)) {
+ if (canVerifyExposureValue) {
// Verify the exposure value compensates as requested
long compensatedExposureValue = getExposureValue(compensatedResult);
mCollector.expectInRange("Exposure setting out of bound", compensatedExposureValue,
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java b/tests/camera/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
rename to tests/camera/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/common.rs b/tests/camera/src/android/hardware/camera2/cts/common.rs
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/common.rs
rename to tests/camera/src/android/hardware/camera2/cts/common.rs
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/crop_yuvf_420_to_yuvx_444.rs b/tests/camera/src/android/hardware/camera2/cts/crop_yuvf_420_to_yuvx_444.rs
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/crop_yuvf_420_to_yuvx_444.rs
rename to tests/camera/src/android/hardware/camera2/cts/crop_yuvf_420_to_yuvx_444.rs
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/AssertHelpers.java b/tests/camera/src/android/hardware/camera2/cts/helpers/AssertHelpers.java
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/helpers/AssertHelpers.java
rename to tests/camera/src/android/hardware/camera2/cts/helpers/AssertHelpers.java
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/Camera2Focuser.java b/tests/camera/src/android/hardware/camera2/cts/helpers/Camera2Focuser.java
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/helpers/Camera2Focuser.java
rename to tests/camera/src/android/hardware/camera2/cts/helpers/Camera2Focuser.java
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/CameraErrorCollector.java b/tests/camera/src/android/hardware/camera2/cts/helpers/CameraErrorCollector.java
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/helpers/CameraErrorCollector.java
rename to tests/camera/src/android/hardware/camera2/cts/helpers/CameraErrorCollector.java
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/CameraMetadataGetter.java b/tests/camera/src/android/hardware/camera2/cts/helpers/CameraMetadataGetter.java
old mode 100755
new mode 100644
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/helpers/CameraMetadataGetter.java
rename to tests/camera/src/android/hardware/camera2/cts/helpers/CameraMetadataGetter.java
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/CameraSessionUtils.java b/tests/camera/src/android/hardware/camera2/cts/helpers/CameraSessionUtils.java
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/helpers/CameraSessionUtils.java
rename to tests/camera/src/android/hardware/camera2/cts/helpers/CameraSessionUtils.java
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/InMatcher.java b/tests/camera/src/android/hardware/camera2/cts/helpers/InMatcher.java
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/helpers/InMatcher.java
rename to tests/camera/src/android/hardware/camera2/cts/helpers/InMatcher.java
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/MaybeNull.java b/tests/camera/src/android/hardware/camera2/cts/helpers/MaybeNull.java
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/helpers/MaybeNull.java
rename to tests/camera/src/android/hardware/camera2/cts/helpers/MaybeNull.java
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/Preconditions.java b/tests/camera/src/android/hardware/camera2/cts/helpers/Preconditions.java
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/helpers/Preconditions.java
rename to tests/camera/src/android/hardware/camera2/cts/helpers/Preconditions.java
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/StaticMetadata.java b/tests/camera/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
rename to tests/camera/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/UncheckedCloseable.java b/tests/camera/src/android/hardware/camera2/cts/helpers/UncheckedCloseable.java
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/helpers/UncheckedCloseable.java
rename to tests/camera/src/android/hardware/camera2/cts/helpers/UncheckedCloseable.java
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/means_yuvx_444_1d_to_single.rs b/tests/camera/src/android/hardware/camera2/cts/means_yuvx_444_1d_to_single.rs
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/means_yuvx_444_1d_to_single.rs
rename to tests/camera/src/android/hardware/camera2/cts/means_yuvx_444_1d_to_single.rs
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/means_yuvx_444_2d_to_1d.rs b/tests/camera/src/android/hardware/camera2/cts/means_yuvx_444_2d_to_1d.rs
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/means_yuvx_444_2d_to_1d.rs
rename to tests/camera/src/android/hardware/camera2/cts/means_yuvx_444_2d_to_1d.rs
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/AllocationCache.java b/tests/camera/src/android/hardware/camera2/cts/rs/AllocationCache.java
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/rs/AllocationCache.java
rename to tests/camera/src/android/hardware/camera2/cts/rs/AllocationCache.java
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/AllocationInfo.java b/tests/camera/src/android/hardware/camera2/cts/rs/AllocationInfo.java
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/rs/AllocationInfo.java
rename to tests/camera/src/android/hardware/camera2/cts/rs/AllocationInfo.java
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/BitmapUtils.java b/tests/camera/src/android/hardware/camera2/cts/rs/BitmapUtils.java
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/rs/BitmapUtils.java
rename to tests/camera/src/android/hardware/camera2/cts/rs/BitmapUtils.java
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/BlockingInputAllocation.java b/tests/camera/src/android/hardware/camera2/cts/rs/BlockingInputAllocation.java
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/rs/BlockingInputAllocation.java
rename to tests/camera/src/android/hardware/camera2/cts/rs/BlockingInputAllocation.java
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/RawConverter.java b/tests/camera/src/android/hardware/camera2/cts/rs/RawConverter.java
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/rs/RawConverter.java
rename to tests/camera/src/android/hardware/camera2/cts/rs/RawConverter.java
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/RenderScriptSingleton.java b/tests/camera/src/android/hardware/camera2/cts/rs/RenderScriptSingleton.java
similarity index 98%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/rs/RenderScriptSingleton.java
rename to tests/camera/src/android/hardware/camera2/cts/rs/RenderScriptSingleton.java
index 8e4c8e9..211dd0d 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/RenderScriptSingleton.java
+++ b/tests/camera/src/android/hardware/camera2/cts/rs/RenderScriptSingleton.java
@@ -70,7 +70,7 @@
sCache.close();
sCache = null;
- sRS.destroy();
+ sRS.releaseAllContexts();
sRS = null;
sContext = null;
}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/Script.java b/tests/camera/src/android/hardware/camera2/cts/rs/Script.java
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/rs/Script.java
rename to tests/camera/src/android/hardware/camera2/cts/rs/Script.java
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/ScriptGraph.java b/tests/camera/src/android/hardware/camera2/cts/rs/ScriptGraph.java
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/rs/ScriptGraph.java
rename to tests/camera/src/android/hardware/camera2/cts/rs/ScriptGraph.java
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/ScriptYuvCrop.java b/tests/camera/src/android/hardware/camera2/cts/rs/ScriptYuvCrop.java
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/rs/ScriptYuvCrop.java
rename to tests/camera/src/android/hardware/camera2/cts/rs/ScriptYuvCrop.java
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/ScriptYuvMeans1d.java b/tests/camera/src/android/hardware/camera2/cts/rs/ScriptYuvMeans1d.java
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/rs/ScriptYuvMeans1d.java
rename to tests/camera/src/android/hardware/camera2/cts/rs/ScriptYuvMeans1d.java
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/ScriptYuvMeans2dTo1d.java b/tests/camera/src/android/hardware/camera2/cts/rs/ScriptYuvMeans2dTo1d.java
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/rs/ScriptYuvMeans2dTo1d.java
rename to tests/camera/src/android/hardware/camera2/cts/rs/ScriptYuvMeans2dTo1d.java
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/ScriptYuvToRgb.java b/tests/camera/src/android/hardware/camera2/cts/rs/ScriptYuvToRgb.java
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/rs/ScriptYuvToRgb.java
rename to tests/camera/src/android/hardware/camera2/cts/rs/ScriptYuvToRgb.java
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/raw_converter.rs b/tests/camera/src/android/hardware/camera2/cts/rs/raw_converter.rs
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/rs/raw_converter.rs
rename to tests/camera/src/android/hardware/camera2/cts/rs/raw_converter.rs
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java
rename to tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java
rename to tests/camera/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
rename to tests/camera/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
diff --git a/tests/tests/hardware/src/android/hardware/cts/CameraCtsActivity.java b/tests/camera/src/android/hardware/cts/CameraCtsActivity.java
similarity index 97%
rename from tests/tests/hardware/src/android/hardware/cts/CameraCtsActivity.java
rename to tests/camera/src/android/hardware/cts/CameraCtsActivity.java
index 1153cac..61e283d 100644
--- a/tests/tests/hardware/src/android/hardware/cts/CameraCtsActivity.java
+++ b/tests/camera/src/android/hardware/cts/CameraCtsActivity.java
@@ -21,7 +21,7 @@
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.ViewGroup;
-import com.android.cts.hardware.R;
+import android.camera.cts.R;
public class CameraCtsActivity extends Activity {
private SurfaceView mSurfaceView;
diff --git a/tests/tests/hardware/src/android/hardware/cts/CameraGLTest.java b/tests/camera/src/android/hardware/cts/CameraGLTest.java
old mode 100755
new mode 100644
similarity index 99%
rename from tests/tests/hardware/src/android/hardware/cts/CameraGLTest.java
rename to tests/camera/src/android/hardware/cts/CameraGLTest.java
index 380e47d..8b0756e
--- a/tests/tests/hardware/src/android/hardware/cts/CameraGLTest.java
+++ b/tests/camera/src/android/hardware/cts/CameraGLTest.java
@@ -171,7 +171,7 @@
if (LOGV) Log.v(TAG, "Shutdown of camera complete.");
}
- /** The camera preview callback. Stops capture after the first callback */
+ /** The camera preview callback. */
private final class PreviewCallback
implements android.hardware.Camera.PreviewCallback {
public void onPreviewFrame(byte [] data, Camera camera) {
@@ -179,7 +179,6 @@
assertNotNull(data);
Size size = camera.getParameters().getPreviewSize();
assertEquals(size.width * size.height * 3 / 2, data.length);
- mCamera.stopPreview();
mPreviewDone.open();
}
}
@@ -350,6 +349,7 @@
mCamera.setPreviewTexture(mSurfaceTexture);
noTimeout = waitForPreviewDone();
assertTrue("Timeout waiting for new preview callback!", noTimeout);
+ mCamera.stopPreview();
terminateMessageLooper();
// Check the order: setPreviewTexture->startPreview.
@@ -359,6 +359,7 @@
mCamera.startPreview();
noTimeout = waitForPreviewDone();
assertTrue("Timeout waiting for new preview callback!", noTimeout);
+ mCamera.stopPreview();
// Check the order: setting preview display to null->startPreview->
// setPreviewTexture.
@@ -368,6 +369,7 @@
mCamera.setPreviewTexture(mSurfaceTexture);
noTimeout = waitForPreviewDone();
assertTrue("Timeout waiting for new preview callback!", noTimeout);
+ mCamera.stopPreview();
terminateMessageLooper();
}
};
@@ -396,6 +398,7 @@
assertTrue("Timeout waiting for new frame from SurfaceTexture!", noTimeout);
noTimeout = waitForPreviewDone();
assertTrue("Timeout waiting for new preview callback!",noTimeout);
+ mCamera.stopPreview();
mGLView.requestRender();
terminateMessageLooper();
@@ -413,6 +416,7 @@
assertTrue("Timeout waiting for new frame from SurfaceTexture!", noTimeout);
noTimeout = waitForPreviewDone();
assertTrue("Timeout waiting for new preview callback!", noTimeout);
+ mCamera.stopPreview();
mGLView.requestRender();
@@ -424,6 +428,7 @@
mCamera.setPreviewTexture(mSurfaceTexture);
noTimeout = waitForPreviewDone();
assertTrue(noTimeout);
+ mCamera.stopPreview();
terminateMessageLooper();
}
};
diff --git a/tests/tests/hardware/src/android/hardware/cts/CameraTest.java b/tests/camera/src/android/hardware/cts/CameraTest.java
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/cts/CameraTest.java
rename to tests/camera/src/android/hardware/cts/CameraTest.java
diff --git a/tests/tests/hardware/src/android/hardware/cts/Camera_ParametersTest.java b/tests/camera/src/android/hardware/cts/Camera_ParametersTest.java
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/cts/Camera_ParametersTest.java
rename to tests/camera/src/android/hardware/cts/Camera_ParametersTest.java
diff --git a/tests/tests/hardware/src/android/hardware/cts/Camera_SizeTest.java b/tests/camera/src/android/hardware/cts/Camera_SizeTest.java
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/cts/Camera_SizeTest.java
rename to tests/camera/src/android/hardware/cts/Camera_SizeTest.java
diff --git a/tests/tests/hardware/src/android/hardware/cts/GLSurfaceViewCtsActivity.java b/tests/camera/src/android/hardware/cts/GLSurfaceViewCtsActivity.java
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/cts/GLSurfaceViewCtsActivity.java
rename to tests/camera/src/android/hardware/cts/GLSurfaceViewCtsActivity.java
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/CameraUtils.java b/tests/camera/src/android/hardware/cts/helpers/CameraUtils.java
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/cts/helpers/CameraUtils.java
rename to tests/camera/src/android/hardware/cts/helpers/CameraUtils.java
diff --git a/tests/tests/hardware/src/android/hardware/multiprocess/camera/cts/Camera1Activity.java b/tests/camera/src/android/hardware/multiprocess/camera/cts/Camera1Activity.java
similarity index 97%
rename from tests/tests/hardware/src/android/hardware/multiprocess/camera/cts/Camera1Activity.java
rename to tests/camera/src/android/hardware/multiprocess/camera/cts/Camera1Activity.java
index 5c27111..3908334 100644
--- a/tests/tests/hardware/src/android/hardware/multiprocess/camera/cts/Camera1Activity.java
+++ b/tests/camera/src/android/hardware/multiprocess/camera/cts/Camera1Activity.java
@@ -18,7 +18,6 @@
import android.app.Activity;
import android.hardware.Camera;
-import android.hardware.multiprocess.ErrorLoggingService;
import android.os.Bundle;
import android.util.Log;
@@ -26,7 +25,7 @@
* Activity implementing basic access of the Camera1 API.
*
* <p />
- * This will log all errors to {@link android.hardware.multiprocess.ErrorLoggingService}.
+ * This will log all errors to {@link android.hardware.multiprocess.camera.cts.ErrorLoggingService}.
*/
public class Camera1Activity extends Activity {
private static final String TAG = "Camera1Activity";
@@ -90,4 +89,4 @@
mErrorServiceConnection = null;
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/tests/hardware/src/android/hardware/multiprocess/camera/cts/Camera2Activity.java b/tests/camera/src/android/hardware/multiprocess/camera/cts/Camera2Activity.java
similarity index 98%
rename from tests/tests/hardware/src/android/hardware/multiprocess/camera/cts/Camera2Activity.java
rename to tests/camera/src/android/hardware/multiprocess/camera/cts/Camera2Activity.java
index 2a78649..418eb7c 100644
--- a/tests/tests/hardware/src/android/hardware/multiprocess/camera/cts/Camera2Activity.java
+++ b/tests/camera/src/android/hardware/multiprocess/camera/cts/Camera2Activity.java
@@ -21,7 +21,6 @@
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
-import android.hardware.multiprocess.ErrorLoggingService;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
@@ -30,7 +29,7 @@
* Activity implementing basic access of the Camera2 API.
*
* <p />
- * This will log all errors to {@link android.hardware.multiprocess.ErrorLoggingService}.
+ * This will log all errors to {@link android.hardware.multiprocess.camera.cts.ErrorLoggingService}.
*/
public class Camera2Activity extends Activity {
private static final String TAG = "Camera2Activity";
diff --git a/tests/tests/hardware/src/android/hardware/multiprocess/camera/cts/CameraEvictionTest.java b/tests/camera/src/android/hardware/multiprocess/camera/cts/CameraEvictionTest.java
similarity index 95%
rename from tests/tests/hardware/src/android/hardware/multiprocess/camera/cts/CameraEvictionTest.java
rename to tests/camera/src/android/hardware/multiprocess/camera/cts/CameraEvictionTest.java
index aa34de3..9f1ae03 100644
--- a/tests/tests/hardware/src/android/hardware/multiprocess/camera/cts/CameraEvictionTest.java
+++ b/tests/camera/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;
@@ -24,7 +25,6 @@
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.cts.CameraCtsActivity;
-import android.hardware.multiprocess.ErrorLoggingService;
import android.os.Handler;
import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
@@ -112,7 +112,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 +233,7 @@
assertTrue("Remote camera service exited early", timeoutExceptionHit);
android.os.Process.killProcess(mProcessPid);
mProcessPid = -1;
+ forceCtsActivityToTop();
}
/**
@@ -337,6 +339,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 +404,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/hardware/src/android/hardware/multiprocess/ErrorLoggingService.java b/tests/camera/src/android/hardware/multiprocess/camera/cts/ErrorLoggingService.java
similarity index 99%
rename from tests/tests/hardware/src/android/hardware/multiprocess/ErrorLoggingService.java
rename to tests/camera/src/android/hardware/multiprocess/camera/cts/ErrorLoggingService.java
index 1b713ba..a45e024 100644
--- a/tests/tests/hardware/src/android/hardware/multiprocess/ErrorLoggingService.java
+++ b/tests/camera/src/android/hardware/multiprocess/camera/cts/ErrorLoggingService.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.hardware.multiprocess;
+package android.hardware.multiprocess.camera.cts;
import android.app.Service;
import android.content.ComponentName;
diff --git a/tests/tests/hardware/src/android/hardware/multiprocess/camera/cts/TestConstants.java b/tests/camera/src/android/hardware/multiprocess/camera/cts/TestConstants.java
similarity index 100%
rename from tests/tests/hardware/src/android/hardware/multiprocess/camera/cts/TestConstants.java
rename to tests/camera/src/android/hardware/multiprocess/camera/cts/TestConstants.java
diff --git a/tests/expectations/knownfailures.txt b/tests/expectations/knownfailures.txt
index 07ef7fa..2001485 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",
@@ -126,6 +165,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",
@@ -215,6 +269,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",
@@ -241,5 +302,77 @@
"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",
+ "android.jobscheduler.cts.ConnectivityConstraintTest#testConnectivityConstraintExecutes_withMobile"
+ ],
+ 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: "protected broadcast not working",
+ names: [
+ "android.permission2.cts.ProtectedBroadcastsTest#testSendProtectedBroadcasts"
+ ],
+ bug: 23192492
+},
+{
+ description: "restricted network is not working",
+ names: [
+ "android.net.cts.ConnectivityManagerTest#testRestrictedNetworks"
+ ],
+ bug: 25651805
+},
+{
+ description: "Read from invalid parcel not working",
+ names: [
+ "android.view.cts.MotionEventTest#testReadFromParcelWithInvalidSampleSize"
+ ],
+ bug: 25652250
}
]
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..f41100a 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;
@@ -53,23 +55,61 @@
@Override
protected void tearDown() throws Exception {
- mContext.unregisterReceiver(mActivityDoneReceiver);
+ if (mActivityDoneReceiver != null) {
+ try {
+ mContext.unregisterReceiver(mActivityDoneReceiver);
+ } catch (IllegalArgumentException e) {
+ // This exception is thrown if mActivityDoneReceiver in
+ // the above call to unregisterReceiver is never registered.
+ // If so, no harm done by ignoring this exception.
+ }
+ mActivityDoneReceiver = null;
+ }
super.tearDown();
}
private void registerBroadcastReceiver(TestcaseType testCaseType) throws Exception {
mTestCaseType = testCaseType;
mLatch = new CountDownLatch(1);
- if (mActivityDoneReceiver != null) {
- mContext.unregisterReceiver(mActivityDoneReceiver);
- }
mActivityDoneReceiver = new ActivityDoneReceiver();
mContext.registerReceiver(mActivityDoneReceiver,
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..bd96d76 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;
@@ -50,20 +51,67 @@
private static final String APPOPS_GET_SHELL_COMMAND = "appops get {0} {1}";
private static final long MINUTE = 1000 * 60;
+ private static final int TIMEOUT_MILLIS = 15000;
- private static final int[] sNetworkTypesToTest = new int[] {
- ConnectivityManager.TYPE_WIFI,
- ConnectivityManager.TYPE_MOBILE,
- };
+ private interface NetworkInterfaceToTest {
+ int getNetworkType();
+ int getTransportType();
+ String getSystemFeature();
+ String getErrorMessage();
+ }
- // Order corresponds to sNetworkTypesToTest
- private static final int[] sTransportTypesToTest = new int[] {
- NetworkCapabilities.TRANSPORT_WIFI,
- NetworkCapabilities.TRANSPORT_CELLULAR,
+ private static final NetworkInterfaceToTest[] sNetworkInterfacesToTest =
+ new NetworkInterfaceToTest[] {
+ new NetworkInterfaceToTest() {
+ @Override
+ public int getNetworkType() {
+ return ConnectivityManager.TYPE_WIFI;
+ }
+
+ @Override
+ public int getTransportType() {
+ return NetworkCapabilities.TRANSPORT_WIFI;
+ }
+
+ @Override
+ public String getSystemFeature() {
+ return PackageManager.FEATURE_WIFI;
+ }
+
+ @Override
+ public String getErrorMessage() {
+ return " Please make sure you are connected to a WiFi access point.";
+ }
+ },
+ new NetworkInterfaceToTest() {
+ @Override
+ public int getNetworkType() {
+ return ConnectivityManager.TYPE_MOBILE;
+ }
+
+ @Override
+ public int getTransportType() {
+ return NetworkCapabilities.TRANSPORT_CELLULAR;
+ }
+
+ @Override
+ public String getSystemFeature() {
+ return PackageManager.FEATURE_TELEPHONY;
+ }
+
+ @Override
+ public String getErrorMessage() {
+ return " 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 +119,49 @@
private String mWriteSettingsMode;
private String mUsageStatsMode;
- private void exerciseRemoteHost(int transportType) 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);
- }
- }
- }
- });
+ private void exerciseRemoteHost(Network network) throws Exception {
+ 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_MILLIS);
+ 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 +174,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);
@@ -190,19 +227,65 @@
}
}
- private boolean shouldTestThisNetworkType(int networkTypeIndex, long tolerance)
- throws Exception {
- NetworkInfo networkInfo = mCm.getNetworkInfo(sNetworkTypesToTest[networkTypeIndex]);
- if (networkInfo == null || !networkInfo.isAvailable()) {
- return false;
+ private class NetworkCallback extends ConnectivityManager.NetworkCallback {
+ private long mTolerance;
+ public boolean success;
+
+ NetworkCallback(long tolerance) {
+ mTolerance = tolerance;
+ success = false;
}
- mStartTime = System.currentTimeMillis() - tolerance;
- exerciseRemoteHost(sTransportTypesToTest[networkTypeIndex]);
- mEndTime = System.currentTimeMillis() + tolerance;
- return true;
+
+ @Override
+ public void onAvailable(Network network) {
+ try {
+ mStartTime = System.currentTimeMillis() - mTolerance;
+ exerciseRemoteHost(network);
+ mEndTime = System.currentTimeMillis() + mTolerance;
+ success = true;
+ synchronized(NetworkUsageStatsTest.this) {
+ NetworkUsageStatsTest.this.notify();
+ }
+ } catch (Exception e) {
+ Log.w(LOG_TAG, "exercising remote host failed.", e);
+ success = false;
+ }
+ }
}
- private String getSubscriberId(int networkType) {
+ private boolean shouldTestThisNetworkType(int networkTypeIndex, final long tolerance)
+ throws Exception {
+ boolean hasFeature = mPm.hasSystemFeature(
+ sNetworkInterfacesToTest[networkTypeIndex].getSystemFeature());
+ if (!hasFeature) {
+ return false;
+ }
+ NetworkCallback callback = new NetworkCallback(tolerance);
+ mCm.requestNetwork(new NetworkRequest.Builder()
+ .addTransportType(sNetworkInterfacesToTest[networkTypeIndex].getTransportType())
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .build(), callback);
+ synchronized(this) {
+ try {
+ wait((int)(TIMEOUT_MILLIS * 1.2));
+ } catch (InterruptedException e) {
+ }
+ }
+ if (callback.success) {
+ return true;
+ }
+
+ // This will always fail at this point as we know 'hasFeature' is true.
+ assertFalse (sNetworkInterfacesToTest[networkTypeIndex].getSystemFeature() +
+ " is a reported system feature, " +
+ "however no corresponding connected network interface was found or the attempt " +
+ "to connect has timed out (timeout = " + TIMEOUT_MILLIS + "ms)." +
+ sNetworkInterfacesToTest[networkTypeIndex].getErrorMessage(), hasFeature);
+ return false;
+ }
+
+ private String getSubscriberId(int networkIndex) {
+ int networkType = sNetworkInterfacesToTest[networkIndex].getNetworkType();
if (ConnectivityManager.TYPE_MOBILE == networkType) {
TelephonyManager tm = (TelephonyManager) getInstrumentation().getContext()
.getSystemService(Context.TELEPHONY_SERVICE);
@@ -212,7 +295,7 @@
}
public void testDeviceSummary() throws Exception {
- for (int i = 0; i < sNetworkTypesToTest.length; ++i) {
+ for (int i = 0; i < sNetworkInterfacesToTest.length; ++i) {
if (!shouldTestThisNetworkType(i, MINUTE/2)) {
continue;
}
@@ -220,7 +303,7 @@
NetworkStats.Bucket bucket = null;
try {
bucket = mNsm.querySummaryForDevice(
- sNetworkTypesToTest[i], getSubscriberId(sNetworkTypesToTest[i]),
+ sNetworkInterfacesToTest[i].getNetworkType(), getSubscriberId(i),
mStartTime, mEndTime);
} catch (RemoteException | SecurityException e) {
fail("testDeviceSummary fails with exception: " + e.toString());
@@ -232,7 +315,7 @@
setAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS, "deny");
try {
bucket = mNsm.querySummaryForDevice(
- sNetworkTypesToTest[i], getSubscriberId(sNetworkTypesToTest[i]),
+ sNetworkInterfacesToTest[i].getNetworkType(), getSubscriberId(i),
mStartTime, mEndTime);
fail("negative testDeviceSummary fails: no exception thrown.");
} catch (RemoteException e) {
@@ -244,7 +327,7 @@
}
public void testUserSummary() throws Exception {
- for (int i = 0; i < sNetworkTypesToTest.length; ++i) {
+ for (int i = 0; i < sNetworkInterfacesToTest.length; ++i) {
if (!shouldTestThisNetworkType(i, MINUTE/2)) {
continue;
}
@@ -252,7 +335,7 @@
NetworkStats.Bucket bucket = null;
try {
bucket = mNsm.querySummaryForUser(
- sNetworkTypesToTest[i], getSubscriberId(sNetworkTypesToTest[i]),
+ sNetworkInterfacesToTest[i].getNetworkType(), getSubscriberId(i),
mStartTime, mEndTime);
} catch (RemoteException | SecurityException e) {
fail("testUserSummary fails with exception: " + e.toString());
@@ -264,7 +347,7 @@
setAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS, "deny");
try {
bucket = mNsm.querySummaryForUser(
- sNetworkTypesToTest[i], getSubscriberId(sNetworkTypesToTest[i]),
+ sNetworkInterfacesToTest[i].getNetworkType(), getSubscriberId(i),
mStartTime, mEndTime);
fail("negative testUserSummary fails: no exception thrown.");
} catch (RemoteException e) {
@@ -276,7 +359,7 @@
}
public void testAppSummary() throws Exception {
- for (int i = 0; i < sNetworkTypesToTest.length; ++i) {
+ for (int i = 0; i < sNetworkInterfacesToTest.length; ++i) {
if (!shouldTestThisNetworkType(i, MINUTE/2)) {
continue;
}
@@ -284,7 +367,7 @@
NetworkStats result = null;
try {
result = mNsm.querySummary(
- sNetworkTypesToTest[i], getSubscriberId(sNetworkTypesToTest[i]),
+ sNetworkInterfacesToTest[i].getNetworkType(), getSubscriberId(i),
mStartTime, mEndTime);
assertTrue(result != null);
NetworkStats.Bucket bucket = new NetworkStats.Bucket();
@@ -317,7 +400,7 @@
setAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS, "deny");
try {
result = mNsm.querySummary(
- sNetworkTypesToTest[i], getSubscriberId(sNetworkTypesToTest[i]),
+ sNetworkInterfacesToTest[i].getNetworkType(), getSubscriberId(i),
mStartTime, mEndTime);
fail("negative testAppSummary fails: no exception thrown.");
} catch (RemoteException e) {
@@ -329,7 +412,7 @@
}
public void testAppDetails() throws Exception {
- for (int i = 0; i < sNetworkTypesToTest.length; ++i) {
+ for (int i = 0; i < sNetworkInterfacesToTest.length; ++i) {
// Relatively large tolerance to accommodate for history bucket size.
if (!shouldTestThisNetworkType(i, MINUTE * 120)) {
continue;
@@ -338,7 +421,7 @@
NetworkStats result = null;
try {
result = mNsm.queryDetails(
- sNetworkTypesToTest[i], getSubscriberId(sNetworkTypesToTest[i]),
+ sNetworkInterfacesToTest[i].getNetworkType(), getSubscriberId(i),
mStartTime, mEndTime);
assertTrue(result != null);
NetworkStats.Bucket bucket = new NetworkStats.Bucket();
@@ -372,7 +455,7 @@
setAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS, "deny");
try {
result = mNsm.queryDetails(
- sNetworkTypesToTest[i], getSubscriberId(sNetworkTypesToTest[i]),
+ sNetworkInterfacesToTest[i].getNetworkType(), getSubscriberId(i),
mStartTime, mEndTime);
fail("negative testAppDetails fails: no exception thrown.");
} catch (RemoteException e) {
@@ -384,7 +467,7 @@
}
public void testUidDetails() throws Exception {
- for (int i = 0; i < sNetworkTypesToTest.length; ++i) {
+ for (int i = 0; i < sNetworkInterfacesToTest.length; ++i) {
// Relatively large tolerance to accommodate for history bucket size.
if (!shouldTestThisNetworkType(i, MINUTE * 120)) {
continue;
@@ -393,7 +476,7 @@
NetworkStats result = null;
try {
result = mNsm.queryDetailsForUid(
- sNetworkTypesToTest[i], getSubscriberId(sNetworkTypesToTest[i]),
+ sNetworkInterfacesToTest[i].getNetworkType(), getSubscriberId(i),
mStartTime, mEndTime, Process.myUid());
assertTrue(result != null);
NetworkStats.Bucket bucket = new NetworkStats.Bucket();
@@ -426,7 +509,7 @@
setAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS, "deny");
try {
result = mNsm.queryDetailsForUid(
- sNetworkTypesToTest[i], getSubscriberId(sNetworkTypesToTest[i]),
+ sNetworkInterfacesToTest[i].getNetworkType(), getSubscriberId(i),
mStartTime, mEndTime, Process.myUid());
fail("negative testUidDetails fails: no exception thrown.");
} catch (RemoteException e) {
diff --git a/tests/tests/app/src/android/app/cts/ActivityManagerMemoryClassTest.java b/tests/tests/app/src/android/app/cts/ActivityManagerMemoryClassTest.java
index dfa278a..639741d 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,13 +51,28 @@
= 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);
expectedMemorySizeForSmallNormalScreen.put(DisplayMetrics.DENSITY_HIGH, 48);
expectedMemorySizeForSmallNormalScreen.put(DisplayMetrics.DENSITY_280, 48);
- expectedMemorySizeForSmallNormalScreen.put(DisplayMetrics.DENSITY_XHIGH, 48);
- expectedMemorySizeForSmallNormalScreen.put(DisplayMetrics.DENSITY_360, 64);
+ expectedMemorySizeForSmallNormalScreen.put(DisplayMetrics.DENSITY_XHIGH, 80);
+ expectedMemorySizeForSmallNormalScreen.put(DisplayMetrics.DENSITY_360, 80);
expectedMemorySizeForSmallNormalScreen.put(DisplayMetrics.DENSITY_400, 96);
expectedMemorySizeForSmallNormalScreen.put(DisplayMetrics.DENSITY_420, 112);
expectedMemorySizeForSmallNormalScreen.put(DisplayMetrics.DENSITY_XXHIGH, 128);
@@ -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/apps/CtsVerifier/res/xml/device_admin.xml b/tests/tests/assist/res/layout/screenshot_activity.xml
similarity index 60%
copy from apps/CtsVerifier/res/xml/device_admin.xml
copy to tests/tests/assist/res/layout/screenshot_activity.xml
index 49d705a..05051dc 100644
--- a/apps/CtsVerifier/res/xml/device_admin.xml
+++ b/tests/tests/assist/res/layout/screenshot_activity.xml
@@ -1,4 +1,5 @@
-<!-- Copyright (C) 2011 The Android Open Source Project
+<?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.
@@ -12,14 +13,10 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-<device-admin xmlns:android="http://schemas.android.com/apk/res/android">
- <uses-policies>
- <limit-password />
- <watch-login />
- <reset-password />
- <force-lock />
- <wipe-data />
- <expire-password />
- </uses-policies>
-</device-admin>
+<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..c21bc9c 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,67 @@
@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 {
+ if (mActivityManager.isLowRamDevice()) {
+ Log.d(TAG, "Not running assist tests on low-RAM device.");
+ return;
+ }
+ 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..29b35c5 100644
--- a/tests/tests/assist/src/android/assist/cts/AssistTestBase.java
+++ b/tests/tests/assist/src/android/assist/cts/AssistTestBase.java
@@ -16,40 +16,62 @@
package android.assist.cts;
-import android.assist.TestStartActivity;
+import android.assist.cts.TestStartActivity;
import android.assist.common.Utils;
+import android.app.ActivityManager;
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 ActivityManager mActivityManager;
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,39 +80,60 @@
@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();
+ mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
}
/**
* 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 +142,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 +178,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 +206,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 +251,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 +465,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..c6ac3a6
--- /dev/null
+++ b/tests/tests/assist/src/android/assist/cts/AssistantContentViewTest.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 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 {
+ if (mActivityManager.isLowRamDevice()) {
+ Log.d(TAG, "Not running assist tests on low-RAM device.");
+ return;
+ }
+ 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..ea4cd3d 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;
@@ -64,7 +63,12 @@
}
public void testContextAndScreenshotOff() throws Exception {
+ if (mActivityManager.isLowRamDevice()) {
+ Log.d(TAG, "Not running assist tests on low-RAM device.");
+ return;
+ }
// 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 +80,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 +92,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..1154179
--- /dev/null
+++ b/tests/tests/assist/src/android/assist/cts/ExtraAssistDataTest.java
@@ -0,0 +1,119 @@
+/*
+ * 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 {
+ if (mActivityManager.isLowRamDevice()) {
+ Log.d(TAG, "Not running assist tests on low-RAM device.");
+ return;
+ }
+ 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..fc7c8fb3 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,17 @@
}
public void testSecureActivity() throws Exception {
+ if (mActivityManager.isLowRamDevice()) {
+ Log.d(TAG, "Not running assist tests on low-RAM device.");
+ return;
+ }
+ 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 +97,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..621361e
--- /dev/null
+++ b/tests/tests/assist/src/android/assist/cts/FocusChangeTest.java
@@ -0,0 +1,116 @@
+/*
+ * 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 {
+ if (mActivityManager.isLowRamDevice()) {
+ Log.d(TAG, "Not running assist tests on low-RAM device.");
+ return;
+ }
+ 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..25f36b7
--- /dev/null
+++ b/tests/tests/assist/src/android/assist/cts/LargeViewHierarchyTest.java
@@ -0,0 +1,109 @@
+/*
+ * 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 {
+ if (mActivityManager.isLowRamDevice()) {
+ Log.d(TAG, "Not running assist tests on low-RAM device.");
+ return;
+ }
+ 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();
+ }
+ }
+ }
+}
diff --git a/tests/tests/assist/src/android/assist/cts/LifecycleTest.java b/tests/tests/assist/src/android/assist/cts/LifecycleTest.java
index 19a1be5..3ed26d1 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
@@ -95,8 +98,12 @@
}
public void testLayerDoesNotTriggerLifecycleMethods() throws Exception {
+ if (mActivityManager.isLowRamDevice()) {
+ Log.d(TAG, "Not running assist tests on low-RAM device.");
+ return;
+ }
mTestActivity.startTest(Utils.LIFECYCLE);
- waitForAssistantToBeReady();
+ waitForAssistantToBeReady(mReadyLatch);
mTestActivity.start3pApp(Utils.LIFECYCLE);
waitForOnResume();
startSession();
@@ -108,16 +115,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..b84e334
--- /dev/null
+++ b/tests/tests/assist/src/android/assist/cts/ScreenshotTest.java
@@ -0,0 +1,146 @@
+/*
+ * 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 {
+ if (mActivityManager.isLowRamDevice()) {
+ Log.d(TAG, "Not running assist tests on low-RAM device.");
+ return;
+ }
+ 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 {
+ if (mActivityManager.isLowRamDevice()) {
+ Log.d(TAG, "Not running assist tests on low-RAM device.");
+ return;
+ }
+ 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 {
+ if (mActivityManager.isLowRamDevice()) {
+ Log.d(TAG, "Not running assist tests on low-RAM device.");
+ return;
+ }
+ 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..089993d
--- /dev/null
+++ b/tests/tests/assist/src/android/assist/cts/TextViewTest.java
@@ -0,0 +1,137 @@
+/*
+ * 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 {
+ if (mActivityManager.isLowRamDevice()) {
+ Log.d(TAG, "Not running assist tests on low-RAM device.");
+ return;
+ }
+ 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();
+ }
+ }
+ }
+}
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..4e05494
--- /dev/null
+++ b/tests/tests/assist/src/android/assist/cts/WebViewTest.java
@@ -0,0 +1,125 @@
+/*
+ * 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 (mActivityManager.isLowRamDevice()) {
+ Log.d(TAG, "Not running assist tests on low-RAM device.");
+ return;
+ }
+ 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();
+ }
+ }
+ }
+}
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/apps/CtsVerifier/res/xml/device_admin.xml b/tests/tests/assist/testapp/res/layout/screenshot_activity.xml
similarity index 60%
copy from apps/CtsVerifier/res/xml/device_admin.xml
copy to tests/tests/assist/testapp/res/layout/screenshot_activity.xml
index 49d705a..05051dc 100644
--- a/apps/CtsVerifier/res/xml/device_admin.xml
+++ b/tests/tests/assist/testapp/res/layout/screenshot_activity.xml
@@ -1,4 +1,5 @@
-<!-- Copyright (C) 2011 The Android Open Source Project
+<?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.
@@ -12,14 +13,10 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-<device-admin xmlns:android="http://schemas.android.com/apk/res/android">
- <uses-policies>
- <limit-password />
- <watch-login />
- <reset-password />
- <force-lock />
- <wipe-data />
- <expire-password />
- </uses-policies>
-</device-admin>
+<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/ContextWrapperTest.java b/tests/tests/content/src/android/content/cts/ContextWrapperTest.java
index 672d3ed..edc9538 100644
--- a/tests/tests/content/src/android/content/cts/ContextWrapperTest.java
+++ b/tests/tests/content/src/android/content/cts/ContextWrapperTest.java
@@ -157,6 +157,7 @@
registerBroadcastReceiver(lowPriorityReceiver, filterLowPriority);
final Intent broadcastIntent = new Intent(ResultReceiver.MOCK_ACTION);
+ broadcastIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
mContextWrapper.sendOrderedBroadcast(broadcastIntent, null);
new PollingCheck(BROADCAST_TIMEOUT) {
@Override
@@ -186,8 +187,10 @@
Bundle bundle = new Bundle();
bundle.putString(KEY_KEPT, VALUE_KEPT);
bundle.putString(KEY_REMOVED, VALUE_REMOVED);
- mContextWrapper.sendOrderedBroadcast(new Intent(ResultReceiver.MOCK_ACTION),
- null, broadcastReceiver, null, 1, INTIAL_RESULT, bundle);
+ Intent intent = new Intent(ResultReceiver.MOCK_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ mContextWrapper.sendOrderedBroadcast(intent, null, broadcastReceiver, null, 1,
+ INTIAL_RESULT, bundle);
synchronized (mLockObj) {
try {
@@ -216,13 +219,13 @@
// Test unwanted intent(action = MOCK_ACTION2)
broadcastReceiver.reset();
- waitForFilteredIntent(mContextWrapper, broadcastReceiver, MOCK_ACTION2);
+ waitForFilteredIntent(mContextWrapper, MOCK_ACTION2);
assertFalse(broadcastReceiver.hadReceivedBroadCast1());
assertFalse(broadcastReceiver.hadReceivedBroadCast2());
// Send wanted intent(action = MOCK_ACTION1)
broadcastReceiver.reset();
- waitForFilteredIntent(mContextWrapper, broadcastReceiver, MOCK_ACTION1);
+ waitForFilteredIntent(mContextWrapper, MOCK_ACTION1);
assertTrue(broadcastReceiver.hadReceivedBroadCast1());
assertFalse(broadcastReceiver.hadReceivedBroadCast2());
@@ -235,13 +238,13 @@
// Test unwanted intent(action = MOCK_ACTION2)
broadcastReceiver2.reset();
- waitForFilteredIntent(mContextWrapper, broadcastReceiver2, MOCK_ACTION2);
+ waitForFilteredIntent(mContextWrapper, MOCK_ACTION2);
assertFalse(broadcastReceiver2.hadReceivedBroadCast1());
assertFalse(broadcastReceiver2.hadReceivedBroadCast2());
// Send wanted intent(action = MOCK_ACTION1), but the receiver is unregistered.
broadcastReceiver2.reset();
- waitForFilteredIntent(mContextWrapper, broadcastReceiver2, MOCK_ACTION1);
+ waitForFilteredIntent(mContextWrapper, MOCK_ACTION1);
assertFalse(broadcastReceiver2.hadReceivedBroadCast1());
assertFalse(broadcastReceiver2.hadReceivedBroadCast2());
}
@@ -256,13 +259,13 @@
// Test unwanted intent(action = MOCK_ACTION2)
broadcastReceiver.reset();
- waitForFilteredIntent(mContextWrapper, broadcastReceiver, MOCK_ACTION2);
+ waitForFilteredIntent(mContextWrapper, MOCK_ACTION2);
assertFalse(broadcastReceiver.hadReceivedBroadCast1());
assertFalse(broadcastReceiver.hadReceivedBroadCast2());
// Send wanted intent(action = MOCK_ACTION1)
broadcastReceiver.reset();
- waitForFilteredIntent(mContextWrapper, broadcastReceiver, MOCK_ACTION1);
+ waitForFilteredIntent(mContextWrapper, MOCK_ACTION1);
assertTrue(broadcastReceiver.hadReceivedBroadCast1());
assertFalse(broadcastReceiver.hadReceivedBroadCast2());
@@ -792,9 +795,9 @@
waitForCondition(con);
}
- private void waitForFilteredIntent(ContextWrapper contextWrapper,
- final FilteredReceiver receiver, final String action) throws InterruptedException {
- contextWrapper.sendOrderedBroadcast(new Intent(action), null);
+ private void waitForFilteredIntent(ContextWrapper contextWrapper, final String action)
+ throws InterruptedException {
+ contextWrapper.sendBroadcast(new Intent(action), null);
synchronized (mLockObj) {
mLockObj.wait(BROADCAST_TIMEOUT);
@@ -849,7 +852,6 @@
private class FilteredReceiver extends BroadcastReceiver {
private boolean mHadReceivedBroadCast1 = false;
-
private boolean mHadReceivedBroadCast2 = false;
public void onReceive(Context context, Intent intent) {
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/Android.mk b/tests/tests/hardware/Android.mk
index 9c26d8a..07d5de9 100644
--- a/tests/tests/hardware/Android.mk
+++ b/tests/tests/hardware/Android.mk
@@ -57,4 +57,6 @@
LOCAL_JAVA_LIBRARIES := android.test.runner
+cts_runtime_hint := 120
+
include $(BUILD_CTS_PACKAGE)
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/SensorBatchingFifoTest.java b/tests/tests/hardware/src/android/hardware/cts/SensorBatchingFifoTest.java
index 4c6362a..328b5cd 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorBatchingFifoTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorBatchingFifoTest.java
@@ -114,7 +114,7 @@
sensor,
false,
sensor.getMinDelay(),
- Integer.MAX_VALUE);
+ Integer.MAX_VALUE /*maxReportLatencyUs*/);
TestSensorOperation op = TestSensorOperation.createOperation(environment,
sensor.getFifoReservedEventCount() * 2);
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorBatchingTests.java b/tests/tests/hardware/src/android/hardware/cts/SensorBatchingTests.java
index 7f7d4eb..4d4f3d3 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorBatchingTests.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorBatchingTests.java
@@ -22,6 +22,7 @@
import android.hardware.cts.helpers.SensorStats;
import android.hardware.cts.helpers.TestSensorEnvironment;
import android.hardware.cts.helpers.sensoroperations.TestSensorOperation;
+import android.hardware.cts.helpers.sensorverification.EventBasicVerification;
import android.hardware.cts.helpers.sensorverification.ISensorVerification;
import java.util.concurrent.TimeUnit;
@@ -44,7 +45,7 @@
public class SensorBatchingTests extends SensorTestCase {
private static final String TAG = "SensorBatchingTests";
- private static final int BATCHING_10S = 10;
+ private static final int BATCHING_PERIOD = 10; // sec
private static final int RATE_50HZ = 20000;
private static final int RATE_FASTEST = SensorManager.SENSOR_DELAY_FASTEST;
@@ -52,202 +53,202 @@
* An arbitrary 'padding' time slot to wait for events after batching latency expires.
* This allows for the test to wait for event arrivals after batching was expected.
*/
- private static final int BATCHING_PADDING_TIME_S = 2;
+ private static final int BATCHING_PADDING_TIME_S = (int) Math.ceil(BATCHING_PERIOD * 0.1f + 2);
public void testAccelerometer_fastest_batching() throws Throwable {
- runBatchingSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_FASTEST, BATCHING_10S);
+ runBatchingSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_FASTEST, BATCHING_PERIOD);
}
public void testAccelerometer_50hz_batching() throws Throwable {
- runBatchingSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_50HZ, BATCHING_10S);
+ runBatchingSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_50HZ, BATCHING_PERIOD);
}
public void testAccelerometer_fastest_flush() throws Throwable {
- runFlushSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_FASTEST, BATCHING_10S);
+ runFlushSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_FASTEST, BATCHING_PERIOD);
}
public void testAccelerometer_50hz_flush() throws Throwable {
- runFlushSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_50HZ, BATCHING_10S);
+ runFlushSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_50HZ, BATCHING_PERIOD);
}
public void testMagneticField_fastest_batching() throws Throwable {
- runBatchingSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_FASTEST, BATCHING_10S);
+ runBatchingSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_FASTEST, BATCHING_PERIOD);
}
public void testMagneticField_50hz_batching() throws Throwable {
- runBatchingSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_50HZ, BATCHING_10S);
+ runBatchingSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_50HZ, BATCHING_PERIOD);
}
public void testMagneticField_fastest_flush() throws Throwable {
- runFlushSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_FASTEST, BATCHING_10S);
+ runFlushSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_FASTEST, BATCHING_PERIOD);
}
public void testMagneticField_50hz_flush() throws Throwable {
- runFlushSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_50HZ, BATCHING_10S);
+ runFlushSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_50HZ, BATCHING_PERIOD);
}
@SuppressWarnings("deprecation")
public void testOrientation_fastest_batching() throws Throwable {
- runBatchingSensorTest(Sensor.TYPE_ORIENTATION, RATE_FASTEST, BATCHING_10S);
+ runBatchingSensorTest(Sensor.TYPE_ORIENTATION, RATE_FASTEST, BATCHING_PERIOD);
}
@SuppressWarnings("deprecation")
public void testOrientation_50hz_batching() throws Throwable {
- runBatchingSensorTest(Sensor.TYPE_ORIENTATION, RATE_50HZ, BATCHING_10S);
+ runBatchingSensorTest(Sensor.TYPE_ORIENTATION, RATE_50HZ, BATCHING_PERIOD);
}
@SuppressWarnings("deprecation")
public void testOrientation_fastest_flush() throws Throwable {
- runFlushSensorTest(Sensor.TYPE_ORIENTATION, RATE_FASTEST, BATCHING_10S);
+ runFlushSensorTest(Sensor.TYPE_ORIENTATION, RATE_FASTEST, BATCHING_PERIOD);
}
@SuppressWarnings("deprecation")
public void testOrientation_50hz_flush() throws Throwable {
- runFlushSensorTest(Sensor.TYPE_ORIENTATION, RATE_50HZ, BATCHING_10S);
+ runFlushSensorTest(Sensor.TYPE_ORIENTATION, RATE_50HZ, BATCHING_PERIOD);
}
public void testGyroscope_fastest_batching() throws Throwable {
- runBatchingSensorTest(Sensor.TYPE_GYROSCOPE, RATE_FASTEST, BATCHING_10S);
+ runBatchingSensorTest(Sensor.TYPE_GYROSCOPE, RATE_FASTEST, BATCHING_PERIOD);
}
public void testGyroscope_50hz_batching() throws Throwable {
- runBatchingSensorTest(Sensor.TYPE_GYROSCOPE, RATE_50HZ, BATCHING_10S);
+ runBatchingSensorTest(Sensor.TYPE_GYROSCOPE, RATE_50HZ, BATCHING_PERIOD);
}
public void testGyroscope_fastest_flush() throws Throwable {
- runFlushSensorTest(Sensor.TYPE_GYROSCOPE, SensorManager.SENSOR_DELAY_FASTEST, BATCHING_10S);
+ runFlushSensorTest(Sensor.TYPE_GYROSCOPE, SensorManager.SENSOR_DELAY_FASTEST, BATCHING_PERIOD);
}
public void testGyroscope_50hz_flush() throws Throwable {
- runFlushSensorTest(Sensor.TYPE_GYROSCOPE, RATE_50HZ, BATCHING_10S);
+ runFlushSensorTest(Sensor.TYPE_GYROSCOPE, RATE_50HZ, BATCHING_PERIOD);
}
public void testPressure_fastest_batching() throws Throwable {
- runBatchingSensorTest(Sensor.TYPE_PRESSURE, RATE_FASTEST, BATCHING_10S);
+ runBatchingSensorTest(Sensor.TYPE_PRESSURE, RATE_FASTEST, BATCHING_PERIOD);
}
public void testPressure_50hz_batching() throws Throwable {
- runBatchingSensorTest(Sensor.TYPE_PRESSURE, RATE_50HZ, BATCHING_10S);
+ runBatchingSensorTest(Sensor.TYPE_PRESSURE, RATE_50HZ, BATCHING_PERIOD);
}
public void testPressure_fastest_flush() throws Throwable {
- runFlushSensorTest(Sensor.TYPE_PRESSURE, SensorManager.SENSOR_DELAY_FASTEST, BATCHING_10S);
+ runFlushSensorTest(Sensor.TYPE_PRESSURE, SensorManager.SENSOR_DELAY_FASTEST, BATCHING_PERIOD);
}
public void testPressure_50hz_flush() throws Throwable {
- runFlushSensorTest(Sensor.TYPE_PRESSURE, RATE_50HZ, BATCHING_10S);
+ runFlushSensorTest(Sensor.TYPE_PRESSURE, RATE_50HZ, BATCHING_PERIOD);
}
public void testGravity_fastest_batching() throws Throwable {
- runBatchingSensorTest(Sensor.TYPE_GRAVITY, RATE_FASTEST, BATCHING_10S);
+ runBatchingSensorTest(Sensor.TYPE_GRAVITY, RATE_FASTEST, BATCHING_PERIOD);
}
public void testGravity_50hz_batching() throws Throwable {
- runBatchingSensorTest(Sensor.TYPE_GRAVITY, RATE_50HZ, BATCHING_10S);
+ runBatchingSensorTest(Sensor.TYPE_GRAVITY, RATE_50HZ, BATCHING_PERIOD);
}
public void testGravity_fastest_flush() throws Throwable {
- runFlushSensorTest(Sensor.TYPE_GRAVITY, SensorManager.SENSOR_DELAY_FASTEST, BATCHING_10S);
+ runFlushSensorTest(Sensor.TYPE_GRAVITY, SensorManager.SENSOR_DELAY_FASTEST, BATCHING_PERIOD);
}
public void testGravity_50hz_flush() throws Throwable {
- runFlushSensorTest(Sensor.TYPE_GRAVITY, RATE_50HZ, BATCHING_10S);
+ runFlushSensorTest(Sensor.TYPE_GRAVITY, RATE_50HZ, BATCHING_PERIOD);
}
public void testRotationVector_fastest_batching() throws Throwable {
- runBatchingSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_FASTEST, BATCHING_10S);
+ runBatchingSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_FASTEST, BATCHING_PERIOD);
}
public void testRotationVector_50hz_batching() throws Throwable {
- runBatchingSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_50HZ, BATCHING_10S);
+ runBatchingSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_50HZ, BATCHING_PERIOD);
}
public void testRotationVector_fastest_flush() throws Throwable {
- runFlushSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_FASTEST, BATCHING_10S);
+ runFlushSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_FASTEST, BATCHING_PERIOD);
}
public void testRotationVector_50hz_flush() throws Throwable {
- runFlushSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_50HZ, BATCHING_10S);
+ runFlushSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_50HZ, BATCHING_PERIOD);
}
public void testMagneticFieldUncalibrated_fastest_batching() throws Throwable {
- runBatchingSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_FASTEST, BATCHING_10S);
+ runBatchingSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_FASTEST, BATCHING_PERIOD);
}
public void testMagneticFieldUncalibrated_50hz_batching() throws Throwable {
- runBatchingSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_50HZ, BATCHING_10S);
+ runBatchingSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_50HZ, BATCHING_PERIOD);
}
public void testMagneticFieldUncalibrated_fastest_flush() throws Throwable {
- runFlushSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_FASTEST, BATCHING_10S);
+ runFlushSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_FASTEST, BATCHING_PERIOD);
}
public void testMagneticFieldUncalibrated_50hz_flush() throws Throwable {
- runFlushSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_50HZ, BATCHING_10S);
+ runFlushSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_50HZ, BATCHING_PERIOD);
}
public void testGameRotationVector_fastest_batching() throws Throwable {
- runBatchingSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_FASTEST, BATCHING_10S);
+ runBatchingSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_FASTEST, BATCHING_PERIOD);
}
public void testGameRotationVector_50hz_batching() throws Throwable {
- runBatchingSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_50HZ, BATCHING_10S);
+ runBatchingSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_50HZ, BATCHING_PERIOD);
}
public void testGameRotationVector_fastest_flush() throws Throwable {
- runFlushSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_FASTEST, BATCHING_10S);
+ runFlushSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_FASTEST, BATCHING_PERIOD);
}
public void testGameRotationVector_50hz_flush() throws Throwable {
- runFlushSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_50HZ, BATCHING_10S);
+ runFlushSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_50HZ, BATCHING_PERIOD);
}
public void testGyroscopeUncalibrated_fastest_batching() throws Throwable {
- runBatchingSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_FASTEST, BATCHING_10S);
+ runBatchingSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_FASTEST, BATCHING_PERIOD);
}
public void testGyroscopeUncalibrated_50hz_batching() throws Throwable {
- runBatchingSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_50HZ, BATCHING_10S);
+ runBatchingSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_50HZ, BATCHING_PERIOD);
}
public void testGyroscopeUncalibrated_fastest_flush() throws Throwable {
- runFlushSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_FASTEST, BATCHING_10S);
+ runFlushSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_FASTEST, BATCHING_PERIOD);
}
public void testGyroscopeUncalibrated_50hz_flush() throws Throwable {
- runFlushSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_50HZ, BATCHING_10S);
+ runFlushSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_50HZ, BATCHING_PERIOD);
}
public void testLinearAcceleration_fastest_batching() throws Throwable {
- runBatchingSensorTest(Sensor.TYPE_LINEAR_ACCELERATION, RATE_FASTEST, BATCHING_10S);
+ runBatchingSensorTest(Sensor.TYPE_LINEAR_ACCELERATION, RATE_FASTEST, BATCHING_PERIOD);
}
public void testLinearAcceleration_50hz_batching() throws Throwable {
- runBatchingSensorTest(Sensor.TYPE_LINEAR_ACCELERATION, RATE_50HZ, BATCHING_10S);
+ runBatchingSensorTest(Sensor.TYPE_LINEAR_ACCELERATION, RATE_50HZ, BATCHING_PERIOD);
}
public void testLinearAcceleration_fastest_flush() throws Throwable {
- runFlushSensorTest(Sensor.TYPE_LINEAR_ACCELERATION, RATE_FASTEST, BATCHING_10S);
+ runFlushSensorTest(Sensor.TYPE_LINEAR_ACCELERATION, RATE_FASTEST, BATCHING_PERIOD);
}
public void testLinearAcceleration_50hz_flush() throws Throwable {
- runFlushSensorTest(Sensor.TYPE_LINEAR_ACCELERATION, RATE_50HZ, BATCHING_10S);
+ runFlushSensorTest(Sensor.TYPE_LINEAR_ACCELERATION, RATE_50HZ, BATCHING_PERIOD);
}
public void testGeomagneticRotationVector_fastest_batching() throws Throwable {
- runBatchingSensorTest(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, RATE_FASTEST, BATCHING_10S);
+ runBatchingSensorTest(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, RATE_FASTEST, BATCHING_PERIOD);
}
public void testGeomagneticRotationVector_50hz_batching() throws Throwable {
- runBatchingSensorTest(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, RATE_50HZ, BATCHING_10S);
+ runBatchingSensorTest(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, RATE_50HZ, BATCHING_PERIOD);
}
public void testGeomagneticRotationVector_fastest_flush() throws Throwable {
- runFlushSensorTest(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, RATE_FASTEST, BATCHING_10S);
+ runFlushSensorTest(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, RATE_FASTEST, BATCHING_PERIOD);
}
public void testGeomagneticRotationVector_50hz_flush() throws Throwable {
- runFlushSensorTest(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, RATE_50HZ, BATCHING_10S);
+ runFlushSensorTest(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, RATE_50HZ, BATCHING_PERIOD);
}
private void runBatchingSensorTest(int sensorType, int rateUs, int maxBatchReportLatencySec)
@@ -264,6 +265,12 @@
TestSensorOperation operation =
TestSensorOperation.createOperation(environment, testDurationSec, TimeUnit.SECONDS);
+ operation.addVerification(
+ EventBasicVerification.getDefault(
+ environment, TimeUnit.SECONDS.toMicros(testDurationSec)
+ )
+ );
+
executeTest(environment, operation, false /* flushExpected */);
}
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..2bbf053 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);
@@ -168,8 +168,8 @@
}
sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
- // orientation sensor is required if the device can physically implement it
- if (hasCompass && hasAccelerometer) {
+ // Note: orientation sensor is deprecated.
+ if (sensor != null) {
assertEquals(Sensor.TYPE_ORIENTATION, sensor.getType());
assertSensorValues(sensor);
}
@@ -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/SensorStats.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java
index 413639a..ad9b0cd 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java
@@ -50,6 +50,8 @@
"event_time_synchronization_count";
public static final String EVENT_TIME_SYNCHRONIZATION_POSITIONS_KEY =
"event_time_synchronization_positions";
+ public static final String EVENT_COUNT_KEY = "event_count";
+ public static final String WRONG_SENSOR_KEY = "wrong_sensor_observed";
public static final String FREQUENCY_KEY = "frequency";
public static final String JITTER_95_PERCENTILE_PERCENT_KEY = "jitter_95_percentile_percent";
public static final String MEAN_KEY = "mean";
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/sensoroperations/TestSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java
index 11fdc93..67dca68 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java
@@ -31,6 +31,7 @@
import android.hardware.cts.helpers.TestSensorManager;
import android.hardware.cts.helpers.SuspendStateMonitor;
import android.hardware.cts.helpers.reporting.ISensorTestNode;
+import android.hardware.cts.helpers.sensorverification.EventBasicVerification;
import android.hardware.cts.helpers.sensorverification.EventGapVerification;
import android.hardware.cts.helpers.sensorverification.EventOrderingVerification;
import android.hardware.cts.helpers.sensorverification.EventTimestampSynchronizationVerification;
@@ -145,9 +146,9 @@
for (ISensorVerification verification : mVerifications) {
failed |= evaluateResults(collectedEvents, verification, sb);
}
+
if (failed) {
trySaveCollectedEvents(parent, listener);
-
String msg = SensorCtsHelper
.formatAssertionMessage("VerifySensorOperation", mEnvironment, sb.toString());
getStats().addValue(SensorStats.ERROR, msg);
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventBasicVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventBasicVerification.java
new file mode 100644
index 0000000..c27cba1
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventBasicVerification.java
@@ -0,0 +1,138 @@
+/*
+ * 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.hardware.cts.helpers.sensorverification;
+
+import junit.framework.Assert;
+
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.TestSensorEnvironment;
+import android.hardware.cts.helpers.TestSensorEvent;
+import android.os.SystemClock;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A {@link ISensorVerification} which verifies if the collected sensor events have any obvious
+ * problems, such as no sample, wrong sensor type, etc.
+ */
+public class EventBasicVerification extends AbstractSensorVerification {
+
+ public static final String PASSED_KEY = "event_basic_passed";
+ private static final long ALLOWED_SENSOR_DELIVERING_DELAY_US =
+ TimeUnit.MILLISECONDS.toMicros(1000);
+
+ private final long mExpectedMinNumEvent;
+ private final Object mSensor;
+ private long mNumEvent;
+ private boolean mWrongSensorObserved;
+
+ /**
+ * Constructs an instance of {@link EventBasicVerification}.
+ *
+ * @param maximumSynchronizationErrorNs The valid threshold for timestamp synchronization.
+ * @param reportLatencyNs The latency on which batching events are received
+ */
+ public EventBasicVerification(
+ long expectedMinNumEvent,
+ Sensor sensor) {
+ mExpectedMinNumEvent = expectedMinNumEvent;
+ mSensor = sensor;
+
+ mNumEvent = 0;
+ mWrongSensorObserved = false;
+ }
+
+ /**
+ * Gets a default {@link EventBasicVerification}.
+ *
+ * @param environment The test environment
+ * @return The verification or null if the verification is not supported in the given
+ * environment.
+ */
+ public static EventBasicVerification getDefault(
+ TestSensorEnvironment environment,
+ long testDurationUs) {
+
+ long minTestDurationUs;
+ long batchUs = environment.getMaxReportLatencyUs();
+ long sampleUs = environment.getExpectedSamplingPeriodUs();
+ if (batchUs > 0) {
+ // test duration deduct allowed delivering latency and portion of time to deliver batch
+ // (which will be 10% of the batching time)
+ long effectiveTime = testDurationUs - ALLOWED_SENSOR_DELIVERING_DELAY_US - batchUs/10;
+
+ // allow part of last batch to be partially delivered (>80%)
+ minTestDurationUs = Math.max(
+ effectiveTime/batchUs * batchUs - batchUs/5,
+ environment.getExpectedSamplingPeriodUs());
+ } else {
+ minTestDurationUs =
+ Math.max(testDurationUs - ALLOWED_SENSOR_DELIVERING_DELAY_US,
+ environment.getExpectedSamplingPeriodUs());
+ }
+
+ long expectedMinNumEvent = minTestDurationUs / environment.getExpectedSamplingPeriodUs();
+ return new EventBasicVerification(expectedMinNumEvent, environment.getSensor());
+ }
+
+ @Override
+ public void verify(TestSensorEnvironment environment, SensorStats stats) {
+ verify(stats);
+ }
+
+ /* visible to unit test */
+ void verify(SensorStats stats) {
+
+ stats.addValue(SensorStats.EVENT_COUNT_KEY, mNumEvent);
+ stats.addValue(SensorStats.WRONG_SENSOR_KEY, mWrongSensorObserved);
+
+ boolean enoughSample = mNumEvent >= mExpectedMinNumEvent;
+ boolean noWrongSensor = !mWrongSensorObserved;
+
+ boolean success = enoughSample && noWrongSensor;
+ stats.addValue(PASSED_KEY, success);
+
+ if (!success) {
+ Assert.fail(String.format("Failed due to (%s%s)",
+ enoughSample?"":"insufficient events, ",
+ noWrongSensor?"":"wrong sensor observed, "));
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public EventBasicVerification clone() {
+ return new EventBasicVerification( mExpectedMinNumEvent, (Sensor)mSensor );
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void addSensorEventInternal(TestSensorEvent event) {
+ if (event.sensor == mSensor) {
+ ++mNumEvent;
+ } else {
+ mWrongSensorObserved = true;
+ }
+ }
+
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventBasicVerificationTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventBasicVerificationTest.java
new file mode 100644
index 0000000..34be3c4
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventBasicVerificationTest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.hardware.cts.helpers.sensorverification;
+
+import junit.framework.TestCase;
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorManager;
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.TestSensorEnvironment;
+import android.hardware.cts.helpers.TestSensorEvent;
+import android.test.AndroidTestCase;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * Tests for {@link EventBasicVerification}.
+ */
+public class EventBasicVerificationTest extends AndroidTestCase {
+
+ /**
+ * Test {@link EventBasicVerification#verify(TestSensorEnvironment, SensorStats)}.
+ */
+ public void testVerify() {
+
+ /* Sensor contents is not used in this verification, use Object as mock */
+ SensorManager mgr = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
+
+ Sensor sensor1 = null;
+
+ // accelerometer is the most likely sensor to exist
+ Sensor sensor2 = mgr.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+
+ SensorStats stats;
+
+ EventBasicVerification verification;
+
+ // case 1
+ verification = getVerification(10, sensor1, sensor1, new float[20][3]);
+ stats = new SensorStats();
+
+ verification.verify(stats);
+ assertEquals(true, stats.getValue(EventBasicVerification.PASSED_KEY));
+ assertEquals(20, (long) stats.getValue(SensorStats.EVENT_COUNT_KEY));
+ assertEquals(false, stats.getValue(SensorStats.WRONG_SENSOR_KEY));
+
+ // case 2
+ verification = getVerification(10, sensor1, sensor1, new float[5][3]);
+ stats = new SensorStats();
+
+ try {
+ verification.verify(stats);
+ fail("Expect an AssertionError due to insufficient samples");
+ } catch (AssertionError e) {
+ //Expected
+ }
+ assertEquals(false, stats.getValue(EventBasicVerification.PASSED_KEY));
+ assertEquals(5, (long) stats.getValue(SensorStats.EVENT_COUNT_KEY));
+ assertEquals(false, stats.getValue(SensorStats.WRONG_SENSOR_KEY));
+
+ // case 3
+ if (sensor1 != sensor2) {
+ // if we cannot even get a second sensor then do not bother this test.
+ verification = getVerification(10, sensor1, sensor2, new float[15][3]);
+ stats = new SensorStats();
+
+ try {
+ verification.verify(stats);
+ fail("Expect an AssertionError due to wrong sensor event");
+ } catch (AssertionError e) {
+ //Expected
+ }
+ assertEquals(false, stats.getValue(EventBasicVerification.PASSED_KEY));
+ // zero here because wrong sensor is used.
+ assertEquals(0, (long) stats.getValue(SensorStats.EVENT_COUNT_KEY));
+ assertEquals(true, stats.getValue(SensorStats.WRONG_SENSOR_KEY));
+ }
+ }
+
+ private static EventBasicVerification getVerification(
+ int expectedMinNumEvent, Sensor sensor, Sensor eventSensor, float[] ... values) {
+
+ Collection<TestSensorEvent> events = new ArrayList<>(values.length);
+ for (float[] value : values) {
+ events.add(new TestSensorEvent(eventSensor, 0, 0, value));
+ }
+ EventBasicVerification verification =
+ new EventBasicVerification(expectedMinNumEvent, sensor);
+ verification.addSensorEvents(events);
+ return verification;
+ }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventGapVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventGapVerification.java
index 76f1594..c64810b 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventGapVerification.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventGapVerification.java
@@ -28,15 +28,16 @@
// Number of events to truncate (discard) from the initial events received
private static final int TRUNCATE_EVENTS_COUNT = 1;
- // Number of event gaps to tolerate is the minimum of 1% of total events received or 10.
- private static final int EVENT_GAP_TOLERANCE_COUNT = 10;
- private static final double EVENT_GAP_TOLERANCE_PERCENT = 0.01;
+ // Number of event gaps to tolerate is 2% of total number of events received rounded up to next
+ // integer or 20, whichever is smaller.
+ private static final int EVENT_GAP_THRESHOLD_MAX = 20;
+ private static final double EVENT_GAP_TOLERANCE = 0.02;
private final int mExpectedDelayUs;
private final List<IndexedEventPair> mEventGaps = new LinkedList<IndexedEventPair>();
private TestSensorEvent mPreviousEvent = null;
- private int mIndex = 0;
+ private int mEventCount = 0;
/**
* Construct a {@link EventGapVerification}
@@ -72,14 +73,16 @@
}
final int count = mEventGaps.size();
- // Ensure that eventGapTolerance is at least 1.
- int eventGapTolerance = (int)Math.max(1, Math.min(EVENT_GAP_TOLERANCE_COUNT,
- EVENT_GAP_TOLERANCE_PERCENT * mIndex));
- stats.addValue(PASSED_KEY, count <= eventGapTolerance);
+ // Ensure the threshold is rounded up.
+ double eventGapThreshold =
+ Math.ceil(Math.min(EVENT_GAP_THRESHOLD_MAX, mEventCount * EVENT_GAP_TOLERANCE));
+ boolean pass = count <= eventGapThreshold;
+
+ stats.addValue(PASSED_KEY, pass);
stats.addValue(SensorStats.EVENT_GAP_COUNT_KEY, count);
stats.addValue(SensorStats.EVENT_GAP_POSITIONS_KEY, getIndexArray(mEventGaps));
- if (count > eventGapTolerance) {
+ if (!pass) {
StringBuilder sb = new StringBuilder();
sb.append(count).append(" events gaps: ");
for (int i = 0; i < Math.min(count, TRUNCATE_MESSAGE_LENGTH); i++) {
@@ -109,16 +112,16 @@
*/
@Override
protected void addSensorEventInternal(TestSensorEvent event) {
- if (mIndex >= TRUNCATE_EVENTS_COUNT) {
+ if (mEventCount >= TRUNCATE_EVENTS_COUNT) {
if (mPreviousEvent != null) {
long deltaNs = event.timestamp - mPreviousEvent.timestamp;
long deltaUs = TimeUnit.MICROSECONDS.convert(deltaNs, TimeUnit.NANOSECONDS);
if (deltaUs > mExpectedDelayUs * THRESHOLD) {
- mEventGaps.add(new IndexedEventPair(mIndex, event, mPreviousEvent));
+ mEventGaps.add(new IndexedEventPair(mEventCount, event, mPreviousEvent));
}
}
mPreviousEvent = event;
}
- mIndex++;
+ mEventCount++;
}
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventTimestampSynchronizationVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventTimestampSynchronizationVerification.java
index 15ff5c03..65d6fa5 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventTimestampSynchronizationVerification.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventTimestampSynchronizationVerification.java
@@ -38,7 +38,8 @@
// number of indices to print in assertion message before truncating
private static final int TRUNCATE_MESSAGE_LENGTH = 3;
- private static final long DEFAULT_THRESHOLD_NS = TimeUnit.MILLISECONDS.toNanos(500);
+ private static final long DEFAULT_THRESHOLD_NS = TimeUnit.MILLISECONDS.toNanos(1000);
+ private static final float ALLOWED_LATENCY_ERROR = 0.1f; //10%
private final ArrayList<TestSensorEvent> mCollectedEvents = new ArrayList<TestSensorEvent>();
@@ -87,9 +88,11 @@
}
// Add an additional filter delay which is a function of the samplingPeriod.
long filterDelayUs = (long)(2.5 * maximumExpectedSamplingPeriodUs);
+
long expectedSyncLatencyNs = TimeUnit.MICROSECONDS.toNanos(reportLatencyUs + filterDelayUs);
- return new EventTimestampSynchronizationVerification(DEFAULT_THRESHOLD_NS,
- expectedSyncLatencyNs);
+ return new EventTimestampSynchronizationVerification(
+ (long) (DEFAULT_THRESHOLD_NS + ALLOWED_LATENCY_ERROR * reportLatencyUs * 1000),
+ expectedSyncLatencyNs);
}
@Override
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FifoLengthVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FifoLengthVerification.java
index 09bbdc8..51811a7 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FifoLengthVerification.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FifoLengthVerification.java
@@ -105,7 +105,7 @@
// Any event that arrives within before 0.5*expectedReportLatency is considered
// to be in the same batch of events, else it is considered as the beginning of a new
// batch.
- if (timestampDiff < 0.5 * mExpectedReportLatencyUs/1000) {
+ if (timestampDiff < mExpectedReportLatencyUs/1000/2) {
batchCount++;
} else {
endofbatch = true;
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/keystore/Android.mk b/tests/tests/keystore/Android.mk
index eaf5389..e51bc04 100644
--- a/tests/tests/keystore/Android.mk
+++ b/tests/tests/keystore/Android.mk
@@ -26,6 +26,8 @@
LOCAL_SDK_VERSION := current
+cts_runtime_hint := 28
+
include $(BUILD_CTS_PACKAGE)
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/tests/keystore/src/android/keystore/cts/BlockCipherTestBase.java b/tests/tests/keystore/src/android/keystore/cts/BlockCipherTestBase.java
index 197adc2..66e8093 100644
--- a/tests/tests/keystore/src/android/keystore/cts/BlockCipherTestBase.java
+++ b/tests/tests/keystore/src/android/keystore/cts/BlockCipherTestBase.java
@@ -30,6 +30,7 @@
import java.security.Key;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
import java.security.Provider;
import java.security.SecureRandom;
import java.security.Security;
@@ -50,6 +51,8 @@
abstract class BlockCipherTestBase extends AndroidTestCase {
+ private static final String EXPECTED_PROVIDER_NAME = TestUtils.EXPECTED_CRYPTO_OP_PROVIDER_NAME;
+
private KeyStore mAndroidKeyStore;
private int mNextKeyId;
@@ -121,7 +124,7 @@
public void testGetProvider() throws Exception {
createCipher();
- Provider expectedProvider = Security.getProvider("AndroidKeyStoreBCWorkaround");
+ Provider expectedProvider = Security.getProvider(EXPECTED_PROVIDER_NAME);
assertSame(expectedProvider, mCipher.getProvider());
}
@@ -1247,8 +1250,8 @@
}
protected void createCipher() throws NoSuchAlgorithmException,
- NoSuchPaddingException {
- mCipher = Cipher.getInstance(getTransformation());
+ NoSuchPaddingException, NoSuchProviderException {
+ mCipher = Cipher.getInstance(getTransformation(), EXPECTED_PROVIDER_NAME);
}
private String getKeyAlgorithm() {
diff --git a/tests/tests/media/Android.mk b/tests/tests/media/Android.mk
index 13daca6..ea7256d 100644
--- a/tests/tests/media/Android.mk
+++ b/tests/tests/media/Android.mk
@@ -51,6 +51,8 @@
#LOCAL_SDK_VERSION := current
LOCAL_JAVA_LIBRARIES += android.test.runner org.apache.http.legacy
+cts_runtime_hint := 265
+
include $(BUILD_CTS_PACKAGE)
include $(call all-makefiles-under,$(LOCAL_PATH))
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/res/raw/video_176x144_mp4_mpeg4_300kbps_25fps_aac_stereo_128kbps_44100hz.mp4 b/tests/tests/media/res/raw/video_176x144_mp4_mpeg4_300kbps_25fps_aac_stereo_128kbps_44100hz.mp4
new file mode 100644
index 0000000..6b6040f
--- /dev/null
+++ b/tests/tests/media/res/raw/video_176x144_mp4_mpeg4_300kbps_25fps_aac_stereo_128kbps_44100hz.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/video_1920x1080_mp4_h264_20480kbps_60fps_aac_stereo_128kbps_44100hz.mp4 b/tests/tests/media/res/raw/video_1920x1080_mp4_h264_20480kbps_60fps_aac_stereo_128kbps_44100hz.mp4
old mode 100644
new mode 100755
index b837dc2..f88fe7a
--- a/tests/tests/media/res/raw/video_1920x1080_mp4_h264_20480kbps_60fps_aac_stereo_128kbps_44100hz.mp4
+++ b/tests/tests/media/res/raw/video_1920x1080_mp4_h264_20480kbps_60fps_aac_stereo_128kbps_44100hz.mp4
Binary files differ
diff --git a/tests/tests/media/src/android/media/cts/AudioRecordTest.java b/tests/tests/media/src/android/media/cts/AudioRecordTest.java
index b1ee3f5..41afab4 100644
--- a/tests/tests/media/src/android/media/cts/AudioRecordTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioRecordTest.java
@@ -19,6 +19,8 @@
import java.nio.ByteBuffer;
import java.util.ArrayList;
+import android.app.ActivityManager;
+import android.content.Context;
import android.content.pm.PackageManager;
import android.cts.util.CtsAndroidTestCase;
import android.media.AudioFormat;
@@ -280,6 +282,9 @@
// Audit modes work best with non-blocking mode
public void testAudioRecordAuditByteBufferResamplerStereoFloat() throws Exception {
+ if (isLowRamDevice()) {
+ return; // skip. FIXME: reenable when AF memory allocation is updated.
+ }
doTest("AuditByteBufferResamplerStereoFloat",
false /*localRecord*/, true /*customHandler*/,
2 /*periodsPerSecond*/, 0 /*markerPeriodsPerSecond*/,
@@ -299,6 +304,9 @@
// Audit buffers can run out of space with high sample rate,
// so keep the channels and pcm encoding low
public void testAudioRecordAuditChannelIndex2() throws Exception {
+ if (isLowRamDevice()) {
+ return; // skip. FIXME: reenable when AF memory allocation is updated.
+ }
doTest("AuditChannelIndex2", true /*localRecord*/, true /*customHandler*/,
2 /*periodsPerSecond*/, 0 /*markerPeriodsPerSecond*/,
false /*useByteBuffer*/, false /*blocking*/,
@@ -696,10 +704,12 @@
long firstSampleTime = 0;
// blank final variables: all successful paths will initialize the times.
+ // this must be declared here for visibility as they are set within the try block.
final long endTime;
final long startTime;
final long stopRequestTime;
final long stopTime;
+ final long coldInputStartTime;
try {
if (markerPeriodsPerSecond != 0) {
@@ -839,11 +849,21 @@
// We've read all the frames, now check the record timing.
endTime = System.currentTimeMillis();
- //Log.d(TAG, "first sample time " + (firstSampleTime - startTime)
+
+ coldInputStartTime = firstSampleTime - startTime;
+ //Log.d(TAG, "first sample time " + coldInputStartTime
// + " test time " + (endTime - firstSampleTime));
- // Verify recording starts within 200 ms of record.startRecording() (typical 100ms)
+
+ if (coldInputStartTime > 200) {
+ Log.w(TAG, "cold input start time way too long "
+ + coldInputStartTime + " > 200ms");
+ } else if (coldInputStartTime > 100) {
+ Log.w(TAG, "cold input start time too long "
+ + coldInputStartTime + " > 100ms");
+ }
+ assertTrue(coldInputStartTime < 5000); // must start within 5 seconds.
+
// Verify recording completes within 50 ms of expected test time (typical 20ms)
- assertEquals(0, firstSampleTime - startTime, 200);
assertEquals(TEST_TIME_MS, endTime - firstSampleTime, auditRecording ? 1000 : 50);
// Even though we've read all the frames we want, the events may not be sent to
@@ -865,6 +885,8 @@
// valid events, issuing right after stop completes. Except for those events,
// no other events should show up after stop.
// This behavior may change in the future but we account for it here in testing.
+ final long SLEEP_AFTER_STOP_FOR_EVENTS_MS = 30;
+ Thread.sleep(SLEEP_AFTER_STOP_FOR_EVENTS_MS);
listener.stop();
// clean up
@@ -945,7 +967,7 @@
// report this
ReportLog log = getReportLog();
- log.printValue(reportName + ": startRecording lag", firstSampleTime - startTime,
+ log.printValue(reportName + ": startRecording lag", coldInputStartTime,
ResultType.LOWER_BETTER, ResultUnit.MS);
log.printValue(reportName + ": stop execution time", stopTime - stopRequestTime,
ResultType.LOWER_BETTER, ResultUnit.MS);
@@ -998,10 +1020,10 @@
assertEquals(AudioRecord.SUCCESS,
mAudioRecord.setNotificationMarkerPosition(mMarkerPosition));
} else {
- // stop() is not sufficient to end all notifications
- // as is not synchronous with the event handling thread
- // so we comment out the line below.
- // fail("onMarkerReached called when not active");
+ // see comment on stop()
+ final long delta = System.currentTimeMillis() - mStopTime;
+ Log.d(TAG, "onMarkerReached called " + delta + " ms after stop");
+ fail("onMarkerReached called when not active");
}
}
@@ -1010,8 +1032,10 @@
int position = getPosition();
mOnPeriodicNotificationCalled.add(position);
} else {
- // see above comments about stop
- // fail("onPeriodicNotification called when not active");
+ // see comment on stop()
+ final long delta = System.currentTimeMillis() - mStopTime;
+ Log.d(TAG, "onPeriodicNotification called " + delta + " ms after stop");
+ fail("onPeriodicNotification called when not active");
}
}
@@ -1022,7 +1046,10 @@
}
public synchronized void stop() {
+ // the listener should be stopped some time after AudioRecord is stopped
+ // as some messages may not yet be posted.
mIsTestActive = false;
+ mStopTime = System.currentTimeMillis();
}
public ArrayList<Integer> getMarkerList() {
@@ -1047,6 +1074,7 @@
}
private long mStartTime;
+ private long mStopTime;
private int mSampleRate;
private boolean mIsTestActive = true;
private AudioRecord mAudioRecord;
@@ -1058,4 +1086,9 @@
return getContext().getPackageManager().hasSystemFeature(
PackageManager.FEATURE_MICROPHONE);
}
+
+ private boolean isLowRamDevice() {
+ return ((ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE))
+ .isLowRamDevice();
+ }
}
diff --git a/tests/tests/media/src/android/media/cts/AudioTrackTest.java b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
index 4c03183..1884493 100644
--- a/tests/tests/media/src/android/media/cts/AudioTrackTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
@@ -16,6 +16,8 @@
package android.media.cts;
+import android.app.ActivityManager;
+import android.content.Context;
import android.content.pm.PackageManager;
import android.cts.util.CtsAndroidTestCase;
import android.media.AudioAttributes;
@@ -1564,11 +1566,17 @@
final int TEST_MODE = AudioTrack.MODE_STREAM;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
final float TEST_SWEEP = 0; // sine wave only
+ final boolean TEST_IS_LOW_RAM_DEVICE = isLowRamDevice();
for (int TEST_FORMAT : TEST_FORMAT_ARRAY) {
double frequency = 400; // frequency changes for each test
for (int TEST_SR : TEST_SR_ARRAY) {
for (int TEST_CONF : TEST_CONF_ARRAY) {
+ final int channelCount = Integer.bitCount(TEST_CONF);
+ if (TEST_IS_LOW_RAM_DEVICE
+ && (TEST_SR > 96000 || channelCount > 4)) {
+ continue; // ignore. FIXME: reenable when AF memory allocation is updated.
+ }
// -------- initialization --------------
final int minBufferSize = AudioTrack.getMinBufferSize(TEST_SR,
TEST_CONF, TEST_FORMAT); // in bytes
@@ -1577,7 +1585,6 @@
assertTrue(TEST_NAME, track.getState() == AudioTrack.STATE_INITIALIZED);
// compute parameters for the source signal data.
- final int channelCount = Integer.bitCount(TEST_CONF);
AudioFormat format = track.getFormat();
assertEquals(TEST_NAME, TEST_SR, format.getSampleRate());
assertEquals(TEST_NAME, TEST_CONF, format.getChannelMask());
@@ -1646,6 +1653,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
}
}
@@ -1893,6 +1901,11 @@
.hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT);
}
+ private boolean isLowRamDevice() {
+ return ((ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE))
+ .isLowRamDevice();
+ }
+
public void testGetTimestamp() throws Exception {
if (!hasAudioOutput()) {
Log.w(TAG,"AUDIO_OUTPUT feature not found. This system might not have a valid "
@@ -2091,6 +2104,12 @@
}
public void testVariableSpeedPlayback() throws Exception {
+ if (!hasAudioOutput()) {
+ Log.w(TAG,"AUDIO_OUTPUT feature not found. This system might not have a valid "
+ + "audio output HAL");
+ return;
+ }
+
final String TEST_NAME = "testVariableSpeedPlayback";
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_FLOAT; // required for test
final int TEST_MODE = AudioTrack.MODE_STATIC; // required for test
diff --git a/tests/tests/media/src/android/media/cts/CodecState.java b/tests/tests/media/src/android/media/cts/CodecState.java
index 6fdbd86..de0bdf8 100644
--- a/tests/tests/media/src/android/media/cts/CodecState.java
+++ b/tests/tests/media/src/android/media/cts/CodecState.java
@@ -220,8 +220,12 @@
mSawInputEOS = true;
// FIX-ME: in tunneled mode we currently use input EOS as output EOS indicator
// we should stream duration
- if (mTunneled && !mIsAudio) {
- mSawOutputEOS = true;
+ if (mTunneled) {
+ if (!mIsAudio) {
+ mSawOutputEOS = true;
+ } else if (mAudioTrack != null) {
+ mAudioTrack.stop();
+ }
}
return false;
}
@@ -260,6 +264,10 @@
index, 0 /* offset */, 0 /* sampleSize */,
0 /* sampleTime */, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
+ if (mTunneled && mAudioTrack != null) {
+ mAudioTrack.stop();
+ }
+
mAvailableInputBufferIndices.removeFirst();
}
@@ -271,7 +279,9 @@
// b/9250789
Log.d(TAG, "CodecState::onOutputFormatChanged " + mime);
+ mIsAudio = false;
if (mime.startsWith("audio/")) {
+ mIsAudio = true;
int sampleRate =
mOutputFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE);
@@ -315,6 +325,9 @@
mSawOutputEOS = true;
+ if (mAudioTrack != null && !mTunneled) {
+ mAudioTrack.stop();
+ }
return false;
}
diff --git a/tests/tests/media/src/android/media/cts/EncodeDecodeTest.java b/tests/tests/media/src/android/media/cts/EncodeDecodeTest.java
index 40934f5..b875719 100644
--- a/tests/tests/media/src/android/media/cts/EncodeDecodeTest.java
+++ b/tests/tests/media/src/android/media/cts/EncodeDecodeTest.java
@@ -16,6 +16,7 @@
package android.media.cts;
+import android.cts.util.MediaUtils;
import android.graphics.ImageFormat;
import android.media.Image;
import android.media.MediaCodec;
@@ -284,6 +285,10 @@
@Override
public void run() {
+ if (mTest.shouldSkip()) {
+ return;
+ }
+
InputSurface inputSurface = null;
try {
if (!mUsePersistentInput) {
@@ -339,6 +344,22 @@
mAllowBT709 = allowBT709;
}
+ private boolean shouldSkip() {
+ if (!MediaUtils.hasEncoder(mMimeType)) {
+ return true;
+ }
+
+ MediaFormat format = MediaFormat.createVideoFormat(mMimeType, mWidth, mHeight);
+ format.setInteger(MediaFormat.KEY_BIT_RATE, mBitRate);
+ format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE);
+ format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
+ if (!MediaUtils.checkEncoderForFormat(format)) {
+ return true;
+ }
+
+ return false;
+ }
+
/**
* Tests encoding and subsequently decoding video from frames generated into a buffer.
* <p>
@@ -348,6 +369,10 @@
* See http://b.android.com/37769 for a discussion of input format pitfalls.
*/
private void encodeDecodeVideoFromBuffer(boolean toSurface) throws Exception {
+ if (shouldSkip()) {
+ return;
+ }
+
MediaCodec encoder = null;
MediaCodec decoder = null;
@@ -550,7 +575,8 @@
ByteBuffer[] encoderOutputBuffers = encoder.getOutputBuffers();
ByteBuffer[] decoderInputBuffers = null;
ByteBuffer[] decoderOutputBuffers = null;
- MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
+ MediaCodec.BufferInfo decoderInfo = new MediaCodec.BufferInfo();
+ MediaCodec.BufferInfo encoderInfo = new MediaCodec.BufferInfo();
MediaFormat decoderOutputFormat = null;
int generateIndex = 0;
int checkIndex = 0;
@@ -589,9 +615,11 @@
boolean inputDone = false;
boolean encoderDone = false;
boolean outputDone = false;
+ int encoderStatus = -1;
while (!outputDone) {
if (VERBOSE) Log.d(TAG, "loop");
+
// If we're not done submitting frames, generate a new one and submit it. By
// doing this on every loop we're working to ensure that the encoder always has
// work to do.
@@ -637,7 +665,10 @@
//
// Once we get EOS from the encoder, we don't need to do this anymore.
if (!encoderDone) {
- int encoderStatus = encoder.dequeueOutputBuffer(info, TIMEOUT_USEC);
+ MediaCodec.BufferInfo info = encoderInfo;
+ if (encoderStatus < 0) {
+ encoderStatus = encoder.dequeueOutputBuffer(info, TIMEOUT_USEC);
+ }
if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {
// no output available yet
if (VERBOSE) Log.d(TAG, "no output from encoder available");
@@ -661,18 +692,7 @@
encodedData.position(info.offset);
encodedData.limit(info.offset + info.size);
- encodedSize += info.size;
- if (outputStream != null) {
- byte[] data = new byte[info.size];
- encodedData.get(data);
- encodedData.position(info.offset);
- try {
- outputStream.write(data);
- } catch (IOException ioe) {
- Log.w(TAG, "failed writing debug data to file");
- throw new RuntimeException(ioe);
- }
- }
+ boolean releaseBuffer = false;
if (!decoderConfigured) {
// Codec config info. Only expected on first packet. One way to
// handle this is to manually stuff the data into the MediaFormat
@@ -694,21 +714,41 @@
if (VERBOSE) Log.d(TAG, "decoder configured (" + info.size + " bytes)");
}
if ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
- // Get a decoder input buffer, blocking until it's available.
+ // Get a decoder input buffer
assertTrue(decoderConfigured);
- int inputBufIndex = decoder.dequeueInputBuffer(-1);
- ByteBuffer inputBuf = decoderInputBuffers[inputBufIndex];
- inputBuf.clear();
- inputBuf.put(encodedData);
- decoder.queueInputBuffer(inputBufIndex, 0, info.size,
- info.presentationTimeUs, info.flags);
+ int inputBufIndex = decoder.dequeueInputBuffer(TIMEOUT_USEC);
+ if (inputBufIndex >= 0) {
+ ByteBuffer inputBuf = decoderInputBuffers[inputBufIndex];
+ inputBuf.clear();
+ inputBuf.put(encodedData);
+ decoder.queueInputBuffer(inputBufIndex, 0, info.size,
+ info.presentationTimeUs, info.flags);
- encoderDone = (info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
- if (VERBOSE) Log.d(TAG, "passed " + info.size + " bytes to decoder"
- + (encoderDone ? " (EOS)" : ""));
+ encoderDone = (info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
+ if (VERBOSE) Log.d(TAG, "passed " + info.size + " bytes to decoder"
+ + (encoderDone ? " (EOS)" : ""));
+ releaseBuffer = true;
+ }
+ } else {
+ releaseBuffer = true;
+ }
+ if (releaseBuffer) {
+ encodedSize += info.size;
+ if (outputStream != null) {
+ byte[] data = new byte[info.size];
+ encodedData.position(info.offset);
+ encodedData.get(data);
+ try {
+ outputStream.write(data);
+ } catch (IOException ioe) {
+ Log.w(TAG, "failed writing debug data to file");
+ throw new RuntimeException(ioe);
+ }
+ }
+ encoder.releaseOutputBuffer(encoderStatus, false);
+ encoderStatus = -1;
}
- encoder.releaseOutputBuffer(encoderStatus, false);
}
}
@@ -719,6 +759,7 @@
// If we're decoding to a Surface, we'll get notified here as usual but the
// ByteBuffer references will be null. The data is sent to Surface instead.
if (decoderConfigured) {
+ MediaCodec.BufferInfo info = decoderInfo;
int decoderStatus = decoder.dequeueOutputBuffer(info, TIMEOUT_USEC);
if (decoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {
// no output available yet
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 813af0f2..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/MediaCodecCencPlayer.java b/tests/tests/media/src/android/media/cts/MediaCodecCencPlayer.java
index 2473078..f5856ed 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecCencPlayer.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecCencPlayer.java
@@ -54,7 +54,7 @@
private boolean mEncryptedAudio;
private boolean mEncryptedVideo;
- private boolean mThreadStarted = false;
+ private volatile boolean mThreadStarted = false;
private byte[] mSessionId;
private CodecState mAudioTrackState;
private int mMediaFormatHeight;
@@ -304,7 +304,7 @@
CodecState state;
if (isVideo) {
state = new CodecState((MediaTimeProvider)this, mVideoExtractor,
- trackIndex, format, codec, true, false,
+ trackIndex, format, codec, true, false,
AudioManager.AUDIO_SESSION_ID_GENERATE);
mVideoCodecStates.put(Integer.valueOf(trackIndex), state);
} else {
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecTest.java b/tests/tests/media/src/android/media/cts/MediaCodecTest.java
index bb05ea0..d3ab54f 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecTest.java
@@ -467,22 +467,32 @@
}
public void testReleaseAfterFlush() throws IOException, InterruptedException {
+ String mimes[] = new String[] { MIME_TYPE, MIME_TYPE_AUDIO};
+ for (String mime : mimes) {
+ if (!MediaUtils.checkEncoder(mime)) {
+ continue;
+ }
+ testReleaseAfterFlush(mime);
+ }
+ }
+
+ private void testReleaseAfterFlush(String mime) throws IOException, InterruptedException {
CountDownLatch buffersExhausted = null;
CountDownLatch codecFlushed = null;
AtomicInteger numBuffers = null;
// sync flush from same thread
- MediaCodec encoder = MediaCodec.createEncoderByType(MIME_TYPE);
- runReleaseAfterFlush(encoder, buffersExhausted, codecFlushed, numBuffers);
+ MediaCodec encoder = MediaCodec.createEncoderByType(mime);
+ runReleaseAfterFlush(mime, encoder, buffersExhausted, codecFlushed, numBuffers);
// sync flush from different thread
- encoder = MediaCodec.createEncoderByType(MIME_TYPE);
+ encoder = MediaCodec.createEncoderByType(mime);
buffersExhausted = new CountDownLatch(1);
codecFlushed = new CountDownLatch(1);
numBuffers = new AtomicInteger();
Thread flushThread = new FlushThread(encoder, buffersExhausted, codecFlushed);
flushThread.start();
- runReleaseAfterFlush(encoder, buffersExhausted, codecFlushed, numBuffers);
+ runReleaseAfterFlush(mime, encoder, buffersExhausted, codecFlushed, numBuffers);
flushThread.join();
// async
@@ -495,19 +505,19 @@
Handler handler = new Handler(callbackThread.getLooper());
// async flush from same thread
- encoder = MediaCodec.createEncoderByType(MIME_TYPE);
+ encoder = MediaCodec.createEncoderByType(mime);
buffersExhausted = null;
codecFlushed = null;
ReleaseAfterFlushCallback callback =
- new ReleaseAfterFlushCallback(encoder, buffersExhausted, codecFlushed, nBuffs);
+ new ReleaseAfterFlushCallback(mime, encoder, buffersExhausted, codecFlushed, nBuffs);
encoder.setCallback(callback, handler); // setCallback before configure, which is called in run
callback.run(); // drive input on main thread
// async flush from different thread
- encoder = MediaCodec.createEncoderByType(MIME_TYPE);
+ encoder = MediaCodec.createEncoderByType(mime);
buffersExhausted = new CountDownLatch(1);
codecFlushed = new CountDownLatch(1);
- callback = new ReleaseAfterFlushCallback(encoder, buffersExhausted, codecFlushed, nBuffs);
+ callback = new ReleaseAfterFlushCallback(mime, encoder, buffersExhausted, codecFlushed, nBuffs);
encoder.setCallback(callback, handler);
flushThread = new FlushThread(encoder, buffersExhausted, codecFlushed);
flushThread.start();
@@ -544,17 +554,21 @@
}
private static class ReleaseAfterFlushCallback extends MediaCodec.Callback implements Runnable {
+ final String mMime;
final MediaCodec mEncoder;
final CountDownLatch mBuffersExhausted, mCodecFlushed;
final int mNumBuffersBeforeFlush;
CountDownLatch mStopInput = new CountDownLatch(1);
+ List<Integer> mInputBufferIndices = new ArrayList<>();
List<Integer> mOutputBufferIndices = new ArrayList<>();
- ReleaseAfterFlushCallback(MediaCodec encoder,
+ ReleaseAfterFlushCallback(String mime,
+ MediaCodec encoder,
CountDownLatch buffersExhausted,
CountDownLatch codecFlushed,
int numBuffersBeforeFlush) {
+ mMime = mime;
mEncoder = encoder;
mBuffersExhausted = buffersExhausted;
mCodecFlushed = codecFlushed;
@@ -563,7 +577,10 @@
@Override
public void onInputBufferAvailable(MediaCodec codec, int index) {
- fail(codec + " onInputBufferAvailable " + index);
+ assertTrue("video onInputBufferAvailable " + index, mMime.startsWith("audio/"));
+ synchronized (mInputBufferIndices) {
+ mInputBufferIndices.add(index);
+ };
}
@Override
@@ -590,11 +607,20 @@
public void run() {
InputSurface inputSurface = null;
try {
- inputSurface = initCodecAndSurface(mEncoder);
+ inputSurface = initCodecAndSurface(mMime, mEncoder);
do {
- GLES20.glClearColor(0.0f, 0.5f, 0.0f, 1.0f);
- GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
- inputSurface.swapBuffers();
+ int inputIndex = -1;
+ if (inputSurface == null) {
+ // asynchronous audio codec
+ synchronized (mInputBufferIndices) {
+ if (mInputBufferIndices.isEmpty()) {
+ continue;
+ } else {
+ inputIndex = mInputBufferIndices.remove(0);
+ }
+ }
+ }
+ feedEncoder(mEncoder, inputSurface, inputIndex);
} while (!mStopInput.await(TIMEOUT_USEC, TimeUnit.MICROSECONDS));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
@@ -606,13 +632,14 @@
}
private static void runReleaseAfterFlush(
+ String mime,
MediaCodec encoder,
CountDownLatch buffersExhausted,
CountDownLatch codecFlushed,
AtomicInteger numBuffers) {
InputSurface inputSurface = null;
try {
- inputSurface = initCodecAndSurface(encoder);
+ inputSurface = initCodecAndSurface(mime, encoder);
List<Integer> outputBufferIndices = getOutputBufferIndices(encoder, inputSurface);
if (numBuffers != null) {
numBuffers.set(outputBufferIndices.size());
@@ -623,19 +650,29 @@
}
}
- private static InputSurface initCodecAndSurface(MediaCodec encoder) {
- InputSurface inputSurface;
- CodecInfo info = getAvcSupportedFormatInfo();
- MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, info.mMaxW, info.mMaxH);
- format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
- MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
- format.setInteger(MediaFormat.KEY_BIT_RATE, info.mBitRate);
- format.setInteger(MediaFormat.KEY_FRAME_RATE, info.mFps);
- format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
- OutputSurface outputSurface = new OutputSurface(1, 1);
- encoder.configure(format, outputSurface.getSurface(), null, MediaCodec.CONFIGURE_FLAG_ENCODE);
- inputSurface = new InputSurface(encoder.createInputSurface());
- inputSurface.makeCurrent();
+ private static InputSurface initCodecAndSurface(String mime, MediaCodec encoder) {
+ MediaFormat format;
+ InputSurface inputSurface = null;
+ if (mime.startsWith("audio/")) {
+ format = MediaFormat.createAudioFormat(mime, AUDIO_SAMPLE_RATE, AUDIO_CHANNEL_COUNT);
+ format.setInteger(MediaFormat.KEY_AAC_PROFILE, AUDIO_AAC_PROFILE);
+ format.setInteger(MediaFormat.KEY_BIT_RATE, AUDIO_BIT_RATE);
+ encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
+ } else if (MIME_TYPE.equals(mime)) {
+ CodecInfo info = getAvcSupportedFormatInfo();
+ format = MediaFormat.createVideoFormat(mime, info.mMaxW, info.mMaxH);
+ format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
+ MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
+ format.setInteger(MediaFormat.KEY_BIT_RATE, info.mBitRate);
+ format.setInteger(MediaFormat.KEY_FRAME_RATE, info.mFps);
+ format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
+ OutputSurface outputSurface = new OutputSurface(1, 1);
+ encoder.configure(format, outputSurface.getSurface(), null, MediaCodec.CONFIGURE_FLAG_ENCODE);
+ inputSurface = new InputSurface(encoder.createInputSurface());
+ inputSurface.makeCurrent();
+ } else {
+ throw new IllegalArgumentException("unsupported mime type: " + mime);
+ }
encoder.start();
return inputSurface;
}
@@ -657,9 +694,7 @@
List<Integer> indices = new ArrayList<>();
do {
feedMoreFrames = indices.isEmpty();
- GLES20.glClearColor(0.0f, 0.5f, 0.0f, 1.0f);
- GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
- inputSurface.swapBuffers();
+ feedEncoder(encoder, inputSurface, -1);
// dequeue buffers until not available
int index = encoder.dequeueOutputBuffer(bufferInfo, TIMEOUT_USEC);
while (index >= 0) {
@@ -672,6 +707,31 @@
return indices;
}
+ /**
+ * @param encoder audio/video encoder
+ * @param inputSurface null for and only for audio encoders
+ * @param inputIndex only used for audio; if -1 the function would attempt to dequeue from encoder;
+ * do not use -1 for asynchronous encoders
+ */
+ private static void feedEncoder(MediaCodec encoder, InputSurface inputSurface, int inputIndex) {
+ if (inputSurface == null) {
+ // audio
+ while (inputIndex == -1) {
+ inputIndex = encoder.dequeueInputBuffer(TIMEOUT_USEC);
+ }
+ ByteBuffer inputBuffer = encoder.getInputBuffer(inputIndex);;
+ for (int i = 0; i < inputBuffer.capacity() / 2; i++) {
+ inputBuffer.putShort((short)i);
+ }
+ encoder.queueInputBuffer(inputIndex, 0, inputBuffer.limit(), 0, 0);
+ } else {
+ // video
+ GLES20.glClearColor(0.0f, 0.5f, 0.0f, 1.0f);
+ GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+ inputSurface.swapBuffers();
+ }
+ }
+
private static void releaseAfterFlush(
MediaCodec encoder,
List<Integer> outputBufferIndices,
@@ -798,6 +858,11 @@
}
public void testDecodeAfterFlush() throws InterruptedException {
+ testDecodeAfterFlush(true /* audio */);
+ testDecodeAfterFlush(false /* audio */);
+ }
+
+ private void testDecodeAfterFlush(final boolean audio) throws InterruptedException {
final int INPUT_RESOURCE_ID =
R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_192kbps_44100hz;
@@ -806,23 +871,27 @@
final int DECODING_TIMEOUT_MS = 10000;
final AtomicBoolean completed = new AtomicBoolean(false);
- Thread videoDecodingThread = new Thread(new Runnable() {
+ Thread decodingThread = new Thread(new Runnable() {
@Override
public void run() {
OutputSurface outputSurface = null;
MediaExtractor mediaExtractor = null;
MediaCodec mediaCodec = null;
try {
- outputSurface = new OutputSurface(1, 1);
- mediaExtractor = getMediaExtractorForMimeType(INPUT_RESOURCE_ID, "video/");
+ String mimeTypePrefix = audio ? "audio/" : "video/";
+ if (!audio) {
+ outputSurface = new OutputSurface(1, 1);
+ }
+ mediaExtractor = getMediaExtractorForMimeType(INPUT_RESOURCE_ID, mimeTypePrefix);
MediaFormat mediaFormat =
mediaExtractor.getTrackFormat(mediaExtractor.getSampleTrackIndex());
if (!MediaUtils.checkDecoderForFormat(mediaFormat)) {
+ completed.set(true);
return; // skip
}
String mimeType = mediaFormat.getString(MediaFormat.KEY_MIME);
mediaCodec = MediaCodec.createDecoderByType(mimeType);
- mediaCodec.configure(mediaFormat, outputSurface.getSurface(),
+ mediaCodec.configure(mediaFormat, outputSurface == null ? null : outputSurface.getSurface(),
null /* crypto */, 0 /* flags */);
mediaCodec.start();
@@ -851,10 +920,10 @@
}
}
});
- videoDecodingThread.start();
- videoDecodingThread.join(DECODING_TIMEOUT_MS);
+ decodingThread.start();
+ decodingThread.join(DECODING_TIMEOUT_MS);
// In case it's timed out, need to stop the thread and have all resources released.
- videoDecodingThread.interrupt();
+ decodingThread.interrupt();
if (!completed.get()) {
throw new RuntimeException("timed out decoding to end-of-stream");
}
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/MediaSyncTest.java b/tests/tests/media/src/android/media/cts/MediaSyncTest.java
index c4fe4c1..de5a5f5 100644
--- a/tests/tests/media/src/android/media/cts/MediaSyncTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaSyncTest.java
@@ -18,6 +18,7 @@
import com.android.cts.media.R;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
import android.cts.util.MediaUtils;
@@ -155,6 +156,11 @@
}
}
+ private boolean hasAudioOutput() {
+ return mActivity.getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT);
+ }
+
/**
* Tests setPlaybackParams is handled correctly for wrong rate.
*/
@@ -337,6 +343,12 @@
* Tests playing back video successfully.
*/
public void testPlayAudio() throws InterruptedException {
+ if (!hasAudioOutput()) {
+ Log.w(LOG_TAG,"AUDIO_OUTPUT feature not found. This system might not have a valid "
+ + "audio output HAL");
+ return;
+ }
+
playAV(INPUT_RESOURCE_ID, 5000 /* lastBufferTimestampMs */,
true /* audio */, false /* video */, 10000 /* timeOutMs */);
}
diff --git a/tests/tests/media/src/android/media/cts/NonBlockingAudioTrack.java b/tests/tests/media/src/android/media/cts/NonBlockingAudioTrack.java
index 3847252..4a4e807 100644
--- a/tests/tests/media/src/android/media/cts/NonBlockingAudioTrack.java
+++ b/tests/tests/media/src/android/media/cts/NonBlockingAudioTrack.java
@@ -43,6 +43,7 @@
private int mSampleRate;
private int mNumBytesQueued = 0;
private LinkedList<QueueElement> mQueue = new LinkedList<QueueElement>();
+ private boolean mStopped;
public NonBlockingAudioTrack(int sampleRate, int channelCount, boolean hwAvSync,
int audioSessionId) {
@@ -107,14 +108,17 @@
}
public void play() {
+ mStopped = false;
mAudioTrack.play();
}
public void stop() {
- mAudioTrack.stop();
-
- mQueue.clear();
- mNumBytesQueued = 0;
+ if (mQueue.isEmpty()) {
+ mAudioTrack.stop();
+ mNumBytesQueued = 0;
+ } else {
+ mStopped = true;
+ }
}
public void pause() {
@@ -128,6 +132,7 @@
mAudioTrack.flush();
mQueue.clear();
mNumBytesQueued = 0;
+ mStopped = false;
}
public void release() {
@@ -135,6 +140,7 @@
mNumBytesQueued = 0;
mAudioTrack.release();
mAudioTrack = null;
+ mStopped = false;
}
public void process() {
@@ -153,6 +159,11 @@
}
mQueue.removeFirst();
}
+ if (mStopped) {
+ mAudioTrack.stop();
+ mNumBytesQueued = 0;
+ mStopped = false;
+ }
}
public int getPlayState() {
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/VideoDecoderPerfTest.java b/tests/tests/media/src/android/media/cts/VideoDecoderPerfTest.java
index f5680f6..8797b9b 100644
--- a/tests/tests/media/src/android/media/cts/VideoDecoderPerfTest.java
+++ b/tests/tests/media/src/android/media/cts/VideoDecoderPerfTest.java
@@ -61,6 +61,7 @@
private MediaFormat mDecOutputFormat;
private double[] mMeasuredFps;
private String[] mResultRawData;
+ private boolean mVerifyResults;
private Resources mResources;
private DeviceReportLog mReportLog;
@@ -68,6 +69,7 @@
@Override
protected void setUp() throws Exception {
super.setUp();
+ mVerifyResults = true;
mResources = mContext.getResources();
mReportLog = new DeviceReportLog();
}
@@ -298,6 +300,10 @@
mResultRawData[round] = result;
}
+ if (!mVerifyResults) {
+ return true;
+ }
+
return MediaUtils.verifyResults(name, mime, w, h, fps);
}
@@ -541,6 +547,20 @@
352, 288, true /* isGoog */);
}
+ public void testMPEG40176x0144Other() throws Exception {
+ mVerifyResults = false;
+ decode(VIDEO_MPEG4,
+ R.raw.video_176x144_mp4_mpeg4_300kbps_25fps_aac_stereo_128kbps_44100hz,
+ 176, 144, false /* isGoog */);
+ }
+
+ public void testMPEG40176x0144Goog() throws Exception {
+ mVerifyResults = false;
+ decode(VIDEO_MPEG4,
+ R.raw.video_176x144_mp4_mpeg4_300kbps_25fps_aac_stereo_128kbps_44100hz,
+ 176, 144, true /* isGoog */);
+ }
+
public void testMPEG40480x0360Other() throws Exception {
decode(VIDEO_MPEG4,
R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz,
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/mediastress/Android.mk b/tests/tests/mediastress/Android.mk
index 5bb23d0..d78ddac 100644
--- a/tests/tests/mediastress/Android.mk
+++ b/tests/tests/mediastress/Android.mk
@@ -33,6 +33,8 @@
LOCAL_SDK_VERSION := current
+cts_runtime_hint := 170
+
include $(BUILD_CTS_PACKAGE)
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/tests/nativeopengl/standalone/jni/tests/EGLCleanup_test.cpp b/tests/tests/nativeopengl/standalone/jni/tests/EGLCleanup_test.cpp
index db39798..2daa2fe 100644
--- a/tests/tests/nativeopengl/standalone/jni/tests/EGLCleanup_test.cpp
+++ b/tests/tests/nativeopengl/standalone/jni/tests/EGLCleanup_test.cpp
@@ -109,7 +109,7 @@
}
private:
- enum { MAX_ITERATIONS = 1000 };
+ enum { MAX_ITERATIONS = 450 };
EGLDisplay mEglDisplay;
EGLSurface mEglSurface;
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/net/src/android/net/wifi/cts/WifiManagerTest.java b/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java
index 152789c..4478bd4 100644
--- a/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java
+++ b/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java
@@ -21,6 +21,8 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.location.LocationManager;
import android.net.NetworkInfo;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
@@ -29,6 +31,7 @@
import android.net.wifi.WifiManager.TxPacketCountListener;
import android.net.wifi.WifiManager.WifiLock;
import android.os.SystemClock;
+import android.provider.Settings;
import android.test.AndroidTestCase;
import android.util.Log;
@@ -278,6 +281,14 @@
Log.d(TAG, "Skipping test as WiFi is not supported");
return;
}
+ if (!hasLocationFeature()) {
+ Log.d(TAG, "Skipping test as location is not supported");
+ return;
+ }
+ if (!isLocationEnabled()) {
+ fail("Please enable location for this test - since Marshmallow WiFi scan results are"
+ + " empty when location is disabled!");
+ }
if (!mWifiManager.isWifiEnabled()) {
setWifiEnabled(true);
}
@@ -307,6 +318,18 @@
}
}
+ // Return true if location is enabled.
+ private boolean isLocationEnabled() {
+ return Settings.Secure.getInt(getContext().getContentResolver(),
+ Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF) !=
+ Settings.Secure.LOCATION_MODE_OFF;
+ }
+
+ // Returns true if the device has location feature.
+ private boolean hasLocationFeature() {
+ return getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_LOCATION);
+ }
+
/**
* test point of wifiManager NetWork:
* 1.add NetWork
diff --git a/tests/tests/os/jni/seccomp-tests/README.android b/tests/tests/os/jni/seccomp-tests/README.android
index 4c5bb83..8b1ac9a 100644
--- a/tests/tests/os/jni/seccomp-tests/README.android
+++ b/tests/tests/os/jni/seccomp-tests/README.android
@@ -11,3 +11,7 @@
- Add get_seccomp_test_list()
The diff of modifications can be found in local-modifications-android.diff.
+
+Additional modification is to backport fixes for Android Native Bridge:
+https://patchwork.kernel.org/patch/7537891/. This is not found in the above
+diff file. The patch is located in local-modifications-strict-args-fd88d16.diff.
diff --git a/tests/tests/os/jni/seccomp-tests/local-modifications-strict-args-fd88d16.diff b/tests/tests/os/jni/seccomp-tests/local-modifications-strict-args-fd88d16.diff
new file mode 100644
index 0000000..a289147
--- /dev/null
+++ b/tests/tests/os/jni/seccomp-tests/local-modifications-strict-args-fd88d16.diff
@@ -0,0 +1,102 @@
+diff --git a/tests/tests/os/jni/seccomp-tests/tests/seccomp_bpf_tests.c b/tests/tests/os/jni/seccomp-tests/tests/seccomp_bpf_tests.c
+index 98b0231..3c238e6 100644
+--- a/tests/tests/os/jni/seccomp-tests/tests/seccomp_bpf_tests.c
++++ b/tests/tests/os/jni/seccomp-tests/tests/seccomp_bpf_tests.c
+@@ -28,6 +28,9 @@
+ #include <string.h>
+ #include <linux/elf.h>
+ #include <sys/uio.h>
++#include <fcntl.h> // ANDROID
++#include <sys/mman.h>
++#include <sys/times.h>
+
+ #define _GNU_SOURCE
+ #include <unistd.h>
+@@ -386,14 +389,16 @@ TEST_SIGNAL(KILL_one, SIGSYS) {
+ }
+
+ TEST_SIGNAL(KILL_one_arg_one, SIGSYS) {
++ void *fatal_address;
+ struct sock_filter filter[] = {
+ BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
+ offsetof(struct seccomp_data, nr)),
+- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getpid, 1, 0),
++ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_times, 1, 0),
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
+ /* Only both with lower 32-bit for now. */
+ BPF_STMT(BPF_LD|BPF_W|BPF_ABS, syscall_arg(0)),
+- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0x0C0FFEE, 0, 1),
++ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K,
++ (unsigned long)&fatal_address, 0, 1),
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL),
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
+ };
+@@ -403,23 +408,29 @@ TEST_SIGNAL(KILL_one_arg_one, SIGSYS) {
+ };
+ long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ pid_t parent = getppid();
+- pid_t pid = getpid();
++ struct tms timebuf;
++ clock_t clock = times(&timebuf);
+ ASSERT_EQ(0, ret);
+
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
+ ASSERT_EQ(0, ret);
+
+ EXPECT_EQ(parent, syscall(__NR_getppid));
+- EXPECT_EQ(pid, syscall(__NR_getpid));
+- /* getpid() should never return. */
+- EXPECT_EQ(0, syscall(__NR_getpid, 0x0C0FFEE));
++ EXPECT_LE(clock, syscall(__NR_times, &timebuf));
++ /* times() should never return. */
++ EXPECT_EQ(0, syscall(__NR_times, &fatal_address));
+ }
+
+ TEST_SIGNAL(KILL_one_arg_six, SIGSYS) {
++#ifndef __NR_mmap2
++ int sysno = __NR_mmap;
++#else
++ int sysno = __NR_mmap2;
++#endif
+ struct sock_filter filter[] = {
+ BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
+ offsetof(struct seccomp_data, nr)),
+- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getpid, 1, 0),
++ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, sysno, 1, 0),
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
+ /* Only both with lower 32-bit for now. */
+ BPF_STMT(BPF_LD|BPF_W|BPF_ABS, syscall_arg(5)),
+@@ -433,16 +444,29 @@ TEST_SIGNAL(KILL_one_arg_six, SIGSYS) {
+ };
+ long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ pid_t parent = getppid();
+- pid_t pid = getpid();
++ int fd;
++ void *map1, *map2;
+ ASSERT_EQ(0, ret);
+
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
+ ASSERT_EQ(0, ret);
+
++ fd = open("/dev/zero", O_RDONLY);
++ ASSERT_NE(-1, fd);
++
+ EXPECT_EQ(parent, syscall(__NR_getppid));
+- EXPECT_EQ(pid, syscall(__NR_getpid));
+- /* getpid() should never return. */
+- EXPECT_EQ(0, syscall(__NR_getpid, 1, 2, 3, 4, 5, 0x0C0FFEE));
++ map1 = (void *)syscall(sysno,
++ NULL, PAGE_SIZE, PROT_READ, MAP_PRIVATE, fd, PAGE_SIZE);
++ EXPECT_NE(MAP_FAILED, map1);
++ /* mmap2() should never return. */
++ map2 = (void *)syscall(sysno,
++ NULL, PAGE_SIZE, PROT_READ, MAP_PRIVATE, fd, 0x0C0FFEE);
++ EXPECT_EQ(MAP_FAILED, map2);
++
++ /* The test failed, so clean up the resources. */
++ munmap(map1, PAGE_SIZE);
++ munmap(map2, PAGE_SIZE);
++ close(fd);
+ }
+
+ /* TODO(wad) add 64-bit versus 32-bit arg tests. */
diff --git a/tests/tests/os/jni/seccomp-tests/tests/seccomp_bpf_tests.c b/tests/tests/os/jni/seccomp-tests/tests/seccomp_bpf_tests.c
index 98b0231..3c238e6 100644
--- a/tests/tests/os/jni/seccomp-tests/tests/seccomp_bpf_tests.c
+++ b/tests/tests/os/jni/seccomp-tests/tests/seccomp_bpf_tests.c
@@ -28,6 +28,9 @@
#include <string.h>
#include <linux/elf.h>
#include <sys/uio.h>
+#include <fcntl.h> // ANDROID
+#include <sys/mman.h>
+#include <sys/times.h>
#define _GNU_SOURCE
#include <unistd.h>
@@ -386,14 +389,16 @@
}
TEST_SIGNAL(KILL_one_arg_one, SIGSYS) {
+ void *fatal_address;
struct sock_filter filter[] = {
BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
offsetof(struct seccomp_data, nr)),
- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getpid, 1, 0),
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_times, 1, 0),
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
/* Only both with lower 32-bit for now. */
BPF_STMT(BPF_LD|BPF_W|BPF_ABS, syscall_arg(0)),
- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0x0C0FFEE, 0, 1),
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K,
+ (unsigned long)&fatal_address, 0, 1),
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL),
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
};
@@ -403,23 +408,29 @@
};
long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
pid_t parent = getppid();
- pid_t pid = getpid();
+ struct tms timebuf;
+ clock_t clock = times(&timebuf);
ASSERT_EQ(0, ret);
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
ASSERT_EQ(0, ret);
EXPECT_EQ(parent, syscall(__NR_getppid));
- EXPECT_EQ(pid, syscall(__NR_getpid));
- /* getpid() should never return. */
- EXPECT_EQ(0, syscall(__NR_getpid, 0x0C0FFEE));
+ EXPECT_LE(clock, syscall(__NR_times, &timebuf));
+ /* times() should never return. */
+ EXPECT_EQ(0, syscall(__NR_times, &fatal_address));
}
TEST_SIGNAL(KILL_one_arg_six, SIGSYS) {
+#ifndef __NR_mmap2
+ int sysno = __NR_mmap;
+#else
+ int sysno = __NR_mmap2;
+#endif
struct sock_filter filter[] = {
BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
offsetof(struct seccomp_data, nr)),
- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getpid, 1, 0),
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, sysno, 1, 0),
BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
/* Only both with lower 32-bit for now. */
BPF_STMT(BPF_LD|BPF_W|BPF_ABS, syscall_arg(5)),
@@ -433,16 +444,29 @@
};
long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
pid_t parent = getppid();
- pid_t pid = getpid();
+ int fd;
+ void *map1, *map2;
ASSERT_EQ(0, ret);
ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
ASSERT_EQ(0, ret);
+ fd = open("/dev/zero", O_RDONLY);
+ ASSERT_NE(-1, fd);
+
EXPECT_EQ(parent, syscall(__NR_getppid));
- EXPECT_EQ(pid, syscall(__NR_getpid));
- /* getpid() should never return. */
- EXPECT_EQ(0, syscall(__NR_getpid, 1, 2, 3, 4, 5, 0x0C0FFEE));
+ map1 = (void *)syscall(sysno,
+ NULL, PAGE_SIZE, PROT_READ, MAP_PRIVATE, fd, PAGE_SIZE);
+ EXPECT_NE(MAP_FAILED, map1);
+ /* mmap2() should never return. */
+ map2 = (void *)syscall(sysno,
+ NULL, PAGE_SIZE, PROT_READ, MAP_PRIVATE, fd, 0x0C0FFEE);
+ EXPECT_EQ(MAP_FAILED, map2);
+
+ /* The test failed, so clean up the resources. */
+ munmap(map1, PAGE_SIZE);
+ munmap(map2, PAGE_SIZE);
+ close(fd);
}
/* TODO(wad) add 64-bit versus 32-bit arg tests. */
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..75ad865 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.1", "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..7ef725b 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());
}
@@ -787,7 +785,11 @@
new File("/dev/ashmem"),
new File("/dev/binder"),
new File("/dev/card0"), // b/13159510
+ new File("/dev/renderD128"),
+ new File("/dev/renderD129"), // b/23798677
new File("/dev/dri/card0"), // b/13159510
+ new File("/dev/dri/renderD128"),
+ new File("/dev/dri/renderD129"), // b/23798677
new File("/dev/felica"), // b/11142586
new File("/dev/felica_ant"), // b/11142586
new File("/dev/felica_cen"), // b/11142586
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..c2607069 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..321ae39 100644
--- a/tests/tests/print/src/android/print/cts/BasePrintTest.java
+++ b/tests/tests/print/src/android/print/cts/BasePrintTest.java
@@ -139,7 +139,20 @@
}
@Override
+ protected void runTest() throws Throwable {
+ // Do nothing if the device does not support printing.
+ if (supportsPrinting()) {
+ super.runTest();
+ }
+ }
+
+ @Override
public void setUp() throws Exception {
+ super.setUp();
+ if (!supportsPrinting()) {
+ return;
+ }
+
// Make sure we start with a clean slate.
clearPrintSpoolerData();
enablePrintServices();
@@ -176,6 +189,10 @@
@Override
public void tearDown() throws Exception {
+ if (!supportsPrinting()) {
+ return;
+ }
+
// Done with the activity.
getActivity().finish();
enableImes();
@@ -193,6 +210,8 @@
disablePrintServices();
// Make sure the spooler is cleaned.
clearPrintSpoolerData();
+
+ super.tearDown();
}
protected void print(final PrintDocumentAdapter adapter) {
@@ -541,6 +560,7 @@
}
protected boolean supportsPrinting() {
- return getActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_PRINTING);
+ return getInstrumentation().getContext().getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_PRINTING);
}
}
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/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..8bb3cc1 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;
}
@@ -242,7 +246,8 @@
mTelecomManager.addNewIncomingCall(TEST_PHONE_ACCOUNT_HANDLE, extras);
try {
- if (!mInCallCallbacks.lock.tryAcquire(3, TimeUnit.SECONDS)) {
+ if (!mInCallCallbacks.lock.tryAcquire(TestUtils.WAIT_FOR_CALL_ADDED_TIMEOUT_S,
+ TimeUnit.SECONDS)) {
fail("No call added to InCallService.");
}
} catch (InterruptedException e) {
@@ -293,7 +298,8 @@
placeNewCallWithPhoneAccount(extras, videoState);
try {
- if (!mInCallCallbacks.lock.tryAcquire(3, TimeUnit.SECONDS)) {
+ if (!mInCallCallbacks.lock.tryAcquire(TestUtils.WAIT_FOR_CALL_ADDED_TIMEOUT_S,
+ TimeUnit.SECONDS)) {
fail("No call added to InCallService.");
}
} catch (InterruptedException e) {
@@ -513,6 +519,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() {
@@ -570,6 +592,25 @@
);
}
+ void assertNotAudioRoute(final InCallService incallService, final int route) {
+ waitUntilConditionIsTrueOrTimeout(
+ new Condition() {
+ @Override
+ public Object expected() {
+ return new Boolean(true);
+ }
+
+ @Override
+ public Object actual() {
+ final CallAudioState state = incallService.getCallAudioState();
+ return route != state.getRoute();
+ }
+ },
+ WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+ "Phone's audio route should not be: " + route
+ );
+ }
+
void assertAudioRoute(final MockConnection connection, final int route) {
waitUntilConditionIsTrueOrTimeout(
new Condition() {
@@ -621,7 +662,7 @@
}
},
WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
- "Call should be in state " + state
+ "Call: " + call + " should be in state " + state
);
}
@@ -731,6 +772,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/BasicInCallServiceTest.java b/tests/tests/telecom/src/android/telecom/cts/BasicInCallServiceTest.java
index 0b5fe61..eda193d 100644
--- a/tests/tests/telecom/src/android/telecom/cts/BasicInCallServiceTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/BasicInCallServiceTest.java
@@ -77,7 +77,8 @@
mContext.startActivity(intent);
try {
- if (callbacks.lock.tryAcquire(3, TimeUnit.SECONDS)) {
+ if (callbacks.lock.tryAcquire(TestUtils.WAIT_FOR_CALL_ADDED_TIMEOUT_S,
+ TimeUnit.SECONDS)) {
return;
}
} catch (InterruptedException e) {
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 afaa3eb3..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..fa19751 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
@@ -72,7 +72,7 @@
extras.putBoolean(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, false);
placeAndVerifyCall(extras);
verifyConnectionForOutgoingCall();
- assertAudioRoute(mInCallCallbacks.getService(), CallAudioState.ROUTE_EARPIECE);
+ assertNotAudioRoute(mInCallCallbacks.getService(), CallAudioState.ROUTE_SPEAKER);
}
public void testStartCallWithSpeakerphoneNotProvided_SpeakerphoneOffByDefault() {
@@ -80,8 +80,10 @@
return;
}
+ AudioManager am = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+
placeAndVerifyCall();
verifyConnectionForOutgoingCall();
- assertAudioRoute(mInCallCallbacks.getService(), CallAudioState.ROUTE_EARPIECE);
+ assertNotAudioRoute(mInCallCallbacks.getService(), CallAudioState.ROUTE_SPEAKER);
}
}
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..d1a9723 100644
--- a/tests/tests/telecom/src/android/telecom/cts/TestUtils.java
+++ b/tests/tests/telecom/src/android/telecom/cts/TestUtils.java
@@ -33,6 +33,7 @@
static final String TAG = "TelecomCTSTests";
static final boolean HAS_TELECOM = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
static final long WAIT_FOR_STATE_CHANGE_TIMEOUT_MS = 10000;
+ static final long WAIT_FOR_CALL_ADDED_TIMEOUT_S = 15;
// Non-final to allow modification by tests not in this package (e.g. permission-related
// tests in the Telecom2 test package.
@@ -63,7 +64,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/CellInfoTest.java b/tests/tests/telephony/src/android/telephony/cts/CellInfoTest.java
index 9a93a60..be29794 100644
--- a/tests/tests/telephony/src/android/telephony/cts/CellInfoTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/CellInfoTest.java
@@ -16,6 +16,7 @@
package android.telephony.cts;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.telephony.CellInfo;
import android.telephony.CellInfoGsm;
@@ -41,6 +42,7 @@
// Maximum and minimum possible RSSI values(in dbm).
private static final int MAX_RRSI = -10;
private static final int MIN_RSSI = -150;
+ private PackageManager mPm;
@Override
protected void setUp() throws Exception {
@@ -48,9 +50,16 @@
mTelephonyManager =
(TelephonyManager)getContext().getSystemService(Context.TELEPHONY_SERVICE);
mCm = (ConnectivityManager)getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
+ mPm = getContext().getPackageManager();
}
public void testCellInfo() throws Throwable {
+
+ if(! (mPm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY))) {
+ Log.d(TAG, "Skipping test that requires FEATURE_TELEPHONY");
+ return;
+ }
+
if (mCm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE) == null) {
Log.d(TAG, "Skipping test that requires ConnectivityManager.TYPE_MOBILE");
return;
@@ -84,39 +93,31 @@
// Verify lte cell information is within correct range.
private void verifyLteInfo(CellInfoLte lte) {
verifyRssiDbm(lte.getCellSignalStrength().getDbm());
- // Verify LTE neighbor information.
- if (!lte.isRegistered()) {
- // Only physical cell id is available for LTE neighbor.
- int pci = lte.getCellIdentity().getPci();
- // Physical cell id should be within [0, 503].
- assertTrue("getPci() out of range [0, 503]", pci >= 0 && pci <= 503);
- }
+ // Verify LTE physical cell id information.
+ // Only physical cell id is available for LTE neighbor.
+ int pci = lte.getCellIdentity().getPci();
+ // Physical cell id should be within [0, 503].
+ assertTrue("getPci() out of range [0, 503]", pci >= 0 && pci <= 503);
}
// Verify wcdma cell information is within correct range.
private void verifyWcdmaInfo(CellInfoWcdma wcdma) {
verifyRssiDbm(wcdma.getCellSignalStrength().getDbm());
- // Verify wcdma neighbor.
- if (!wcdma.isRegistered()) {
- // For wcdma neighbor, only primary scrambling code is available.
- // Primary scrambling code should be within [0, 511].
- int psc = wcdma.getCellIdentity().getPsc();
- assertTrue("getPsc() out of range [0, 511]", psc >= 0 && psc <= 511);
- }
+ // Verify wcdma primary scrambling code information.
+ // Primary scrambling code should be within [0, 511].
+ int psc = wcdma.getCellIdentity().getPsc();
+ assertTrue("getPsc() out of range [0, 511]", psc >= 0 && psc <= 511);
}
// Verify gsm cell information is within correct range.
private void verifyGsmInfo(CellInfoGsm gsm) {
verifyRssiDbm(gsm.getCellSignalStrength().getDbm());
- // Verify gsm neighbor.
- if (!gsm.isRegistered()) {
- // lac and cid are available in GSM neighbor information.
- // Local area code and cellid should be with [0, 65535].
- int lac = gsm.getCellIdentity().getLac();
- assertTrue("getLac() out of range [0, 65535]", lac >= 0 && lac <= 65535);
- int cid = gsm.getCellIdentity().getCid();
- assertTrue("getCid() out range [0, 65535]", cid >= 0 && cid <= 65535);
- }
+ // Verify gsm local area code and cellid.
+ // Local area code and cellid should be with [0, 65535].
+ int lac = gsm.getCellIdentity().getLac();
+ assertTrue("getLac() out of range [0, 65535]", lac >= 0 && lac <= 65535);
+ int cid = gsm.getCellIdentity().getCid();
+ assertTrue("getCid() out range [0, 65535]", cid >= 0 && cid <= 65535);
}
// Rssi(in dbm) should be within [MIN_RSSI, MAX_RSSI].
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/SmsMessageTest.java b/tests/tests/telephony/src/android/telephony/cts/SmsMessageTest.java
index d90e394..2db9ba1 100644
--- a/tests/tests/telephony/src/android/telephony/cts/SmsMessageTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/SmsMessageTest.java
@@ -301,11 +301,19 @@
int[] result = SmsMessage.calculateLength(LONG_TEXT_WITH_32BIT_CHARS, false);
assertEquals(3, result[0]);
assertEquals(LONG_TEXT_WITH_32BIT_CHARS.length(), result[1]);
+ // 3 parts, each with (SmsMessage.MAX_USER_DATA_BYTES_WITH_HEADER / 2) 16-bit
+ // characters. We need to subtract one because a 32-bit character crosses the
+ // boundary of 2 parts.
+ int preMaxChars = 3 * SmsMessage.MAX_USER_DATA_BYTES_WITH_HEADER / 2 - 1;
+ // If EMS is not supported, break down EMS into single segment SMS
+ // and add page info "x/y".
+ // In the case of UCS2 encoding type, we need 8 bytes for this
+ // but we only have 6 bytes from UDH, so truncate the limit for
+ // each segment by 2 bytes (1 char). This log sms has three segments,
+ // so truncate the limit by 3 char in total
+ int maxChars = SmsMessage.hasEmsSupport() ? preMaxChars : preMaxChars - 3;
assertRemaining(LONG_TEXT_WITH_32BIT_CHARS.length(), result[2],
- // 3 parts, each with (SmsMessage.MAX_USER_DATA_BYTES_WITH_HEADER / 2) 16-bit
- // characters. We need to subtract one because a 32-bit character crosses the
- // boundary of 2 parts.
- 3 * SmsMessage.MAX_USER_DATA_BYTES_WITH_HEADER / 2 - 1);
+ maxChars);
assertEquals(SmsMessage.ENCODING_16BIT, result[3]);
}
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/text/src/android/text/method/cts/PasswordTransformationMethodTest.java b/tests/tests/text/src/android/text/method/cts/PasswordTransformationMethodTest.java
index 4262a31..72a8e72 100644
--- a/tests/tests/text/src/android/text/method/cts/PasswordTransformationMethodTest.java
+++ b/tests/tests/text/src/android/text/method/cts/PasswordTransformationMethodTest.java
@@ -19,6 +19,7 @@
import android.cts.util.PollingCheck;
import android.graphics.Rect;
+import android.os.ParcelFileDescriptor;
import android.provider.Settings.SettingNotFoundException;
import android.provider.Settings.System;
import android.test.ActivityInstrumentationTestCase2;
@@ -31,6 +32,10 @@
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.util.Scanner;
+
/**
* Test {@link PasswordTransformationMethod}.
*/
@@ -97,10 +102,39 @@
mEditText = (EditText) getActivity().findViewById(EDIT_TXT_ID);
assertTrue(mEditText.isFocused());
+ enableAppOps();
savePasswordPref();
switchShowPassword(true);
}
+ private void enableAppOps() {
+ StringBuilder cmd = new StringBuilder();
+ cmd.append("appops set ");
+ cmd.append(getInstrumentation().getContext().getPackageName());
+ cmd.append(" android:write_settings allow");
+ getInstrumentation().getUiAutomation().executeShellCommand(cmd.toString());
+
+ StringBuilder query = new StringBuilder();
+ query.append("appops get ");
+ query.append(getInstrumentation().getContext().getPackageName());
+ query.append(" android:write_settings");
+ String queryStr = query.toString();
+
+ String result = "No operations.";
+ while (result.contains("No operations")) {
+ ParcelFileDescriptor pfd = getInstrumentation().getUiAutomation().executeShellCommand(
+ queryStr);
+ InputStream inputStream = new FileInputStream(pfd.getFileDescriptor());
+ result = convertStreamToString(inputStream);
+ }
+ }
+
+ private String convertStreamToString(InputStream is) {
+ try (Scanner scanner = new Scanner(is).useDelimiter("\\A")) {
+ return scanner.hasNext() ? scanner.next() : "";
+ }
+ }
+
@Override
protected void tearDown() throws Exception {
resumePasswordPref();
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..2f3f48c8 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();
@@ -245,14 +251,14 @@
final int frameCount = stats.getFrameCount();
for (int i = 0; i < frameCount; i++) {
final long expectedTimeNano = stats.getFramePostedTimeNano(i);
- assertTrue(expectedTimeNano > lastExpectedTimeNano);
+ assertTrue(expectedTimeNano >= lastExpectedTimeNano);
lastExpectedTimeNano = expectedTimeNano;
final long presentedTimeNano = stats.getFramePresentedTimeNano(i);
if (lastPresentedTimeNano == FrameStats.UNDEFINED_TIME_NANO) {
assertTrue(presentedTimeNano == FrameStats.UNDEFINED_TIME_NANO);
} else if (presentedTimeNano != FrameStats.UNDEFINED_TIME_NANO) {
- assertTrue(presentedTimeNano > lastPresentedTimeNano);
+ assertTrue(presentedTimeNano >= lastPresentedTimeNano);
}
lastPresentedTimeNano = presentedTimeNano;
@@ -260,7 +266,7 @@
if (lastPreparedTimeNano == FrameStats.UNDEFINED_TIME_NANO) {
assertTrue(preparedTimeNano == FrameStats.UNDEFINED_TIME_NANO);
} else if (preparedTimeNano != FrameStats.UNDEFINED_TIME_NANO) {
- assertTrue(preparedTimeNano > lastPreparedTimeNano);
+ assertTrue(preparedTimeNano >= lastPreparedTimeNano);
}
lastPreparedTimeNano = preparedTimeNano;
}
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/src/android/voiceinteraction/cts/VoiceInteractionTest.java b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionTest.java
index 0fa89e1..6b47eb4 100644
--- a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionTest.java
+++ b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionTest.java
@@ -59,7 +59,16 @@
@Override
protected void tearDown() throws Exception {
- mContext.unregisterReceiver(mReceiver);
+ if (mReceiver != null) {
+ try {
+ mContext.unregisterReceiver(mReceiver);
+ } catch (IllegalArgumentException e) {
+ // This exception is thrown if mReceiver in
+ // the above call to unregisterReceiver is never registered.
+ // If so, no harm done by ignoring this exception.
+ }
+ mReceiver = null;
+ }
super.tearDown();
}
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..529c160 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,30 @@
@Override
protected void tearDown() throws Exception {
- mContext.unregisterReceiver(mActivityDoneReceiver);
+ if (mActivityDoneReceiver != null) {
+ try {
+ mContext.unregisterReceiver(mActivityDoneReceiver);
+ } catch (IllegalArgumentException e) {
+ // This exception is thrown if mActivityDoneReceiver in
+ // the above call to unregisterReceiver is never registered.
+ // If so, no harm done by ignoring this exception.
+ }
+ mActivityDoneReceiver = null;
+ }
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);
@@ -69,9 +90,6 @@
protected void registerBroadcastReceiver(Utils.TestcaseType testCaseType) throws Exception {
mTestCaseType = testCaseType;
mLatch = new CountDownLatch(1);
- if (mActivityDoneReceiver != null) {
- mContext.unregisterReceiver(mActivityDoneReceiver);
- }
mActivityDoneReceiver = new ActivityDoneReceiver();
mContext.registerReceiver(mActivityDoneReceiver,
new IntentFilter(Utils.BROADCAST_INTENT + testCaseType.toString()));
diff --git a/tests/tests/voicesettings/src/android/voicesettings/cts/ZenModeTest.java b/tests/tests/voicesettings/src/android/voicesettings/cts/ZenModeTest.java
index 8c2efbef..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/tests/tests/widget/src/android/widget/cts/TextViewTest.java b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
index 3130a26..01938c2 100644
--- a/tests/tests/widget/src/android/widget/cts/TextViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
@@ -24,6 +24,7 @@
import android.app.Instrumentation;
import android.app.Instrumentation.ActivityMonitor;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
import android.content.res.Resources.NotFoundException;
import android.cts.util.PollingCheck;
@@ -4416,9 +4417,18 @@
public void testSetGetBreakStrategy() {
TextView tv = new TextView(mActivity);
+ final PackageManager pm = getInstrumentation().getTargetContext().getPackageManager();
+
// The default value is from the theme, here the default is BREAK_STRATEGY_HIGH_QUALITY for
- // TextView.
- assertEquals(Layout.BREAK_STRATEGY_HIGH_QUALITY, tv.getBreakStrategy());
+ // TextView except for Android Wear. The default value for Android Wear is
+ // BREAK_STRATEGY_BALANCED.
+ if (pm.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+ // Android Wear
+ assertEquals(Layout.BREAK_STRATEGY_BALANCED, tv.getBreakStrategy());
+ } else {
+ // All other form factor.
+ assertEquals(Layout.BREAK_STRATEGY_HIGH_QUALITY, tv.getBreakStrategy());
+ }
tv.setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE);
assertEquals(Layout.BREAK_STRATEGY_SIMPLE, tv.getBreakStrategy());
diff --git a/tools/Android.mk b/tools/Android.mk
index 8377036..8cb90db 100644
--- a/tools/Android.mk
+++ b/tools/Android.mk
@@ -19,12 +19,13 @@
TF_JAR := $(HOST_OUT_JAVA_LIBRARIES)/tradefed-prebuilt.jar
CTS_TF_JAR := $(HOST_OUT_JAVA_LIBRARIES)/cts-tradefed.jar
CTS_TF_EXEC_PATH ?= $(HOST_OUT_EXECUTABLES)/cts-tradefed
+PRECONDITIONS_APK := $(CTS_TESTCASES_OUT)/CtsPreconditionsApp.apk
cts_prebuilt_jar := $(HOST_OUT)/cts/android-cts/tools/cts-prebuilt.jar
$(cts_prebuilt_jar): PRIVATE_TESTS_DIR := $(HOST_OUT)/cts/android-cts/repository/testcases
$(cts_prebuilt_jar): PRIVATE_PLANS_DIR := $(HOST_OUT)/cts/android-cts/repository/plans
$(cts_prebuilt_jar): PRIVATE_TOOLS_DIR := $(HOST_OUT)/cts/android-cts/tools
-$(cts_prebuilt_jar): $(JUNIT_HOST_JAR) $(HOSTTESTLIB_JAR) $(TF_JAR) $(CTS_TF_JAR) $(CTS_TF_EXEC_PATH) $(ADDITIONAL_TF_JARS) | $(ACP) $(HOST_OUT_EXECUTABLES)/adb
+$(cts_prebuilt_jar): $(JUNIT_HOST_JAR) $(HOSTTESTLIB_JAR) $(TF_JAR) $(CTS_TF_JAR) $(CTS_TF_EXEC_PATH) $(ADDITIONAL_TF_JARS) $(PRECONDITIONS_APK) | $(ACP) $(HOST_OUT_EXECUTABLES)/adb
mkdir -p $(PRIVATE_TESTS_DIR)
mkdir -p $(PRIVATE_PLANS_DIR)
mkdir -p $(PRIVATE_TOOLS_DIR)
diff --git a/tools/cts-media/copy_media.sh b/tools/cts-media/copy_media.sh
index cf0d099..cfec807 100755
--- a/tools/cts-media/copy_media.sh
+++ b/tools/cts-media/copy_media.sh
@@ -17,15 +17,15 @@
max_resolution=3
if [ $# -eq 0 ]; then
echo "assuming default resolution"
-elif [ "$1" == "-s" ]; then
+elif [ "$1" = "-s" ]; then
adb_options=""$1" "$2""
-elif [ "$1" == "720x480" ]; then
+elif [ "$1" = "720x480" ]; then
max_resolution=1
-elif [ "$1" == "1280x720" ]; then
+elif [ "$1" = "1280x720" ]; then
max_resolution=2
-elif [ "$1" == "1920x1080" ]; then
+elif [ "$1" = "1920x1080" ]; then
max_resolution=3
-elif [ "$1" == "all" ]; then
+elif [ "$1" = "all" ]; then
max_resolution=3
else
echo "Usage: copy_media.sh [720x480|1280x720|1920x1080] [-s serial]"
diff --git a/tools/cts-media/get_achievable_rates.py b/tools/cts-media/get_achievable_rates.py
new file mode 100755
index 0000000..9dde743
--- /dev/null
+++ b/tools/cts-media/get_achievable_rates.py
@@ -0,0 +1,402 @@
+#!/usr/bin/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 argparse, math, re, sys
+import xml.etree.ElementTree as ET
+from collections import defaultdict, namedtuple
+import itertools
+
+
+def createLookup(values, key):
+ """Creates a lookup table for a collection of values based on keys.
+
+ Arguments:
+ values: a collection of arbitrary values. Must be iterable.
+ key: a function of one argument that returns the key for a value.
+
+ Returns:
+ A dict mapping keys (as generated by the key argument) to lists of
+ values. All values in the lists have the same key, and are in the order
+ they appeared in the collection.
+ """
+ lookup = defaultdict(list)
+ for v in values:
+ lookup[key(v)].append(v)
+ return lookup
+
+
+def _intify(value):
+ """Returns a value converted to int if possible, else the original value."""
+ try:
+ return int(value)
+ except ValueError:
+ return value
+
+
+class Size(namedtuple('Size', ['width', 'height'])):
+ """A namedtuple with width and height fields."""
+ def __str__(self):
+ return '%dx%d' % (self.width, self.height)
+
+
+class _VideoResultBase(object):
+ """Helper methods for results. Not for use by applications.
+
+ Attributes:
+ codec: The name of the codec (string) or None
+ size: Size representing the video size or None
+ mime: The mime-type of the codec (string) or None
+ rates: The measured achievable frame rates
+ is_decoder: True iff codec is a decoder.
+ """
+
+ def __init__(self, is_decoder):
+ self.codec = None
+ self.mime = None
+ self.size = None
+ self._rates_from_failure = []
+ self._rates_from_message = []
+ self.is_decoder = is_decoder
+
+ def _inited(self):
+ """Returns true iff codec, mime and size was set."""
+ return None not in (self.codec, self.mime, self.size)
+
+ def __len__(self):
+ # don't report any result if codec name, mime type and size is unclear
+ if not self._inited():
+ return 0
+ return len(self.rates)
+
+ @property
+ def rates(self):
+ return self._rates_from_failure or self._rates_from_message
+
+ def _parseDict(self, value):
+ """Parses a MediaFormat from its string representation sans brackets."""
+ return dict((k, _intify(v))
+ for k, v in re.findall(r'([^ =]+)=([^ [=]+(?:|\[[^\]]+\]))(?:, |$)', value))
+
+ def _cleanFormat(self, format):
+ """Removes internal fields from a parsed MediaFormat."""
+ format.pop('what', None)
+ format.pop('image-data', None)
+
+ MESSAGE_PATTERN = r'(?P<key>\w+)=(?P<value>\{[^}]*\}|[^ ,{}]+)'
+
+ def _parsePartialResult(self, message_match):
+ """Parses a partial test result conforming to the message pattern.
+
+ Returns:
+ A tuple of string key and int, string or dict value, where dict has
+ string keys mapping to int or string values.
+ """
+ key, value = message_match.group('key', 'value')
+ if value.startswith('{'):
+ value = self._parseDict(value[1:-1])
+ if key.endswith('Format'):
+ self._cleanFormat(value)
+ else:
+ value = _intify(value)
+ return key, value
+
+ def _parseValuesFromBracket(self, line):
+ """Returns the values enclosed in brackets without the brackets.
+
+ Parses a line matching the pattern "<tag>: [<values>]" and returns <values>.
+
+ Raises:
+ ValueError: if the line does not match the pattern.
+ """
+ try:
+ return re.match(r'^[^:]+: *\[(?P<values>.*)\]\.$', line).group('values')
+ except AttributeError:
+ raise ValueError('line does not match "tag: [value]": %s' % line)
+
+ def _parseRawData(self, line):
+ """Parses the raw data line for video performance tests.
+
+ Yields:
+ Dict objects corresponding to parsed results, mapping string keys to
+ int, string or dict values.
+ """
+ try:
+ values = self._parseValuesFromBracket(line)
+ result = {}
+ for m in re.finditer(self.MESSAGE_PATTERN + r'(?P<sep>,? +|$)', values):
+ key, value = self._parsePartialResult(m)
+ result[key] = value
+ if m.group('sep') != ' ':
+ yield result
+ result = {}
+ except ValueError:
+ print >> sys.stderr, 'could not parse line %s' % repr(line)
+
+ def _tryParseMeasuredFrameRate(self, line):
+ """Parses a line starting with 'Measured frame rate:'."""
+ if line.startswith('Measured frame rate: '):
+ try:
+ values = self._parseValuesFromBracket(line)
+ values = re.split(r' *, *', values)
+ self._rates_from_failure = list(map(float, values))
+ except ValueError:
+ print >> sys.stderr, 'could not parse line %s' % repr(line)
+
+ def parse(self, test):
+ """Parses the ValueArray and FailedScene lines of a test result.
+
+ Arguments:
+ test: An ElementTree <Test> element.
+ """
+ failure = test.find('FailedScene')
+ if failure is not None:
+ trace = failure.find('StackTrace')
+ if trace is not None:
+ for line in re.split(r'[\r\n]+', trace.text):
+ self._parseFailureLine(line)
+ details = test.find('Details')
+ if details is not None:
+ for array in details.iter('ValueArray'):
+ message = array.get('message')
+ self._parseMessage(message, array)
+
+ def _parseFailureLine(self, line):
+ raise NotImplementedError
+
+ def _parseMessage(self, message, array):
+ raise NotImplementedError
+
+ def getData(self):
+ """Gets the parsed test result data.
+
+ Yields:
+ Result objects containing at least codec, size, mime and rates attributes."""
+ yield self
+
+
+class VideoEncoderDecoderTestResult(_VideoResultBase):
+ """Represents a result from a VideoEncoderDecoderTest performance case."""
+
+ def __init__(self, unused_m):
+ super(VideoEncoderDecoderTestResult, self).__init__(is_decoder=False)
+
+ # If a VideoEncoderDecoderTest succeeds, it provides the results in the
+ # message of a ValueArray. If fails, it provides the results in the failure
+ # using raw data. (For now it also includes some data in the ValueArrays even
+ # if it fails, which we ignore.)
+
+ def _parseFailureLine(self, line):
+ """Handles parsing a line from the failure log."""
+ self._tryParseMeasuredFrameRate(line)
+ if line.startswith('Raw data: '):
+ for result in self._parseRawData(line):
+ fmt = result['EncOutputFormat']
+ self.size = Size(fmt['width'], fmt['height'])
+ self.codec = result['codec']
+ self.mime = fmt['mime']
+
+ def _parseMessage(self, message, array):
+ """Handles parsing a message from ValueArrays."""
+ if message.startswith('codec='):
+ result = dict(self._parsePartialResult(m)
+ for m in re.finditer(self.MESSAGE_PATTERN + '(?: |$)', message))
+ if 'EncInputFormat' in result:
+ self.codec = result['codec']
+ fmt = result['EncInputFormat']
+ self.size = Size(fmt['width'], fmt['height'])
+ self.mime = result['EncOutputFormat']['mime']
+ self._rates_from_message.append(1000000./result['min'])
+
+
+class VideoDecoderPerfTestResult(_VideoResultBase):
+ """Represents a result from a VideoDecoderPerfTest performance case."""
+
+ # If a VideoDecoderPerfTest succeeds, it provides the results in the message
+ # of a ValueArray. If fails, it provides the results in the failure only
+ # using raw data.
+
+ def __init__(self, unused_m):
+ super(VideoDecoderPerfTestResult, self).__init__(is_decoder=True)
+
+ def _parseFailureLine(self, line):
+ """Handles parsing a line from the failure log."""
+ self._tryParseMeasuredFrameRate(line)
+ # if the test failed, we can only get the codec/size/mime from the raw data.
+ if line.startswith('Raw data: '):
+ for result in self._parseRawData(line):
+ fmt = result['DecOutputFormat']
+ self.size = Size(fmt['width'], fmt['height'])
+ self.codec = result['codec']
+ self.mime = result['mime']
+
+ def _parseMessage(self, message, array):
+ """Handles parsing a message from ValueArrays."""
+ if message.startswith('codec='):
+ result = dict(self._parsePartialResult(m)
+ for m in re.finditer(self.MESSAGE_PATTERN + '(?: |$)', message))
+ if result.get('decodeto') == 'surface':
+ self.codec = result['codec']
+ fmt = result['DecOutputFormat']
+ self.size = Size(fmt['width'], fmt['height'])
+ self.mime = result['mime']
+ self._rates_from_message.append(1000000. / result['min'])
+
+
+class Results(object):
+ """Container that keeps all test results."""
+ def __init__(self):
+ self._results = [] # namedtuples
+ self._device = None
+
+ VIDEO_ENCODER_DECODER_TEST_REGEX = re.compile(
+ 'test(.*)(\d{4})x(\d{4})(Goog|Other)$')
+
+ VIDEO_DECODER_PERF_TEST_REGEX = re.compile(
+ 'test(VP[89]|H26[34]|MPEG4|HEVC)(\d+)x(\d+)(.*)$')
+
+ TestCaseSpec = namedtuple('TestCaseSpec', 'package path class_ regex result_class')
+
+ def _getTestCases(self):
+ return [
+ self.TestCaseSpec(package='CtsDeviceVideoPerf',
+ path='TestSuite/TestSuite/TestSuite/TestSuite/TestCase',
+ class_='VideoEncoderDecoderTest',
+ regex=self.VIDEO_ENCODER_DECODER_TEST_REGEX,
+ result_class=VideoEncoderDecoderTestResult),
+ self.TestCaseSpec(package='CtsMediaTestCases',
+ path='TestSuite/TestSuite/TestSuite/TestCase',
+ class_='VideoDecoderPerfTest',
+ regex=self.VIDEO_DECODER_PERF_TEST_REGEX,
+ result_class=VideoDecoderPerfTestResult)
+ ]
+
+ def _verifyDeviceInfo(self, device):
+ assert self._device in (None, device), "expected %s device" % self._device
+ self._device = device
+
+ def importXml(self, xml):
+ self._verifyDeviceInfo(xml.find('DeviceInfo/BuildInfo').get('buildName'))
+
+ packages = createLookup(self._getTestCases(), lambda tc: tc.package)
+
+ for pkg in xml.iter('TestPackage'):
+ tests_in_package = packages.get(pkg.get('name'))
+ if not tests_in_package:
+ continue
+ paths = createLookup(tests_in_package, lambda tc: tc.path)
+ for path, tests_in_path in paths.items():
+ classes = createLookup(tests_in_path, lambda tc: tc.class_)
+ for tc in pkg.iterfind(path):
+ tests_in_class = classes.get(tc.get('name'))
+ if not tests_in_class:
+ continue
+ for test in tc.iter('Test'):
+ for tc in tests_in_class:
+ m = tc.regex.match(test.get('name'))
+ if m:
+ result = tc.result_class(m)
+ result.parse(test)
+ self._results.append(result)
+
+ def importFile(self, path):
+ print >> sys.stderr, 'Importing "%s"...' % path
+ try:
+ return self.importXml(ET.parse(path))
+ except ET.ParseError:
+ raise ValueError('not a valid XML file')
+
+ def getData(self):
+ for result in self._results:
+ for data in result.getData():
+ yield data
+
+ def dumpXml(self, results):
+ yield '<?xml version="1.0" encoding="utf-8" ?>'
+ yield '<!-- Copyright 2015 The Android Open Source Project'
+ yield ''
+ yield ' Licensed under the Apache License, Version 2.0 (the "License");'
+ yield ' you may not use this file except in compliance with the License.'
+ yield ' You may obtain a copy of the License at'
+ yield ''
+ yield ' http://www.apache.org/licenses/LICENSE-2.0'
+ yield ''
+ yield ' Unless required by applicable law or agreed to in writing, software'
+ yield ' distributed under the License is distributed on an "AS IS" BASIS,'
+ yield ' WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.'
+ yield ' See the License for the specific language governing permissions and'
+ yield ' limitations under the License.'
+ yield '-->'
+ yield ''
+ yield '<MediaCodecs>'
+ last_section = None
+ Comp = namedtuple('Comp', 'is_decoder google mime name')
+ by_comp = createLookup(results,
+ lambda e: Comp(is_decoder=e.is_decoder, google='.google.' in e.codec, mime=e.mime, name=e.codec))
+ for comp in sorted(by_comp):
+ section = 'Decoders' if comp.is_decoder else 'Encoders'
+ if section != last_section:
+ if last_section:
+ yield ' </%s>' % last_section
+ yield ' <%s>' % section
+ last_section = section
+ yield ' <MediaCodec name="%s" type="%s" update="true">' % (comp.name, comp.mime)
+ by_size = createLookup(by_comp[comp], lambda e: e.size)
+ for size in sorted(by_size):
+ values = list(itertools.chain(*(e.rates for e in by_size[size])))
+ min_, max_ = min(values), max(values)
+ med_ = int(math.sqrt(min_ * max_))
+ yield ' <Limit name="measured-frame-rate-%s" range="%d-%d" />' % (size, med_, med_)
+ yield ' </MediaCodec>'
+ if last_section:
+ yield ' </%s>' % last_section
+ yield '</MediaCodecs>'
+
+
+class Main(object):
+ """Executor of this utility."""
+
+ def __init__(self):
+ self._result = Results()
+
+ self._parser = argparse.ArgumentParser('get_achievable_framerates')
+ self._parser.add_argument('result_xml', nargs='+')
+
+ def _parseArgs(self):
+ self._args = self._parser.parse_args()
+
+ def _importXml(self, xml):
+ self._result.importFile(xml)
+
+ def _report(self):
+ for line in self._result.dumpXml(r for r in self._result.getData() if r):
+ print line
+
+ def run(self):
+ self._parseArgs()
+ try:
+ for xml in self._args.result_xml:
+ try:
+ self._importXml(xml)
+ except (ValueError, IOError, AssertionError) as e:
+ print >> sys.stderr, e
+ raise KeyboardInterrupt
+ self._report()
+ except KeyboardInterrupt:
+ print >> sys.stderr, 'Interrupted.'
+
+if __name__ == '__main__':
+ Main().run()
+
diff --git a/tools/tradefed-host/.classpath b/tools/tradefed-host/.classpath
index e716219..dbeeb01 100644
--- a/tools/tradefed-host/.classpath
+++ b/tools/tradefed-host/.classpath
@@ -7,5 +7,6 @@
<classpathentry exported="true" kind="var" path="CTS_SRC_ROOT/out/host/common/obj/JAVA_LIBRARIES/ctsdeviceinfolib_intermediates/javalib.jar"/>
<classpathentry kind="var" path="CTS_SRC_ROOT/out/host/common/obj/JAVA_LIBRARIES/hosttestlib_intermediates/javalib.jar"/>
<classpathentry kind="var" path="CTS_SRC_ROOT/prebuilts/misc/common/tradefed/tradefed-prebuilt.jar"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/cts-commonutil"/>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/tools/tradefed-host/preconditions/Android.mk b/tools/tradefed-host/preconditions/Android.mk
new file mode 100644
index 0000000..bcd7b49
--- /dev/null
+++ b/tools/tradefed-host/preconditions/Android.mk
@@ -0,0 +1,36 @@
+# 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
+# When built, explicitly put it in the data partition.
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_DEX_PREOPT := false
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsPreconditionsApp
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tools/tradefed-host/preconditions/AndroidManifest.xml b/tools/tradefed-host/preconditions/AndroidManifest.xml
new file mode 100644
index 0000000..02b3534
--- /dev/null
+++ b/tools/tradefed-host/preconditions/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.cts.preconditions">
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <!-- self-instrumenting test package. -->
+ <instrumentation
+ android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:label="CTS device-side preconditions test"
+ android:targetPackage="com.android.cts.preconditions" >
+ </instrumentation>
+</manifest>
diff --git a/tools/tradefed-host/preconditions/src/com/android/cts/preconditions/PreconditionsTest.java b/tools/tradefed-host/preconditions/src/com/android/cts/preconditions/PreconditionsTest.java
new file mode 100644
index 0000000..64a2b31
--- /dev/null
+++ b/tools/tradefed-host/preconditions/src/com/android/cts/preconditions/PreconditionsTest.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.preconditions;
+
+import android.app.KeyguardManager;
+import android.content.Context;
+import android.os.Environment;
+import android.test.AndroidTestCase;
+
+/**
+ * An AndroidTestCase class to verify that device-side preconditions are met for CTS
+ */
+public class PreconditionsTest extends AndroidTestCase {
+
+ /**
+ * Test if device has no screen lock
+ * @throws Exception
+ */
+ public void testScreenUnlocked() throws Exception {
+ KeyguardManager km =
+ (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
+ assertFalse("Device must have screen lock disabled", km.isDeviceSecure());
+ }
+
+ /**
+ * Test if device has accessible external storage
+ * @throws Exception
+ */
+ public void testExternalStoragePresent() throws Exception {
+ String state = Environment.getExternalStorageState();
+ assertTrue("Device must have writable external storage mounted in order to run CTS",
+ Environment.MEDIA_MOUNTED.equals(state));
+ }
+
+}
diff --git a/tools/tradefed-host/res/config/cts.xml b/tools/tradefed-host/res/config/cts.xml
index 416b400..3f89630 100644
--- a/tools/tradefed-host/res/config/cts.xml
+++ b/tools/tradefed-host/res/config/cts.xml
@@ -19,6 +19,7 @@
<option name="enable-root" value="false" />
<build_provider class="com.android.cts.tradefed.build.CtsBuildProvider" />
<device_recovery class="com.android.tradefed.device.WaitDeviceRecovery" />
+ <target_preparer class="com.android.cts.tradefed.targetprep.DevicePreconditionPreparer" />
<test class="com.android.cts.tradefed.testtype.CtsTest" />
<logger class="com.android.tradefed.log.FileLogger" />
<result_reporter class="com.android.cts.tradefed.result.CtsXmlResultReporter" />
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..a39d8b6 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_r2";
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/CtsXmlResultReporter.java b/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java
index 51b457d..3efc7fc 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java
@@ -432,6 +432,9 @@
serializer.attribute(ns, "endtime", endTime);
serializer.attribute(ns, "version", CTS_RESULT_FILE_VERSION);
serializer.attribute(ns, "suite", mSuiteName);
+ if (mReferenceUrl != null) {
+ serializer.attribute(ns, "referenceUrl", mReferenceUrl);
+ }
mResults.serialize(serializer, mBuildInfo.getBuildId());
// TODO: not sure why, but the serializer doesn't like this statement
//serializer.endTag(ns, RESULT_TAG);
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/targetprep/DevicePreconditionPreparer.java b/tools/tradefed-host/src/com/android/cts/tradefed/targetprep/DevicePreconditionPreparer.java
new file mode 100644
index 0000000..039470b
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/targetprep/DevicePreconditionPreparer.java
@@ -0,0 +1,213 @@
+/*
+ * 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.tradefed.targetprep;
+
+import com.android.cts.tradefed.build.CtsBuildHelper;
+import com.android.cts.tradefed.testtype.Abi;
+import com.android.ddmlib.testrunner.TestIdentifier;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.build.IFolderBuildInfo;
+import com.android.tradefed.config.Option;
+import com.android.tradefed.config.OptionClass;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.result.InputStreamSource;
+import com.android.tradefed.result.ITestInvocationListener;
+import com.android.tradefed.result.LogDataType;
+import com.android.tradefed.result.TestSummary;
+import com.android.tradefed.testtype.IAbi;
+import com.android.tradefed.testtype.InstrumentationTest;
+import com.android.tradefed.targetprep.BuildError;
+import com.android.tradefed.targetprep.ITargetPreparer;
+import com.android.tradefed.targetprep.TargetSetupError;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * A {@link ITargetPreparer} that performs precondition checks on the device-side for CTS.
+ * <p/>
+ * This class instruments an APK containing tests verifying that the device meets CTS
+ * preconditions. At present, the APK contains tests to ensure that the device's screen is not
+ * locked, and that the device's external storage is present and writable. The test lives under
+ * //cts/tools/tradefed-host/preconditions, and can be modified to perform further checks and tasks
+ * from the device-side.
+ */
+@OptionClass(alias="device-precondition-preparer")
+public class DevicePreconditionPreparer implements ITargetPreparer {
+
+ /* This option also exists in the HostPreconditionPreparer */
+ @Option(name = "skip-preconditions",
+ description = "Whether to skip precondition checks and automation")
+ protected boolean mSkipPreconditions = false;
+
+ /* Constants for the InstrumentationTest */
+ private static final String APK_NAME = "CtsPreconditionsApp.apk";
+ private static final String PACKAGE_NAME = "com.android.cts.preconditions";
+ private static final String RUNNER_NAME = "android.support.test.runner.AndroidJUnitRunner";
+
+ /* Map used to track test failures */
+ private ConcurrentHashMap<TestIdentifier, String> testFailures = new ConcurrentHashMap<>();
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setUp(ITestDevice device, IBuildInfo buildInfo) throws TargetSetupError,
+ BuildError, DeviceNotAvailableException {
+ if (mSkipPreconditions) {
+ return; // skipping device-side preconditions
+ }
+
+ try {
+ if (!instrument(device, buildInfo)) {
+ throw new TargetSetupError("Not all device-side preconditions met");
+ }
+ } catch (FileNotFoundException e) {
+ throw new TargetSetupError(
+ String.format("Couldn't find %s to instrument", APK_NAME), e);
+ }
+ }
+
+ /* Instruments the APK on the device, and logs precondition test failures, if any are found.
+ * Returns true if all tests pass, and otherwise returns false */
+ private boolean instrument(ITestDevice device, IBuildInfo buildInfo)
+ throws DeviceNotAvailableException, FileNotFoundException {
+ ITestInvocationListener listener = new PreconditionPreparerListener();
+ CtsBuildHelper mCtsBuild = CtsBuildHelper.createBuildHelper(buildInfo);
+ File apkFile = mCtsBuild.getTestApp(APK_NAME); // get the APK file with the CtsBuildHelper
+ InstrumentationTest instrTest = new InstrumentationTest();
+ instrTest.setDevice(device);
+ instrTest.setInstallFile(apkFile);
+ instrTest.setPackageName(PACKAGE_NAME);
+ instrTest.setRunnerName(RUNNER_NAME);
+ instrTest.run(listener);
+ boolean success = true;
+ if (!testFailures.isEmpty()) {
+ success = false; // at least one precondition has failed
+ for (TestIdentifier test : testFailures.keySet()) {
+ String trace = testFailures.get(test);
+ CLog.e("Precondition test %s failed.\n%s", test.getTestName(), trace);
+ }
+ }
+ return success;
+ }
+
+ /**
+ * The PreconditionPreparerListener is an implementation of ITestInvocationListener
+ * that adds entries to the ConcurrentHashMap 'testFailures' of the outer class whenever
+ * a test fails. The listener also logs information if the test run fails, for debugging
+ * purposes.
+ */
+ public class PreconditionPreparerListener implements ITestInvocationListener {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void testFailed(TestIdentifier test, String trace) {
+ testFailures.put(test, trace);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void testRunFailed(String errorMessage) {
+ CLog.e("Device-side preconditions test run failed: %s", errorMessage);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void testEnded(TestIdentifier test, Map<String, String> metrics) {}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void invocationStarted(IBuildInfo buildInfo) {}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void testLog(String dataName, LogDataType dataType, InputStreamSource dataStream) {}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void invocationEnded(long elapsedTime) {}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void invocationFailed(Throwable cause) {}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public TestSummary getSummary() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void testRunStarted(String runName, int testCount) {}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void testStarted(TestIdentifier test) {}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void testAssumptionFailure(TestIdentifier test, String trace) {}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void testIgnored(TestIdentifier test) {}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void testRunStopped(long elapsedTime) {}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void testRunEnded(long elapsedTime, Map<String, String> runMetrics) {}
+ }
+}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/CtsTest.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/CtsTest.java
index d74cce5..244f348 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/CtsTest.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/CtsTest.java
@@ -64,6 +64,7 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
@@ -251,6 +252,20 @@
}
}
+
+ /**
+ * A {@link Comparator} for sorting {@link TestPackage}s by running time hint.
+ */
+ static class RuntimeHintComparator implements Comparator<TestPackage> {
+
+ @Override
+ public int compare(TestPackage left, TestPackage right) {
+ return Long.compare(left.getPackageDef().getRuntimeHint(),
+ right.getPackageDef().getRuntimeHint());
+ }
+
+ }
+
/**
* A {@link ResultForwarder} that will forward a bugreport on each failed test.
*/
@@ -811,6 +826,10 @@
int numTestPackages = testPackageList.size();
int totalShards = Math.min(mTotalShards, numTestPackages);
+ // Sort test packages by running time hint, to force packages with large expected
+ // running times to different shards if possible.
+ Collections.sort(testPackageList, new RuntimeHintComparator());
+
List<TestPackage> shardTestPackageList = new ArrayList<>();
for (int i = mShardAssignment; i < numTestPackages; i += totalShards) {
shardTestPackageList.add(testPackageList.get(i));
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/tradefed-host/src/com/android/cts/tradefed/testtype/GeeTest.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/GeeTest.java
index 2e4420d..9cbcd20 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/GeeTest.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/GeeTest.java
@@ -47,7 +47,7 @@
private static final String ANDROID_PATH_SEPARATOR = "/";
private static final String GTEST_FLAG_FILTER = "--gtest_filter=";
- private int mMaxTestTimeMs = 1 * 60 * 1000;
+ private int mMaxTestTimeMs = 1 * 90 * 1000;
private CtsBuildHelper mCtsBuild;
private ITestDevice mDevice;
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ITestPackageDef.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ITestPackageDef.java
index 13f3572..630dee3 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ITestPackageDef.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ITestPackageDef.java
@@ -72,6 +72,11 @@
public IAbi getAbi();
/**
+ * @return the estimated running time of this test package.
+ */
+ public long getRuntimeHint();
+
+ /**
* Set the filter to use for tests
*
* @param testFilter
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageDef.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageDef.java
index 12c3ddd..d08c0bc 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageDef.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageDef.java
@@ -66,6 +66,7 @@
private String mRunTimeArgs = null;
private String mTestPackageName = null;
private String mDigest = null;
+ private long mRuntimeHint = 0;
private IAbi mAbi = null;
private List<ITargetPreparer> mPreparers = null;
@@ -137,6 +138,18 @@
* {@inheritDoc}
*/
@Override
+ public long getRuntimeHint() {
+ return mRuntimeHint;
+ }
+
+ void setRuntimeHint(long runtimeHint) {
+ mRuntimeHint = runtimeHint;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
public String getName() {
return mName;
}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageXmlParser.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageXmlParser.java
index 649dd9e..951c461 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageXmlParser.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageXmlParser.java
@@ -86,6 +86,10 @@
final String targetNameSpace = attributes.getValue("targetNameSpace");
final String runTimeArgs = attributes.getValue("runtimeArgs");
final String testType = getTestType(attributes);
+ long runTimeHint = 0;
+ if (attributes.getValue("runtimeHint") != null) {
+ runTimeHint = Long.parseLong(attributes.getValue("runtimeHint"));
+ }
for (String abiName : AbiUtils.getAbisSupportedByCts()) {
Abi abi = new Abi(abiName, AbiUtils.getBitness(abiName));
@@ -102,6 +106,7 @@
}
packageDef.setTargetBinaryName(targetBinaryName);
packageDef.setTargetNameSpace(targetNameSpace);
+ packageDef.setRuntimeHint(runTimeHint);
packageDef.setAbi(abi);
mPackageDefs.put(abiName, packageDef);
}
diff --git a/tools/tradefed-host/tests/src/com/android/cts/tradefed/result/CtsXmlResultReporterTest.java b/tools/tradefed-host/tests/src/com/android/cts/tradefed/result/CtsXmlResultReporterTest.java
index 51a6153..958dbe4 100644
--- a/tools/tradefed-host/tests/src/com/android/cts/tradefed/result/CtsXmlResultReporterTest.java
+++ b/tools/tradefed-host/tests/src/com/android/cts/tradefed/result/CtsXmlResultReporterTest.java
@@ -48,8 +48,9 @@
*/
public class CtsXmlResultReporterTest extends TestCase {
+ private static final String TEST_SUMMARY_URL = "http://www.google.com?q=android";
private static final List<TestSummary> SUMMARY_LIST =
- new ArrayList<>(Arrays.asList(new TestSummary("TEST_SUMMARY_URL")));
+ new ArrayList<>(Arrays.asList(new TestSummary(TEST_SUMMARY_URL)));
private CtsXmlResultReporter mResultReporter;
private ByteArrayOutputStream mOutputStream;
private File mBuildDir;
@@ -116,7 +117,7 @@
"<?xml-stylesheet type=\"text/xsl\" href=\"cts_result.xsl\"?>";
final String expectedTestOutput = String.format(
"<TestResult testPlan=\"NA\" starttime=\"ignore\" endtime=\"ignore\" " +
- "version=\"%s\" suite=\"%s\"> ", CTS_RESULT_FILE_VERSION, "CTS" );
+ "version=\"%s\" suite=\"%s\"> ", CTS_RESULT_FILE_VERSION, "CTS");
final String expectedSummaryOutput =
"<Summary failed=\"0\" notExecuted=\"0\" timeout=\"0\" pass=\"0\" />";
final String expectedEndTag = "</TestResult>";
@@ -128,7 +129,7 @@
assertTrue(String.format("test output did not contain expected test result [%s]. Got %s",
expectedTestOutput, actualOutput), actualOutput.contains(expectedTestOutput));
assertTrue(String.format("test output did not contain expected test summary [%s]. Got %s",
- expectedTestOutput, actualOutput), actualOutput.contains(expectedSummaryOutput));
+ expectedSummaryOutput, actualOutput), actualOutput.contains(expectedSummaryOutput));
assertTrue(String.format("test output did not contain expected TestResult end tag. Got %s",
actualOutput), actualOutput.endsWith(expectedEndTag));
EasyMock.verify(mMockBuild);
@@ -146,10 +147,15 @@
mResultReporter.testStarted(testId);
mResultReporter.testEnded(testId, emptyMap);
mResultReporter.testRunEnded(3000, emptyMap);
- mResultReporter.invocationEnded(1);
mResultReporter.putSummary(SUMMARY_LIST);
+ mResultReporter.invocationEnded(1);
String output = getOutput();
// TODO: consider doing xml based compare
+ final String expectedTestOutput = String.format(
+ "<TestResult testPlan=\"NA\" starttime=\"ignore\" endtime=\"ignore\" " +
+ "version=\"%s\" suite=\"%s\" referenceUrl=\"%s\"> ",
+ CTS_RESULT_FILE_VERSION, "CTS", TEST_SUMMARY_URL);
+ assertTrue("Found output: " + output, output.contains(expectedTestOutput));
assertTrue(output.contains(
"<Summary failed=\"0\" notExecuted=\"0\" timeout=\"0\" pass=\"1\" />"));
assertTrue(output.contains("<TestPackage name=\"\" appPackageName=\"run\" abi=\"" +
diff --git a/tools/utils/buildCts.py b/tools/utils/buildCts.py
index 2b7e093..a6c76b1 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():
@@ -224,6 +222,20 @@
plan.ExcludeTests(package, test_list)
self.__WritePlan(plan, 'CTS-hardware')
+ # CTS - sub plan for camera tests which is public, large
+ plan = tools.TestPlan(packages)
+ plan.Exclude('.*')
+ plan.Include(r'android\.camera$')
+ misc_camera_tests = BuildCtsMiscCameraList()
+ for package, test_list in misc_camera_tests.iteritems():
+ plan.Include(package+'$')
+ plan.IncludeTests(package, test_list)
+ for package, test_list in flaky_tests.iteritems():
+ plan.ExcludeTests(package, test_list)
+ for package, test_list in releasekey_tests.iteritems():
+ plan.ExcludeTests(package, test_list)
+ self.__WritePlan(plan, 'CTS-camera')
+
# CTS - sub plan for media tests which is public, large
plan = tools.TestPlan(packages)
plan.Exclude('.*')
@@ -273,9 +285,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 +295,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)
@@ -430,7 +448,7 @@
""" Construct a defaultdict that maps package name to a list of tests
that flaky during dev cycle and cause other subsequent tests to fail. """
return {
- 'android.hardware' : [
+ 'android.camera' : [
'android.hardware.cts.CameraTest#testVideoSnapshot',
'android.hardware.cts.CameraGLTest#testCameraToSurfaceTextureMetadata',
'android.hardware.cts.CameraGLTest#testSetPreviewTextureBothCallbacks',
@@ -461,11 +479,60 @@
""" 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 BuildCtsMiscCameraList():
+ """ Construct a defaultdict that maps package name to a list of tests
+ that are relevant to camera but does not reside in camera test package """
+ return {
+ 'android.app' : [
+ 'android.app.cts.SystemFeaturesTest#testCameraFeatures',
+ ],
+ 'android.permission' : [
+ 'android.permission.cts.CameraPermissionTest',
+ 'android.permission.cts.Camera2PermissionTest',
+ ],
'' : []}
def LogGenerateDescription(name):