Squashed mnc-dev changes:
This contains all of the changes from b54aa51 to
791e51a on mnc-dev, except the changes
to tests/tests/security.
Bug: 24846656
Change-Id: I01f53a1a238ac49f86928e0e22796dc73e0e34af
diff --git a/apps/CameraITS/pymodules/its/device.py b/apps/CameraITS/pymodules/its/device.py
index 756f959..835a4a4 100644
--- a/apps/CameraITS/pymodules/its/device.py
+++ b/apps/CameraITS/pymodules/its/device.py
@@ -448,8 +448,8 @@
The out_surfaces field can specify the width(s), height(s), and
format(s) of the captured image. The formats may be "yuv", "jpeg",
- "dng", "raw", "raw10", or "raw12". The default is a YUV420 frame ("yuv")
- corresponding to a full sensor frame.
+ "dng", "raw", "raw10", "raw12", or "rawStats". The default is a YUV420
+ frame ("yuv") corresponding to a full sensor frame.
Note that one or more surfaces can be specified, allowing a capture to
request images back in multiple formats (e.g.) raw+yuv, raw+jpeg,
@@ -536,6 +536,25 @@
yuv_caps = do_capture( [req1,req2], yuv_fmt )
yuv_caps, raw_caps = do_capture( [req1,req2], [yuv_fmt,raw_fmt] )
+ The "rawStats" format processes the raw image and returns a new image
+ of statistics from the raw image. The format takes additional keys,
+ "gridWidth" and "gridHeight" which are size of grid cells in a 2D grid
+ of the raw image. For each grid cell, the mean and variance of each raw
+ channel is computed, and the do_capture call returns two 4-element float
+ images of dimensions (rawWidth / gridWidth, rawHeight / gridHeight),
+ concatenated back-to-back, where the first iamge contains the 4-channel
+ means and the second contains the 4-channel variances.
+
+ For the rawStats format, if the gridWidth is not provided then the raw
+ image width is used as the default, and similarly for gridHeight. With
+ this, the following is an example of a output description that computes
+ the mean and variance across each image row:
+
+ {
+ "gridHeight": 1,
+ "format": "rawStats"
+ }
+
Args:
cap_request: The Python dict/list specifying the capture(s), which
will be converted to JSON and sent to the device.
@@ -550,7 +569,8 @@
* data: the image data as a numpy array of bytes.
* width: the width of the captured image.
* height: the height of the captured image.
- * format: image the format, in ["yuv","jpeg","raw","raw10","dng"].
+ * format: image the format, in [
+ "yuv","jpeg","raw","raw10","raw12","rawStats","dng"].
* metadata: the capture result object (Python dictionary).
"""
cmd = {}
@@ -577,9 +597,13 @@
nsurf = 1 if out_surfaces is None else len(cmd["outputSurfaces"])
if len(formats) > len(set(formats)):
raise its.error.Error('Duplicate format requested')
- if "dng" in formats and "raw" in formats or \
- "dng" in formats and "raw10" in formats or \
- "raw" in formats and "raw10" in formats:
+ raw_formats = 0;
+ raw_formats += 1 if "dng" in formats else 0
+ raw_formats += 1 if "raw" in formats else 0
+ raw_formats += 1 if "raw10" in formats else 0
+ raw_formats += 1 if "raw12" in formats else 0
+ raw_formats += 1 if "rawStats" in formats else 0
+ if raw_formats > 1:
raise its.error.Error('Different raw formats not supported')
# Detect long exposure time and set timeout accordingly
@@ -603,14 +627,16 @@
# the burst, however individual images of different formats can come
# out in any order for that capture.
nbufs = 0
- bufs = {"yuv":[], "raw":[], "raw10":[], "dng":[], "jpeg":[]}
+ bufs = {"yuv":[], "raw":[], "raw10":[], "raw12":[],
+ "rawStats":[], "dng":[], "jpeg":[]}
mds = []
widths = None
heights = None
while nbufs < ncap*nsurf or len(mds) < ncap:
jsonObj,buf = self.__read_response_from_socket()
if jsonObj['tag'] in ['jpegImage', 'yuvImage', 'rawImage', \
- 'raw10Image', 'dngImage'] and buf is not None:
+ 'raw10Image', 'raw12Image', 'rawStatsImage', 'dngImage'] \
+ and buf is not None:
fmt = jsonObj['tag'][:-5]
bufs[fmt].append(buf)
nbufs += 1
diff --git a/apps/CameraITS/pymodules/its/image.py b/apps/CameraITS/pymodules/its/image.py
index ea01a3e..a5ac60b 100644
--- a/apps/CameraITS/pymodules/its/image.py
+++ b/apps/CameraITS/pymodules/its/image.py
@@ -81,6 +81,25 @@
else:
raise its.error.Error('Invalid format %s' % (cap["format"]))
+def unpack_rawstats_capture(cap):
+ """Unpack a rawStats capture to the mean and variance images.
+
+ Args:
+ cap: A capture object as returned by its.device.do_capture.
+
+ Returns:
+ Tuple (mean_image var_image) of float-4 images, with non-normalized
+ pixel values computed from the RAW16 images on the device
+ """
+ assert(cap["format"] == "rawStats")
+ w = cap["width"]
+ h = cap["height"]
+ img = numpy.ndarray(shape=(2*h*w*4,), dtype='<f', buffer=cap["data"])
+ analysis_image = img.reshape(2,h,w,4)
+ mean_image = analysis_image[0,:,:,:].reshape(h,w,4)
+ var_image = analysis_image[1,:,:,:].reshape(h,w,4)
+ return mean_image, var_image
+
def unpack_raw10_capture(cap, props):
"""Unpack a raw-10 capture to a raw-16 capture.
@@ -604,6 +623,21 @@
variances.append(numpy.var(img[:,:,i], dtype=numpy.float64))
return variances
+def compute_image_snrs(img):
+ """Calculate the SNR (db) of each color channel in the image.
+
+ Args:
+ img: Numpy float image array, with pixel values in [0,1].
+
+ Returns:
+ A list of SNR value, one per color channel in the image.
+ """
+ means = compute_image_means(img)
+ variances = compute_image_variances(img)
+ std_devs = [math.sqrt(v) for v in variances]
+ snr = [20 * math.log10(m/s) for m,s in zip(means, std_devs)]
+ return snr
+
def write_image(img, fname, apply_gamma=False):
"""Save a float-3 numpy array image to a file.
diff --git a/apps/CameraITS/pymodules/its/objects.py b/apps/CameraITS/pymodules/its/objects.py
index 82346ec..ac384fb 100644
--- a/apps/CameraITS/pymodules/its/objects.py
+++ b/apps/CameraITS/pymodules/its/objects.py
@@ -152,24 +152,37 @@
return req
-def get_available_output_sizes(fmt, props):
+def get_available_output_sizes(fmt, props, max_size=None, match_ar_size=None):
"""Return a sorted list of available output sizes for a given format.
Args:
fmt: the output format, as a string in
["jpg", "yuv", "raw", "raw10", "raw12"].
props: the object returned from its.device.get_camera_properties().
+ max_size: (Optional) A (w,h) tuple.
+ Sizes larger than max_size (either w or h) will be discarded.
+ match_ar_size: (Optional) A (w,h) tuple.
+ Sizes not matching the aspect ratio of match_ar_size will be
+ discarded.
Returns:
A sorted list of (w,h) tuples (sorted large-to-small).
"""
- fmt_codes = {"raw":0x20, "raw10":0x25, "raw12":0x26, "yuv":0x23,
+ AR_TOLERANCE = 0.03
+ fmt_codes = {"raw":0x20, "raw10":0x25, "raw12":0x26,"yuv":0x23,
"jpg":0x100, "jpeg":0x100}
configs = props['android.scaler.streamConfigurationMap']\
['availableStreamConfigurations']
fmt_configs = [cfg for cfg in configs if cfg['format'] == fmt_codes[fmt]]
out_configs = [cfg for cfg in fmt_configs if cfg['input'] == False]
out_sizes = [(cfg['width'],cfg['height']) for cfg in out_configs]
+ if max_size:
+ out_sizes = [s for s in out_sizes if
+ s[0] <= max_size[0] and s[1] <= max_size[1]]
+ if match_ar_size:
+ ar = match_ar_size[0] / float(match_ar_size[1])
+ out_sizes = [s for s in out_sizes if
+ abs(ar - s[0] / float(s[1])) <= AR_TOLERANCE]
out_sizes.sort(reverse=True)
return out_sizes
diff --git a/apps/CameraITS/tests/inprog/test_rawstats.py b/apps/CameraITS/tests/inprog/test_rawstats.py
new file mode 100644
index 0000000..8083f0b
--- /dev/null
+++ b/apps/CameraITS/tests/inprog/test_rawstats.py
@@ -0,0 +1,48 @@
+# Copyright 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import its.image
+import its.caps
+import its.device
+import its.objects
+import its.target
+import os.path
+import math
+
+def main():
+ """Test capturing some rawstats data.
+ """
+ NAME = os.path.basename(__file__).split(".")[0]
+
+ with its.device.ItsSession() as cam:
+
+ cam.do_3a(do_af=False);
+ req = its.objects.auto_capture_request()
+
+ for (gw,gh) in [(16,16)]:#,(4080,1)]:
+ cap = cam.do_capture(req,
+ {"format":"rawStats","gridWidth":gw,"gridHeight":gh})
+ mean_image, var_image = its.image.unpack_rawstats_capture(cap)
+
+ if gw > 1 and gh > 1:
+ h,w,_ = mean_image.shape
+ for ch in range(4):
+ m = mean_image[:,:,ch].reshape(h,w,1)/1023.0
+ v = var_image[:,:,ch].reshape(h,w,1)
+ its.image.write_image(m, "%s_mean_ch%d.jpg" % (NAME,ch), True)
+ its.image.write_image(v, "%s_var_ch%d.jpg" % (NAME,ch), True)
+
+if __name__ == '__main__':
+ main()
+
diff --git a/apps/CameraITS/tests/scene1/test_exposure.py b/apps/CameraITS/tests/scene1/test_exposure.py
index 26c398d..89bc724 100644
--- a/apps/CameraITS/tests/scene1/test_exposure.py
+++ b/apps/CameraITS/tests/scene1/test_exposure.py
@@ -35,8 +35,10 @@
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 = []
@@ -50,21 +52,33 @@
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=%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])
+ # 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)
@@ -73,9 +87,9 @@
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))
diff --git a/apps/CameraITS/tests/scene1/test_param_noise_reduction.py b/apps/CameraITS/tests/scene1/test_param_noise_reduction.py
index 35cfc07..1072684 100644
--- a/apps/CameraITS/tests/scene1/test_param_noise_reduction.py
+++ b/apps/CameraITS/tests/scene1/test_param_noise_reduction.py
@@ -35,12 +35,13 @@
"""
NAME = os.path.basename(__file__).split(".")[0]
- RELATIVE_ERROR_TOLERANCE = 0.1
- # List of variances for R,G,B.
- variances = [[],[],[]]
+ NUM_SAMPLES_PER_MODE = 4
+ SNR_TOLERANCE = 3 # unit in db
+ # List of SNRs for R,G,B.
+ snrs = [[], [], []]
- # Reference (baseline) variance for each of R,G,B.
- ref_variance = []
+ # Reference (baseline) SNR for each of R,G,B.
+ ref_snr = []
nr_modes_reported = []
@@ -60,8 +61,8 @@
rgb_image,
"%s_low_gain.jpg" % (NAME))
rgb_tile = its.image.get_image_patch(rgb_image, 0.45, 0.45, 0.1, 0.1)
- ref_variance = its.image.compute_image_variances(rgb_tile)
- print "Ref variances:", ref_variance
+ 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
@@ -70,58 +71,74 @@
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;
- 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)
- 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_vars = its.image.compute_image_variances(rgb_tile)
+ 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):
- variance = rgb_vars[chan]
- variances[chan].append(variance / ref_variance[chan])
- print "Variances with NR mode [0,1,2]:", variances
+ snrs[chan].append(rgb_snrs[chan])
# Draw a plot.
for j in range(3):
- pylab.plot(range(5), variances[j], "rgb"[j])
- matplotlib.pyplot.savefig("%s_plot_variances.png" % (NAME))
+ pylab.plot(range(5), snrs[j], "rgb"[j])
+ matplotlib.pyplot.savefig("%s_plot_SNRs.png" % (NAME))
assert(nr_modes_reported == [0,1,2,3,4])
for j in range(3):
- # Smaller variance is better
+ # Larger SNR is better
# Verify OFF(0) is not better than FAST(1)
- assert(variances[j][0] >
- variances[j][1] * (1.0 - RELATIVE_ERROR_TOLERANCE))
+ assert(snrs[j][0] <
+ snrs[j][1] + SNR_TOLERANCE)
# Verify FAST(1) is not better than HQ(2)
- assert(variances[j][1] >
- variances[j][2] * (1.0 - RELATIVE_ERROR_TOLERANCE))
+ assert(snrs[j][1] <
+ snrs[j][2] + SNR_TOLERANCE)
# Verify HQ(2) is better than OFF(0)
- assert(variances[j][0] > variances[j][2])
+ assert(snrs[j][0] < snrs[j][2])
if its.caps.noise_reduction_mode(props, 3):
# Verify OFF(0) is not better than MINIMAL(3)
- assert(variances[j][0] >
- variances[j][3] * (1.0 - RELATIVE_ERROR_TOLERANCE))
+ assert(snrs[j][0] <
+ snrs[j][3] + SNR_TOLERANCE)
# Verify MINIMAL(3) is not better than HQ(2)
- assert(variances[j][3] >
- variances[j][2] * (1.0 - RELATIVE_ERROR_TOLERANCE))
+ assert(snrs[j][3] <
+ snrs[j][2] + SNR_TOLERANCE)
if its.caps.noise_reduction_mode(props, 4):
# Verify ZSL(4) is close to MINIMAL(3)
- assert(numpy.isclose(variances[j][4], variances[j][3],
- RELATIVE_ERROR_TOLERANCE))
+ assert(numpy.isclose(snrs[j][4], snrs[j][3],
+ atol=SNR_TOLERANCE))
elif its.caps.noise_reduction_mode(props, 4):
# Verify ZSL(4) is close to OFF(0)
- assert(numpy.isclose(variances[j][4], variances[j][0],
- RELATIVE_ERROR_TOLERANCE))
+ assert(numpy.isclose(snrs[j][4], snrs[j][0],
+ atol=SNR_TOLERANCE))
if __name__ == '__main__':
main()
diff --git a/apps/CameraITS/tests/scene1/test_raw_burst_sensitivity.py b/apps/CameraITS/tests/scene1/test_raw_burst_sensitivity.py
index 6c2b5c1..e176312 100644
--- a/apps/CameraITS/tests/scene1/test_raw_burst_sensitivity.py
+++ b/apps/CameraITS/tests/scene1/test_raw_burst_sensitivity.py
@@ -44,6 +44,8 @@
# Expose for the scene with min sensitivity
sens_min, sens_max = props['android.sensor.info.sensitivityRange']
+ # Digital gains might not be visible on RAW data
+ sens_max = props['android.sensor.maxAnalogSensitivity']
sens_step = (sens_max - sens_min) / NUM_STEPS
s_ae,e_ae,_,_,_ = cam.do_3a(get_results=True)
s_e_prod = s_ae * e_ae
diff --git a/apps/CameraITS/tests/scene1/test_raw_sensitivity.py b/apps/CameraITS/tests/scene1/test_raw_sensitivity.py
index 14c5eb0..cc0ce14 100644
--- a/apps/CameraITS/tests/scene1/test_raw_sensitivity.py
+++ b/apps/CameraITS/tests/scene1/test_raw_sensitivity.py
@@ -42,6 +42,8 @@
# Expose for the scene with min sensitivity
sens_min, sens_max = props['android.sensor.info.sensitivityRange']
+ # Digital gains might not be visible on RAW data
+ sens_max = props['android.sensor.maxAnalogSensitivity']
sens_step = (sens_max - sens_min) / NUM_STEPS
s_ae,e_ae,_,_,_ = cam.do_3a(get_results=True)
s_e_prod = s_ae * e_ae
diff --git a/apps/CameraITS/tests/scene1/test_reprocess_noise_reduction.py b/apps/CameraITS/tests/scene1/test_reprocess_noise_reduction.py
index 757dfeb..f0a6fbe 100644
--- a/apps/CameraITS/tests/scene1/test_reprocess_noise_reduction.py
+++ b/apps/CameraITS/tests/scene1/test_reprocess_noise_reduction.py
@@ -38,7 +38,8 @@
NAME = os.path.basename(__file__).split(".")[0]
- RELATIVE_ERROR_TOLERANCE = 0.1
+ NUM_SAMPLES_PER_MODE = 4
+ SNR_TOLERANCE = 3 # unit in db
with its.device.ItsSession() as cam:
props = cam.get_camera_properties()
@@ -60,7 +61,7 @@
for reprocess_format in reprocess_formats:
# List of variances for R, G, B.
- variances = []
+ snrs = [[], [], []]
nr_modes_reported = []
# NR mode 0 with low gain
@@ -77,71 +78,90 @@
img = its.image.decompress_jpeg_to_rgb_image(cap["data"])
its.image.write_image(img, "%s_low_gain_fmt=jpg.jpg" % (NAME))
tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
- ref_variance = its.image.compute_image_variances(tile)
- print "Ref variances:", ref_variance
+ ref_snr = its.image.compute_image_snrs(tile)
+ print "Ref SNRs:", ref_snr
+ e, s = its.target.get_target_exposure_combos(cam)["maxSensitivity"]
for nr_mode in range(5):
# Skip unavailable modes
if not its.caps.noise_reduction_mode(props, nr_mode):
nr_modes_reported.append(nr_mode)
- variances.append(0)
+ for channel in range(3):
+ snrs[channel].append(0)
continue
- # NR modes with high gain
- e, s = its.target.get_target_exposure_combos(cam) \
- ["maxSensitivity"]
- req = its.objects.manual_capture_request(s, e)
- req["android.noiseReduction.mode"] = nr_mode
- cap = cam.do_capture(req, out_surface, reprocess_format)
- nr_modes_reported.append(
- cap["metadata"]["android.noiseReduction.mode"])
+ rgb_snr_list = []
+ # Capture several images to account for per frame noise
+ # variations
+ for n in range(NUM_SAMPLES_PER_MODE):
+ req = its.objects.manual_capture_request(s, e)
+ req["android.noiseReduction.mode"] = nr_mode
+ cap = cam.do_capture(req, out_surface, reprocess_format)
- img = its.image.decompress_jpeg_to_rgb_image(cap["data"])
- its.image.write_image(
- img, "%s_high_gain_nr=%d_fmt=jpg.jpg" % (NAME, nr_mode))
- tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
- # Get the variances for R, G, and B channels
- variance = its.image.compute_image_variances(tile)
- variances.append(
- [variance[chan] / ref_variance[chan] for chan in range(3)])
- print "Variances with NR mode [0,1,2,3,4]:", variances
+ img = its.image.decompress_jpeg_to_rgb_image(cap["data"])
+ if n == 0:
+ its.image.write_image(
+ img,
+ "%s_high_gain_nr=%d_fmt=jpg.jpg"
+ %(NAME, nr_mode))
+ nr_modes_reported.append(
+ cap["metadata"]["android.noiseReduction.mode"])
+
+ tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
+ # Get the variances for R, G, and B channels
+ rgb_snrs = its.image.compute_image_snrs(tile)
+ rgb_snr_list.append(rgb_snrs)
+
+ r_snrs = [rgb[0] for rgb in rgb_snr_list]
+ g_snrs = [rgb[1] for rgb in rgb_snr_list]
+ b_snrs = [rgb[2] for rgb in rgb_snr_list]
+ rgb_snrs = [numpy.mean(r_snrs),
+ numpy.mean(g_snrs),
+ numpy.mean(b_snrs)]
+ print "NR mode", nr_mode, "SNRs:"
+ print " R SNR:", rgb_snrs[0],\
+ "Min:", min(r_snrs), "Max:", max(r_snrs)
+ print " G SNR:", rgb_snrs[1],\
+ "Min:", min(g_snrs), "Max:", max(g_snrs)
+ print " B SNR:", rgb_snrs[2],\
+ "Min:", min(b_snrs), "Max:", max(b_snrs)
+
+ for chan in range(3):
+ snrs[chan].append(rgb_snrs[chan])
# Draw a plot.
- for chan in range(3):
- line = []
- for nr_mode in range(5):
- line.append(variances[nr_mode][chan])
- pylab.plot(range(5), line, "rgb"[chan])
+ for channel in range(3):
+ pylab.plot(range(5), snrs[channel], "rgb"[channel])
- matplotlib.pyplot.savefig("%s_plot_%s_variances.png" %
+ matplotlib.pyplot.savefig("%s_plot_%s_SNRs.png" %
(NAME, reprocess_format))
assert(nr_modes_reported == [0,1,2,3,4])
for j in range(3):
- # Smaller variance is better
+ # Larger is better
# Verify OFF(0) is not better than FAST(1)
- assert(variances[0][j] >
- variances[1][j] * (1.0 - RELATIVE_ERROR_TOLERANCE))
+ assert(snrs[j][0] <
+ snrs[j][1] + SNR_TOLERANCE)
# Verify FAST(1) is not better than HQ(2)
- assert(variances[1][j] >
- variances[2][j] * (1.0 - RELATIVE_ERROR_TOLERANCE))
+ assert(snrs[j][1] <
+ snrs[j][2] + SNR_TOLERANCE)
# Verify HQ(2) is better than OFF(0)
- assert(variances[0][j] > variances[2][j])
+ assert(snrs[j][0] < snrs[j][2])
if its.caps.noise_reduction_mode(props, 3):
# Verify OFF(0) is not better than MINIMAL(3)
- assert(variances[0][j] >
- variances[3][j] * (1.0 - RELATIVE_ERROR_TOLERANCE))
+ assert(snrs[j][0] <
+ snrs[j][3] + SNR_TOLERANCE)
# Verify MINIMAL(3) is not better than HQ(2)
- assert(variances[3][j] >
- variances[2][j] * (1.0 - RELATIVE_ERROR_TOLERANCE))
+ assert(snrs[j][3] <
+ snrs[j][2] + SNR_TOLERANCE)
# Verify ZSL(4) is close to MINIMAL(3)
- assert(numpy.isclose(variances[4][j], variances[3][j],
- RELATIVE_ERROR_TOLERANCE))
+ assert(numpy.isclose(snrs[j][4], snrs[j][3],
+ atol=SNR_TOLERANCE))
else:
# Verify ZSL(4) is close to OFF(0)
- assert(numpy.isclose(variances[4][j], variances[0][j],
- RELATIVE_ERROR_TOLERANCE))
+ assert(numpy.isclose(snrs[j][4], snrs[j][0],
+ atol=SNR_TOLERANCE))
if __name__ == '__main__':
main()
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_dng.py b/apps/CameraITS/tests/scene1/test_yuv_plus_dng.py
index 33e7763..268b64a 100644
--- a/apps/CameraITS/tests/scene1/test_yuv_plus_dng.py
+++ b/apps/CameraITS/tests/scene1/test_yuv_plus_dng.py
@@ -31,6 +31,12 @@
cam.do_3a()
req = its.objects.auto_capture_request()
+ max_dng_size = \
+ its.objects.get_available_output_sizes("raw", props)[0]
+ w,h = its.objects.get_available_output_sizes(
+ "yuv", props, (1920, 1080), max_dng_size)[0]
+ out_surfaces = [{"format":"dng"},
+ {"format":"yuv", "width":w, "height":h}]
cap_dng, cap_yuv = cam.do_capture(req, cam.CAP_DNG_YUV)
img = its.image.convert_capture_to_rgb_image(cap_yuv)
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_jpeg.py b/apps/CameraITS/tests/scene1/test_yuv_plus_jpeg.py
index 9ce8d76..78378eb 100644
--- a/apps/CameraITS/tests/scene1/test_yuv_plus_jpeg.py
+++ b/apps/CameraITS/tests/scene1/test_yuv_plus_jpeg.py
@@ -27,13 +27,17 @@
THRESHOLD_MAX_RMS_DIFF = 0.01
- fmt_yuv = {"format":"yuv"}
- fmt_jpeg = {"format":"jpeg"}
-
with its.device.ItsSession() as cam:
props = cam.get_camera_properties()
its.caps.skip_unless(its.caps.compute_target_exposure(props))
+ max_jpeg_size = \
+ its.objects.get_available_output_sizes("jpeg", props)[0]
+ w,h = its.objects.get_available_output_sizes(
+ "yuv", props, (1920, 1080), max_jpeg_size)[0]
+ fmt_yuv = {"format":"yuv", "width":w, "height":h}
+ fmt_jpeg = {"format":"jpeg"}
+
# Use a manual request with a linear tonemap so that the YUV and JPEG
# should look the same (once converted by the its.image module).
e, s = its.target.get_target_exposure_combos(cam)["midExposureTime"]
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_raw.py b/apps/CameraITS/tests/scene1/test_yuv_plus_raw.py
index f13801b..bfa6a28 100644
--- a/apps/CameraITS/tests/scene1/test_yuv_plus_raw.py
+++ b/apps/CameraITS/tests/scene1/test_yuv_plus_raw.py
@@ -38,7 +38,13 @@
e, s = its.target.get_target_exposure_combos(cam)["midExposureTime"]
req = its.objects.manual_capture_request(s, e, True, props)
- cap_raw, cap_yuv = cam.do_capture(req, cam.CAP_RAW_YUV)
+ max_raw_size = \
+ its.objects.get_available_output_sizes("raw", props)[0]
+ w,h = its.objects.get_available_output_sizes(
+ "yuv", props, (1920, 1080), max_raw_size)[0]
+ out_surfaces = [{"format":"raw"},
+ {"format":"yuv", "width":w, "height":h}]
+ cap_raw, cap_yuv = cam.do_capture(req, out_surfaces)
img = its.image.convert_capture_to_rgb_image(cap_yuv)
its.image.write_image(img, "%s_yuv.jpg" % (NAME), True)
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_raw10.py b/apps/CameraITS/tests/scene1/test_yuv_plus_raw10.py
index e52946d..322af10 100644
--- a/apps/CameraITS/tests/scene1/test_yuv_plus_raw10.py
+++ b/apps/CameraITS/tests/scene1/test_yuv_plus_raw10.py
@@ -38,8 +38,13 @@
e, s = its.target.get_target_exposure_combos(cam)["midExposureTime"]
req = its.objects.manual_capture_request(s, e, True, props)
+ max_raw10_size = \
+ its.objects.get_available_output_sizes("raw10", props)[0]
+ w,h = its.objects.get_available_output_sizes(
+ "yuv", props, (1920, 1080), max_raw10_size)[0]
cap_raw, cap_yuv = cam.do_capture(req,
- [{"format":"raw10"}, {"format":"yuv"}])
+ [{"format":"raw10"},
+ {"format":"yuv", "width":w, "height":h}])
img = its.image.convert_capture_to_rgb_image(cap_yuv)
its.image.write_image(img, "%s_yuv.jpg" % (NAME), True)
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_raw12.py b/apps/CameraITS/tests/scene1/test_yuv_plus_raw12.py
index c5c3c73..b3cca0b 100644
--- a/apps/CameraITS/tests/scene1/test_yuv_plus_raw12.py
+++ b/apps/CameraITS/tests/scene1/test_yuv_plus_raw12.py
@@ -38,8 +38,13 @@
e, s = its.target.get_target_exposure_combos(cam)["midExposureTime"]
req = its.objects.manual_capture_request(s, e, True, props)
+ max_raw12_size = \
+ its.objects.get_available_output_sizes("raw12", props)[0]
+ w,h = its.objects.get_available_output_sizes(
+ "yuv", props, (1920, 1080), max_raw12_size)[0]
cap_raw, cap_yuv = cam.do_capture(req,
- [{"format":"raw12"}, {"format":"yuv"}])
+ [{"format":"raw12"},
+ {"format":"yuv", "width":w, "height":h}])
img = its.image.convert_capture_to_rgb_image(cap_yuv)
its.image.write_image(img, "%s_yuv.jpg" % (NAME), True)
diff --git a/apps/CameraITS/tests/scene3/test_reprocess_edge_enhancement.py b/apps/CameraITS/tests/scene3/test_reprocess_edge_enhancement.py
index 73834cb..e96a9ee 100644
--- a/apps/CameraITS/tests/scene3/test_reprocess_edge_enhancement.py
+++ b/apps/CameraITS/tests/scene3/test_reprocess_edge_enhancement.py
@@ -53,22 +53,28 @@
"""
NAME = os.path.basename(__file__).split(".")[0]
+ NUM_SAMPLES = 4
req = its.objects.manual_capture_request(sensitivity, exp)
req["android.lens.focusDistance"] = fd
req["android.edge.mode"] = edge_mode
if (reprocess_format != None):
req["android.reprocess.effectiveExposureFactor"] = 1.0
- cap = cam.do_capture(req, out_surface, reprocess_format)
- img = its.image.decompress_jpeg_to_rgb_image(cap["data"])
- its.image.write_image(img, "%s_edge=%d_reprocess_fmt_%s.jpg" %
- (NAME, edge_mode, reprocess_format))
- tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
+ sharpness_list = []
+ for n in range(NUM_SAMPLES):
+ cap = cam.do_capture(req, out_surface, reprocess_format)
+ img = its.image.decompress_jpeg_to_rgb_image(cap["data"])
+ if n == 0:
+ its.image.write_image(img, "%s_reprocess_fmt_%s_edge=%d.jpg" %
+ (NAME, reprocess_format, edge_mode))
+ res_edge_mode = cap["metadata"]["android.edge.mode"]
+ tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
+ sharpness_list.append(its.image.compute_image_sharpness(tile))
ret = {}
- ret["edge_mode"] = cap["metadata"]["android.edge.mode"]
- ret["sharpness"] = its.image.compute_image_sharpness(tile)
+ ret["edge_mode"] = res_edge_mode
+ ret["sharpness"] = numpy.mean(sharpness_list)
return ret
diff --git a/apps/CtsVerifier/Android.mk b/apps/CtsVerifier/Android.mk
index 8cec7ea..34246cc 100644
--- a/apps/CtsVerifier/Android.mk
+++ b/apps/CtsVerifier/Android.mk
@@ -41,7 +41,7 @@
LOCAL_PACKAGE_NAME := CtsVerifier
-LOCAL_AAPT_FLAGS += --version-name "6.0_r0 $(BUILD_NUMBER)"
+LOCAL_AAPT_FLAGS += --version-name "6.0_r1 $(BUILD_NUMBER)"
LOCAL_JNI_SHARED_LIBRARIES := libctsverifier_jni libaudioloopback_jni
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 5c188b7..5e8ab90 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -363,6 +363,7 @@
<meta-data android:name="test_category" android:value="@string/test_category_security" />
<meta-data android:name="test_excluded_features"
android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch" />
+ <meta-data android:name="test_required_features" android:value="android.hardware.fingerprint" />
</activity>
<activity android:name=".security.ScreenLockBoundKeysTest"
android:label="@string/sec_lock_bound_key_test"
@@ -753,7 +754,7 @@
android:screenOrientation="locked" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
- <category android:name="android.cts.intent.category.MANUAL_TEST"/>
+ <category android:name="android.cts.intent.category.MANUAL_TEST_disabled"/>
</intent-filter>
<meta-data
@@ -762,8 +763,6 @@
<meta-data
android:name="test_required_features"
android:value="android.hardware.sensor.accelerometer:android.hardware.sensor.gyroscope:android.hardware.sensor.compass:android.hardware.camera.any" />
- <meta-data android:name="test_excluded_features"
- android:value="android.hardware.type.television" />
</activity>
<activity
android:name=".sensors.RVCVRecordActivity"
@@ -1397,6 +1396,13 @@
</intent-filter>
</activity-alias>
+ <activity android:name=".managedprovisioning.AuthenticationBoundKeyTestActivity">
+ <intent-filter>
+ <action android:name="com.android.cts.verifier.managedprovisioning.action.AUTH_BOUND_KEY_TEST" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
<activity android:name=".managedprovisioning.ByodFlowTestActivity"
android:launchMode="singleTask"
android:label="@string/provisioning_byod">
@@ -1424,6 +1430,8 @@
<action android:name="com.android.cts.verifier.managedprovisioning.BYOD_KEYGUARD_DISABLED_FEATURES" />
<action android:name="com.android.cts.verifier.managedprovisioning.BYOD_LOCKNOW" />
<action android:name="com.android.cts.verifier.managedprovisioning.TEST_NFC_BEAM" />
+ <action android:name="com.android.cts.verifier.managedprovisioning.action.TEST_CROSS_PROFILE_INTENTS_DIALOG" />
+ <action android:name="com.android.cts.verifier.managedprovisioning.action.TEST_APP_LINKING_DIALOG" />
<category android:name="android.intent.category.DEFAULT"></category>
</intent-filter>
</activity>
@@ -1451,11 +1459,87 @@
<activity android:name=".managedprovisioning.CrossProfileTestActivity">
<intent-filter>
- <action android:name="com.android.cts.verifier.managedprovisioning.CROSS_PROFILE" />
- <!-- We need to have at least one activity listening to this intent in the parent
- to test if it is forwarded from the managed profile to the parent -->
+ <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" />
- <category android:name="android.intent.category.DEFAULT"></category>
+ <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>
@@ -1573,6 +1657,18 @@
android:value="android.software.live_tv" />
</activity>
+ <activity android:name=".tv.AppLinkTestActivity"
+ android:label="@string/tv_app_link_test"
+ android:launchMode="singleTask">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.cts.intent.category.MANUAL_TEST" />
+ </intent-filter>
+ <meta-data android:name="test_category" android:value="@string/test_category_tv" />
+ <meta-data android:name="test_required_features"
+ android:value="android.software.live_tv" />
+ </activity>
+
<activity android:name=".screenpinning.ScreenPinningTestActivity"
android:label="@string/screen_pinning_test">
<intent-filter>
@@ -1612,30 +1708,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>
@@ -1644,6 +1756,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/keychain_main.xml b/apps/CtsVerifier/res/layout/keychain_main.xml
index 01eb255..3f695cd 100644
--- a/apps/CtsVerifier/res/layout/keychain_main.xml
+++ b/apps/CtsVerifier/res/layout/keychain_main.xml
@@ -24,22 +24,36 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
- android:padding="10dip" >
+ android:padding="10dip">
- <TextView
- android:id="@+id/test_instruction"
- style="@style/InstructionsFont"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_weight="0" />
-
- <TextView
- android:id="@+id/test_log"
+ <ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
- android:layout_gravity="bottom"
- android:orientation="vertical" />
+ android:fillViewport="true">
+
+ <LinearLayout
+ android:id="@+id/test_messages"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/test_instruction"
+ style="@style/InstructionsFont"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="0" />
+
+ <TextView
+ android:id="@+id/test_log"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:orientation="vertical" />
+
+ </LinearLayout>
+ </ScrollView>
<LinearLayout
android:id="@+id/action_buttons"
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 77566a3..d8a96eb 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -1277,7 +1277,25 @@
<string name="provisioning_byod_no_video_capture_resolver">No video capture app present. Skip test.</string>
<string name="provisioning_byod_no_audio_capture_resolver">No audio capture app present. Skip test.</string>
<string name="provisioning_byod_capture_media_error">Error while capturing media from managed profile.</string>
+ <string name="provisioning_byod_capture_image_error">Error while capturing image from managed profile.</string>
+ <string name="provisioning_byod_auth_bound_key">Autentication-boud keys</string>
+ <string name="provisioning_byod_auth_bound_key_info">
+ This test verifies keystore cryptographic keys can be bound to device credentials.
+ These keys should only be available if there was a recent enough authentication.
+ </string>
+ <string name="provisioning_byod_auth_bound_key_instruction">
+ This test verifies keystore cryptographic keys can be bound to device lockscreen challenge or fingerprints (if available).
+ These keys should only be available if there was a recent enough authentication. \n
+
+ 1. Press "Set up" to open Security settings. Create a lockscreen password and if available, enroll a fingerprint.\n
+ 2. Go through the list of tests.\n
+ 3. Mark the overall test pass or fail.\n
+ 4. Once the set of tests are completed, remove the lockscreen challenge.
+ </string>
+ <string name="provisioning_byod_auth_bound_key_set_up">Set up</string>
+ <string name="provisioning_byod_lockscreen_bound_key">Lockscreen-bound key test</string>
+ <string name="provisioning_byod_fingerprint_bound_key">Fingerprint-bound key test</string>
<!-- Strings for DeskClock -->
<string name="deskclock_tests">Alarms and Timers Tests</string>
<string name="deskclock_tests_info">
@@ -1436,12 +1454,13 @@
<string name="provisioning_byod_profile_visible">Profile-aware accounts settings</string>
<string name="provisioning_byod_admin_visible">Profile-aware device administrator settings</string>
<string name="provisioning_byod_workapps_visible">Badged work apps visible in Launcher</string>
- <string name="provisioning_byod_cross_profile">Open app cross profiles</string>
- <string name="provisioning_byod_cross_profile_app_personal">
- You selected the CTS Verifier option.
- </string>
+ <string name="provisioning_byod_cross_profile_from_personal">Open app cross profiles from the personal side</string>
+ <string name="provisioning_byod_cross_profile_from_work">Open app cross profiles from the work side</string>
+ <string name="provisioning_app_linking">App links from the work side</string>
+ <string name="provisioning_byod_cross_profile_app_personal">You selected the personal option.</string>
<string name="provisioning_byod_cross_profile_app_work">You selected the Work option.</string>
- <string name="provisioning_byod_cross_profile_instruction">
+ <string name="provisioning_byod_cross_profile_app_ctsverifier"> You selected the ctsverifier option </string>
+ <string name="provisioning_byod_cross_profile_from_personal_instruction">
Please press the Go button to start an action.\n
\n
You should be asked to choose either \"CTS Verifier\" or \"Work\" to complete the action.
@@ -1449,6 +1468,25 @@
\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.
@@ -1640,6 +1678,7 @@
<string name="provisioning_byod_send_share_intent">Send share intent</string>
<string name="provisioning_byod_cannot_resolve_beam_activity">Cannot find beam activity</string>
+ <string name="test_failed_cannot_start_intent">Cannot start the given intent.</string>
<string name="provisioning_byod_no_activity">Cannot communicate with activity in the work profile.</string>
<string name="provisioning_byod_delete_profile">Initiate deletion of work profile.</string>
<string name="provisioning_byod_profile_deleted">Work profile deleted.</string>
@@ -1875,7 +1914,6 @@
Do you see the programs named \"Dummy Program\" and their descriptions
"Dummy Program Description" in the EPG?
</string>
- <string name="tv_input_discover_test_yes">Yes</string>
<string name="tv_parental_control_test">TV app parental controls test</string>
<string name="tv_parental_control_test_info">
@@ -1964,6 +2002,24 @@
The playback position should be moved to the next position.
</string>
+ <string name="tv_app_link_test">TV app app-link test</string>
+ <string name="tv_app_link_test_info">
+ Verify that the bundled TV app supports linking to channel apps. If TV input service provides
+ links for its specific channels, TV app should show the links in a proper format.
+ </string>
+ <string name="tv_app_link_test_select_app_link">
+ Select the \"Launch TV app\" button, then check if you can see a menu with \"Cts App-Link Text\"
+ text in red background. If you see the link, select it to follow the link.
+ </string>
+ <string name="tv_app_link_test_verify_link_clicked">
+ The app-link must have been clicked and the activity should be changed correctly.
+ </string>
+ <string name="tv_input_link_test_verify_link_interface">
+ Do you see the app-link card similar to the image on the left?\n
+ 1) You should see the poster art image, but the color could be differ.\n
+ 2) You should see the text \"Cts App-Link Text\".\n
+ </string>
+
<string name="overlay_view_text">Overlay View Dummy Text</string>
<string name="fake_rating">Fake</string>
@@ -1984,25 +2040,34 @@
<string name="error_screen_pinning_did_not_exit">Screen was not unpinned.</string>
<string name="error_screen_pinning_couldnt_exit">Could not exit screen pinning through API.</string>
- <!-- Audio Devices Notifcations Test -->
- <string name="audio_devices_notifications_test">Audio Devices Notifications Test</string>
- <string name="audio_devices_notification_instructions">
+ <!-- Audio Devices Notifcations Tests -->
+ <string name="audio_out_devices_notifications_test">Audio Output Devices Notifications Test</string>
+ <string name="audio_out_devices_notification_instructions">
Click the "Clear Messages" button then connect and disconnect a wired headset.
Note if the appropriate notification messages appear below.
</string>
+ <string name="audio_in_devices_notifications_test">Audio Input Devices Notifications Test</string>
+ <string name="audio_in_devices_notification_instructions">
+ Click the "Clear Messages" button then connect and disconnect a microphone or wired headset.
+ Note if the appropriate notification messages appear below.
+ </string>
<string name="audio_dev_notification_clearmsgs">Clear Messages</string>
<string name="audio_dev_notification_connectMsg">CONNECT DETECTED</string>
<string name="audio_dev_notification_disconnectMsg">DISCONNECT DETECTED</string>
- <!-- Audio Routing Notifcations Test -->
- <string name="audio_routingnotifications_test">Audio Routing Notifications Test</string>
- <string name="audio_dev_routingnotification_instructions">
- Click on the "Play" button in the AudioTrack Routing Notifictions section below to
+ <string name="audio_input_routingnotifications_test">Audio Input Routing Notifications Test</string>
+ <string name="audio_input_routingnotification_instructions">
+ Click on the "Record" button in the AudioRecord Routing Notifications section below to
+ start recording. Insert a wired headset or microphone. Observe a message acknowledging the
+ rerouting event below. Remove the wired headset and observe the new routing message.
+ Click on the "Stop" button to stop recording.\n
+ </string>
+ <string name="audio_output_routingnotifications_test">Audio Output Routing Notifications Test</string>
+ <string name="audio_output_routingnotification_instructions">
+ Click on the "Play" button in the AudioTrack Routing Notifications section below to
start (silent) playback. Insert a wired headset. Observe a message acknowledging the
rerouting event below. Remove the wired headset and observe the new routing message.
Click on the "Stop" button to stop playback.\n
- Repeat the process with "Record" and "Stop" button in the AudioRecord Routing
- Notifications section below.
</string>
<string name="audio_routingnotification_playBtn">Play</string>
<string name="audio_routingnotification_playStopBtn">Stop</string>
@@ -2013,14 +2078,19 @@
<string name="audio_routingnotification_trackRoutingMsg">AudioTrack rerouting</string>
<string name="audio_routingnotification_recordRoutingMsg">AudioRecord rerouting</string>
+ <!-- Audio general text -->
+ <string name="audio_general_headset_port_exists">Does this device have a headset port?</string>
+ <string name="audio_general_headset_no">No</string>
+ <string name="audio_general_headset_yes">Yes</string>
+
<!-- Audio Loopback Latency Test -->
<string name="audio_loopback_test">Audio Loopback Latency Test</string>
<string name="audio_loopback_info">
- This test requires the Loopback Plug. Please connect a Loopback Plug on the headset
+ This test requires the Loopback Plug. Please connect a Loopback Plug into the headset
connector, and proceed with the instructions on the screen.
The system will measure the input-output audio latency by injecting a pulse on the output,
and computing the distance between replicas of the pulse.
- You can vary the Audio Level slider to ensure the pulse will feed back at adecuate levels.
+ You can vary the Audio Level slider to ensure the pulse will feed back at adequate levels.
Repeat until a confidence level >= 0.6 is achieved.
</string>
<string name="audio_loopback_instructions">
@@ -2055,7 +2125,7 @@
<string name="audio_frequency_speaker_test">Audio Frequency Speaker Test</string>
<string name="audio_frequency_speaker_info">
This test requires an external USB reference microphone. Please connect the USB microphone and proceed with the instructions on the screen.
- The system will measure frequency response of the left and right speakers (if there are two speakers), or twice the response of the mono speaker.
+ The system will measure frequency response of the left and right speakers (if there are two speakers), or the response of the mono speaker twice.
</string>
<string name="audio_frequency_speaker_instructions">
Please connect an USB reference microphone and press "USB Reference microphone ready"
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/DialogTestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/DialogTestListActivity.java
index 789effa..91b8d93 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/DialogTestListActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/DialogTestListActivity.java
@@ -23,6 +23,7 @@
import android.content.Intent;
import android.database.DataSetObserver;
import android.os.Bundle;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
@@ -41,6 +42,7 @@
* Instructions are shown on top of the screen and a test preparation button is provided.
*/
public abstract class DialogTestListActivity extends PassFailButtons.TestListActivity {
+ private final String TAG = "DialogTestListActivity";
private final int mLayoutId;
private final int mTitleStringId;
private final int mInfoStringId;
@@ -170,7 +172,13 @@
mCurrentTestPosition = position;
((DialogTestListItem)test).performTest(this);
} else {
- super.handleItemClick(l, v, position, id);
+ try {
+ super.handleItemClick(l, v, position, id);
+ } catch (ActivityNotFoundException e) {
+ Log.d(TAG, "handleItemClick() threw exception: ", e);
+ setTestResult(test, TestResult.TEST_RESULT_FAILED);
+ showToast(R.string.test_failed_cannot_start_intent);
+ }
}
}
@@ -196,7 +204,7 @@
// do nothing, override in subclass if needed
}
- protected void setTestResult(DialogTestListItem test, int result) {
+ protected void setTestResult(TestListAdapter.TestListItem test, int result) {
// Bundle result in an intent to feed into handleLaunchTestResult
Intent resultIntent = new Intent();
TestResult.addResultData(resultIntent, result, test.testName, /* testDetails */ null,
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyLineActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyLineActivity.java
index d3e2571..508fae0 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyLineActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyLineActivity.java
@@ -64,6 +64,9 @@
OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
Context mContext;
+ Button mHeadsetPortYes;
+ Button mHeadsetPortNo;
+
Button mLoopbackPlugReady;
LinearLayout mLinearLayout;
Button mTestButton;
@@ -116,6 +119,20 @@
Log.i(TAG, "audio loopback test");
startAudioTest();
break;
+ case R.id.audio_general_headset_yes:
+ Log.i(TAG, "User confirms Headset Port existence");
+ mLoopbackPlugReady.setEnabled(true);
+ recordHeasetPortFound(true);
+ mHeadsetPortYes.setEnabled(false);
+ mHeadsetPortNo.setEnabled(false);
+ break;
+ case R.id.audio_general_headset_no:
+ Log.i(TAG, "User denies Headset Port existence");
+ recordHeasetPortFound(false);
+ getPassButton().setEnabled(true);
+ mHeadsetPortYes.setEnabled(false);
+ mHeadsetPortNo.setEnabled(false);
+ break;
}
}
}
@@ -127,8 +144,14 @@
mContext = this;
+ mHeadsetPortYes = (Button)findViewById(R.id.audio_general_headset_yes);
+ mHeadsetPortYes.setOnClickListener(mBtnClickListener);
+ mHeadsetPortNo = (Button)findViewById(R.id.audio_general_headset_no);
+ mHeadsetPortNo.setOnClickListener(mBtnClickListener);
+
mLoopbackPlugReady = (Button)findViewById(R.id.audio_frequency_line_plug_ready_btn);
mLoopbackPlugReady.setOnClickListener(mBtnClickListener);
+ mLoopbackPlugReady.setEnabled(false);
mLinearLayout = (LinearLayout)findViewById(R.id.audio_frequency_line_layout);
mTestButton = (Button)findViewById(R.id.audio_frequency_line_test_btn);
mTestButton.setOnClickListener(mBtnClickListener);
@@ -479,6 +502,14 @@
Log.v(TAG, "Results Recorded");
}
+ private void recordHeasetPortFound(boolean found) {
+ getReportLog().addValue(
+ "User Reported Headset Port",
+ found ? 1.0 : 0,
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+ }
+
private void startRecording() {
synchronized (mRecordingLock) {
mIsRecording = true;
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyMicActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyMicActivity.java
index b37a721..03d84e1 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyMicActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyMicActivity.java
@@ -72,6 +72,9 @@
final OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
Context mContext;
+ Button mHeadsetPortYes;
+ Button mHeadsetPortNo;
+
Button mSpeakersReady; //user signal to have connected external speakers
Button mTest1Button; //execute test 1
Button mUsbMicReady; //user signal to have connected USB Microphone
@@ -137,6 +140,20 @@
case R.id.audio_frequency_mic_test2_btn:
startTest2();
break;
+ case R.id.audio_general_headset_yes:
+ Log.i(TAG, "User confirms Headset Port existence");
+ mSpeakersReady.setEnabled(true);
+ recordHeasetPortFound(true);
+ mHeadsetPortYes.setEnabled(false);
+ mHeadsetPortNo.setEnabled(false);
+ break;
+ case R.id.audio_general_headset_no:
+ Log.i(TAG, "User denies Headset Port existence");
+ recordHeasetPortFound(false);
+ getPassButton().setEnabled(true);
+ mHeadsetPortYes.setEnabled(false);
+ mHeadsetPortNo.setEnabled(false);
+ break;
}
}
}
@@ -146,10 +163,17 @@
super.onCreate(savedInstanceState);
setContentView(R.layout.audio_frequency_mic_activity);
mContext = this;
+
+ mHeadsetPortYes = (Button)findViewById(R.id.audio_general_headset_yes);
+ mHeadsetPortYes.setOnClickListener(mBtnClickListener);
+ mHeadsetPortNo = (Button)findViewById(R.id.audio_general_headset_no);
+ mHeadsetPortNo.setOnClickListener(mBtnClickListener);
+
mSpeakerReadyText = (TextView) findViewById(R.id.audio_frequency_mic_speakers_ready_status);
mSpeakersReady = (Button)findViewById(R.id.audio_frequency_mic_speakers_ready_btn);
mSpeakersReady.setOnClickListener(mBtnClickListener);
+ mSpeakersReady.setEnabled(false);
mTest1Button = (Button)findViewById(R.id.audio_frequency_mic_test1_btn);
mTest1Button.setOnClickListener(mBtnClickListener);
mTest1Result = (TextView)findViewById(R.id.audio_frequency_mic_results1_text);
@@ -644,6 +668,14 @@
Log.v(TAG, "Results Recorded");
}
+ private void recordHeasetPortFound(boolean found) {
+ getReportLog().addValue(
+ "User Reported Headset Port",
+ found ? 1.0 : 0,
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+ }
+
private void startRecording() {
synchronized (mRecordingLock) {
mIsRecording = true;
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioDeviceNotificationsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioInputDeviceNotificationsActivity.java
similarity index 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/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/features/FeatureSummaryActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureSummaryActivity.java
index 51e0a62..3c108da 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureSummaryActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureSummaryActivity.java
@@ -232,6 +232,7 @@
public static final Feature[] ALL_MNC_FEATURES = {
new Feature(PackageManager.FEATURE_MIDI, false),
+ new Feature(PackageManager.FEATURE_AUDIO_PRO, false),
};
@Override
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/AuthenticationBoundKeyTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/AuthenticationBoundKeyTestActivity.java
new file mode 100644
index 0000000..073412d
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/AuthenticationBoundKeyTestActivity.java
@@ -0,0 +1,381 @@
+package com.android.cts.verifier.managedprovisioning;
+
+import android.Manifest;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.KeyguardManager;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.hardware.fingerprint.FingerprintManager;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.CountDownTimer;
+import android.provider.Settings;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyPermanentlyInvalidatedException;
+import android.security.keystore.KeyProperties;
+import android.security.keystore.UserNotAuthenticatedException;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Toast;
+
+import com.android.cts.verifier.ArrayTestListAdapter;
+import com.android.cts.verifier.DialogTestListActivity;
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.TestResult;
+
+import java.io.IOException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.KeyGenerator;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+
+/**
+ * Test device credential-bound keys in work profile.
+ * Currently there are two types, one is keys bound to lockscreen passwords which can be configured
+ * to remain available within a certain timeout after the latest successful user authentication.
+ * The other is keys bound to fingerprint authentication which require explicit fingerprint
+ * authentication before they can be accessed.
+ */
+public class AuthenticationBoundKeyTestActivity extends DialogTestListActivity {
+
+ public static final String ACTION_AUTH_BOUND_KEY_TEST =
+ "com.android.cts.verifier.managedprovisioning.action.AUTH_BOUND_KEY_TEST";
+
+ private static final int AUTHENTICATION_DURATION_SECONDS = 5;
+ private static final String LOCKSCREEN_KEY_NAME = "mp_lockscreen_key";
+ private static final String FINGERPRINT_KEY_NAME = "mp_fingerprint_key";
+ private static final byte[] SECRET_BYTE_ARRAY = new byte[] {1, 2, 3, 4, 5, 6};
+ private static final int CONFIRM_CREDENTIALS_REQUEST_CODE = 1;
+ private static final int FINGERPRINT_PERMISSION_REQUEST_CODE = 0;
+
+ private static final int LOCKSCREEN = 1;
+ private static final int FINGERPRINT = 2;
+
+ private static final String KEYSTORE_NAME = "AndroidKeyStore";
+ private static final String CIPHER_TRANSFORMATION = KeyProperties.KEY_ALGORITHM_AES + "/"
+ + KeyProperties.BLOCK_MODE_CBC + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7;
+
+
+ private KeyguardManager mKeyguardManager;
+ private FingerprintManager mFingerprintManager;
+ private boolean mFingerprintSupported;
+
+ private DialogTestListItem mLockScreenBoundKeyTest;
+ private DialogTestListItem mFingerprintBoundKeyTest;
+
+ private Cipher mFingerprintCipher;
+
+ public AuthenticationBoundKeyTestActivity() {
+ super(R.layout.provisioning_byod,
+ R.string.provisioning_byod_auth_bound_key,
+ R.string.provisioning_byod_auth_bound_key_info,
+ R.string.provisioning_byod_auth_bound_key_instruction);
+ }
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ mKeyguardManager = (KeyguardManager) getSystemService(KEYGUARD_SERVICE);
+ mFingerprintManager = (FingerprintManager) getSystemService(FINGERPRINT_SERVICE);
+ mFingerprintSupported = mFingerprintManager != null
+ && mFingerprintManager.isHardwareDetected();
+ // Need to have valid mFingerprintSupported value before calling super.onCreate() because
+ // mFingerprintSupported is used in setupTests() which gets called by super.onCreate().
+ super.onCreate(savedInstanceState);
+
+ mPrepareTestButton.setText(R.string.provisioning_byod_auth_bound_key_set_up);
+ mPrepareTestButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View arg0) {
+ startActivity(new Intent(Settings.ACTION_SECURITY_SETTINGS));
+ }
+ });
+ if (mFingerprintSupported) {
+ requestPermissions(new String[] {Manifest.permission.USE_FINGERPRINT},
+ FINGERPRINT_PERMISSION_REQUEST_CODE);
+ }
+ }
+
+ private class LockscreenCountDownTester extends CountDownTimer {
+
+ private Toast mToast;
+
+ public LockscreenCountDownTester() {
+ // Wait for AUTHENTICATION_DURATION_SECONDS so the key is evicted before the real test.
+ super(AUTHENTICATION_DURATION_SECONDS * 1000, 1000);
+ mToast = Toast.makeText(AuthenticationBoundKeyTestActivity.this, "", Toast.LENGTH_SHORT);
+ }
+
+ @Override
+ public void onFinish() {
+ mToast.cancel();
+ if (tryEncryptWithLockscreenKey()) {
+ showToast("Test failed. Key accessible without auth.");
+ setTestResult(mLockScreenBoundKeyTest, TestResult.TEST_RESULT_FAILED);
+ } else {
+ // Start the Confirm Credentials screen.
+ Intent intent = mKeyguardManager.createConfirmDeviceCredentialIntent(null, null);
+ if (intent != null) {
+ startActivityForResult(intent, CONFIRM_CREDENTIALS_REQUEST_CODE);
+ } else {
+ showToast("Test failed. No lockscreen password exists.");
+ setTestResult(mLockScreenBoundKeyTest, TestResult.TEST_RESULT_FAILED);
+ }
+ }
+ }
+
+ @Override
+ public void onTick(long millisUntilFinished) {
+ mToast.setText(String.format("Lockscreen challenge start in %d seconds..",
+ millisUntilFinished / 1000));
+ mToast.show();
+ }
+ }
+
+
+ @Override
+ protected void setupTests(ArrayTestListAdapter adapter) {
+ mLockScreenBoundKeyTest = new DialogTestListItem(this,
+ R.string.provisioning_byod_lockscreen_bound_key,
+ "BYOD_LockScreenBoundKeyTest") {
+
+ @Override
+ public void performTest(DialogTestListActivity activity) {
+ if (checkPreconditions()) {
+ createKey(LOCKSCREEN);
+ new LockscreenCountDownTester().start();
+ }
+ }
+ };
+ adapter.add(mLockScreenBoundKeyTest);
+
+ if (mFingerprintSupported) {
+ mFingerprintBoundKeyTest = new DialogTestListItem(this,
+ R.string.provisioning_byod_fingerprint_bound_key,
+ "BYOD_FingerprintBoundKeyTest") {
+
+ @Override
+ public void performTest(DialogTestListActivity activity) {
+ if (checkPreconditions()) {
+ createKey(FINGERPRINT);
+ mFingerprintCipher = initFingerprintEncryptionCipher();
+ if (tryEncryptWithFingerprintKey(mFingerprintCipher)) {
+ showToast("Test failed. Key accessible without auth.");
+ setTestResult(mFingerprintBoundKeyTest, TestResult.TEST_RESULT_FAILED);
+ } else {
+ new FingerprintAuthDialogFragment().show(getFragmentManager(),
+ "fingerprint_dialog");
+ }
+ }
+ }
+ };
+ adapter.add(mFingerprintBoundKeyTest);
+ }
+ }
+
+ private boolean checkPreconditions() {
+ if (!mKeyguardManager.isKeyguardSecure()) {
+ showToast("Please set a lockscreen password.");
+ return false;
+ } else if (mFingerprintSupported && !mFingerprintManager.hasEnrolledFingerprints()) {
+ showToast("Please enroll a fingerprint.");
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ private String getKeyName(int testType) {
+ return testType == LOCKSCREEN ? LOCKSCREEN_KEY_NAME : FINGERPRINT_KEY_NAME;
+ }
+ /**
+ * Creates a symmetric key in the Android Key Store which can only be used after the user has
+ * authenticated with device credentials.
+ */
+ private void createKey(int testType) {
+ try {
+ // Set the alias of the entry in Android KeyStore where the key will appear
+ // and the constrains (purposes) in the constructor of the Builder
+ KeyGenParameterSpec.Builder builder;
+ builder = new KeyGenParameterSpec.Builder(getKeyName(testType),
+ KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
+ .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
+ .setUserAuthenticationRequired(true)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7);
+ if (testType == LOCKSCREEN) {
+ // Require that the user unlocked the lockscreen in the last 5 seconds
+ builder.setUserAuthenticationValidityDurationSeconds(
+ AUTHENTICATION_DURATION_SECONDS);
+ }
+ KeyGenerator keyGenerator = KeyGenerator.getInstance(
+ KeyProperties.KEY_ALGORITHM_AES, KEYSTORE_NAME);
+ keyGenerator.init(builder.build());
+ keyGenerator.generateKey();
+ } catch (NoSuchAlgorithmException | NoSuchProviderException
+ | InvalidAlgorithmParameterException e) {
+ throw new RuntimeException("Failed to create a symmetric key", e);
+ }
+ }
+
+ private SecretKey loadSecretKey(int testType) {
+ try {
+ KeyStore keyStore = KeyStore.getInstance(KEYSTORE_NAME);
+ keyStore.load(null);
+ return (SecretKey) keyStore.getKey(getKeyName(testType), null);
+ } catch (UnrecoverableKeyException | CertificateException |KeyStoreException | IOException
+ | NoSuchAlgorithmException e) {
+ throw new RuntimeException("Failed to load a symmetric key", e);
+ }
+ }
+
+ private boolean tryEncryptWithLockscreenKey() {
+ try {
+ // Try encrypting something, it will only work if the user authenticated within
+ // the last AUTHENTICATION_DURATION_SECONDS seconds.
+ Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
+ cipher.init(Cipher.ENCRYPT_MODE, loadSecretKey(LOCKSCREEN));
+ cipher.doFinal(SECRET_BYTE_ARRAY);
+ return true;
+ } catch (UserNotAuthenticatedException e) {
+ // User is not authenticated, let's authenticate with device credentials.
+ return false;
+ } catch (KeyPermanentlyInvalidatedException e) {
+ // This happens if the lock screen has been disabled or reset after the key was
+ // generated.
+ createKey(LOCKSCREEN);
+ showToast("Set up lockscreen after test ran. Retry the test.");
+ return false;
+ } catch (IllegalBlockSizeException | BadPaddingException | InvalidKeyException
+ | NoSuchPaddingException | NoSuchAlgorithmException e) {
+ throw new RuntimeException("Encrypt with lockscreen-bound key failed", e);
+ }
+ }
+
+ private Cipher initFingerprintEncryptionCipher() {
+ try {
+ Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
+ cipher.init(Cipher.ENCRYPT_MODE, loadSecretKey(FINGERPRINT));
+ return cipher;
+ } catch (NoSuchPaddingException | NoSuchAlgorithmException e) {
+ return null;
+ } catch (KeyPermanentlyInvalidatedException e) {
+ // This happens if the lock screen has been disabled or reset after the key was
+ // generated after the key was generated.
+ createKey(FINGERPRINT);
+ showToast("Set up lockscreen after test ran. Retry the test.");
+ return null;
+ } catch (InvalidKeyException e) {
+ throw new RuntimeException("Init cipher with fingerprint-bound key failed", e);
+ }
+ }
+
+ private boolean tryEncryptWithFingerprintKey(Cipher cipher) {
+
+ try {
+ cipher.doFinal(SECRET_BYTE_ARRAY);
+ return true;
+ } catch (IllegalBlockSizeException e) {
+ // Cannot encrypt, key is unavailable
+ return false;
+ } catch (BadPaddingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ protected void handleActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case CONFIRM_CREDENTIALS_REQUEST_CODE:
+ if (resultCode == RESULT_OK) {
+ if (tryEncryptWithLockscreenKey()) {
+ setTestResult(mLockScreenBoundKeyTest, TestResult.TEST_RESULT_PASSED);
+ } else {
+ showToast("Test failed. Key not accessible after auth");
+ setTestResult(mLockScreenBoundKeyTest, TestResult.TEST_RESULT_FAILED);
+ }
+ } else {
+ showToast("Lockscreen challenge canceled.");
+ setTestResult(mLockScreenBoundKeyTest, TestResult.TEST_RESULT_FAILED);
+ }
+ break;
+ default:
+ super.handleActivityResult(requestCode, resultCode, data);
+ }
+ }
+
+ private void showToast(String message) {
+ Toast.makeText(this, message, Toast.LENGTH_LONG).show();
+ }
+
+ public class FingerprintAuthDialogFragment extends DialogFragment {
+
+ private CancellationSignal mCancellationSignal;
+ private FingerprintManagerCallback mFingerprintManagerCallback;
+ private boolean mSelfCancelled;
+
+ class FingerprintManagerCallback extends FingerprintManager.AuthenticationCallback {
+ @Override
+ public void onAuthenticationError(int errMsgId, CharSequence errString) {
+ if (!mSelfCancelled) {
+ showToast(errString.toString());
+ }
+ }
+
+ @Override
+ public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
+ showToast(helpString.toString());
+ }
+
+ @Override
+ public void onAuthenticationFailed() {
+ showToast(getString(R.string.sec_fp_auth_failed));
+ }
+
+ @Override
+ public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
+ if (tryEncryptWithFingerprintKey(mFingerprintCipher)) {
+ showToast("Test passed.");
+ setTestResult(mFingerprintBoundKeyTest, TestResult.TEST_RESULT_PASSED);
+ } else {
+ showToast("Test failed. Key not accessible after auth");
+ setTestResult(mFingerprintBoundKeyTest, TestResult.TEST_RESULT_FAILED);
+ }
+ FingerprintAuthDialogFragment.this.dismiss();
+ }
+ }
+
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ mCancellationSignal.cancel();
+ mSelfCancelled = true;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ mCancellationSignal = new CancellationSignal();
+ mSelfCancelled = false;
+ mFingerprintManagerCallback = new FingerprintManagerCallback();
+ mFingerprintManager.authenticate(new FingerprintManager.CryptoObject(mFingerprintCipher),
+ mCancellationSignal, 0, mFingerprintManagerCallback, null);
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ builder.setMessage(R.string.sec_fp_dialog_message);
+ return builder.create();
+ }
+
+ }
+
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
index 0200a4f..b129665 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
@@ -19,6 +19,7 @@
import android.app.admin.DevicePolicyManager;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
@@ -59,7 +60,9 @@
private DialogTestListItem mProfileAccountVisibleTest;
private DialogTestListItem mDeviceAdminVisibleTest;
private DialogTestListItem mWorkAppVisibleTest;
- private DialogTestListItem mCrossProfileIntentFiltersTest;
+ private DialogTestListItem mCrossProfileIntentFiltersTestFromPersonal;
+ private DialogTestListItem mCrossProfileIntentFiltersTestFromWork;
+ private DialogTestListItem mAppLinkingTest;
private DialogTestListItem mDisableNonMarketTest;
private DialogTestListItem mEnableNonMarketTest;
private DialogTestListItem mWorkNotificationBadgedTest;
@@ -78,6 +81,7 @@
private DialogTestListItem mCrossProfileAudioCaptureSupportTest;
private TestListItem mKeyguardDisabledFeaturesTest;
private DialogTestListItem mDisableNfcBeamTest;
+ private TestListItem mAuthenticationBoundKeyTest;
public ByodFlowTestActivity() {
super(R.layout.provisioning_byod,
@@ -264,20 +268,39 @@
R.string.provisioning_byod_print_settings_instruction,
new Intent(Settings.ACTION_PRINT_SETTINGS));
- Intent intent = new Intent(CrossProfileTestActivity.ACTION_CROSS_PROFILE);
+ Intent intent = new Intent(CrossProfileTestActivity.ACTION_CROSS_PROFILE_TO_WORK);
+ intent.putExtra(CrossProfileTestActivity.EXTRA_STARTED_FROM_WORK, false);
Intent chooser = Intent.createChooser(intent,
getResources().getString(R.string.provisioning_cross_profile_chooser));
- mCrossProfileIntentFiltersTest = new DialogTestListItem(this,
- R.string.provisioning_byod_cross_profile,
- "BYOD_CrossProfileIntentFiltersTest",
- R.string.provisioning_byod_cross_profile_instruction,
+ mCrossProfileIntentFiltersTestFromPersonal = new DialogTestListItem(this,
+ R.string.provisioning_byod_cross_profile_from_personal,
+ "BYOD_CrossProfileIntentFiltersTestFromPersonal",
+ R.string.provisioning_byod_cross_profile_from_personal_instruction,
chooser);
+ mCrossProfileIntentFiltersTestFromWork = new DialogTestListItem(this,
+ R.string.provisioning_byod_cross_profile_from_work,
+ "BYOD_CrossProfileIntentFiltersTestFromWork",
+ R.string.provisioning_byod_cross_profile_from_work_instruction,
+ new Intent(ByodHelperActivity.ACTION_TEST_CROSS_PROFILE_INTENTS_DIALOG));
+
+ mAppLinkingTest = new DialogTestListItem(this,
+ R.string.provisioning_app_linking,
+ "BYOD_AppLinking",
+ R.string.provisioning_byod_app_linking_instruction,
+ new Intent(ByodHelperActivity.ACTION_TEST_APP_LINKING_DIALOG));
+
mKeyguardDisabledFeaturesTest = TestListItem.newTest(this,
R.string.provisioning_byod_keyguard_disabled_features,
KeyguardDisabledFeaturesActivity.class.getName(),
new Intent(this, KeyguardDisabledFeaturesActivity.class), null);
+ mAuthenticationBoundKeyTest = TestListItem.newTest(this,
+ R.string.provisioning_byod_auth_bound_key,
+ AuthenticationBoundKeyTestActivity.class.getName(),
+ new Intent(AuthenticationBoundKeyTestActivity.ACTION_AUTH_BOUND_KEY_TEST),
+ null);
+
// Test for checking if the required intent filters are set during managed provisioning.
mIntentFiltersTest = new DialogTestListItem(this,
R.string.provisioning_byod_cross_profile_intent_filters,
@@ -287,7 +310,7 @@
checkIntentFilters();
}
};
-
+
Intent permissionCheckIntent = new Intent(
PermissionLockdownTestActivity.ACTION_MANAGED_PROFILE_CHECK_PERMISSION_LOCKDOWN);
mPermissionLockdownTest = new DialogTestListItem(this,
@@ -314,12 +337,15 @@
adapter.add(mDataUsageSettingsVisibleTest);
adapter.add(mPrintSettingsVisibleTest);
- adapter.add(mCrossProfileIntentFiltersTest);
+ adapter.add(mCrossProfileIntentFiltersTestFromPersonal);
+ adapter.add(mCrossProfileIntentFiltersTestFromWork);
+ adapter.add(mAppLinkingTest);
adapter.add(mDisableNonMarketTest);
adapter.add(mEnableNonMarketTest);
adapter.add(mIntentFiltersTest);
adapter.add(mPermissionLockdownTest);
adapter.add(mKeyguardDisabledFeaturesTest);
+ adapter.add(mAuthenticationBoundKeyTest);
if (canResolveIntent(ByodHelperActivity.getCaptureImageIntent())) {
// Capture image intent can be resolved in primary profile, so test.
@@ -495,7 +521,8 @@
ByodHelperActivity.class.getName(),
WorkNotificationTestActivity.class.getName(),
WorkStatusTestActivity.class.getName(),
- PermissionLockdownTestActivity.ACTIVITY_ALIAS
+ PermissionLockdownTestActivity.ACTIVITY_ALIAS,
+ AuthenticationBoundKeyTestActivity.class.getName()
};
for (String component : components) {
getPackageManager().setComponentEnabledSetting(new ComponentName(this, component),
@@ -503,4 +530,5 @@
PackageManager.DONT_KILL_APP);
}
}
+
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java
index 3d7d42d..9ea5061 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java
@@ -27,6 +27,7 @@
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
+import android.os.UserManager;
import android.provider.MediaStore;
import android.provider.Settings;
import android.support.v4.content.FileProvider;
@@ -84,6 +85,18 @@
public static final String ACTION_CHECK_INTENT_FILTERS =
"com.android.cts.verifier.managedprovisioning.action.CHECK_INTENT_FILTERS";
+ // Primary -> managed intent: will send a cross profile intent and check if the user sees an
+ // intent picker dialog and can open the apps.
+ public static final String ACTION_TEST_CROSS_PROFILE_INTENTS_DIALOG =
+ "com.android.cts.verifier.managedprovisioning.action.TEST_CROSS_PROFILE_INTENTS_DIALOG";
+
+ // Primary -> managed intent: will send an app link intent and check if the user sees a
+ // dialog and can open the apps. This test is extremely similar to
+ // ACTION_TEST_CROSS_PROFILE_INTENTS_DIALOG, but the intent used is a web intent, and there is
+ // some behavior which is specific to web intents.
+ public static final String ACTION_TEST_APP_LINKING_DIALOG =
+ "com.android.cts.verifier.managedprovisioning.action.TEST_APP_LINKING_DIALOG";
+
public static final int RESULT_FAILED = RESULT_FIRST_USER;
private static final int REQUEST_INSTALL_PACKAGE = 1;
@@ -211,6 +224,16 @@
startActivity(testNfcBeamIntent);
finish();
return;
+ } else if (action.equals(ACTION_TEST_CROSS_PROFILE_INTENTS_DIALOG)) {
+ sendIntentInsideChooser(new Intent(
+ CrossProfileTestActivity.ACTION_CROSS_PROFILE_TO_PERSONAL));
+ } else if (action.equals(ACTION_TEST_APP_LINKING_DIALOG)) {
+ mDevicePolicyManager.addUserRestriction(
+ DeviceAdminTestReceiver.getReceiverComponentName(),
+ UserManager.ALLOW_PARENT_PROFILE_APP_LINKING);
+ Intent toSend = new Intent(Intent.ACTION_VIEW);
+ toSend.setData(Uri.parse("http://com.android.cts.verifier"));
+ sendIntentInsideChooser(toSend);
}
// This activity has no UI and is only used to respond to CtsVerifier in the primary side.
finish();
@@ -346,6 +369,13 @@
DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED);
}
+ private void sendIntentInsideChooser(Intent toSend) {
+ toSend.putExtra(CrossProfileTestActivity.EXTRA_STARTED_FROM_WORK, true);
+ Intent chooser = Intent.createChooser(toSend,
+ getResources().getString(R.string.provisioning_cross_profile_chooser));
+ startActivity(chooser);
+ }
+
@Override
public void onDialogClose() {
finish();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodPresentMediaDialog.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodPresentMediaDialog.java
index b3f126b..15f5bc8 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodPresentMediaDialog.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodPresentMediaDialog.java
@@ -31,10 +31,14 @@
import android.widget.ImageView;
import android.widget.Toast;
import android.widget.VideoView;
-
+import android.view.Display;
+import android.graphics.BitmapFactory;
+import android.graphics.Point;
+import android.content.ContentResolver;
import com.android.cts.verifier.R;
import java.io.IOException;
+import java.io.InputStream;
/**
* This dialog shows/plays an image, video or audio uri.
@@ -46,6 +50,7 @@
private static final String KEY_IMAGE_URI = "image";
private static final String KEY_AUDIO_URI = "audio";
+ private Bitmap scaled = null;
/**
* Get a dialogFragment showing an image.
*/
@@ -79,6 +84,16 @@
return dialog;
}
+ private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight){
+ // Raw height and width of image
+ final int height = options.outHeight;
+ final int width = options.outWidth;
+ if(reqWidth <= 0 || reqHeight <= 0) {
+ return 1;
+ }
+ return Math.max(height/reqHeight, width/reqWidth) + 1;
+ }
+
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Dialog dialog = new Dialog(getActivity());
@@ -114,11 +129,36 @@
} else if (arguments.containsKey(KEY_IMAGE_URI)) {
// Show image UI.
dialog.setTitle(getString(R.string.provisioning_byod_verify_image_title));
-
- Uri uri = (Uri) getArguments().getParcelable(KEY_IMAGE_URI);
+ Uri uri = (Uri)getArguments().getParcelable(KEY_IMAGE_URI);
ImageView imageView = (ImageView) dialog.findViewById(R.id.imageView);
imageView.setVisibility(View.VISIBLE);
- imageView.setImageURI(uri);
+
+ try{
+ InputStream input = getActivity().getContentResolver().openInputStream(uri);
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ BitmapFactory.decodeStream(input, null, options);
+ //scale the picture
+ Display display = getActivity().getWindowManager().getDefaultDisplay();
+ Point size = new Point();
+ display.getSize(size);
+ int reqWidth = size.x;
+ int reqHeight = size.y;
+ options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
+
+ options.inJustDecodeBounds = false;
+ input.close();
+ input = getActivity().getContentResolver().openInputStream(uri);
+ scaled = BitmapFactory.decodeStream(input, null, options);
+ input.close();
+ imageView.setImageBitmap(scaled);
+ }catch(IOException e){
+ Log.e(TAG, "Cannot get image.", e);
+ Toast.makeText(getActivity(),R.string.provisioning_byod_capture_image_error,
+ Toast.LENGTH_SHORT).show();
+ getActivity().finish();
+ }
+
} else if (arguments.containsKey(KEY_AUDIO_URI)) {
// Show audio playback UI.
dialog.setTitle(getString(R.string.provisioning_byod_verify_audio_title));
@@ -161,6 +201,13 @@
((DialogCallback) getActivity()).onDialogClose();
}
+ @Override
+ public void onDestroyView() {
+ if(scaled!=null){
+ scaled.recycle();
+ }
+ super.onDestroyView();
+ }
public interface DialogCallback {
public abstract void onDialogClose();
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CrossProfileTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CrossProfileTestActivity.java
index 6c38e12..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..ed6b5eb 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
@@ -64,7 +64,9 @@
filter.addAction(ByodHelperActivity.ACTION_KEYGUARD_DISABLED_FEATURES);
filter.addAction(ByodHelperActivity.ACTION_LOCKNOW);
filter.addAction(ByodHelperActivity.ACTION_TEST_NFC_BEAM);
- filter.addAction(CrossProfileTestActivity.ACTION_CROSS_PROFILE);
+ filter.addAction(ByodHelperActivity.ACTION_TEST_CROSS_PROFILE_INTENTS_DIALOG);
+ filter.addAction(ByodHelperActivity.ACTION_TEST_APP_LINKING_DIALOG);
+ filter.addAction(CrossProfileTestActivity.ACTION_CROSS_PROFILE_TO_WORK);
filter.addAction(WorkNotificationTestActivity.ACTION_WORK_NOTIFICATION);
filter.addAction(WorkNotificationTestActivity.ACTION_WORK_NOTIFICATION_ON_LOCKSCREEN);
filter.addAction(WorkNotificationTestActivity.ACTION_CLEAR_WORK_NOTIFICATION);
@@ -72,12 +74,14 @@
filter.addAction(WorkStatusTestActivity.ACTION_WORK_STATUS_ICON);
filter.addAction(
PermissionLockdownTestActivity.ACTION_MANAGED_PROFILE_CHECK_PERMISSION_LOCKDOWN);
+ filter.addAction(AuthenticationBoundKeyTestActivity.ACTION_AUTH_BOUND_KEY_TEST);
dpm.addCrossProfileIntentFilter(getWho(context), filter,
DevicePolicyManager.FLAG_MANAGED_CAN_ACCESS_PARENT);
// Work -> primary direction
filter = new IntentFilter();
filter.addAction(ByodHelperActivity.ACTION_PROFILE_OWNER_STATUS);
+ filter.addAction(CrossProfileTestActivity.ACTION_CROSS_PROFILE_TO_PERSONAL);
dpm.addCrossProfileIntentFilter(getWho(context), filter,
DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/IntentFiltersTestHelper.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/IntentFiltersTestHelper.java
index 7dc99c0..eef6299 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/IntentFiltersTestHelper.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/IntentFiltersTestHelper.java
@@ -30,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;
@@ -39,6 +40,7 @@
import android.util.Log;
import android.widget.Toast;
+import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
@@ -51,132 +53,83 @@
private static final String TAG = "IntentFiltersTestHelper";
// These are the intents which can be forwarded to the managed profile.
- private static final Intent[] forwardedIntentsFromPrimary = new Intent[] {
- new Intent(Intent.ACTION_SEND).setType("*/*"),
- new Intent(Intent.ACTION_SEND_MULTIPLE).setType("*/*")
- };
+ private static final ArrayList<Intent> forwardedIntentsFromPrimary =
+ new ArrayList<>(Arrays.asList(
+ new Intent(Intent.ACTION_SEND).setType("*/*"),
+ new Intent(Intent.ACTION_SEND_MULTIPLE).setType("*/*")
+ ));
// These are the intents which can be forwarded to the primary profile.
- private static final Intent[] forwardedIntentsFromManaged = new Intent[] {
- new Intent(AlarmClock.ACTION_SET_ALARM),
- new Intent(AlarmClock.ACTION_SET_TIMER),
- new Intent(AlarmClock.ACTION_SHOW_ALARMS),
- new Intent(MediaStore.ACTION_IMAGE_CAPTURE),
- new Intent(MediaStore.ACTION_VIDEO_CAPTURE),
- new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA),
- new Intent(MediaStore.INTENT_ACTION_VIDEO_CAMERA),
- new Intent(MediaStore.ACTION_IMAGE_CAPTURE_SECURE),
- new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE),
- new Intent(Intent.ACTION_SENDTO).setData(Uri.parse("sms:07700900100")),
- new Intent(Intent.ACTION_SENDTO).setData(Uri.parse("smsto:07700900100")),
- new Intent(Intent.ACTION_SENDTO).setData(Uri.parse("mms:07700900100")),
- new Intent(Intent.ACTION_SENDTO).setData(Uri.parse("mmsto:07700900100")),
- new Intent(Intent.ACTION_VIEW).setData(
- Uri.parse("sms:07700900100?body=Hello%20world")).addCategory(
- Intent.CATEGORY_BROWSABLE),
- new Intent(Intent.ACTION_VIEW).setData(
- Uri.parse("smsto:07700900100?body=Hello%20world")).addCategory(
- Intent.CATEGORY_BROWSABLE),
- new Intent(Intent.ACTION_VIEW).setData(
- Uri.parse("mms:07700900100?body=Hello%20world")).addCategory(
- Intent.CATEGORY_BROWSABLE),
- new Intent(Intent.ACTION_VIEW).setData(
- Uri.parse("mmsto:07700900100?body=Hello%20world")).addCategory(
- Intent.CATEGORY_BROWSABLE),
- new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS),
- new Intent(Settings.ACTION_AIRPLANE_MODE_SETTINGS),
- new Intent(Settings.ACTION_APN_SETTINGS),
- new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS),
- new Intent(Settings.ACTION_CAPTIONING_SETTINGS),
- new Intent(Settings.ACTION_DATA_ROAMING_SETTINGS),
- new Intent(Settings.ACTION_DATE_SETTINGS),
- new Intent(Settings.ACTION_DEVICE_INFO_SETTINGS),
- new Intent(Settings.ACTION_DISPLAY_SETTINGS),
- new Intent(Settings.ACTION_DREAM_SETTINGS),
- new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS),
- new Intent(Settings.ACTION_INPUT_METHOD_SUBTYPE_SETTINGS),
- new Intent(Settings.ACTION_LOCALE_SETTINGS),
- new Intent(Settings.ACTION_NETWORK_OPERATOR_SETTINGS),
- new Intent(Settings.ACTION_NFC_SETTINGS),
- new Intent(Settings.ACTION_NFCSHARING_SETTINGS),
- new Intent(Settings.ACTION_PRIVACY_SETTINGS),
- new Intent(Settings.ACTION_SETTINGS),
- new Intent(Settings.ACTION_SOUND_SETTINGS),
- new Intent(Settings.ACTION_WIRELESS_SETTINGS),
- new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD),
- new Intent("android.net.vpn.SETTINGS"),
- new Intent(CardEmulation.ACTION_CHANGE_DEFAULT),
- new Intent("android.settings.ACCOUNT_SYNC_SETTINGS"),
- new Intent(Settings.ACTION_BATTERY_SAVER_SETTINGS),
- new Intent(Settings.ACTION_HOME_SETTINGS),
- new Intent("android.settings.LICENSE"),
- new Intent("android.settings.NOTIFICATION_SETTINGS"),
- new Intent(Settings.ACTION_SHOW_REGULATORY_INFO),
- new Intent("android.settings.USER_SETTINGS"),
- new Intent("android.settings.ZEN_MODE_SETTINGS"),
- new Intent("com.android.settings.ACCESSIBILITY_COLOR_SPACE_SETTINGS"),
- new Intent("com.android.settings.STORAGE_USB_SETTINGS"),
- new Intent("com.android.settings.TTS_SETTINGS"),
- new Intent("com.android.settings.USER_DICTIONARY_EDIT"),
- new Intent(Intent.ACTION_CALL).setData(Uri.parse("tel:123")),
- new Intent("android.intent.action.CALL_EMERGENCY").setData(Uri.parse("tel:123")),
- new Intent("android.intent.action.CALL_PRIVILEGED").setData(Uri.parse("tel:123")),
- new Intent(Intent.ACTION_DIAL).setData(Uri.parse("tel:123")),
- new Intent(Intent.ACTION_VIEW).setData(Uri.parse("tel:123")).addCategory(
- Intent.CATEGORY_BROWSABLE),
- new Intent(Settings.ACTION_MEMORY_CARD_SETTINGS),
- new Intent(Settings.ACTION_NFC_PAYMENT_SETTINGS),
- new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS),
- new Intent(Settings.ACTION_INTERNAL_STORAGE_SETTINGS),
- new Intent(Settings.ACTION_SYNC_SETTINGS),
- new Intent(Settings.ACTION_ADD_ACCOUNT),
- new Intent(Intent.ACTION_GET_CONTENT).setType("*/*").addCategory(
- Intent.CATEGORY_OPENABLE),
- new Intent(Intent.ACTION_OPEN_DOCUMENT).setType("*/*").addCategory(
- Intent.CATEGORY_OPENABLE),
- new Intent(Settings.ACTION_MANAGE_APPLICATIONS_SETTINGS),
- new Intent(Settings.ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS),
- new Intent(Settings.ACTION_APPLICATION_SETTINGS),
- new Intent("android.settings.ACTION_OTHER_SOUND_SETTINGS"),
- new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION),
- new Intent(Settings.ACTION_WIFI_IP_SETTINGS),
- new Intent(Settings.ACTION_WIFI_SETTINGS),
- new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)
- };
+ private static final ArrayList<Intent> forwardedIntentsFromManaged =
+ new ArrayList<>(Arrays.asList(
+ new Intent(AlarmClock.ACTION_SET_ALARM),
+ new Intent(AlarmClock.ACTION_SET_TIMER),
+ new Intent(AlarmClock.ACTION_SHOW_ALARMS),
+ new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS),
+ new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS),
+ new Intent(Settings.ACTION_CAPTIONING_SETTINGS),
+ new Intent(Settings.ACTION_DATE_SETTINGS),
+ new Intent(Settings.ACTION_DEVICE_INFO_SETTINGS),
+ new Intent(Settings.ACTION_DISPLAY_SETTINGS),
+ new Intent(Settings.ACTION_LOCALE_SETTINGS),
+ new Intent(Settings.ACTION_PRIVACY_SETTINGS),
+ new Intent(Settings.ACTION_SETTINGS),
+ new Intent(Settings.ACTION_WIRELESS_SETTINGS),
+ new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD),
+ new Intent("android.net.vpn.SETTINGS"),
+ new Intent("android.settings.ACCOUNT_SYNC_SETTINGS"),
+ new Intent(Settings.ACTION_BATTERY_SAVER_SETTINGS),
+ new Intent("android.settings.LICENSE"),
+ new Intent("android.settings.NOTIFICATION_SETTINGS"),
+ new Intent("android.settings.USER_SETTINGS"),
+ new Intent("android.settings.ZEN_MODE_SETTINGS"),
+ new Intent("com.android.settings.ACCESSIBILITY_COLOR_SPACE_SETTINGS"),
+ new Intent("com.android.settings.STORAGE_USB_SETTINGS"),
+ new Intent("com.android.settings.TTS_SETTINGS"),
+ new Intent("com.android.settings.USER_DICTIONARY_EDIT"),
+ new Intent(Settings.ACTION_INTERNAL_STORAGE_SETTINGS),
+ new Intent(Settings.ACTION_SYNC_SETTINGS),
+ new Intent(Settings.ACTION_ADD_ACCOUNT),
+ new Intent(Intent.ACTION_GET_CONTENT).setType("*/*").addCategory(
+ Intent.CATEGORY_OPENABLE),
+ new Intent(Intent.ACTION_OPEN_DOCUMENT).setType("*/*").addCategory(
+ Intent.CATEGORY_OPENABLE),
+ new Intent(Settings.ACTION_MANAGE_APPLICATIONS_SETTINGS),
+ new Intent(Settings.ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS),
+ new Intent(Settings.ACTION_APPLICATION_SETTINGS)
+ ));
// These are the intents which cannot be forwarded to the primary profile.
- private static final Intent[] notForwardedIntentsFromManaged = new Intent[] {
- new Intent(Intent.ACTION_INSERT).setData(
- Uri.parse("content://browser/bookmarks")),
- new Intent(Intent.ACTION_VIEW).setData(
- Uri.parse("http://www.example.com")).addCategory(
- Intent.CATEGORY_BROWSABLE),
- new Intent(Intent.ACTION_SENDTO).setData(
- Uri.parse("mailto:user@example.com")),
- new Intent(Intent.ACTION_VIEW).setData(
- Uri.parse("mailto:user@example.com")).addCategory(
- Intent.CATEGORY_BROWSABLE),
- new Intent(Intent.ACTION_VIEW).setData(
- Uri.parse("geo:0,0?q=BuckinghamPalace")),
- new Intent(Intent.ACTION_VIEW).setData(
- Uri.parse("http://example.com/oceans.mp4")).setType("video/mp4"),
- new Intent(Intent.ACTION_VIEW).setData(
- Uri.parse("http://www.example.com/horse.mp3")).setType("audio/*"),
- new Intent(MediaStore.INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH),
- new Intent(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_APPLICATION_DETAILS_SETTINGS).setData(
- Uri.parse("package:com.android.chrome")),
- 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),
- new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS)
- };
+ 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;
@@ -187,6 +140,124 @@
IntentFiltersTestHelper(Context context) {
mContext = context;
+
+ addIntentsThatDependOnDeviceFeatures();
+ }
+
+ private void addIntentsThatDependOnDeviceFeatures() {
+ PackageManager pm = mContext.getPackageManager();
+
+ if (pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ forwardedIntentsFromManaged.addAll(Arrays.asList(
+ new Intent(Intent.ACTION_DIAL).setData(Uri.parse("tel:123")),
+ new Intent(Intent.ACTION_CALL).setData(Uri.parse("tel:123")),
+ new Intent("android.intent.action.CALL_EMERGENCY").setData(
+ Uri.parse("tel:123")),
+ new Intent("android.intent.action.CALL_PRIVILEGED").setData(
+ Uri.parse("tel:123")),
+ new Intent(Intent.ACTION_VIEW).setData(Uri.parse("tel:123")).addCategory(
+ Intent.CATEGORY_BROWSABLE),
+ new Intent(Settings.ACTION_NETWORK_OPERATOR_SETTINGS),
+ new Intent(Settings.ACTION_DATA_ROAMING_SETTINGS),
+ new Intent(Intent.ACTION_SENDTO).setData(Uri.parse("sms:07700900100")),
+ new Intent(Intent.ACTION_SENDTO).setData(Uri.parse("smsto:07700900100")),
+ new Intent(Intent.ACTION_SENDTO).setData(Uri.parse("mms:07700900100")),
+ new Intent(Intent.ACTION_SENDTO).setData(Uri.parse("mmsto:07700900100")),
+ new Intent(Intent.ACTION_VIEW).setData(
+ Uri.parse("sms:07700900100?body=Hello%20world")).addCategory(
+ Intent.CATEGORY_BROWSABLE),
+ new Intent(Intent.ACTION_VIEW).setData(
+ Uri.parse("smsto:07700900100?body=Hello%20world")).addCategory(
+ Intent.CATEGORY_BROWSABLE),
+ new Intent(Intent.ACTION_VIEW).setData(
+ Uri.parse("mms:07700900100?body=Hello%20world")).addCategory(
+ Intent.CATEGORY_BROWSABLE),
+ new Intent(Intent.ACTION_VIEW).setData(
+ Uri.parse("mmsto:07700900100?body=Hello%20world")).addCategory(
+ Intent.CATEGORY_BROWSABLE),
+ new Intent(Settings.ACTION_APN_SETTINGS)));
+ }
+
+ if (pm.hasSystemFeature(PackageManager.FEATURE_NFC)) {
+ forwardedIntentsFromManaged.addAll(Arrays.asList(
+ new Intent(Settings.ACTION_NFC_SETTINGS),
+ new Intent(Settings.ACTION_NFCSHARING_SETTINGS),
+ new Intent(Settings.ACTION_NFC_PAYMENT_SETTINGS)));
+ }
+
+ if (pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
+ forwardedIntentsFromManaged.add(
+ new Intent(CardEmulation.ACTION_CHANGE_DEFAULT));
+ }
+
+ if (pm.hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
+ forwardedIntentsFromManaged.addAll(Arrays.asList(
+ new Intent(MediaStore.ACTION_IMAGE_CAPTURE),
+ new Intent(MediaStore.ACTION_VIDEO_CAPTURE),
+ new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA),
+ new Intent(MediaStore.INTENT_ACTION_VIDEO_CAMERA),
+ new Intent(MediaStore.ACTION_IMAGE_CAPTURE_SECURE),
+ new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE)));
+ }
+
+ final String state = Environment.getExternalStorageState();
+ if (Environment.MEDIA_MOUNTED.equals(state)) {
+ forwardedIntentsFromManaged.add(
+ new Intent(Settings.ACTION_MEMORY_CARD_SETTINGS));
+ }
+
+ if (pm.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
+ forwardedIntentsFromManaged.addAll(Arrays.asList(
+ new Intent(Settings.ACTION_WIFI_IP_SETTINGS),
+ new Intent(Settings.ACTION_WIFI_SETTINGS)));
+ }
+
+ if (pm.hasSystemFeature(PackageManager.FEATURE_MICROPHONE)) {
+ forwardedIntentsFromManaged.addAll(Arrays.asList(
+ new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION),
+ new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)));
+ }
+
+ if (pm.hasSystemFeature(PackageManager.FEATURE_LOCATION)) {
+ forwardedIntentsFromManaged.add(
+ new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS));
+ }
+
+ if (pm.hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)) {
+ forwardedIntentsFromManaged.addAll(Arrays.asList(
+ new Intent(Settings.ACTION_SOUND_SETTINGS),
+ new Intent("android.settings.ACTION_OTHER_SOUND_SETTINGS")));
+ notForwardedIntentsFromManaged.add(
+ new Intent(AudioEffect.ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL));
+ }
+
+ if (pm.hasSystemFeature(PackageManager.FEATURE_HOME_SCREEN)) {
+ forwardedIntentsFromManaged.add(
+ new Intent(Settings.ACTION_HOME_SETTINGS));
+ }
+
+ if (pm.hasSystemFeature(PackageManager.FEATURE_INPUT_METHODS)) {
+ forwardedIntentsFromManaged.addAll(Arrays.asList(
+ new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS),
+ new Intent(Settings.ACTION_INPUT_METHOD_SUBTYPE_SETTINGS)));
+ notForwardedIntentsFromManaged.add(
+ new Intent("android.settings.SHOW_INPUT_METHOD_PICKER"));
+ }
+
+ if (!pm.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+ forwardedIntentsFromManaged.add(
+ new Intent(Settings.ACTION_DREAM_SETTINGS));
+ }
+
+ if (!pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
+ forwardedIntentsFromManaged.add(
+ new Intent(Settings.ACTION_AIRPLANE_MODE_SETTINGS));
+ }
+
+ if (pm.hasSystemFeature(PackageManager.FEATURE_PRINTING)) {
+ notForwardedIntentsFromManaged.add(
+ new Intent(Settings.ACTION_PRINT_SETTINGS));
+ }
}
public boolean checkCrossProfileIntentFilters(int flag) {
@@ -298,7 +369,7 @@
* @return {@code null} if all the intents are correctly handled
* otherwise, the first intent in the list which is not handled correctly.
*/
- private Intent checkForIntentsNotHandled(Intent[] intentList,
+ private Intent checkForIntentsNotHandled(ArrayList<Intent> intentList,
ActivityInfo expectedForwarderActivityInfo, boolean canResolve) {
for (Intent intent : intentList) {
if (canForwarderActivityHandleIntent(intent,
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/screenpinning/ScreenPinningTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/screenpinning/ScreenPinningTestActivity.java
index 8e72ebb..0728fb5 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/screenpinning/ScreenPinningTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/screenpinning/ScreenPinningTestActivity.java
@@ -31,6 +31,8 @@
private static final String TAG = "ScreenPinningTestActivity";
private static final String KEY_CURRENT_TEST = "keyCurrentTest";
+ private static final long TASK_MODE_CHECK_DELAY = 200;
+ private static final int MAX_TASK_MODE_CHECK_COUNT = 5;
private Test[] mTests;
private int mTestIndex;
@@ -203,10 +205,18 @@
return;
}
stopLockTask();
- if (!mActivityManager.isInLockTaskMode()) {
- succeed();
- } else {
- error(R.string.error_screen_pinning_couldnt_exit);
+ for (int retry = MAX_TASK_MODE_CHECK_COUNT; retry > 0; retry--) {
+ try {
+ Thread.sleep(TASK_MODE_CHECK_DELAY);
+ } catch (InterruptedException e) {
+ }
+ Log.d(TAG, "Check unpin ... " + retry);
+ if (!mActivityManager.isInLockTaskMode()) {
+ succeed();
+ break;
+ } else if (retry == 1) {
+ error(R.string.error_screen_pinning_couldnt_exit);
+ }
}
};
};
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/security/FingerprintBoundKeysTest.java b/apps/CtsVerifier/src/com/android/cts/verifier/security/FingerprintBoundKeysTest.java
index 70899c6..bca7a66 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/security/FingerprintBoundKeysTest.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/security/FingerprintBoundKeysTest.java
@@ -89,6 +89,22 @@
if (requestCode == FINGERPRINT_PERMISSION_REQUEST_CODE && state[0] == PackageManager.PERMISSION_GRANTED) {
mFingerprintManager = (FingerprintManager) getSystemService(Context.FINGERPRINT_SERVICE);
mKeyguardManager = (KeyguardManager) getSystemService(KeyguardManager.class);
+ Button startTestButton = (Button) findViewById(R.id.sec_start_test_button);
+
+ if (!mKeyguardManager.isKeyguardSecure()) {
+ // Show a message that the user hasn't set up a lock screen.
+ showToast( "Secure lock screen hasn't been set up.\n"
+ + "Go to 'Settings -> Security -> Screen lock' to set up a lock screen");
+ startTestButton.setEnabled(false);
+ return;
+ } else if (!mFingerprintManager.hasEnrolledFingerprints()) {
+ showToast("No fingerprints enrolled.\n"
+ + "Go to 'Settings -> Security -> Fingerprint' to set up a fingerprint");
+ startTestButton.setEnabled(false);
+ return;
+ }
+
+ createKey();
try {
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
@@ -107,7 +123,6 @@
throw new RuntimeException("Failed to init Cipher", e);
}
- Button startTestButton = (Button) findViewById(R.id.sec_start_test_button);
startTestButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
@@ -117,21 +132,7 @@
showAuthenticationScreen();
}
}
-
});
-
- if (!mKeyguardManager.isKeyguardSecure()) {
- // Show a message that the user hasn't set up a lock screen.
- showToast( "Secure lock screen hasn't been set up.\n"
- + "Go to 'Settings -> Security -> Screen lock' to set up a lock screen");
- startTestButton.setEnabled(false);
- } else if (!mFingerprintManager.hasEnrolledFingerprints()) {
- showToast("No fingerprints enrolled.\n"
- + "Go to 'Settings -> Security -> Fingerprint' to set up a fingerprint");
- startTestButton.setEnabled(false);
- } else {
- createKey();
- }
}
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVCameraPreview.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVCameraPreview.java
index a5b58f6..fa89b71 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVCameraPreview.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVCameraPreview.java
@@ -23,9 +23,9 @@
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
+import android.view.ViewGroup;
import java.io.IOException;
-import java.util.List;
/** Camera preview class */
public class RVCVCameraPreview extends SurfaceView implements SurfaceHolder.Callback {
@@ -34,15 +34,16 @@
private SurfaceHolder mHolder;
private Camera mCamera;
+ private float mAspect;
+ private int mRotation;
/**
* Constructor
* @param context Activity context
- * @param camera Camera object to be previewed
*/
- public RVCVCameraPreview(Context context, Camera camera) {
+ public RVCVCameraPreview(Context context) {
super(context);
- mCamera = camera;
+ mCamera = null;
initSurface();
}
@@ -55,8 +56,10 @@
super(context, attrs);
}
- public void init(Camera camera) {
+ public void init(Camera camera, float aspectRatio, int rotation) {
this.mCamera = camera;
+ mAspect = aspectRatio;
+ mRotation = rotation;
initSurface();
}
@@ -86,6 +89,20 @@
try {
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
+ int v_height = getHeight();
+ int v_width = getWidth();
+ ViewGroup.LayoutParams layout = getLayoutParams();
+ if ( (float)v_height/v_width >
+ mAspect) {
+ layout.height = (int)(v_width * mAspect);
+ layout.width = v_width;
+ }else {
+ layout.width = (int)(v_height / mAspect);
+ layout.height = v_height;
+ }
+ Log.d(TAG, String.format("Layout (%d, %d) -> (%d, %d)", v_width, v_height,
+ layout.width, layout.height));
+ setLayoutParams(layout);
} catch (IOException e) {
if (LOCAL_LOGD) Log.d(TAG, "Error when starting camera preview: " + e.getMessage());
}
@@ -111,8 +128,7 @@
// stop preview before making changes
mCamera.stopPreview();
- // the activity using this view is locked to this orientation, so hard code is fine
- mCamera.setDisplayOrientation(90);
+ mCamera.setDisplayOrientation(mRotation);
//do the same as if it is created again
surfaceCreated(holder);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVRecordActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVRecordActivity.java
index f90b27c..be5ec52 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVRecordActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVRecordActivity.java
@@ -46,6 +46,7 @@
import java.io.OutputStreamWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
+import java.util.List;
// ----------------------------------------------------------------------
@@ -67,7 +68,7 @@
private VideoRecorder mVideoRecorder;
private RVSensorLogger mRVSensorLogger;
private CoverageManager mCoverManager;
- private CameraPreviewer mPreviewer;
+ private CameraContext mCameraContext;
public static final int AXIS_NONE = 0;
public static final int AXIS_ALL = SensorManager.AXIS_X +
@@ -99,7 +100,7 @@
super.onPause();
mController.quit();
- mPreviewer.end();
+ mCameraContext.end();
endSoundPool();
}
@@ -128,8 +129,8 @@
*
*/
private void init() {
- mPreviewer = new CameraPreviewer();
- mPreviewer.init();
+ mCameraContext = new CameraContext();
+ mCameraContext.init();
mCoverManager = new CoverageManager();
mIndicatorView.setDataProvider(
@@ -140,7 +141,7 @@
initSoundPool();
mRVSensorLogger = new RVSensorLogger(this);
- mVideoRecorder = new VideoRecorder(mPreviewer.getCamera());
+ mVideoRecorder = new VideoRecorder(mCameraContext.getCamera(), mCameraContext.getProfile());
if (LOG_RAW_SENSORS) {
mRawSensorLogger = new RawSensorLogger(mRecordDir);
@@ -173,7 +174,8 @@
// X and Y
final String axisName = "YXZ";
- message("Manipulate the device in " + axisName.charAt(axis-1) + " axis (as illustrated) about the pattern.");
+ message("Manipulate the device in " + axisName.charAt(axis - 1) +
+ " axis (as illustrated) about the pattern.");
}
/**
@@ -250,20 +252,28 @@
* Start the sensor recording
*/
public void startRecordSensor() {
- mRVSensorLogger.init();
- if (LOG_RAW_SENSORS) {
- mRawSensorLogger.init();
- }
+ runOnUiThread(new Runnable() {
+ public void run() {
+ mRVSensorLogger.init();
+ if (LOG_RAW_SENSORS) {
+ mRawSensorLogger.init();
+ }
+ }
+ });
}
/**
* Stop the sensor recording
*/
public void stopRecordSensor() {
- mRVSensorLogger.end();
- if (LOG_RAW_SENSORS) {
- mRawSensorLogger.end();
- }
+ runOnUiThread(new Runnable() {
+ public void run() {
+ mRVSensorLogger.end();
+ if (LOG_RAW_SENSORS) {
+ mRawSensorLogger.end();
+ }
+ }
+ });
}
/**
@@ -365,20 +375,93 @@
/**
* Camera preview control class
*/
- class CameraPreviewer {
+ class CameraContext {
private Camera mCamera;
+ private CamcorderProfile mProfile;
+ private Camera.CameraInfo mCameraInfo;
- CameraPreviewer() {
+ private int [] mPreferredProfiles = {
+ CamcorderProfile.QUALITY_480P, // smaller -> faster
+ CamcorderProfile.QUALITY_720P,
+ CamcorderProfile.QUALITY_1080P,
+ CamcorderProfile.QUALITY_HIGH // existence guaranteed
+ };
+
+ CameraContext() {
try {
- mCamera = Camera.open(); // attempt to get a default Camera instance
+ mCamera = Camera.open(); // attempt to get a default Camera instance (0)
+ mProfile = null;
+ if (mCamera != null) {
+ mCameraInfo = new Camera.CameraInfo();
+ Camera.getCameraInfo(0, mCameraInfo);
+ setupCamera();
+ }
}
- catch (Exception e) {
+ catch (Exception e){
// Camera is not available (in use or does not exist)
Log.e(TAG, "Cannot obtain Camera!");
}
}
/**
+ * Find a preferred camera profile and set preview and picture size property accordingly.
+ */
+ void setupCamera() {
+ CamcorderProfile profile = null;
+ Camera.Parameters param = mCamera.getParameters();
+ List<Camera.Size> pre_sz = param.getSupportedPreviewSizes();
+ List<Camera.Size> pic_sz = param.getSupportedPictureSizes();
+
+ for (int i : mPreferredProfiles) {
+ if (CamcorderProfile.hasProfile(i)) {
+ profile = CamcorderProfile.get(i);
+
+ int valid = 0;
+ for (Camera.Size j : pre_sz) {
+ if (j.width == profile.videoFrameWidth &&
+ j.height == profile.videoFrameHeight) {
+ ++valid;
+ break;
+ }
+ }
+ for (Camera.Size j : pic_sz) {
+ if (j.width == profile.videoFrameWidth &&
+ j.height == profile.videoFrameHeight) {
+ ++valid;
+ break;
+ }
+ }
+ if (valid == 2) {
+ param.setPreviewSize(profile.videoFrameWidth, profile.videoFrameHeight);
+ param.setPictureSize(profile.videoFrameWidth, profile.videoFrameHeight);
+ mCamera.setParameters(param);
+ break;
+ } else {
+ profile = null;
+ }
+ }
+ }
+ if (profile != null) {
+ float fovW = param.getHorizontalViewAngle();
+ float fovH = param.getVerticalViewAngle();
+ writeVideoMetaInfo(profile.videoFrameWidth, profile.videoFrameHeight,
+ profile.videoFrameRate, fovW, fovH);
+ } else {
+ Log.e(TAG, "Cannot find a proper video profile");
+ }
+ mProfile = profile;
+
+ }
+
+
+ /**
+ * Get sensor information of the camera being used
+ */
+ public Camera.CameraInfo getCameraInfo() {
+ return mCameraInfo;
+ }
+
+ /**
* Get the camera to be previewed
* @return Reference to Camera used
*/
@@ -387,12 +470,20 @@
}
/**
+ * Get the camera profile to be used
+ * @return Reference to Camera profile
+ */
+ public CamcorderProfile getProfile() {
+ return mProfile;
+ }
+
+ /**
* Setup the camera
*/
public void init() {
if (mCamera != null) {
double alpha = mCamera.getParameters().getHorizontalViewAngle()*Math.PI/180.0;
- int width = 1920;
+ int width = mProfile.videoFrameWidth;
double fx = width/2/Math.tan(alpha/2.0);
if (LOCAL_LOGV) Log.v(TAG, "View angle="
@@ -400,7 +491,9 @@
RVCVCameraPreview cameraPreview =
(RVCVCameraPreview) findViewById(R.id.cam_preview);
- cameraPreview.init(mCamera);
+ cameraPreview.init(mCamera,
+ (float)mProfile.videoFrameWidth/mProfile.videoFrameHeight,
+ mCameraInfo.orientation);
} else {
message("Cannot open camera!");
finish();
@@ -466,26 +559,22 @@
class VideoRecorder
{
private MediaRecorder mRecorder;
+ private CamcorderProfile mProfile;
private Camera mCamera;
private boolean mRunning = false;
- private int [] mPreferredProfiles = { CamcorderProfile.QUALITY_480P, // smaller -> faster
- CamcorderProfile.QUALITY_720P,
- CamcorderProfile.QUALITY_1080P,
- CamcorderProfile.QUALITY_HIGH // existence guaranteed
- };
-
-
- VideoRecorder(Camera camera) {
+ VideoRecorder(Camera camera, CamcorderProfile profile){
mCamera = camera;
+ mProfile = profile;
}
/**
* Initialize and start recording
*/
public void init() {
- float fovW = mCamera.getParameters().getHorizontalViewAngle();
- float fovH = mCamera.getParameters().getVerticalViewAngle();
+ if (mCamera == null || mProfile ==null){
+ return;
+ }
mRecorder = new MediaRecorder();
mCamera.unlock();
@@ -494,17 +583,7 @@
mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
mRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
- CamcorderProfile profile = null;
- for (int i: mPreferredProfiles) {
- if (CamcorderProfile.hasProfile(i)) {
- profile = CamcorderProfile.get(i);
- mRecorder.setProfile(profile);
- break;
- }
- }
-
- writeVideoMetaInfo(profile.videoFrameWidth, profile.videoFrameHeight,
- profile.videoFrameRate, fovW, fovH);
+ mRecorder.setProfile(mProfile);
try {
mRecorder.setOutputFile(getVideoRecFilePath());
@@ -689,8 +768,20 @@
*/
public void init() {
mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
+ if (mSensorManager == null) {
+ Log.e(TAG,"SensorManager is null!");
+ }
mRVSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);
- mSensorManager.registerListener(this, mRVSensor, SENSOR_RATE);
+ if (mRVSensor != null) {
+ if (LOCAL_LOGV) Log.v(TAG, "Got RV Sensor");
+ }else {
+ Log.e(TAG, "Did not get RV sensor");
+ }
+ if(mSensorManager.registerListener(this, mRVSensor, SENSOR_RATE)) {
+ if (LOCAL_LOGV) Log.v(TAG,"Register listener successfull");
+ } else {
+ Log.e(TAG,"Register listener failed");
+ }
try {
mLogWriter= new OutputStreamWriter(
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckAnalyzer.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckAnalyzer.java
index 9afd1a9..3dc7270 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckAnalyzer.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckAnalyzer.java
@@ -15,12 +15,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
import android.media.MediaCodec;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.os.Debug;
import android.os.Environment;
+import android.os.PowerManager;
import android.util.JsonWriter;
import android.util.Log;
@@ -777,14 +777,16 @@
VideoMetaInfo meta = new VideoMetaInfo(new File(mPath, "videometa.json"));
int decimation = 1;
+ boolean use_timestamp = true;
+ // roughly determine if decimation is necessary
if (meta.fps > DECIMATION_FPS_TARGET) {
decimation = (int)(meta.fps / DECIMATION_FPS_TARGET);
meta.fps /=decimation;
}
VideoDecoderForOpenCV videoDecoder = new VideoDecoderForOpenCV(
- new File(mPath, "video.mp4"), decimation); // every 3 frame process 1 frame
+ new File(mPath, "video.mp4"), decimation);
Mat frame;
@@ -820,12 +822,17 @@
}
long startTime = System.nanoTime();
+ long [] ts = new long[1];
- while ((frame = videoDecoder.getFrame()) !=null) {
+ while ((frame = videoDecoder.getFrame(ts)) !=null) {
if (LOCAL_LOGV) {
Log.v(TAG, "got a frame " + i);
}
+ if (use_timestamp && ts[0] == -1) {
+ use_timestamp = false;
+ }
+
// has to be in front, as there are cases where execution
// will skip the later part of this while
i++;
@@ -873,8 +880,16 @@
// if error is reasonable, add it into the results
if (error < REPROJECTION_THREASHOLD) {
double [] rv = new double[3];
+ double timestamp;
+
rvec.get(0,0, rv);
- recs.add(new AttitudeRec((double) i / meta.fps, rodr2rpy(rv)));
+ if (use_timestamp) {
+ timestamp = (double)ts[0] / 1e6;
+ } else {
+ timestamp = (double) i / meta.fps;
+ }
+ if (LOCAL_LOGV) Log.v(TAG, String.format("Added frame %d ts = %f", i, timestamp));
+ recs.add(new AttitudeRec(timestamp, rodr2rpy(rv)));
}
if (OUTPUT_DEBUG_IMAGE) {
@@ -906,6 +921,8 @@
* One issue right now is that the glReadPixels is quite slow .. around 6.5ms for a 720p frame
*/
private class VideoDecoderForOpenCV implements Runnable {
+ static final String TAG = "VideoDecoderForOpenCV";
+
private MediaExtractor extractor=null;
private MediaCodec decoder=null;
private CtsMediaOutputSurface surface=null;
@@ -1031,7 +1048,7 @@
}
if (decoder == null) {
- Log.e("VideoDecoderForOpenCV", "Can't find video info!");
+ Log.e(TAG, "Can't find video info!");
return;
}
valid = true;
@@ -1060,6 +1077,7 @@
long timeoutUs = 10000;
int iframe = 0;
+ long frameTimestamp = 0;
while (!Thread.interrupted()) {
if (!isEOS) {
@@ -1076,8 +1094,12 @@
MediaCodec.BUFFER_FLAG_END_OF_STREAM);
isEOS = true;
} else {
- decoder.queueInputBuffer(inIndex, 0, sampleSize,
- extractor.getSampleTime(), 0);
+ frameTimestamp = extractor.getSampleTime();
+ decoder.queueInputBuffer(inIndex, 0, sampleSize, frameTimestamp, 0);
+ if (LOCAL_LOGD) {
+ Log.d(TAG, String.format("Frame %d sample time %f s",
+ iframe, (double)frameTimestamp/1e6));
+ }
extractor.advance();
}
}
@@ -1088,19 +1110,19 @@
switch (outIndex) {
case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
if (LOCAL_LOGD) {
- Log.d("VideoDecoderForOpenCV", "INFO_OUTPUT_BUFFERS_CHANGED");
+ Log.d(TAG, "INFO_OUTPUT_BUFFERS_CHANGED");
}
outputBuffers = decoder.getOutputBuffers();
break;
case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
outFormat = decoder.getOutputFormat();
if (LOCAL_LOGD) {
- Log.d("VideoDecoderForOpenCV", "New format " + outFormat);
+ Log.d(TAG, "New format " + outFormat);
}
break;
case MediaCodec.INFO_TRY_AGAIN_LATER:
if (LOCAL_LOGD) {
- Log.d("VideoDecoderForOpenCV", "dequeueOutputBuffer timed out!");
+ Log.d(TAG, "dequeueOutputBuffer timed out!");
}
break;
default:
@@ -1118,12 +1140,12 @@
if (doRender) {
surface.awaitNewImage();
surface.drawImage();
- if (LOCAL_LOGD) {
- Log.d("VideoDecoderForOpenCV", "Finish drawing a frame!");
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "Finish drawing a frame!");
}
if ((iframe++ % mDecimation) == 0) {
//Send the frame for processing
- mMatBuffer.put();
+ mMatBuffer.put(frameTimestamp);
}
}
break;
@@ -1149,8 +1171,8 @@
* Get next valid frame
* @return Frame in OpenCV mat
*/
- public Mat getFrame() {
- return mMatBuffer.get();
+ public Mat getFrame(long ts[]) {
+ return mMatBuffer.get(ts);
}
/**
@@ -1168,6 +1190,7 @@
private Mat mat;
private byte[] bytes;
private ByteBuffer buf;
+ private long timestamp;
private boolean full;
private int mWidth, mHeight;
@@ -1180,6 +1203,7 @@
mat = new Mat(height, width, CvType.CV_8UC4); //RGBA
buf = ByteBuffer.allocateDirect(width*height*4);
bytes = new byte[width*height*4];
+ timestamp = -1;
mValid = true;
full = false;
@@ -1190,7 +1214,7 @@
notifyAll();
}
- public synchronized Mat get() {
+ public synchronized Mat get(long ts[]) {
if (!mValid) return null;
while (full == false) {
@@ -1204,9 +1228,11 @@
mat.put(0,0, bytes);
full = false;
notifyAll();
+ ts[0] = timestamp;
return mat;
}
- public synchronized void put() {
+
+ public synchronized void put(long ts) {
while (full) {
try {
wait();
@@ -1219,6 +1245,7 @@
buf.get(bytes);
buf.rewind();
+ timestamp = ts;
full = true;
notifyAll();
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckTestActivity.java
index 9a74a0e..35c4d56 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckTestActivity.java
@@ -16,9 +16,10 @@
package com.android.cts.verifier.sensors;
-
+import android.content.Context;
import android.hardware.cts.helpers.SensorTestStateNotSupportedException;
import android.os.Bundle;
+import android.os.PowerManager;
import com.android.cts.verifier.sensors.base.SensorCtsVerifierTestActivity;
import com.android.cts.verifier.sensors.helpers.OpenCVLibrary;
@@ -82,7 +83,7 @@
while(retry-->0) {
try {
- Thread.sleep(100);
+ Thread.sleep(250);
} catch (InterruptedException e) {
//
}
@@ -146,7 +147,15 @@
// Analysis of recorded video and sensor data using RVCXAnalyzer
RVCVXCheckAnalyzer analyzer = new RVCVXCheckAnalyzer(mRecPath);
+
+ // acquire a partial wake lock just in case CPU fall asleep
+ PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
+ PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+ "RVCVXCheckAnalyzer");
+
+ wl.acquire();
mReport = analyzer.processDataSet();
+ wl.release();
playSound();
vibrate(500);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SignificantMotionTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SignificantMotionTestActivity.java
index 6dbf8cc..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);
}
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)