Merge "Delta should be greater than reserved size when check storage" into pie-cts-dev am: 760aa0a75f am: eea22c9a41 am: 96ef9f737a am: a670426615 am: 61e9524b7b
am: 3852bb41a3
Change-Id: Ic1d870da5490289e2b88643f15a73b3f6cd148ed
diff --git a/.gitignore b/.gitignore
index 33bfd97..ff66172 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,3 +9,6 @@
*.iml
*.class
*.sw*
+
+# Jars added by Idea's "Konfigure kotlin in project" action
+**/lib/kotlin-*.jar
\ No newline at end of file
diff --git a/CtsCoverage.mk b/CtsCoverage.mk
index 0ac533b..3c065c8 100644
--- a/CtsCoverage.mk
+++ b/CtsCoverage.mk
@@ -31,12 +31,17 @@
$(hide) mkdir -p $(dir $@)
$(hide) $(ACP) $< $@
+system_api_xml_description := $(TARGET_OUT_COMMON_INTERMEDIATES)/system-api.xml
+
cts-test-coverage-report := $(coverage_out)/test-coverage.html
+cts-system-api-coverage-report := $(coverage_out)/system-api-coverage.html
+cts-system-api-xml-coverage-report := $(coverage_out)/system-api-coverage.xml
cts-verifier-coverage-report := $(coverage_out)/verifier-coverage.html
cts-combined-coverage-report := $(coverage_out)/combined-coverage.html
cts-combined-xml-coverage-report := $(coverage_out)/combined-coverage.xml
cts_api_coverage_dependencies := $(cts_api_coverage_exe) $(dexdeps_exe) $(api_xml_description) $(napi_xml_description)
+cts_system_api_coverage_dependencies := $(cts_api_coverage_exe) $(dexdeps_exe) $(system_api_xml_description)
android_cts_zip := $(HOST_OUT)/cts/android-cts.zip
cts_verifier_apk := $(call intermediates-dir-for,APPS,CtsVerifier)/package.apk
@@ -50,6 +55,24 @@
$(call generate-coverage-report-cts,"CTS Tests API-NDK Coverage Report",\
$(PRIVATE_TEST_CASES),html)
+$(cts-system-api-coverage-report): PRIVATE_TEST_CASES := $(COMPATIBILITY_TESTCASES_OUT_cts)
+$(cts-system-api-coverage-report): PRIVATE_CTS_API_COVERAGE_EXE := $(cts_api_coverage_exe)
+$(cts-system-api-coverage-report): PRIVATE_DEXDEPS_EXE := $(dexdeps_exe)
+$(cts-system-api-coverage-report): PRIVATE_API_XML_DESC := $(system_api_xml_description)
+$(cts-system-api-coverage-report): PRIVATE_NAPI_XML_DESC := ""
+$(cts-system-api-coverage-report) : $(android_cts_zip) $(cts_system_api_coverage_dependencies) | $(ACP)
+ $(call generate-coverage-report-cts,"CTS System API Coverage Report",\
+ $(PRIVATE_TEST_CASES),html)
+
+$(cts-system-api-xml-coverage-report): PRIVATE_TEST_CASES := $(COMPATIBILITY_TESTCASES_OUT_cts)
+$(cts-system-api-xml-coverage-report): PRIVATE_CTS_API_COVERAGE_EXE := $(cts_api_coverage_exe)
+$(cts-system-api-xml-coverage-report): PRIVATE_DEXDEPS_EXE := $(dexdeps_exe)
+$(cts-system-api-xml-coverage-report): PRIVATE_API_XML_DESC := $(system_api_xml_description)
+$(cts-system-api-xml-coverage-report): PRIVATE_NAPI_XML_DESC := ""
+$(cts-system-api-xml-coverage-report) : $(android_cts_zip) $(cts_system_api_coverage_dependencies) | $(ACP)
+ $(call generate-coverage-report-cts,"CTS System API Coverage Report - XML",\
+ $(PRIVATE_TEST_CASES),xml)
+
$(cts-verifier-coverage-report): PRIVATE_TEST_CASES := $(cts_verifier_apk)
$(cts-verifier-coverage-report): PRIVATE_CTS_API_COVERAGE_EXE := $(cts_api_coverage_exe)
$(cts-verifier-coverage-report): PRIVATE_DEXDEPS_EXE := $(dexdeps_exe)
@@ -80,6 +103,12 @@
.PHONY: cts-test-coverage
cts-test-coverage : $(cts-test-coverage-report)
+.PHONY: cts-system-api-coverage
+cts-system-api-coverage : $(cts-system-api-coverage-report)
+
+.PHONY: cts-system-api-xml-coverage
+cts-system-api-xml-coverage : $(cts-system-api-xml-coverage-report)
+
.PHONY: cts-verifier-coverage
cts-verifier-coverage : $(cts-verifier-coverage-report)
@@ -94,6 +123,8 @@
# Put the test coverage report in the dist dir if "cts-api-coverage" is among the build goals.
$(call dist-for-goals, cts-api-coverage, $(cts-test-coverage-report):cts-test-coverage-report.html)
+$(call dist-for-goals, cts-api-coverage, $(cts-system-api-coverage-report):cts-system-api-coverage-report.html)
+$(call dist-for-goals, cts-api-coverage, $(cts-system-api-xml-coverage-report):cts-system-api-coverage-report.xml)
$(call dist-for-goals, cts-api-coverage, $(cts-verifier-coverage-report):cts-verifier-coverage-report.html)
$(call dist-for-goals, cts-api-coverage, $(cts-combined-coverage-report):cts-combined-coverage-report.html)
$(call dist-for-goals, cts-api-coverage, $(cts-combined-xml-coverage-report):cts-combined-coverage-report.xml)
@@ -110,12 +141,16 @@
# Reset temp vars
cts_api_coverage_dependencies :=
+cts_system_api_coverage_dependencies :=
cts-combined-coverage-report :=
cts-combined-xml-coverage-report :=
cts-verifier-coverage-report :=
cts-test-coverage-report :=
+cts-system-api-coverage-report :=
+cts-system-api-xml-coverage-report :=
api_xml_description :=
api_text_description :=
+system_api_xml_description :=
napi_xml_description :=
napi_text_description :=
coverage_out :=
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 39a0f35..926b808 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,5 +1,6 @@
[Builtin Hooks]
clang_format = true
+xmllint = true
[Builtin Hooks Options]
clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
diff --git a/apps/CameraITS/CameraITS.pdf b/apps/CameraITS/CameraITS.pdf
index 5f1e481..f45d652 100644
--- a/apps/CameraITS/CameraITS.pdf
+++ b/apps/CameraITS/CameraITS.pdf
Binary files differ
diff --git a/apps/CameraITS/build/envsetup.sh b/apps/CameraITS/build/envsetup.sh
index ae12e10..6dcdf79 100644
--- a/apps/CameraITS/build/envsetup.sh
+++ b/apps/CameraITS/build/envsetup.sh
@@ -31,7 +31,7 @@
python -V 2>&1 | grep -q "Python 2.7" || \
echo ">> Require python 2.7" >&2
-for M in numpy PIL matplotlib scipy.stats scipy.spatial
+for M in numpy PIL matplotlib scipy.stats scipy.spatial serial
do
python -c "import $M" >/dev/null 2>&1 || \
echo ">> Require Python $M module" >&2
diff --git a/apps/CameraITS/pymodules/its/device.py b/apps/CameraITS/pymodules/its/device.py
index 3311318..bc1d3a6 100644
--- a/apps/CameraITS/pymodules/its/device.py
+++ b/apps/CameraITS/pymodules/its/device.py
@@ -27,6 +27,7 @@
from collections import namedtuple
+
class ItsSession(object):
"""Controls a device over adb to run ITS scripts.
@@ -320,6 +321,23 @@
if data['tag'] != 'vibrationStarted':
raise its.error.Error('Invalid command response')
+ def set_audio_restriction(self, mode):
+ """Set the audio restriction mode for this camera device.
+
+ Args:
+ mode: the audio restriction mode. See CameraDevice.java for valid
+ value.
+ Returns:
+ Nothing.
+ """
+ cmd = {}
+ cmd["cmdName"] = "setAudioRestriction"
+ cmd["mode"] = mode
+ self.sock.send(json.dumps(cmd) + "\n")
+ data,_ = self.__read_response_from_socket()
+ if data["tag"] != "audioRestrictionSet":
+ raise its.error.Error("Invalid command response")
+
def get_sensors(self):
"""Get all sensors on the device.
@@ -1091,7 +1109,7 @@
return device_bfp
def parse_camera_ids(ids):
- """ Parse the string of camera IDs into array of CameraIdCombo tuples.
+ """Parse the string of camera IDs into array of CameraIdCombo tuples.
"""
CameraIdCombo = namedtuple('CameraIdCombo', ['id', 'sub_id'])
id_combos = []
@@ -1105,6 +1123,35 @@
assert(False), 'Camera id parameters must be either ID, or ID:SUB_ID'
return id_combos
+
+def get_build_sdk_version(device_id=None):
+ """Get the build version of the device."""
+ if not device_id:
+ device_id = get_device_id()
+ cmd = 'adb -s %s shell getprop ro.build.version.sdk' % device_id
+ try:
+ build_sdk_version = int(subprocess.check_output(cmd.split()).rstrip())
+ print 'Build SDK version: %d' % build_sdk_version
+ except (subprocess.CalledProcessError, ValueError):
+ print 'No build_sdk_version.'
+ assert 0
+ return build_sdk_version
+
+
+def get_first_api_level(device_id=None):
+ """Get the first API level for device."""
+ if not device_id:
+ device_id = get_device_id()
+ cmd = 'adb -s %s shell getprop ro.product.first_api_level' % device_id
+ try:
+ first_api_level = int(subprocess.check_output(cmd.split()).rstrip())
+ print 'First API level: %d' % first_api_level
+ except (subprocess.CalledProcessError, ValueError):
+ print 'No first_api_level. Setting to build version.'
+ first_api_level = get_build_sdk_version(device_id)
+ return first_api_level
+
+
def _run(cmd):
"""Replacement for os.system, with hiding of stdout+stderr messages.
"""
diff --git a/apps/CameraITS/tests/scene0/test_audio_restriction.py b/apps/CameraITS/tests/scene0/test_audio_restriction.py
new file mode 100644
index 0000000..c771381
--- /dev/null
+++ b/apps/CameraITS/tests/scene0/test_audio_restriction.py
@@ -0,0 +1,100 @@
+# Copyright 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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 math
+import os.path
+import time
+
+import its.caps
+import its.device
+import matplotlib
+from matplotlib import pylab
+import numpy as np
+
+NAME = os.path.basename(__file__).split(".")[0]
+
+# if the var(x) > var(stable) * this threshold, then device is considered vibrated
+# Test results shows the variance difference is larger for higher sampling frequency
+# This threshold is good enough for 50hz samples.
+THRESHOLD_VIBRATION_VAR = 10.0
+
+# Match CameraDevice.java constant
+AUDIO_RESTRICTION_NONE = 0
+AUDIO_RESTRICTION_VIBRATION = 1
+AUDIO_RESTRICTION_VIBRATION_SOUND = 2
+
+# The sleep time between vibrator on/off to avoid getting some residual vibrations
+SLEEP_BETWEEN_SAMPLES_SEC = 0.5
+# The sleep time to collect sensor samples
+SLEEP_COLLECT_SAMPLES_SEC = 1.0
+
+def calc_magnitude(e):
+ x = e["x"]
+ y = e["y"]
+ z = e["z"]
+ return math.sqrt(x*x + y*y + z*z)
+
+def main():
+ """Test vibrations can be muted by the camera audio restriction API."""
+
+ with its.device.ItsSession() as cam:
+ props = cam.get_camera_properties()
+ props = cam.override_with_hidden_physical_camera_props(props)
+ sensors = cam.get_sensors()
+
+ its.caps.skip_unless(sensors.get("accel") and sensors.get("vibrator"))
+
+ cam.start_sensor_events()
+ pattern_ms = [0, 1000]
+ cam.do_vibrate(pattern_ms)
+ test_length_second = sum(pattern_ms) / 1000
+ time.sleep(test_length_second)
+ events = cam.get_sensor_events()
+ print "Accelerometer events over %ds: %d " % (test_length_second, len(events["accel"]))
+ times_ms = [e["time"]/float(1e6) for e in events["accel"]]
+ t0 = times_ms[0]
+ times_ms = [t - t0 for t in times_ms]
+ magnitudes = [calc_magnitude(e) for e in events["accel"]]
+ var_w_vibration = np.var(magnitudes)
+
+ time.sleep(SLEEP_BETWEEN_SAMPLES_SEC)
+ cam.start_sensor_events()
+ time.sleep(SLEEP_COLLECT_SAMPLES_SEC)
+ events = cam.get_sensor_events()
+ magnitudes = [calc_magnitude(e) for e in events["accel"]]
+ var_wo_vibration = np.var(magnitudes)
+
+ if var_w_vibration < var_wo_vibration * THRESHOLD_VIBRATION_VAR:
+ print "Warning: unable to detect vibration, variance w/wo vibration too close:"\
+ " %f/%f. Make sure device is on non-dampening surface" % (
+ var_w_vibration, var_wo_vibration)
+
+ time.sleep(SLEEP_BETWEEN_SAMPLES_SEC)
+ cam.start_sensor_events()
+ cam.set_audio_restriction(AUDIO_RESTRICTION_VIBRATION)
+ cam.do_vibrate(pattern_ms)
+ time.sleep(SLEEP_COLLECT_SAMPLES_SEC)
+ events = cam.get_sensor_events()
+ magnitudes = [calc_magnitude(e) for e in events["accel"]]
+ var_w_vibration_restricted = np.var(magnitudes)
+
+ print "Accel variance with/without/restricted vibration (%f, %f, %f)" % (
+ var_w_vibration, var_wo_vibration, var_w_vibration_restricted)
+
+ e_msg = "Device vibrated while vibration is muted"
+ assert var_w_vibration_restricted < var_wo_vibration * THRESHOLD_VIBRATION_VAR, e_msg
+
+if __name__ == "__main__":
+ main()
+
diff --git a/apps/CameraITS/tests/scene1/scene1.pdf b/apps/CameraITS/tests/scene1_1/scene1_1.pdf
similarity index 100%
rename from apps/CameraITS/tests/scene1/scene1.pdf
rename to apps/CameraITS/tests/scene1_1/scene1_1.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene1/scene1_0.5_scaled.pdf b/apps/CameraITS/tests/scene1_1/scene1_1_0.5x_scaled.pdf
similarity index 100%
rename from apps/CameraITS/tests/scene1/scene1_0.5_scaled.pdf
rename to apps/CameraITS/tests/scene1_1/scene1_1_0.5x_scaled.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene1/scene1_0.67_scaled.pdf b/apps/CameraITS/tests/scene1_1/scene1_1_0.67x_scaled.pdf
similarity index 100%
rename from apps/CameraITS/tests/scene1/scene1_0.67_scaled.pdf
rename to apps/CameraITS/tests/scene1_1/scene1_1_0.67x_scaled.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene1/test_3a.py b/apps/CameraITS/tests/scene1_1/test_3a.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_3a.py
rename to apps/CameraITS/tests/scene1_1/test_3a.py
diff --git a/apps/CameraITS/tests/scene1/test_ae_af.py b/apps/CameraITS/tests/scene1_1/test_ae_af.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_ae_af.py
rename to apps/CameraITS/tests/scene1_1/test_ae_af.py
diff --git a/apps/CameraITS/tests/scene1/test_ae_precapture_trigger.py b/apps/CameraITS/tests/scene1_1/test_ae_precapture_trigger.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_ae_precapture_trigger.py
rename to apps/CameraITS/tests/scene1_1/test_ae_precapture_trigger.py
diff --git a/apps/CameraITS/tests/scene1/test_auto_vs_manual.py b/apps/CameraITS/tests/scene1_1/test_auto_vs_manual.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_auto_vs_manual.py
rename to apps/CameraITS/tests/scene1_1/test_auto_vs_manual.py
diff --git a/apps/CameraITS/tests/scene1/test_black_white.py b/apps/CameraITS/tests/scene1_1/test_black_white.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_black_white.py
rename to apps/CameraITS/tests/scene1_1/test_black_white.py
diff --git a/apps/CameraITS/tests/scene1/test_burst_sameness_manual.py b/apps/CameraITS/tests/scene1_1/test_burst_sameness_manual.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_burst_sameness_manual.py
rename to apps/CameraITS/tests/scene1_1/test_burst_sameness_manual.py
diff --git a/apps/CameraITS/tests/scene1/test_capture_result.py b/apps/CameraITS/tests/scene1_1/test_capture_result.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_capture_result.py
rename to apps/CameraITS/tests/scene1_1/test_capture_result.py
diff --git a/apps/CameraITS/tests/scene1/test_channel_saturation.py b/apps/CameraITS/tests/scene1_1/test_channel_saturation.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_channel_saturation.py
rename to apps/CameraITS/tests/scene1_1/test_channel_saturation.py
diff --git a/apps/CameraITS/tests/scene1/test_crop_region_raw.py b/apps/CameraITS/tests/scene1_1/test_crop_region_raw.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_crop_region_raw.py
rename to apps/CameraITS/tests/scene1_1/test_crop_region_raw.py
diff --git a/apps/CameraITS/tests/scene1/test_crop_regions.py b/apps/CameraITS/tests/scene1_1/test_crop_regions.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_crop_regions.py
rename to apps/CameraITS/tests/scene1_1/test_crop_regions.py
diff --git a/apps/CameraITS/tests/scene1/test_dng_noise_model.py b/apps/CameraITS/tests/scene1_1/test_dng_noise_model.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_dng_noise_model.py
rename to apps/CameraITS/tests/scene1_1/test_dng_noise_model.py
diff --git a/apps/CameraITS/tests/scene1/test_ev_compensation_advanced.py b/apps/CameraITS/tests/scene1_1/test_ev_compensation_advanced.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_ev_compensation_advanced.py
rename to apps/CameraITS/tests/scene1_1/test_ev_compensation_advanced.py
diff --git a/apps/CameraITS/tests/scene1/test_ev_compensation_basic.py b/apps/CameraITS/tests/scene1_1/test_ev_compensation_basic.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_ev_compensation_basic.py
rename to apps/CameraITS/tests/scene1_1/test_ev_compensation_basic.py
diff --git a/apps/CameraITS/tests/scene1/test_exposure.py b/apps/CameraITS/tests/scene1_1/test_exposure.py
similarity index 75%
rename from apps/CameraITS/tests/scene1/test_exposure.py
rename to apps/CameraITS/tests/scene1_1/test_exposure.py
index a13f020..bf52d6f 100644
--- a/apps/CameraITS/tests/scene1/test_exposure.py
+++ b/apps/CameraITS/tests/scene1_1/test_exposure.py
@@ -25,6 +25,7 @@
IMG_STATS_GRID = 9 # find used to find the center 11.11%
NAME = os.path.basename(__file__).split('.')[0]
+NUM_PTS_2X_GAIN = 3 # 3 points every 2x increase in gain
THRESHOLD_MAX_OUTLIER_DIFF = 0.1
THRESHOLD_MIN_LEVEL = 0.1
THRESHOLD_MAX_LEVEL = 0.9
@@ -36,6 +37,41 @@
THRESH_EXP_KNEE = 6E6 # exposures less than knee have relaxed tol
+def find_fit_and_check(chan, mults, values, thresh_max_level_diff):
+ """Find line fit and check values.
+
+ Check for linearity. Verify sample pixel mean values are close to each
+ other. Also ensure that the images aren't clamped to 0 or 1
+ (which would make them look like flat lines).
+
+ Args:
+ chan: [0:2] RGB channel
+ mults: list of multiplication values for gain*m, exp/m
+ values: mean values for chan
+ thresh_max_level_diff: threshold for max difference
+ """
+
+ m, b = numpy.polyfit(mults, values, 1).tolist()
+ max_val = max(values)
+ min_val = min(values)
+ max_diff = max_val - min_val
+ print 'Channel %d line fit (y = mx+b): m = %f, b = %f' % (chan, m, b)
+ print 'Channel max %f min %f diff %f' % (max_val, min_val, max_diff)
+ e_msg = 'max_diff: %.4f, THRESH: %.3f' % (
+ max_diff, thresh_max_level_diff)
+ assert max_diff < thresh_max_level_diff, e_msg
+ e_msg = 'b: %.2f, THRESH_MIN: %.1f, THRESH_MAX: %.1f' % (
+ b, THRESHOLD_MIN_LEVEL, THRESHOLD_MAX_LEVEL)
+ assert THRESHOLD_MAX_LEVEL > b > THRESHOLD_MIN_LEVEL, e_msg
+ for v in values:
+ e_msg = 'v: %.2f, THRESH_MIN: %.1f, THRESH_MAX: %.1f' % (
+ v, THRESHOLD_MIN_LEVEL, THRESHOLD_MAX_LEVEL)
+ assert THRESHOLD_MAX_LEVEL > v > THRESHOLD_MIN_LEVEL, e_msg
+ e_msg = 'v: %.2f, b: %.2f, THRESH_MAX_OUTLIER_DIFF: %.1f' % (
+ v, b, THRESHOLD_MAX_OUTLIER_DIFF)
+ assert abs(v - b) < THRESHOLD_MAX_OUTLIER_DIFF, e_msg
+
+
def get_raw_active_array_size(props):
"""Return the active array w, h from props."""
aaw = (props['android.sensor.info.preCorrectionActiveArraySize']['right'] -
@@ -137,7 +173,7 @@
raw_b_means.append(b[IMG_STATS_GRID/2, IMG_STATS_GRID/2]
* request_result_ratio)
# Test 3 steps per 2x gain
- m *= pow(2, 1.0 / 3)
+ m *= pow(2, 1.0 / NUM_PTS_2X_GAIN)
# Allow more threshold for devices with wider exposure range
if m >= 64.0:
@@ -145,60 +181,40 @@
# Draw plots
pylab.figure('rgb data')
- pylab.plot(mults, r_means, 'ro-')
- pylab.plot(mults, g_means, 'go-')
- pylab.plot(mults, b_means, 'bo-')
+ pylab.semilogx(mults, r_means, 'ro-')
+ pylab.semilogx(mults, g_means, 'go-')
+ pylab.semilogx(mults, b_means, 'bo-')
pylab.title(NAME + 'RGB Data')
pylab.xlabel('Gain Multiplier')
pylab.ylabel('Normalized RGB Plane Avg')
+ pylab.minorticks_off()
+ pylab.xticks(mults[0::NUM_PTS_2X_GAIN], mults[0::NUM_PTS_2X_GAIN])
pylab.ylim([0, 1])
matplotlib.pyplot.savefig('%s_plot_means.png' % (NAME))
if process_raw and debug:
pylab.figure('raw data')
- pylab.plot(mults, raw_r_means, 'ro-', label='R')
- pylab.plot(mults, raw_gr_means, 'go-', label='GR')
- pylab.plot(mults, raw_gb_means, 'ko-', label='GB')
- pylab.plot(mults, raw_b_means, 'bo-', label='B')
+ pylab.semilogx(mults, raw_r_means, 'ro-', label='R')
+ pylab.semilogx(mults, raw_gr_means, 'go-', label='GR')
+ pylab.semilogx(mults, raw_gb_means, 'ko-', label='GB')
+ pylab.semilogx(mults, raw_b_means, 'bo-', label='B')
pylab.title(NAME + 'RAW Data')
pylab.xlabel('Gain Multiplier')
pylab.ylabel('Normalized RAW Plane Avg')
+ pylab.minorticks_off()
+ pylab.xticks(mults[0::NUM_PTS_2X_GAIN], mults[0::NUM_PTS_2X_GAIN])
pylab.ylim([0, 1])
pylab.legend(numpoints=1)
matplotlib.pyplot.savefig('%s_plot_raw_means.png' % (NAME))
- # Check for linearity. Verify sample pixel mean values are close to each
- # other. Also ensure that the images aren't clamped to 0 or 1
- # (which would make them look like flat lines).
for chan in xrange(3):
values = [r_means, g_means, b_means][chan]
- m, b = numpy.polyfit(mults, values, 1).tolist()
- max_val = max(values)
- min_val = min(values)
- max_diff = max_val - min_val
- print 'Channel %d line fit (y = mx+b): m = %f, b = %f' % (chan, m, b)
- print 'Channel max %f min %f diff %f' % (max_val, min_val, max_diff)
- assert max_diff < threshold_max_level_diff
- assert b > THRESHOLD_MIN_LEVEL and b < THRESHOLD_MAX_LEVEL
- for v in values:
- assert v > THRESHOLD_MIN_LEVEL and v < THRESHOLD_MAX_LEVEL
- assert abs(v - b) < THRESHOLD_MAX_OUTLIER_DIFF
+ find_fit_and_check(chan, mults, values, threshold_max_level_diff)
if process_raw and debug:
for chan in xrange(4):
values = [raw_r_means, raw_gr_means, raw_gb_means,
raw_b_means][chan]
- m, b = numpy.polyfit(mults, values, 1).tolist()
- max_val = max(values)
- min_val = min(values)
- max_diff = max_val - min_val
- print 'Channel %d line fit (y = mx+b): m = %f, b = %f' % (chan,
- m, b)
- print 'Channel max %f min %f diff %f' % (max_val, min_val, max_diff)
- assert max_diff < threshold_max_level_diff
- assert b > THRESHOLD_MIN_LEVEL and b < THRESHOLD_MAX_LEVEL
- for v in values:
- assert v > THRESHOLD_MIN_LEVEL and v < THRESHOLD_MAX_LEVEL
- assert abs(v - b) < THRESHOLD_MAX_OUTLIER_DIFF
+ find_fit_and_check(chan, mults, values, threshold_max_level_diff)
if __name__ == '__main__':
main()
diff --git a/apps/CameraITS/tests/scene1/test_jpeg.py b/apps/CameraITS/tests/scene1_1/test_jpeg.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_jpeg.py
rename to apps/CameraITS/tests/scene1_1/test_jpeg.py
diff --git a/apps/CameraITS/tests/scene1/test_latching.py b/apps/CameraITS/tests/scene1_1/test_latching.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_latching.py
rename to apps/CameraITS/tests/scene1_1/test_latching.py
diff --git a/apps/CameraITS/tests/scene1/test_linearity.py b/apps/CameraITS/tests/scene1_1/test_linearity.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_linearity.py
rename to apps/CameraITS/tests/scene1_1/test_linearity.py
diff --git a/apps/CameraITS/tests/scene1/test_locked_burst.py b/apps/CameraITS/tests/scene1_1/test_locked_burst.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_locked_burst.py
rename to apps/CameraITS/tests/scene1_1/test_locked_burst.py
diff --git a/apps/CameraITS/tests/scene1/test_multi_camera_match.py b/apps/CameraITS/tests/scene1_1/test_multi_camera_match.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_multi_camera_match.py
rename to apps/CameraITS/tests/scene1_1/test_multi_camera_match.py
diff --git a/apps/CameraITS/tests/scene1/test_param_color_correction.py b/apps/CameraITS/tests/scene1_1/test_param_color_correction.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_param_color_correction.py
rename to apps/CameraITS/tests/scene1_1/test_param_color_correction.py
diff --git a/apps/CameraITS/tests/scene1/test_param_exposure_time.py b/apps/CameraITS/tests/scene1_1/test_param_exposure_time.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_param_exposure_time.py
rename to apps/CameraITS/tests/scene1_1/test_param_exposure_time.py
diff --git a/apps/CameraITS/tests/scene1/test_param_flash_mode.py b/apps/CameraITS/tests/scene1_1/test_param_flash_mode.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_param_flash_mode.py
rename to apps/CameraITS/tests/scene1_1/test_param_flash_mode.py
diff --git a/apps/CameraITS/tests/scene1/test_param_noise_reduction.py b/apps/CameraITS/tests/scene1_1/test_param_noise_reduction.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_param_noise_reduction.py
rename to apps/CameraITS/tests/scene1_1/test_param_noise_reduction.py
diff --git a/apps/CameraITS/tests/scene1/scene1.pdf b/apps/CameraITS/tests/scene1_2/scene1_2.pdf
similarity index 100%
copy from apps/CameraITS/tests/scene1/scene1.pdf
copy to apps/CameraITS/tests/scene1_2/scene1_2.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene1/scene1_0.5_scaled.pdf b/apps/CameraITS/tests/scene1_2/scene1_2_0.5x_scaled.pdf
similarity index 100%
copy from apps/CameraITS/tests/scene1/scene1_0.5_scaled.pdf
copy to apps/CameraITS/tests/scene1_2/scene1_2_0.5x_scaled.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene1/scene1_0.67_scaled.pdf b/apps/CameraITS/tests/scene1_2/scene1_2_0.67x_scaled.pdf
similarity index 100%
copy from apps/CameraITS/tests/scene1/scene1_0.67_scaled.pdf
copy to apps/CameraITS/tests/scene1_2/scene1_2_0.67x_scaled.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene1/test_param_sensitivity.py b/apps/CameraITS/tests/scene1_2/test_param_sensitivity.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_param_sensitivity.py
rename to apps/CameraITS/tests/scene1_2/test_param_sensitivity.py
diff --git a/apps/CameraITS/tests/scene1/test_param_shading_mode.py b/apps/CameraITS/tests/scene1_2/test_param_shading_mode.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_param_shading_mode.py
rename to apps/CameraITS/tests/scene1_2/test_param_shading_mode.py
diff --git a/apps/CameraITS/tests/scene1/test_param_tonemap_mode.py b/apps/CameraITS/tests/scene1_2/test_param_tonemap_mode.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_param_tonemap_mode.py
rename to apps/CameraITS/tests/scene1_2/test_param_tonemap_mode.py
diff --git a/apps/CameraITS/tests/scene1/test_post_raw_sensitivity_boost.py b/apps/CameraITS/tests/scene1_2/test_post_raw_sensitivity_boost.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_post_raw_sensitivity_boost.py
rename to apps/CameraITS/tests/scene1_2/test_post_raw_sensitivity_boost.py
diff --git a/apps/CameraITS/tests/scene1/test_raw_burst_sensitivity.py b/apps/CameraITS/tests/scene1_2/test_raw_burst_sensitivity.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_raw_burst_sensitivity.py
rename to apps/CameraITS/tests/scene1_2/test_raw_burst_sensitivity.py
diff --git a/apps/CameraITS/tests/scene1/test_raw_exposure.py b/apps/CameraITS/tests/scene1_2/test_raw_exposure.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_raw_exposure.py
rename to apps/CameraITS/tests/scene1_2/test_raw_exposure.py
diff --git a/apps/CameraITS/tests/scene1/test_raw_sensitivity.py b/apps/CameraITS/tests/scene1_2/test_raw_sensitivity.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_raw_sensitivity.py
rename to apps/CameraITS/tests/scene1_2/test_raw_sensitivity.py
diff --git a/apps/CameraITS/tests/scene1/test_reprocess_noise_reduction.py b/apps/CameraITS/tests/scene1_2/test_reprocess_noise_reduction.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_reprocess_noise_reduction.py
rename to apps/CameraITS/tests/scene1_2/test_reprocess_noise_reduction.py
diff --git a/apps/CameraITS/tests/scene1/test_tonemap_sequence.py b/apps/CameraITS/tests/scene1_2/test_tonemap_sequence.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_tonemap_sequence.py
rename to apps/CameraITS/tests/scene1_2/test_tonemap_sequence.py
diff --git a/apps/CameraITS/tests/scene1/test_yuv_jpeg_all.py b/apps/CameraITS/tests/scene1_2/test_yuv_jpeg_all.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_yuv_jpeg_all.py
rename to apps/CameraITS/tests/scene1_2/test_yuv_jpeg_all.py
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_dng.py b/apps/CameraITS/tests/scene1_2/test_yuv_plus_dng.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_yuv_plus_dng.py
rename to apps/CameraITS/tests/scene1_2/test_yuv_plus_dng.py
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_jpeg.py b/apps/CameraITS/tests/scene1_2/test_yuv_plus_jpeg.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_yuv_plus_jpeg.py
rename to apps/CameraITS/tests/scene1_2/test_yuv_plus_jpeg.py
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_raw.py b/apps/CameraITS/tests/scene1_2/test_yuv_plus_raw.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_yuv_plus_raw.py
rename to apps/CameraITS/tests/scene1_2/test_yuv_plus_raw.py
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_raw10.py b/apps/CameraITS/tests/scene1_2/test_yuv_plus_raw10.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_yuv_plus_raw10.py
rename to apps/CameraITS/tests/scene1_2/test_yuv_plus_raw10.py
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_raw12.py b/apps/CameraITS/tests/scene1_2/test_yuv_plus_raw12.py
similarity index 100%
rename from apps/CameraITS/tests/scene1/test_yuv_plus_raw12.py
rename to apps/CameraITS/tests/scene1_2/test_yuv_plus_raw12.py
diff --git a/apps/CameraITS/tests/scene2/SampleTarget.jpg b/apps/CameraITS/tests/scene2_a/SampleTarget.jpg
similarity index 100%
rename from apps/CameraITS/tests/scene2/SampleTarget.jpg
rename to apps/CameraITS/tests/scene2_a/SampleTarget.jpg
Binary files differ
diff --git a/apps/CameraITS/tests/scene2/scene2.pdf b/apps/CameraITS/tests/scene2_a/scene2_a.pdf
similarity index 100%
rename from apps/CameraITS/tests/scene2/scene2.pdf
rename to apps/CameraITS/tests/scene2_a/scene2_a.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene2/scene2_0.5_scaled.pdf b/apps/CameraITS/tests/scene2_a/scene2_a_0.5x_scaled.pdf
similarity index 100%
rename from apps/CameraITS/tests/scene2/scene2_0.5_scaled.pdf
rename to apps/CameraITS/tests/scene2_a/scene2_a_0.5x_scaled.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene2/scene2_0.67_scaled.pdf b/apps/CameraITS/tests/scene2_a/scene2_a_0.67x_scaled.pdf
similarity index 100%
rename from apps/CameraITS/tests/scene2/scene2_0.67_scaled.pdf
rename to apps/CameraITS/tests/scene2_a/scene2_a_0.67x_scaled.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene2/test_auto_per_frame_control.py b/apps/CameraITS/tests/scene2_a/test_auto_per_frame_control.py
similarity index 100%
rename from apps/CameraITS/tests/scene2/test_auto_per_frame_control.py
rename to apps/CameraITS/tests/scene2_a/test_auto_per_frame_control.py
diff --git a/apps/CameraITS/tests/scene2/test_effects.py b/apps/CameraITS/tests/scene2_a/test_effects.py
similarity index 100%
rename from apps/CameraITS/tests/scene2/test_effects.py
rename to apps/CameraITS/tests/scene2_a/test_effects.py
diff --git a/apps/CameraITS/tests/scene2/test_faces.py b/apps/CameraITS/tests/scene2_a/test_faces.py
similarity index 100%
rename from apps/CameraITS/tests/scene2/test_faces.py
rename to apps/CameraITS/tests/scene2_a/test_faces.py
diff --git a/apps/CameraITS/tests/scene2/test_format_combos.py b/apps/CameraITS/tests/scene2_a/test_format_combos.py
similarity index 100%
rename from apps/CameraITS/tests/scene2/test_format_combos.py
rename to apps/CameraITS/tests/scene2_a/test_format_combos.py
diff --git a/apps/CameraITS/tests/scene2/test_num_faces.py b/apps/CameraITS/tests/scene2_a/test_num_faces.py
similarity index 100%
rename from apps/CameraITS/tests/scene2/test_num_faces.py
rename to apps/CameraITS/tests/scene2_a/test_num_faces.py
diff --git a/apps/CameraITS/tests/scene2b/scene2b.pdf b/apps/CameraITS/tests/scene2_b/scene2_b.pdf
similarity index 100%
rename from apps/CameraITS/tests/scene2b/scene2b.pdf
rename to apps/CameraITS/tests/scene2_b/scene2_b.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene2b/scene2b_0.5_scaled.pdf b/apps/CameraITS/tests/scene2_b/scene2_b_0.5x_scaled.pdf
similarity index 100%
rename from apps/CameraITS/tests/scene2b/scene2b_0.5_scaled.pdf
rename to apps/CameraITS/tests/scene2_b/scene2_b_0.5x_scaled.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene2b/scene2b_0.67_scaled.pdf b/apps/CameraITS/tests/scene2_b/scene2_b_0.67x_scaled.pdf
similarity index 100%
rename from apps/CameraITS/tests/scene2b/scene2b_0.67_scaled.pdf
rename to apps/CameraITS/tests/scene2_b/scene2_b_0.67x_scaled.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene2c/scene2c.pdf b/apps/CameraITS/tests/scene2_c/scene2_c.pdf
similarity index 100%
rename from apps/CameraITS/tests/scene2c/scene2c.pdf
rename to apps/CameraITS/tests/scene2_c/scene2_c.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene2c/scene2c_0.5_scaled.pdf b/apps/CameraITS/tests/scene2_c/scene2_c_0.5x_scaled.pdf
similarity index 100%
rename from apps/CameraITS/tests/scene2c/scene2c_0.5_scaled.pdf
rename to apps/CameraITS/tests/scene2_c/scene2_c_0.5x_scaled.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene2c/scene2c_0.67_scaled.pdf b/apps/CameraITS/tests/scene2_c/scene2_c_0.67x_scaled.pdf
similarity index 100%
rename from apps/CameraITS/tests/scene2c/scene2c_0.67_scaled.pdf
rename to apps/CameraITS/tests/scene2_c/scene2_c_0.67x_scaled.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene2b/test_num_faces.py b/apps/CameraITS/tests/scene2b/test_num_faces.py
deleted file mode 100644
index 044c154..0000000
--- a/apps/CameraITS/tests/scene2b/test_num_faces.py
+++ /dev/null
@@ -1,100 +0,0 @@
-# Copyright 2014 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import os.path
-import cv2
-import its.caps
-import its.device
-import its.image
-import its.objects
-
-NAME = os.path.basename(__file__).split('.')[0]
-NUM_TEST_FRAMES = 20
-NUM_FACES = 3
-FD_MODE_OFF = 0
-FD_MODE_SIMPLE = 1
-FD_MODE_FULL = 2
-W, H = 640, 480
-
-
-def main():
- """Test face detection."""
- with its.device.ItsSession() as cam:
- props = cam.get_camera_properties()
- its.caps.skip_unless(its.caps.face_detect(props))
- mono_camera = its.caps.mono_camera(props)
- fd_modes = props['android.statistics.info.availableFaceDetectModes']
- a = props['android.sensor.info.activeArraySize']
- aw, ah = a['right'] - a['left'], a['bottom'] - a['top']
-
- if its.caps.read_3a(props):
- _, _, _, _, _ = cam.do_3a(get_results=True,
- mono_camera=mono_camera)
-
- for fd_mode in fd_modes:
- assert FD_MODE_OFF <= fd_mode <= FD_MODE_FULL
- req = its.objects.auto_capture_request()
- req['android.statistics.faceDetectMode'] = fd_mode
- fmt = {'format': 'yuv', 'width': W, 'height': H}
- caps = cam.do_capture([req]*NUM_TEST_FRAMES, fmt)
- for i, cap in enumerate(caps):
- md = cap['metadata']
- assert md['android.statistics.faceDetectMode'] == fd_mode
- faces = md['android.statistics.faces']
-
- # 0 faces should be returned for OFF mode
- if fd_mode == FD_MODE_OFF:
- assert not faces
- continue
- # Face detection could take several frames to warm up,
- # but should detect the correct number of faces in last frame
- if i == NUM_TEST_FRAMES - 1:
- img = its.image.convert_capture_to_rgb_image(cap,
- props=props)
- fnd_faces = len(faces)
- print 'Found %d face(s), expected %d.' % (fnd_faces,
- NUM_FACES)
- # draw boxes around faces
- for rect in [face['bounds'] for face in faces]:
- top_left = (int(round(rect['left']*W/aw)),
- int(round(rect['top']*H/ah)))
- bot_rght = (int(round(rect['right']*W/aw)),
- int(round(rect['bottom']*H/ah)))
- cv2.rectangle(img, top_left, bot_rght, (0, 1, 0), 2)
- img_name = '%s_fd_mode_%s.jpg' % (NAME, fd_mode)
- its.image.write_image(img, img_name)
- assert fnd_faces == NUM_FACES
- if not faces:
- continue
-
- print 'Frame %d face metadata:' % i
- print ' Faces:', faces
- print ''
-
- # Reasonable scores for faces
- face_scores = [face['score'] for face in faces]
- for score in face_scores:
- assert score >= 1 and score <= 100
- # Face bounds should be within active array
- face_rectangles = [face['bounds'] for face in faces]
- for rect in face_rectangles:
- assert rect['top'] < rect['bottom']
- assert rect['left'] < rect['right']
- assert 0 <= rect['top'] <= ah
- assert 0 <= rect['bottom'] <= ah
- assert 0 <= rect['left'] <= aw
- assert 0 <= rect['right'] <= aw
-
-if __name__ == '__main__':
- main()
diff --git a/apps/CameraITS/tests/scene2c/test_num_faces.py b/apps/CameraITS/tests/scene2c/test_num_faces.py
deleted file mode 100644
index 044c154..0000000
--- a/apps/CameraITS/tests/scene2c/test_num_faces.py
+++ /dev/null
@@ -1,100 +0,0 @@
-# Copyright 2014 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import os.path
-import cv2
-import its.caps
-import its.device
-import its.image
-import its.objects
-
-NAME = os.path.basename(__file__).split('.')[0]
-NUM_TEST_FRAMES = 20
-NUM_FACES = 3
-FD_MODE_OFF = 0
-FD_MODE_SIMPLE = 1
-FD_MODE_FULL = 2
-W, H = 640, 480
-
-
-def main():
- """Test face detection."""
- with its.device.ItsSession() as cam:
- props = cam.get_camera_properties()
- its.caps.skip_unless(its.caps.face_detect(props))
- mono_camera = its.caps.mono_camera(props)
- fd_modes = props['android.statistics.info.availableFaceDetectModes']
- a = props['android.sensor.info.activeArraySize']
- aw, ah = a['right'] - a['left'], a['bottom'] - a['top']
-
- if its.caps.read_3a(props):
- _, _, _, _, _ = cam.do_3a(get_results=True,
- mono_camera=mono_camera)
-
- for fd_mode in fd_modes:
- assert FD_MODE_OFF <= fd_mode <= FD_MODE_FULL
- req = its.objects.auto_capture_request()
- req['android.statistics.faceDetectMode'] = fd_mode
- fmt = {'format': 'yuv', 'width': W, 'height': H}
- caps = cam.do_capture([req]*NUM_TEST_FRAMES, fmt)
- for i, cap in enumerate(caps):
- md = cap['metadata']
- assert md['android.statistics.faceDetectMode'] == fd_mode
- faces = md['android.statistics.faces']
-
- # 0 faces should be returned for OFF mode
- if fd_mode == FD_MODE_OFF:
- assert not faces
- continue
- # Face detection could take several frames to warm up,
- # but should detect the correct number of faces in last frame
- if i == NUM_TEST_FRAMES - 1:
- img = its.image.convert_capture_to_rgb_image(cap,
- props=props)
- fnd_faces = len(faces)
- print 'Found %d face(s), expected %d.' % (fnd_faces,
- NUM_FACES)
- # draw boxes around faces
- for rect in [face['bounds'] for face in faces]:
- top_left = (int(round(rect['left']*W/aw)),
- int(round(rect['top']*H/ah)))
- bot_rght = (int(round(rect['right']*W/aw)),
- int(round(rect['bottom']*H/ah)))
- cv2.rectangle(img, top_left, bot_rght, (0, 1, 0), 2)
- img_name = '%s_fd_mode_%s.jpg' % (NAME, fd_mode)
- its.image.write_image(img, img_name)
- assert fnd_faces == NUM_FACES
- if not faces:
- continue
-
- print 'Frame %d face metadata:' % i
- print ' Faces:', faces
- print ''
-
- # Reasonable scores for faces
- face_scores = [face['score'] for face in faces]
- for score in face_scores:
- assert score >= 1 and score <= 100
- # Face bounds should be within active array
- face_rectangles = [face['bounds'] for face in faces]
- for rect in face_rectangles:
- assert rect['top'] < rect['bottom']
- assert rect['left'] < rect['right']
- assert 0 <= rect['top'] <= ah
- assert 0 <= rect['bottom'] <= ah
- assert 0 <= rect['left'] <= aw
- assert 0 <= rect['right'] <= aw
-
-if __name__ == '__main__':
- main()
diff --git a/apps/CameraITS/tests/scene3/scene3_0.5_scaled.pdf b/apps/CameraITS/tests/scene3/scene3_0.5x_scaled.pdf
similarity index 100%
rename from apps/CameraITS/tests/scene3/scene3_0.5_scaled.pdf
rename to apps/CameraITS/tests/scene3/scene3_0.5x_scaled.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene3/scene3_0.67_scaled.pdf b/apps/CameraITS/tests/scene3/scene3_0.67x_scaled.pdf
similarity index 100%
rename from apps/CameraITS/tests/scene3/scene3_0.67_scaled.pdf
rename to apps/CameraITS/tests/scene3/scene3_0.67x_scaled.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene4/scene4_0.5_scaled.pdf b/apps/CameraITS/tests/scene4/scene4_0.5x_scaled.pdf
similarity index 100%
rename from apps/CameraITS/tests/scene4/scene4_0.5_scaled.pdf
rename to apps/CameraITS/tests/scene4/scene4_0.5x_scaled.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene4/scene4_0.67_scaled.pdf b/apps/CameraITS/tests/scene4/scene4_0.67x_scaled.pdf
similarity index 100%
rename from apps/CameraITS/tests/scene4/scene4_0.67_scaled.pdf
rename to apps/CameraITS/tests/scene4/scene4_0.67x_scaled.pdf
Binary files differ
diff --git a/apps/CameraITS/tools/load_scene.py b/apps/CameraITS/tools/load_scene.py
index e25a3f5..97b6ead 100644
--- a/apps/CameraITS/tools/load_scene.py
+++ b/apps/CameraITS/tools/load_scene.py
@@ -21,6 +21,8 @@
import its.cv2image
import numpy as np
+LOAD_SCENE_DELAY = 2 # seconds
+
def main():
"""Load charts on device and display."""
@@ -51,10 +53,10 @@
dst_scene_file = '/sdcard/Download/%s.pdf' % scene
chart_scaling = its.cv2image.calc_chart_scaling(chart_distance, camera_fov)
if np.isclose(chart_scaling, its.cv2image.SCALE_TELE_IN_WFOV_BOX, atol=0.01):
- file_name = '%s_%s_scaled.pdf' % (
+ file_name = '%s_%sx_scaled.pdf' % (
scene, str(its.cv2image.SCALE_TELE_IN_WFOV_BOX))
elif np.isclose(chart_scaling, its.cv2image.SCALE_RFOV_IN_WFOV_BOX, atol=0.01):
- file_name = '%s_%s_scaled.pdf' % (
+ file_name = '%s_%sx_scaled.pdf' % (
scene, str(its.cv2image.SCALE_RFOV_IN_WFOV_BOX))
else:
file_name = '%s.pdf' % scene
@@ -63,7 +65,7 @@
cmd = 'adb -s %s push %s /mnt%s' % (screen_id, src_scene_file,
dst_scene_file)
subprocess.Popen(cmd.split())
- time.sleep(1) # wait-for-device doesn't always seem to work...
+ time.sleep(LOAD_SCENE_DELAY) # wait-for-device doesn't always seem to work
# The intent require PDF viewing app be installed on device.
# Also the first time such app is opened it might request some permission,
# so it's better to grant those permissions before using this script
diff --git a/apps/CameraITS/tools/run_all_tests.py b/apps/CameraITS/tools/run_all_tests.py
index c2f4fef..c5c8077 100644
--- a/apps/CameraITS/tools/run_all_tests.py
+++ b/apps/CameraITS/tools/run_all_tests.py
@@ -29,8 +29,6 @@
from its.device import ItsSession
import its.image
-import numpy as np
-
# For sanity checking the installed APK's target SDK version
MIN_SUPPORTED_SDK_VERSION = 28 # P
@@ -42,6 +40,7 @@
CHART_SCALE_STOP = 1.35
CHART_SCALE_STEP = 0.025
FACING_EXTERNAL = 2
+NOT_YET_MANDATED_ALL = 100
NUM_TRYS = 2
PROC_TIMEOUT_CODE = -101 # terminated process return -process_id
PROC_TIMEOUT_TIME = 900 # timeout in seconds for a process (15 minutes)
@@ -51,21 +50,63 @@
VGA_HEIGHT = 480
VGA_WIDTH = 640
-# Not yet mandated tests
+# All possible scenes
+# Notes on scene names:
+# scene*_1/2/... are same scene split to load balance run times for scenes
+# scene*_a/b/... are similar scenes that share one or more tests
+ALL_SCENES = ['scene0', 'scene1_1', 'scene1_2', 'scene2_a', 'scene2_b',
+ 'scene2_c', 'scene3', 'scene4', 'scene5', 'sensor_fusion']
+
+# Scenes that are logically grouped and can be called as group
+GROUPED_SCENES = {
+ 'scene1': ['scene1_1', 'scene1_2'],
+ 'scene2': ['scene2_a', 'scene2_b', 'scene2_c']
+}
+
+# Scenes that can be automated through tablet display
+AUTO_SCENES = ['scene0', 'scene1_1', 'scene1_2', 'scene2_a', 'scene2_b',
+ 'scene2_c', 'scene3', 'scene4']
+
+SCENE_REQ = {
+ 'scene0': None,
+ 'scene1_1': 'A grey card covering at least the middle 30% of the scene',
+ 'scene1_2': 'A grey card covering at least the middle 30% of the scene',
+ 'scene2_a': 'The picture in tests/scene2_a.pdf with 3 faces',
+ 'scene2_b': 'The picture in tests/scene2_b.pdf with 3 faces',
+ 'scene2_c': 'The picture in tests/scene2_c.pdf with 3 faces',
+ 'scene3': 'The ISO 12233 chart',
+ 'scene4': 'A specific test page of a circle covering at least the '
+ 'middle 50% of the scene. See CameraITS.pdf section 2.3.4 '
+ 'for more details',
+ 'scene5': 'Capture images with a diffuser attached to the camera. See '
+ 'CameraITS.pdf section 2.3.4 for more details',
+ 'sensor_fusion': 'Rotating checkboard pattern. See '
+ 'sensor_fusion/SensorFusion.pdf for detailed '
+ 'instructions.\nNote that this test will be skipped '
+ 'on devices not supporting REALTIME camera timestamp.'
+}
+
+SCENE_EXTRA_ARGS = {
+ 'scene5': ['doAF=False']
+}
+
+# Not yet mandated tests ['test', first_api_level mandatory]
+# ie. ['test_test_patterns', 28] is MANDATED for first_api_level >= 28
NOT_YET_MANDATED = {
'scene0': [
- 'test_test_patterns',
- 'test_tonemap_curve'
+ ['test_test_patterns', 28],
+ ['test_tonemap_curve', 28]
],
- 'scene1': [
- 'test_ae_precapture_trigger',
- 'test_channel_saturation'
+ 'scene1_1': [
+ ['test_ae_precapture_trigger', 28],
+ ['test_channel_saturation', 29]
],
- 'scene2': [
- 'test_auto_per_frame_control'
+ 'scene1_2': [],
+ 'scene2_a': [
+ ['test_auto_per_frame_control', NOT_YET_MANDATED_ALL]
],
- 'scene2b': [],
- 'scene2c': [],
+ 'scene2_b': [],
+ 'scene2_c': [],
'scene3': [],
'scene4': [],
'scene5': [],
@@ -80,19 +121,21 @@
'test_read_write',
'test_sensor_events'
],
- 'scene1': [
+ 'scene1_1': [
'test_exposure',
'test_dng_noise_model',
'test_linearity',
+ ],
+ 'scene1_2': [
'test_raw_exposure',
'test_raw_sensitivity'
],
- 'scene2': [
+ 'scene2_a': [
'test_faces',
'test_num_faces'
],
- 'scene2b': [],
- 'scene2c': [],
+ 'scene2_b': [],
+ 'scene2_c': [],
'scene3': [],
'scene4': [
'test_aspect_ratio_and_crop'
@@ -103,6 +146,67 @@
]
}
+# Tests run in more than 1 scene.
+# List is created of type ['scene_source', 'test_to_be_repeated']
+# for the test run in current scene.
+REPEATED_TESTS = {
+ 'scene0': [],
+ 'scene1_1': [],
+ 'scene1_2': [],
+ 'scene2_a': [],
+ 'scene2_b': [
+ ['scene2_a', 'test_num_faces']
+ ],
+ 'scene2_c': [
+ ['scene2_a', 'test_num_faces']
+ ],
+ 'scene3': [],
+ 'scene4': [],
+ 'scene5': [],
+ 'sensor_fusion': []
+}
+
+
+def determine_not_yet_mandated_tests(device_id):
+ """Determine from NEW_YET_MANDATED & phone info not_yet_mandated tests.
+
+ Args:
+ device_id: string of device id number
+
+ Returns:
+ dict of not yet mandated tests
+ """
+ # initialize not_yet_mandated
+ not_yet_mandated = {}
+ for scene in ALL_SCENES:
+ not_yet_mandated[scene] = []
+
+ # Determine first API level for device
+ first_api_level = its.device.get_first_api_level(device_id)
+
+ # Determine which scenes are not yet mandated for first api level
+ for scene, tests in NOT_YET_MANDATED.items():
+ for test in tests:
+ if test[1] >= first_api_level:
+ not_yet_mandated[scene].append(test[0])
+ return not_yet_mandated
+
+
+def expand_scene(scene, scenes):
+ """Expand a grouped scene and append its sub_scenes to scenes.
+
+ Args:
+ scene: scene in GROUPED_SCENES dict
+ scenes: list of scenes to append to
+
+ Returns:
+ updated scenes
+ """
+ print 'Expanding %s to %s.' % (scene, str(GROUPED_SCENES[scene]))
+ for sub_scene in GROUPED_SCENES[scene]:
+ scenes.append(sub_scene)
+
+
def run_subprocess_with_timeout(cmd, fout, ferr, outdir):
"""Run subprocess with a timeout.
@@ -191,50 +295,24 @@
camera Ids. Ex: "camera=0,1" or "camera=1"
device: device id for adb
scenes: the test scene(s) to be executed. Use comma to separate
- multiple scenes. Ex: "scenes=scene0,scene1" or
- "scenes=0,1,sensor_fusion" (sceneX can be abbreviated by X
- where X is a integer)
- chart: [Experimental] another android device served as test chart
- display. When this argument presents, change of test scene
+ multiple scenes. Ex: "scenes=scene0,scene1_1" or
+ "scenes=0,1_1,sensor_fusion" (sceneX can be abbreviated by X
+ where X is scene name minus 'scene')
+ chart: another android device served as test chart display.
+ When this argument presents, change of test scene
will be handled automatically. Note that this argument
requires special physical/hardware setup to work and may not
work on all android devices.
result: Device ID to forward results to (in addition to the device
that the tests are running on).
- rot_rig: [Experimental] ID of the rotation rig being used (formatted as
+ rot_rig: ID of the rotation rig being used (formatted as
"<vendor ID>:<product ID>:<channel #>" or "default")
tmp_dir: location of temp directory for output files
skip_scene_validation: force skip scene validation. Used when test scene
is setup up front and don't require tester validation.
- dist: [Experimental] chart distance in cm.
+ dist: chart distance in cm.
"""
- all_scenes = ["scene0", "scene1", "scene2", "scene2b", "scene2c", "scene3", "scene4", "scene5",
- "sensor_fusion"]
-
- auto_scenes = ["scene0", "scene1", "scene2", "scene2b", "scene2c", "scene3", "scene4"]
-
- scene_req = {
- "scene0": None,
- "scene1": "A grey card covering at least the middle 30% of the scene",
- "scene2": "A picture containing human faces",
- "scene2b": "A picture containing human faces",
- "scene2c": "A picture containing human faces",
- "scene3": "The ISO 12233 chart",
- "scene4": "A specific test page of a circle covering at least the "
- "middle 50% of the scene. See CameraITS.pdf section 2.3.4 "
- "for more details",
- "scene5": "Capture images with a diffuser attached to the camera. See "
- "CameraITS.pdf section 2.3.4 for more details",
- "sensor_fusion": "Rotating checkboard pattern. See "
- "sensor_fusion/SensorFusion.pdf for detailed "
- "instructions.\nNote that this test will be skipped "
- "on devices not supporting REALTIME camera timestamp."
- }
- scene_extra_args = {
- "scene5": ["doAF=False"]
- }
-
camera_id_combos = []
scenes = []
chart_host_id = None
@@ -275,7 +353,7 @@
merge_result_switch = result_device_id is not None
# Run through all scenes if user does not supply one
- possible_scenes = auto_scenes if auto_scene_switch else all_scenes
+ possible_scenes = AUTO_SCENES if auto_scene_switch else ALL_SCENES
if not scenes:
scenes = possible_scenes
else:
@@ -285,14 +363,19 @@
for s in scenes:
if s in possible_scenes:
temp_scenes.append(s)
+ elif GROUPED_SCENES.has_key(s):
+ expand_scene(s, temp_scenes)
else:
try:
# Try replace "X" to "sceneX"
scene_str = "scene" + s
- if scene_str not in possible_scenes:
+ if scene_str in possible_scenes:
+ temp_scenes.append(scene_str)
+ elif GROUPED_SCENES.has_key(scene_str):
+ expand_scene(scene_str, temp_scenes)
+ else:
valid_scenes = False
break
- temp_scenes.append(scene_str)
except ValueError:
valid_scenes = False
break
@@ -300,7 +383,8 @@
if not valid_scenes:
print 'Unknown scene specified:', s
assert False
- scenes = temp_scenes
+ # assign temp_scenes back to scenes and remove duplicates
+ scenes = sorted(set(temp_scenes), key=temp_scenes.index)
# Make output directories to hold the generated files.
topdir = tempfile.mkdtemp(dir=tmp_dir)
@@ -395,11 +479,11 @@
# Initialize test results
results = {}
result_key = ItsSession.RESULT_KEY
- for s in all_scenes:
+ for s in ALL_SCENES:
results[s] = {result_key: ItsSession.RESULT_NOT_EXECUTED}
camera_fov = calc_camera_fov(id_combo.id, id_combo.sub_id)
- id_combo_string = id_combo.id;
+ id_combo_string = id_combo.id
has_hidden_sub_camera = id_combo.sub_id is not None
if has_hidden_sub_camera:
id_combo_string += ":" + id_combo.sub_id
@@ -414,11 +498,15 @@
tot_tests = []
tot_pass = 0
+ not_yet_mandated = determine_not_yet_mandated_tests(device_id)
for scene in scenes:
skip_code = None
- tests = [(s[:-3], os.path.join("tests", scene, s))
- for s in os.listdir(os.path.join("tests", scene))
- if s[-3:] == ".py" and s[:4] == "test"]
+ tests = [(s[:-3], os.path.join('tests', scene, s))
+ for s in os.listdir(os.path.join('tests', scene))
+ if s[-3:] == '.py' and s[:4] == 'test']
+ if REPEATED_TESTS[scene]:
+ for t in REPEATED_TESTS[scene]:
+ tests.append((t[1], os.path.join('tests', t[0], t[1]+'.py')))
tests.sort()
tot_tests.extend(tests)
@@ -428,7 +516,7 @@
num_not_mandated_fail = 0
numfail = 0
validate_switch = True
- if scene_req[scene] is not None:
+ if SCENE_REQ[scene] is not None:
out_path = os.path.join(topdir, id_combo_string, scene+".jpg")
out_arg = "out=" + out_path
if scene == 'sensor_fusion':
@@ -451,8 +539,8 @@
else:
# Skip scene validation under certain conditions
if validate_switch and not merge_result_switch:
- scene_arg = 'scene=' + scene_req[scene]
- extra_args = scene_extra_args.get(scene, [])
+ scene_arg = 'scene=' + SCENE_REQ[scene]
+ extra_args = SCENE_EXTRA_ARGS.get(scene, [])
cmd = ['python',
os.path.join(os.getcwd(),
'tools/validate_scene.py'),
@@ -536,7 +624,7 @@
elif test_code == SKIP_RET_CODE:
retstr = "SKIP "
numskip += 1
- elif test_code != 0 and testname in NOT_YET_MANDATED[scene]:
+ elif test_code != 0 and testname in not_yet_mandated[scene]:
retstr = "FAIL*"
num_not_mandated_fail += 1
else:
diff --git a/apps/CtsVerifier/Android.mk b/apps/CtsVerifier/Android.mk
index af6dc5b..15ddafa 100644
--- a/apps/CtsVerifier/Android.mk
+++ b/apps/CtsVerifier/Android.mk
@@ -56,6 +56,7 @@
LOCAL_JAVA_LIBRARIES += android.test.runner.stubs
LOCAL_JAVA_LIBRARIES += android.test.base.stubs
LOCAL_JAVA_LIBRARIES += android.test.mock.stubs
+LOCAL_JAVA_LIBRARIES += android.car
LOCAL_JAVA_LIBRARIES += voip-common
LOCAL_PACKAGE_NAME := CtsVerifier
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 238d522..d752ab6 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -22,6 +22,7 @@
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="29"/>
+ <uses-permission android:name="android.car.permission.CAR_POWERTRAIN" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"/>
@@ -159,6 +160,17 @@
android:theme="@style/OverlayTheme"
android:label="Overlaying Activity"/>
+ <activity
+ android:name=".battery.BatterySaverTestActivity"
+ android:label="@string/battery_saver_test"
+ android:configChanges="keyboardHidden|orientation|screenSize">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.cts.intent.category.MANUAL_TEST" />
+ </intent-filter>
+ <meta-data android:name="test_category" android:value="@string/test_category_other" />
+ </activity>
+
<activity android:name=".forcestop.RecentTaskRemovalTestActivity"
android:label="@string/remove_from_recents_test"
android:configChanges="keyboardHidden|orientation|screenSize">
@@ -1308,6 +1320,16 @@
android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch" />
</activity>
+ <activity android:name=".security.ProtectedConfirmationTest"
+ android:label="@string/sec_protected_confirmation_test"
+ android:configChanges="keyboardHidden|orientation|screenSize" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.cts.intent.category.MANUAL_TEST" />
+ </intent-filter>
+ <meta-data android:name="test_category" android:value="@string/test_category_security" />
+ </activity>
+
<activity android:name=".security.ScreenLockBoundKeysTest"
android:label="@string/sec_lock_bound_key_test"
android:configChanges="keyboardHidden|orientation|screenSize" >
@@ -2074,12 +2096,26 @@
</intent-filter>
</receiver>
+ <receiver android:name=".notifications.AutomaticZenRuleStatusReceiver">
+ <intent-filter>
+ <action android:name="android.app.action.AUTOMATIC_ZEN_RULE_STATUS_CHANGED"/>
+ </intent-filter>
+ </receiver>
+
<activity android:name=".notifications.ConditionProviderVerifierActivity"
android:label="@string/cp_test">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.cts.intent.category.MANUAL_TEST" />
</intent-filter>
+ <intent-filter>
+ <action android:name="android.app.action.AUTOMATIC_ZEN_RULE" />
+ </intent-filter>
+ <meta-data android:name="android.service.zen.automatic.ruleType"
+ android:value="@string/cp_rule_type" />
+ <meta-data android:name="android.service.zen.automatic.ruleInstanceLimit"
+ android:value="2" />
+
<meta-data android:name="test_category" android:value="@string/test_category_notifications" />
<meta-data android:name="test_excluded_features"
android:value="android.hardware.type.automotive:android.hardware.type.television:android.software.leanback:android.hardware.type.watch" />
@@ -3500,6 +3536,30 @@
</intent-filter>
</activity-alias>
+ <activity android:name=".car.GearSelectionTestActivity"
+ android:label="@string/gear_selection_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_car" />
+ <meta-data
+ android:name="test_required_features"
+ android:value="android.hardware.type.automotive"/>
+ </activity>
+
+ <activity android:name=".car.ParkingBrakeOnTestActivity"
+ android:label="@string/parking_brake_on_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_car" />
+ <meta-data
+ android:name="test_required_features"
+ android:value="android.hardware.type.automotive"/>
+ </activity>
+
<!-- 6DoF sensor test -->
<activity
android:name="com.android.cts.verifier.sensors.sixdof.Activities.StartActivity"
diff --git a/apps/CtsVerifier/proguard.flags b/apps/CtsVerifier/proguard.flags
index 85f378e..a05d149 100644
--- a/apps/CtsVerifier/proguard.flags
+++ b/apps/CtsVerifier/proguard.flags
@@ -19,7 +19,7 @@
}
# ensure we keep public camera test methods, these are needed at runtime
--keepclassmembers class * extends android.hardware.camera2.cts.testcases.Camera2AndroidTestCase {
+-keepclassmembers class * extends android.hardware.camera2.cts.testcases.Camera2AndroidBasicTestCase {
public <methods>;
}
diff --git a/apps/CtsVerifier/res/layout/gear_selection_test.xml b/apps/CtsVerifier/res/layout/gear_selection_test.xml
new file mode 100644
index 0000000..20508c1
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/gear_selection_test.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/expected_gear_selection_title"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:text="@string/expected_gear_selection_title"
+ android:textSize="50dip" />
+
+ <TextView
+ android:id="@+id/current_gear_selection_title"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:text="@string/current_gear_selection_title"
+ android:textSize="50dip" />
+
+
+ </LinearLayout>
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="2"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/expected_gear_selection"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:textSize="75dip" />
+
+ <TextView
+ android:id="@+id/current_gear_selection"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:textSize="75dip" />
+
+ </LinearLayout>
+
+ <include layout="@layout/pass_fail_buttons" />
+
+</LinearLayout>
diff --git a/apps/CtsVerifier/res/layout/iva_pass_fail_item.xml b/apps/CtsVerifier/res/layout/iva_pass_fail_item.xml
new file mode 100644
index 0000000..4bf5b63
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/iva_pass_fail_item.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" >
+
+ <ImageView
+ android:id="@+id/nls_status"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginTop="10dip"
+ android:contentDescription="@string/pass_button_text"
+ android:padding="10dip"
+ android:src="@drawable/fs_indeterminate" />
+
+ <TextView
+ android:id="@+id/nls_instructions"
+ style="@style/InstructionsSmallFont"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentTop="true"
+ android:layout_toRightOf="@id/nls_status"
+ android:text="@string/nls_enable_service" />
+
+ <Button
+ android:id="@+id/iva_action_button_pass"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_below="@id/nls_instructions"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_toRightOf="@id/nls_status"
+ android:text="@string/iva_pass"
+ android:onClick="actionPassed" />
+ <Button
+ android:id="@+id/iva_action_button_fail"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_toRightOf="@id/nls_status"
+ android:layout_below="@id/iva_action_button_pass"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:onClick="actionFailed"
+ android:text="@string/iva_fail" />
+
+ <Button
+ android:id="@+id/nls_action_button"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_below="@id/nls_instructions"
+ android:layout_marginLeft="20dip"
+ android:layout_marginRight="20dip"
+ android:layout_toRightOf="@id/nls_status"
+ android:onClick="actionPressed"
+ android:visibility="gone"
+ android:text="@string/nls_start_settings" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/ordered_test.xml b/apps/CtsVerifier/res/layout/ordered_test.xml
new file mode 100644
index 0000000..1c0b029
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/ordered_test.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView
+ android:id="@+id/txt_instruction"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/InstructionsSmallFont"
+ android:layout_alignParentTop="true" />
+
+ <Button
+ android:id="@+id/btn_next"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/next_button_text"
+ android:layout_below="@id/txt_instruction" />
+
+ <include android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ layout="@layout/pass_fail_buttons" />
+</RelativeLayout>
diff --git a/apps/CtsVerifier/res/layout/parking_brake_on_test.xml b/apps/CtsVerifier/res/layout/parking_brake_on_test.xml
new file mode 100644
index 0000000..23faaab
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/parking_brake_on_test.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView
+ android:id="@+id/instruction"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:gravity="center"
+ android:textSize="50dip" />
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="2"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/current_parking_brake_on_value_title"
+ android:layout_width="0dp"
+ android:layout_weight="2"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:text="@string/current_parking_brake_on_value_title"
+ android:textSize="50dip" />
+
+ <TextView
+ android:id="@+id/current_parking_brake_on_value"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:textSize="50dip" />
+
+ </LinearLayout>
+
+ <include layout="@layout/pass_fail_buttons" />
+
+</LinearLayout>
diff --git a/apps/CtsVerifier/res/layout/screen_pinning.xml b/apps/CtsVerifier/res/layout/screen_pinning.xml
deleted file mode 100644
index f1d7b65..0000000
--- a/apps/CtsVerifier/res/layout/screen_pinning.xml
+++ /dev/null
@@ -1,62 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
-
- <TextView
- android:id="@+id/error_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
-
- <ScrollView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentTop="true" >
-
- <RelativeLayout
- android:id="@+id/instructions_group"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical" >
-
- <LinearLayout
- android:id="@+id/instructions_list"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="vertical" >
-
- </LinearLayout>
-
- <Button
- android:id="@+id/next_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentEnd="true"
- android:layout_below="@id/instructions_list"
- android:text="@string/next_button_text" />
-
- </RelativeLayout>
- </ScrollView>
-
- <include
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- layout="@layout/pass_fail_buttons" />
-
-</RelativeLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/sec_protected_confirmation_main.xml b/apps/CtsVerifier/res/layout/sec_protected_confirmation_main.xml
new file mode 100644
index 0000000..ffa8d46
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/sec_protected_confirmation_main.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:padding="10dip"
+ >
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/sec_protected_confirmation_tee_layout"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="10dp"
+ android:orientation="horizontal"
+ android:layout_centerInParent="true"
+ android:gravity="center"
+ >
+
+ <Button android:id="@+id/sec_start_test_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/sec_protected_confirmation_tee_test"
+ />
+
+ <ImageView android:id="@+id/sec_protected_confirmation_tee_test_success"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/fs_good"
+ />
+
+ </LinearLayout>
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/sec_protected_confirmation_strongbox_layout"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="10dp"
+ android:orientation="horizontal"
+ android:layout_centerHorizontal="true"
+ android:layout_below="@id/sec_protected_confirmation_tee_layout"
+ android:gravity="center"
+ >
+
+ <Button android:id="@+id/sec_start_test_strongbox_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/sec_protected_confirmation_strongbox_test"
+ />
+
+ <ImageView android:id="@+id/sec_protected_confirmation_strongbox_test_success"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/fs_good"
+ />
+
+ </LinearLayout>
+
+ <include android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ layout="@layout/pass_fail_buttons"
+ />
+
+</RelativeLayout>
+
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 9a93fbf..2be358b 100755
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -23,6 +23,7 @@
<string name="fail_button_text">Fail</string>
<string name="next_button_text">Next</string>
<string name="go_button_text">Go</string>
+ <string name="tests_completed_successfully">All tests completed successfully.</string>
<string name="retry_button_text">Retry</string>
<string name="finish_button_text">Finish</string>
@@ -76,6 +77,35 @@
<string name="bu_generate_error">Error occurred while generating test data...</string>
<string name="bu_settings">Settings</string>
+ <!-- Strings for BatterySaverTestActivity -->
+ <string name="battery_saver_test">Battery Saver Test</string>
+ <string name="battery_saver_test_info">
+ This test verifies that the device provides a user affordance to enable and disable
+ Battery Saver feature.
+ </string>
+ <string name="battery_saver_test_no_battery_detected">
+ No battery detected. This test will only work on devices that contain a battery.
+ </string>
+ <string name="battery_saver_test_unplug">
+ Unplug the device or run \"adb shell dumpsys battery unplug\".
+ </string>
+ <string name="battery_saver_test_device_plugged_in">
+ The device is still plugged in.
+ </string>
+ <string name="battery_saver_test_enable_bs">
+ Look for a button that allows you to turn Battery Saver on and off. The button may be in the
+ Quick Settings section or on the Battery page of device settings. Use the button to turn
+ Battery Saver on.
+ </string>
+ <string name="battery_saver_test_disable_bs">
+ Now use the button to turn Battery Saver off.
+ </string>
+ <string name="battery_saver_test_start_turn_bs_off">
+ Turn Battery Saver off to begin the test.
+ </string>
+ <string name="battery_saver_test_bs_on">Battery Saver is on.</string>
+ <string name="battery_saver_test_bs_off">Battery Saver is off.</string>
+
<!-- Strings for Device Administration tests -->
<string name="da_policy_serialization_test">Policy Serialization Test</string>
<string name="da_policy_serialization_info">This test checks that a device policy is properly
@@ -104,6 +134,19 @@
framework correctly tries to open the CAR_DOCK app again.</string>
<string name="car_mode_enable">Enable Car Mode</string>
<string name="car_dock_activity_text">Press the Home button</string>
+ <string name="gear_selection_test">Gear Selection Test</string>
+ <string name="gear_selection_test_desc">This test ensures that the
+ GEAR_SELECTION property is implemented correctly.\n\nShift the car\'s
+ gear to the expected gear value. If it is able to shift to all the
+ implemented gears then the pass button will be enabled.</string>
+ <string name="expected_gear_selection_title">Expected Gear Selection</string>
+ <string name="current_gear_selection_title">Current Gear Selection</string>
+ <string name="parking_brake_on_test">PARKING_BRAKE_ON Test</string>
+ <string name="parking_brake_on_test_desc">This test ensures that the
+ PARKING_BRAKE_ON property is implemented correctly.\n\nFollow the
+ instructions on the screen to engage and disengage the parking brake. When
+ the instructions are completed, the pass button will be enabled.</string>
+ <string name="current_parking_brake_on_value_title">Current PARKING_BRAKE_ON Value:</string>
<string name="da_no_policy">1. Press the \"Generate Policy\" to create a random device
policy\n\n2. Press \"Apply Policy\" to put the policy into effect.\n\n3. Reboot your
device and return to this test in the CTS Verifier.
@@ -217,6 +260,23 @@
are unusable without an authentication.
</string>
+ <!-- Strings for protected confirmation test -->
+ <string name="sec_protected_confirmation_test">Android Protected Confirmation Test</string>
+ <string name="sec_protected_confirmation_test_info">
+ This test ensures that - if Android Protected Confirmation is supported by the device under
+ test - the user is able to see the confirmation message and confirm it. It also assures that
+ Keymaster signs a message with a confirmation key only if the message was previously
+ confirmed by the user.
+ </string>
+ <string name="sec_protected_confirmation_not_supported_title">Android Protected Confirmation not supported</string>
+ <string name="sec_protected_confirmation_not_supported_info">
+ Android Protected Confirmation is not implemented by this device. This is okay because this
+ is an optional feature. Set this test to passed and continue.
+ </string>
+ <string name="sec_protected_confirmation_message">This is a CtsVerifier test message!</string>
+ <string name="sec_protected_confirmation_tee_test">Start Tee Test</string>
+ <string name="sec_protected_confirmation_strongbox_test">Start Strongbox Test</string>
+
<!-- Strings for BluetoothActivity -->
<string name="bluetooth_test">Bluetooth Test</string>
<string name="bluetooth_test_info">
@@ -2010,9 +2070,17 @@
<string name="cp_service_started">Service should start once enabled.</string>
<string name="cp_service_stopped">Service should stop once disabled.</string>
<string name="cp_unsubscribe_rule">Unsubscribing to Automatic Zen Rule</string>
- <string name="cp_delete_rule">Deleting Automatic Zen Rule</string>
+ <string name="cp_delete_rule">Deleting Automatic Zen Rule via api</string>
<string name="cp_get_rules">Retrieving Automatic Zen Rules</string>
<string name="cp_get_rule">Retrieving Automatic Zen Rule</string>
+ <string name="cp_show_rules">Click this button to launch the Automatic Zen Rule listing page in settings, and then return to this screen</string>
+ <string name="cp_show_rules_verification">Was the automatic rule screen shown?</string>
+ <string name="cp_disable_rule">Please disable rule "123" and return here.</string>
+ <string name="cp_enable_rule">Please enable rule "123" and return here.</string>
+ <string name="cp_delete_rule_broadcast">Please delete rule "123" and return here.</string>
+ <string name="cp_rule_type">CTS rule</string>
+ <string name="iva_pass">Pass</string>
+ <string name="iva_fail">Fail</string>
<string name="cacert_test">CA Cert Notification Test</string>
<string name="cacert_info">This test checks that when a CA Certificate is installed, the user is notified.</string>
@@ -4342,7 +4410,6 @@
<string name="screen_pin_check_pinned">Press Next to verify the app is pinned.</string>
<string name="screen_pin_no_exit">Try to leave the app without unpinning the screen. Press next once you have verified you cannot leave.</string>
<string name="screen_pin_exit">Use interactions defined by your device to unpin such as long pressing the back and overview button, then press next.</string>
- <string name="screen_pinning_done">All tests completed successfully.</string>
<string name="error_screen_no_longer_pinned">The screen was no longer pinned.</string>
<string name="error_screen_already_pinned">Cannot start the test with the screen already pinned.</string>
@@ -5103,10 +5170,15 @@
<string name="bubbles_notification_test_verify_9">Click the button below and verify that a bubble
appears on screen, auto-expanded.</string>
<string name="bubbles_notification_test_button_9">Send auto-expanded bubble notification</string>
- <string name="bubbles_notification_no_bubbles_low_mem">No bubbles on low memory device; no tests to run</string>
+ <string name="bubbles_notification_test_title_10">No bubbles on low memory device</string>
+ <string name="bubbles_notification_test_verify_10">Click the button below and verify that a
+ bubble does NOT appear on screen. Verify that there is a notification in the notification
+ shade.</string>
+ <string name="bubbles_notification_test_button_10">Add bubble</string>
<string name="bubbles_test_summary_title">Test Complete</string>
<string name="bubbles_test_summary">%1$d out of %2$d tests passed</string>
<string name="bubble_activity_title">Bubble Activity</string>
+
<!-- Strings for Instant Apps -->
<string name="ia_instruction_heading_label">Instructions:</string>
<string name="ia_instruction_text_photo_label">READ BEFORE STARTING TEST</string>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/OrderedTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/OrderedTestActivity.java
new file mode 100644
index 0000000..d24fdb7
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/OrderedTestActivity.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.verifier;
+
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.Toast;
+
+/**
+ * {@link PassFailButtons.Activity} that supports showing a series of tests in order.
+ */
+public abstract class OrderedTestActivity extends PassFailButtons.Activity {
+ private static final String KEY_CURRENT_TEST = "current_test";
+
+ private Test[] mTests;
+ private int mTestIndex;
+
+ private Button mNextButton;
+ private TextView mInstructions;
+
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.ordered_test);
+ setPassFailButtonClickListeners();
+
+ mTests = getTests();
+
+ mInstructions = findViewById(R.id.txt_instruction);
+
+ mNextButton = findViewById(R.id.btn_next);
+ mNextButton.setOnClickListener(v -> {
+ if ((mTestIndex >= 0) && (mTestIndex < mTests.length)) {
+ mTests[mTestIndex].onNextClick();
+ }
+ });
+
+ // Don't allow pass until all tests complete.
+ findViewById(R.id.pass_button).setVisibility(View.GONE);
+
+ // Figure out if we are in a test or starting from the beginning.
+ if (savedInstanceState != null && savedInstanceState.containsKey(KEY_CURRENT_TEST)) {
+ mTestIndex = savedInstanceState.getInt(KEY_CURRENT_TEST);
+ } else {
+ mTestIndex = 0;
+ }
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+
+ mTests[mTestIndex].run();
+ }
+
+ /** Returns a list of tests to run in order. */
+ protected abstract Test[] getTests();
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+
+ outState.putInt(KEY_CURRENT_TEST, mTestIndex);
+ }
+
+ protected void succeed() {
+ runOnUiThread(() -> {
+ mTestIndex++;
+ if (mTestIndex < mTests.length) {
+ mTests[mTestIndex].run();
+ } else {
+ // On test completion, hide "next" and "fail" buttons, and show "pass" button
+ // instead.
+ mInstructions.setText(R.string.tests_completed_successfully);
+ mNextButton.setVisibility(View.GONE);
+ findViewById(R.id.pass_button).setVisibility(View.VISIBLE);
+ findViewById(R.id.fail_button).setVisibility(View.GONE);
+ }
+ });
+ }
+
+ protected void error(int stringResId) {
+ Toast.makeText(this, stringResId, Toast.LENGTH_SHORT).show();
+ }
+
+ protected abstract class Test {
+ private final int mStringId;
+
+ public Test(int stringResId) {
+ mStringId = stringResId;
+ }
+
+ protected void run() {
+ showText();
+ }
+
+ protected void showText() {
+ if (mStringId == 0) {
+ return;
+ }
+ mInstructions.setText(mStringId);
+ }
+
+ protected void onNextClick() {
+ }
+ }
+
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/battery/BatterySaverTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/battery/BatterySaverTestActivity.java
new file mode 100644
index 0000000..f0dc540
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/battery/BatterySaverTestActivity.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.battery;
+
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.BatteryManager;
+import android.os.Bundle;
+import android.os.PowerManager;
+import android.view.View;
+
+import com.android.cts.verifier.OrderedTestActivity;
+import com.android.cts.verifier.R;
+
+public class BatterySaverTestActivity extends OrderedTestActivity {
+ private PowerManager mPowerManager;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setInfoResources(R.string.battery_saver_test, R.string.battery_saver_test_info, -1);
+
+ mPowerManager = getSystemService(PowerManager.class);
+ }
+
+ @Override
+ protected OrderedTestActivity.Test[] getTests() {
+ return new Test[]{
+ // Confirm a battery exists before proceeding with the rest of the tests,
+ mConfirmBatteryExists,
+ // Verify device is unplugged.
+ mConfirmUnplugged,
+ // Verify battery saver is off.
+ mConfirmBsOff,
+ mEnableBs,
+ mDisableBs
+ };
+ }
+
+ private final Test mConfirmBatteryExists = new Test(
+ R.string.battery_saver_test_no_battery_detected) {
+ @Override
+ protected void run() {
+ super.run();
+
+ final Intent batteryInfo = registerReceiver(null, new
+ IntentFilter(Intent.ACTION_BATTERY_CHANGED));
+
+ if (batteryInfo.getBooleanExtra(BatteryManager.EXTRA_PRESENT, true)) {
+ succeed();
+ } else {
+ findViewById(R.id.btn_next).setVisibility(View.GONE);
+ // Set both pass and fail to visible in case the device is meant to have a battery.
+ findViewById(R.id.pass_button).setVisibility(View.VISIBLE);
+ findViewById(R.id.fail_button).setVisibility(View.VISIBLE);
+ }
+ }
+ };
+
+ private final Test mConfirmUnplugged = new Test(R.string.battery_saver_test_unplug) {
+ @Override
+ protected void run() {
+ super.run();
+
+ if (!isPluggedIn()) {
+ succeed();
+ }
+ }
+
+ @Override
+ protected void onNextClick() {
+ if (isPluggedIn()) {
+ error(R.string.battery_saver_test_device_plugged_in);
+ } else {
+ succeed();
+ }
+ }
+
+ private boolean isPluggedIn() {
+ Intent intent = registerReceiver(null,
+ new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
+ int plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
+ return plugged == BatteryManager.BATTERY_PLUGGED_AC
+ || plugged == BatteryManager.BATTERY_PLUGGED_USB
+ || plugged == BatteryManager.BATTERY_PLUGGED_WIRELESS;
+ }
+ };
+
+ private final Test mConfirmBsOff = new Test(R.string.battery_saver_test_start_turn_bs_off) {
+ @Override
+ protected void run() {
+ super.run();
+
+ if (!mPowerManager.isPowerSaveMode()) {
+ succeed();
+ }
+ }
+
+ @Override
+ protected void onNextClick() {
+ if (mPowerManager.isPowerSaveMode()) {
+ error(R.string.battery_saver_test_bs_on);
+ } else {
+ succeed();
+ }
+ }
+ };
+
+ private final Test mEnableBs = new Test(R.string.battery_saver_test_enable_bs) {
+ @Override
+ protected void onNextClick() {
+ if (mPowerManager.isPowerSaveMode()) {
+ succeed();
+ } else {
+ error(R.string.battery_saver_test_bs_off);
+ }
+ }
+ };
+
+ private final Test mDisableBs = new Test(R.string.battery_saver_test_disable_bs) {
+ @Override
+ protected void onNextClick() {
+ if (!mPowerManager.isPowerSaveMode()) {
+ succeed();
+ } else {
+ error(R.string.battery_saver_test_bs_on);
+ }
+ }
+ };
+}
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 7745898..f39b590 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
@@ -42,6 +42,7 @@
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
+import android.media.AudioAttributes;
import android.media.Image;
import android.media.ImageReader;
import android.media.ImageWriter;
@@ -147,6 +148,7 @@
public static final String TRIGGER_AF_KEY = "af";
public static final String VIB_PATTERN_KEY = "pattern";
public static final String EVCOMP_KEY = "evComp";
+ public static final String AUDIO_RESTRICTION_MODE_KEY = "mode";
private CameraManager mCameraManager = null;
private HandlerThread mCameraThread = null;
@@ -264,7 +266,7 @@
mSensorThread.start();
mSensorHandler = new Handler(mSensorThread.getLooper());
mSensorManager.registerListener(this, mAccelSensor,
- SensorManager.SENSOR_DELAY_NORMAL, mSensorHandler);
+ /*100hz*/ 10000, mSensorHandler);
mSensorManager.registerListener(this, mMagSensor,
SensorManager.SENSOR_DELAY_NORMAL, mSensorHandler);
mSensorManager.registerListener(this, mGyroSensor,
@@ -687,6 +689,8 @@
doCapture(cmdObj);
} else if ("doVibrate".equals(cmdObj.getString("cmdName"))) {
doVibrate(cmdObj);
+ } else if ("setAudioRestriction".equals(cmdObj.getString("cmdName"))) {
+ doSetAudioRestriction(cmdObj);
} else if ("getCameraIds".equals(cmdObj.getString("cmdName"))) {
doGetCameraIds();
} else if ("doReprocessCapture".equals(cmdObj.getString("cmdName"))) {
@@ -907,6 +911,7 @@
obj.put("accel", mAccelSensor != null);
obj.put("mag", mMagSensor != null);
obj.put("gyro", mGyroSensor != null);
+ obj.put("vibrator", mVibrator.hasVibrator());
mSocketRunnableObj.sendResponse("sensorExistence", null, obj, null);
} catch (org.json.JSONException e) {
throw new ItsException("JSON error: ", e);
@@ -1305,13 +1310,35 @@
pattern[i] = patternArray.getLong(i);
}
Logt.i(TAG, String.format("Starting vibrator, pattern length %d",len));
- mVibrator.vibrate(pattern, -1);
+
+ // Mark the vibrator as alarm to test the audio restriction API
+ // TODO: consider making this configurable
+ AudioAttributes audioAttributes = new AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_ALARM).build();
+ mVibrator.vibrate(pattern, -1, audioAttributes);
mSocketRunnableObj.sendResponse("vibrationStarted", "");
} catch (org.json.JSONException e) {
throw new ItsException("JSON error: ", e);
}
}
+ private void doSetAudioRestriction(JSONObject params) throws ItsException {
+ try {
+ if (mCamera == null) {
+ throw new ItsException("Camera is closed");
+ }
+ int mode = params.getInt(AUDIO_RESTRICTION_MODE_KEY);
+ mCamera.setCameraAudioRestriction(mode);
+ Logt.i(TAG, String.format("Set audio restriction mode to %d", mode));
+
+ mSocketRunnableObj.sendResponse("audioRestrictionSet", "");
+ } catch (org.json.JSONException e) {
+ throw new ItsException("JSON error: ", e);
+ } catch (android.hardware.camera2.CameraAccessException e) {
+ throw new ItsException("Access error: ", e);
+ }
+ }
+
/**
* Parse jsonOutputSpecs to get output surface sizes and formats. Create input and output
* image readers for the parsed output surface sizes, output formats, and the given input
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsTestActivity.java
index 7d2f25d..1a59cb1 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsTestActivity.java
@@ -84,8 +84,11 @@
// Scenes
private static final ArrayList<String> mSceneIds = new ArrayList<String> () { {
add("scene0");
- add("scene1");
- add("scene2");
+ add("scene1_1");
+ add("scene1_2");
+ add("scene2_a");
+ add("scene2_b");
+ add("scene2_c");
add("scene3");
add("scene4");
add("scene5");
@@ -95,8 +98,9 @@
private static final ArrayList<String> mHiddenPhysicalCameraSceneIds =
new ArrayList<String> () { {
add("scene0");
- add("scene1");
- add("scene2");
+ add("scene1_1");
+ add("scene1_2");
+ add("scene2_a");
add("scene4");
add("sensor_fusion");
}};
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/performance/CameraPerformanceActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/performance/CameraPerformanceActivity.java
index 5510521..59b7a03 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/performance/CameraPerformanceActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/performance/CameraPerformanceActivity.java
@@ -21,7 +21,7 @@
import android.app.ProgressDialog;
import android.content.Context;
import android.hardware.camera2.cts.PerformanceTest;
-import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
+import android.hardware.camera2.cts.testcases.Camera2AndroidBasicTestCase;
import android.hardware.cts.CameraTestCase;
import android.hardware.cts.LegacyCameraPerformanceTest;
import android.os.Bundle;
@@ -201,8 +201,8 @@
Enumeration<Test> tests = ((TestSuite) s).tests();
while (tests.hasMoreElements()) {
Test test = tests.nextElement();
- if (test instanceof Camera2AndroidTestCase) {
- Camera2AndroidTestCase testCase = (Camera2AndroidTestCase) test;
+ if (test instanceof Camera2AndroidBasicTestCase) {
+ Camera2AndroidBasicTestCase testCase = (Camera2AndroidBasicTestCase) test;
// The base case class has one internal test that can
// be ignored for the purpose of this test activity.
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/video/CameraVideoActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/video/CameraVideoActivity.java
index 3904f09..bfc41e4 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/video/CameraVideoActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/video/CameraVideoActivity.java
@@ -138,16 +138,12 @@
* @see #MEDIA_TYPE_IMAGE
* @see #MEDIA_TYPE_VIDEO
*/
- private static File getOutputMediaFile(int type) {
- // Question: why do I need to comment this to get it working?
- // Logcat says "external storage not ready"
- // if (Environment.getExternalStorageState() != Environment.MEDIA_MOUNTED) {
- // Log.e(TAG, "external storage not ready");
- // return null;
- // }
-
- File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
- Environment.DIRECTORY_MOVIES), TAG);
+ private File getOutputMediaFile(int type) {
+ File mediaStorageDir = new File(getExternalFilesDir(null), TAG);
+ if (mediaStorageDir == null) {
+ Log.e(TAG, "failed to retrieve external files directory");
+ return null;
+ }
if (!mediaStorageDir.exists()) {
if (!mediaStorageDir.mkdirs()) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/car/GearSelectionTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/car/GearSelectionTestActivity.java
new file mode 100644
index 0000000..89197d7
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/car/GearSelectionTestActivity.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.car;
+
+import android.car.Car;
+import android.car.VehicleGear;
+import android.car.hardware.CarPropertyConfig;
+import android.car.hardware.CarPropertyValue;
+import android.car.hardware.property.CarPropertyManager;
+import android.car.VehicleAreaType;
+import android.car.VehiclePropertyIds;
+import android.os.Bundle;
+import android.widget.TextView;
+import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import java.util.Arrays;
+import java.util.List;
+
+/** A CTS Verifier test case to verify GEAR_SELECTION is implemented correctly.*/
+public class GearSelectionTestActivity extends PassFailButtons.Activity {
+ private static final String TAG = GearSelectionTestActivity.class.getSimpleName();
+ private List<Integer> mSupportedGears;
+ private int mGearsAchievedCount = 0;
+ private TextView mExpectedGearSelectionTextView;
+ private TextView mCurrentGearSelectionTextView;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Setup the UI.
+ setContentView(R.layout.gear_selection_test);
+ setPassFailButtonClickListeners();
+ setInfoResources(R.string.gear_selection_test, R.string.gear_selection_test_desc, -1);
+ getPassButton().setEnabled(false);
+
+ mExpectedGearSelectionTextView = (TextView) findViewById(R.id.expected_gear_selection);
+ mCurrentGearSelectionTextView = (TextView) findViewById(R.id.current_gear_selection);
+
+ CarPropertyManager carPropertyManager =
+ (CarPropertyManager) Car.createCar(this).getCarManager(Car.PROPERTY_SERVICE);
+
+ // TODO(b/138961351): Verify test works on manual transmission.
+ mSupportedGears = carPropertyManager.getPropertyList(new ArraySet<>(Arrays.asList(new
+ Integer[]{VehiclePropertyIds.GEAR_SELECTION}))).get(0).getConfigArray();
+
+ if(mSupportedGears.size() != 0){
+ Log.i(TAG, "New Expected Gear: " + VehicleGear.toString(mSupportedGears.get(0)));
+ mExpectedGearSelectionTextView.setText(VehicleGear.toString(mSupportedGears.get(0)));
+ } else {
+ Log.e(TAG, "No gears specified in the config array of GEAR_SELECTION property");
+ mExpectedGearSelectionTextView.setText("ERROR");
+ }
+
+ if(!carPropertyManager.registerCallback(mCarPropertyEventCallback,
+ VehiclePropertyIds.GEAR_SELECTION, CarPropertyManager.SENSOR_RATE_ONCHANGE)) {
+ Log.e(TAG, "Failed to register callback for GEAR_SELECTION with CarPropertyManager");
+ }
+ }
+
+ private final CarPropertyManager.CarPropertyEventCallback mCarPropertyEventCallback =
+ new CarPropertyManager.CarPropertyEventCallback() {
+ @Override
+ public void onChangeEvent(CarPropertyValue value) {
+ if(value.getStatus() != CarPropertyValue.STATUS_AVAILABLE) {
+ Log.e(TAG, "New CarPropertyValue's status is not available - propId: " +
+ value.getPropertyId() + " status: " + value.getStatus());
+ return;
+ }
+ Integer newGearSelection = (Integer) value.getValue();
+ mCurrentGearSelectionTextView.setText(VehicleGear.toString(newGearSelection));
+ Log.i(TAG, "New Gear Selection: " + VehicleGear.toString(newGearSelection));
+
+ if (mSupportedGears.size() == 0) {
+ Log.e(TAG, "No gears specified in the config array of GEAR_SELECTION property");
+ return;
+ }
+
+ // Check to see if new gear matches the expected gear.
+ if(newGearSelection == mSupportedGears.get(mGearsAchievedCount)) {
+ mGearsAchievedCount++;
+ Log.i(TAG, "Matched gear: " + VehicleGear.toString(newGearSelection));
+ // Check to see if the test is finished.
+ if (mGearsAchievedCount >= mSupportedGears.size()) {
+ mExpectedGearSelectionTextView.setText("Finished");
+ getPassButton().setEnabled(true);
+ Log.i(TAG, "Finished Test");
+ } else {
+ // Test is not finished so update the expected gear.
+ mExpectedGearSelectionTextView.setText(
+ VehicleGear.toString(mSupportedGears.get(mGearsAchievedCount)));
+ Log.i(TAG, "New Expected Gear: " +
+ VehicleGear.toString(mSupportedGears.get(mGearsAchievedCount)));
+ }
+ }
+ }
+
+ @Override
+ public void onErrorEvent(int propId, int zone) {
+ Log.e(TAG, "propId: " + propId + " zone: " + zone);
+ }
+ };
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/car/ParkingBrakeOnTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/car/ParkingBrakeOnTestActivity.java
new file mode 100644
index 0000000..c89e895
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/car/ParkingBrakeOnTestActivity.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.car;
+
+import android.car.Car;
+import android.car.hardware.CarPropertyConfig;
+import android.car.hardware.CarPropertyValue;
+import android.car.hardware.property.CarPropertyManager;
+import android.car.VehicleAreaType;
+import android.car.VehiclePropertyIds;
+import android.os.Bundle;
+import android.widget.TextView;
+import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import java.util.Arrays;
+import java.util.List;
+
+/** A CTS Verifier test case to verify PARKING_BRAKE_ON is implemented correctly.*/
+public class ParkingBrakeOnTestActivity extends PassFailButtons.Activity {
+ private static final String TAG = ParkingBrakeOnTestActivity.class.getSimpleName();
+ private static final int TOTAL_MATCHES_NEEDED_TO_FINISH = 2;
+ private Boolean mCurrentParkingBrakeOnValue;
+ private TextView mInstructionTextView;
+ private TextView mCurrentParkingBrakeOnValueTextView;
+ private int mTotalTimesNewValueMatchInstruction = 0;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Setup the UI.
+ setContentView(R.layout.parking_brake_on_test);
+ setPassFailButtonClickListeners();
+ setInfoResources(R.string.parking_brake_on_test, R.string.parking_brake_on_test_desc, -1);
+ getPassButton().setEnabled(false);
+
+ mInstructionTextView = (TextView) findViewById(R.id.instruction);
+ mInstructionTextView.setText("Waiting to get first PARKING_BRAKE_ON callback");
+ mCurrentParkingBrakeOnValueTextView =
+ (TextView) findViewById(R.id.current_parking_brake_on_value);
+
+
+ CarPropertyManager carPropertyManager =
+ (CarPropertyManager) Car.createCar(this).getCarManager(Car.PROPERTY_SERVICE);
+
+ if(!carPropertyManager.registerCallback(mCarPropertyEventCallback,
+ VehiclePropertyIds.PARKING_BRAKE_ON, CarPropertyManager.SENSOR_RATE_ONCHANGE)) {
+ mInstructionTextView.setText("ERROR: Unable to register for PARKING_BRAKE_ON callback");
+ Log.e(TAG, "Failed to register callback for PARKING_BRAKE_ON with CarPropertyManager");
+ }
+ }
+
+ private final CarPropertyManager.CarPropertyEventCallback mCarPropertyEventCallback =
+ new CarPropertyManager.CarPropertyEventCallback() {
+ @Override
+ public void onChangeEvent(CarPropertyValue value) {
+ if(value.getStatus() != CarPropertyValue.STATUS_AVAILABLE) {
+ Log.e(TAG, "New CarPropertyValue's status is not available - propId: " +
+ value.getPropertyId() + " status: " + value.getStatus());
+ return;
+ }
+
+ Boolean newValue = (Boolean) value.getValue();
+ Log.i(TAG, "New PARKING_BRAKE_ON value: " + newValue);
+
+ // On the first callback, mCurrentParkingBrakeOnValue will be null, so just save the
+ // current value. All other callbacks, check if the PARKING_BRAKE_ON value has switched.
+ // If switched, update the count.
+ if (mCurrentParkingBrakeOnValue != null &&
+ !mCurrentParkingBrakeOnValue.equals(newValue)) {
+ mTotalTimesNewValueMatchInstruction++;
+ }
+
+ mCurrentParkingBrakeOnValue = newValue;
+ mCurrentParkingBrakeOnValueTextView.setText(mCurrentParkingBrakeOnValue.toString());
+
+ // Check if the test is finished. If not finished, update the instructions.
+ if(mTotalTimesNewValueMatchInstruction >= TOTAL_MATCHES_NEEDED_TO_FINISH) {
+ mInstructionTextView.setText("Test Finished!");
+ getPassButton().setEnabled(true);
+ } else if(mCurrentParkingBrakeOnValue) {
+ mInstructionTextView.setText("Disengage the Parking Brake");
+ } else {
+ mInstructionTextView.setText("Engage the Parking Brake");
+ }
+ }
+
+ @Override
+ public void onErrorEvent(int propId, int zone) {
+ Log.e(TAG, "propId: " + propId + " zone: " + zone);
+ }
+ };
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/AutomaticZenRuleStatusReceiver.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/AutomaticZenRuleStatusReceiver.java
new file mode 100644
index 0000000..9194cf1
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/AutomaticZenRuleStatusReceiver.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.notifications;
+
+import static android.app.NotificationManager.ACTION_AUTOMATIC_ZEN_RULE_STATUS_CHANGED;
+import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_UNKNOWN;
+import static android.app.NotificationManager.EXTRA_AUTOMATIC_ZEN_RULE_STATUS;
+
+import android.app.NotificationManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.util.Log;
+
+public class AutomaticZenRuleStatusReceiver extends BroadcastReceiver {
+ private static final String TAG = "AZRReceiver";
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ SharedPreferences prefs = context.getSharedPreferences(
+ ConditionProviderVerifierActivity.PREFS, Context.MODE_PRIVATE);
+ SharedPreferences.Editor editor = prefs.edit();
+ if (ACTION_AUTOMATIC_ZEN_RULE_STATUS_CHANGED.equals(intent.getAction())) {
+ String id = intent.getStringExtra(
+ NotificationManager.EXTRA_AUTOMATIC_ZEN_RULE_ID);
+ int status = intent.getIntExtra(EXTRA_AUTOMATIC_ZEN_RULE_STATUS,
+ AUTOMATIC_RULE_STATUS_UNKNOWN);
+ Log.d(TAG, "Got broadcast for rule status change: " + id + " " + status);
+ editor.putInt(id, status);
+ editor.commit();
+ }
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BlockChangeReceiver.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BlockChangeReceiver.java
index 084e907..454b5d2 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BlockChangeReceiver.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BlockChangeReceiver.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.notifications;
import static android.app.NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED;
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BubblesVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BubblesVerifierActivity.java
index 3dc41e6..8760ef8 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BubblesVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BubblesVerifierActivity.java
@@ -34,7 +34,6 @@
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
-import android.widget.Toast;
import com.android.cts.verifier.PassFailButtons;
import com.android.cts.verifier.R;
@@ -103,9 +102,14 @@
runNextTestOrShowSummary();
});
+ // Make sure they're enabled
+ mTests.add(new EnableBubbleTest());
+
ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
- if (!am.isLowRamDevice()) {
- mTests.add(new EnableBubbleTest());
+ if (am.isLowRamDevice()) {
+ // Bubbles don't occur on low ram, instead they just show as notifs so test that
+ mTests.add(new LowRamBubbleTest());
+ } else {
mTests.add(new SendBubbleTest());
mTests.add(new SuppressNotifTest());
mTests.add(new AddNotifTest());
@@ -115,10 +119,6 @@
mTests.add(new DismissBubbleTest());
mTests.add(new DismissNotificationTest());
mTests.add(new AutoExpandBubbleTest());
- } else {
- Toast.makeText(getApplicationContext(),
- getResources().getString(R.string.bubbles_notification_no_bubbles_low_mem),
- Toast.LENGTH_LONG).show();
}
setPassFailButtonClickListeners();
@@ -462,6 +462,32 @@
}
}
+ private class LowRamBubbleTest extends BubblesTestStep {
+ @Override
+ public int getButtonText() {
+ return R.string.bubbles_notification_test_button_10;
+ }
+
+ @Override
+ public int getTestTitle() {
+ return R.string.bubbles_notification_test_title_10;
+ }
+
+ @Override
+ public int getTestDescription() {
+ return R.string.bubbles_notification_test_verify_10;
+ }
+
+ @Override
+ public void performTestAction() {
+ Notification.Builder builder =
+ getBasicNotifBuilder("Bubble notification", "Low ram test");
+ builder.setBubbleMetadata(getBasicBubbleBuilder().build());
+
+ mNotificationManager.notify(NOTIFICATION_ID, builder.build());
+ }
+ }
+
/** Creates a minimally filled out {@link android.app.Notification.BubbleMetadata.Builder} */
private Notification.BubbleMetadata.Builder getBasicBubbleBuilder() {
Context context = getApplicationContext();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/ConditionProviderVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/ConditionProviderVerifierActivity.java
index 1b14d2b..bad4639 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/ConditionProviderVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/ConditionProviderVerifierActivity.java
@@ -16,7 +16,12 @@
package com.android.cts.verifier.notifications;
+import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_DISABLED;
+import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_ENABLED;
+import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_REMOVED;
+import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_UNKNOWN;
import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
+import static android.provider.Settings.EXTRA_APP_PACKAGE;
import android.app.ActivityManager;
import android.app.AutomaticZenRule;
@@ -24,6 +29,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.SharedPreferences;
import android.net.Uri;
import android.provider.Settings;
import android.service.notification.ZenPolicy;
@@ -31,6 +37,7 @@
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.Button;
import com.android.cts.verifier.R;
@@ -41,10 +48,14 @@
public class ConditionProviderVerifierActivity extends InteractiveVerifierActivity
implements Runnable {
+ private static final String TAG = "CPVerifier";
protected static final String CP_PACKAGE = "com.android.cts.verifier";
protected static final String CP_PATH = CP_PACKAGE +
"/com.android.cts.verifier.notifications.MockConditionProvider";
+ protected static final String PREFS = "zen_prefs";
+ private static final String BROADCAST_RULE_NAME = "123";
+
@Override
protected int getTitleResource() {
return R.string.cp_test;
@@ -73,6 +84,11 @@
tests.add(new UpdateAutomaticZenRuleWithZenPolicyTest());
tests.add(new GetAutomaticZenRuleTest());
tests.add(new GetAutomaticZenRulesTest());
+ tests.add(new VerifyRulesIntent());
+ tests.add(new VerifyRulesAvailableToUsers());
+ tests.add(new ReceiveRuleDisableNoticeTest());
+ tests.add(new ReceiveRuleEnabledNoticeTest());
+ tests.add(new ReceiveRuleDeletedNoticeTest());
tests.add(new SubscribeAutomaticZenRuleTest());
tests.add(new DeleteAutomaticZenRuleTest());
tests.add(new UnsubscribeAutomaticZenRuleTest());
@@ -626,6 +642,292 @@
}
}
+ protected class VerifyRulesIntent extends InteractiveTestCase {
+ @Override
+ protected View inflate(ViewGroup parent) {
+ return createSettingsItem(parent, R.string.cp_show_rules);
+ }
+
+ @Override
+ boolean autoStart() {
+ return true;
+ }
+
+ @Override
+ protected void test() {
+ Intent settings = new Intent(Settings.ACTION_CONDITION_PROVIDER_SETTINGS);
+ if (settings.resolveActivity(mPackageManager) == null) {
+ logFail("no settings activity");
+ status = FAIL;
+ } else {
+ if (buttonPressed) {
+ status = PASS;
+ } else {
+ status = RETEST_AFTER_LONG_DELAY;
+ }
+ next();
+ }
+ }
+
+ protected void tearDown() {
+ // wait for the service to start
+ delay();
+ }
+
+ @Override
+ protected Intent getIntent() {
+ return new Intent(Settings.ACTION_CONDITION_PROVIDER_SETTINGS);
+ }
+ }
+
+ protected class VerifyRulesAvailableToUsers extends InteractiveTestCase {
+ @Override
+ protected View inflate(ViewGroup parent) {
+ return createPassFailItem(parent, R.string.cp_show_rules_verification);
+ }
+
+ @Override
+ boolean autoStart() {
+ return true;
+ }
+
+ @Override
+ protected void test() {
+ status = WAIT_FOR_USER;
+ next();
+ }
+ }
+
+ /**
+ * Sends the user to settings to disable the rule. Waits to receive the broadcast that the rule
+ * was disabled, and confirms that the broadcast contains the correct extras.
+ */
+ protected class ReceiveRuleDisableNoticeTest extends InteractiveTestCase {
+ private final int EXPECTED_STATUS = AUTOMATIC_RULE_STATUS_DISABLED;
+ private int mRetries = 2;
+ private View mView;
+ private String mId;
+ @Override
+ protected View inflate(ViewGroup parent) {
+ mView = createNlsSettingsItem(parent, R.string.cp_disable_rule);
+ Button button = mView.findViewById(R.id.nls_action_button);
+ button.setEnabled(false);
+ return mView;
+ }
+
+ @Override
+ protected void setUp() {
+ status = READY;
+ // create enabled so it's ready to be disabled in app
+ AutomaticZenRule rule = new AutomaticZenRule(BROADCAST_RULE_NAME, null,
+ new ComponentName(CP_PACKAGE,
+ ConditionProviderVerifierActivity.this.getClass().getName()),
+ Uri.EMPTY, null, INTERRUPTION_FILTER_PRIORITY, true);
+ mId = mNm.addAutomaticZenRule(rule);
+ Button button = mView.findViewById(R.id.nls_action_button);
+ button.setEnabled(true);
+ }
+
+ @Override
+ boolean autoStart() {
+ return true;
+ }
+
+ @Override
+ protected void test() {
+ SharedPreferences prefs = mContext.getSharedPreferences(PREFS, Context.MODE_PRIVATE);
+
+ AutomaticZenRule rule = mNm.getAutomaticZenRule(mId);
+
+ if (!rule.isEnabled()) {
+ Log.d(TAG, "Check pref for broadcast " + prefs.contains(mId)
+ + " " + prefs.getInt(mId, AUTOMATIC_RULE_STATUS_UNKNOWN));
+ if (prefs.contains(mId)
+ && EXPECTED_STATUS == prefs.getInt(mId, AUTOMATIC_RULE_STATUS_UNKNOWN)) {
+ status = PASS;
+ } else {
+ if (mRetries > 0) {
+ mRetries--;
+ status = RETEST;
+ } else {
+ status = FAIL;
+ }
+ }
+ } else {
+ Log.d(TAG, "Waiting for user");
+ // user hasn't jumped to settings yet
+ status = WAIT_FOR_USER;
+ }
+
+ next();
+ }
+
+ protected void tearDown() {
+ mNm.removeAutomaticZenRule(mId);
+ SharedPreferences prefs = mContext.getSharedPreferences(PREFS, Context.MODE_PRIVATE);
+ prefs.edit().clear().commit();
+ }
+
+ @Override
+ protected Intent getIntent() {
+ return new Intent(Settings.ACTION_CONDITION_PROVIDER_SETTINGS)
+ .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ }
+ }
+
+ /**
+ * Sends the user to settings to enable the rule. Waits to receive the broadcast that the rule
+ * was enabled, and confirms that the broadcast contains the correct extras.
+ */
+ protected class ReceiveRuleEnabledNoticeTest extends InteractiveTestCase {
+ private final int EXPECTED_STATUS = AUTOMATIC_RULE_STATUS_ENABLED;
+ private int mRetries = 2;
+ private View mView;
+ private String mId;
+ @Override
+ protected View inflate(ViewGroup parent) {
+ mView = createNlsSettingsItem(parent, R.string.cp_enable_rule);
+ Button button = mView.findViewById(R.id.nls_action_button);
+ button.setEnabled(false);
+ return mView;
+ }
+
+ @Override
+ protected void setUp() {
+ status = READY;
+ // create disabled so it's ready to be enabled in Settings
+ AutomaticZenRule rule = new AutomaticZenRule(BROADCAST_RULE_NAME, null,
+ new ComponentName(CP_PACKAGE,
+ ConditionProviderVerifierActivity.this.getClass().getName()),
+ Uri.EMPTY, null, INTERRUPTION_FILTER_PRIORITY, false);
+ mId = mNm.addAutomaticZenRule(rule);
+ Button button = mView.findViewById(R.id.nls_action_button);
+ button.setEnabled(true);
+ }
+
+ @Override
+ boolean autoStart() {
+ return true;
+ }
+
+ @Override
+ protected void test() {
+ SharedPreferences prefs = mContext.getSharedPreferences(PREFS, Context.MODE_PRIVATE);
+
+ AutomaticZenRule rule = mNm.getAutomaticZenRule(mId);
+
+ if (rule.isEnabled()) {
+ Log.d(TAG, "Check pref for broadcast " + prefs.contains(mId)
+ + " " + prefs.getInt(mId, AUTOMATIC_RULE_STATUS_UNKNOWN));
+ if (prefs.contains(mId)
+ && EXPECTED_STATUS == prefs.getInt(mId, AUTOMATIC_RULE_STATUS_UNKNOWN)) {
+ status = PASS;
+ } else {
+ if (mRetries > 0) {
+ mRetries--;
+ status = RETEST;
+ } else {
+ status = FAIL;
+ }
+ }
+ } else {
+ Log.d(TAG, "Waiting for user");
+ // user hasn't jumped to settings yet
+ status = WAIT_FOR_USER;
+ }
+
+ next();
+ }
+
+ protected void tearDown() {
+ mNm.removeAutomaticZenRule(mId);
+ SharedPreferences prefs = mContext.getSharedPreferences(PREFS, Context.MODE_PRIVATE);
+ prefs.edit().clear().commit();
+ }
+
+ @Override
+ protected Intent getIntent() {
+ return new Intent(Settings.ACTION_CONDITION_PROVIDER_SETTINGS)
+ .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ }
+ }
+
+ /**
+ * Sends the user to settings to delete the rule. Waits to receive the broadcast that the rule
+ * was deleted, and confirms that the broadcast contains the correct extras.
+ */
+ protected class ReceiveRuleDeletedNoticeTest extends InteractiveTestCase {
+ private final int EXPECTED_STATUS = AUTOMATIC_RULE_STATUS_REMOVED;
+ private int mRetries = 2;
+ private View mView;
+ private String mId;
+ @Override
+ protected View inflate(ViewGroup parent) {
+ mView = createNlsSettingsItem(parent, R.string.cp_delete_rule_broadcast);
+ Button button = mView.findViewById(R.id.nls_action_button);
+ button.setEnabled(false);
+ return mView;
+ }
+
+ @Override
+ protected void setUp() {
+ status = READY;
+ AutomaticZenRule rule = new AutomaticZenRule(BROADCAST_RULE_NAME, null,
+ new ComponentName(CP_PACKAGE,
+ ConditionProviderVerifierActivity.this.getClass().getName()),
+ Uri.EMPTY, null, INTERRUPTION_FILTER_PRIORITY, true);
+ mId = mNm.addAutomaticZenRule(rule);
+ Button button = mView.findViewById(R.id.nls_action_button);
+ button.setEnabled(true);
+ }
+
+ @Override
+ boolean autoStart() {
+ return true;
+ }
+
+ @Override
+ protected void test() {
+ SharedPreferences prefs = mContext.getSharedPreferences(PREFS, Context.MODE_PRIVATE);
+
+ AutomaticZenRule rule = mNm.getAutomaticZenRule(mId);
+
+ if (rule == null) {
+ Log.d(TAG, "Check pref for broadcast " + prefs.contains(mId)
+ + " " + prefs.getInt(mId, AUTOMATIC_RULE_STATUS_UNKNOWN));
+ if (prefs.contains(mId)
+ && EXPECTED_STATUS == prefs.getInt(mId, AUTOMATIC_RULE_STATUS_UNKNOWN)) {
+ status = PASS;
+ } else {
+ if (mRetries > 0) {
+ mRetries--;
+ status = RETEST;
+ } else {
+ status = FAIL;
+ }
+ }
+ } else {
+ Log.d(TAG, "Waiting for user");
+ // user hasn't jumped to settings yet
+ status = WAIT_FOR_USER;
+ }
+
+ next();
+ }
+
+ protected void tearDown() {
+ mNm.removeAutomaticZenRule(mId);
+ SharedPreferences prefs = mContext.getSharedPreferences(PREFS, Context.MODE_PRIVATE);
+ prefs.edit().clear().commit();
+ }
+
+ @Override
+ protected Intent getIntent() {
+ return new Intent(Settings.ACTION_CONDITION_PROVIDER_SETTINGS)
+ .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ }
+ }
+
private class DeleteAutomaticZenRuleTest extends InteractiveTestCase {
private String id1 = null;
private String id2 = null;
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java
index f1f08ff..296df5d 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java
@@ -98,6 +98,7 @@
protected int status;
private View view;
protected long delayTime = 3000;
+ boolean buttonPressed;
protected abstract View inflate(ViewGroup parent);
View getView(ViewGroup parent) {
@@ -247,7 +248,7 @@
protected View createUserItem(ViewGroup parent, int actionId, int messageId,
Object... messageFormatArgs) {
View item = mInflater.inflate(R.layout.nls_item, parent, false);
- TextView instructions = (TextView) item.findViewById(R.id.nls_instructions);
+ TextView instructions = item.findViewById(R.id.nls_instructions);
instructions.setText(getString(messageId, messageFormatArgs));
Button button = (Button) item.findViewById(R.id.nls_action_button);
button.setText(actionId);
@@ -255,15 +256,22 @@
return item;
}
- protected View createAutoItem(ViewGroup parent, int stringId) {
+ protected View createAutoItem(ViewGroup parent, int stringId) {
View item = mInflater.inflate(R.layout.nls_item, parent, false);
- TextView instructions = (TextView) item.findViewById(R.id.nls_instructions);
+ TextView instructions = item.findViewById(R.id.nls_instructions);
instructions.setText(stringId);
View button = item.findViewById(R.id.nls_action_button);
button.setVisibility(View.GONE);
return item;
}
+ protected View createPassFailItem(ViewGroup parent, int stringId) {
+ View item = mInflater.inflate(R.layout.iva_pass_fail_item, parent, false);
+ TextView instructions = item.findViewById(R.id.nls_instructions);
+ instructions.setText(stringId);
+ return item;
+ }
+
// Test management
abstract protected List<InteractiveTestCase> createTestItems();
@@ -381,10 +389,25 @@
}
if (mCurrentTest != null) {
mCurrentTest.mUserVerified = true;
+ mCurrentTest.buttonPressed = true;
}
}
}
+ public void actionPassed(View v) {
+ if (mCurrentTest != null) {
+ mCurrentTest.mUserVerified = true;
+ mCurrentTest.status = PASS;
+ next();
+ }
+ }
+
+ public void actionFailed(View v) {
+ if (mCurrentTest != null) {
+ mCurrentTest.setFailed();
+ }
+ }
+
// Utilities
protected PendingIntent makeIntent(int code, String tag) {
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 a6e5f98..180306e 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/screenpinning/ScreenPinningTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/screenpinning/ScreenPinningTestActivity.java
@@ -18,78 +18,40 @@
import android.app.ActivityManager;
import android.os.Bundle;
import android.util.Log;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.Button;
-import android.widget.LinearLayout;
import android.widget.TextView;
-import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.OrderedTestActivity;
import com.android.cts.verifier.R;
-public class ScreenPinningTestActivity extends PassFailButtons.Activity {
+public class ScreenPinningTestActivity extends OrderedTestActivity {
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;
-
private ActivityManager mActivityManager;
- private Button mNextButton;
- private LinearLayout mInstructions;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.screen_pinning);
- setPassFailButtonClickListeners();
-
- mTests = new Test[] {
- // Verify not already pinned.
- mCheckStartedUnpinned,
- // Enter pinning, verify pinned, try leaving and have the user exit.
- mCheckStartPinning,
- mCheckIsPinned,
- mCheckTryLeave,
- mCheckUnpin,
- // Enter pinning, verify pinned, and use APIs to exit.
- mCheckStartPinning,
- mCheckIsPinned,
- mCheckUnpinFromCode,
- // All done.
- mDone,
- };
mActivityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
- mInstructions = (LinearLayout) findViewById(R.id.instructions_list);
-
- mNextButton = (Button) findViewById(R.id.next_button);
- mNextButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- if ((mTestIndex >= 0) && (mTestIndex < mTests.length)) {
- mTests[mTestIndex].onNextClick();
- }
- }
- });
-
- // Don't allow pass until all tests complete.
- findViewById(R.id.pass_button).setVisibility(View.GONE);
-
- // Figure out if we are in a test or starting from the beginning.
- if (savedInstanceState != null && savedInstanceState.containsKey(KEY_CURRENT_TEST)) {
- mTestIndex = savedInstanceState.getInt(KEY_CURRENT_TEST);
- } else {
- mTestIndex = 0;
- }
- mTests[mTestIndex].run();
- };
+ }
@Override
- protected void onSaveInstanceState(Bundle outState) {
- outState.putInt(KEY_CURRENT_TEST, mTestIndex);
+ protected Test[] getTests() {
+ return new Test[]{
+ // Verify not already pinned.
+ mCheckStartedUnpinned,
+ // Enter pinning, verify pinned, try leaving and have the user exit.
+ mCheckStartPinning,
+ mCheckIsPinned,
+ mCheckTryLeave,
+ mCheckUnpin,
+ // Enter pinning, verify pinned, and use APIs to exit.
+ mCheckStartPinning,
+ mCheckIsPinned,
+ mCheckUnpinFromCode
+ };
}
@Override
@@ -98,27 +60,8 @@
// Users can still leave by pressing fail (or when done the pass) button.
}
- private void show(int id) {
- TextView tv = new TextView(this);
- tv.setPadding(10, 10, 10, 30);
- tv.setText(id);
- mInstructions.removeAllViews();
- mInstructions.addView(tv);
- }
-
- private void succeed() {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mTestIndex++;
- if (mTestIndex < mTests.length) {
- mTests[mTestIndex].run();
- }
- }
- });
- }
-
- private void error(int errorId) {
+ @Override
+ protected void error(int errorId) {
error(errorId, new Throwable());
}
@@ -128,10 +71,8 @@
public void run() {
String error = getString(errorId);
Log.d(TAG, error, cause);
- // No more instructions needed.
- findViewById(R.id.instructions_group).setVisibility(View.GONE);
- ((TextView) findViewById(R.id.error_text)).setText(error);
+ ((TextView) findViewById(R.id.txt_instruction)).setText(error);
}
});
}
@@ -211,40 +152,6 @@
error(R.string.error_screen_pinning_couldnt_exit);
}
}
- };
+ }
};
-
- private final Test mDone = new Test(R.string.screen_pinning_done) {
- @Override
- protected void run() {
- super.run();
- // On test completion, hide "next" button, and show "pass" button
- // instead.
- mNextButton.setVisibility(View.GONE);
- findViewById(R.id.pass_button).setVisibility(View.VISIBLE);
- };
- };
-
- private abstract class Test {
- private final int mResId;
-
- public Test(int showId) {
- mResId = showId;
- }
-
- protected void run() {
- showText();
- }
-
- public void showText() {
- if (mResId == 0) {
- return;
- }
- show(mResId);
- }
-
- protected void onNextClick() {
- }
- }
-
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/security/ProtectedConfirmationTest.java b/apps/CtsVerifier/src/com/android/cts/verifier/security/ProtectedConfirmationTest.java
new file mode 100644
index 0000000..62c7772
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/security/ProtectedConfirmationTest.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.security;
+
+import android.content.pm.PackageManager;
+import android.icu.util.Calendar;
+import android.os.Bundle;
+import android.security.ConfirmationAlreadyPresentingException;
+import android.security.ConfirmationCallback;
+import android.security.ConfirmationNotAvailableException;
+import android.security.ConfirmationPrompt;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.Toast;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import java.io.IOException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.UnrecoverableEntryException;
+import java.security.cert.CertificateException;
+import java.util.Date;
+
+public class ProtectedConfirmationTest extends PassFailButtons.Activity {
+
+ /**
+ * Alias for our key in the Android Key Store.
+ */
+ private static final String KEY_NAME = "my_confirmation_key";
+ private boolean teeTestSuccess = false;
+ private boolean strongboxTestSuccess = false;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.sec_protected_confirmation_main);
+ setPassFailButtonClickListeners();
+
+ boolean protectedConfirmationSupported =
+ ConfirmationPrompt.isSupported(getApplicationContext());
+ if (protectedConfirmationSupported) {
+ setInfoResources(R.string.sec_protected_confirmation_test,
+ R.string.sec_protected_confirmation_test_info, -1);
+ getPassButton().setEnabled(false);
+ } else {
+ setInfoResources(R.string.sec_protected_confirmation_not_supported_title,
+ R.string.sec_protected_confirmation_not_supported_info, -1);
+ getPassButton().setEnabled(true);
+ return;
+ }
+ boolean hasStrongbox = this.getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_STRONGBOX_KEYSTORE);
+
+ findViewById(R.id.sec_protected_confirmation_tee_test_success)
+ .setVisibility(View.INVISIBLE);
+ findViewById(R.id.sec_protected_confirmation_strongbox_test_success)
+ .setVisibility(View.INVISIBLE);
+ Button startTestButton = (Button) findViewById(R.id.sec_start_test_button);
+ startTestButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ showToast("Test running...");
+ v.post(new Runnable() {
+ @Override
+ public void run() {
+ createKey(false /* useStrongbox */);
+ if (trySign(getString(R.string.sec_protected_confirmation_message)
+ .getBytes())) {
+ showToast("Test failed. Key could sign without confirmation.");
+ } else {
+ showConfirmationPrompt(
+ getString(R.string.sec_protected_confirmation_message),
+ false /* useStrongbox */);
+ }
+ }
+ });
+ }
+
+ });
+
+ Button startStrongboxTestButton =
+ (Button) findViewById(R.id.sec_start_test_strongbox_button);
+ if (hasStrongbox) {
+ startStrongboxTestButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ showToast("Test running...");
+ v.post(new Runnable() {
+ @Override
+ public void run() {
+ createKey(true /* useStrongbox */);
+ if (trySign(getString(R.string.sec_protected_confirmation_message)
+ .getBytes())) {
+ showToast("Test failed. Key could sign without confirmation.");
+ } else {
+ showConfirmationPrompt(
+ getString(R.string.sec_protected_confirmation_message),
+ true /* useStrongbox */);
+ }
+ }
+ });
+ }
+
+ });
+ } else {
+ startStrongboxTestButton.setVisibility(View.GONE);
+ // since strongbox is available we mark the strongbox test as passed so that the tee
+ // test alone can make the test pass.
+ strongboxTestSuccess = true;
+ }
+
+ }
+
+ /**
+ * Creates an asymmetric signing key in AndroidKeyStore which can only be used for signing
+ * user confirmed messages.
+ */
+ private void createKey(boolean useStrongbox) {
+ Calendar calendar = Calendar.getInstance();
+ Date validityStart = calendar.getTime();
+ calendar.add(Calendar.YEAR, 1);
+ Date validityEnd = calendar.getTime();
+ try {
+ KeyPairGenerator kpg = KeyPairGenerator.getInstance(
+ KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore");
+ KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(
+ KEY_NAME,
+ KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY);
+ builder.setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512);
+ builder.setAttestationChallenge("CtsVerifierTest".getBytes());
+ builder.setUserConfirmationRequired(true);
+ builder.setIsStrongBoxBacked(useStrongbox);
+ builder.setKeyValidityStart(validityStart);
+ builder.setKeyValidityEnd(validityEnd);
+ kpg.initialize(builder.build());
+ kpg.generateKeyPair();
+ } catch (NoSuchAlgorithmException | NoSuchProviderException |
+ InvalidAlgorithmParameterException e) {
+ throw new RuntimeException("Failed to create confirmation key", e);
+ }
+ }
+
+ private boolean trySign(byte[] dataThatWasConfirmed) {
+ try {
+ KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+ keyStore.load(null);
+ KeyStore.Entry key = keyStore.getEntry(KEY_NAME, null);
+ Signature s = Signature.getInstance("SHA256withECDSA");
+ s.initSign(((KeyStore.PrivateKeyEntry) key).getPrivateKey());
+ s.update(dataThatWasConfirmed);
+ s.sign();
+ } catch (CertificateException | KeyStoreException | IOException | NoSuchAlgorithmException |
+ UnrecoverableEntryException | InvalidKeyException e) {
+ throw new RuntimeException("Failed to load confirmation key", e);
+ } catch (SignatureException e) {
+ return false;
+ }
+ return true;
+ }
+
+ private void showConfirmationPrompt(String confirmationMessage, boolean useStrongbox) {
+ ConfirmationPrompt.Builder builder = new ConfirmationPrompt.Builder(this);
+ builder.setPromptText(confirmationMessage);
+ builder.setExtraData(new byte[]{0x1, 0x02, 0x03});
+ ConfirmationPrompt prompt = builder.build();
+ try {
+ prompt.presentPrompt(getMainExecutor(),
+ new ConfirmationCallback() {
+ @Override
+ public void onConfirmed(byte[] dataThatWasConfirmed) {
+ super.onConfirmed(dataThatWasConfirmed);
+ if (trySign(dataThatWasConfirmed)) {
+ markTestSuccess(useStrongbox);
+ } else {
+ showToast("Failed to sign confirmed message");
+ }
+ }
+
+ @Override
+ public void onDismissed() {
+ super.onDismissed();
+ showToast("User dismissed the dialog.");
+ }
+
+ @Override
+ public void onCanceled() {
+ super.onCanceled();
+ showToast("Confirmation dialog was canceled.");
+ }
+
+ @Override
+ public void onError(Throwable e) {
+ super.onError(e);
+ throw new RuntimeException("Confirmation Callback encountered an error",
+ e);
+ }
+ });
+ } catch (ConfirmationAlreadyPresentingException | ConfirmationNotAvailableException e) {
+ throw new RuntimeException("Error trying to present the confirmation prompt", e);
+ }
+ }
+
+ private void showToast(String message) {
+ Toast.makeText(this, message, Toast.LENGTH_LONG)
+ .show();
+ }
+
+ private void markTestSuccess(boolean strongbox) {
+ if (strongbox) {
+ if (!strongboxTestSuccess) {
+ findViewById(R.id.sec_protected_confirmation_strongbox_test_success)
+ .setVisibility(View.VISIBLE);
+ }
+ strongboxTestSuccess = true;
+ } else {
+ if (!teeTestSuccess) {
+ findViewById(R.id.sec_protected_confirmation_tee_test_success)
+ .setVisibility(View.VISIBLE);
+ }
+ teeTestSuccess = true;
+ }
+ if (strongboxTestSuccess && teeTestSuccess) {
+ showToast("Test passed.");
+ getPassButton().setEnabled(true);
+ }
+ }
+}
+
diff --git a/apps/PermissionApp/AndroidManifest.xml b/apps/PermissionApp/AndroidManifest.xml
index be43556..4efd277 100644
--- a/apps/PermissionApp/AndroidManifest.xml
+++ b/apps/PermissionApp/AndroidManifest.xml
@@ -38,6 +38,7 @@
<uses-permission android:name="com.android.cts.permissionapp.permA"/>
<uses-permission android:name="com.android.cts.permissionapp.permB"/>
<uses-permission android:name="android.permission.READ_CONTACTS"/>
+ <uses-permission android:name="android.permission.WRITE_CONTACTS"/>
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS"/>
<application android:label="CtsPermissionApp"
diff --git a/common/device-side/test-app/Android.bp b/common/device-side/test-app/Android.bp
index 14e711a..74f549d 100644
--- a/common/device-side/test-app/Android.bp
+++ b/common/device-side/test-app/Android.bp
@@ -30,7 +30,7 @@
"compatibility-common-util-devicesidelib",
"compatibility-device-info-tests",
"compatibility-device-info",
- "compatibility-device-util-tests",
+ "compatibility-device-util-tests-axt",
"compatibility-device-util-axt",
],
diff --git a/common/device-side/util-axt/Android.bp b/common/device-side/util-axt/Android.bp
index fe2b1b7..169184a 100644
--- a/common/device-side/util-axt/Android.bp
+++ b/common/device-side/util-axt/Android.bp
@@ -12,14 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// temporary compatibility-device-util variant that brings in androidx.test transitively, instead
-// of android.support.test target. Will be removed after androidx.test CTS conversion is complete.
java_library_static {
name: "compatibility-device-util-axt",
sdk_version: "test_current",
srcs: [
"src/**/*.java",
+ "src/**/*.kt",
"src/**/*.aidl",
],
diff --git a/common/device-side/util-axt/OWNERS b/common/device-side/util-axt/OWNERS
index b61fd53..55fc077 100644
--- a/common/device-side/util-axt/OWNERS
+++ b/common/device-side/util-axt/OWNERS
@@ -1,2 +1,2 @@
-per-file Android.bp=guangzhu@google.com, fdeng@google.com, moonk@google.com, jdesprez@google.com, aaronholden@google.com, yuji@google.com, nickrose@google.com, felipeal@google.com
+per-file Android.bp=guangzhu@google.com, fdeng@google.com, moonk@google.com, jdesprez@google.com, aaronholden@google.com, yuji@google.com, nickrose@google.com, felipeal@google.com, eugenesusla@google.com, svetoslavganov@google.com
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/ExceptionUtils.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/ExceptionUtils.java
new file mode 100644
index 0000000..0899966
--- /dev/null
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/ExceptionUtils.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compatibility.common.util;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.function.Function;
+
+/**
+ * Utilities to deal with exceptions
+ */
+public class ExceptionUtils {
+ private ExceptionUtils() {}
+
+ /**
+ * Rethrow a given exception, optionally wrapping it in a {@link RuntimeException}
+ */
+ public static RuntimeException propagate(@NonNull Throwable t) {
+ if (t == null) {
+ throw new NullPointerException();
+ }
+ propagateIfInstanceOf(t, Error.class);
+ propagateIfInstanceOf(t, RuntimeException.class);
+ throw new RuntimeException(t);
+ }
+
+ /**
+ * Rethrow a given exception, if it's of type {@code E}
+ */
+ public static <E extends Throwable> void propagateIfInstanceOf(
+ @Nullable Throwable t, Class<E> c) throws E {
+ if (t != null && c.isInstance(t)) {
+ throw c.cast(t);
+ }
+ }
+
+ /**
+ * Gets the root {@link Throwable#getCause() cause} of {@code t}
+ */
+ public static @NonNull Throwable getRootCause(@NonNull Throwable t) {
+ while (t.getCause() != null) t = t.getCause();
+ return t;
+ }
+
+ /**
+ * Appends {@code cause} at the end of the causal chain of {@code t}
+ *
+ * @return {@code t} for convenience
+ */
+ public static @NonNull Throwable appendCause(@NonNull Throwable t, @Nullable Throwable cause) {
+ if (cause != null) {
+ getRootCause(t).initCause(cause);
+ }
+ return t;
+ }
+
+ /**
+ * Runs the given {@code action}, and if any exceptions are thrown in the process, applies
+ * given {@code exceptionTransformer}, rethrowing the result.
+ */
+ public static <R> R wrappingExceptions(
+ Function<Throwable, Throwable> exceptionTransformer, ThrowingSupplier<R> action) {
+ try {
+ return action.get();
+ } catch (Throwable t) {
+ Throwable transformed;
+ try {
+ transformed = exceptionTransformer.apply(t);
+ } catch (Throwable t2) {
+ transformed = new RuntimeException("Failed to apply exception transformation",
+ ExceptionUtils.appendCause(t2, t));
+ }
+ throw ExceptionUtils.propagate(transformed);
+ }
+ }
+
+ /**
+ * @see #wrappingExceptions(Function, ThrowingSupplier)
+ */
+ public static void wrappingExceptions(
+ Function<Throwable, Throwable> exceptionTransformer, ThrowingRunnable action) {
+ wrappingExceptions(exceptionTransformer, () -> {
+ action.run();
+ return null;
+ });
+ }
+}
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/FileUtils.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/FileUtils.java
index ceada01..b628bce 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/FileUtils.java
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/FileUtils.java
@@ -54,7 +54,12 @@
public static final int S_IXOTH = 00001;
static {
- System.loadLibrary("cts_jni");
+ try {
+ // Required only for the native methods.
+ System.loadLibrary("cts_jni");
+ } catch (UnsatisfiedLinkError e) {
+ System.out.println("JNI not loaded");
+ }
}
public static class FileStatus {
@@ -93,13 +98,18 @@
* @param status object to set the fields on
* @param statLinks or don't stat links (lstat vs stat)
* @return whether or not we were able to stat the file
+ *
+ * If you call this method, make sure to link in the libcts_jni library.
*/
public native static boolean getFileStatus(String path, FileStatus status, boolean statLinks);
+ /** If you call this method, make sure to link in the libcts_jni library. */
public native static String getUserName(int uid);
+ /** If you call this method, make sure to link in the libcts_jni library. */
public native static String getGroupName(int gid);
+ /** If you call this method, make sure to link in the libcts_jni library. */
public native static int setPermissions(String file, int mode);
/**
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/FutureResultActivity.kt b/common/device-side/util-axt/src/com/android/compatibility/common/util/FutureResultActivity.kt
new file mode 100644
index 0000000..49397a5
--- /dev/null
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/FutureResultActivity.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compatibility.common.util
+
+import android.app.Activity
+import android.content.Intent
+import java.util.concurrent.CompletableFuture
+import java.util.concurrent.ConcurrentHashMap
+import java.util.concurrent.atomic.AtomicInteger
+
+/**
+ * An [Activity] that exposes a special [startActivityForResult],
+ * returning future resultCode as a [CompletableFuture]
+ */
+class FutureResultActivity : Activity() {
+
+ companion object {
+
+ /** requestCode -> Future<resultCode> */
+ private val requests = ConcurrentHashMap<Int, CompletableFuture<Int>>()
+ private val nextRequestCode = AtomicInteger(0)
+
+ fun doAndAwaitStart(act: () -> Unit): CompletableFuture<Int> {
+ val requestCode = nextRequestCode.get()
+ act()
+ PollingCheck.waitFor(60_000) {
+ nextRequestCode.get() >= requestCode + 1
+ }
+ return requests[requestCode]!!
+ }
+ }
+
+ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ requests[requestCode]!!.complete(resultCode)
+ }
+
+ fun startActivityForResult(intent: Intent): CompletableFuture<Int> {
+ val requestCode = nextRequestCode.getAndIncrement()
+ val future = CompletableFuture<Int>()
+ requests[requestCode] = future
+ startActivityForResult(intent, requestCode)
+ return future
+ }
+}
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/ProtoUtils.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/ProtoUtils.java
new file mode 100644
index 0000000..ac99446
--- /dev/null
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/ProtoUtils.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compatibility.common.util;
+
+import android.app.UiAutomation;
+
+/**
+ * Utility class for proto dumps.
+ */
+public class ProtoUtils {
+ public static final String DUMPSYS_JOB_SCHEDULER = "dumpsys jobscheduler --proto";
+
+ /**
+ * Call onto the device with an adb shell command and get the results of
+ * that as a proto of the given type.
+ *
+ * @param clazz A protobuf message class. e.g. MyProto
+ * @param command The adb shell command to run. e.g. "dumpsys jobscheduler --proto"
+ */
+ public static <T> T getProto(UiAutomation automation, Class<T> clazz, String command)
+ throws Exception {
+ return clazz.cast(clazz.getDeclaredMethod("parseFrom", byte[].class)
+ .invoke(null, SystemUtil.runShellCommandByteOutput(automation, command)));
+ }
+}
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/SystemUtil.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/SystemUtil.java
index 16fce10..d63b745 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/SystemUtil.java
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/SystemUtil.java
@@ -31,6 +31,7 @@
import androidx.annotation.NonNull;
import androidx.test.InstrumentationRegistry;
+import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.concurrent.Callable;
@@ -81,21 +82,30 @@
*/
public static String runShellCommand(UiAutomation automation, String cmd)
throws IOException {
+ return new String(runShellCommandByteOutput(automation, cmd));
+ }
+
+ /**
+ * Executes a shell command using shell user identity, and return the standard output as a byte
+ * array
+ * <p>Note: calling this function requires API level 21 or above
+ *
+ * @param automation {@link UiAutomation} instance, obtained from a test running in
+ * instrumentation framework
+ * @param cmd the command to run
+ * @return the standard output of the command as a byte array
+ */
+ static byte[] runShellCommandByteOutput(UiAutomation automation, String cmd)
+ throws IOException {
Log.v(TAG, "Running command: " + cmd);
if (cmd.startsWith("pm grant ") || cmd.startsWith("pm revoke ")) {
throw new UnsupportedOperationException("Use UiAutomation.grantRuntimePermission() "
+ "or revokeRuntimePermission() directly, which are more robust.");
}
ParcelFileDescriptor pfd = automation.executeShellCommand(cmd);
- byte[] buf = new byte[512];
- int bytesRead;
- FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
- StringBuffer stdout = new StringBuffer();
- while ((bytesRead = fis.read(buf)) != -1) {
- stdout.append(new String(buf, 0, bytesRead));
+ try (FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
+ return FileUtils.readInputStreamFully(fis);
}
- fis.close();
- return stdout.toString();
}
/**
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/ThrowingRunnable.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/ThrowingSupplier.java
similarity index 65%
copy from common/device-side/util/src/com/android/compatibility/common/util/ThrowingRunnable.java
copy to common/device-side/util-axt/src/com/android/compatibility/common/util/ThrowingSupplier.java
index 0588cff..f1e0006 100644
--- a/common/device-side/util/src/com/android/compatibility/common/util/ThrowingRunnable.java
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/ThrowingSupplier.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,12 +15,16 @@
*/
package com.android.compatibility.common.util;
+import java.util.function.Supplier;
+
/**
- * Similar to {@link Runnable} but has {@code throws Exception}.
+ * Similar to {@link Supplier} but has {@code throws Exception}.
+ *
+ * @param <T> type of the value produced
*/
-public interface ThrowingRunnable {
+public interface ThrowingSupplier<T> {
/**
- * Similar to {@link Runnable#run} but has {@code throws Exception}.
+ * Similar to {@link Supplier#get} but has {@code throws Exception}.
*/
- void run() throws Exception;
+ T get() throws Exception;
}
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/UiAutomatorUtils.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/UiAutomatorUtils.java
new file mode 100644
index 0000000..f516459
--- /dev/null
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/UiAutomatorUtils.java
@@ -0,0 +1,65 @@
+package com.android.compatibility.common.util;
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 static org.junit.Assert.assertNotNull;
+
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.BySelector;
+import android.support.test.uiautomator.Direction;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+
+import androidx.test.InstrumentationRegistry;
+
+public class UiAutomatorUtils {
+ private UiAutomatorUtils() {}
+
+ public static UiDevice getUiDevice() {
+ return UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ }
+
+ public static UiObject2 waitFindObject(BySelector selector) {
+ return waitFindObject(selector, 100_000);
+ }
+
+ public static UiObject2 waitFindObject(BySelector selector, long timeoutMs) {
+ final UiObject2 view = waitFindObjectOrNull(selector, timeoutMs);
+ ExceptionUtils.wrappingExceptions(UiDumpUtils::wrapWithUiDump, () -> {
+ assertNotNull("View not found after waiting for " + timeoutMs + "ms: " + selector,
+ view);
+ });
+ return view;
+ }
+
+ public static UiObject2 waitFindObjectOrNull(BySelector selector, long timeoutMs) {
+ UiObject2 view = null;
+ long start = System.currentTimeMillis();
+ while (view == null && start + timeoutMs > System.currentTimeMillis()) {
+ view = getUiDevice().wait(Until.findObject(selector), timeoutMs / 10);
+
+ if (view == null) {
+ UiObject2 scrollable = getUiDevice().findObject(By.scrollable(true));
+ if (scrollable != null) {
+ scrollable.scroll(Direction.DOWN, 1);
+ }
+ }
+ }
+ return view;
+ }
+}
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/UiDumpUtils.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/UiDumpUtils.java
new file mode 100644
index 0000000..a36c1eb
--- /dev/null
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/UiDumpUtils.java
@@ -0,0 +1,566 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compatibility.common.util;
+
+import static android.text.TextUtils.isEmpty;
+
+import android.app.Instrumentation;
+import android.app.UiAutomation;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+import android.view.accessibility.AccessibilityWindowInfo;
+
+import androidx.test.InstrumentationRegistry;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.BiFunction;
+import java.util.function.BiPredicate;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.ToIntFunction;
+import java.util.stream.Stream;
+
+/**
+ * Utilities to dump the view hierrarchy as an indented tree
+ *
+ * @see #dumpNodes(AccessibilityNodeInfo, StringBuilder)
+ * @see #wrapWithUiDump(Throwable)
+ */
+@SuppressWarnings({"PointlessBitwiseExpression"})
+public class UiDumpUtils {
+ private UiDumpUtils() {}
+
+ private static final boolean CONCISE = false;
+ private static final boolean SHOW_ACTIONS = false;
+ private static final boolean IGNORE_INVISIBLE = false;
+
+ private static final int IGNORED_ACTIONS = 0
+ | AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS
+ | AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS
+ | AccessibilityNodeInfo.ACTION_FOCUS
+ | AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+ | AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+ | AccessibilityNodeInfo.ACTION_SELECT
+ | AccessibilityNodeInfo.ACTION_SET_SELECTION
+ | AccessibilityNodeInfo.ACTION_CLEAR_SELECTION
+ | AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT
+ | AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT
+ ;
+
+ private static final int SPECIALLY_HANDLED_ACTIONS = 0
+ | AccessibilityNodeInfo.ACTION_CLICK
+ | AccessibilityNodeInfo.ACTION_LONG_CLICK
+ | AccessibilityNodeInfo.ACTION_EXPAND
+ | AccessibilityNodeInfo.ACTION_COLLAPSE
+ | AccessibilityNodeInfo.ACTION_FOCUS
+ | AccessibilityNodeInfo.ACTION_CLEAR_FOCUS
+ | AccessibilityNodeInfo.ACTION_SCROLL_FORWARD
+ | AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD
+ | AccessibilityNodeInfo.ACTION_SET_TEXT
+ ;
+
+ /** name -> typical_value */
+ private static Map<String, Boolean> sNodeFlags = new LinkedHashMap<>();
+ static {
+ sNodeFlags.put("focused", false);
+ sNodeFlags.put("selected", false);
+ sNodeFlags.put("contextClickable", false);
+ sNodeFlags.put("dismissable", false);
+ sNodeFlags.put("enabled", true);
+ sNodeFlags.put("password", false);
+ sNodeFlags.put("visibleToUser", true);
+ sNodeFlags.put("contentInvalid", false);
+ sNodeFlags.put("heading", false);
+ sNodeFlags.put("showingHintText", false);
+
+ // Less important flags below
+ // Too spammy to report all, but can uncomment what's necessary
+
+// sNodeFlags.put("focusable", true);
+// sNodeFlags.put("accessibilityFocused", false);
+// sNodeFlags.put("screenReaderFocusable", true);
+// sNodeFlags.put("clickable", false);
+// sNodeFlags.put("longClickable", false);
+// sNodeFlags.put("checkable", false);
+// sNodeFlags.put("checked", false);
+// sNodeFlags.put("editable", false);
+// sNodeFlags.put("scrollable", false);
+// sNodeFlags.put("importantForAccessibility", true);
+// sNodeFlags.put("multiLine", false);
+ }
+
+ /** action -> pictogram */
+ private static Map<AccessibilityAction, String> sNodeActions = new LinkedHashMap<>();
+ static {
+ sNodeActions.put(AccessibilityAction.ACTION_PASTE, "\uD83D\uDCCB");
+ sNodeActions.put(AccessibilityAction.ACTION_CUT, "✂");
+ sNodeActions.put(AccessibilityAction.ACTION_COPY, "⎘");
+ sNodeActions.put(AccessibilityAction.ACTION_SCROLL_BACKWARD, "←");
+ sNodeActions.put(AccessibilityAction.ACTION_SCROLL_LEFT, "←");
+ sNodeActions.put(AccessibilityAction.ACTION_SCROLL_FORWARD, "→");
+ sNodeActions.put(AccessibilityAction.ACTION_SCROLL_RIGHT, "→");
+ sNodeActions.put(AccessibilityAction.ACTION_SCROLL_DOWN, "↓");
+ sNodeActions.put(AccessibilityAction.ACTION_SCROLL_UP, "↑");
+ }
+
+ private static Instrumentation sInstrumentation = InstrumentationRegistry.getInstrumentation();
+ private static UiAutomation sUiAutomation = sInstrumentation.getUiAutomation();
+
+ private static int sScreenArea;
+ static {
+ Point displaySize = new Point();
+ sInstrumentation.getContext()
+ .getSystemService(WindowManager.class)
+ .getDefaultDisplay()
+ .getRealSize(displaySize);
+ sScreenArea = displaySize.x * displaySize.y;
+ }
+
+
+ /**
+ * Wraps the given exception, with one containing UI hierrarchy {@link #dumpNodes dump}
+ * in its message.
+ *
+ * <p>
+ * Can be used together with {@link ExceptionUtils#wrappingExceptions}, e.g:
+ * {@code
+ * ExceptionUtils.wrappingExceptions(UiDumpUtils::wrapWithUiDump, () -> {
+ * // UI-testing code
+ * });
+ * }
+ */
+ public static UiDumpWrapperException wrapWithUiDump(Throwable cause) {
+ return (cause instanceof UiDumpWrapperException)
+ ? (UiDumpWrapperException) cause
+ : new UiDumpWrapperException(cause);
+ }
+
+ /**
+ * Dumps UI hierarchy with a given {@code root} as indented text tree into {@code out}.
+ */
+ public static void dumpNodes(AccessibilityNodeInfo root, StringBuilder out) {
+ if (root == null) {
+ appendNode(out, root);
+ return;
+ }
+
+ out.append("--- ").append(root.getPackageName()).append(" ---\n|");
+
+ recursively(root, AccessibilityNodeInfo::getChildCount, AccessibilityNodeInfo::getChild,
+ node -> {
+ if (appendNode(out, node)) {
+ out.append("\n|");
+ }
+ },
+ action -> node -> {
+ out.append(" ");
+ action.accept(node);
+ });
+ }
+
+ private static <T> void recursively(T node,
+ ToIntFunction<T> getChildCount, BiFunction<T, Integer, T> getChildAt,
+ Consumer<T> action, Function<Consumer<T>, Consumer<T>> actionChange) {
+ if (node == null) return;
+
+ action.accept(node);
+ Consumer<T> childAction = actionChange.apply(action);
+
+ int size = getChildCount.applyAsInt(node);
+ for (int i = 0; i < size; i++) {
+ recursively(getChildAt.apply(node, i),
+ getChildCount, getChildAt, childAction, actionChange);
+ }
+ }
+
+ private static StringBuilder appendWindow(AccessibilityWindowInfo window, StringBuilder out) {
+ if (window == null) {
+ out.append("<null window>");
+ } else {
+ if (!isEmpty(window.getTitle())) {
+ out.append(window.getTitle());
+ if (CONCISE) return out;
+ out.append(" ");
+ }
+ out.append(valueToString(
+ AccessibilityWindowInfo.class, "TYPE_", window.getType())).append(" ");
+ if (CONCISE) return out;
+ appendArea(out, window::getBoundsInScreen);
+
+ Rect bounds = new Rect();
+ window.getBoundsInScreen(bounds);
+ out.append(bounds.width()).append("x").append(bounds.height()).append(" ");
+ if (window.isInPictureInPictureMode()) out.append("#PIP ");
+ }
+ return out;
+ }
+
+ private static void appendArea(StringBuilder out, Consumer<Rect> getBoundsInScreen) {
+ Rect rect = new Rect();
+ getBoundsInScreen.accept(rect);
+ out.append("size:");
+ out.append(toStringRounding((float) area(rect) * 100 / sScreenArea)).append("% ");
+ }
+
+ private static boolean appendNode(StringBuilder out, AccessibilityNodeInfo node) {
+ if (node == null) {
+ out.append("<null node>");
+ return true;
+ }
+
+ if (IGNORE_INVISIBLE && !node.isVisibleToUser()) return false;
+
+ boolean markedClickable = false;
+ boolean markedNonFocusable = false;
+
+ try {
+ if (node.isFocused() || node.isAccessibilityFocused()) {
+ out.append(">");
+ }
+
+ if ((node.getActions() & AccessibilityNodeInfo.ACTION_EXPAND) != 0) {
+ out.append("[+] ");
+ }
+ if ((node.getActions() & AccessibilityNodeInfo.ACTION_COLLAPSE) != 0) {
+ out.append("[-] ");
+ }
+
+ CharSequence txt = node.getText();
+ if (node.isCheckable()) {
+ out.append("[").append(node.isChecked() ? "X" : "_").append("] ");
+ } else if (node.isEditable()) {
+ if (txt == null) txt = "";
+ out.append("[");
+ appendTextWithCursor(out, node, txt);
+ out.append("] ");
+ } else if (node.isClickable()) {
+ markedClickable = true;
+ out.append("[");
+ } else if (!node.isImportantForAccessibility()) {
+ markedNonFocusable = true;
+ out.append("(");
+ }
+
+ if (appendNodeText(out, node)) return true;
+ } finally {
+ backspaceIf(' ', out);
+ if (markedClickable) {
+ out.append("]");
+ if (node.isLongClickable()) out.append("+");
+ out.append(" ");
+ }
+ if (markedNonFocusable) out.append(") ");
+
+ if (CONCISE) out.append(" ");
+
+ for (Map.Entry<String, Boolean> prop : sNodeFlags.entrySet()) {
+ boolean value = call(node, boolGetter(prop.getKey()));
+ if (value != prop.getValue()) {
+ out.append("#");
+ if (!value) out.append("not_");
+ out.append(prop.getKey()).append(" ");
+ }
+ }
+
+ if (SHOW_ACTIONS) {
+ LinkedHashSet<String> symbols = new LinkedHashSet<>();
+ for (AccessibilityAction accessibilityAction : node.getActionList()) {
+ String symbol = sNodeActions.get(accessibilityAction);
+ if (symbol != null) symbols.add(symbol);
+ }
+ merge(symbols, "←", "→", "↔");
+ merge(symbols, "↑", "↓", "↕");
+ symbols.forEach(out::append);
+ if (!symbols.isEmpty()) out.append(" ");
+
+ getActions(node)
+ .map(a -> "[" + actionToString(a) + "] ")
+ .forEach(out::append);
+ }
+
+ Bundle extras = node.getExtras();
+ for (String extra : extras.keySet()) {
+ if (extra.equals("AccessibilityNodeInfo.chromeRole")) continue;
+ if (extra.equals("AccessibilityNodeInfo.roleDescription")) continue;
+ String value = "" + extras.get(extra);
+ if (value.isEmpty()) continue;
+ out.append(extra).append(":").append(value).append(" ");
+ }
+ }
+ return true;
+ }
+
+ private static StringBuilder appendTextWithCursor(StringBuilder out, AccessibilityNodeInfo node,
+ CharSequence txt) {
+ out.append(txt);
+ insertAtEnd(out, txt.length() - 1 - node.getTextSelectionStart(), "ꕯ");
+ if (node.getTextSelectionEnd() != node.getTextSelectionStart()) {
+ insertAtEnd(out, txt.length() - 1 - node.getTextSelectionEnd(), "ꕯ");
+ }
+ return out;
+ }
+
+ private static boolean appendNodeText(StringBuilder out, AccessibilityNodeInfo node) {
+ CharSequence txt = node.getText();
+
+ Bundle extras = node.getExtras();
+ if (extras.containsKey("AccessibilityNodeInfo.roleDescription")) {
+ out.append("<").append(extras.getString("AccessibilityNodeInfo.chromeRole"))
+ .append("> ");
+ } else if (extras.containsKey("AccessibilityNodeInfo.chromeRole")) {
+ out.append("<").append(extras.getString("AccessibilityNodeInfo.chromeRole"))
+ .append("> ");
+ }
+
+ if (CONCISE) {
+ if (!isEmpty(node.getContentDescription())) {
+ out.append(escape(node.getContentDescription()));
+ return true;
+ }
+ if (!isEmpty(node.getPaneTitle())) {
+ out.append(escape(node.getPaneTitle()));
+ return true;
+ }
+ if (!isEmpty(txt) && !node.isEditable()) {
+ out.append('"');
+ if (node.getTextSelectionStart() > 0 || node.getTextSelectionEnd() > 0) {
+ appendTextWithCursor(out, node, txt);
+ } else {
+ out.append(escape(txt));
+ }
+ out.append('"');
+ return true;
+ }
+ if (!isEmpty(node.getViewIdResourceName())) {
+ out.append("@").append(fromLast("/", node.getViewIdResourceName()));
+ return true;
+ }
+ }
+
+ if (node.getParent() == null && node.getWindow() != null) {
+ appendWindow(node.getWindow(), out);
+ if (CONCISE) return true;
+ out.append(" ");
+ }
+
+ if (!extras.containsKey("AccessibilityNodeInfo.chromeRole")) {
+ out.append(fromLast(".", node.getClassName())).append(" ");
+ }
+ ifNotEmpty(node.getViewIdResourceName(),
+ s -> out.append("@").append(fromLast("/", s)).append(" "));
+ ifNotEmpty(node.getPaneTitle(), s -> out.append("## ").append(s).append(" "));
+ ifNotEmpty(txt, s -> out.append("\"").append(s).append("\" "));
+
+ ifNotEmpty(node.getContentDescription(), s -> out.append("//").append(s).append(" "));
+
+ appendArea(out, node::getBoundsInScreen);
+ return false;
+ }
+
+ private static <T> String valueToString(Class<?> clazz, String prefix, T value) {
+ String s = flagsToString(clazz, prefix, value, Objects::equals);
+ if (s.isEmpty()) s = "" + value;
+ return s;
+ }
+
+ private static <T> String flagsToString(Class<?> clazz, String prefix, T flags,
+ BiPredicate<T, T> test) {
+ return mkStr(sb -> {
+ consts(clazz, prefix)
+ .filter(f -> box(f.getType()).isInstance(flags))
+ .forEach(c -> {
+ try {
+ if (test.test(flags, read(null, c))) {
+ sb.append(c.getName().substring(prefix.length())).append("|");
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("Error while dealing with " + c, e);
+ }
+ });
+ backspace(sb);
+ });
+ }
+
+ private static Class box(Class c) {
+ return c == int.class ? Integer.class : c;
+ }
+
+ private static Stream<Field> consts(Class<?> clazz, String prefix) {
+ return Arrays.stream(clazz.getDeclaredFields())
+ .filter(f -> isConst(f) && f.getName().startsWith(prefix));
+ }
+
+ private static boolean isConst(Field f) {
+ return Modifier.isStatic(f.getModifiers()) && Modifier.isFinal(f.getModifiers());
+ }
+
+ private static Character last(StringBuilder sb) {
+ return sb.length() == 0 ? null : sb.charAt(sb.length() - 1);
+ }
+
+ private static StringBuilder backspaceIf(char c, StringBuilder sb) {
+ if (Objects.equals(last(sb), c)) backspace(sb);
+ return sb;
+ }
+
+ private static StringBuilder backspace(StringBuilder sb) {
+ if (sb.length() != 0) {
+ sb.deleteCharAt(sb.length() - 1);
+ }
+ return sb;
+ }
+
+ private static String toStringRounding(float f) {
+ return f >= 5.0 ? "" + (int) f : String.format("%.1f", f);
+ }
+
+ private static int area(Rect r) {
+ return Math.abs((r.right - r.left) * (r.bottom - r.top));
+ }
+
+ private static String escape(CharSequence s) {
+ return mkStr(out -> {
+ for (int i = 0; i < s.length(); i++) {
+ char c = s.charAt(i);
+ if (c < 127 || c == 0xa0 || c >= 0x2000 && c < 0x2070) {
+ out.append(c);
+ } else {
+ out.append("\\u").append(Integer.toHexString(c));
+ }
+ }
+ });
+ }
+
+ private static Stream<AccessibilityAction> getActions(
+ AccessibilityNodeInfo node) {
+ if (node == null) return Stream.empty();
+ return node.getActionList().stream()
+ .filter(a -> !AccessibilityAction.ACTION_SHOW_ON_SCREEN.equals(a)
+ && (a.getId()
+ & ~IGNORED_ACTIONS
+ & ~SPECIALLY_HANDLED_ACTIONS
+ ) != 0);
+ }
+
+ private static String actionToString(AccessibilityAction a) {
+ if (!isEmpty(a.getLabel())) return a.getLabel().toString();
+ return valueToString(AccessibilityAction.class, "ACTION_", a);
+ }
+
+ private static void merge(Set<String> symbols, String a, String b, String ab) {
+ if (symbols.contains(a) && symbols.contains(b)) {
+ symbols.add(ab);
+ symbols.remove(a);
+ symbols.remove(b);
+ }
+ }
+
+ private static String fromLast(String substr, CharSequence whole) {
+ String wholeStr = whole.toString();
+ int idx = wholeStr.lastIndexOf(substr);
+ if (idx < 0) return wholeStr;
+ return wholeStr.substring(idx + substr.length());
+ }
+
+ private static String boolGetter(String propName) {
+ return "is" + Character.toUpperCase(propName.charAt(0)) + propName.substring(1);
+ }
+
+ private static <T> T read(Object o, Field f) {
+ try {
+ f.setAccessible(true);
+ return (T) f.get(o);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static <T> T call(Object o, String methodName, Object... args) {
+ Class clazz = o instanceof Class ? (Class) o : o.getClass();
+ try {
+ Method method = clazz.getDeclaredMethod(methodName, mapToTypes(args));
+ method.setAccessible(true);
+ //noinspection unchecked
+ return (T) method.invoke(o, args);
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException(
+ newlineSeparated(Arrays.asList(clazz.getDeclaredMethods())), e);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static Class[] mapToTypes(Object[] args) {
+ return Arrays.stream(args).map(Object::getClass).toArray(Class[]::new);
+ }
+
+ private static void ifNotEmpty(CharSequence t, Consumer<CharSequence> f) {
+ if (!isEmpty(t)) {
+ f.accept(t);
+ }
+ }
+
+ private static StringBuilder insertAtEnd(StringBuilder sb, int pos, String s) {
+ return sb.insert(sb.length() - 1 - pos, s);
+ }
+
+ private static <T, R> R fold(List<T> l, R init, BiFunction<R, T, R> combine) {
+ R result = init;
+ for (T t : l) {
+ result = combine.apply(result, t);
+ }
+ return result;
+ }
+
+ private static <T> String toString(List<T> l, String sep, Function<T, String> elemToStr) {
+ return fold(l, "", (a, b) -> a + sep + elemToStr.apply(b));
+ }
+
+ private static <T> String toString(List<T> l, String sep) {
+ return toString(l, sep, String::valueOf);
+ }
+
+ private static String newlineSeparated(List<?> l) {
+ return toString(l, "\n");
+ }
+
+ private static String mkStr(Consumer<StringBuilder> build) {
+ StringBuilder t = new StringBuilder();
+ build.accept(t);
+ return t.toString();
+ }
+
+ private static class UiDumpWrapperException extends RuntimeException {
+ private UiDumpWrapperException(Throwable cause) {
+ super(cause.getMessage() + "\n\nWhile displaying the following UI:\n"
+ + mkStr(sb -> dumpNodes(sUiAutomation.getRootInActiveWindow(), sb)), cause);
+ }
+ }
+}
diff --git a/common/device-side/util/Android.bp b/common/device-side/util/Android.bp
deleted file mode 100644
index 134b4f7..0000000
--- a/common/device-side/util/Android.bp
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-java_library_static {
- name: "compatibility-device-util",
- sdk_version: "test_current",
-
- srcs: [
- "src/**/*.java",
- "src/**/*.aidl",
- ],
-
- static_libs: [
- "compatibility-common-util-devicesidelib",
- "android-support-test",
- "ub-uiautomator",
- "mockito-target-minus-junit4",
- "androidx.annotation_annotation",
- "truth-prebuilt",
- ],
-
- libs: [
- "android.test.runner.stubs",
- "android.test.base.stubs",
- ],
-
- jarjar_rules: "protobuf-jarjar-rules.txt",
-}
\ No newline at end of file
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/ActivitiesWatcher.java b/common/device-side/util/src/com/android/compatibility/common/util/ActivitiesWatcher.java
deleted file mode 100644
index a2b0152..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/ActivitiesWatcher.java
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.util;
-
-import android.app.Activity;
-import android.app.Application.ActivityLifecycleCallbacks;
-import android.os.Bundle;
-import android.util.ArrayMap;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-
-import java.util.Map;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Helper object used to watch for activities lifecycle events.
- *
- * <p><b>NOTE:</b> currently it's limited to just one occurrence of each event.
- *
- * <p>These limitations will be fixed as needed (A.K.A. K.I.S.S. :-)
- */
-public final class ActivitiesWatcher implements ActivityLifecycleCallbacks {
-
- private static final String TAG = ActivitiesWatcher.class.getSimpleName();
-
- private final Map<String, ActivityWatcher> mWatchers = new ArrayMap<>();
- private final long mTimeoutMs;
-
- /**
- * Default constructor.
- *
- * @param timeoutMs how long to wait for given lifecycle event before timing out.
- */
- public ActivitiesWatcher(long timeoutMs) {
- mTimeoutMs = timeoutMs;
- }
-
- @Override
- public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
- Log.v(TAG, "onActivityCreated(): " + activity);
- notifyWatcher(activity, ActivityLifecycle.CREATED);
- }
-
- @Override
- public void onActivityStarted(Activity activity) {
- Log.v(TAG, "onActivityStarted(): " + activity);
- notifyWatcher(activity, ActivityLifecycle.STARTED);
- }
-
- @Override
- public void onActivityResumed(Activity activity) {
- Log.v(TAG, "onActivityResumed(): " + activity);
- notifyWatcher(activity, ActivityLifecycle.RESUMED);
- }
-
- @Override
- public void onActivityPaused(Activity activity) {
- Log.v(TAG, "onActivityPaused(): " + activity);
- notifyWatcher(activity, ActivityLifecycle.PAUSED);
- }
-
- @Override
- public void onActivityStopped(Activity activity) {
- Log.v(TAG, "onActivityStopped(): " + activity);
- notifyWatcher(activity, ActivityLifecycle.STOPPED);
- }
-
- @Override
- public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
- Log.v(TAG, "onActivitySaveInstanceState(): " + activity);
- notifyWatcher(activity, ActivityLifecycle.SAVE_INSTANCE);
- }
-
- @Override
- public void onActivityDestroyed(Activity activity) {
- Log.v(TAG, "onActivityDestroyed(): " + activity);
- notifyWatcher(activity, ActivityLifecycle.DESTROYED);
- }
-
- /**
- * Gets a watcher for the given activity.
- *
- * @throws IllegalStateException if already registered.
- */
- public ActivityWatcher watch(@NonNull Class<? extends Activity> clazz) {
- return watch(clazz.getName());
- }
-
- @Override
- public String toString() {
- return "[ActivitiesWatcher: activities=" + mWatchers.keySet() + "]";
- }
-
- /**
- * Gets a watcher for the given activity.
- *
- * @throws IllegalStateException if already registered.
- */
- public ActivityWatcher watch(@NonNull String className) {
- if (mWatchers.containsKey(className)) {
- throw new IllegalStateException("Already watching " + className);
- }
- Log.d(TAG, "Registering watcher for " + className);
- final ActivityWatcher watcher = new ActivityWatcher(mTimeoutMs);
- mWatchers.put(className, watcher);
- return watcher;
- }
-
- private void notifyWatcher(@NonNull Activity activity, @NonNull ActivityLifecycle lifecycle) {
- final String className = activity.getComponentName().getClassName();
- final ActivityWatcher watcher = mWatchers.get(className);
- if (watcher != null) {
- Log.d(TAG, "notifying watcher of " + className + " of " + lifecycle);
- watcher.notify(lifecycle);
- } else {
- Log.v(TAG, lifecycle + ": no watcher for " + className);
- }
- }
-
- /**
- * Object used to watch for acitivity lifecycle events.
- *
- * <p><b>NOTE: </b>currently it only supports one occurrence for each event.
- */
- public static final class ActivityWatcher {
- private final CountDownLatch mCreatedLatch = new CountDownLatch(1);
- private final CountDownLatch mStartedLatch = new CountDownLatch(1);
- private final CountDownLatch mResumedLatch = new CountDownLatch(1);
- private final CountDownLatch mPausedLatch = new CountDownLatch(1);
- private final CountDownLatch mStoppedLatch = new CountDownLatch(1);
- private final CountDownLatch mSaveInstanceLatch = new CountDownLatch(1);
- private final CountDownLatch mDestroyedLatch = new CountDownLatch(1);
- private final long mTimeoutMs;
-
- private ActivityWatcher(long timeoutMs) {
- mTimeoutMs = timeoutMs;
- }
-
- /**
- * Blocks until the given lifecycle event happens.
- *
- * @throws IllegalStateException if it times out while waiting.
- * @throws InterruptedException if interrupted while waiting.
- */
- public void waitFor(@NonNull ActivityLifecycle lifecycle) throws InterruptedException {
- final CountDownLatch latch = getLatch(lifecycle);
- final boolean called = latch.await(mTimeoutMs, TimeUnit.MILLISECONDS);
- if (!called) {
- throw new IllegalStateException(lifecycle + " not called in " + mTimeoutMs + " ms");
- }
- }
-
- private CountDownLatch getLatch(@NonNull ActivityLifecycle lifecycle) {
- switch (lifecycle) {
- case CREATED:
- return mCreatedLatch;
- case STARTED:
- return mStartedLatch;
- case RESUMED:
- return mResumedLatch;
- case PAUSED:
- return mPausedLatch;
- case STOPPED:
- return mStoppedLatch;
- case SAVE_INSTANCE:
- return mSaveInstanceLatch;
- case DESTROYED:
- return mDestroyedLatch;
- default:
- throw new IllegalArgumentException("unsupported lifecycle: " + lifecycle);
- }
- }
-
- private void notify(@NonNull ActivityLifecycle lifecycle) {
- getLatch(lifecycle).countDown();
- }
- }
-
- /**
- * Supported activity lifecycle.
- */
- public enum ActivityLifecycle {
- CREATED,
- STARTED,
- RESUMED,
- PAUSED,
- STOPPED,
- SAVE_INSTANCE,
- DESTROYED
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/ActivityLauncher.java b/common/device-side/util/src/com/android/compatibility/common/util/ActivityLauncher.java
deleted file mode 100644
index 20d9331..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/ActivityLauncher.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.util;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.support.test.rule.ActivityTestRule;
-
-import androidx.annotation.NonNull;
-
-import com.android.compatibility.common.util.ActivitiesWatcher.ActivityWatcher;
-
-/**
- * Helper used to launch an activity and watch for its lifecycle events.
- *
- * @param <A> activity type
- */
-public final class ActivityLauncher<A extends Activity> {
-
- private final ActivityWatcher mWatcher;
- private final ActivityTestRule<A> mActivityTestRule;
- private final Intent mLaunchIntent;
-
- public ActivityLauncher(@NonNull Context context, @NonNull ActivitiesWatcher watcher,
- @NonNull Class<A> activityClass) {
- mWatcher = watcher.watch(activityClass);
- mActivityTestRule = new ActivityTestRule<>(activityClass);
- mLaunchIntent = new Intent(context, activityClass);
- }
-
- /**
- * Gets a watcher for the activity lifecycle events.
- */
- @NonNull
- public ActivityWatcher getWatcher() {
- return mWatcher;
- }
-
- /**
- * Launches the activity.
- */
- @NonNull
- public A launchActivity() {
- return mActivityTestRule.launchActivity(mLaunchIntent);
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/AdoptShellPermissionsRule.java b/common/device-side/util/src/com/android/compatibility/common/util/AdoptShellPermissionsRule.java
deleted file mode 100644
index f7b50b4..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/AdoptShellPermissionsRule.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import android.app.UiAutomation;
-import android.support.test.InstrumentationRegistry;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-
-/**
- * Custom JUnit4 rule that runs a test adopting Shell's permissions, revoking them at the end.
- *
- * <p>NOTE: should only be used in the cases where *every* test in a class requires the permission.
- * For a more fine-grained access, use
- * {@link SystemUtil#runWithShellPermissionIdentity(ThrowingRunnable)}
- * or {@link SystemUtil#callWithShellPermissionIdentity(java.util.concurrent.Callable)} instead.
- */
-public class AdoptShellPermissionsRule implements TestRule {
-
- private final UiAutomation mUiAutomation;
-
- private final String[] mPermissions;
-
- public AdoptShellPermissionsRule() {
- this(InstrumentationRegistry.getInstrumentation().getUiAutomation());
- }
-
- public AdoptShellPermissionsRule(@NonNull UiAutomation uiAutomation) {
- this(uiAutomation, (String[]) null);
- }
-
- public AdoptShellPermissionsRule(@NonNull UiAutomation uiAutomation,
- @Nullable String... permissions) {
- mUiAutomation = uiAutomation;
- mPermissions = permissions;
- }
-
- @Override
- public Statement apply(Statement base, Description description) {
- return new Statement() {
-
- @Override
- public void evaluate() throws Throwable {
- if (mPermissions != null) {
- mUiAutomation.adoptShellPermissionIdentity(mPermissions);
- } else {
- mUiAutomation.adoptShellPermissionIdentity();
- }
- try {
- base.evaluate();
- } finally {
- mUiAutomation.dropShellPermissionIdentity();
- }
- }
- };
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/AmUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/AmUtils.java
deleted file mode 100644
index f3e178b..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/AmUtils.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.util;
-
-public class AmUtils {
- private static final String TAG = "CtsAmUtils";
-
- private static final String DUMPSYS_ACTIVITY_PROCESSES = "dumpsys activity --proto processes";
-
- private AmUtils() {
- }
-
- /** Run "adb shell am make-uid-idle PACKAGE" */
- public static void runMakeUidIdle(String packageName) {
- SystemUtil.runShellCommandForNoOutput("am make-uid-idle " + packageName);
- }
-
- /** Run "adb shell am kill PACKAGE" */
- public static void runKill(String packageName) throws Exception {
- runKill(packageName, false /* wait */);
- }
-
- public static void runKill(String packageName, boolean wait) throws Exception {
- SystemUtil.runShellCommandForNoOutput("am kill --user cur " + packageName);
-
- if (!wait) {
- return;
- }
-
- TestUtils.waitUntil("package process was not killed:" + packageName,
- () -> !isProcessRunning(packageName));
- }
-
- private static boolean isProcessRunning(String packageName) {
- final String output = SystemUtil.runShellCommand("ps -A -o NAME");
- String[] packages = output.split("\\n");
- for (int i = packages.length -1; i >=0; --i) {
- if (packages[i].equals(packageName)) {
- return true;
- }
- }
- return false;
- }
-
- /** Run "adb shell am set-standby-bucket" */
- public static void setStandbyBucket(String packageName, int value) {
- SystemUtil.runShellCommandForNoOutput("am set-standby-bucket " + packageName
- + " " + value);
- }
-
- /** Wait until all broad queues are idle. */
- public static void waitForBroadcastIdle() {
- SystemUtil.runCommandAndPrintOnLogcat(TAG, "am wait-for-broadcast-idle");
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/ApiLevelUtil.java b/common/device-side/util/src/com/android/compatibility/common/util/ApiLevelUtil.java
deleted file mode 100644
index 943ebc7..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/ApiLevelUtil.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import android.os.Build;
-
-import java.lang.reflect.Field;
-
-/**
- * Device-side compatibility utility class for reading device API level.
- */
-public class ApiLevelUtil {
-
- public static boolean isBefore(int version) {
- return Build.VERSION.SDK_INT < version;
- }
-
- public static boolean isBefore(String version) {
- return Build.VERSION.SDK_INT < resolveVersionString(version);
- }
-
- public static boolean isAfter(int version) {
- return Build.VERSION.SDK_INT > version;
- }
-
- public static boolean isAfter(String version) {
- return Build.VERSION.SDK_INT > resolveVersionString(version);
- }
-
- public static boolean isAtLeast(int version) {
- return Build.VERSION.SDK_INT >= version;
- }
-
- public static boolean isAtLeast(String version) {
- return Build.VERSION.SDK_INT >= resolveVersionString(version);
- }
-
- public static boolean isAtMost(int version) {
- return Build.VERSION.SDK_INT <= version;
- }
-
- public static boolean isAtMost(String version) {
- return Build.VERSION.SDK_INT <= resolveVersionString(version);
- }
-
- public static int getApiLevel() {
- return Build.VERSION.SDK_INT;
- }
-
- public static boolean codenameEquals(String name) {
- return Build.VERSION.CODENAME.equalsIgnoreCase(name.trim());
- }
-
- public static boolean codenameStartsWith(String prefix) {
- return Build.VERSION.CODENAME.startsWith(prefix);
- }
-
- public static String getCodename() {
- return Build.VERSION.CODENAME;
- }
-
- protected static int resolveVersionString(String versionString) {
- // Attempt 1: Parse version string as an integer, e.g. "23" for M
- try {
- return Integer.parseInt(versionString);
- } catch (NumberFormatException e) { /* ignore for alternate approaches below */ }
- // Attempt 2: Find matching field in VersionCodes utility class, return value
- try {
- Field versionField = VersionCodes.class.getField(versionString.toUpperCase());
- return versionField.getInt(null); // no instance for VERSION_CODES, use null
- } catch (IllegalAccessException | NoSuchFieldException e) { /* ignore */ }
- // Attempt 3: Find field within android.os.Build.VERSION_CODES
- try {
- Field versionField = Build.VERSION_CODES.class.getField(versionString.toUpperCase());
- return versionField.getInt(null); // no instance for VERSION_CODES, use null
- } catch (IllegalAccessException | NoSuchFieldException e) {
- throw new RuntimeException(
- String.format("Failed to parse version string %s", versionString), e);
- }
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/AppOpsUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/AppOpsUtils.java
deleted file mode 100644
index 939d6da..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/AppOpsUtils.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2018 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import static android.app.AppOpsManager.MODE_ALLOWED;
-import static android.app.AppOpsManager.MODE_DEFAULT;
-import static android.app.AppOpsManager.MODE_ERRORED;
-import static android.app.AppOpsManager.MODE_IGNORED;
-
-import android.app.AppOpsManager;
-import android.support.test.InstrumentationRegistry;
-
-import java.io.IOException;
-
-/**
- * Utilities for controlling App Ops settings, and testing whether ops are logged.
- */
-public class AppOpsUtils {
-
- /**
- * Resets a package's app ops configuration to the device default. See AppOpsManager for the
- * default op settings.
- *
- * <p>
- * It's recommended to call this in setUp() and tearDown() of your test so the test starts and
- * ends with a reproducible default state, and so doesn't affect other tests.
- *
- * <p>
- * Some app ops are configured to be non-resettable, which means that the state of these will
- * not be reset even when calling this method.
- */
- public static String reset(String packageName) throws IOException {
- return runCommand("appops reset " + packageName);
- }
-
- /**
- * Sets the app op mode (e.g. allowed, denied) for a single package and operation.
- */
- public static String setOpMode(String packageName, String opStr, int mode)
- throws IOException {
- String modeStr;
- switch (mode) {
- case MODE_ALLOWED:
- modeStr = "allow";
- break;
- case MODE_ERRORED:
- modeStr = "deny";
- break;
- case MODE_IGNORED:
- modeStr = "ignore";
- break;
- case MODE_DEFAULT:
- modeStr = "default";
- break;
- default:
- throw new IllegalArgumentException("Unexpected app op type");
- }
- String command = "appops set " + packageName + " " + opStr + " " + modeStr;
- return runCommand(command);
- }
-
- /**
- * Get the app op mode (e.g. MODE_ALLOWED, MODE_DEFAULT) for a single package and operation.
- */
- public static int getOpMode(String packageName, String opStr)
- throws IOException {
- String opState = getOpState(packageName, opStr);
- if (opState.contains(" allow")) {
- return MODE_ALLOWED;
- } else if (opState.contains(" deny")) {
- return MODE_ERRORED;
- } else if (opState.contains(" ignore")) {
- return MODE_IGNORED;
- } else if (opState.contains(" default")) {
- return MODE_DEFAULT;
- } else {
- throw new IllegalStateException("Unexpected app op mode returned " + opState);
- }
- }
-
- /**
- * Returns whether an allowed operation has been logged by the AppOpsManager for a
- * package. Operations are noted when the app attempts to perform them and calls e.g.
- * {@link AppOpsManager#noteOperation}.
- *
- * @param opStr The public string constant of the operation (e.g. OPSTR_READ_SMS).
- */
- public static boolean allowedOperationLogged(String packageName, String opStr)
- throws IOException {
- return getOpState(packageName, opStr).contains(" time=");
- }
-
- /**
- * Returns whether a rejected operation has been logged by the AppOpsManager for a
- * package. Operations are noted when the app attempts to perform them and calls e.g.
- * {@link AppOpsManager#noteOperation}.
- *
- * @param opStr The public string constant of the operation (e.g. OPSTR_READ_SMS).
- */
- public static boolean rejectedOperationLogged(String packageName, String opStr)
- throws IOException {
- return getOpState(packageName, opStr).contains(" rejectTime=");
- }
-
- /**
- * Returns the app op state for a package. Includes information on when the operation was last
- * attempted to be performed by the package.
- *
- * Format: "SEND_SMS: allow; time=+23h12m54s980ms ago; rejectTime=+1h10m23s180ms"
- */
- private static String getOpState(String packageName, String opStr) throws IOException {
- return runCommand("appops get " + packageName + " " + opStr);
- }
-
- private static String runCommand(String command) throws IOException {
- return SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(), command);
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/AppStandbyUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/AppStandbyUtils.java
deleted file mode 100644
index 6eeaae2..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/AppStandbyUtils.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import android.util.Log;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class AppStandbyUtils {
- private static final String TAG = "CtsAppStandbyUtils";
-
- /**
- * Returns if app standby is enabled.
- *
- * @return true if enabled; or false if disabled.
- */
- public static boolean isAppStandbyEnabled() {
- final String result = SystemUtil.runShellCommand(
- "dumpsys usagestats is-app-standby-enabled").trim();
- return Boolean.parseBoolean(result);
- }
-
- /**
- * Sets enabled state for app standby feature for runtime switch.
- *
- * App standby feature has 2 switches. This one affects the switch at runtime. If the build
- * switch is off, enabling the runtime switch will not enable App standby.
- *
- * @param enabled if App standby is enabled.
- */
- public static void setAppStandbyEnabledAtRuntime(boolean enabled) {
- final String value = enabled ? "1" : "0";
- Log.d(TAG, "Setting AppStandby " + (enabled ? "enabled" : "disabled") + " at runtime.");
- SettingsUtils.putGlobalSetting("app_standby_enabled", value);
- }
-
- /**
- * Returns if app standby is enabled at runtime. Note {@link #isAppStandbyEnabled()} may still
- * return {@code false} if this method returns {@code true}, because app standby can be disabled
- * at build time as well.
- *
- * @return true if enabled at runtime; or false if disabled at runtime.
- */
- public static boolean isAppStandbyEnabledAtRuntime() {
- final String result =
- SystemUtil.runShellCommand("settings get global app_standby_enabled").trim();
- final boolean boolResult = result.equals("1") || result.equals("null");
- Log.d(TAG, "AppStandby is " + (boolResult ? "enabled" : "disabled") + " at runtime.");
- return boolResult;
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/BatteryUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/BatteryUtils.java
deleted file mode 100644
index 272bc67..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/BatteryUtils.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.util;
-
-import static com.android.compatibility.common.util.SettingsUtils.putGlobalSetting;
-import static com.android.compatibility.common.util.TestUtils.waitUntil;
-
-import android.content.pm.PackageManager;
-import android.os.BatteryManager;
-import android.os.PowerManager;
-import android.provider.Settings.Global;
-import android.support.test.InstrumentationRegistry;
-import android.util.Log;
-
-import org.junit.Assume;
-
-public class BatteryUtils {
- private static final String TAG = "CtsBatteryUtils";
-
- private BatteryUtils() {
- }
-
- public static BatteryManager getBatteryManager() {
- return InstrumentationRegistry.getContext().getSystemService(BatteryManager.class);
- }
-
- public static PowerManager getPowerManager() {
- return InstrumentationRegistry.getContext().getSystemService(PowerManager.class);
- }
-
- /** Make the target device think it's off charger. */
- public static void runDumpsysBatteryUnplug() {
- SystemUtil.runShellCommandForNoOutput("cmd battery unplug");
-
- Log.d(TAG, "Battery UNPLUGGED");
- }
-
- /**
- * Set the battery level to {@code level} percent. The valid range is [0, 100].
- */
- public static void runDumpsysBatterySetLevel(int level) throws Exception {
- SystemUtil.runShellCommandForNoOutput(("cmd battery set level " + level));
-
- Log.d(TAG, "Battery level set to " + level);
- }
-
- /**
- * Set whether the device is plugged in to a charger or not.
- */
- public static void runDumpsysBatterySetPluggedIn(boolean pluggedIn) throws Exception {
- SystemUtil.runShellCommandForNoOutput(("cmd battery set ac " + (pluggedIn ? "1" : "0")));
-
- Log.d(TAG, "Battery AC set to " + pluggedIn);
- }
-
- /** Reset the effect of all the previous {@code runDumpsysBattery*} call */
- public static void runDumpsysBatteryReset() {
- SystemUtil.runShellCommandForNoOutput(("cmd battery reset"));
-
- Log.d(TAG, "Battery RESET");
- }
-
- /**
- * Enable / disable battery saver. Note {@link #runDumpsysBatteryUnplug} must have been
- * executed before enabling BS.
- */
- public static void enableBatterySaver(boolean enabled) throws Exception {
- if (enabled) {
- SystemUtil.runShellCommandForNoOutput("cmd power set-mode 1");
- putGlobalSetting(Global.LOW_POWER_MODE, "1");
- waitUntil("Battery saver still off", () -> getPowerManager().isPowerSaveMode());
- waitUntil("Location mode still " + getPowerManager().getLocationPowerSaveMode(),
- () -> (PowerManager.LOCATION_MODE_NO_CHANGE
- != getPowerManager().getLocationPowerSaveMode()));
-
- Thread.sleep(500);
- waitUntil("Force all apps standby still off",
- () -> SystemUtil.runShellCommand("dumpsys alarm")
- .contains(" Force all apps standby: true\n"));
-
- } else {
- SystemUtil.runShellCommandForNoOutput("cmd power set-mode 0");
- putGlobalSetting(Global.LOW_POWER_MODE, "0");
- putGlobalSetting(Global.LOW_POWER_MODE_STICKY, "0");
- waitUntil("Battery saver still on", () -> !getPowerManager().isPowerSaveMode());
- waitUntil("Location mode still " + getPowerManager().getLocationPowerSaveMode(),
- () -> (PowerManager.LOCATION_MODE_NO_CHANGE
- == getPowerManager().getLocationPowerSaveMode()));
-
- Thread.sleep(500);
- waitUntil("Force all apps standby still on",
- () -> SystemUtil.runShellCommand("dumpsys alarm")
- .contains(" Force all apps standby: false\n"));
- }
-
- AmUtils.waitForBroadcastIdle();
- Log.d(TAG, "Battery saver turned " + (enabled ? "ON" : "OFF"));
- }
-
- /**
- * Turn on/off screen.
- */
- public static void turnOnScreen(boolean on) throws Exception {
- if (on) {
- SystemUtil.runShellCommandForNoOutput("input keyevent KEYCODE_WAKEUP");
- waitUntil("Device still not interactive", () -> getPowerManager().isInteractive());
-
- } else {
- SystemUtil.runShellCommandForNoOutput("input keyevent KEYCODE_SLEEP");
- waitUntil("Device still interactive", () -> !getPowerManager().isInteractive());
- }
- AmUtils.waitForBroadcastIdle();
- Log.d(TAG, "Screen turned " + (on ? "ON" : "OFF"));
- }
-
- /** @return true if the device supports battery saver. */
- public static boolean isBatterySaverSupported() {
- final PackageManager pm = InstrumentationRegistry.getContext().getPackageManager();
- return !pm.hasSystemFeature(PackageManager.FEATURE_WATCH);
- }
-
- /** "Assume" the current device supports battery saver. */
- public static void assumeBatterySaverFeature() {
- Assume.assumeTrue("Device doesn't support battery saver", isBatterySaverSupported());
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/BeforeAfterRule.java b/common/device-side/util/src/com/android/compatibility/common/util/BeforeAfterRule.java
deleted file mode 100644
index be75671..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/BeforeAfterRule.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.util;
-
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-
-/**
- * Custom JUnit4 rule that provides "before" / "after" callbacks, which is useful to use with
- * {@link org.junit.rules.RuleChain}.
- */
-public class BeforeAfterRule implements TestRule {
- @Override
- public Statement apply(Statement base, Description description) {
- return new Statement() {
-
- @Override
- public void evaluate() throws Throwable {
- onBefore(base, description);
- try {
- base.evaluate();
- } finally {
- onAfter(base, description);
- }
- }
- };
- }
-
- protected void onBefore(Statement base, Description description) throws Throwable {
- }
-
- protected void onAfter(Statement base, Description description) throws Throwable {
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/BitmapUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/BitmapUtils.java
deleted file mode 100644
index 88753b1..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/BitmapUtils.java
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.compatibility.common.util;
-
-import android.app.WallpaperManager;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.CompressFormat;
-import android.graphics.Color;
-import android.util.Log;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.lang.reflect.Method;
-import java.util.Random;
-
-public class BitmapUtils {
- private static final String TAG = "BitmapUtils";
-
- private BitmapUtils() {}
-
- private static Boolean compareBasicBitmapsInfo(Bitmap bmp1, Bitmap bmp2) {
- if (bmp1 == bmp2) {
- return Boolean.TRUE;
- }
-
- if (bmp1 == null) {
- Log.d(TAG, "compareBitmaps() failed because bmp1 is null");
- return Boolean.FALSE;
- }
-
- if (bmp2 == null) {
- Log.d(TAG, "compareBitmaps() failed because bmp2 is null");
- return Boolean.FALSE;
- }
-
- if ((bmp1.getWidth() != bmp2.getWidth()) || (bmp1.getHeight() != bmp2.getHeight())) {
- Log.d(TAG, "compareBitmaps() failed because sizes don't match "
- + "bmp1=(" + bmp1.getWidth() + "x" + bmp1.getHeight() + "), "
- + "bmp2=(" + bmp2.getWidth() + "x" + bmp2.getHeight() + ")");
- return Boolean.FALSE;
- }
- return null;
- }
-
- /**
- * Compares two bitmaps by pixels.
- */
- public static boolean compareBitmaps(Bitmap bmp1, Bitmap bmp2) {
- final Boolean basicComparison = compareBasicBitmapsInfo(bmp1, bmp2);
- if (basicComparison != null) return basicComparison.booleanValue();
-
- for (int i = 0; i < bmp1.getWidth(); i++) {
- for (int j = 0; j < bmp1.getHeight(); j++) {
- if (bmp1.getPixel(i, j) != bmp2.getPixel(i, j)) {
- Log.d(TAG, "compareBitmaps(): pixels (" + i + ", " + j + ") don't match");
- return false;
- }
- }
- }
- return true;
- }
-
- /**
- * Compares two bitmaps by pixels, with a buffer for mistmatches.
- *
- * <p>For example, if {@code minimumPrecision} is 0.99, at least 99% of the pixels should
- * match.
- */
- public static boolean compareBitmaps(Bitmap bmp1, Bitmap bmp2, double minimumPrecision) {
- final Boolean basicComparison = compareBasicBitmapsInfo(bmp1, bmp2);
- if (basicComparison != null) return basicComparison.booleanValue();
-
- final int width = bmp1.getWidth();
- final int height = bmp1.getHeight();
-
- final long numberPixels = width * height;
- long numberMismatches = 0;
-
- for (int i = 0; i < width; i++) {
- for (int j = 0; j < height; j++) {
- if (bmp1.getPixel(i, j) != bmp2.getPixel(i, j)) {
- numberMismatches++;
- if (numberMismatches <= 10) {
- // Let's not spam logcat...
- Log.w(TAG, "compareBitmaps(): pixels (" + i + ", " + j + ") don't match");
- }
- }
- }
- }
- final double actualPrecision = ((double) numberPixels - numberMismatches) / (numberPixels);
- Log.v(TAG, "compareBitmaps(): numberPixels=" + numberPixels
- + ", numberMismatches=" + numberMismatches
- + ", minimumPrecision=" + minimumPrecision
- + ", actualPrecision=" + actualPrecision);
- return actualPrecision >= minimumPrecision;
- }
-
- public static Bitmap generateRandomBitmap(int width, int height) {
- final Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- final Random generator = new Random();
- for (int x = 0; x < width; x++) {
- for (int y = 0; y < height; y++) {
- bmp.setPixel(x, y, generator.nextInt(Integer.MAX_VALUE));
- }
- }
- return bmp;
- }
-
- public static Bitmap generateWhiteBitmap(int width, int height) {
- final Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- bmp.eraseColor(Color.WHITE);
- return bmp;
- }
-
- public static Bitmap getWallpaperBitmap(Context context) throws Exception {
- WallpaperManager wallpaperManager = WallpaperManager.getInstance(context);
- Class<?> noparams[] = {};
- Class<?> wmClass = wallpaperManager.getClass();
- Method methodGetBitmap = wmClass.getDeclaredMethod("getBitmap", noparams);
- return (Bitmap) methodGetBitmap.invoke(wallpaperManager, null);
- }
-
- public static ByteArrayInputStream bitmapToInputStream(Bitmap bmp) {
- final ByteArrayOutputStream bos = new ByteArrayOutputStream();
- bmp.compress(CompressFormat.PNG, 0 /*ignored for PNG*/, bos);
- byte[] bitmapData = bos.toByteArray();
- return new ByteArrayInputStream(bitmapData);
- }
-
- private static void logIfBitmapSolidColor(String fileName, Bitmap bitmap) {
- int firstColor = bitmap.getPixel(0, 0);
- for (int x = 0; x < bitmap.getWidth(); x++) {
- for (int y = 0; y < bitmap.getHeight(); y++) {
- if (bitmap.getPixel(x, y) != firstColor) {
- return;
- }
- }
- }
-
- Log.w(TAG, String.format("%s entire bitmap color is %x", fileName, firstColor));
- }
-
- public static void saveBitmap(Bitmap bitmap, String directoryName, String fileName) {
- new File(directoryName).mkdirs(); // create dirs if needed
-
- Log.d(TAG, "Saving file: " + fileName + " in directory: " + directoryName);
-
- if (bitmap == null) {
- Log.d(TAG, "File not saved, bitmap was null");
- return;
- }
-
- logIfBitmapSolidColor(fileName, bitmap);
-
- File file = new File(directoryName, fileName);
- try (FileOutputStream fileStream = new FileOutputStream(file)) {
- bitmap.compress(Bitmap.CompressFormat.PNG, 0 /* ignored for PNG */, fileStream);
- fileStream.flush();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/BlockedNumberService.java b/common/device-side/util/src/com/android/compatibility/common/util/BlockedNumberService.java
deleted file mode 100644
index 360c078..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/BlockedNumberService.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import static android.provider.BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER;
-import static android.provider.BlockedNumberContract.BlockedNumbers.CONTENT_URI;
-
-import android.app.IntentService;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.ResultReceiver;
-import android.util.Log;
-
-/**
- * A service to handle interactions with the BlockedNumberProvider. The BlockedNumberProvider
- * can only be accessed by the primary user. This service can be run as a singleton service
- * which will then be able to access the BlockedNumberProvider from a test running in a
- * secondary user.
- */
-public class BlockedNumberService extends IntentService {
-
- static final String INSERT_ACTION = "android.telecom.cts.InsertBlockedNumber";
- static final String DELETE_ACTION = "android.telecom.cts.DeleteBlockedNumber";
- static final String PHONE_NUMBER_EXTRA = "number";
- static final String URI_EXTRA = "uri";
- static final String ROWS_EXTRA = "rows";
- static final String RESULT_RECEIVER_EXTRA = "resultReceiver";
-
- private static final String TAG = "CtsBlockNumberSvc";
-
- private ContentResolver mContentResolver;
-
- public BlockedNumberService() {
- super(BlockedNumberService.class.getName());
- }
-
- @Override
- public void onHandleIntent(Intent intent) {
- Log.i(TAG, "Starting BlockedNumberService service: " + intent);
- if (intent == null) {
- return;
- }
- Bundle bundle;
- mContentResolver = getContentResolver();
- switch (intent.getAction()) {
- case INSERT_ACTION:
- bundle = insertBlockedNumber(intent.getStringExtra(PHONE_NUMBER_EXTRA));
- break;
- case DELETE_ACTION:
- bundle = deleteBlockedNumber(Uri.parse(intent.getStringExtra(URI_EXTRA)));
- break;
- default:
- bundle = new Bundle();
- break;
- }
- ResultReceiver receiver = intent.getParcelableExtra(RESULT_RECEIVER_EXTRA);
- receiver.send(0, bundle);
- }
-
- private Bundle insertBlockedNumber(String number) {
- Log.i(TAG, "insertBlockedNumber: " + number);
-
- ContentValues cv = new ContentValues();
- cv.put(COLUMN_ORIGINAL_NUMBER, number);
- Uri uri = mContentResolver.insert(CONTENT_URI, cv);
- Bundle bundle = new Bundle();
- bundle.putString(URI_EXTRA, uri.toString());
- return bundle;
- }
-
- private Bundle deleteBlockedNumber(Uri uri) {
- Log.i(TAG, "deleteBlockedNumber: " + uri);
-
- int rows = mContentResolver.delete(uri, null, null);
- Bundle bundle = new Bundle();
- bundle.putInt(ROWS_EXTRA, rows);
- return bundle;
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/BlockedNumberUtil.java b/common/device-side/util/src/com/android/compatibility/common/util/BlockedNumberUtil.java
deleted file mode 100644
index e5a0ce4..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/BlockedNumberUtil.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import static com.android.compatibility.common.util.BlockedNumberService.DELETE_ACTION;
-import static com.android.compatibility.common.util.BlockedNumberService.INSERT_ACTION;
-import static com.android.compatibility.common.util.BlockedNumberService.PHONE_NUMBER_EXTRA;
-import static com.android.compatibility.common.util.BlockedNumberService.RESULT_RECEIVER_EXTRA;
-import static com.android.compatibility.common.util.BlockedNumberService.ROWS_EXTRA;
-import static com.android.compatibility.common.util.BlockedNumberService.URI_EXTRA;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.ResultReceiver;
-
-import junit.framework.TestCase;
-
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Utility for starting the blocked number service.
- */
-public class BlockedNumberUtil {
-
- private static final int TIMEOUT = 2;
-
- private BlockedNumberUtil() {}
-
- /** Insert a phone number into the blocked number provider and returns the resulting Uri. */
- public static Uri insertBlockedNumber(Context context, String phoneNumber) {
- Intent intent = new Intent(INSERT_ACTION);
- intent.putExtra(PHONE_NUMBER_EXTRA, phoneNumber);
-
- return Uri.parse(runBlockedNumberService(context, intent).getString(URI_EXTRA));
- }
-
- /** Remove a number from the blocked number provider and returns the number of rows deleted. */
- public static int deleteBlockedNumber(Context context, Uri uri) {
- Intent intent = new Intent(DELETE_ACTION);
- intent.putExtra(URI_EXTRA, uri.toString());
-
- return runBlockedNumberService(context, intent).getInt(ROWS_EXTRA);
- }
-
- /** Start the blocked number service. */
- static Bundle runBlockedNumberService(Context context, Intent intent) {
- // Temporarily allow background service
- SystemUtil.runShellCommand("cmd deviceidle tempwhitelist " + context.getPackageName());
-
- final Semaphore semaphore = new Semaphore(0);
- final Bundle result = new Bundle();
-
- ResultReceiver receiver = new ResultReceiver(new Handler(Looper.getMainLooper())) {
- @Override
- protected void onReceiveResult(int resultCode, Bundle resultData) {
- result.putAll(resultData);
- semaphore.release();
- }
- };
- intent.putExtra(RESULT_RECEIVER_EXTRA, receiver);
- intent.setComponent(new ComponentName(context, BlockedNumberService.class));
-
- context.startService(intent);
-
- try {
- TestCase.assertTrue(semaphore.tryAcquire(TIMEOUT, TimeUnit.SECONDS));
- } catch (InterruptedException e) {
- TestCase.fail("Timed out waiting for result from BlockedNumberService");
- }
- return result;
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/BlockingBroadcastReceiver.java b/common/device-side/util/src/com/android/compatibility/common/util/BlockingBroadcastReceiver.java
deleted file mode 100755
index c26ddd0..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/BlockingBroadcastReceiver.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.util;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import androidx.annotation.Nullable;
-import android.util.Log;
-
-import java.util.concurrent.ArrayBlockingQueue;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.TimeUnit;
-
-/**
- * A receiver that allows caller to wait for the broadcast synchronously. Notice that you should not
- * reuse the instance. Usage is typically like this:
- * <pre>
- * BlockingBroadcastReceiver receiver = new BlockingBroadcastReceiver(context, "action");
- * try {
- * receiver.register();
- * Intent intent = receiver.awaitForBroadcast();
- * // assert the intent
- * } finally {
- * receiver.unregisterQuietly();
- * }
- * </pre>
- */
-public class BlockingBroadcastReceiver extends BroadcastReceiver {
- private static final String TAG = "BlockingBroadcast";
-
- private static final int DEFAULT_TIMEOUT_SECONDS = 30;
-
- private final BlockingQueue<Intent> mBlockingQueue;
- private final String mExpectedAction;
- private final Context mContext;
-
- public BlockingBroadcastReceiver(Context context, String expectedAction) {
- mContext = context;
- mExpectedAction = expectedAction;
- mBlockingQueue = new ArrayBlockingQueue<>(1);
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- if (mExpectedAction.equals(intent.getAction())) {
- mBlockingQueue.add(intent);
- }
- }
-
- public void register() {
- mContext.registerReceiver(this, new IntentFilter(mExpectedAction));
- }
-
- /**
- * Wait until the broadcast and return the received broadcast intent. {@code null} is returned
- * if no broadcast with expected action is received within 30 seconds.
- */
- public @Nullable Intent awaitForBroadcast() {
- try {
- return mBlockingQueue.poll(DEFAULT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
- } catch (InterruptedException e) {
- Log.e(TAG, "waitForBroadcast get interrupted: ", e);
- }
- return null;
- }
-
- /**
- * Wait until the broadcast and return the received broadcast intent. {@code null} is returned
- * if no broadcast with expected action is received within the given timeout.
- */
- public @Nullable Intent awaitForBroadcast(long timeoutMillis) {
- try {
- return mBlockingQueue.poll(timeoutMillis, TimeUnit.MILLISECONDS);
- } catch (InterruptedException e) {
- Log.e(TAG, "waitForBroadcast get interrupted: ", e);
- }
- return null;
- }
-
- public void unregisterQuietly() {
- try {
- mContext.unregisterReceiver(this);
- } catch (Exception ex) {
- Log.e(TAG, "Failed to unregister BlockingBroadcastReceiver: ", ex);
- }
- }
-}
\ No newline at end of file
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/BroadcastRpcBase.java b/common/device-side/util/src/com/android/compatibility/common/util/BroadcastRpcBase.java
deleted file mode 100644
index 772e7d3..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/BroadcastRpcBase.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.util;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.support.test.InstrumentationRegistry;
-import android.util.Log;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
-
-/**
- * Base class to help broadcast-based RPC.
- */
-public abstract class BroadcastRpcBase<TRequest, TResponse> {
- private static final String TAG = "BroadcastRpc";
-
- private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
-
- static final String ACTION_REQUEST = "ACTION_REQUEST";
- static final String EXTRA_PAYLOAD = "EXTRA_PAYLOAD";
- static final String EXTRA_EXCEPTION = "EXTRA_EXCEPTION";
-
- static Handler sMainHandler = new Handler(Looper.getMainLooper());
-
- /** Implement in a subclass */
- protected abstract byte[] requestToBytes(TRequest request);
-
- /** Implement in a subclass */
- protected abstract TResponse bytesToResponse(byte[] bytes);
-
- public TResponse invoke(ComponentName targetReceiver, TRequest request) throws Exception {
- // Create a request intent.
- Log.i(TAG, "Sending to: " + targetReceiver + (VERBOSE ? "\nRequest: " + request : ""));
-
- final Intent requestIntent = new Intent(ACTION_REQUEST)
- .setComponent(targetReceiver)
- .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
- .putExtra(EXTRA_PAYLOAD, requestToBytes(request));
-
- // Send it.
- final CountDownLatch latch = new CountDownLatch(1);
- final AtomicReference<Bundle> responseBundle = new AtomicReference<>();
-
- InstrumentationRegistry.getContext().sendOrderedBroadcast(
- requestIntent, null, new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- responseBundle.set(getResultExtras(false));
- latch.countDown();
- }
- }, sMainHandler, 0, null, null);
-
- // Wait for a reply and check it.
- final boolean responseArrived = latch.await(60, TimeUnit.SECONDS);
- assertTrue("Didn't receive broadcast result.", responseArrived);
-
- // TODO If responseArrived is false, print if the package / component is installed?
-
- assertNotNull("Didn't receive result extras", responseBundle.get());
-
- final String exception = responseBundle.get().getString(EXTRA_EXCEPTION);
- if (exception != null) {
- fail("Target throw exception: receiver=" + targetReceiver
- + "\nException: " + exception);
- }
-
- final byte[] resultPayload = responseBundle.get().getByteArray(EXTRA_PAYLOAD);
- assertNotNull("Didn't receive result payload", resultPayload);
-
- Log.i(TAG, "Response received: " + (VERBOSE ? resultPayload.toString() : ""));
-
- return bytesToResponse(resultPayload);
- }
-
- /**
- * Base class for a receiver for a broadcast-based RPC.
- */
- public abstract static class ReceiverBase<TRequest, TResponse> extends BroadcastReceiver {
- @Override
- public final void onReceive(Context context, Intent intent) {
- assertEquals(ACTION_REQUEST, intent.getAction());
-
- // Parse the request.
- final TRequest request = bytesToRequest(intent.getByteArrayExtra(EXTRA_PAYLOAD));
-
- Log.i(TAG, "Request received: " + (VERBOSE ? request.toString() : ""));
-
- Throwable exception = null;
-
- // Handle it and generate a response.
- TResponse response = null;
- try {
- response = handleRequest(context, request);
- Log.i(TAG, "Response generated: " + (VERBOSE ? response.toString() : ""));
- } catch (Throwable e) {
- exception = e;
- Log.e(TAG, "Exception thrown: " + e.getMessage(), e);
- }
-
- // Send back.
- final Bundle extras = new Bundle();
- if (response != null) {
- extras.putByteArray(EXTRA_PAYLOAD, responseToBytes(response));
- }
- if (exception != null) {
- extras.putString(EXTRA_EXCEPTION,
- exception.toString() + "\n" + Log.getStackTraceString(exception));
- }
- setResultExtras(extras);
- }
-
- /** Implement in a subclass */
- protected abstract TResponse handleRequest(Context context, TRequest request)
- throws Exception;
-
- /** Implement in a subclass */
- protected abstract byte[] responseToBytes(TResponse response);
-
- /** Implement in a subclass */
- protected abstract TRequest bytesToRequest(byte[] bytes);
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/BroadcastTestBase.java b/common/device-side/util/src/com/android/compatibility/common/util/BroadcastTestBase.java
deleted file mode 100644
index 7500050..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/BroadcastTestBase.java
+++ /dev/null
@@ -1,125 +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.compatibility.common.util;
-
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.os.Bundle;
-import android.test.ActivityInstrumentationTestCase2;
-import android.util.Log;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-public class BroadcastTestBase extends ActivityInstrumentationTestCase2<
- BroadcastTestStartActivity> {
- static final String TAG = "BroadcastTestBase";
- protected static final int TIMEOUT_MS = 20 * 1000;
-
- protected Context mContext;
- protected Bundle mResultExtras;
- private CountDownLatch mLatch;
- protected ActivityDoneReceiver mActivityDoneReceiver = null;
- private BroadcastTestStartActivity mActivity;
- private BroadcastUtils.TestcaseType mTestCaseType;
- protected boolean mHasFeature;
-
- public BroadcastTestBase() {
- super(BroadcastTestStartActivity.class);
- }
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mHasFeature = false;
- }
-
- @Override
- protected void tearDown() throws Exception {
- Log.v(TAG, getClass().getSimpleName() + ".tearDown(): hasFeature=" + mHasFeature
- + " receiver=" + mActivityDoneReceiver);
- if (mHasFeature && mActivityDoneReceiver != null) {
- try {
- mContext.unregisterReceiver(mActivityDoneReceiver);
- } catch (IllegalArgumentException e) {
- // This exception is thrown if mActivityDoneReceiver in
- // the above call to unregisterReceiver is never registered.
- // If so, no harm done by ignoring this exception.
- }
- mActivityDoneReceiver = null;
- }
- super.tearDown();
- }
-
- protected boolean isIntentSupported(String intentStr) {
- Intent intent = new Intent(intentStr);
- final PackageManager manager = mContext.getPackageManager();
- assertNotNull(manager);
- if (manager.resolveActivity(intent, 0) == null) {
- Log.i(TAG, "No Activity found for the intent: " + intentStr);
- return false;
- }
- return true;
- }
-
- protected void startTestActivity(String intentSuffix) {
- Intent intent = new Intent();
- intent.setAction("android.intent.action.TEST_START_ACTIVITY_" + intentSuffix);
- intent.setComponent(new ComponentName(getInstrumentation().getContext(),
- BroadcastTestStartActivity.class));
- setActivityIntent(intent);
- mActivity = getActivity();
- }
-
- protected void registerBroadcastReceiver(BroadcastUtils.TestcaseType testCaseType) throws Exception {
- mTestCaseType = testCaseType;
- mLatch = new CountDownLatch(1);
- mActivityDoneReceiver = new ActivityDoneReceiver();
- mContext.registerReceiver(mActivityDoneReceiver,
- new IntentFilter(BroadcastUtils.BROADCAST_INTENT + testCaseType.toString()));
- }
-
- protected boolean startTestAndWaitForBroadcast(BroadcastUtils.TestcaseType testCaseType,
- String pkg, String cls) throws Exception {
- Log.i(TAG, "Begin Testing: " + testCaseType);
- registerBroadcastReceiver(testCaseType);
- mActivity.startTest(testCaseType.toString(), pkg, cls);
- if (!mLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
- fail("Failed to receive broadcast in " + TIMEOUT_MS + "msec");
- return false;
- }
- return true;
- }
-
- class ActivityDoneReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(
- BroadcastUtils.BROADCAST_INTENT +
- BroadcastTestBase.this.mTestCaseType.toString())) {
- Bundle extras = intent.getExtras();
- Log.i(TAG, "received_broadcast for " + BroadcastUtils.toBundleString(extras));
- BroadcastTestBase.this.mResultExtras = extras;
- mLatch.countDown();
- }
- }
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/BroadcastTestStartActivity.java b/common/device-side/util/src/com/android/compatibility/common/util/BroadcastTestStartActivity.java
deleted file mode 100644
index 4b3e85d..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/BroadcastTestStartActivity.java
+++ /dev/null
@@ -1,78 +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.compatibility.common.util;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.content.ComponentName;
-import android.os.Bundle;
-import android.util.Log;
-
-public class BroadcastTestStartActivity extends Activity {
- static final String TAG = "BroadcastTestStartActivity";
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- Log.i(TAG, " in onCreate");
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- Log.i(TAG, " in onResume");
- }
-
- void startTest(String testCaseType, String pkg, String cls) {
- Intent intent = new Intent();
- Log.i(TAG, "received_testcasetype = " + testCaseType);
- intent.putExtra(BroadcastUtils.TESTCASE_TYPE, testCaseType);
- intent.setAction("android.intent.action.VIMAIN_" + testCaseType);
- intent.setComponent(new ComponentName(pkg, cls));
- startActivity(intent);
- }
-
- @Override
- protected void onPause() {
- Log.i(TAG, " in onPause");
- super.onPause();
- }
-
- @Override
- protected void onStart() {
- super.onStart();
- Log.i(TAG, " in onStart");
- }
-
- @Override
- protected void onRestart() {
- super.onRestart();
- Log.i(TAG, " in onRestart");
- }
-
- @Override
- protected void onStop() {
- Log.i(TAG, " in onStop");
- super.onStop();
- }
-
- @Override
- protected void onDestroy() {
- Log.i(TAG, " in onDestroy");
- super.onDestroy();
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/BroadcastUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/BroadcastUtils.java
deleted file mode 100644
index a4661fc..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/BroadcastUtils.java
+++ /dev/null
@@ -1,49 +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.compatibility.common.util;
-
-import android.os.Bundle;
-
-public class BroadcastUtils {
- public enum TestcaseType {
- ZEN_MODE_ON,
- ZEN_MODE_OFF,
- AIRPLANE_MODE_ON,
- AIRPLANE_MODE_OFF,
- BATTERYSAVER_MODE_ON,
- BATTERYSAVER_MODE_OFF,
- THEATER_MODE_ON,
- THEATER_MODE_OFF
- }
- public static final String TESTCASE_TYPE = "Testcase_type";
- public static final String BROADCAST_INTENT =
- "android.intent.action.FROM_UTIL_CTS_TEST_";
- public static final int NUM_MINUTES_FOR_ZENMODE = 10;
-
- public static final String toBundleString(Bundle bundle) {
- if (bundle == null) {
- return "*** Bundle is null ****";
- }
- StringBuilder buf = new StringBuilder();
- if (bundle != null) {
- buf.append("extras: ");
- for (String s : bundle.keySet()) {
- buf.append("(" + s + " = " + bundle.get(s) + "), ");
- }
- }
- return buf.toString();
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/BundleUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/BundleUtils.java
deleted file mode 100644
index eda641d..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/BundleUtils.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.util;
-
-import android.os.Bundle;
-
-public class BundleUtils {
- private BundleUtils() {
- }
-
- public static Bundle makeBundle(Object... keysAndValues) {
- if ((keysAndValues.length % 2) != 0) {
- throw new IllegalArgumentException("Argument count not even.");
- }
-
- if (keysAndValues.length == 0) {
- return null;
- }
- final Bundle ret = new Bundle();
-
- for (int i = keysAndValues.length - 2; i >= 0; i -= 2) {
- final String key = keysAndValues[i].toString();
- final Object value = keysAndValues[i + 1];
-
- if (value == null) {
- ret.putString(key, null);
-
- } else if (value instanceof Boolean) {
- ret.putBoolean(key, (Boolean) value);
-
- } else if (value instanceof Integer) {
- ret.putInt(key, (Integer) value);
-
- } else if (value instanceof String) {
- ret.putString(key, (String) value);
-
- } else if (value instanceof Bundle) {
- ret.putBundle(key, (Bundle) value);
- } else {
- throw new IllegalArgumentException(
- "Type not supported yet: " + value.getClass().getName());
- }
- }
- return ret;
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicDeviceExecutor.java b/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicDeviceExecutor.java
deleted file mode 100644
index 7d7aaf0..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicDeviceExecutor.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import android.content.Context;
-import android.text.TextUtils;
-import android.util.Log;
-
-import java.lang.reflect.Method;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Execute business logic methods for device side test cases
- */
-public class BusinessLogicDeviceExecutor extends BusinessLogicExecutor {
-
- private Context mContext;
- private Object mTestObj;
-
- public BusinessLogicDeviceExecutor(Context context, Object testObj) {
- mContext = context;
- mTestObj = testObj;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- protected Object getTestObject() {
- return mTestObj;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void logInfo(String format, Object... args) {
- Log.i(LOG_TAG, String.format(format, args));
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void logDebug(String format, Object... args) {
- Log.d(LOG_TAG, String.format(format, args));
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- protected String formatExecutionString(String method, String... args) {
- return String.format("%s(%s)", method, TextUtils.join(", ", args));
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- protected ResolvedMethod getResolvedMethod(Class cls, String methodName, String... args)
- throws ClassNotFoundException {
- List<Method> nameMatches = getMethodsWithName(cls, methodName);
- for (Method m : nameMatches) {
- ResolvedMethod rm = new ResolvedMethod(m);
- int paramTypesMatched = 0;
- int argsUsed = 0;
- Class[] paramTypes = m.getParameterTypes();
- for (Class paramType : paramTypes) {
- if (argsUsed == args.length && paramType.equals(String.class)) {
- // We've used up all supplied string args, so this method will not match.
- // If paramType is the Context class, we can match a paramType without needing
- // more string args. similarly, paramType "String[]" can be matched with zero
- // string args. If we add support for more paramTypes, this logic may require
- // adjustment.
- break;
- }
- if (paramType.equals(String.class)) {
- // Type "String" -- supply the next available arg
- rm.addArg(args[argsUsed++]);
- } else if (Context.class.isAssignableFrom(paramType)) {
- // Type "Context" -- supply the context from the test case
- rm.addArg(mContext);
- } else if (paramType.equals(Class.forName(STRING_ARRAY_CLASS))) {
- // Type "String[]" (or "String...") -- supply all remaining args
- rm.addArg(Arrays.copyOfRange(args, argsUsed, args.length));
- argsUsed += (args.length - argsUsed);
- } else {
- break; // Param type is unrecognized, this method will not match.
- }
- paramTypesMatched++; // A param type has been matched when reaching this point.
- }
- if (paramTypesMatched == paramTypes.length && argsUsed == args.length) {
- return rm; // Args match, methods match, so return the first method-args pairing.
- }
- // Not a match, try args for next method that matches by name.
- }
- throw new RuntimeException(String.format(
- "BusinessLogic: Failed to invoke action method %s with args: %s", methodName,
- Arrays.toString(args)));
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicTestCase.java b/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicTestCase.java
deleted file mode 100644
index d567a5f..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicTestCase.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.util;
-
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.junit.Assume.assumeTrue;
-
-import android.app.Instrumentation;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.net.Uri;
-import android.os.ParcelFileDescriptor;
-import android.support.test.InstrumentationRegistry;
-import android.util.Log;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.rules.TestName;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.lang.reflect.Field;
-import java.util.Map;
-
-/**
- * Device-side base class for tests leveraging the Business Logic service.
- */
-public class BusinessLogicTestCase {
- private static final String TAG = "BusinessLogicTestCase";
-
- /* String marking the beginning of the parameter in a test name */
- private static final String PARAM_START = "[";
-
- public static final String CONTENT_PROVIDER =
- String.format("%s://android.tradefed.contentprovider", ContentResolver.SCHEME_CONTENT);
-
- /* Test name rule that tracks the current test method under execution */
- @Rule public TestName mTestCase = new TestName();
-
- protected BusinessLogic mBusinessLogic;
- protected boolean mCanReadBusinessLogic = true;
-
- @Before
- public void handleBusinessLogic() {
- loadBusinessLogic();
- executeBusinessLogic();
- }
-
- protected void executeBusinessLogic() {
- String methodName = mTestCase.getMethodName();
- assertTrue(String.format("Test \"%s\" is unable to execute as it depends on the missing "
- + "remote configuration.", methodName), mCanReadBusinessLogic);
- if (methodName.contains(PARAM_START)) {
- // Strip parameter suffix (e.g. "[0]") from method name
- methodName = methodName.substring(0, methodName.lastIndexOf(PARAM_START));
- }
- String testName = String.format("%s#%s", this.getClass().getName(), methodName);
- if (mBusinessLogic.hasLogicFor(testName)) {
- Log.i(TAG, "Finding business logic for test case: " + testName);
- BusinessLogicExecutor executor = new BusinessLogicDeviceExecutor(getContext(), this);
- mBusinessLogic.applyLogicFor(testName, executor);
- }
- }
-
- protected void loadBusinessLogic() {
- String uriPath = String.format("%s/%s", CONTENT_PROVIDER, BusinessLogic.DEVICE_FILE);
- Uri sdcardUri = Uri.parse(uriPath);
- Context appContext = InstrumentationRegistry.getTargetContext();
- try {
- ContentResolver resolver = appContext.getContentResolver();
- ParcelFileDescriptor descriptor = resolver.openFileDescriptor(sdcardUri, "r");
- mBusinessLogic = BusinessLogicFactory.createFromFile(
- new ParcelFileDescriptor.AutoCloseInputStream(descriptor));
- return;
- } catch (FileNotFoundException e) {
- // Log the error and use the fallback too
- Log.e(TAG, "Error while using content provider for config", e);
- }
- // Fallback to reading the business logic directly.
- File businessLogicFile = new File(BusinessLogic.DEVICE_FILE);
- if (businessLogicFile.canRead()) {
- mBusinessLogic = BusinessLogicFactory.createFromFile(businessLogicFile);
- } else {
- mCanReadBusinessLogic = false;
- }
- }
-
- protected static Instrumentation getInstrumentation() {
- return InstrumentationRegistry.getInstrumentation();
- }
-
- protected static Context getContext() {
- return getInstrumentation().getTargetContext();
- }
-
- public static void skipTest(String message) {
- assumeTrue(message, false);
- }
-
- public static void failTest(String message) {
- fail(message);
- }
-
- public void mapPut(String mapName, String key, String value) {
- boolean put = false;
- for (Field f : getClass().getDeclaredFields()) {
- if (f.getName().equalsIgnoreCase(mapName) && Map.class.isAssignableFrom(f.getType())) {
- try {
- ((Map) f.get(this)).put(key, value);
- put = true;
- } catch (IllegalAccessException e) {
- Log.w(String.format("failed to invoke mapPut on field \"%s\". Resuming...",
- f.getName()), e);
- // continue iterating through fields, throw exception if no other fields match
- }
- }
- }
- if (!put) {
- throw new RuntimeException(String.format("Failed to find map %s in class %s", mapName,
- getClass().getName()));
- }
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/CTSResult.java b/common/device-side/util/src/com/android/compatibility/common/util/CTSResult.java
deleted file mode 100644
index d60155a..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/CTSResult.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.android.compatibility.common.util;
-
-public interface CTSResult {
- public static final int RESULT_OK = 1;
- public static final int RESULT_FAIL = 2;
- public void setResult(int resultCode);
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/CallbackAsserter.java b/common/device-side/util/src/com/android/compatibility/common/util/CallbackAsserter.java
deleted file mode 100644
index 436161a..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/CallbackAsserter.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.util;
-
-import static junit.framework.Assert.fail;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.support.test.InstrumentationRegistry;
-import android.util.Log;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.function.Predicate;
-
-/**
- * CallbackAsserter helps wait until a callback is called.
- */
-public class CallbackAsserter {
- private static final String TAG = "CallbackAsserter";
-
- final CountDownLatch mLatch = new CountDownLatch(1);
-
- CallbackAsserter() {
- }
-
- /**
- * Call this to assert a callback be called within the given timeout.
- */
- public final void assertCalled(String message, int timeoutSeconds) throws Exception {
- try {
- if (mLatch.await(timeoutSeconds, TimeUnit.SECONDS)) {
- return;
- }
- fail("Didn't receive callback: " + message);
- } finally {
- cleanUp();
- }
- }
-
- void cleanUp() {
- }
-
- /**
- * Create an instance for a broadcast.
- */
- public static CallbackAsserter forBroadcast(IntentFilter filter) {
- return forBroadcast(filter, null);
- }
-
- /**
- * Create an instance for a broadcast.
- */
- public static CallbackAsserter forBroadcast(IntentFilter filter, Predicate<Intent> checker) {
- return new BroadcastAsserter(filter, checker);
- }
-
- /**
- * Create an instance for a content changed notification.
- */
- public static CallbackAsserter forContentUri(Uri watchUri) {
- return forContentUri(watchUri, null);
- }
-
- /**
- * Create an instance for a content changed notification.
- */
- public static CallbackAsserter forContentUri(Uri watchUri, Predicate<Uri> checker) {
- return new ContentObserverAsserter(watchUri, checker);
- }
-
- private static class BroadcastAsserter extends CallbackAsserter {
- private final BroadcastReceiver mReceiver;
-
- BroadcastAsserter(IntentFilter filter, Predicate<Intent> checker) {
- mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (checker != null && !checker.test(intent)) {
- Log.v(TAG, "Ignoring intent: " + intent);
- return;
- }
- mLatch.countDown();
- }
- };
- InstrumentationRegistry.getContext().registerReceiver(mReceiver, filter);
- }
-
- @Override
- void cleanUp() {
- InstrumentationRegistry.getContext().unregisterReceiver(mReceiver);
- }
- }
-
- private static class ContentObserverAsserter extends CallbackAsserter {
- private final ContentObserver mObserver;
-
- ContentObserverAsserter(Uri watchUri, Predicate<Uri> checker) {
- mObserver = new ContentObserver(null) {
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- if (checker != null && !checker.test(uri)) {
- Log.v(TAG, "Ignoring notification on URI: " + uri);
- return;
- }
- mLatch.countDown();
- }
- };
- InstrumentationRegistry.getContext().getContentResolver().registerContentObserver(
- watchUri, true, mObserver);
- }
-
- @Override
- void cleanUp() {
- InstrumentationRegistry.getContext().getContentResolver().unregisterContentObserver(
- mObserver);
- }
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/ColorUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/ColorUtils.java
deleted file mode 100644
index c0da13d..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/ColorUtils.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.compatibility.common.util;
-
-import static org.junit.Assert.fail;
-
-import android.graphics.Color;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import java.util.function.Function;
-import java.util.function.IntUnaryOperator;
-
-public class ColorUtils {
- public static void verifyColor(int expected, int observed) {
- verifyColor(expected, observed, 0);
- }
-
- public static void verifyColor(int expected, int observed, int tolerance) {
- verifyColor("", expected, observed, tolerance);
- }
-
- /**
- * Verify that two colors match within a per-channel tolerance.
- *
- * @param s String with extra information about the test with an error.
- * @param expected Expected color.
- * @param observed Observed color.
- * @param tolerance Per-channel tolerance by which the color can mismatch.
- */
- public static void verifyColor(@NonNull String s, int expected, int observed, int tolerance) {
- s += " expected 0x" + Integer.toHexString(expected)
- + ", observed 0x" + Integer.toHexString(observed)
- + ", tolerated channel error 0x" + tolerance;
- String red = verifyChannel("red", expected, observed, tolerance, (i) -> Color.red(i));
- String green = verifyChannel("green", expected, observed, tolerance, (i) -> Color.green(i));
- String blue = verifyChannel("blue", expected, observed, tolerance, (i) -> Color.blue(i));
- String alpha = verifyChannel("alpha", expected, observed, tolerance, (i) -> Color.alpha(i));
-
- buildErrorString(s, red, green, blue, alpha);
- }
-
- private static void buildErrorString(@NonNull String s, @Nullable String red,
- @Nullable String green, @Nullable String blue, @Nullable String alpha) {
- String err = null;
- for (String channel : new String[]{red, green, blue, alpha}) {
- if (channel == null) continue;
- if (err == null) err = s;
- err += "\n\t\t" + channel;
- }
- if (err != null) {
- fail(err);
- }
- }
-
- private static String verifyChannel(String channelName, int expected, int observed,
- int tolerance, IntUnaryOperator f) {
- int e = f.applyAsInt(expected);
- int o = f.applyAsInt(observed);
- if (Math.abs(e - o) <= tolerance) {
- return null;
- }
- return "Channel " + channelName + " mismatch: expected<0x" + Integer.toHexString(e)
- + ">, observed: <0x" + Integer.toHexString(o) + ">";
- }
-
- /**
- * Verify that two colors match within a per-channel tolerance.
- *
- * @param msg String with extra information about the test with an error.
- * @param expected Expected color.
- * @param observed Observed color.
- * @param tolerance Per-channel tolerance by which the color can mismatch.
- */
- public static void verifyColor(@NonNull String msg, Color expected, Color observed,
- float tolerance) {
- if (!expected.getColorSpace().equals(observed.getColorSpace())) {
- fail("Cannot compare Colors with different color spaces! expected: " + expected
- + "\tobserved: " + observed);
- }
- msg += " expected " + expected + ", observed " + observed + ", tolerated channel error "
- + tolerance;
- String red = verifyChannel("red", expected, observed, tolerance, (c) -> c.red());
- String green = verifyChannel("green", expected, observed, tolerance, (c) -> c.green());
- String blue = verifyChannel("blue", expected, observed, tolerance, (c) -> c.blue());
- String alpha = verifyChannel("alpha", expected, observed, tolerance, (c) -> c.alpha());
-
- buildErrorString(msg, red, green, blue, alpha);
- }
-
- private static String verifyChannel(String channelName, Color expected, Color observed,
- float tolerance, Function<Color, Float> f) {
- float e = f.apply(expected);
- float o = f.apply(observed);
- float diff = Math.abs(e - o);
- if (diff <= tolerance) {
- return null;
- }
- return "Channel " + channelName + " mismatch: expected<" + e + ">, observed: <" + o
- + ">, difference: <" + diff + ">";
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/ConnectivityUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/ConnectivityUtils.java
deleted file mode 100644
index 09a0a85..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/ConnectivityUtils.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.util;
-
-import static org.junit.Assert.assertTrue;
-
-import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-
-public class ConnectivityUtils {
- private ConnectivityUtils() {
- }
-
- /** @return true when the device has a network connection. */
- public static boolean isNetworkConnected(Context context) {
- final NetworkInfo networkInfo = context.getSystemService(ConnectivityManager.class)
- .getActiveNetworkInfo();
- return (networkInfo != null) && networkInfo.isConnected();
- }
-
- /** Assert that the device has a network connection. */
- public static void assertNetworkConnected(Context context) {
- assertTrue("Network must be connected", isNetworkConnected(context));
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/CpuFeatures.java b/common/device-side/util/src/com/android/compatibility/common/util/CpuFeatures.java
deleted file mode 100644
index 9360942..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/CpuFeatures.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * 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.compatibility.common.util;
-
-import android.os.Build;
-
-public class CpuFeatures {
-
- public static final String ARMEABI_V7 = "armeabi-v7a";
-
- public static final String ARMEABI = "armeabi";
-
- public static final String MIPSABI = "mips";
-
- public static final String X86ABI = "x86";
-
- public static final int HWCAP_VFP = (1 << 6);
-
- public static final int HWCAP_NEON = (1 << 12);
-
- public static final int HWCAP_VFPv3 = (1 << 13);
-
- public static final int HWCAP_VFPv4 = (1 << 16);
-
- public static final int HWCAP_IDIVA = (1 << 17);
-
- public static final int HWCAP_IDIVT = (1 << 18);
-
- static {
- System.loadLibrary("cts_jni");
- }
-
- public static native boolean isArmCpu();
-
- public static native boolean isMipsCpu();
-
- public static native boolean isX86Cpu();
-
- public static native boolean isArm64Cpu();
-
- public static native boolean isMips64Cpu();
-
- public static native boolean isX86_64Cpu();
-
- public static native int getHwCaps();
-
- public static boolean isArm64CpuIn32BitMode() {
- if (!isArmCpu()) {
- return false;
- }
-
- for (String abi : Build.SUPPORTED_64_BIT_ABIS) {
- if (abi.equals("arm64-v8a")) {
- return true;
- }
- }
-
- return false;
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/CtsAndroidTestCase.java b/common/device-side/util/src/com/android/compatibility/common/util/CtsAndroidTestCase.java
deleted file mode 100644
index 1ffad1d..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/CtsAndroidTestCase.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-package com.android.compatibility.common.util;
-
-import android.content.Context;
-import android.test.ActivityInstrumentationTestCase2;
-
-/**
- * This class emulates AndroidTestCase, but internally it is ActivityInstrumentationTestCase2
- * to access Instrumentation.
- * DummyActivity is not supposed to be accessed.
- */
-public class CtsAndroidTestCase extends ActivityInstrumentationTestCase2<DummyActivity> {
- public CtsAndroidTestCase() {
- super(DummyActivity.class);
- }
-
- public Context getContext() {
- return getInstrumentation().getContext();
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/CtsKeyEventUtil.java b/common/device-side/util/src/com/android/compatibility/common/util/CtsKeyEventUtil.java
deleted file mode 100644
index 97e4310..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/CtsKeyEventUtil.java
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import android.app.Instrumentation;
-import android.os.Looper;
-import android.os.SystemClock;
-import android.util.Log;
-import android.view.InputDevice;
-import android.view.KeyCharacterMap;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.inputmethod.InputMethodManager;
-
-import java.lang.reflect.Field;
-
-/**
- * Utility class to send KeyEvents bypassing the IME. The code is similar to functions in
- * {@link Instrumentation} and {@link android.test.InstrumentationTestCase} classes. It uses
- * {@link InputMethodManager#dispatchKeyEventFromInputMethod(View, KeyEvent)} to send the events.
- * After sending the events waits for idle.
- */
-public final class CtsKeyEventUtil {
-
- private CtsKeyEventUtil() {}
-
- /**
- * Sends the key events corresponding to the text to the app being instrumented.
- *
- * @param instrumentation the instrumentation used to run the test.
- * @param targetView View to find the ViewRootImpl and dispatch.
- * @param text The text to be sent. Null value returns immediately.
- */
- public static void sendString(final Instrumentation instrumentation, final View targetView,
- final String text) {
- if (text == null) {
- return;
- }
-
- KeyEvent[] events = getKeyEvents(text);
-
- if (events != null) {
- for (int i = 0; i < events.length; i++) {
- // We have to change the time of an event before injecting it because
- // all KeyEvents returned by KeyCharacterMap.getEvents() have the same
- // time stamp and the system rejects too old events. Hence, it is
- // possible for an event to become stale before it is injected if it
- // takes too long to inject the preceding ones.
- sendKey(instrumentation, targetView, KeyEvent.changeTimeRepeat(
- events[i], SystemClock.uptimeMillis(), 0 /* newRepeat */));
- }
- }
- }
-
- /**
- * Sends a series of key events through instrumentation. For instance:
- * sendKeys(view, KEYCODE_DPAD_LEFT, KEYCODE_DPAD_CENTER).
- *
- * @param instrumentation the instrumentation used to run the test.
- * @param targetView View to find the ViewRootImpl and dispatch.
- * @param keys The series of key codes.
- */
- public static void sendKeys(final Instrumentation instrumentation, final View targetView,
- final int...keys) {
- final int count = keys.length;
-
- for (int i = 0; i < count; i++) {
- try {
- sendKeyDownUp(instrumentation, targetView, keys[i]);
- } catch (SecurityException e) {
- // Ignore security exceptions that are now thrown
- // when trying to send to another app, to retain
- // compatibility with existing tests.
- }
- }
- }
-
- /**
- * Sends a series of key events through instrumentation. The sequence of keys is a string
- * containing the key names as specified in KeyEvent, without the KEYCODE_ prefix. For
- * instance: sendKeys(view, "DPAD_LEFT A B C DPAD_CENTER"). Each key can be repeated by using
- * the N* prefix. For instance, to send two KEYCODE_DPAD_LEFT, use the following:
- * sendKeys(view, "2*DPAD_LEFT").
- *
- * @param instrumentation the instrumentation used to run the test.
- * @param targetView View to find the ViewRootImpl and dispatch.
- * @param keysSequence The sequence of keys.
- */
- public static void sendKeys(final Instrumentation instrumentation, final View targetView,
- final String keysSequence) {
- final String[] keys = keysSequence.split(" ");
- final int count = keys.length;
-
- for (int i = 0; i < count; i++) {
- String key = keys[i];
- int repeater = key.indexOf('*');
-
- int keyCount;
- try {
- keyCount = repeater == -1 ? 1 : Integer.parseInt(key.substring(0, repeater));
- } catch (NumberFormatException e) {
- Log.w("ActivityTestCase", "Invalid repeat count: " + key);
- continue;
- }
-
- if (repeater != -1) {
- key = key.substring(repeater + 1);
- }
-
- for (int j = 0; j < keyCount; j++) {
- try {
- final Field keyCodeField = KeyEvent.class.getField("KEYCODE_" + key);
- final int keyCode = keyCodeField.getInt(null);
- try {
- sendKeyDownUp(instrumentation, targetView, keyCode);
- } catch (SecurityException e) {
- // Ignore security exceptions that are now thrown
- // when trying to send to another app, to retain
- // compatibility with existing tests.
- }
- } catch (NoSuchFieldException e) {
- Log.w("ActivityTestCase", "Unknown keycode: KEYCODE_" + key);
- break;
- } catch (IllegalAccessException e) {
- Log.w("ActivityTestCase", "Unknown keycode: KEYCODE_" + key);
- break;
- }
- }
- }
- }
-
- /**
- * Sends an up and down key events.
- *
- * @param instrumentation the instrumentation used to run the test.
- * @param targetView View to find the ViewRootImpl and dispatch.
- * @param key The integer keycode for the event to be sent.
- */
- public static void sendKeyDownUp(final Instrumentation instrumentation, final View targetView,
- final int key) {
- sendKey(instrumentation, targetView, new KeyEvent(KeyEvent.ACTION_DOWN, key));
- sendKey(instrumentation, targetView, new KeyEvent(KeyEvent.ACTION_UP, key));
- }
-
- /**
- * Sends a key event.
- *
- * @param instrumentation the instrumentation used to run the test.
- * @param targetView View to find the ViewRootImpl and dispatch.
- * @param event KeyEvent to be send.
- */
- public static void sendKey(final Instrumentation instrumentation, final View targetView,
- final KeyEvent event) {
- validateNotAppThread();
-
- long downTime = event.getDownTime();
- long eventTime = event.getEventTime();
- int action = event.getAction();
- int code = event.getKeyCode();
- int repeatCount = event.getRepeatCount();
- int metaState = event.getMetaState();
- int deviceId = event.getDeviceId();
- int scanCode = event.getScanCode();
- int source = event.getSource();
- int flags = event.getFlags();
- if (source == InputDevice.SOURCE_UNKNOWN) {
- source = InputDevice.SOURCE_KEYBOARD;
- }
- if (eventTime == 0) {
- eventTime = SystemClock.uptimeMillis();
- }
- if (downTime == 0) {
- downTime = eventTime;
- }
-
- final KeyEvent newEvent = new KeyEvent(downTime, eventTime, action, code, repeatCount,
- metaState, deviceId, scanCode, flags, source);
-
- InputMethodManager imm = targetView.getContext().getSystemService(InputMethodManager.class);
- imm.dispatchKeyEventFromInputMethod(null, newEvent);
- instrumentation.waitForIdleSync();
- }
-
- /**
- * Sends a key event while holding another modifier key down, then releases both keys and
- * waits for idle sync. Useful for sending combinations like shift + tab.
- *
- * @param instrumentation the instrumentation used to run the test.
- * @param targetView View to find the ViewRootImpl and dispatch.
- * @param keyCodeToSend The integer keycode for the event to be sent.
- * @param modifierKeyCodeToHold The integer keycode of the modifier to be held.
- */
- public static void sendKeyWhileHoldingModifier(final Instrumentation instrumentation,
- final View targetView, final int keyCodeToSend,
- final int modifierKeyCodeToHold) {
- final int metaState = getMetaStateForModifierKeyCode(modifierKeyCodeToHold);
- final long downTime = SystemClock.uptimeMillis();
-
- final KeyEvent holdKeyDown = new KeyEvent(downTime, downTime, KeyEvent.ACTION_DOWN,
- modifierKeyCodeToHold, 0 /* repeat */);
- sendKey(instrumentation ,targetView, holdKeyDown);
-
- final KeyEvent keyDown = new KeyEvent(downTime, downTime, KeyEvent.ACTION_DOWN,
- keyCodeToSend, 0 /* repeat */, metaState);
- sendKey(instrumentation, targetView, keyDown);
-
- final KeyEvent keyUp = new KeyEvent(downTime, downTime, KeyEvent.ACTION_UP,
- keyCodeToSend, 0 /* repeat */, metaState);
- sendKey(instrumentation, targetView, keyUp);
-
- final KeyEvent holdKeyUp = new KeyEvent(downTime, downTime, KeyEvent.ACTION_UP,
- modifierKeyCodeToHold, 0 /* repeat */);
- sendKey(instrumentation, targetView, holdKeyUp);
-
- instrumentation.waitForIdleSync();
- }
-
- private static int getMetaStateForModifierKeyCode(int modifierKeyCode) {
- if (!KeyEvent.isModifierKey(modifierKeyCode)) {
- throw new IllegalArgumentException("Modifier key expected, but got: "
- + KeyEvent.keyCodeToString(modifierKeyCode));
- }
-
- int metaState;
- switch (modifierKeyCode) {
- case KeyEvent.KEYCODE_SHIFT_LEFT:
- metaState = KeyEvent.META_SHIFT_LEFT_ON;
- break;
- case KeyEvent.KEYCODE_SHIFT_RIGHT:
- metaState = KeyEvent.META_SHIFT_RIGHT_ON;
- break;
- case KeyEvent.KEYCODE_ALT_LEFT:
- metaState = KeyEvent.META_ALT_LEFT_ON;
- break;
- case KeyEvent.KEYCODE_ALT_RIGHT:
- metaState = KeyEvent.META_ALT_RIGHT_ON;
- break;
- case KeyEvent.KEYCODE_CTRL_LEFT:
- metaState = KeyEvent.META_CTRL_LEFT_ON;
- break;
- case KeyEvent.KEYCODE_CTRL_RIGHT:
- metaState = KeyEvent.META_CTRL_RIGHT_ON;
- break;
- case KeyEvent.KEYCODE_META_LEFT:
- metaState = KeyEvent.META_META_LEFT_ON;
- break;
- case KeyEvent.KEYCODE_META_RIGHT:
- metaState = KeyEvent.META_META_RIGHT_ON;
- break;
- case KeyEvent.KEYCODE_SYM:
- metaState = KeyEvent.META_SYM_ON;
- break;
- case KeyEvent.KEYCODE_NUM:
- metaState = KeyEvent.META_NUM_LOCK_ON;
- break;
- case KeyEvent.KEYCODE_FUNCTION:
- metaState = KeyEvent.META_FUNCTION_ON;
- break;
- default:
- // Safety net: all modifier keys need to have at least one meta state associated.
- throw new UnsupportedOperationException("No meta state associated with "
- + "modifier key: " + KeyEvent.keyCodeToString(modifierKeyCode));
- }
-
- return KeyEvent.normalizeMetaState(metaState);
- }
-
- private static KeyEvent[] getKeyEvents(final String text) {
- KeyCharacterMap keyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
- return keyCharacterMap.getEvents(text.toCharArray());
- }
-
- private static void validateNotAppThread() {
- if (Looper.myLooper() == Looper.getMainLooper()) {
- throw new RuntimeException(
- "This method can not be called from the main application thread");
- }
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/CtsMockitoUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/CtsMockitoUtils.java
deleted file mode 100644
index 54985dc..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/CtsMockitoUtils.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.util;
-
-import org.mockito.verification.VerificationMode;
-
-public class CtsMockitoUtils {
- private CtsMockitoUtils() {}
-
- public static VerificationMode within(long timeout) {
- return new Within(timeout);
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/CtsMouseUtil.java b/common/device-side/util/src/com/android/compatibility/common/util/CtsMouseUtil.java
deleted file mode 100644
index e53eb08..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/CtsMouseUtil.java
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import static org.mockito.Matchers.argThat;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-
-import android.app.Instrumentation;
-import android.app.UiAutomation;
-import android.os.SystemClock;
-import android.view.InputDevice;
-import android.view.MotionEvent;
-import android.view.View;
-
-import org.mockito.ArgumentMatcher;
-import org.mockito.InOrder;
-
-public final class CtsMouseUtil {
-
- private CtsMouseUtil() {}
-
- public static View.OnHoverListener installHoverListener(View view) {
- return installHoverListener(view, true);
- }
-
- public static View.OnHoverListener installHoverListener(View view, boolean result) {
- final View.OnHoverListener mockListener = mock(View.OnHoverListener.class);
- view.setOnHoverListener((v, event) -> {
- // Clone the event to work around event instance reuse in the framework.
- mockListener.onHover(v, MotionEvent.obtain(event));
- return result;
- });
- return mockListener;
- }
-
- public static void clearHoverListener(View view) {
- view.setOnHoverListener(null);
- }
-
- public static MotionEvent obtainMouseEvent(int action, View anchor, int offsetX, int offsetY) {
- final long eventTime = SystemClock.uptimeMillis();
- final int[] screenPos = new int[2];
- anchor.getLocationOnScreen(screenPos);
- final int x = screenPos[0] + offsetX;
- final int y = screenPos[1] + offsetY;
- MotionEvent event = MotionEvent.obtain(eventTime, eventTime, action, x, y, 0);
- event.setSource(InputDevice.SOURCE_MOUSE);
- return event;
- }
-
- /**
- * Emulates a hover move on a point relative to the top-left corner of the passed {@link View}.
- * Offset parameters are used to compute the final screen coordinates of the tap point.
- *
- * @param instrumentation the instrumentation used to run the test
- * @param anchor the anchor view to determine the tap location on the screen
- * @param offsetX extra X offset for the move
- * @param offsetY extra Y offset for the move
- */
- public static void emulateHoverOnView(Instrumentation instrumentation, View anchor, int offsetX,
- int offsetY) {
- final long downTime = SystemClock.uptimeMillis();
- final UiAutomation uiAutomation = instrumentation.getUiAutomation();
- final int[] screenPos = new int[2];
- anchor.getLocationOnScreen(screenPos);
- final int x = screenPos[0] + offsetX;
- final int y = screenPos[1] + offsetY;
-
- injectHoverEvent(uiAutomation, downTime, x, y);
- }
-
- private static void injectHoverEvent(UiAutomation uiAutomation, long downTime, int xOnScreen,
- int yOnScreen) {
- MotionEvent event = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_HOVER_MOVE,
- xOnScreen, yOnScreen, 0);
- event.setSource(InputDevice.SOURCE_MOUSE);
- uiAutomation.injectInputEvent(event, true);
- event.recycle();
- }
-
- public static class ActionMatcher implements ArgumentMatcher<MotionEvent> {
- private final int mAction;
-
- public ActionMatcher(int action) {
- mAction = action;
- }
-
- @Override
- public boolean matches(MotionEvent actual) {
- return actual.getAction() == mAction;
- }
-
- @Override
- public String toString() {
- return "action=" + MotionEvent.actionToString(mAction);
- }
- }
-
- public static class PositionMatcher extends ActionMatcher {
- private final int mX;
- private final int mY;
-
- public PositionMatcher(int action, int x, int y) {
- super(action);
- mX = x;
- mY = y;
- }
-
- @Override
- public boolean matches(MotionEvent actual) {
- return super.matches(actual)
- && ((int) actual.getX()) == mX
- && ((int) actual.getY()) == mY;
- }
-
- @Override
- public String toString() {
- return super.toString() + "@(" + mX + "," + mY + ")";
- }
- }
-
- public static void verifyEnterMove(View.OnHoverListener listener, View view, int moveCount) {
- final InOrder inOrder = inOrder(listener);
- verifyEnterMoveInternal(listener, view, moveCount, inOrder);
- inOrder.verifyNoMoreInteractions();
- }
-
- public static void verifyEnterMoveExit(
- View.OnHoverListener listener, View view, int moveCount) {
- final InOrder inOrder = inOrder(listener);
- verifyEnterMoveInternal(listener, view, moveCount, inOrder);
- inOrder.verify(listener, times(1)).onHover(eq(view),
- argThat(new ActionMatcher(MotionEvent.ACTION_HOVER_EXIT)));
- inOrder.verifyNoMoreInteractions();
- }
-
- private static void verifyEnterMoveInternal(
- View.OnHoverListener listener, View view, int moveCount, InOrder inOrder) {
- inOrder.verify(listener, times(1)).onHover(eq(view),
- argThat(new ActionMatcher(MotionEvent.ACTION_HOVER_ENTER)));
- inOrder.verify(listener, times(moveCount)).onHover(eq(view),
- argThat(new ActionMatcher(MotionEvent.ACTION_HOVER_MOVE)));
- }
-}
-
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/CtsTouchUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/CtsTouchUtils.java
deleted file mode 100644
index bd53549..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/CtsTouchUtils.java
+++ /dev/null
@@ -1,614 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import android.app.Instrumentation;
-import android.app.UiAutomation;
-import android.graphics.Point;
-import android.os.SystemClock;
-import android.support.test.rule.ActivityTestRule;
-import android.util.SparseArray;
-import android.view.InputDevice;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-
-/**
- * Test utilities for touch emulation.
- */
-public final class CtsTouchUtils {
- /**
- * Interface definition for a callback to be invoked when an event has been injected.
- */
- public interface EventInjectionListener {
- /**
- * Callback method to be invoked when a {MotionEvent#ACTION_DOWN} has been injected.
- * @param xOnScreen X coordinate of the injected event.
- * @param yOnScreen Y coordinate of the injected event.
- */
- public void onDownInjected(int xOnScreen, int yOnScreen);
-
- /**
- * Callback method to be invoked when a {MotionEvent#ACTION_MOVE} has been injected.
- * @param xOnScreen X coordinates of the injected event.
- * @param yOnScreen Y coordinates of the injected event.
- */
- public void onMoveInjected(int[] xOnScreen, int[] yOnScreen);
-
- /**
- * Callback method to be invoked when a {MotionEvent#ACTION_UP} has been injected.
- * @param xOnScreen X coordinate of the injected event.
- * @param yOnScreen Y coordinate of the injected event.
- */
- public void onUpInjected(int xOnScreen, int yOnScreen);
- }
-
- private CtsTouchUtils() {}
-
- /**
- * Emulates a tap in the center of the passed {@link View}.
- *
- * @param instrumentation the instrumentation used to run the test
- * @param view the view to "tap"
- */
- public static void emulateTapOnViewCenter(Instrumentation instrumentation,
- ActivityTestRule<?> activityTestRule, View view) {
- emulateTapOnView(instrumentation, activityTestRule, view, view.getWidth() / 2,
- view.getHeight() / 2);
- }
-
- /**
- * Emulates a tap on a point relative to the top-left corner of the passed {@link View}. Offset
- * parameters are used to compute the final screen coordinates of the tap point.
- *
- * @param instrumentation the instrumentation used to run the test
- * @param anchorView the anchor view to determine the tap location on the screen
- * @param offsetX extra X offset for the tap
- * @param offsetY extra Y offset for the tap
- */
- public static void emulateTapOnView(Instrumentation instrumentation,
- ActivityTestRule<?> activityTestRule, View anchorView,
- int offsetX, int offsetY) {
- final int touchSlop = ViewConfiguration.get(anchorView.getContext()).getScaledTouchSlop();
- // Get anchor coordinates on the screen
- final int[] viewOnScreenXY = new int[2];
- anchorView.getLocationOnScreen(viewOnScreenXY);
- int xOnScreen = viewOnScreenXY[0] + offsetX;
- int yOnScreen = viewOnScreenXY[1] + offsetY;
- final UiAutomation uiAutomation = instrumentation.getUiAutomation();
- final long downTime = SystemClock.uptimeMillis();
-
- injectDownEvent(uiAutomation, downTime, xOnScreen, yOnScreen, null);
- injectMoveEventForTap(uiAutomation, downTime, touchSlop, xOnScreen, yOnScreen);
- injectUpEvent(uiAutomation, downTime, false, xOnScreen, yOnScreen, null);
-
- // Wait for the system to process all events in the queue
- if (activityTestRule != null) {
- WidgetTestUtils.runOnMainAndDrawSync(activityTestRule,
- activityTestRule.getActivity().getWindow().getDecorView(), null);
- } else {
- instrumentation.waitForIdleSync();
- }
- }
-
- /**
- * Emulates a double tap in the center of the passed {@link View}.
- *
- * @param instrumentation the instrumentation used to run the test
- * @param view the view to "double tap"
- */
- public static void emulateDoubleTapOnViewCenter(Instrumentation instrumentation,
- ActivityTestRule<?> activityTestRule, View view) {
- emulateDoubleTapOnView(instrumentation, activityTestRule, view, view.getWidth() / 2,
- view.getHeight() / 2);
- }
-
- /**
- * Emulates a double tap on a point relative to the top-left corner of the passed {@link View}.
- * Offset parameters are used to compute the final screen coordinates of the tap points.
- *
- * @param instrumentation the instrumentation used to run the test
- * @param anchorView the anchor view to determine the tap location on the screen
- * @param offsetX extra X offset for the taps
- * @param offsetY extra Y offset for the taps
- */
- public static void emulateDoubleTapOnView(Instrumentation instrumentation,
- ActivityTestRule<?> activityTestRule, View anchorView,
- int offsetX, int offsetY) {
- final int touchSlop = ViewConfiguration.get(anchorView.getContext()).getScaledTouchSlop();
- // Get anchor coordinates on the screen
- final int[] viewOnScreenXY = new int[2];
- anchorView.getLocationOnScreen(viewOnScreenXY);
- int xOnScreen = viewOnScreenXY[0] + offsetX;
- int yOnScreen = viewOnScreenXY[1] + offsetY;
- final UiAutomation uiAutomation = instrumentation.getUiAutomation();
- final long downTime = SystemClock.uptimeMillis();
-
- injectDownEvent(uiAutomation, downTime, xOnScreen, yOnScreen, null);
- injectMoveEventForTap(uiAutomation, downTime, touchSlop, xOnScreen, yOnScreen);
- injectUpEvent(uiAutomation, downTime, false, xOnScreen, yOnScreen, null);
- injectDownEvent(uiAutomation, downTime, xOnScreen, yOnScreen, null);
- injectMoveEventForTap(uiAutomation, downTime, touchSlop, xOnScreen, yOnScreen);
- injectUpEvent(uiAutomation, downTime, false, xOnScreen, yOnScreen, null);
-
- // Wait for the system to process all events in the queue
- if (activityTestRule != null) {
- WidgetTestUtils.runOnMainAndDrawSync(activityTestRule,
- activityTestRule.getActivity().getWindow().getDecorView(), null);
- } else {
- instrumentation.waitForIdleSync();
- }
- }
-
- /**
- * Emulates a linear drag gesture between 2 points across the screen.
- *
- * @param instrumentation the instrumentation used to run the test
- * @param dragStartX Start X of the emulated drag gesture
- * @param dragStartY Start Y of the emulated drag gesture
- * @param dragAmountX X amount of the emulated drag gesture
- * @param dragAmountY Y amount of the emulated drag gesture
- */
- public static void emulateDragGesture(Instrumentation instrumentation,
- ActivityTestRule<?> activityTestRule,
- int dragStartX, int dragStartY, int dragAmountX, int dragAmountY) {
- emulateDragGesture(instrumentation, activityTestRule,
- dragStartX, dragStartY, dragAmountX, dragAmountY,
- 2000, 20, null);
- }
-
- private static void emulateDragGesture(Instrumentation instrumentation,
- ActivityTestRule<?> activityTestRule,
- int dragStartX, int dragStartY, int dragAmountX, int dragAmountY,
- int dragDurationMs, int moveEventCount) {
- emulateDragGesture(instrumentation, activityTestRule,
- dragStartX, dragStartY, dragAmountX, dragAmountY,
- dragDurationMs, moveEventCount, null);
- }
-
- private static void emulateDragGesture(Instrumentation instrumentation,
- ActivityTestRule<?> activityTestRule,
- int dragStartX, int dragStartY, int dragAmountX, int dragAmountY,
- int dragDurationMs, int moveEventCount,
- EventInjectionListener eventInjectionListener) {
- // We are using the UiAutomation object to inject events so that drag works
- // across view / window boundaries (such as for the emulated drag and drop
- // sequences)
- final UiAutomation uiAutomation = instrumentation.getUiAutomation();
- final long downTime = SystemClock.uptimeMillis();
-
- injectDownEvent(uiAutomation, downTime, dragStartX, dragStartY, eventInjectionListener);
-
- // Inject a sequence of MOVE events that emulate the "move" part of the gesture
- injectMoveEventsForDrag(uiAutomation, downTime, true, dragStartX, dragStartY,
- dragStartX + dragAmountX, dragStartY + dragAmountY, moveEventCount, dragDurationMs,
- eventInjectionListener);
-
- injectUpEvent(uiAutomation, downTime, true, dragStartX + dragAmountX,
- dragStartY + dragAmountY, eventInjectionListener);
-
- // Wait for the system to process all events in the queue
- if (activityTestRule != null) {
- WidgetTestUtils.runOnMainAndDrawSync(activityTestRule,
- activityTestRule.getActivity().getWindow().getDecorView(), null);
- } else {
- instrumentation.waitForIdleSync();
- }
- }
-
- /**
- * Emulates a series of linear drag gestures across the screen between multiple points without
- * lifting the finger. Note that this function does not support curve movements between the
- * points.
- *
- * @param instrumentation the instrumentation used to run the test
- * @param coordinates the ordered list of points for the drag gesture
- */
- public static void emulateDragGesture(Instrumentation instrumentation,
- ActivityTestRule<?> activityTestRule, SparseArray<Point> coordinates) {
- emulateDragGesture(instrumentation, activityTestRule, coordinates, 2000, 20);
- }
-
- private static void emulateDragGesture(Instrumentation instrumentation,
- ActivityTestRule<?> activityTestRule,
- SparseArray<Point> coordinates, int dragDurationMs, int moveEventCount) {
- final int coordinatesSize = coordinates.size();
- if (coordinatesSize < 2) {
- throw new IllegalArgumentException("Need at least 2 points for emulating drag");
- }
- // We are using the UiAutomation object to inject events so that drag works
- // across view / window boundaries (such as for the emulated drag and drop
- // sequences)
- final UiAutomation uiAutomation = instrumentation.getUiAutomation();
- final long downTime = SystemClock.uptimeMillis();
-
- injectDownEvent(uiAutomation, downTime, coordinates.get(0).x, coordinates.get(0).y, null);
-
- // Move to each coordinate.
- for (int i = 0; i < coordinatesSize - 1; i++) {
- // Inject a sequence of MOVE events that emulate the "move" part of the gesture.
- injectMoveEventsForDrag(uiAutomation,
- downTime,
- true,
- coordinates.get(i).x,
- coordinates.get(i).y,
- coordinates.get(i + 1).x,
- coordinates.get(i + 1).y,
- moveEventCount,
- dragDurationMs,
- null);
- }
-
- injectUpEvent(uiAutomation,
- downTime,
- true,
- coordinates.get(coordinatesSize - 1).x,
- coordinates.get(coordinatesSize - 1).y,
- null);
-
- // Wait for the system to process all events in the queue
- if (activityTestRule != null) {
- WidgetTestUtils.runOnMainAndDrawSync(activityTestRule,
- activityTestRule.getActivity().getWindow().getDecorView(), null);
- } else {
- instrumentation.waitForIdleSync();
- }
- }
-
- private static long injectDownEvent(UiAutomation uiAutomation, long downTime, int xOnScreen,
- int yOnScreen, EventInjectionListener eventInjectionListener) {
- MotionEvent eventDown = MotionEvent.obtain(
- downTime, downTime, MotionEvent.ACTION_DOWN, xOnScreen, yOnScreen, 1);
- eventDown.setSource(InputDevice.SOURCE_TOUCHSCREEN);
- uiAutomation.injectInputEvent(eventDown, true);
- if (eventInjectionListener != null) {
- eventInjectionListener.onDownInjected(xOnScreen, yOnScreen);
- }
- eventDown.recycle();
- return downTime;
- }
-
- private static void injectMoveEventForTap(UiAutomation uiAutomation, long downTime,
- int touchSlop, int xOnScreen, int yOnScreen) {
- MotionEvent eventMove = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_MOVE,
- xOnScreen + (touchSlop / 2.0f), yOnScreen + (touchSlop / 2.0f), 1);
- eventMove.setSource(InputDevice.SOURCE_TOUCHSCREEN);
- uiAutomation.injectInputEvent(eventMove, true);
- eventMove.recycle();
- }
-
- private static void injectMoveEventsForDrag(UiAutomation uiAutomation, long downTime,
- boolean useCurrentEventTime, int dragStartX, int dragStartY, int dragEndX, int dragEndY,
- int moveEventCount, int dragDurationMs, EventInjectionListener eventInjectionListener) {
- final int dragAmountX = dragEndX - dragStartX;
- final int dragAmountY = dragEndY - dragStartY;
- final int sleepTime = dragDurationMs / moveEventCount;
-
- // sleep for a bit to emulate the overall drag gesture.
- long prevEventTime = downTime;
- SystemClock.sleep(sleepTime);
- for (int i = 0; i < moveEventCount; i++) {
- // Note that the first MOVE event is generated "away" from the coordinates
- // of the start / DOWN event, and the last MOVE event is generated
- // at the same coordinates as the subsequent UP event.
- final int moveX = dragStartX + dragAmountX * (i + 1) / moveEventCount;
- final int moveY = dragStartY + dragAmountY * (i + 1) / moveEventCount;
- long eventTime = useCurrentEventTime ? SystemClock.uptimeMillis() : downTime;
-
- // If necessary, generate history for our next MOVE event. The history is generated
- // to be spaced at 10 millisecond intervals, interpolating the coordinates from the
- // last generated MOVE event to our current one.
- int historyEventCount = (int) ((eventTime - prevEventTime) / 10);
- int[] xCoordsForListener = (eventInjectionListener == null) ? null :
- new int[Math.max(1, historyEventCount)];
- int[] yCoordsForListener = (eventInjectionListener == null) ? null :
- new int[Math.max(1, historyEventCount)];
- MotionEvent eventMove = null;
- if (historyEventCount == 0) {
- eventMove = MotionEvent.obtain(
- downTime, eventTime, MotionEvent.ACTION_MOVE, moveX, moveY, 1);
- if (eventInjectionListener != null) {
- xCoordsForListener[0] = moveX;
- yCoordsForListener[0] = moveY;
- }
- } else {
- final int prevMoveX = dragStartX + dragAmountX * i / moveEventCount;
- final int prevMoveY = dragStartY + dragAmountY * i / moveEventCount;
- final int deltaMoveX = moveX - prevMoveX;
- final int deltaMoveY = moveY - prevMoveY;
- final long deltaTime = (eventTime - prevEventTime);
- for (int historyIndex = 0; historyIndex < historyEventCount; historyIndex++) {
- int stepMoveX = prevMoveX + deltaMoveX * (historyIndex + 1) / historyEventCount;
- int stepMoveY = prevMoveY + deltaMoveY * (historyIndex + 1) / historyEventCount;
- long stepEventTime = useCurrentEventTime
- ? prevEventTime + deltaTime * (historyIndex + 1) / historyEventCount
- : downTime;
- if (historyIndex == 0) {
- // Generate the first event in our sequence
- eventMove = MotionEvent.obtain(downTime, stepEventTime,
- MotionEvent.ACTION_MOVE, stepMoveX, stepMoveY, 1);
- } else {
- // and then add to it
- eventMove.addBatch(stepEventTime, stepMoveX, stepMoveY, 1.0f, 1.0f, 1);
- }
- if (eventInjectionListener != null) {
- xCoordsForListener[historyIndex] = stepMoveX;
- yCoordsForListener[historyIndex] = stepMoveY;
- }
- }
- }
-
- eventMove.setSource(InputDevice.SOURCE_TOUCHSCREEN);
- uiAutomation.injectInputEvent(eventMove, true);
- if (eventInjectionListener != null) {
- eventInjectionListener.onMoveInjected(xCoordsForListener, yCoordsForListener);
- }
- eventMove.recycle();
- prevEventTime = eventTime;
-
- // sleep for a bit to emulate the overall drag gesture.
- SystemClock.sleep(sleepTime);
- }
- }
-
- private static void injectUpEvent(UiAutomation uiAutomation, long downTime,
- boolean useCurrentEventTime, int xOnScreen, int yOnScreen,
- EventInjectionListener eventInjectionListener) {
- long eventTime = useCurrentEventTime ? SystemClock.uptimeMillis() : downTime;
- MotionEvent eventUp = MotionEvent.obtain(
- downTime, eventTime, MotionEvent.ACTION_UP, xOnScreen, yOnScreen, 1);
- eventUp.setSource(InputDevice.SOURCE_TOUCHSCREEN);
- uiAutomation.injectInputEvent(eventUp, true);
- if (eventInjectionListener != null) {
- eventInjectionListener.onUpInjected(xOnScreen, yOnScreen);
- }
- eventUp.recycle();
- }
-
- /**
- * Emulates a fling gesture across the horizontal center of the passed view.
- *
- * @param instrumentation the instrumentation used to run the test
- * @param view the view to fling
- * @param isDownwardsFlingGesture if <code>true</code>, the emulated fling will
- * be a downwards gesture
- * @return The vertical amount of emulated fling in pixels
- */
- public static int emulateFlingGesture(Instrumentation instrumentation,
- ActivityTestRule<?> activityTestRule, View view, boolean isDownwardsFlingGesture) {
- return emulateFlingGesture(instrumentation, activityTestRule,
- view, isDownwardsFlingGesture, null);
- }
-
- /**
- * Emulates a fling gesture across the horizontal center of the passed view.
- *
- * @param instrumentation the instrumentation used to run the test
- * @param view the view to fling
- * @param isDownwardsFlingGesture if <code>true</code>, the emulated fling will
- * be a downwards gesture
- * @param eventInjectionListener optional listener to notify about the injected events
- * @return The vertical amount of emulated fling in pixels
- */
- public static int emulateFlingGesture(Instrumentation instrumentation,
- ActivityTestRule<?> activityTestRule, View view, boolean isDownwardsFlingGesture,
- EventInjectionListener eventInjectionListener) {
- final ViewConfiguration configuration = ViewConfiguration.get(view.getContext());
- final int flingVelocity = (configuration.getScaledMinimumFlingVelocity() +
- configuration.getScaledMaximumFlingVelocity()) / 2;
- // Get view coordinates on the screen
- final int[] viewOnScreenXY = new int[2];
- view.getLocationOnScreen(viewOnScreenXY);
-
- // Our fling gesture will be from 25% height of the view to 75% height of the view
- // for downwards fling gesture, and the other way around for upwards fling gesture
- final int viewHeight = view.getHeight();
- final int x = viewOnScreenXY[0] + view.getWidth() / 2;
- final int startY = isDownwardsFlingGesture ? viewOnScreenXY[1] + viewHeight / 4
- : viewOnScreenXY[1] + 3 * viewHeight / 4;
- final int amountY = isDownwardsFlingGesture ? viewHeight / 2 : -viewHeight / 2;
-
- // Compute fling gesture duration based on the distance (50% height of the view) and
- // fling velocity
- final int durationMs = (1000 * viewHeight) / (2 * flingVelocity);
-
- // And do the same event injection sequence as our generic drag gesture
- emulateDragGesture(instrumentation, activityTestRule,
- x, startY, 0, amountY, durationMs, durationMs / 16,
- eventInjectionListener);
-
- return amountY;
- }
-
- private static class ViewStateSnapshot {
- final View mFirst;
- final View mLast;
- final int mFirstTop;
- final int mLastBottom;
- final int mChildCount;
- private ViewStateSnapshot(ViewGroup viewGroup) {
- mChildCount = viewGroup.getChildCount();
- if (mChildCount == 0) {
- mFirst = mLast = null;
- mFirstTop = mLastBottom = Integer.MIN_VALUE;
- } else {
- mFirst = viewGroup.getChildAt(0);
- mLast = viewGroup.getChildAt(mChildCount - 1);
- mFirstTop = mFirst.getTop();
- mLastBottom = mLast.getBottom();
- }
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
-
- final ViewStateSnapshot that = (ViewStateSnapshot) o;
- return mFirstTop == that.mFirstTop &&
- mLastBottom == that.mLastBottom &&
- mFirst == that.mFirst &&
- mLast == that.mLast &&
- mChildCount == that.mChildCount;
- }
-
- @Override
- public int hashCode() {
- int result = mFirst != null ? mFirst.hashCode() : 0;
- result = 31 * result + (mLast != null ? mLast.hashCode() : 0);
- result = 31 * result + mFirstTop;
- result = 31 * result + mLastBottom;
- result = 31 * result + mChildCount;
- return result;
- }
- }
-
- /**
- * Emulates a scroll to the bottom of the specified {@link ViewGroup}.
- *
- * @param instrumentation the instrumentation used to run the test
- * @param viewGroup View group
- */
- public static void emulateScrollToBottom(Instrumentation instrumentation,
- ActivityTestRule<?> activityTestRule, ViewGroup viewGroup) {
- final int[] viewGroupOnScreenXY = new int[2];
- viewGroup.getLocationOnScreen(viewGroupOnScreenXY);
-
- final int emulatedX = viewGroupOnScreenXY[0] + viewGroup.getWidth() / 2;
- final int emulatedStartY = viewGroupOnScreenXY[1] + 3 * viewGroup.getHeight() / 4;
- final int swipeAmount = viewGroup.getHeight() / 2;
-
- ViewStateSnapshot prev;
- ViewStateSnapshot next = new ViewStateSnapshot(viewGroup);
- do {
- prev = next;
- emulateDragGesture(instrumentation, activityTestRule,
- emulatedX, emulatedStartY, 0, -swipeAmount, 300, 10);
- next = new ViewStateSnapshot(viewGroup);
- } while (!prev.equals(next));
- }
-
- /**
- * Emulates a long press in the center of the passed {@link View}.
- *
- * @param instrumentation the instrumentation used to run the test
- * @param view the view to "long press"
- */
- public static void emulateLongPressOnViewCenter(Instrumentation instrumentation,
- ActivityTestRule<?> activityTestRule, View view) {
- emulateLongPressOnViewCenter(instrumentation, activityTestRule, view, 0);
- }
-
- /**
- * Emulates a long press in the center of the passed {@link View}.
- *
- * @param instrumentation the instrumentation used to run the test
- * @param view the view to "long press"
- * @param extraWaitMs the duration of emulated "long press" in milliseconds starting
- * after system-level long press timeout.
- */
- public static void emulateLongPressOnViewCenter(Instrumentation instrumentation,
- ActivityTestRule<?> activityTestRule, View view, long extraWaitMs) {
- final int touchSlop = ViewConfiguration.get(view.getContext()).getScaledTouchSlop();
- // Use instrumentation to emulate a tap on the spinner to bring down its popup
- final int[] viewOnScreenXY = new int[2];
- view.getLocationOnScreen(viewOnScreenXY);
- int xOnScreen = viewOnScreenXY[0] + view.getWidth() / 2;
- int yOnScreen = viewOnScreenXY[1] + view.getHeight() / 2;
-
- emulateLongPressOnScreen(instrumentation, activityTestRule,
- xOnScreen, yOnScreen, touchSlop, extraWaitMs, true);
- }
-
- /**
- * Emulates a long press confirmed on a point relative to the top-left corner of the passed
- * {@link View}. Offset parameters are used to compute the final screen coordinates of the
- * press point.
- *
- * @param instrumentation the instrumentation used to run the test
- * @param view the view to "long press"
- * @param offsetX extra X offset for the tap
- * @param offsetY extra Y offset for the tap
- */
- public static void emulateLongPressOnView(Instrumentation instrumentation,
- ActivityTestRule<?> activityTestRule, View view, int offsetX, int offsetY) {
- final int touchSlop = ViewConfiguration.get(view.getContext()).getScaledTouchSlop();
- final int[] viewOnScreenXY = new int[2];
- view.getLocationOnScreen(viewOnScreenXY);
- int xOnScreen = viewOnScreenXY[0] + offsetX;
- int yOnScreen = viewOnScreenXY[1] + offsetY;
-
- emulateLongPressOnScreen(instrumentation, activityTestRule,
- xOnScreen, yOnScreen, touchSlop, 0, true);
- }
-
- /**
- * Emulates a long press then a linear drag gesture between 2 points across the screen.
- * This is used for drag selection.
- *
- * @param instrumentation the instrumentation used to run the test
- * @param dragStartX Start X of the emulated drag gesture
- * @param dragStartY Start Y of the emulated drag gesture
- * @param dragAmountX X amount of the emulated drag gesture
- * @param dragAmountY Y amount of the emulated drag gesture
- */
- public static void emulateLongPressAndDragGesture(Instrumentation instrumentation,
- ActivityTestRule<?> activityTestRule,
- int dragStartX, int dragStartY, int dragAmountX, int dragAmountY) {
- emulateLongPressOnScreen(instrumentation, activityTestRule, dragStartX, dragStartY,
- 0 /* touchSlop */, 0 /* extraWaitMs */, false /* upGesture */);
- emulateDragGesture(instrumentation, activityTestRule, dragStartX, dragStartY, dragAmountX,
- dragAmountY);
- }
-
- /**
- * Emulates a long press on the screen.
- *
- * @param instrumentation the instrumentation used to run the test
- * @param xOnScreen X position on screen for the "long press"
- * @param yOnScreen Y position on screen for the "long press"
- * @param extraWaitMs extra duration of emulated long press in milliseconds added
- * after the system-level "long press" timeout.
- * @param upGesture whether to include an up event.
- */
- private static void emulateLongPressOnScreen(Instrumentation instrumentation,
- ActivityTestRule<?> activityTestRule,
- int xOnScreen, int yOnScreen, int touchSlop, long extraWaitMs, boolean upGesture) {
- final UiAutomation uiAutomation = instrumentation.getUiAutomation();
- final long downTime = SystemClock.uptimeMillis();
-
- injectDownEvent(uiAutomation, downTime, xOnScreen, yOnScreen, null);
- injectMoveEventForTap(uiAutomation, downTime, touchSlop, xOnScreen, yOnScreen);
- SystemClock.sleep((long) (ViewConfiguration.getLongPressTimeout() * 1.5f) + extraWaitMs);
- if (upGesture) {
- injectUpEvent(uiAutomation, downTime, false, xOnScreen, yOnScreen, null);
- }
-
- // Wait for the system to process all events in the queue
- if (activityTestRule != null) {
- WidgetTestUtils.runOnMainAndDrawSync(activityTestRule,
- activityTestRule.getActivity().getWindow().getDecorView(), null);
- } else {
- instrumentation.waitForIdleSync();
- }
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/DeviceConfigStateChangerRule.java b/common/device-side/util/src/com/android/compatibility/common/util/DeviceConfigStateChangerRule.java
deleted file mode 100644
index a13da52..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/DeviceConfigStateChangerRule.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.util;
-
-import android.content.Context;
-import android.provider.DeviceConfig;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-/**
- * JUnit rule used to set a {@link DeviceConfig} preference before the test is run.
- *
- * <p>It stores the current value before the test, changes it (if necessary), then restores it after
- * the test (if necessary).
- */
-public class DeviceConfigStateChangerRule extends StateChangerRule<String> {
-
- /**
- * Default constructor.
- *
- * @param context context used to retrieve the {@link DeviceConfig} provider.
- * @param namespace {@code DeviceConfig} namespace.
- * @param key prefence key.
- * @param value value to be set before the test is run.
- */
- public DeviceConfigStateChangerRule(@NonNull Context context, @NonNull String namespace,
- @NonNull String key, @Nullable String value) {
- this(new DeviceConfigStateManager(context, namespace, key), value);
- }
-
- /**
- * Alternative constructor used when then test case already defines a
- * {@link DeviceConfigStateManager}.
- */
- public DeviceConfigStateChangerRule(@NonNull DeviceConfigStateManager dcsm,
- @Nullable String value) {
- super(dcsm, value);
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/DeviceConfigStateKeeperRule.java b/common/device-side/util/src/com/android/compatibility/common/util/DeviceConfigStateKeeperRule.java
deleted file mode 100644
index 6f186de..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/DeviceConfigStateKeeperRule.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.util;
-
-import android.content.Context;
-import android.provider.DeviceConfig;
-
-import androidx.annotation.NonNull;
-
-/**
- * JUnit rule used to restore a {@link DeviceConfig} preference after the test is run.
- *
- * <p>It stores the current value before the test, and restores it after the test (if necessary).
- */
-public class DeviceConfigStateKeeperRule extends StateKeeperRule<String> {
-
- /**
- * Default constructor.
- *
- * @param context context used to retrieve the {@link DeviceConfig} provider.
- * @param namespace {@code DeviceConfig} namespace.
- * @param key prefence key.
- */
- public DeviceConfigStateKeeperRule(@NonNull Context context, @NonNull String namespace,
- @NonNull String key) {
- super(new DeviceConfigStateManager(context, namespace, key));
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/DeviceConfigStateManager.java b/common/device-side/util/src/com/android/compatibility/common/util/DeviceConfigStateManager.java
deleted file mode 100644
index 040641c..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/DeviceConfigStateManager.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
-
-import android.content.Context;
-import android.provider.DeviceConfig;
-import android.provider.Settings;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.google.common.base.Preconditions;
-
-import java.util.concurrent.atomic.AtomicReference;
-
-/**
- * Manages the state of a preference backed by {@link DeviceConfig}.
- */
-public final class DeviceConfigStateManager implements StateManager<String> {
-
- private static final String TAG = DeviceConfigStateManager.class.getSimpleName();
-
- private final Context mContext;
- private final String mNamespace;
- private final String mKey;
-
- /**
- * Default constructor.
- *
- * @param context context used to retrieve the {@link Settings} provider.
- * @param namespace settings namespace.
- * @param key prefence key.
- */
- public DeviceConfigStateManager(@NonNull Context context, @NonNull String namespace,
- @NonNull String key) {
- debug("DeviceConfigStateManager", "namespace=%s, key=%s", namespace, key);
-
- mContext = Preconditions.checkNotNull(context);
- mNamespace = Preconditions.checkNotNull(namespace);
- mKey = Preconditions.checkNotNull(key);
- }
-
- @Override
- public void set(@Nullable String value) {
- debug("set", "new value is %s", value);
- runWithShellPermissionIdentity(() -> setWithPermissionsGranted(value),
- "android.permission.READ_DEVICE_CONFIG", "android.permission.WRITE_DEVICE_CONFIG");
- }
-
- private void setWithPermissionsGranted(@Nullable String value) {
- final OneTimeDeviceConfigListener listener = new OneTimeDeviceConfigListener(mNamespace,
- mKey);
- DeviceConfig.addOnPropertiesChangedListener(mNamespace, mContext.getMainExecutor(),
- listener);
-
- DeviceConfig.setProperty(mNamespace, mKey, value, /* makeDefault= */ false);
- listener.assertCalled();
- }
-
- @Override
- @Nullable
- public String get() {
- final AtomicReference<String> reference = new AtomicReference<>();
- runWithShellPermissionIdentity(()
- -> reference.set(DeviceConfig.getProperty(mNamespace, mKey)),
- "android.permission.READ_DEVICE_CONFIG");
- debug("get", "returning %s", reference.get());
-
- return reference.get();
- }
-
- private void debug(@NonNull String methodName, @NonNull String msg, Object...args) {
- if (!Log.isLoggable(TAG, Log.DEBUG)) return;
-
- final String prefix = String.format("%s(%s:%s): ", methodName, mNamespace, mKey);
- Log.d(TAG, prefix + String.format(msg, args));
- }
-
- @Override
- public String toString() {
- return "DeviceConfigStateManager[namespace=" + mNamespace + ", key=" + mKey + "]";
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/DeviceInfoStore.java b/common/device-side/util/src/com/android/compatibility/common/util/DeviceInfoStore.java
deleted file mode 100644
index 03f69fa..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/DeviceInfoStore.java
+++ /dev/null
@@ -1,260 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.util;
-
-import android.util.JsonWriter;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-import java.util.List;
-
-public class DeviceInfoStore extends InfoStore {
-
- protected File mJsonFile;
- protected JsonWriter mJsonWriter = null;
-
- public DeviceInfoStore() {
- mJsonFile = null;
- }
-
- public DeviceInfoStore(File file) throws Exception {
- mJsonFile = file;
- }
-
- /**
- * Opens the file for storage and creates the writer.
- */
- @Override
- public void open() throws IOException {
- FileOutputStream out = new FileOutputStream(mJsonFile);
- mJsonWriter = new JsonWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8));
- // TODO(agathaman): remove to make json output less pretty
- mJsonWriter.setIndent(" ");
- mJsonWriter.beginObject();
- }
-
- /**
- * Closes the writer.
- */
- @Override
- public void close() throws Exception {
- mJsonWriter.endObject();
- mJsonWriter.flush();
- mJsonWriter.close();
- }
-
- /**
- * Start a new group of result.
- */
- @Override
- public void startGroup() throws IOException {
- mJsonWriter.beginObject();
- }
-
- /**
- * Start a new group of result with specified name.
- */
- @Override
- public void startGroup(String name) throws IOException {
- mJsonWriter.name(name);
- mJsonWriter.beginObject();
- }
-
- /**
- * Complete adding result to the last started group.
- */
- @Override
- public void endGroup() throws IOException {
- mJsonWriter.endObject();
- }
-
- /**
- * Start a new array of result.
- */
- @Override
- public void startArray() throws IOException {
- mJsonWriter.beginArray();
- }
-
- /**
- * Start a new array of result with specified name.
- */
- @Override
- public void startArray(String name) throws IOException {
- checkName(name);
- mJsonWriter.name(name);
- mJsonWriter.beginArray();
- }
-
- /**
- * Complete adding result to the last started array.
- */
- @Override
- public void endArray() throws IOException {
- mJsonWriter.endArray();
- }
-
- /**
- * Adds a int value to the InfoStore
- */
- @Override
- public void addResult(String name, int value) throws IOException {
- checkName(name);
- mJsonWriter.name(name);
- mJsonWriter.value(value);
- }
-
- /**
- * Adds a long value to the InfoStore
- */
- @Override
- public void addResult(String name, long value) throws IOException {
- checkName(name);
- mJsonWriter.name(name);
- mJsonWriter.value(value);
- }
-
- /**
- * Adds a float value to the InfoStore
- */
- @Override
- public void addResult(String name, float value) throws IOException {
- addResult(name, (double) value);
- }
-
- /**
- * Adds a double value to the InfoStore
- */
- @Override
- public void addResult(String name, double value) throws IOException {
- checkName(name);
- if (isDoubleNaNOrInfinite(value)) {
- return;
- } else {
- mJsonWriter.name(name);
- mJsonWriter.value(value);
- }
- }
-
- /**
- * Adds a boolean value to the InfoStore
- */
- @Override
- public void addResult(String name, boolean value) throws IOException {
- checkName(name);
- mJsonWriter.name(name);
- mJsonWriter.value(value);
- }
-
- /**
- * Adds a String value to the InfoStore
- */
- @Override
- public void addResult(String name, String value) throws IOException {
- checkName(name);
- mJsonWriter.name(name);
- mJsonWriter.value(checkString(value));
- }
-
- /**
- * Adds a int array to the InfoStore
- */
- @Override
- public void addArrayResult(String name, int[] array) throws IOException {
- checkName(name);
- mJsonWriter.name(name);
- mJsonWriter.beginArray();
- for (int value : checkArray(array)) {
- mJsonWriter.value(value);
- }
- mJsonWriter.endArray();
- }
-
- /**
- * Adds a long array to the InfoStore
- */
- @Override
- public void addArrayResult(String name, long[] array) throws IOException {
- checkName(name);
- mJsonWriter.name(name);
- mJsonWriter.beginArray();
- for (long value : checkArray(array)) {
- mJsonWriter.value(value);
- }
- mJsonWriter.endArray();
- }
-
- /**
- * Adds a float array to the InfoStore
- */
- @Override
- public void addArrayResult(String name, float[] array) throws IOException {
- double[] doubleArray = new double[array.length];
- for (int i = 0; i < array.length; i++) {
- doubleArray[i] = array[i];
- }
- addArrayResult(name, doubleArray);
- }
-
- /**
- * Adds a double array to the InfoStore
- */
- @Override
- public void addArrayResult(String name, double[] array) throws IOException {
- checkName(name);
- mJsonWriter.name(name);
- mJsonWriter.beginArray();
- for (double value : checkArray(array)) {
- if (isDoubleNaNOrInfinite(value)) {
- continue;
- }
- mJsonWriter.value(value);
- }
- mJsonWriter.endArray();
- }
-
- /**
- * Adds a boolean array to the InfoStore
- */
- @Override
- public void addArrayResult(String name, boolean[] array) throws IOException {
- checkName(name);
- mJsonWriter.name(name);
- mJsonWriter.beginArray();
- for (boolean value : checkArray(array)) {
- mJsonWriter.value(value);
- }
- mJsonWriter.endArray();
- }
-
- /**
- * Adds a List of String to the InfoStore
- */
- @Override
- public void addListResult(String name, List<String> list) throws IOException {
- checkName(name);
- mJsonWriter.name(name);
- mJsonWriter.beginArray();
- for (String value : checkStringList(list)) {
- mJsonWriter.value(checkString(value));
- }
- mJsonWriter.endArray();
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/DeviceReportLog.java b/common/device-side/util/src/com/android/compatibility/common/util/DeviceReportLog.java
deleted file mode 100644
index d170263..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/DeviceReportLog.java
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import android.app.Instrumentation;
-import android.os.Bundle;
-import android.os.Environment;
-import android.util.Log;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.List;
-
-/**
- * Handles adding results to the report for device side tests.
- *
- * NOTE: tests MUST call {@link #submit(Instrumentation)} if and only if the test passes in order to
- * send the results to the runner.
- */
-public class DeviceReportLog extends ReportLog {
- private static final String TAG = DeviceReportLog.class.getSimpleName();
- private static final String RESULT = "COMPATIBILITY_TEST_RESULT";
- private static final int INST_STATUS_ERROR = -1;
- private static final int INST_STATUS_IN_PROGRESS = 2;
-
- private ReportLogDeviceInfoStore store;
-
- public DeviceReportLog(String reportLogName, String streamName) {
- this(reportLogName, streamName,
- new File(Environment.getExternalStorageDirectory(), "report-log-files"));
- }
-
- public DeviceReportLog(String reportLogName, String streamName, File logDirectory) {
- super(reportLogName, streamName);
- try {
- // dir value must match the src-dir value configured in ReportLogCollector target
- // preparer in cts/harness/tools/cts-tradefed/res/config/cts-preconditions.xml
- if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
- throw new IOException("External storage is not mounted");
- } else if ((!logDirectory.exists() && !logDirectory.mkdirs())
- || (logDirectory.exists() && !logDirectory.isDirectory())) {
- throw new IOException("Cannot create directory for device info files");
- } else {
- File jsonFile = new File(logDirectory, mReportLogName + ".reportlog.json");
- store = new ReportLogDeviceInfoStore(jsonFile, mStreamName);
- store.open();
- }
- } catch (Exception e) {
- Log.e(TAG, "Could not create report log file.", e);
- }
- }
-
- /**
- * Adds a double metric to the report.
- */
- @Override
- public void addValue(String source, String message, double value, ResultType type,
- ResultUnit unit) {
- super.addValue(source, message, value, type, unit);
- try {
- store.addResult(message, value);
- } catch (Exception e) {
- Log.e(TAG, "Could not log metric.", e);
- }
- }
-
- /**
- * Adds a double metric to the report.
- */
- @Override
- public void addValue(String message, double value, ResultType type, ResultUnit unit) {
- super.addValue(message, value, type, unit);
- try {
- store.addResult(message, value);
- } catch (Exception e) {
- Log.e(TAG, "Could not log metric.", e);
- }
- }
-
- /**
- * Adds a double array of metrics to the report.
- */
- @Override
- public void addValues(String source, String message, double[] values, ResultType type,
- ResultUnit unit) {
- super.addValues(source, message, values, type, unit);
- try {
- store.addArrayResult(message, values);
- } catch (Exception e) {
- Log.e(TAG, "Could not log metric.", e);
- }
- }
-
- /**
- * Adds a double array of metrics to the report.
- */
- @Override
- public void addValues(String message, double[] values, ResultType type, ResultUnit unit) {
- super.addValues(message, values, type, unit);
- try {
- store.addArrayResult(message, values);
- } catch (Exception e) {
- Log.e(TAG, "Could not log metric.", e);
- }
- }
-
- /**
- * Adds an int metric to the report.
- */
- @Override
- public void addValue(String message, int value, ResultType type, ResultUnit unit) {
- try {
- store.addResult(message, value);
- } catch (Exception e) {
- Log.e(TAG, "Could not log metric.", e);
- }
- }
-
- /**
- * Adds a long metric to the report.
- */
- @Override
- public void addValue(String message, long value, ResultType type, ResultUnit unit) {
- try {
- store.addResult(message, value);
- } catch (Exception e) {
- Log.e(TAG, "Could not log metric.", e);
- }
- }
-
- /**
- * Adds a float metric to the report.
- */
- @Override
- public void addValue(String message, float value, ResultType type, ResultUnit unit) {
- try {
- store.addResult(message, value);
- } catch (Exception e) {
- Log.e(TAG, "Could not log metric.", e);
- }
- }
-
- /**
- * Adds a boolean metric to the report.
- */
- @Override
- public void addValue(String message, boolean value, ResultType type, ResultUnit unit) {
- try {
- store.addResult(message, value);
- } catch (Exception e) {
- Log.e(TAG, "Could not log metric.", e);
- }
- }
-
- /**
- * Adds a String metric to the report.
- */
- @Override
- public void addValue(String message, String value, ResultType type, ResultUnit unit) {
- try {
- store.addResult(message, value);
- } catch (Exception e) {
- Log.e(TAG, "Could not log metric.", e);
- }
- }
-
- /**
- * Adds an int array of metrics to the report.
- */
- @Override
- public void addValues(String message, int[] values, ResultType type, ResultUnit unit) {
- try {
- store.addArrayResult(message, values);
- } catch (Exception e) {
- Log.e(TAG, "Could not log metric.", e);
- }
- }
-
- /**
- * Adds a long array of metrics to the report.
- */
- @Override
- public void addValues(String message, long[] values, ResultType type, ResultUnit unit) {
- try {
- store.addArrayResult(message, values);
- } catch (Exception e) {
- Log.e(TAG, "Could not log metric.", e);
- }
- }
-
- /**
- * Adds a float array of metrics to the report.
- */
- @Override
- public void addValues(String message, float[] values, ResultType type, ResultUnit unit) {
- try {
- store.addArrayResult(message, values);
- } catch (Exception e) {
- Log.e(TAG, "Could not log metric.", e);
- }
- }
-
- /**
- * Adds a boolean array of metrics to the report.
- */
- @Override
- public void addValues(String message, boolean[] values, ResultType type, ResultUnit unit) {
- try {
- store.addArrayResult(message, values);
- } catch (Exception e) {
- Log.e(TAG, "Could not log metric.", e);
- }
- }
-
- /**
- * Adds a String List of metrics to the report.
- */
- @Override
- public void addValues(String message, List<String> values, ResultType type, ResultUnit unit) {
- try {
- store.addListResult(message, values);
- } catch (Exception e) {
- Log.e(TAG, "Could not log metric.", e);
- }
- }
-
- /**
- * Sets the summary double metric of the report.
- *
- * NOTE: messages over {@value Metric#MAX_MESSAGE_LENGTH} chars will be trimmed.
- */
- @Override
- public void setSummary(String message, double value, ResultType type, ResultUnit unit) {
- super.setSummary(message, value, type, unit);
- try {
- store.addResult(message, value);
- } catch (Exception e) {
- Log.e(TAG, "Could not log metric.", e);
- }
- }
-
- /**
- * Closes report file and submits report to instrumentation.
- */
- public void submit(Instrumentation instrumentation) {
- try {
- store.close();
- Bundle output = new Bundle();
- output.putString(RESULT, serialize(this));
- instrumentation.sendStatus(INST_STATUS_IN_PROGRESS, output);
- } catch (Exception e) {
- Log.e(TAG, "ReportLog Submit Failed", e);
- instrumentation.sendStatus(INST_STATUS_ERROR, null);
- }
- }
-
- /**
- * Closes report file. Static functions that do not have access to instrumentation can
- * use this to close report logs. Summary, if present, is not reported to instrumentation, hence
- * does not appear in the result XML.
- */
- public void submit() {
- try {
- store.close();
- } catch (Exception e) {
- Log.e(TAG, "ReportLog Submit Failed", e);
- }
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/DoubleVisitor.java b/common/device-side/util/src/com/android/compatibility/common/util/DoubleVisitor.java
deleted file mode 100644
index 96fb3bb..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/DoubleVisitor.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.util;
-
-import androidx.annotation.NonNull;
-
-/**
- * Implements the Visitor design pattern to visit 2 related objects (like a view and the activity
- * hosting it).
- *
- * @param <V1> 1st visited object
- * @param <V2> 2nd visited object
- */
-public interface DoubleVisitor<V1, V2> {
-
- /**
- * Visit those objects.
- */
- void visit(@NonNull V1 visited1, @NonNull V2 visited2);
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/DummyActivity.java b/common/device-side/util/src/com/android/compatibility/common/util/DummyActivity.java
deleted file mode 100644
index 672106c..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/DummyActivity.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-package com.android.compatibility.common.util;
-
-import android.app.Activity;
-
-public class DummyActivity extends Activity {
-
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/DynamicConfigDeviceSide.java b/common/device-side/util/src/com/android/compatibility/common/util/DynamicConfigDeviceSide.java
deleted file mode 100644
index 0e443fb..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/DynamicConfigDeviceSide.java
+++ /dev/null
@@ -1,69 +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.compatibility.common.util;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.net.Uri;
-import android.os.Environment;
-import android.os.ParcelFileDescriptor;
-import android.util.Log;
-
-import android.support.test.InstrumentationRegistry;
-
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.FileNotFoundException;
-
-/**
- * Load dynamic config for device side test cases
- */
-public class DynamicConfigDeviceSide extends DynamicConfig {
-
- public static final String CONTENT_PROVIDER =
- String.format("%s://android.tradefed.contentprovider", ContentResolver.SCHEME_CONTENT);
-
- public DynamicConfigDeviceSide(String moduleName) throws XmlPullParserException, IOException {
- this(moduleName, new File(CONFIG_FOLDER_ON_DEVICE));
- }
-
- public DynamicConfigDeviceSide(String moduleName, File configFolder)
- throws XmlPullParserException, IOException {
- if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
- throw new IOException("External storage is not mounted");
- }
- // Use the content provider to get the config:
- String uriPath = String.format("%s/%s/%s.dynamic", CONTENT_PROVIDER, configFolder.getAbsolutePath(), moduleName);
- Uri sdcardUri = Uri.parse(uriPath);
- Context appContext = InstrumentationRegistry.getTargetContext();
- try {
- ContentResolver resolver = appContext.getContentResolver();
- ParcelFileDescriptor descriptor = resolver.openFileDescriptor(sdcardUri,"r");
-
- initializeConfig(new ParcelFileDescriptor.AutoCloseInputStream(descriptor));
- return;
- } catch (FileNotFoundException e) {
- // Log the error and use the fallback too
- Log.e("DynamicConfigDeviceSide", "Error while using content provider for config", e);
- }
- // Fallback to the direct search
- File configFile = getConfigFile(configFolder, moduleName);
- initializeConfig(configFile);
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/FakeKeys.java b/common/device-side/util/src/com/android/compatibility/common/util/FakeKeys.java
deleted file mode 100644
index 85e06ea..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/FakeKeys.java
+++ /dev/null
@@ -1,469 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.util;
-
-// Copied from cts/tests/tests/keystore/src/android/keystore/cts/AndroidKeyStoreTest.java
-
-public class FakeKeys {
- /*
- * The keys and certificates below are generated with:
- *
- * openssl req -new -x509 -days 3650 -extensions v3_ca -keyout cakey.pem -out cacert.pem
- * openssl req -newkey rsa:1024 -keyout userkey.pem -nodes -days 3650 -out userkey.req
- * mkdir -p demoCA/newcerts
- * touch demoCA/index.txt
- * echo "01" > demoCA/serial
- * openssl ca -out usercert.pem -in userkey.req -cert cacert.pem -keyfile cakey.pem -days 3650
- */
- public static class FAKE_RSA_1 {
- /**
- * Generated from above and converted with:
- *
- * openssl pkcs8 -topk8 -outform d -in userkey.pem -nocrypt | xxd -i | sed 's/0x/(byte) 0x/g'
- */
- public static final byte[] privateKey = {
- (byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0x78, (byte) 0x02, (byte) 0x01,
- (byte) 0x00, (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a,
- (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01,
- (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x00, (byte) 0x04, (byte) 0x82,
- (byte) 0x02, (byte) 0x62, (byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0x5e,
- (byte) 0x02, (byte) 0x01, (byte) 0x00, (byte) 0x02, (byte) 0x81, (byte) 0x81,
- (byte) 0x00, (byte) 0xce, (byte) 0x29, (byte) 0xeb, (byte) 0xf6, (byte) 0x5b,
- (byte) 0x25, (byte) 0xdc, (byte) 0xa1, (byte) 0xa6, (byte) 0x2c, (byte) 0x66,
- (byte) 0xcb, (byte) 0x20, (byte) 0x90, (byte) 0x27, (byte) 0x86, (byte) 0x8a,
- (byte) 0x44, (byte) 0x71, (byte) 0x50, (byte) 0xda, (byte) 0xd3, (byte) 0x02,
- (byte) 0x77, (byte) 0x55, (byte) 0xe9, (byte) 0xe8, (byte) 0x08, (byte) 0xf3,
- (byte) 0x36, (byte) 0x9a, (byte) 0xae, (byte) 0xab, (byte) 0x04, (byte) 0x6d,
- (byte) 0x00, (byte) 0x99, (byte) 0xbf, (byte) 0x7d, (byte) 0x0f, (byte) 0x67,
- (byte) 0x8b, (byte) 0x1d, (byte) 0xd4, (byte) 0x2b, (byte) 0x7c, (byte) 0xcb,
- (byte) 0xcd, (byte) 0x33, (byte) 0xc7, (byte) 0x84, (byte) 0x30, (byte) 0xe2,
- (byte) 0x45, (byte) 0x21, (byte) 0xb3, (byte) 0x75, (byte) 0xf5, (byte) 0x79,
- (byte) 0x02, (byte) 0xda, (byte) 0x50, (byte) 0xa3, (byte) 0x8b, (byte) 0xce,
- (byte) 0xc3, (byte) 0x8e, (byte) 0x0f, (byte) 0x25, (byte) 0xeb, (byte) 0x08,
- (byte) 0x2c, (byte) 0xdd, (byte) 0x1c, (byte) 0xcf, (byte) 0xff, (byte) 0x3b,
- (byte) 0xde, (byte) 0xb6, (byte) 0xaa, (byte) 0x2a, (byte) 0xa9, (byte) 0xc4,
- (byte) 0x8a, (byte) 0x24, (byte) 0x24, (byte) 0xe6, (byte) 0x29, (byte) 0x0d,
- (byte) 0x98, (byte) 0x4c, (byte) 0x32, (byte) 0xa1, (byte) 0x7b, (byte) 0x23,
- (byte) 0x2b, (byte) 0x42, (byte) 0x30, (byte) 0xee, (byte) 0x78, (byte) 0x08,
- (byte) 0x47, (byte) 0xad, (byte) 0xf2, (byte) 0x96, (byte) 0xd5, (byte) 0xf1,
- (byte) 0x62, (byte) 0x42, (byte) 0x2d, (byte) 0x35, (byte) 0x19, (byte) 0xb4,
- (byte) 0x3c, (byte) 0xc9, (byte) 0xc3, (byte) 0x5f, (byte) 0x03, (byte) 0x16,
- (byte) 0x3a, (byte) 0x23, (byte) 0xac, (byte) 0xcb, (byte) 0xce, (byte) 0x9e,
- (byte) 0x51, (byte) 0x2e, (byte) 0x6d, (byte) 0x02, (byte) 0x03, (byte) 0x01,
- (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x81, (byte) 0x80, (byte) 0x16,
- (byte) 0x59, (byte) 0xc3, (byte) 0x24, (byte) 0x1d, (byte) 0x33, (byte) 0x98,
- (byte) 0x9c, (byte) 0xc9, (byte) 0xc8, (byte) 0x2c, (byte) 0x88, (byte) 0xbf,
- (byte) 0x0a, (byte) 0x01, (byte) 0xce, (byte) 0xfb, (byte) 0x34, (byte) 0x7a,
- (byte) 0x58, (byte) 0x7a, (byte) 0xb0, (byte) 0xbf, (byte) 0xa6, (byte) 0xb2,
- (byte) 0x60, (byte) 0xbe, (byte) 0x70, (byte) 0x21, (byte) 0xf5, (byte) 0xfc,
- (byte) 0x85, (byte) 0x0d, (byte) 0x33, (byte) 0x58, (byte) 0xa1, (byte) 0xe5,
- (byte) 0x09, (byte) 0x36, (byte) 0x84, (byte) 0xb2, (byte) 0x04, (byte) 0x0a,
- (byte) 0x02, (byte) 0xd3, (byte) 0x88, (byte) 0x1f, (byte) 0x0c, (byte) 0x2b,
- (byte) 0x1d, (byte) 0xe9, (byte) 0x3d, (byte) 0xe7, (byte) 0x79, (byte) 0xf9,
- (byte) 0x32, (byte) 0x5c, (byte) 0x8a, (byte) 0x75, (byte) 0x49, (byte) 0x12,
- (byte) 0xe4, (byte) 0x05, (byte) 0x26, (byte) 0xd4, (byte) 0x2e, (byte) 0x9e,
- (byte) 0x1f, (byte) 0xcc, (byte) 0x54, (byte) 0xad, (byte) 0x33, (byte) 0x8d,
- (byte) 0x99, (byte) 0x00, (byte) 0xdc, (byte) 0xf5, (byte) 0xb4, (byte) 0xa2,
- (byte) 0x2f, (byte) 0xba, (byte) 0xe5, (byte) 0x62, (byte) 0x30, (byte) 0x6d,
- (byte) 0xe6, (byte) 0x3d, (byte) 0xeb, (byte) 0x24, (byte) 0xc2, (byte) 0xdc,
- (byte) 0x5f, (byte) 0xb7, (byte) 0x16, (byte) 0x35, (byte) 0xa3, (byte) 0x98,
- (byte) 0x98, (byte) 0xa8, (byte) 0xef, (byte) 0xe8, (byte) 0xc4, (byte) 0x96,
- (byte) 0x6d, (byte) 0x38, (byte) 0xab, (byte) 0x26, (byte) 0x6d, (byte) 0x30,
- (byte) 0xc2, (byte) 0xa0, (byte) 0x44, (byte) 0xe4, (byte) 0xff, (byte) 0x7e,
- (byte) 0xbe, (byte) 0x7c, (byte) 0x33, (byte) 0xa5, (byte) 0x10, (byte) 0xad,
- (byte) 0xd7, (byte) 0x1e, (byte) 0x13, (byte) 0x20, (byte) 0xb3, (byte) 0x1f,
- (byte) 0x41, (byte) 0x02, (byte) 0x41, (byte) 0x00, (byte) 0xf1, (byte) 0x89,
- (byte) 0x07, (byte) 0x0f, (byte) 0xe8, (byte) 0xcf, (byte) 0xab, (byte) 0x13,
- (byte) 0x2a, (byte) 0x8f, (byte) 0x88, (byte) 0x80, (byte) 0x11, (byte) 0x9a,
- (byte) 0x79, (byte) 0xb6, (byte) 0x59, (byte) 0x3a, (byte) 0x50, (byte) 0x6e,
- (byte) 0x57, (byte) 0x37, (byte) 0xab, (byte) 0x2a, (byte) 0xd2, (byte) 0xaa,
- (byte) 0xd9, (byte) 0x72, (byte) 0x73, (byte) 0xff, (byte) 0x8b, (byte) 0x47,
- (byte) 0x76, (byte) 0xdd, (byte) 0xdc, (byte) 0xf5, (byte) 0x97, (byte) 0x44,
- (byte) 0x3a, (byte) 0x78, (byte) 0xbe, (byte) 0x17, (byte) 0xb4, (byte) 0x22,
- (byte) 0x6f, (byte) 0xe5, (byte) 0x23, (byte) 0x70, (byte) 0x1d, (byte) 0x10,
- (byte) 0x5d, (byte) 0xba, (byte) 0x16, (byte) 0x81, (byte) 0xf1, (byte) 0x45,
- (byte) 0xce, (byte) 0x30, (byte) 0xb4, (byte) 0xab, (byte) 0x80, (byte) 0xe4,
- (byte) 0x98, (byte) 0x31, (byte) 0x02, (byte) 0x41, (byte) 0x00, (byte) 0xda,
- (byte) 0x82, (byte) 0x9d, (byte) 0x3f, (byte) 0xca, (byte) 0x2f, (byte) 0xe1,
- (byte) 0xd4, (byte) 0x86, (byte) 0x77, (byte) 0x48, (byte) 0xa6, (byte) 0xab,
- (byte) 0xab, (byte) 0x1c, (byte) 0x42, (byte) 0x5c, (byte) 0xd5, (byte) 0xc7,
- (byte) 0x46, (byte) 0x59, (byte) 0x91, (byte) 0x3f, (byte) 0xfc, (byte) 0xcc,
- (byte) 0xec, (byte) 0xc2, (byte) 0x40, (byte) 0x12, (byte) 0x2c, (byte) 0x8d,
- (byte) 0x1f, (byte) 0xa2, (byte) 0x18, (byte) 0x88, (byte) 0xee, (byte) 0x82,
- (byte) 0x4a, (byte) 0x5a, (byte) 0x5e, (byte) 0x88, (byte) 0x20, (byte) 0xe3,
- (byte) 0x7b, (byte) 0xe0, (byte) 0xd8, (byte) 0x3a, (byte) 0x52, (byte) 0x9a,
- (byte) 0x26, (byte) 0x6a, (byte) 0x04, (byte) 0xec, (byte) 0xe8, (byte) 0xb9,
- (byte) 0x48, (byte) 0x40, (byte) 0xe1, (byte) 0xe1, (byte) 0x83, (byte) 0xa6,
- (byte) 0x67, (byte) 0xa6, (byte) 0xfd, (byte) 0x02, (byte) 0x41, (byte) 0x00,
- (byte) 0x89, (byte) 0x72, (byte) 0x3e, (byte) 0xb0, (byte) 0x90, (byte) 0xfd,
- (byte) 0x4c, (byte) 0x0e, (byte) 0xd6, (byte) 0x13, (byte) 0x63, (byte) 0xcb,
- (byte) 0xed, (byte) 0x38, (byte) 0x88, (byte) 0xb6, (byte) 0x79, (byte) 0xc4,
- (byte) 0x33, (byte) 0x6c, (byte) 0xf6, (byte) 0xf8, (byte) 0xd8, (byte) 0xd0,
- (byte) 0xbf, (byte) 0x9d, (byte) 0x35, (byte) 0xac, (byte) 0x69, (byte) 0xd2,
- (byte) 0x2b, (byte) 0xc1, (byte) 0xf9, (byte) 0x24, (byte) 0x7b, (byte) 0xce,
- (byte) 0xcd, (byte) 0xcb, (byte) 0xa7, (byte) 0xb2, (byte) 0x7a, (byte) 0x0a,
- (byte) 0x27, (byte) 0x19, (byte) 0xc9, (byte) 0xaf, (byte) 0x0d, (byte) 0x21,
- (byte) 0x89, (byte) 0x88, (byte) 0x7c, (byte) 0xad, (byte) 0x9e, (byte) 0x8d,
- (byte) 0x47, (byte) 0x6d, (byte) 0x3f, (byte) 0xce, (byte) 0x7b, (byte) 0xa1,
- (byte) 0x74, (byte) 0xf1, (byte) 0xa0, (byte) 0xa1, (byte) 0x02, (byte) 0x41,
- (byte) 0x00, (byte) 0xd9, (byte) 0xa8, (byte) 0xf5, (byte) 0xfe, (byte) 0xce,
- (byte) 0xe6, (byte) 0x77, (byte) 0x6b, (byte) 0xfe, (byte) 0x2d, (byte) 0xe0,
- (byte) 0x1e, (byte) 0xb6, (byte) 0x2e, (byte) 0x12, (byte) 0x4e, (byte) 0x40,
- (byte) 0xaf, (byte) 0x6a, (byte) 0x7b, (byte) 0x37, (byte) 0x49, (byte) 0x2a,
- (byte) 0x96, (byte) 0x25, (byte) 0x83, (byte) 0x49, (byte) 0xd4, (byte) 0x0c,
- (byte) 0xc6, (byte) 0x78, (byte) 0x25, (byte) 0x24, (byte) 0x90, (byte) 0x90,
- (byte) 0x06, (byte) 0x15, (byte) 0x9e, (byte) 0xfe, (byte) 0xf9, (byte) 0xdf,
- (byte) 0x5b, (byte) 0xf3, (byte) 0x7e, (byte) 0x38, (byte) 0x70, (byte) 0xeb,
- (byte) 0x57, (byte) 0xd0, (byte) 0xd9, (byte) 0xa7, (byte) 0x0e, (byte) 0x14,
- (byte) 0xf7, (byte) 0x95, (byte) 0x68, (byte) 0xd5, (byte) 0xc8, (byte) 0xab,
- (byte) 0x9d, (byte) 0x3a, (byte) 0x2b, (byte) 0x51, (byte) 0xf9, (byte) 0x02,
- (byte) 0x41, (byte) 0x00, (byte) 0x96, (byte) 0xdf, (byte) 0xe9, (byte) 0x67,
- (byte) 0x6c, (byte) 0xdc, (byte) 0x90, (byte) 0x14, (byte) 0xb4, (byte) 0x1d,
- (byte) 0x22, (byte) 0x33, (byte) 0x4a, (byte) 0x31, (byte) 0xc1, (byte) 0x9d,
- (byte) 0x2e, (byte) 0xff, (byte) 0x9a, (byte) 0x2a, (byte) 0x95, (byte) 0x4b,
- (byte) 0x27, (byte) 0x74, (byte) 0xcb, (byte) 0x21, (byte) 0xc3, (byte) 0xd2,
- (byte) 0x0b, (byte) 0xb2, (byte) 0x46, (byte) 0x87, (byte) 0xf8, (byte) 0x28,
- (byte) 0x01, (byte) 0x8b, (byte) 0xd8, (byte) 0xb9, (byte) 0x4b, (byte) 0xcd,
- (byte) 0x9a, (byte) 0x96, (byte) 0x41, (byte) 0x0e, (byte) 0x36, (byte) 0x6d,
- (byte) 0x40, (byte) 0x42, (byte) 0xbc, (byte) 0xd9, (byte) 0xd3, (byte) 0x7b,
- (byte) 0xbc, (byte) 0xa7, (byte) 0x92, (byte) 0x90, (byte) 0xdd, (byte) 0xa1,
- (byte) 0x9c, (byte) 0xce, (byte) 0xa1, (byte) 0x87, (byte) 0x11, (byte) 0x51
- };
-
- /**
- * Generated from above and converted with:
- *
- * openssl x509 -outform d -in cacert.pem | xxd -i | sed 's/0x/(byte) 0x/g'
- */
- public static final byte[] caCertificate = {
- (byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0xce, (byte) 0x30, (byte) 0x82,
- (byte) 0x02, (byte) 0x37, (byte) 0xa0, (byte) 0x03, (byte) 0x02, (byte) 0x01,
- (byte) 0x02, (byte) 0x02, (byte) 0x09, (byte) 0x00, (byte) 0xe1, (byte) 0x6a,
- (byte) 0xa2, (byte) 0xf4, (byte) 0x2e, (byte) 0x55, (byte) 0x48, (byte) 0x0a,
- (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86,
- (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01,
- (byte) 0x05, (byte) 0x05, (byte) 0x00, (byte) 0x30, (byte) 0x4f, (byte) 0x31,
- (byte) 0x0b, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55,
- (byte) 0x04, (byte) 0x06, (byte) 0x13, (byte) 0x02, (byte) 0x55, (byte) 0x53,
- (byte) 0x31, (byte) 0x0b, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03,
- (byte) 0x55, (byte) 0x04, (byte) 0x08, (byte) 0x13, (byte) 0x02, (byte) 0x43,
- (byte) 0x41, (byte) 0x31, (byte) 0x16, (byte) 0x30, (byte) 0x14, (byte) 0x06,
- (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x07, (byte) 0x13, (byte) 0x0d,
- (byte) 0x4d, (byte) 0x6f, (byte) 0x75, (byte) 0x6e, (byte) 0x74, (byte) 0x61,
- (byte) 0x69, (byte) 0x6e, (byte) 0x20, (byte) 0x56, (byte) 0x69, (byte) 0x65,
- (byte) 0x77, (byte) 0x31, (byte) 0x1b, (byte) 0x30, (byte) 0x19, (byte) 0x06,
- (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x0a, (byte) 0x13, (byte) 0x12,
- (byte) 0x41, (byte) 0x6e, (byte) 0x64, (byte) 0x72, (byte) 0x6f, (byte) 0x69,
- (byte) 0x64, (byte) 0x20, (byte) 0x54, (byte) 0x65, (byte) 0x73, (byte) 0x74,
- (byte) 0x20, (byte) 0x43, (byte) 0x61, (byte) 0x73, (byte) 0x65, (byte) 0x73,
- (byte) 0x30, (byte) 0x1e, (byte) 0x17, (byte) 0x0d, (byte) 0x31, (byte) 0x32,
- (byte) 0x30, (byte) 0x38, (byte) 0x31, (byte) 0x34, (byte) 0x31, (byte) 0x36,
- (byte) 0x35, (byte) 0x35, (byte) 0x34, (byte) 0x34, (byte) 0x5a, (byte) 0x17,
- (byte) 0x0d, (byte) 0x32, (byte) 0x32, (byte) 0x30, (byte) 0x38, (byte) 0x31,
- (byte) 0x32, (byte) 0x31, (byte) 0x36, (byte) 0x35, (byte) 0x35, (byte) 0x34,
- (byte) 0x34, (byte) 0x5a, (byte) 0x30, (byte) 0x4f, (byte) 0x31, (byte) 0x0b,
- (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
- (byte) 0x06, (byte) 0x13, (byte) 0x02, (byte) 0x55, (byte) 0x53, (byte) 0x31,
- (byte) 0x0b, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55,
- (byte) 0x04, (byte) 0x08, (byte) 0x13, (byte) 0x02, (byte) 0x43, (byte) 0x41,
- (byte) 0x31, (byte) 0x16, (byte) 0x30, (byte) 0x14, (byte) 0x06, (byte) 0x03,
- (byte) 0x55, (byte) 0x04, (byte) 0x07, (byte) 0x13, (byte) 0x0d, (byte) 0x4d,
- (byte) 0x6f, (byte) 0x75, (byte) 0x6e, (byte) 0x74, (byte) 0x61, (byte) 0x69,
- (byte) 0x6e, (byte) 0x20, (byte) 0x56, (byte) 0x69, (byte) 0x65, (byte) 0x77,
- (byte) 0x31, (byte) 0x1b, (byte) 0x30, (byte) 0x19, (byte) 0x06, (byte) 0x03,
- (byte) 0x55, (byte) 0x04, (byte) 0x0a, (byte) 0x13, (byte) 0x12, (byte) 0x41,
- (byte) 0x6e, (byte) 0x64, (byte) 0x72, (byte) 0x6f, (byte) 0x69, (byte) 0x64,
- (byte) 0x20, (byte) 0x54, (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x20,
- (byte) 0x43, (byte) 0x61, (byte) 0x73, (byte) 0x65, (byte) 0x73, (byte) 0x30,
- (byte) 0x81, (byte) 0x9f, (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09,
- (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d,
- (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x00, (byte) 0x03,
- (byte) 0x81, (byte) 0x8d, (byte) 0x00, (byte) 0x30, (byte) 0x81, (byte) 0x89,
- (byte) 0x02, (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0xa3, (byte) 0x72,
- (byte) 0xab, (byte) 0xd0, (byte) 0xe4, (byte) 0xad, (byte) 0x2f, (byte) 0xe7,
- (byte) 0xe2, (byte) 0x79, (byte) 0x07, (byte) 0x36, (byte) 0x3d, (byte) 0x0c,
- (byte) 0x8d, (byte) 0x42, (byte) 0x9a, (byte) 0x0a, (byte) 0x33, (byte) 0x64,
- (byte) 0xb3, (byte) 0xcd, (byte) 0xb2, (byte) 0xd7, (byte) 0x3a, (byte) 0x42,
- (byte) 0x06, (byte) 0x77, (byte) 0x45, (byte) 0x29, (byte) 0xe9, (byte) 0xcb,
- (byte) 0xb7, (byte) 0x4a, (byte) 0xd6, (byte) 0xee, (byte) 0xad, (byte) 0x01,
- (byte) 0x91, (byte) 0x9b, (byte) 0x0c, (byte) 0x59, (byte) 0xa1, (byte) 0x03,
- (byte) 0xfa, (byte) 0xf0, (byte) 0x5a, (byte) 0x7c, (byte) 0x4f, (byte) 0xf7,
- (byte) 0x8d, (byte) 0x36, (byte) 0x0f, (byte) 0x1f, (byte) 0x45, (byte) 0x7d,
- (byte) 0x1b, (byte) 0x31, (byte) 0xa1, (byte) 0x35, (byte) 0x0b, (byte) 0x00,
- (byte) 0xed, (byte) 0x7a, (byte) 0xb6, (byte) 0xc8, (byte) 0x4e, (byte) 0xa9,
- (byte) 0x86, (byte) 0x4c, (byte) 0x7b, (byte) 0x99, (byte) 0x57, (byte) 0x41,
- (byte) 0x12, (byte) 0xef, (byte) 0x6b, (byte) 0xbc, (byte) 0x3d, (byte) 0x60,
- (byte) 0xf2, (byte) 0x99, (byte) 0x1a, (byte) 0xcd, (byte) 0xed, (byte) 0x56,
- (byte) 0xa4, (byte) 0xe5, (byte) 0x36, (byte) 0x9f, (byte) 0x24, (byte) 0x1f,
- (byte) 0xdc, (byte) 0x89, (byte) 0x40, (byte) 0xc8, (byte) 0x99, (byte) 0x92,
- (byte) 0xab, (byte) 0x4a, (byte) 0xb5, (byte) 0x61, (byte) 0x45, (byte) 0x62,
- (byte) 0xff, (byte) 0xa3, (byte) 0x45, (byte) 0x65, (byte) 0xaf, (byte) 0xf6,
- (byte) 0x27, (byte) 0x30, (byte) 0x51, (byte) 0x0e, (byte) 0x0e, (byte) 0xeb,
- (byte) 0x79, (byte) 0x0c, (byte) 0xbe, (byte) 0xb3, (byte) 0x0a, (byte) 0x6f,
- (byte) 0x29, (byte) 0x06, (byte) 0xdc, (byte) 0x2f, (byte) 0x6b, (byte) 0x51,
- (byte) 0x02, (byte) 0x03, (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0xa3,
- (byte) 0x81, (byte) 0xb1, (byte) 0x30, (byte) 0x81, (byte) 0xae, (byte) 0x30,
- (byte) 0x1d, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x0e,
- (byte) 0x04, (byte) 0x16, (byte) 0x04, (byte) 0x14, (byte) 0x33, (byte) 0x05,
- (byte) 0xee, (byte) 0xfe, (byte) 0x6f, (byte) 0x60, (byte) 0xc7, (byte) 0xf9,
- (byte) 0xa9, (byte) 0xd2, (byte) 0x73, (byte) 0x5c, (byte) 0x8f, (byte) 0x6d,
- (byte) 0xa2, (byte) 0x2f, (byte) 0x97, (byte) 0x8e, (byte) 0x5d, (byte) 0x51,
- (byte) 0x30, (byte) 0x7f, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d,
- (byte) 0x23, (byte) 0x04, (byte) 0x78, (byte) 0x30, (byte) 0x76, (byte) 0x80,
- (byte) 0x14, (byte) 0x33, (byte) 0x05, (byte) 0xee, (byte) 0xfe, (byte) 0x6f,
- (byte) 0x60, (byte) 0xc7, (byte) 0xf9, (byte) 0xa9, (byte) 0xd2, (byte) 0x73,
- (byte) 0x5c, (byte) 0x8f, (byte) 0x6d, (byte) 0xa2, (byte) 0x2f, (byte) 0x97,
- (byte) 0x8e, (byte) 0x5d, (byte) 0x51, (byte) 0xa1, (byte) 0x53, (byte) 0xa4,
- (byte) 0x51, (byte) 0x30, (byte) 0x4f, (byte) 0x31, (byte) 0x0b, (byte) 0x30,
- (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x06,
- (byte) 0x13, (byte) 0x02, (byte) 0x55, (byte) 0x53, (byte) 0x31, (byte) 0x0b,
- (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
- (byte) 0x08, (byte) 0x13, (byte) 0x02, (byte) 0x43, (byte) 0x41, (byte) 0x31,
- (byte) 0x16, (byte) 0x30, (byte) 0x14, (byte) 0x06, (byte) 0x03, (byte) 0x55,
- (byte) 0x04, (byte) 0x07, (byte) 0x13, (byte) 0x0d, (byte) 0x4d, (byte) 0x6f,
- (byte) 0x75, (byte) 0x6e, (byte) 0x74, (byte) 0x61, (byte) 0x69, (byte) 0x6e,
- (byte) 0x20, (byte) 0x56, (byte) 0x69, (byte) 0x65, (byte) 0x77, (byte) 0x31,
- (byte) 0x1b, (byte) 0x30, (byte) 0x19, (byte) 0x06, (byte) 0x03, (byte) 0x55,
- (byte) 0x04, (byte) 0x0a, (byte) 0x13, (byte) 0x12, (byte) 0x41, (byte) 0x6e,
- (byte) 0x64, (byte) 0x72, (byte) 0x6f, (byte) 0x69, (byte) 0x64, (byte) 0x20,
- (byte) 0x54, (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x20, (byte) 0x43,
- (byte) 0x61, (byte) 0x73, (byte) 0x65, (byte) 0x73, (byte) 0x82, (byte) 0x09,
- (byte) 0x00, (byte) 0xe1, (byte) 0x6a, (byte) 0xa2, (byte) 0xf4, (byte) 0x2e,
- (byte) 0x55, (byte) 0x48, (byte) 0x0a, (byte) 0x30, (byte) 0x0c, (byte) 0x06,
- (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x13, (byte) 0x04, (byte) 0x05,
- (byte) 0x30, (byte) 0x03, (byte) 0x01, (byte) 0x01, (byte) 0xff, (byte) 0x30,
- (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86, (byte) 0x48,
- (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01, (byte) 0x05,
- (byte) 0x05, (byte) 0x00, (byte) 0x03, (byte) 0x81, (byte) 0x81, (byte) 0x00,
- (byte) 0x8c, (byte) 0x30, (byte) 0x42, (byte) 0xfa, (byte) 0xeb, (byte) 0x1a,
- (byte) 0x26, (byte) 0xeb, (byte) 0xda, (byte) 0x56, (byte) 0x32, (byte) 0xf2,
- (byte) 0x9d, (byte) 0xa5, (byte) 0x24, (byte) 0xd8, (byte) 0x3a, (byte) 0xda,
- (byte) 0x30, (byte) 0xa6, (byte) 0x8b, (byte) 0x46, (byte) 0xfe, (byte) 0xfe,
- (byte) 0xdb, (byte) 0xf1, (byte) 0xe6, (byte) 0xe1, (byte) 0x7c, (byte) 0x1b,
- (byte) 0xe7, (byte) 0x77, (byte) 0x00, (byte) 0xa1, (byte) 0x1c, (byte) 0x19,
- (byte) 0x17, (byte) 0x73, (byte) 0xb0, (byte) 0xf0, (byte) 0x9d, (byte) 0xf3,
- (byte) 0x4f, (byte) 0xb6, (byte) 0xbc, (byte) 0xc7, (byte) 0x47, (byte) 0x85,
- (byte) 0x2a, (byte) 0x4a, (byte) 0xa1, (byte) 0xa5, (byte) 0x58, (byte) 0xf5,
- (byte) 0xc5, (byte) 0x1a, (byte) 0x51, (byte) 0xb1, (byte) 0x04, (byte) 0x80,
- (byte) 0xee, (byte) 0x3a, (byte) 0xec, (byte) 0x2f, (byte) 0xe1, (byte) 0xfd,
- (byte) 0x58, (byte) 0xeb, (byte) 0xed, (byte) 0x82, (byte) 0x9e, (byte) 0x38,
- (byte) 0xa3, (byte) 0x24, (byte) 0x75, (byte) 0xf7, (byte) 0x3e, (byte) 0xc2,
- (byte) 0xc5, (byte) 0x27, (byte) 0xeb, (byte) 0x6f, (byte) 0x7b, (byte) 0x50,
- (byte) 0xda, (byte) 0x43, (byte) 0xdc, (byte) 0x3b, (byte) 0x0b, (byte) 0x6f,
- (byte) 0x78, (byte) 0x8f, (byte) 0xb0, (byte) 0x66, (byte) 0xe1, (byte) 0x12,
- (byte) 0x87, (byte) 0x5f, (byte) 0x97, (byte) 0x7b, (byte) 0xca, (byte) 0x14,
- (byte) 0x79, (byte) 0xf7, (byte) 0xe8, (byte) 0x6c, (byte) 0x72, (byte) 0xdb,
- (byte) 0x91, (byte) 0x65, (byte) 0x17, (byte) 0x54, (byte) 0xe0, (byte) 0x74,
- (byte) 0x1d, (byte) 0xac, (byte) 0x47, (byte) 0x04, (byte) 0x12, (byte) 0xe0,
- (byte) 0xc3, (byte) 0x66, (byte) 0x19, (byte) 0x05, (byte) 0x2e, (byte) 0x7e,
- (byte) 0xf1, (byte) 0x61
- };
- }
-
- /*
- * The keys and certificates below are generated with:
- *
- * openssl req -new -x509 -days 3650 -extensions v3_ca -keyout cakey.pem -out cacert.pem
- * openssl dsaparam -out dsaparam.pem 1024
- * openssl req -newkey dsa:dsaparam.pem -keyout userkey.pem -nodes -days 3650 -out userkey.req
- * mkdir -p demoCA/newcerts
- * touch demoCA/index.txt
- * echo "01" > demoCA/serial
- * openssl ca -out usercert.pem -in userkey.req -cert cacert.pem -keyfile cakey.pem -days 3650
- */
- public static class FAKE_DSA_1 {
- /**
- * Generated from above and converted with: openssl pkcs8 -topk8 -outform d
- * -in userkey.pem -nocrypt | xxd -i | sed 's/0x/(byte) 0x/g'
- */
- public static final byte[] privateKey = {
- (byte) 0x30, (byte) 0x82, (byte) 0x01, (byte) 0x4c, (byte) 0x02, (byte) 0x01,
- (byte) 0x00, (byte) 0x30, (byte) 0x82, (byte) 0x01, (byte) 0x2c, (byte) 0x06,
- (byte) 0x07, (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0xce, (byte) 0x38,
- (byte) 0x04, (byte) 0x01, (byte) 0x30, (byte) 0x82, (byte) 0x01, (byte) 0x1f,
- (byte) 0x02, (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0xb3, (byte) 0x23,
- (byte) 0xf7, (byte) 0x86, (byte) 0xbd, (byte) 0x3b, (byte) 0x86, (byte) 0xcc,
- (byte) 0xc3, (byte) 0x91, (byte) 0xc0, (byte) 0x30, (byte) 0x32, (byte) 0x02,
- (byte) 0x47, (byte) 0x35, (byte) 0x01, (byte) 0xef, (byte) 0xee, (byte) 0x98,
- (byte) 0x13, (byte) 0x56, (byte) 0x49, (byte) 0x47, (byte) 0xb5, (byte) 0x20,
- (byte) 0xa8, (byte) 0x60, (byte) 0xcb, (byte) 0xc0, (byte) 0xd5, (byte) 0x77,
- (byte) 0xc1, (byte) 0x69, (byte) 0xcd, (byte) 0x18, (byte) 0x34, (byte) 0x92,
- (byte) 0xf2, (byte) 0x6a, (byte) 0x2a, (byte) 0x10, (byte) 0x59, (byte) 0x1c,
- (byte) 0x91, (byte) 0x20, (byte) 0x51, (byte) 0xca, (byte) 0x37, (byte) 0xb2,
- (byte) 0x87, (byte) 0xa6, (byte) 0x8a, (byte) 0x02, (byte) 0xfd, (byte) 0x45,
- (byte) 0x46, (byte) 0xf9, (byte) 0x76, (byte) 0xb1, (byte) 0x35, (byte) 0x38,
- (byte) 0x8d, (byte) 0xff, (byte) 0x4c, (byte) 0x5d, (byte) 0x75, (byte) 0x8f,
- (byte) 0x66, (byte) 0x15, (byte) 0x7d, (byte) 0x7b, (byte) 0xda, (byte) 0xdb,
- (byte) 0x57, (byte) 0x39, (byte) 0xff, (byte) 0x91, (byte) 0x3f, (byte) 0xdd,
- (byte) 0xe2, (byte) 0xb4, (byte) 0x22, (byte) 0x60, (byte) 0x4c, (byte) 0x32,
- (byte) 0x3b, (byte) 0x9d, (byte) 0x34, (byte) 0x9f, (byte) 0xb9, (byte) 0x5d,
- (byte) 0x75, (byte) 0xb9, (byte) 0xd3, (byte) 0x7f, (byte) 0x11, (byte) 0xba,
- (byte) 0xb7, (byte) 0xc8, (byte) 0x32, (byte) 0xc6, (byte) 0xce, (byte) 0x71,
- (byte) 0x91, (byte) 0xd3, (byte) 0x32, (byte) 0xaf, (byte) 0x4d, (byte) 0x7e,
- (byte) 0x7c, (byte) 0x15, (byte) 0xf7, (byte) 0x71, (byte) 0x2c, (byte) 0x52,
- (byte) 0x65, (byte) 0x4d, (byte) 0xa9, (byte) 0x81, (byte) 0x25, (byte) 0x35,
- (byte) 0xce, (byte) 0x0b, (byte) 0x5b, (byte) 0x56, (byte) 0xfe, (byte) 0xf1,
- (byte) 0x02, (byte) 0x15, (byte) 0x00, (byte) 0xeb, (byte) 0x4e, (byte) 0x7f,
- (byte) 0x7a, (byte) 0x31, (byte) 0xb3, (byte) 0x7d, (byte) 0x8d, (byte) 0xb2,
- (byte) 0xf7, (byte) 0xaf, (byte) 0xad, (byte) 0xb1, (byte) 0x42, (byte) 0x92,
- (byte) 0xf3, (byte) 0x6c, (byte) 0xe4, (byte) 0xed, (byte) 0x8b, (byte) 0x02,
- (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0x81, (byte) 0xc8, (byte) 0x36,
- (byte) 0x48, (byte) 0xdb, (byte) 0x71, (byte) 0x2b, (byte) 0x91, (byte) 0xce,
- (byte) 0x6d, (byte) 0xbc, (byte) 0xb8, (byte) 0xf9, (byte) 0xcb, (byte) 0x50,
- (byte) 0x91, (byte) 0x10, (byte) 0x8a, (byte) 0xf8, (byte) 0x37, (byte) 0x50,
- (byte) 0xda, (byte) 0x4f, (byte) 0xc8, (byte) 0x4d, (byte) 0x73, (byte) 0xcb,
- (byte) 0x4d, (byte) 0xb0, (byte) 0x19, (byte) 0x54, (byte) 0x5a, (byte) 0xf3,
- (byte) 0x6c, (byte) 0xc9, (byte) 0xd8, (byte) 0x96, (byte) 0xd9, (byte) 0xb0,
- (byte) 0x54, (byte) 0x7e, (byte) 0x7d, (byte) 0xe2, (byte) 0x58, (byte) 0x0e,
- (byte) 0x5f, (byte) 0xc0, (byte) 0xce, (byte) 0xb9, (byte) 0x5c, (byte) 0xe3,
- (byte) 0xd3, (byte) 0xdf, (byte) 0xcf, (byte) 0x45, (byte) 0x74, (byte) 0xfb,
- (byte) 0xe6, (byte) 0x20, (byte) 0xe7, (byte) 0xfc, (byte) 0x0f, (byte) 0xca,
- (byte) 0xdb, (byte) 0xc0, (byte) 0x0b, (byte) 0xe1, (byte) 0x5a, (byte) 0x16,
- (byte) 0x1d, (byte) 0xb3, (byte) 0x2e, (byte) 0xe5, (byte) 0x5f, (byte) 0x89,
- (byte) 0x17, (byte) 0x73, (byte) 0x50, (byte) 0xd1, (byte) 0x4a, (byte) 0x60,
- (byte) 0xb7, (byte) 0xaa, (byte) 0xf0, (byte) 0xc7, (byte) 0xc5, (byte) 0x03,
- (byte) 0x4e, (byte) 0x36, (byte) 0x51, (byte) 0x9e, (byte) 0x2f, (byte) 0xfa,
- (byte) 0xf3, (byte) 0xd6, (byte) 0x58, (byte) 0x14, (byte) 0x02, (byte) 0xb4,
- (byte) 0x41, (byte) 0xd6, (byte) 0x72, (byte) 0x6f, (byte) 0x58, (byte) 0x5b,
- (byte) 0x2d, (byte) 0x23, (byte) 0xc0, (byte) 0x75, (byte) 0x4f, (byte) 0x39,
- (byte) 0xa8, (byte) 0x6a, (byte) 0xdf, (byte) 0x79, (byte) 0x21, (byte) 0xf2,
- (byte) 0x77, (byte) 0x91, (byte) 0x3f, (byte) 0x1c, (byte) 0x4d, (byte) 0x48,
- (byte) 0x78, (byte) 0xcd, (byte) 0xed, (byte) 0x79, (byte) 0x23, (byte) 0x04,
- (byte) 0x17, (byte) 0x02, (byte) 0x15, (byte) 0x00, (byte) 0xc7, (byte) 0xe7,
- (byte) 0xe2, (byte) 0x6b, (byte) 0x14, (byte) 0xe6, (byte) 0x31, (byte) 0x12,
- (byte) 0xb2, (byte) 0x1e, (byte) 0xd4, (byte) 0xf2, (byte) 0x9b, (byte) 0x2c,
- (byte) 0xf6, (byte) 0x54, (byte) 0x4c, (byte) 0x12, (byte) 0xe8, (byte) 0x22
-
- };
-
- /**
- * Generated from above and converted with:
- *
- * openssl x509 -outform d -in cacert.pem | xxd -i | sed 's/0x/(byte) 0x/g'
- */
- public static final byte[] caCertificate = new byte[] {
- (byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0x8a, (byte) 0x30, (byte) 0x82,
- (byte) 0x01, (byte) 0xf3, (byte) 0xa0, (byte) 0x03, (byte) 0x02, (byte) 0x01,
- (byte) 0x02, (byte) 0x02, (byte) 0x09, (byte) 0x00, (byte) 0x87, (byte) 0xc0,
- (byte) 0x68, (byte) 0x7f, (byte) 0x42, (byte) 0x92, (byte) 0x0b, (byte) 0x7a,
- (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86,
- (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01,
- (byte) 0x05, (byte) 0x05, (byte) 0x00, (byte) 0x30, (byte) 0x5e, (byte) 0x31,
- (byte) 0x0b, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55,
- (byte) 0x04, (byte) 0x06, (byte) 0x13, (byte) 0x02, (byte) 0x41, (byte) 0x55,
- (byte) 0x31, (byte) 0x13, (byte) 0x30, (byte) 0x11, (byte) 0x06, (byte) 0x03,
- (byte) 0x55, (byte) 0x04, (byte) 0x08, (byte) 0x0c, (byte) 0x0a, (byte) 0x53,
- (byte) 0x6f, (byte) 0x6d, (byte) 0x65, (byte) 0x2d, (byte) 0x53, (byte) 0x74,
- (byte) 0x61, (byte) 0x74, (byte) 0x65, (byte) 0x31, (byte) 0x21, (byte) 0x30,
- (byte) 0x1f, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x0a,
- (byte) 0x0c, (byte) 0x18, (byte) 0x49, (byte) 0x6e, (byte) 0x74, (byte) 0x65,
- (byte) 0x72, (byte) 0x6e, (byte) 0x65, (byte) 0x74, (byte) 0x20, (byte) 0x57,
- (byte) 0x69, (byte) 0x64, (byte) 0x67, (byte) 0x69, (byte) 0x74, (byte) 0x73,
- (byte) 0x20, (byte) 0x50, (byte) 0x74, (byte) 0x79, (byte) 0x20, (byte) 0x4c,
- (byte) 0x74, (byte) 0x64, (byte) 0x31, (byte) 0x17, (byte) 0x30, (byte) 0x15,
- (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x03, (byte) 0x0c,
- (byte) 0x0e, (byte) 0x63, (byte) 0x61, (byte) 0x2e, (byte) 0x65, (byte) 0x78,
- (byte) 0x61, (byte) 0x6d, (byte) 0x70, (byte) 0x6c, (byte) 0x65, (byte) 0x2e,
- (byte) 0x63, (byte) 0x6f, (byte) 0x6d, (byte) 0x30, (byte) 0x1e, (byte) 0x17,
- (byte) 0x0d, (byte) 0x31, (byte) 0x33, (byte) 0x30, (byte) 0x38, (byte) 0x32,
- (byte) 0x37, (byte) 0x32, (byte) 0x33, (byte) 0x33, (byte) 0x31, (byte) 0x32,
- (byte) 0x39, (byte) 0x5a, (byte) 0x17, (byte) 0x0d, (byte) 0x32, (byte) 0x33,
- (byte) 0x30, (byte) 0x38, (byte) 0x32, (byte) 0x35, (byte) 0x32, (byte) 0x33,
- (byte) 0x33, (byte) 0x31, (byte) 0x32, (byte) 0x39, (byte) 0x5a, (byte) 0x30,
- (byte) 0x5e, (byte) 0x31, (byte) 0x0b, (byte) 0x30, (byte) 0x09, (byte) 0x06,
- (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x06, (byte) 0x13, (byte) 0x02,
- (byte) 0x41, (byte) 0x55, (byte) 0x31, (byte) 0x13, (byte) 0x30, (byte) 0x11,
- (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x08, (byte) 0x0c,
- (byte) 0x0a, (byte) 0x53, (byte) 0x6f, (byte) 0x6d, (byte) 0x65, (byte) 0x2d,
- (byte) 0x53, (byte) 0x74, (byte) 0x61, (byte) 0x74, (byte) 0x65, (byte) 0x31,
- (byte) 0x21, (byte) 0x30, (byte) 0x1f, (byte) 0x06, (byte) 0x03, (byte) 0x55,
- (byte) 0x04, (byte) 0x0a, (byte) 0x0c, (byte) 0x18, (byte) 0x49, (byte) 0x6e,
- (byte) 0x74, (byte) 0x65, (byte) 0x72, (byte) 0x6e, (byte) 0x65, (byte) 0x74,
- (byte) 0x20, (byte) 0x57, (byte) 0x69, (byte) 0x64, (byte) 0x67, (byte) 0x69,
- (byte) 0x74, (byte) 0x73, (byte) 0x20, (byte) 0x50, (byte) 0x74, (byte) 0x79,
- (byte) 0x20, (byte) 0x4c, (byte) 0x74, (byte) 0x64, (byte) 0x31, (byte) 0x17,
- (byte) 0x30, (byte) 0x15, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
- (byte) 0x03, (byte) 0x0c, (byte) 0x0e, (byte) 0x63, (byte) 0x61, (byte) 0x2e,
- (byte) 0x65, (byte) 0x78, (byte) 0x61, (byte) 0x6d, (byte) 0x70, (byte) 0x6c,
- (byte) 0x65, (byte) 0x2e, (byte) 0x63, (byte) 0x6f, (byte) 0x6d, (byte) 0x30,
- (byte) 0x81, (byte) 0x9f, (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09,
- (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d,
- (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x00, (byte) 0x03,
- (byte) 0x81, (byte) 0x8d, (byte) 0x00, (byte) 0x30, (byte) 0x81, (byte) 0x89,
- (byte) 0x02, (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0xa4, (byte) 0xc7,
- (byte) 0x06, (byte) 0xba, (byte) 0xdf, (byte) 0x2b, (byte) 0xee, (byte) 0xd2,
- (byte) 0xb9, (byte) 0xe4, (byte) 0x52, (byte) 0x21, (byte) 0x68, (byte) 0x2b,
- (byte) 0x83, (byte) 0xdf, (byte) 0xe3, (byte) 0x9c, (byte) 0x08, (byte) 0x73,
- (byte) 0xdd, (byte) 0x90, (byte) 0xea, (byte) 0x97, (byte) 0x0c, (byte) 0x96,
- (byte) 0x20, (byte) 0xb1, (byte) 0xee, (byte) 0x11, (byte) 0xd5, (byte) 0xd4,
- (byte) 0x7c, (byte) 0x44, (byte) 0x96, (byte) 0x2e, (byte) 0x6e, (byte) 0xa2,
- (byte) 0xb2, (byte) 0xa3, (byte) 0x4b, (byte) 0x0f, (byte) 0x32, (byte) 0x90,
- (byte) 0xaf, (byte) 0x5c, (byte) 0x6f, (byte) 0x00, (byte) 0x88, (byte) 0x45,
- (byte) 0x4e, (byte) 0x9b, (byte) 0x26, (byte) 0xc1, (byte) 0x94, (byte) 0x3c,
- (byte) 0xfe, (byte) 0x10, (byte) 0xbd, (byte) 0xda, (byte) 0xf2, (byte) 0x8d,
- (byte) 0x03, (byte) 0x52, (byte) 0x32, (byte) 0x11, (byte) 0xff, (byte) 0xf6,
- (byte) 0xf9, (byte) 0x6e, (byte) 0x8f, (byte) 0x0f, (byte) 0xc8, (byte) 0x0a,
- (byte) 0x48, (byte) 0x39, (byte) 0x33, (byte) 0xb9, (byte) 0x0c, (byte) 0xb3,
- (byte) 0x2b, (byte) 0xab, (byte) 0x7d, (byte) 0x79, (byte) 0x6f, (byte) 0x57,
- (byte) 0x5b, (byte) 0xb8, (byte) 0x84, (byte) 0xb6, (byte) 0xcc, (byte) 0xe8,
- (byte) 0x30, (byte) 0x78, (byte) 0xff, (byte) 0x92, (byte) 0xe5, (byte) 0x43,
- (byte) 0x2e, (byte) 0xef, (byte) 0x66, (byte) 0x98, (byte) 0xb4, (byte) 0xfe,
- (byte) 0xa2, (byte) 0x40, (byte) 0xf2, (byte) 0x1f, (byte) 0xd0, (byte) 0x86,
- (byte) 0x16, (byte) 0xc8, (byte) 0x45, (byte) 0xc4, (byte) 0x52, (byte) 0xcb,
- (byte) 0x31, (byte) 0x5c, (byte) 0x9f, (byte) 0x32, (byte) 0x3b, (byte) 0xf7,
- (byte) 0x19, (byte) 0x08, (byte) 0xc7, (byte) 0x00, (byte) 0x21, (byte) 0x7d,
- (byte) 0x02, (byte) 0x03, (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0xa3,
- (byte) 0x50, (byte) 0x30, (byte) 0x4e, (byte) 0x30, (byte) 0x1d, (byte) 0x06,
- (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x0e, (byte) 0x04, (byte) 0x16,
- (byte) 0x04, (byte) 0x14, (byte) 0x47, (byte) 0x82, (byte) 0xa3, (byte) 0xf1,
- (byte) 0xc2, (byte) 0x7e, (byte) 0x3a, (byte) 0xde, (byte) 0x4f, (byte) 0x30,
- (byte) 0x4c, (byte) 0x7f, (byte) 0x72, (byte) 0x81, (byte) 0x15, (byte) 0x32,
- (byte) 0xda, (byte) 0x7f, (byte) 0x58, (byte) 0x18, (byte) 0x30, (byte) 0x1f,
- (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x23, (byte) 0x04,
- (byte) 0x18, (byte) 0x30, (byte) 0x16, (byte) 0x80, (byte) 0x14, (byte) 0x47,
- (byte) 0x82, (byte) 0xa3, (byte) 0xf1, (byte) 0xc2, (byte) 0x7e, (byte) 0x3a,
- (byte) 0xde, (byte) 0x4f, (byte) 0x30, (byte) 0x4c, (byte) 0x7f, (byte) 0x72,
- (byte) 0x81, (byte) 0x15, (byte) 0x32, (byte) 0xda, (byte) 0x7f, (byte) 0x58,
- (byte) 0x18, (byte) 0x30, (byte) 0x0c, (byte) 0x06, (byte) 0x03, (byte) 0x55,
- (byte) 0x1d, (byte) 0x13, (byte) 0x04, (byte) 0x05, (byte) 0x30, (byte) 0x03,
- (byte) 0x01, (byte) 0x01, (byte) 0xff, (byte) 0x30, (byte) 0x0d, (byte) 0x06,
- (byte) 0x09, (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7,
- (byte) 0x0d, (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x05, (byte) 0x00,
- (byte) 0x03, (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0x08, (byte) 0x7f,
- (byte) 0x6a, (byte) 0x48, (byte) 0x90, (byte) 0x7b, (byte) 0x9b, (byte) 0x72,
- (byte) 0x13, (byte) 0xa7, (byte) 0xef, (byte) 0x6b, (byte) 0x0b, (byte) 0x59,
- (byte) 0xe5, (byte) 0x49, (byte) 0x72, (byte) 0x3a, (byte) 0xc8, (byte) 0x84,
- (byte) 0xcc, (byte) 0x23, (byte) 0x18, (byte) 0x4c, (byte) 0xec, (byte) 0xc7,
- (byte) 0xef, (byte) 0xcb, (byte) 0xa7, (byte) 0xbe, (byte) 0xe4, (byte) 0xef,
- (byte) 0x8f, (byte) 0xc6, (byte) 0x06, (byte) 0x8c, (byte) 0xc0, (byte) 0xe4,
- (byte) 0x2f, (byte) 0x2a, (byte) 0xc0, (byte) 0x35, (byte) 0x7d, (byte) 0x5e,
- (byte) 0x19, (byte) 0x29, (byte) 0x8c, (byte) 0xb9, (byte) 0xf1, (byte) 0x1e,
- (byte) 0xaf, (byte) 0x82, (byte) 0xd8, (byte) 0xe3, (byte) 0x88, (byte) 0xe1,
- (byte) 0x31, (byte) 0xc8, (byte) 0x82, (byte) 0x1f, (byte) 0x83, (byte) 0xa9,
- (byte) 0xde, (byte) 0xfe, (byte) 0x4b, (byte) 0xe2, (byte) 0x78, (byte) 0x64,
- (byte) 0xed, (byte) 0xa4, (byte) 0x7b, (byte) 0xee, (byte) 0x8d, (byte) 0x71,
- (byte) 0x1b, (byte) 0x44, (byte) 0xe6, (byte) 0xb7, (byte) 0xe8, (byte) 0xc5,
- (byte) 0x9a, (byte) 0x93, (byte) 0x92, (byte) 0x6f, (byte) 0x6f, (byte) 0xdb,
- (byte) 0xbd, (byte) 0xd7, (byte) 0x03, (byte) 0x85, (byte) 0xa9, (byte) 0x5f,
- (byte) 0x53, (byte) 0x5f, (byte) 0x5d, (byte) 0x30, (byte) 0xc6, (byte) 0xd9,
- (byte) 0xce, (byte) 0x34, (byte) 0xa8, (byte) 0xbe, (byte) 0x31, (byte) 0x47,
- (byte) 0x1c, (byte) 0xa4, (byte) 0x7f, (byte) 0xc0, (byte) 0x2c, (byte) 0xbc,
- (byte) 0xfe, (byte) 0x1a, (byte) 0x31, (byte) 0xd8, (byte) 0x77, (byte) 0x4d,
- (byte) 0xfc, (byte) 0x45, (byte) 0x84, (byte) 0xfc, (byte) 0x45, (byte) 0x12,
- (byte) 0xab, (byte) 0x50, (byte) 0xe4, (byte) 0x45, (byte) 0xe5, (byte) 0x11
- };
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/FeatureUtil.java b/common/device-side/util/src/com/android/compatibility/common/util/FeatureUtil.java
deleted file mode 100644
index c9681d2..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/FeatureUtil.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import android.content.Context;
-import android.content.pm.FeatureInfo;
-import android.content.pm.PackageManager;
-import android.content.res.Configuration;
-import android.os.Build;
-import android.support.test.InstrumentationRegistry;
-
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Device-side utility class for detecting system features
- */
-public class FeatureUtil {
-
- public static final String AUTOMOTIVE_FEATURE = "android.hardware.type.automotive";
- public static final String LEANBACK_FEATURE = "android.software.leanback";
- public static final String LOW_RAM_FEATURE = "android.hardware.ram.low";
- public static final String TELEPHONY_FEATURE = "android.hardware.telephony";
- public static final String TV_FEATURE = "android.hardware.type.television";
- public static final String WATCH_FEATURE = "android.hardware.type.watch";
-
-
- /** Returns true if the device has a given system feature */
- public static boolean hasSystemFeature(String feature) {
- return getPackageManager().hasSystemFeature(feature);
- }
-
- /** Returns true if the device has any feature in a given collection of system features */
- public static boolean hasAnySystemFeature(String... features) {
- PackageManager pm = getPackageManager();
- for (String feature : features) {
- if (pm.hasSystemFeature(feature)) {
- return true;
- }
- }
- return false;
- }
-
- /** Returns true if the device has all features in a given collection of system features */
- public static boolean hasAllSystemFeatures(String... features) {
- PackageManager pm = getPackageManager();
- for (String feature : features) {
- if (!pm.hasSystemFeature(feature)) {
- return false;
- }
- }
- return true;
- }
-
- /** Returns all system features of the device */
- public static Set<String> getAllFeatures() {
- Set<String> allFeatures = new HashSet<String>();
- for (FeatureInfo fi : getPackageManager().getSystemAvailableFeatures()) {
- allFeatures.add(fi.name);
- }
- return allFeatures;
- }
-
- /** Returns true if the device has feature TV_FEATURE or feature LEANBACK_FEATURE */
- public static boolean isTV() {
- return hasAnySystemFeature(TV_FEATURE, LEANBACK_FEATURE);
- }
-
- /** Returns true if the device has feature WATCH_FEATURE */
- public static boolean isWatch() {
- return hasSystemFeature(WATCH_FEATURE);
- }
-
- /** Returns true if the device has feature AUTOMOTIVE_FEATURE */
- public static boolean isAutomotive() {
- return hasSystemFeature(AUTOMOTIVE_FEATURE);
- }
-
- public static boolean isVrHeadset() {
- int maskedUiMode = (getConfiguration().uiMode & Configuration.UI_MODE_TYPE_MASK);
- return (maskedUiMode == Configuration.UI_MODE_TYPE_VR_HEADSET);
- }
-
- /** Returns true if the device is a low ram device:
- * 1. API level >= O_MR1
- * 2. device has feature LOW_RAM_FEATURE
- */
- public static boolean isLowRam() {
- return ApiLevelUtil.isAtLeast(Build.VERSION_CODES.O_MR1) &&
- hasSystemFeature(LOW_RAM_FEATURE);
- }
-
- private static Context getContext() {
- return InstrumentationRegistry.getInstrumentation().getTargetContext();
- }
-
- private static PackageManager getPackageManager() {
- return getContext().getPackageManager();
- }
-
- private static Configuration getConfiguration() {
- return getContext().getResources().getConfiguration();
- }
-
- /** Returns true if the device has feature TELEPHONY_FEATURE */
- public static boolean hasTelephony() {
- return hasSystemFeature(TELEPHONY_FEATURE);
- }
-
- /** Returns true if the device has feature FEATURE_MICROPHONE */
- public static boolean hasMicrophone() {
- return hasSystemFeature(getPackageManager().FEATURE_MICROPHONE);
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/FileCopyHelper.java b/common/device-side/util/src/com/android/compatibility/common/util/FileCopyHelper.java
deleted file mode 100644
index f58dbd0..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/FileCopyHelper.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import android.content.Context;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.ArrayList;
-
-/**
- * FileCopyHelper is used to copy files from resources to the
- * application directory and responsible for deleting the files.
- *
- * @see MediaStore_VideoTest
- * @see MediaStore_Images_MediaTest
- * @see MediaStore_Images_ThumbnailsTest
- */
-public class FileCopyHelper {
- /** The context. */
- private Context mContext;
-
- /** The files added. */
- private ArrayList<String> mFilesList;
-
- /**
- * Instantiates a new file copy helper.
- *
- * @param context the context
- */
- public FileCopyHelper(Context context) {
- mContext = context;
- mFilesList = new ArrayList<String>();
- }
-
- /**
- * Copy the file from the resources with a filename.
- *
- * @param resId the res id
- * @param fileName the file name
- *
- * @return the absolute path of the destination file
- * @throws IOException
- */
- public String copy(int resId, String fileName) throws IOException {
- InputStream source = mContext.getResources().openRawResource(resId);
- OutputStream target = mContext.openFileOutput(fileName, Context.MODE_WORLD_READABLE);
- copyFile(source, target);
- mFilesList.add(fileName);
- return mContext.getFileStreamPath(fileName).getAbsolutePath();
- }
-
- public void copyToExternalStorage(int resId, File path) throws IOException {
- InputStream source = mContext.getResources().openRawResource(resId);
- OutputStream target = new FileOutputStream(path);
- copyFile(source, target);
- }
-
- private void copyFile(InputStream source, OutputStream target) throws IOException {
- try {
- byte[] buffer = new byte[1024];
- for (int len = source.read(buffer); len > 0; len = source.read(buffer)) {
- target.write(buffer, 0, len);
- }
- } finally {
- if (source != null) {
- source.close();
- }
- if (target != null) {
- target.close();
- }
- }
- }
-
- /**
- * Delete all the files copied by the helper.
- */
- public void clear(){
- for (String path : mFilesList) {
- mContext.deleteFile(path);
- }
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/FileUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/FileUtils.java
deleted file mode 100644
index ceada01..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/FileUtils.java
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-/** Bits and pieces copied from hidden API of android.os.FileUtils. */
-public class FileUtils {
-
- public static final int S_IFMT = 0170000;
- public static final int S_IFSOCK = 0140000;
- public static final int S_IFLNK = 0120000;
- public static final int S_IFREG = 0100000;
- public static final int S_IFBLK = 0060000;
- public static final int S_IFDIR = 0040000;
- public static final int S_IFCHR = 0020000;
- public static final int S_IFIFO = 0010000;
-
- public static final int S_ISUID = 0004000;
- public static final int S_ISGID = 0002000;
- public static final int S_ISVTX = 0001000;
-
- public static final int S_IRWXU = 00700;
- public static final int S_IRUSR = 00400;
- public static final int S_IWUSR = 00200;
- public static final int S_IXUSR = 00100;
-
- public static final int S_IRWXG = 00070;
- public static final int S_IRGRP = 00040;
- public static final int S_IWGRP = 00020;
- public static final int S_IXGRP = 00010;
-
- public static final int S_IRWXO = 00007;
- public static final int S_IROTH = 00004;
- public static final int S_IWOTH = 00002;
- public static final int S_IXOTH = 00001;
-
- static {
- System.loadLibrary("cts_jni");
- }
-
- public static class FileStatus {
-
- public int dev;
- public int ino;
- public int mode;
- public int nlink;
- public int uid;
- public int gid;
- public int rdev;
- public long size;
- public int blksize;
- public long blocks;
- public long atime;
- public long mtime;
- public long ctime;
-
- public boolean hasModeFlag(int flag) {
- if (((S_IRWXU | S_IRWXG | S_IRWXO) & flag) != flag) {
- throw new IllegalArgumentException("Inappropriate flag " + flag);
- }
- return (mode & flag) == flag;
- }
-
- public boolean isOfType(int type) {
- if ((type & S_IFMT) != type) {
- throw new IllegalArgumentException("Unknown type " + type);
- }
- return (mode & S_IFMT) == type;
- }
- }
-
- /**
- * @param path of the file to stat
- * @param status object to set the fields on
- * @param statLinks or don't stat links (lstat vs stat)
- * @return whether or not we were able to stat the file
- */
- public native static boolean getFileStatus(String path, FileStatus status, boolean statLinks);
-
- public native static String getUserName(int uid);
-
- public native static String getGroupName(int gid);
-
- public native static int setPermissions(String file, int mode);
-
- /**
- * Copy data from a source stream to destFile.
- * Return true if succeed, return false if failed.
- */
- public static boolean copyToFile(InputStream inputStream, File destFile) {
- try {
- if (destFile.exists()) {
- destFile.delete();
- }
- FileOutputStream out = new FileOutputStream(destFile);
- try {
- byte[] buffer = new byte[4096];
- int bytesRead;
- while ((bytesRead = inputStream.read(buffer)) >= 0) {
- out.write(buffer, 0, bytesRead);
- }
- } finally {
- out.flush();
- try {
- out.getFD().sync();
- } catch (IOException e) {
- }
- out.close();
- }
- return true;
- } catch (IOException e) {
- return false;
- }
- }
-
- public static void createFile(File file, int numBytes) throws IOException {
- File parentFile = file.getParentFile();
- if (parentFile != null) {
- parentFile.mkdirs();
- }
- byte[] buffer = new byte[numBytes];
- FileOutputStream output = new FileOutputStream(file);
- try {
- output.write(buffer);
- } finally {
- output.close();
- }
- }
-
- public static byte[] readInputStreamFully(InputStream is) {
- ByteArrayOutputStream os = new ByteArrayOutputStream();
- byte[] buffer = new byte[32768];
- int count;
- try {
- while ((count = is.read(buffer)) != -1) {
- os.write(buffer, 0, count);
- }
- is.close();
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- return os.toByteArray();
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/IBinderParcelable.java b/common/device-side/util/src/com/android/compatibility/common/util/IBinderParcelable.java
deleted file mode 100644
index f3c53fe..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/IBinderParcelable.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.util;
-
-import android.os.IBinder;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-public class IBinderParcelable implements Parcelable {
- public IBinder binder;
-
- public IBinderParcelable(IBinder source) {
- binder = source;
- }
-
- public int describeContents() {
- return 0;
- }
-
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeStrongBinder(binder);
- }
-
- public static final Parcelable.Creator<IBinderParcelable>
- CREATOR = new Parcelable.Creator<IBinderParcelable>() {
-
- public IBinderParcelable createFromParcel(Parcel source) {
- return new IBinderParcelable(source);
- }
-
- public IBinderParcelable[] newArray(int size) {
- return new IBinderParcelable[size];
- }
- };
-
- private IBinderParcelable(Parcel source) {
- binder = source.readStrongBinder();
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/ImeAwareEditText.java b/common/device-side/util/src/com/android/compatibility/common/util/ImeAwareEditText.java
deleted file mode 100644
index db148bf..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/ImeAwareEditText.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputConnection;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.EditText;
-
-public class ImeAwareEditText extends EditText {
- private boolean mHasPendingShowSoftInputRequest;
- final Runnable mRunShowSoftInputIfNecessary = () -> showSoftInputIfNecessary();
-
- public ImeAwareEditText(Context context) {
- super(context, null);
- }
-
- public ImeAwareEditText(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public ImeAwareEditText(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
-
- public ImeAwareEditText(Context context, AttributeSet attrs, int defStyleAttr,
- int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- }
-
- /**
- * This method is called back by the system when the system is about to establish a connection
- * to the current input method.
- *
- * <p>This is a good and reliable signal to schedule a pending task to call
- * {@link InputMethodManager#showSoftInput(View, int)}.</p>
- *
- * @param editorInfo context about the text input field.
- * @return {@link InputConnection} to be passed to the input method.
- */
- @Override
- public InputConnection onCreateInputConnection(EditorInfo editorInfo) {
- final InputConnection ic = super.onCreateInputConnection(editorInfo);
- if (mHasPendingShowSoftInputRequest) {
- removeCallbacks(mRunShowSoftInputIfNecessary);
- post(mRunShowSoftInputIfNecessary);
- }
- return ic;
- }
-
- private void showSoftInputIfNecessary() {
- if (mHasPendingShowSoftInputRequest) {
- final InputMethodManager imm =
- getContext().getSystemService(InputMethodManager.class);
- imm.showSoftInput(this, 0);
- mHasPendingShowSoftInputRequest = false;
- }
- }
-
- public void scheduleShowSoftInput() {
- final InputMethodManager imm = getContext().getSystemService(InputMethodManager.class);
- if (imm.isActive(this)) {
- // This means that ImeAwareEditText is already connected to the IME.
- // InputMethodManager#showSoftInput() is guaranteed to pass client-side focus check.
- mHasPendingShowSoftInputRequest = false;
- removeCallbacks(mRunShowSoftInputIfNecessary);
- imm.showSoftInput(this, 0);
- return;
- }
-
- // Otherwise, InputMethodManager#showSoftInput() should be deferred after
- // onCreateInputConnection().
- mHasPendingShowSoftInputRequest = true;
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/LocationUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/LocationUtils.java
deleted file mode 100644
index f233851..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/LocationUtils.java
+++ /dev/null
@@ -1,40 +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.compatibility.common.util;
-
-import android.app.Instrumentation;
-import android.util.Log;
-
-import java.io.IOException;
-
-public class LocationUtils {
- private static String TAG = "LocationUtils";
-
- public static void registerMockLocationProvider(Instrumentation instrumentation,
- boolean enable) {
- StringBuilder command = new StringBuilder();
- command.append("appops set ");
- command.append(instrumentation.getContext().getPackageName());
- command.append(" android:mock_location ");
- command.append(enable ? "allow" : "deny");
- try {
- SystemUtil.runShellCommand(instrumentation, command.toString());
- } catch (IOException e) {
- Log.e(TAG, "Error managing mock location app. Command: " + command, e);
- }
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/MediaPerfUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/MediaPerfUtils.java
deleted file mode 100644
index 469e99a..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/MediaPerfUtils.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.util;
-
-import android.media.MediaFormat;
-import android.util.Range;
-
-import com.android.compatibility.common.util.DeviceReportLog;
-import com.android.compatibility.common.util.ResultType;
-import com.android.compatibility.common.util.ResultUnit;
-
-import java.util.Arrays;
-import android.util.Log;
-
-public class MediaPerfUtils {
- private static final String TAG = "MediaPerfUtils";
-
- private static final int MOVING_AVERAGE_NUM_FRAMES = 10;
- private static final int MOVING_AVERAGE_WINDOW_MS = 1000;
-
- // allow a variance of 2x for measured frame rates (e.g. half of lower-limit to double of
- // upper-limit of the published values). Also allow an extra 10% margin. This also acts as
- // a limit for the size of the published rates (e.g. upper-limit / lower-limit <= tolerance).
- private static final double FRAMERATE_TOLERANCE = 2.0 * 1.1;
-
- /*
- * ------------------ HELPER METHODS FOR ACHIEVABLE FRAME RATES ------------------
- */
-
- /** removes brackets from format to be included in JSON. */
- private static String formatForReport(MediaFormat format) {
- String asString = "" + format;
- return asString.substring(1, asString.length() - 1);
- }
-
- /**
- * Adds performance header info to |log| for |codecName|, |round|, |configFormat|, |inputFormat|
- * and |outputFormat|. Also appends same to |message| and returns the resulting base message
- * for logging purposes.
- */
- public static String addPerformanceHeadersToLog(
- DeviceReportLog log, String message, int round, String codecName,
- MediaFormat configFormat, MediaFormat inputFormat, MediaFormat outputFormat) {
- String mime = configFormat.getString(MediaFormat.KEY_MIME);
- int width = configFormat.getInteger(MediaFormat.KEY_WIDTH);
- int height = configFormat.getInteger(MediaFormat.KEY_HEIGHT);
-
- log.addValue("round", round, ResultType.NEUTRAL, ResultUnit.NONE);
- log.addValue("codec_name", codecName, ResultType.NEUTRAL, ResultUnit.NONE);
- log.addValue("mime_type", mime, ResultType.NEUTRAL, ResultUnit.NONE);
- log.addValue("width", width, ResultType.NEUTRAL, ResultUnit.NONE);
- log.addValue("height", height, ResultType.NEUTRAL, ResultUnit.NONE);
- log.addValue("config_format", formatForReport(configFormat),
- ResultType.NEUTRAL, ResultUnit.NONE);
- log.addValue("input_format", formatForReport(inputFormat),
- ResultType.NEUTRAL, ResultUnit.NONE);
- log.addValue("output_format", formatForReport(outputFormat),
- ResultType.NEUTRAL, ResultUnit.NONE);
-
- message += " codec=" + codecName + " round=" + round + " configFormat=" + configFormat
- + " inputFormat=" + inputFormat + " outputFormat=" + outputFormat;
-
- Range<Double> reported =
- MediaUtils.getVideoCapabilities(codecName, mime)
- .getAchievableFrameRatesFor(width, height);
- if (reported != null) {
- log.addValue("reported_low", reported.getLower(), ResultType.NEUTRAL, ResultUnit.FPS);
- log.addValue("reported_high", reported.getUpper(), ResultType.NEUTRAL, ResultUnit.FPS);
- message += " reported=" + reported.getLower() + "-" + reported.getUpper();
- }
-
- return message;
- }
-
- /**
- * Adds performance statistics based on the raw |stats| to |log|. Also prints the same into
- * logcat. Returns the "final fps" value.
- */
- public static double addPerformanceStatsToLog(
- DeviceReportLog log, MediaUtils.Stats durationsUsStats, String message) {
-
- MediaUtils.Stats frameAvgUsStats =
- durationsUsStats.movingAverage(MOVING_AVERAGE_NUM_FRAMES);
- log.addValue(
- "window_frames", MOVING_AVERAGE_NUM_FRAMES, ResultType.NEUTRAL, ResultUnit.COUNT);
- logPerformanceStats(log, frameAvgUsStats, "frame_avg_stats",
- message + " window=" + MOVING_AVERAGE_NUM_FRAMES);
-
- MediaUtils.Stats timeAvgUsStats =
- durationsUsStats.movingAverageOverSum(MOVING_AVERAGE_WINDOW_MS * 1000);
- log.addValue("window_time", MOVING_AVERAGE_WINDOW_MS, ResultType.NEUTRAL, ResultUnit.MS);
- double fps = logPerformanceStats(log, timeAvgUsStats, "time_avg_stats",
- message + " windowMs=" + MOVING_AVERAGE_WINDOW_MS);
-
- log.setSummary("fps", fps, ResultType.HIGHER_BETTER, ResultUnit.FPS);
- return fps;
- }
-
- /**
- * Adds performance statistics based on the processed |stats| to |log| using |prefix|.
- * Also prints the same into logcat using |message| as the base message. Returns the fps value
- * for |stats|. |prefix| must be lowercase alphanumeric underscored format.
- */
- private static double logPerformanceStats(
- DeviceReportLog log, MediaUtils.Stats statsUs, String prefix, String message) {
- final String[] labels = {
- "min", "p5", "p10", "p20", "p30", "p40", "p50", "p60", "p70", "p80", "p90", "p95", "max"
- };
- final double[] points = {
- 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 95, 100
- };
-
- int num = statsUs.getNum();
- long avg = Math.round(statsUs.getAverage());
- long stdev = Math.round(statsUs.getStdev());
- log.addValue(prefix + "_num", num, ResultType.NEUTRAL, ResultUnit.COUNT);
- log.addValue(prefix + "_avg", avg / 1000., ResultType.LOWER_BETTER, ResultUnit.MS);
- log.addValue(prefix + "_stdev", stdev / 1000., ResultType.LOWER_BETTER, ResultUnit.MS);
- message += " num=" + num + " avg=" + avg + " stdev=" + stdev;
- final double[] percentiles = statsUs.getPercentiles(points);
- for (int i = 0; i < labels.length; ++i) {
- long p = Math.round(percentiles[i]);
- message += " " + labels[i] + "=" + p;
- log.addValue(prefix + "_" + labels[i], p / 1000., ResultType.NEUTRAL, ResultUnit.MS);
- }
-
- // print result to logcat in case test aborts before logs are written
- Log.i(TAG, message);
-
- return 1e6 / percentiles[points.length - 2];
- }
-
- /** Verifies |measuredFps| against reported achievable rates. Returns null if at least
- * one measurement falls within the margins of the reported range. Otherwise, returns
- * an error message to display.*/
- public static String verifyAchievableFrameRates(
- String name, String mime, int w, int h, double... measuredFps) {
- Range<Double> reported =
- MediaUtils.getVideoCapabilities(name, mime).getAchievableFrameRatesFor(w, h);
- String kind = "achievable frame rates for " + name + " " + mime + " " + w + "x" + h;
- if (reported == null) {
- return "Failed to get " + kind;
- }
- double lowerBoundary1 = reported.getLower() / FRAMERATE_TOLERANCE;
- double upperBoundary1 = reported.getUpper() * FRAMERATE_TOLERANCE;
- double lowerBoundary2 = reported.getUpper() / Math.pow(FRAMERATE_TOLERANCE, 2);
- double upperBoundary2 = reported.getLower() * Math.pow(FRAMERATE_TOLERANCE, 2);
- Log.d(TAG, name + " " + mime + " " + w + "x" + h +
- " lowerBoundary1 " + lowerBoundary1 + " upperBoundary1 " + upperBoundary1 +
- " lowerBoundary2 " + lowerBoundary2 + " upperBoundary2 " + upperBoundary2 +
- " measured " + Arrays.toString(measuredFps));
-
- for (double measured : measuredFps) {
- if (measured >= lowerBoundary1 && measured <= upperBoundary1
- && measured >= lowerBoundary2 && measured <= upperBoundary2) {
- return null;
- }
- }
-
- return "Expected " + kind + ": " + reported + ".\n"
- + "Measured frame rate: " + Arrays.toString(measuredFps) + ".\n";
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/MediaUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/MediaUtils.java
deleted file mode 100644
index c81f648..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/MediaUtils.java
+++ /dev/null
@@ -1,1243 +0,0 @@
-/*
- * Copyright 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.util;
-
-import android.content.Context;
-import android.content.res.AssetFileDescriptor;
-import android.drm.DrmConvertedStatus;
-import android.drm.DrmManagerClient;
-import android.graphics.ImageFormat;
-import android.graphics.Rect;
-import android.media.Image;
-import android.media.Image.Plane;
-import android.media.MediaCodec;
-import android.media.MediaCodec.BufferInfo;
-import android.media.MediaCodecInfo;
-import android.media.MediaCodecInfo.CodecCapabilities;
-import android.media.MediaCodecInfo.VideoCapabilities;
-import android.media.MediaCodecList;
-import android.media.MediaExtractor;
-import android.media.MediaFormat;
-import android.net.Uri;
-import android.util.Log;
-import android.util.Range;
-
-import com.android.compatibility.common.util.DeviceReportLog;
-import com.android.compatibility.common.util.ResultType;
-import com.android.compatibility.common.util.ResultUnit;
-
-import java.lang.reflect.Method;
-import java.nio.ByteBuffer;
-import java.security.MessageDigest;
-
-import static java.lang.reflect.Modifier.isPublic;
-import static java.lang.reflect.Modifier.isStatic;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-
-import static junit.framework.Assert.assertTrue;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.RandomAccessFile;
-
-public class MediaUtils {
- private static final String TAG = "MediaUtils";
-
- /*
- * ----------------------- HELPER METHODS FOR SKIPPING TESTS -----------------------
- */
- private static final int ALL_AV_TRACKS = -1;
-
- private static final MediaCodecList sMCL = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
-
- /**
- * Returns the test name (heuristically).
- *
- * Since it uses heuristics, this method has only been verified for media
- * tests. This centralizes the way to signal errors during a test.
- */
- public static String getTestName() {
- return getTestName(false /* withClass */);
- }
-
- /**
- * Returns the test name with the full class (heuristically).
- *
- * Since it uses heuristics, this method has only been verified for media
- * tests. This centralizes the way to signal errors during a test.
- */
- public static String getTestNameWithClass() {
- return getTestName(true /* withClass */);
- }
-
- private static String getTestName(boolean withClass) {
- int bestScore = -1;
- String testName = "test???";
- Map<Thread, StackTraceElement[]> traces = Thread.getAllStackTraces();
- for (Map.Entry<Thread, StackTraceElement[]> entry : traces.entrySet()) {
- StackTraceElement[] stack = entry.getValue();
- for (int index = 0; index < stack.length; ++index) {
- // method name must start with "test"
- String methodName = stack[index].getMethodName();
- if (!methodName.startsWith("test")) {
- continue;
- }
-
- int score = 0;
- // see if there is a public non-static void method that takes no argument
- Class<?> clazz;
- try {
- clazz = Class.forName(stack[index].getClassName());
- ++score;
- for (final Method method : clazz.getDeclaredMethods()) {
- if (method.getName().equals(methodName)
- && isPublic(method.getModifiers())
- && !isStatic(method.getModifiers())
- && method.getParameterTypes().length == 0
- && method.getReturnType().equals(Void.TYPE)) {
- ++score;
- break;
- }
- }
- if (score == 1) {
- // if we could read the class, but method is not public void, it is
- // not a candidate
- continue;
- }
- } catch (ClassNotFoundException e) {
- }
-
- // even if we cannot verify the method signature, there are signals in the stack
-
- // usually test method is invoked by reflection
- int depth = 1;
- while (index + depth < stack.length
- && stack[index + depth].getMethodName().equals("invoke")
- && stack[index + depth].getClassName().equals(
- "java.lang.reflect.Method")) {
- ++depth;
- }
- if (depth > 1) {
- ++score;
- // and usually test method is run by runMethod method in android.test package
- if (index + depth < stack.length) {
- if (stack[index + depth].getClassName().startsWith("android.test.")) {
- ++score;
- }
- if (stack[index + depth].getMethodName().equals("runMethod")) {
- ++score;
- }
- }
- }
-
- if (score > bestScore) {
- bestScore = score;
- testName = methodName;
- if (withClass) {
- testName = stack[index].getClassName() + "." + testName;
- }
- }
- }
- }
- return testName;
- }
-
- /**
- * Finds test name (heuristically) and prints out standard skip message.
- *
- * Since it uses heuristics, this method has only been verified for media
- * tests. This centralizes the way to signal a skipped test.
- */
- public static void skipTest(String tag, String reason) {
- Log.i(tag, "SKIPPING " + getTestName() + "(): " + reason);
- DeviceReportLog log = new DeviceReportLog("CtsMediaSkippedTests", "test_skipped");
- try {
- log.addValue("reason", reason, ResultType.NEUTRAL, ResultUnit.NONE);
- log.addValue(
- "test", getTestNameWithClass(), ResultType.NEUTRAL, ResultUnit.NONE);
- log.submit();
- } catch (NullPointerException e) { }
- }
-
- /**
- * Finds test name (heuristically) and prints out standard skip message.
- *
- * Since it uses heuristics, this method has only been verified for media
- * tests. This centralizes the way to signal a skipped test.
- */
- public static void skipTest(String reason) {
- skipTest(TAG, reason);
- }
-
- public static boolean check(boolean result, String message) {
- if (!result) {
- skipTest(message);
- }
- return result;
- }
-
- /*
- * ------------------- HELPER METHODS FOR CHECKING CODEC SUPPORT -------------------
- */
-
- public static boolean isGoogle(String codecName) {
- codecName = codecName.toLowerCase();
- return codecName.startsWith("omx.google.")
- || codecName.startsWith("c2.android.")
- || codecName.startsWith("c2.google.");
- }
-
- // returns the list of codecs that support any one of the formats
- private static String[] getCodecNames(
- boolean isEncoder, Boolean isGoog, MediaFormat... formats) {
- MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
- ArrayList<String> result = new ArrayList<>();
- for (MediaCodecInfo info : mcl.getCodecInfos()) {
- if (info.isEncoder() != isEncoder) {
- continue;
- }
- if (isGoog != null && isGoogle(info.getName()) != isGoog) {
- continue;
- }
-
- for (MediaFormat format : formats) {
- String mime = format.getString(MediaFormat.KEY_MIME);
-
- CodecCapabilities caps = null;
- try {
- caps = info.getCapabilitiesForType(mime);
- } catch (IllegalArgumentException e) { // mime is not supported
- continue;
- }
- if (caps.isFormatSupported(format)) {
- result.add(info.getName());
- break;
- }
- }
- }
- return result.toArray(new String[result.size()]);
- }
-
- /* Use isGoog = null to query all decoders */
- public static String[] getDecoderNames(/* Nullable */ Boolean isGoog, MediaFormat... formats) {
- return getCodecNames(false /* isEncoder */, isGoog, formats);
- }
-
- public static String[] getDecoderNames(MediaFormat... formats) {
- return getCodecNames(false /* isEncoder */, null /* isGoog */, formats);
- }
-
- /* Use isGoog = null to query all decoders */
- public static String[] getEncoderNames(/* Nullable */ Boolean isGoog, MediaFormat... formats) {
- return getCodecNames(true /* isEncoder */, isGoog, formats);
- }
-
- public static String[] getEncoderNames(MediaFormat... formats) {
- return getCodecNames(true /* isEncoder */, null /* isGoog */, formats);
- }
-
- public static String[] getDecoderNamesForMime(String mime) {
- MediaFormat format = new MediaFormat();
- format.setString(MediaFormat.KEY_MIME, mime);
- return getCodecNames(false /* isEncoder */, null /* isGoog */, format);
- }
-
- public static String[] getEncoderNamesForMime(String mime) {
- MediaFormat format = new MediaFormat();
- format.setString(MediaFormat.KEY_MIME, mime);
- return getCodecNames(true /* isEncoder */, null /* isGoog */, format);
- }
-
- public static void verifyNumCodecs(
- int count, boolean isEncoder, Boolean isGoog, MediaFormat... formats) {
- String desc = (isEncoder ? "encoders" : "decoders") + " for "
- + (formats.length == 1 ? formats[0].toString() : Arrays.toString(formats));
- if (isGoog != null) {
- desc = (isGoog ? "Google " : "non-Google ") + desc;
- }
-
- String[] codecs = getCodecNames(isEncoder, isGoog, formats);
- assertTrue("test can only verify " + count + " " + desc + "; found " + codecs.length + ": "
- + Arrays.toString(codecs), codecs.length <= count);
- }
-
- public static MediaCodec getDecoder(MediaFormat format) {
- String decoder = sMCL.findDecoderForFormat(format);
- if (decoder != null) {
- try {
- return MediaCodec.createByCodecName(decoder);
- } catch (IOException e) {
- }
- }
- return null;
- }
-
- public static boolean canEncode(MediaFormat format) {
- if (sMCL.findEncoderForFormat(format) == null) {
- Log.i(TAG, "no encoder for " + format);
- return false;
- }
- return true;
- }
-
- public static boolean canDecode(MediaFormat format) {
- if (sMCL.findDecoderForFormat(format) == null) {
- Log.i(TAG, "no decoder for " + format);
- return false;
- }
- return true;
- }
-
- public static boolean supports(String codecName, String mime, int w, int h) {
- // While this could be simply written as such, give more graceful feedback.
- // MediaFormat format = MediaFormat.createVideoFormat(mime, w, h);
- // return supports(codecName, format);
-
- VideoCapabilities vidCap = getVideoCapabilities(codecName, mime);
- if (vidCap == null) {
- return false;
- } else if (vidCap.isSizeSupported(w, h)) {
- return true;
- }
-
- Log.w(TAG, "unsupported size " + w + "x" + h);
- return false;
- }
-
- public static boolean supports(String codecName, MediaFormat format) {
- MediaCodec codec;
- try {
- codec = MediaCodec.createByCodecName(codecName);
- } catch (IOException e) {
- Log.w(TAG, "codec not found: " + codecName);
- return false;
- }
-
- String mime = format.getString(MediaFormat.KEY_MIME);
- CodecCapabilities cap = null;
- try {
- cap = codec.getCodecInfo().getCapabilitiesForType(mime);
- return cap.isFormatSupported(format);
- } catch (IllegalArgumentException e) {
- Log.w(TAG, "not supported mime: " + mime);
- return false;
- } finally {
- codec.release();
- }
- }
-
- public static boolean hasCodecForTrack(MediaExtractor ex, int track) {
- int count = ex.getTrackCount();
- if (track < 0 || track >= count) {
- throw new IndexOutOfBoundsException(track + " not in [0.." + (count - 1) + "]");
- }
- return canDecode(ex.getTrackFormat(track));
- }
-
- /**
- * return true iff all audio and video tracks are supported
- */
- public static boolean hasCodecsForMedia(MediaExtractor ex) {
- for (int i = 0; i < ex.getTrackCount(); ++i) {
- MediaFormat format = ex.getTrackFormat(i);
- // only check for audio and video codecs
- String mime = format.getString(MediaFormat.KEY_MIME).toLowerCase();
- if (!mime.startsWith("audio/") && !mime.startsWith("video/")) {
- continue;
- }
- if (!canDecode(format)) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * return true iff any track starting with mimePrefix is supported
- */
- public static boolean hasCodecForMediaAndDomain(MediaExtractor ex, String mimePrefix) {
- mimePrefix = mimePrefix.toLowerCase();
- for (int i = 0; i < ex.getTrackCount(); ++i) {
- MediaFormat format = ex.getTrackFormat(i);
- String mime = format.getString(MediaFormat.KEY_MIME);
- if (mime.toLowerCase().startsWith(mimePrefix)) {
- if (canDecode(format)) {
- return true;
- }
- Log.i(TAG, "no decoder for " + format);
- }
- }
- return false;
- }
-
- private static boolean hasCodecsForResourceCombo(
- Context context, int resourceId, int track, String mimePrefix) {
- try {
- AssetFileDescriptor afd = null;
- MediaExtractor ex = null;
- try {
- afd = context.getResources().openRawResourceFd(resourceId);
- ex = new MediaExtractor();
- ex.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
- if (mimePrefix != null) {
- return hasCodecForMediaAndDomain(ex, mimePrefix);
- } else if (track == ALL_AV_TRACKS) {
- return hasCodecsForMedia(ex);
- } else {
- return hasCodecForTrack(ex, track);
- }
- } finally {
- if (ex != null) {
- ex.release();
- }
- if (afd != null) {
- afd.close();
- }
- }
- } catch (IOException e) {
- Log.i(TAG, "could not open resource");
- }
- return false;
- }
-
- /**
- * return true iff all audio and video tracks are supported
- */
- public static boolean hasCodecsForResource(Context context, int resourceId) {
- return hasCodecsForResourceCombo(context, resourceId, ALL_AV_TRACKS, null /* mimePrefix */);
- }
-
- public static boolean checkCodecsForResource(Context context, int resourceId) {
- return check(hasCodecsForResource(context, resourceId), "no decoder found");
- }
-
- /**
- * return true iff track is supported.
- */
- public static boolean hasCodecForResource(Context context, int resourceId, int track) {
- return hasCodecsForResourceCombo(context, resourceId, track, null /* mimePrefix */);
- }
-
- public static boolean checkCodecForResource(Context context, int resourceId, int track) {
- return check(hasCodecForResource(context, resourceId, track), "no decoder found");
- }
-
- /**
- * return true iff any track starting with mimePrefix is supported
- */
- public static boolean hasCodecForResourceAndDomain(
- Context context, int resourceId, String mimePrefix) {
- return hasCodecsForResourceCombo(context, resourceId, ALL_AV_TRACKS, mimePrefix);
- }
-
- /**
- * return true iff all audio and video tracks are supported
- */
- public static boolean hasCodecsForPath(Context context, String path) {
- MediaExtractor ex = null;
- try {
- ex = getExtractorForPath(context, path);
- return hasCodecsForMedia(ex);
- } catch (IOException e) {
- Log.i(TAG, "could not open path " + path);
- } finally {
- if (ex != null) {
- ex.release();
- }
- }
- return true;
- }
-
- private static MediaExtractor getExtractorForPath(Context context, String path)
- throws IOException {
- Uri uri = Uri.parse(path);
- String scheme = uri.getScheme();
- MediaExtractor ex = new MediaExtractor();
- try {
- if (scheme == null) { // file
- ex.setDataSource(path);
- } else if (scheme.equalsIgnoreCase("file")) {
- ex.setDataSource(uri.getPath());
- } else {
- ex.setDataSource(context, uri, null);
- }
- } catch (IOException e) {
- ex.release();
- throw e;
- }
- return ex;
- }
-
- public static boolean checkCodecsForPath(Context context, String path) {
- return check(hasCodecsForPath(context, path), "no decoder found");
- }
-
- public static boolean hasCodecForDomain(boolean encoder, String domain) {
- for (MediaCodecInfo info : sMCL.getCodecInfos()) {
- if (encoder != info.isEncoder()) {
- continue;
- }
-
- for (String type : info.getSupportedTypes()) {
- if (type.toLowerCase().startsWith(domain.toLowerCase() + "/")) {
- Log.i(TAG, "found codec " + info.getName() + " for mime " + type);
- return true;
- }
- }
- }
- return false;
- }
-
- public static boolean checkCodecForDomain(boolean encoder, String domain) {
- return check(hasCodecForDomain(encoder, domain),
- "no " + domain + (encoder ? " encoder" : " decoder") + " found");
- }
-
- private static boolean hasCodecForMime(boolean encoder, String mime) {
- for (MediaCodecInfo info : sMCL.getCodecInfos()) {
- if (encoder != info.isEncoder()) {
- continue;
- }
-
- for (String type : info.getSupportedTypes()) {
- if (type.equalsIgnoreCase(mime)) {
- Log.i(TAG, "found codec " + info.getName() + " for mime " + mime);
- return true;
- }
- }
- }
- return false;
- }
-
- private static boolean hasCodecForMimes(boolean encoder, String[] mimes) {
- for (String mime : mimes) {
- if (!hasCodecForMime(encoder, mime)) {
- Log.i(TAG, "no " + (encoder ? "encoder" : "decoder") + " for mime " + mime);
- return false;
- }
- }
- return true;
- }
-
-
- public static boolean hasEncoder(String... mimes) {
- return hasCodecForMimes(true /* encoder */, mimes);
- }
-
- public static boolean hasDecoder(String... mimes) {
- return hasCodecForMimes(false /* encoder */, mimes);
- }
-
- public static boolean checkDecoder(String... mimes) {
- return check(hasCodecForMimes(false /* encoder */, mimes), "no decoder found");
- }
-
- public static boolean checkEncoder(String... mimes) {
- return check(hasCodecForMimes(true /* encoder */, mimes), "no encoder found");
- }
-
- public static boolean canDecodeVideo(String mime, int width, int height, float rate) {
- MediaFormat format = MediaFormat.createVideoFormat(mime, width, height);
- format.setFloat(MediaFormat.KEY_FRAME_RATE, rate);
- return canDecode(format);
- }
-
- public static boolean canDecodeVideo(
- String mime, int width, int height, float rate,
- Integer profile, Integer level, Integer bitrate) {
- MediaFormat format = MediaFormat.createVideoFormat(mime, width, height);
- format.setFloat(MediaFormat.KEY_FRAME_RATE, rate);
- if (profile != null) {
- format.setInteger(MediaFormat.KEY_PROFILE, profile);
- if (level != null) {
- format.setInteger(MediaFormat.KEY_LEVEL, level);
- }
- }
- if (bitrate != null) {
- format.setInteger(MediaFormat.KEY_BIT_RATE, bitrate);
- }
- return canDecode(format);
- }
-
- public static boolean checkEncoderForFormat(MediaFormat format) {
- return check(canEncode(format), "no encoder for " + format);
- }
-
- public static boolean checkDecoderForFormat(MediaFormat format) {
- return check(canDecode(format), "no decoder for " + format);
- }
-
- /*
- * ----------------------- HELPER METHODS FOR MEDIA HANDLING -----------------------
- */
-
- public static VideoCapabilities getVideoCapabilities(String codecName, String mime) {
- for (MediaCodecInfo info : sMCL.getCodecInfos()) {
- if (!info.getName().equalsIgnoreCase(codecName)) {
- continue;
- }
- CodecCapabilities caps;
- try {
- caps = info.getCapabilitiesForType(mime);
- } catch (IllegalArgumentException e) {
- // mime is not supported
- Log.w(TAG, "not supported mime: " + mime);
- return null;
- }
- VideoCapabilities vidCaps = caps.getVideoCapabilities();
- if (vidCaps == null) {
- Log.w(TAG, "not a video codec: " + codecName);
- }
- return vidCaps;
- }
- Log.w(TAG, "codec not found: " + codecName);
- return null;
- }
-
- public static MediaFormat getTrackFormatForResource(
- Context context,
- int resourceId,
- String mimeTypePrefix) throws IOException {
- MediaExtractor extractor = new MediaExtractor();
- AssetFileDescriptor afd = context.getResources().openRawResourceFd(resourceId);
- try {
- extractor.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
- } finally {
- afd.close();
- }
- return getTrackFormatForExtractor(extractor, mimeTypePrefix);
- }
-
- public static MediaFormat getTrackFormatForPath(
- Context context, String path, String mimeTypePrefix)
- throws IOException {
- MediaExtractor extractor = getExtractorForPath(context, path);
- return getTrackFormatForExtractor(extractor, mimeTypePrefix);
- }
-
- private static MediaFormat getTrackFormatForExtractor(
- MediaExtractor extractor,
- String mimeTypePrefix) {
- int trackIndex;
- MediaFormat format = null;
- for (trackIndex = 0; trackIndex < extractor.getTrackCount(); trackIndex++) {
- MediaFormat trackMediaFormat = extractor.getTrackFormat(trackIndex);
- if (trackMediaFormat.getString(MediaFormat.KEY_MIME).startsWith(mimeTypePrefix)) {
- format = trackMediaFormat;
- break;
- }
- }
- extractor.release();
- if (format == null) {
- throw new RuntimeException("couldn't get a track for " + mimeTypePrefix);
- }
-
- return format;
- }
-
- public static MediaExtractor createMediaExtractorForMimeType(
- Context context, int resourceId, String mimeTypePrefix)
- throws IOException {
- MediaExtractor extractor = new MediaExtractor();
- AssetFileDescriptor afd = context.getResources().openRawResourceFd(resourceId);
- try {
- extractor.setDataSource(
- afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
- } finally {
- afd.close();
- }
- int trackIndex;
- for (trackIndex = 0; trackIndex < extractor.getTrackCount(); trackIndex++) {
- MediaFormat trackMediaFormat = extractor.getTrackFormat(trackIndex);
- if (trackMediaFormat.getString(MediaFormat.KEY_MIME).startsWith(mimeTypePrefix)) {
- extractor.selectTrack(trackIndex);
- break;
- }
- }
- if (trackIndex == extractor.getTrackCount()) {
- extractor.release();
- throw new IllegalStateException("couldn't get a track for " + mimeTypePrefix);
- }
-
- return extractor;
- }
-
- /*
- * ---------------------- HELPER METHODS FOR CODEC CONFIGURATION
- */
-
- /** Format must contain mime, width and height.
- * Throws Exception if encoder does not support this width and height */
- public static void setMaxEncoderFrameAndBitrates(
- MediaCodec encoder, MediaFormat format, int maxFps) {
- String mime = format.getString(MediaFormat.KEY_MIME);
-
- VideoCapabilities vidCaps =
- encoder.getCodecInfo().getCapabilitiesForType(mime).getVideoCapabilities();
- setMaxEncoderFrameAndBitrates(vidCaps, format, maxFps);
- }
-
- public static void setMaxEncoderFrameAndBitrates(
- VideoCapabilities vidCaps, MediaFormat format, int maxFps) {
- int width = format.getInteger(MediaFormat.KEY_WIDTH);
- int height = format.getInteger(MediaFormat.KEY_HEIGHT);
-
- int maxWidth = vidCaps.getSupportedWidths().getUpper();
- int maxHeight = vidCaps.getSupportedHeightsFor(maxWidth).getUpper();
- int frameRate = Math.min(
- maxFps, vidCaps.getSupportedFrameRatesFor(width, height).getUpper().intValue());
- format.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);
-
- int bitrate = vidCaps.getBitrateRange().clamp(
- (int)(vidCaps.getBitrateRange().getUpper() /
- Math.sqrt((double)maxWidth * maxHeight / width / height)));
- format.setInteger(MediaFormat.KEY_BIT_RATE, bitrate);
- }
-
- /*
- * ------------------ HELPER METHODS FOR STATISTICS AND REPORTING ------------------
- */
-
- // TODO: migrate this into com.android.compatibility.common.util.Stat
- public static class Stats {
- /** does not support NaN or Inf in |data| */
- public Stats(double[] data) {
- mData = data;
- if (mData != null) {
- mNum = mData.length;
- }
- }
-
- public int getNum() {
- return mNum;
- }
-
- /** calculate mSumX and mSumXX */
- private void analyze() {
- if (mAnalyzed) {
- return;
- }
-
- if (mData != null) {
- for (double x : mData) {
- if (!(x >= mMinX)) { // mMinX may be NaN
- mMinX = x;
- }
- if (!(x <= mMaxX)) { // mMaxX may be NaN
- mMaxX = x;
- }
- mSumX += x;
- mSumXX += x * x;
- }
- }
- mAnalyzed = true;
- }
-
- /** returns the maximum or NaN if it does not exist */
- public double getMin() {
- analyze();
- return mMinX;
- }
-
- /** returns the minimum or NaN if it does not exist */
- public double getMax() {
- analyze();
- return mMaxX;
- }
-
- /** returns the average or NaN if it does not exist. */
- public double getAverage() {
- analyze();
- if (mNum == 0) {
- return Double.NaN;
- } else {
- return mSumX / mNum;
- }
- }
-
- /** returns the standard deviation or NaN if it does not exist. */
- public double getStdev() {
- analyze();
- if (mNum == 0) {
- return Double.NaN;
- } else {
- double average = mSumX / mNum;
- return Math.sqrt(mSumXX / mNum - average * average);
- }
- }
-
- /** returns the statistics for the moving average over n values */
- public Stats movingAverage(int n) {
- if (n < 1 || mNum < n) {
- return new Stats(null);
- } else if (n == 1) {
- return this;
- }
-
- double[] avgs = new double[mNum - n + 1];
- double sum = 0;
- for (int i = 0; i < mNum; ++i) {
- sum += mData[i];
- if (i >= n - 1) {
- avgs[i - n + 1] = sum / n;
- sum -= mData[i - n + 1];
- }
- }
- return new Stats(avgs);
- }
-
- /** returns the statistics for the moving average over a window over the
- * cumulative sum. Basically, moves a window from: [0, window] to
- * [sum - window, sum] over the cumulative sum, over ((sum - window) / average)
- * steps, and returns the average value over each window.
- * This method is used to average time-diff data over a window of a constant time.
- */
- public Stats movingAverageOverSum(double window) {
- if (window <= 0 || mNum < 1) {
- return new Stats(null);
- }
-
- analyze();
- double average = mSumX / mNum;
- if (window >= mSumX) {
- return new Stats(new double[] { average });
- }
- int samples = (int)Math.ceil((mSumX - window) / average);
- double[] avgs = new double[samples];
-
- // A somewhat brute force approach to calculating the moving average.
- // TODO: add support for weights in Stats, so we can do a more refined approach.
- double sum = 0; // sum of elements in the window
- int num = 0; // number of elements in the moving window
- int bi = 0; // index of the first element in the moving window
- int ei = 0; // index of the last element in the moving window
- double space = window; // space at the end of the window
- double foot = 0; // space at the beginning of the window
-
- // invariants: foot + sum + space == window
- // bi + num == ei
- //
- // window: |-------------------------------|
- // | <-----sum------> |
- // <foot> <---space-->
- // | |
- // intervals: |-----------|-------|-------|--------------------|--------|
- // ^bi ^ei
-
- int ix = 0; // index in the result
- while (ix < samples) {
- // add intervals while there is space in the window
- while (ei < mData.length && mData[ei] <= space) {
- space -= mData[ei];
- sum += mData[ei];
- num++;
- ei++;
- }
-
- // calculate average over window and deal with odds and ends (e.g. if there are no
- // intervals in the current window: pick whichever element overlaps the window
- // most.
- if (num > 0) {
- avgs[ix++] = sum / num;
- } else if (bi > 0 && foot > space) {
- // consider previous
- avgs[ix++] = mData[bi - 1];
- } else if (ei == mData.length) {
- break;
- } else {
- avgs[ix++] = mData[ei];
- }
-
- // move the window to the next position
- foot -= average;
- space += average;
-
- // remove intervals that are now partially or wholly outside of the window
- while (bi < ei && foot < 0) {
- foot += mData[bi];
- sum -= mData[bi];
- num--;
- bi++;
- }
- }
- return new Stats(Arrays.copyOf(avgs, ix));
- }
-
- /** calculate mSortedData */
- private void sort() {
- if (mSorted || mNum == 0) {
- return;
- }
- mSortedData = Arrays.copyOf(mData, mNum);
- Arrays.sort(mSortedData);
- mSorted = true;
- }
-
- /** returns an array of percentiles for the points using nearest rank */
- public double[] getPercentiles(double... points) {
- sort();
- double[] res = new double[points.length];
- for (int i = 0; i < points.length; ++i) {
- if (mNum < 1 || points[i] < 0 || points[i] > 100) {
- res[i] = Double.NaN;
- } else {
- res[i] = mSortedData[(int)Math.round(points[i] / 100 * (mNum - 1))];
- }
- }
- return res;
- }
-
- @Override
- public boolean equals(Object o) {
- if (o instanceof Stats) {
- Stats other = (Stats)o;
- if (other.mNum != mNum) {
- return false;
- } else if (mNum == 0) {
- return true;
- }
- return Arrays.equals(mData, other.mData);
- }
- return false;
- }
-
- private double[] mData;
- private double mSumX = 0;
- private double mSumXX = 0;
- private double mMinX = Double.NaN;
- private double mMaxX = Double.NaN;
- private int mNum = 0;
- private boolean mAnalyzed = false;
- private double[] mSortedData;
- private boolean mSorted = false;
- }
-
- /**
- * Convert a forward lock .dm message stream to a .fl file
- * @param context Context to use
- * @param dmStream The .dm message
- * @param flFile The output file to be written
- * @return success
- */
- public static boolean convertDmToFl(
- Context context,
- InputStream dmStream,
- RandomAccessFile flFile) {
- final String MIMETYPE_DRM_MESSAGE = "application/vnd.oma.drm.message";
- byte[] dmData = new byte[10000];
- int totalRead = 0;
- int numRead;
- while (true) {
- try {
- numRead = dmStream.read(dmData, totalRead, dmData.length - totalRead);
- } catch (IOException e) {
- Log.w(TAG, "Failed to read from input file");
- return false;
- }
- if (numRead == -1) {
- break;
- }
- totalRead += numRead;
- if (totalRead == dmData.length) {
- // grow array
- dmData = Arrays.copyOf(dmData, dmData.length + 10000);
- }
- }
- byte[] fileData = Arrays.copyOf(dmData, totalRead);
-
- DrmManagerClient drmClient = null;
- try {
- drmClient = new DrmManagerClient(context);
- } catch (IllegalArgumentException e) {
- Log.w(TAG, "DrmManagerClient instance could not be created, context is Illegal.");
- return false;
- } catch (IllegalStateException e) {
- Log.w(TAG, "DrmManagerClient didn't initialize properly.");
- return false;
- }
-
- try {
- int convertSessionId = -1;
- try {
- convertSessionId = drmClient.openConvertSession(MIMETYPE_DRM_MESSAGE);
- } catch (IllegalArgumentException e) {
- Log.w(TAG, "Conversion of Mimetype: " + MIMETYPE_DRM_MESSAGE
- + " is not supported.", e);
- return false;
- } catch (IllegalStateException e) {
- Log.w(TAG, "Could not access Open DrmFramework.", e);
- return false;
- }
-
- if (convertSessionId < 0) {
- Log.w(TAG, "Failed to open session.");
- return false;
- }
-
- DrmConvertedStatus convertedStatus = null;
- try {
- convertedStatus = drmClient.convertData(convertSessionId, fileData);
- } catch (IllegalArgumentException e) {
- Log.w(TAG, "Buffer with data to convert is illegal. Convertsession: "
- + convertSessionId, e);
- return false;
- } catch (IllegalStateException e) {
- Log.w(TAG, "Could not convert data. Convertsession: " + convertSessionId, e);
- return false;
- }
-
- if (convertedStatus == null ||
- convertedStatus.statusCode != DrmConvertedStatus.STATUS_OK ||
- convertedStatus.convertedData == null) {
- Log.w(TAG, "Error in converting data. Convertsession: " + convertSessionId);
- try {
- DrmConvertedStatus result = drmClient.closeConvertSession(convertSessionId);
- if (result.statusCode != DrmConvertedStatus.STATUS_OK) {
- Log.w(TAG, "Conversion failed with status: " + result.statusCode);
- return false;
- }
- } catch (IllegalStateException e) {
- Log.w(TAG, "Could not close session. Convertsession: " +
- convertSessionId, e);
- }
- return false;
- }
-
- try {
- flFile.write(convertedStatus.convertedData, 0, convertedStatus.convertedData.length);
- } catch (IOException e) {
- Log.w(TAG, "Failed to write to output file: " + e);
- return false;
- }
-
- try {
- convertedStatus = drmClient.closeConvertSession(convertSessionId);
- } catch (IllegalStateException e) {
- Log.w(TAG, "Could not close convertsession. Convertsession: " +
- convertSessionId, e);
- return false;
- }
-
- if (convertedStatus == null ||
- convertedStatus.statusCode != DrmConvertedStatus.STATUS_OK ||
- convertedStatus.convertedData == null) {
- Log.w(TAG, "Error in closing session. Convertsession: " + convertSessionId);
- return false;
- }
-
- try {
- flFile.seek(convertedStatus.offset);
- flFile.write(convertedStatus.convertedData);
- } catch (IOException e) {
- Log.w(TAG, "Could not update file.", e);
- return false;
- }
-
- return true;
- } finally {
- drmClient.close();
- }
- }
-
- /**
- * @param decoder new MediaCodec object
- * @param ex MediaExtractor after setDataSource and selectTrack
- * @param frameMD5Sums reference MD5 checksum for decoded frames
- * @return true if decoded frames checksums matches reference checksums
- * @throws IOException
- */
- public static boolean verifyDecoder(
- MediaCodec decoder, MediaExtractor ex, List<String> frameMD5Sums)
- throws IOException {
-
- int trackIndex = ex.getSampleTrackIndex();
- MediaFormat format = ex.getTrackFormat(trackIndex);
- decoder.configure(format, null /* surface */, null /* crypto */, 0 /* flags */);
- decoder.start();
-
- boolean sawInputEOS = false;
- boolean sawOutputEOS = false;
- final long kTimeOutUs = 5000; // 5ms timeout
- int decodedFrameCount = 0;
- int expectedFrameCount = frameMD5Sums.size();
- MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
-
- while (!sawOutputEOS) {
- // handle input
- if (!sawInputEOS) {
- int inIdx = decoder.dequeueInputBuffer(kTimeOutUs);
- if (inIdx >= 0) {
- ByteBuffer buffer = decoder.getInputBuffer(inIdx);
- int sampleSize = ex.readSampleData(buffer, 0);
- if (sampleSize < 0) {
- final int flagEOS = MediaCodec.BUFFER_FLAG_END_OF_STREAM;
- decoder.queueInputBuffer(inIdx, 0, 0, 0, flagEOS);
- sawInputEOS = true;
- } else {
- decoder.queueInputBuffer(inIdx, 0, sampleSize, ex.getSampleTime(), 0);
- ex.advance();
- }
- }
- }
-
- // handle output
- int outputBufIndex = decoder.dequeueOutputBuffer(info, kTimeOutUs);
- if (outputBufIndex >= 0) {
- try {
- if (info.size > 0) {
- // Disregard 0-sized buffers at the end.
- String md5CheckSum = "";
- Image image = decoder.getOutputImage(outputBufIndex);
- md5CheckSum = getImageMD5Checksum(image);
-
- if (!md5CheckSum.equals(frameMD5Sums.get(decodedFrameCount))) {
- Log.d(TAG,
- String.format(
- "Frame %d md5sum mismatch: %s(actual) vs %s(expected)",
- decodedFrameCount, md5CheckSum,
- frameMD5Sums.get(decodedFrameCount)));
- return false;
- }
-
- decodedFrameCount++;
- }
- } catch (Exception e) {
- Log.e(TAG, "getOutputImage md5CheckSum failed", e);
- return false;
- } finally {
- decoder.releaseOutputBuffer(outputBufIndex, false /* render */);
- }
- if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
- sawOutputEOS = true;
- }
- } else if (outputBufIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
- MediaFormat decOutputFormat = decoder.getOutputFormat();
- Log.d(TAG, "output format " + decOutputFormat);
- } else if (outputBufIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
- Log.i(TAG, "Skip handling MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED");
- } else if (outputBufIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
- continue;
- } else {
- Log.w(TAG, "decoder.dequeueOutputBuffer() unrecognized index: " + outputBufIndex);
- return false;
- }
- }
-
- if (decodedFrameCount != expectedFrameCount) {
- return false;
- }
-
- return true;
- }
-
- public static String getImageMD5Checksum(Image image) throws Exception {
- int format = image.getFormat();
- if (ImageFormat.YUV_420_888 != format) {
- Log.w(TAG, "unsupported image format");
- return "";
- }
-
- MessageDigest md = MessageDigest.getInstance("MD5");
-
- Rect crop = image.getCropRect();
- int cropLeft = crop.left;
- int cropRight = crop.right;
- int cropTop = crop.top;
- int cropBottom = crop.bottom;
-
- int imageWidth = cropRight - cropLeft;
- int imageHeight = cropBottom - cropTop;
-
- Image.Plane[] planes = image.getPlanes();
- for (int i = 0; i < planes.length; ++i) {
- ByteBuffer buf = planes[i].getBuffer();
-
- int width, height, rowStride, pixelStride, x, y, top, left;
- rowStride = planes[i].getRowStride();
- pixelStride = planes[i].getPixelStride();
- if (i == 0) {
- width = imageWidth;
- height = imageHeight;
- left = cropLeft;
- top = cropTop;
- } else {
- width = imageWidth / 2;
- height = imageHeight /2;
- left = cropLeft / 2;
- top = cropTop / 2;
- }
- // local contiguous pixel buffer
- byte[] bb = new byte[width * height];
- if (buf.hasArray()) {
- byte b[] = buf.array();
- int offs = buf.arrayOffset() + left * pixelStride;
- if (pixelStride == 1) {
- for (y = 0; y < height; ++y) {
- System.arraycopy(bb, y * width, b, (top + y) * rowStride + offs, width);
- }
- } else {
- // do it pixel-by-pixel
- for (y = 0; y < height; ++y) {
- int lineOffset = offs + (top + y) * rowStride;
- for (x = 0; x < width; ++x) {
- bb[y * width + x] = b[lineOffset + x * pixelStride];
- }
- }
- }
- } else { // almost always ends up here due to direct buffers
- int pos = buf.position();
- if (pixelStride == 1) {
- for (y = 0; y < height; ++y) {
- buf.position(pos + left + (top + y) * rowStride);
- buf.get(bb, y * width, width);
- }
- } else {
- // local line buffer
- byte[] lb = new byte[rowStride];
- // do it pixel-by-pixel
- for (y = 0; y < height; ++y) {
- buf.position(pos + left * pixelStride + (top + y) * rowStride);
- // we're only guaranteed to have pixelStride * (width - 1) + 1 bytes
- buf.get(lb, 0, pixelStride * (width - 1) + 1);
- for (x = 0; x < width; ++x) {
- bb[y * width + x] = lb[x * pixelStride];
- }
- }
- }
- buf.position(pos);
- }
- md.update(bb, 0, width * height);
- }
-
- return convertByteArrayToHEXString(md.digest());
- }
-
- private static String convertByteArrayToHEXString(byte[] ba) throws Exception {
- StringBuilder result = new StringBuilder();
- for (int i = 0; i < ba.length; i++) {
- result.append(Integer.toString((ba[i] & 0xff) + 0x100, 16).substring(1));
- }
- return result.toString();
- }
-
-
- /*
- * -------------------------------------- END --------------------------------------
- */
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/MoreMatchers.java b/common/device-side/util/src/com/android/compatibility/common/util/MoreMatchers.java
deleted file mode 100644
index cee610e..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/MoreMatchers.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.util;
-
-import org.mockito.ArgumentMatchers;
-
-public class MoreMatchers {
- private MoreMatchers() {
- }
-
- public static <T> T anyOrNull(Class<T> clazz) {
- return ArgumentMatchers.argThat(value -> true);
- }
-
- public static String anyStringOrNull() {
- return ArgumentMatchers.argThat(value -> true);
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/NullWebViewUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/NullWebViewUtils.java
deleted file mode 100644
index 3153adb..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/NullWebViewUtils.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * 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.compatibility.common.util;
-
-import android.content.Context;
-import android.content.pm.PackageManager;
-
-/**
- * Utilities to enable the android.webkit.* CTS tests (and others that rely on a functioning
- * android.webkit.WebView implementation) to determine whether a functioning WebView is present
- * on the device or not.
- *
- * Test cases that require android.webkit.* classes should wrap their first usage of WebView in a
- * try catch block, and pass any exception that is thrown to
- * NullWebViewUtils.determineIfWebViewAvailable. The return value of
- * NullWebViewUtils.isWebViewAvailable will then determine if the test should expect to be able to
- * use a WebView.
- */
-public class NullWebViewUtils {
-
- private static boolean sWebViewUnavailable;
-
- /**
- * @param context Current Activity context, used to query the PackageManager.
- * @param t An exception thrown by trying to invoke android.webkit.* APIs.
- */
- public static void determineIfWebViewAvailable(Context context, Throwable t) {
- sWebViewUnavailable = !hasWebViewFeature(context) && checkCauseWasUnsupportedOperation(t);
- }
-
- /**
- * After calling determineIfWebViewAvailable, this returns whether a WebView is available on the
- * device and wheter the test can rely on it.
- * @return True iff. PackageManager determined that there is no WebView on the device and the
- * exception thrown from android.webkit.* was UnsupportedOperationException.
- */
- public static boolean isWebViewAvailable() {
- return !sWebViewUnavailable;
- }
-
- private static boolean hasWebViewFeature(Context context) {
- // Query the system property that determins if there is a functional WebView on the device.
- PackageManager pm = context.getPackageManager();
- return pm.hasSystemFeature(PackageManager.FEATURE_WEBVIEW);
- }
-
- private static boolean checkCauseWasUnsupportedOperation(Throwable t) {
- if (t == null) return false;
- while (t.getCause() != null) {
- t = t.getCause();
- }
- return t instanceof UnsupportedOperationException;
- }
-
- /**
- * Some CTS tests (by design) first use android.webkit.* from a background thread. This helper
- * allows the test to catch the UnsupportedOperationException from that background thread, and
- * then query the result from the test main thread.
- */
- public static class NullWebViewFromThreadExceptionHandler
- implements Thread.UncaughtExceptionHandler {
- private Throwable mPendingException;
-
- @Override
- public void uncaughtException(Thread t, Throwable e) {
- mPendingException = e;
- }
-
- public boolean isWebViewAvailable(Context context) {
- return hasWebViewFeature(context) ||
- !checkCauseWasUnsupportedOperation(mPendingException);
- }
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/OnFailureRule.java b/common/device-side/util/src/com/android/compatibility/common/util/OnFailureRule.java
deleted file mode 100644
index 585c145..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/OnFailureRule.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.util;
-
-import android.util.Log;
-
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-
-/**
- * Custom JUnit4 rule that provides a callback upon test failures.
- */
-public abstract class OnFailureRule implements TestRule {
- private String mLogTag = "OnFailureRule";
-
- public OnFailureRule() {
- }
-
- public OnFailureRule(String logTag) {
- mLogTag = logTag;
- }
-
- @Override
- public Statement apply(Statement base, Description description) {
- return new Statement() {
-
- @Override
- public void evaluate() throws Throwable {
- try {
- base.evaluate();
- } catch (Throwable t) {
- Log.e(mLogTag, "Test failed: description=" + description + "\nThrowable=" + t);
- onTestFailure(base, description, t);
- throw t;
- }
- }
- };
- }
-
- protected abstract void onTestFailure(Statement base, Description description, Throwable t);
-}
\ No newline at end of file
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/OneTimeDeviceConfigListener.java b/common/device-side/util/src/com/android/compatibility/common/util/OneTimeDeviceConfigListener.java
deleted file mode 100644
index e5be3f41..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/OneTimeDeviceConfigListener.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.util;
-
-import android.os.SystemClock;
-import android.provider.DeviceConfig;
-import android.provider.DeviceConfig.OnPropertiesChangedListener;
-import android.provider.DeviceConfig.Properties;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-
-import com.google.common.base.Preconditions;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Helper used to block tests until a device config value has been updated.
- */
-public final class OneTimeDeviceConfigListener implements OnPropertiesChangedListener {
-
- public static final long DEFAULT_TIMEOUT_MS = 5_000;
-
- private static final String TAG = OneTimeDeviceConfigListener.class.getSimpleName();
-
- private final String mNamespace;
- private final String mKey;
- private final long mTimeoutMs;
- private final long mStarted = SystemClock.elapsedRealtime();
-
- private final CountDownLatch mLatch = new CountDownLatch(1);
-
- public OneTimeDeviceConfigListener(@NonNull String namespace, @NonNull String key) {
- this(namespace, key, DEFAULT_TIMEOUT_MS);
- }
-
- public OneTimeDeviceConfigListener(@NonNull String namespace, @NonNull String key,
- long timeoutMs) {
- mNamespace = Preconditions.checkNotNull(namespace);
- mKey = Preconditions.checkNotNull(key);
- mTimeoutMs = timeoutMs;
- }
-
- @Override
- public void onPropertiesChanged(@NonNull Properties properties) {
- if (!properties.getNamespace().equals(mNamespace)
- || !properties.getKeyset().contains(mKey)) {
- Log.d(TAG, "ignoring callback for namespace: " + properties.getNamespace());
- return;
- }
- mLatch.countDown();
- DeviceConfig.removeOnPropertiesChangedListener(this);
- }
-
- /**
- * Blocks for a few seconds until it's called.
- *
- * @throws IllegalStateException if it's not called.
- */
- public void assertCalled() {
- try {
- final boolean updated = mLatch.await(mTimeoutMs, TimeUnit.MILLISECONDS);
- if (!updated) {
- throw new RetryableException(
- "Settings " + mKey + " not called in " + mTimeoutMs + "ms");
- }
- final long delta = SystemClock.elapsedRealtime() - mStarted;
- Log.v(TAG, TestNameUtils.getCurrentTestName() + "/" + mKey + ": " + delta + "ms");
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- throw new IllegalStateException("Interrupted", e);
- }
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/OneTimeSettingsListener.java b/common/device-side/util/src/com/android/compatibility/common/util/OneTimeSettingsListener.java
deleted file mode 100644
index 79c80c9..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/OneTimeSettingsListener.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import static com.android.compatibility.common.util.SettingsUtils.NAMESPACE_GLOBAL;
-import static com.android.compatibility.common.util.SettingsUtils.NAMESPACE_SECURE;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.SystemClock;
-import android.provider.Settings;
-import android.util.Log;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Helper used to block tests until a secure settings value has been updated.
- */
-public final class OneTimeSettingsListener extends ContentObserver {
-
- private static final String TAG = "OneTimeSettingsListener";
- public static final long DEFAULT_TIMEOUT_MS = 30_000;
-
- private final CountDownLatch mLatch = new CountDownLatch(1);
- private final ContentResolver mResolver;
- private final String mKey;
- private final long mStarted;
-
- public OneTimeSettingsListener(Context context, String namespace, String key) {
- super(new Handler(Looper.getMainLooper()));
- mStarted = SystemClock.elapsedRealtime();
- mKey = key;
- mResolver = context.getContentResolver();
- final Uri uri;
- switch (namespace) {
- case NAMESPACE_SECURE:
- uri = Settings.Secure.getUriFor(key);
- break;
- case NAMESPACE_GLOBAL:
- uri = Settings.Global.getUriFor(key);
- break;
- default:
- throw new IllegalArgumentException("invalid namespace: " + namespace);
- }
- mResolver.registerContentObserver(uri, false, this);
- }
-
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- mResolver.unregisterContentObserver(this);
- mLatch.countDown();
- }
-
- /**
- * Blocks for a few seconds until it's called.
- *
- * @throws IllegalStateException if it's not called.
- */
- public void assertCalled() {
- try {
- final boolean updated = mLatch.await(DEFAULT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
- if (!updated) {
- throw new RetryableException(
- "Settings " + mKey + " not called in " + DEFAULT_TIMEOUT_MS + "ms");
- }
- final long delta = SystemClock.elapsedRealtime() - mStarted;
- // TODO: usually it's notified in ~50-150ms, but for some reason it takes ~10s
- // on some ViewAttributesTest methods, hence the 30s limit
- Log.v(TAG, TestNameUtils.getCurrentTestName() + "/" + mKey + ": " + delta + "ms");
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- throw new IllegalStateException("Interrupted", e);
- }
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/PackageUtil.java b/common/device-side/util/src/com/android/compatibility/common/util/PackageUtil.java
deleted file mode 100644
index e7b6976..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/PackageUtil.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.os.Build;
-import android.support.test.InstrumentationRegistry;
-import android.util.Log;
-
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-
-/**
- * Device-side utility class for PackageManager-related operations
- */
-public class PackageUtil {
-
- private static final String TAG = PackageUtil.class.getSimpleName();
-
- private static final int SYSTEM_APP_MASK =
- ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
- private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
-
- /** Returns true if a package with the given name exists on the device */
- public static boolean exists(String packageName) {
- try {
- return (getPackageManager().getApplicationInfo(packageName,
- PackageManager.GET_META_DATA) != null);
- } catch(PackageManager.NameNotFoundException e) {
- return false;
- }
- }
-
- /** Returns true if a package with the given name AND SHA digest exists on the device */
- public static boolean exists(String packageName, String sha) {
- try {
- if (getPackageManager().getApplicationInfo(
- packageName, PackageManager.GET_META_DATA) == null) {
- return false;
- }
- return sha.equals(computePackageSignatureDigest(packageName));
- } catch (NoSuchAlgorithmException | PackageManager.NameNotFoundException e) {
- return false;
- }
- }
-
- /** Returns true if the app for the given package name is a system app for this device */
- public static boolean isSystemApp(String packageName) {
- try {
- ApplicationInfo ai = getPackageManager().getApplicationInfo(packageName,
- PackageManager.GET_META_DATA);
- return ai != null && ((ai.flags & SYSTEM_APP_MASK) != 0);
- } catch(PackageManager.NameNotFoundException e) {
- return false;
- }
- }
-
- /** Returns the version string of the package name, or null if the package can't be found */
- public static String getVersionString(String packageName) {
- try {
- PackageInfo info = getPackageManager().getPackageInfo(packageName,
- PackageManager.GET_META_DATA);
- return info.versionName;
- } catch (PackageManager.NameNotFoundException | NullPointerException e) {
- Log.w(TAG, "Could not find version string for package " + packageName);
- return null;
- }
- }
-
- /**
- * Compute the signature SHA digest for a package.
- * @param package the name of the package for which the signature SHA digest is requested
- * @return the signature SHA digest
- */
- public static String computePackageSignatureDigest(String packageName)
- throws NoSuchAlgorithmException, PackageManager.NameNotFoundException {
- PackageInfo packageInfo = getPackageManager()
- .getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
- MessageDigest messageDigest = MessageDigest.getInstance("SHA256");
- messageDigest.update(packageInfo.signatures[0].toByteArray());
-
- final byte[] digest = messageDigest.digest();
- final int digestLength = digest.length;
- final int charCount = 3 * digestLength - 1;
-
- final char[] chars = new char[charCount];
- for (int i = 0; i < digestLength; i++) {
- final int byteHex = digest[i] & 0xFF;
- chars[i * 3] = HEX_ARRAY[byteHex >>> 4];
- chars[i * 3 + 1] = HEX_ARRAY[byteHex & 0x0F];
- if (i < digestLength - 1) {
- chars[i * 3 + 2] = ':';
- }
- }
- return new String(chars);
- }
-
- private static PackageManager getPackageManager() {
- return InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageManager();
- }
-
- private static boolean hasDeviceFeature(final String requiredFeature) {
- return InstrumentationRegistry.getContext()
- .getPackageManager()
- .hasSystemFeature(requiredFeature);
- }
-
- /**
- * Rotation support is indicated by explicitly having both landscape and portrait
- * features or not listing either at all.
- */
- public static boolean supportsRotation() {
- final boolean supportsLandscape = hasDeviceFeature(PackageManager.FEATURE_SCREEN_LANDSCAPE);
- final boolean supportsPortrait = hasDeviceFeature(PackageManager.FEATURE_SCREEN_PORTRAIT);
- return (supportsLandscape && supportsPortrait)
- || (!supportsLandscape && !supportsPortrait);
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/ParcelUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/ParcelUtils.java
deleted file mode 100644
index ecaa722..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/ParcelUtils.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.util;
-
-import static org.junit.Assert.assertNotNull;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-public class ParcelUtils {
- private ParcelUtils() {
- }
-
- /** Convert a Parcelable into a byte[]. */
- public static byte[] toBytes(Parcelable p) {
- assertNotNull(p);
-
- final Parcel parcel = Parcel.obtain();
- parcel.writeParcelable(p, 0);
- byte[] data = parcel.marshall();
- parcel.recycle();
-
- return data;
- }
-
- /** Decode a byte[] into a Parcelable. */
- public static <T extends Parcelable> T fromBytes(byte[] data) {
- assertNotNull(data);
-
- final Parcel parcel = Parcel.obtain();
- parcel.unmarshall(data, 0, data.length);
- parcel.setDataPosition(0);
- T ret = parcel.readParcelable(ParcelUtils.class.getClassLoader());
- parcel.recycle();
-
- return ret;
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/PollingCheck.java b/common/device-side/util/src/com/android/compatibility/common/util/PollingCheck.java
deleted file mode 100644
index bcc3530..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/PollingCheck.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import java.util.concurrent.Callable;
-
-import junit.framework.Assert;
-
-public abstract class PollingCheck {
- private static final long TIME_SLICE = 50;
- private long mTimeout = 3000;
-
- public static interface PollingCheckCondition {
- boolean canProceed();
- }
-
- public PollingCheck() {
- }
-
- public PollingCheck(long timeout) {
- mTimeout = timeout;
- }
-
- protected abstract boolean check();
-
- public void run() {
- if (check()) {
- return;
- }
-
- long timeout = mTimeout;
- while (timeout > 0) {
- try {
- Thread.sleep(TIME_SLICE);
- } catch (InterruptedException e) {
- Assert.fail("unexpected InterruptedException");
- }
-
- if (check()) {
- return;
- }
-
- timeout -= TIME_SLICE;
- }
-
- Assert.fail("unexpected timeout");
- }
-
- public static void check(CharSequence message, long timeout, Callable<Boolean> condition)
- throws Exception {
- while (timeout > 0) {
- if (condition.call()) {
- return;
- }
-
- Thread.sleep(TIME_SLICE);
- timeout -= TIME_SLICE;
- }
-
- Assert.fail(message.toString());
- }
-
- public static void waitFor(final PollingCheckCondition condition) {
- new PollingCheck() {
- @Override
- protected boolean check() {
- return condition.canProceed();
- }
- }.run();
- }
-
- public static void waitFor(long timeout, final PollingCheckCondition condition) {
- new PollingCheck(timeout) {
- @Override
- protected boolean check() {
- return condition.canProceed();
- }
- }.run();
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/PropertyUtil.java b/common/device-side/util/src/com/android/compatibility/common/util/PropertyUtil.java
deleted file mode 100644
index c95b8df..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/PropertyUtil.java
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import android.os.Build;
-import android.support.test.InstrumentationRegistry;
-
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Scanner;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Device-side utility class for reading properties and gathering information for testing
- * Android device compatibility.
- */
-public class PropertyUtil {
-
- /**
- * Name of read-only property detailing the first API level for which the product was
- * shipped. Property should be undefined for factory ROM products.
- */
- public static final String FIRST_API_LEVEL = "ro.product.first_api_level";
- private static final String BUILD_TYPE_PROPERTY = "ro.build.type";
- private static final String MANUFACTURER_PROPERTY = "ro.product.manufacturer";
- private static final String TAG_DEV_KEYS = "dev-keys";
- private static final String VNDK_VERSION = "ro.vndk.version";
-
- public static final String GOOGLE_SETTINGS_QUERY =
- "content query --uri content://com.google.settings/partner";
-
- /** Value to be returned by getPropertyInt() if property is not found */
- public static int INT_VALUE_IF_UNSET = -1;
-
- /** Returns whether the device build is a user build */
- public static boolean isUserBuild() {
- return propertyEquals(BUILD_TYPE_PROPERTY, "user");
- }
-
- /** Returns whether this build is built with dev-keys */
- public static boolean isDevKeysBuild() {
- for (String tag : Build.TAGS.split(",")) {
- if (TAG_DEV_KEYS.equals(tag.trim())) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Return the first API level for this product. If the read-only property is unset,
- * this means the first API level is the current API level, and the current API level
- * is returned.
- */
- public static int getFirstApiLevel() {
- int firstApiLevel = getPropertyInt(FIRST_API_LEVEL);
- return (firstApiLevel == INT_VALUE_IF_UNSET) ? Build.VERSION.SDK_INT : firstApiLevel;
- }
-
- /**
- * Return whether the SDK version of the vendor partiton is newer than the given API level.
- * If the property is set to non-integer value, this means the vendor partition is using
- * current API level and true is returned.
- */
- public static boolean isVendorApiLevelNewerThan(int apiLevel) {
- int vendorApiLevel = getPropertyInt(VNDK_VERSION);
- if (vendorApiLevel == INT_VALUE_IF_UNSET) {
- return true;
- }
- return vendorApiLevel > apiLevel;
- }
-
- /**
- * Return the manufacturer of this product. If unset, return null.
- */
- public static String getManufacturer() {
- return getProperty(MANUFACTURER_PROPERTY);
- }
-
- /** Returns a mapping from client ID names to client ID values */
- public static Map<String, String> getClientIds() throws IOException {
- Map<String,String> clientIds = new HashMap<>();
- String queryOutput = SystemUtil.runShellCommand(
- InstrumentationRegistry.getInstrumentation(), GOOGLE_SETTINGS_QUERY);
- for (String line : queryOutput.split("[\\r?\\n]+")) {
- // Expected line format: "Row: 1 _id=123, name=<property_name>, value=<property_value>"
- Pattern pattern = Pattern.compile("name=([a-z_]*), value=(.*)$");
- Matcher matcher = pattern.matcher(line);
- if (matcher.find()) {
- String name = matcher.group(1);
- String value = matcher.group(2);
- if (name.contains("client_id")) {
- clientIds.put(name, value); // only add name-value pair for client ids
- }
- }
- }
- return clientIds;
- }
-
- /** Returns whether the property exists on this device */
- public static boolean propertyExists(String property) {
- return getProperty(property) != null;
- }
-
- /** Returns whether the property value is equal to a given string */
- public static boolean propertyEquals(String property, String value) {
- if (value == null) {
- return !propertyExists(property); // null value implies property does not exist
- }
- return value.equals(getProperty(property));
- }
-
- /**
- * Returns whether the property value matches a given regular expression. The method uses
- * String.matches(), requiring a complete match (i.e. expression matches entire value string)
- */
- public static boolean propertyMatches(String property, String regex) {
- if (regex == null || regex.isEmpty()) {
- // null or empty pattern implies property does not exist
- return !propertyExists(property);
- }
- String value = getProperty(property);
- return (value == null) ? false : value.matches(regex);
- }
-
- /**
- * Retrieves the desired integer property, returning INT_VALUE_IF_UNSET if not found.
- */
- public static int getPropertyInt(String property) {
- String value = getProperty(property);
- if (value == null) {
- return INT_VALUE_IF_UNSET;
- }
- try {
- return Integer.parseInt(value);
- } catch (NumberFormatException e) {
- return INT_VALUE_IF_UNSET;
- }
- }
-
- /** Retrieves the desired property value in string form */
- public static String getProperty(String property) {
- Scanner scanner = null;
- try {
- Process process = new ProcessBuilder("getprop", property).start();
- scanner = new Scanner(process.getInputStream());
- String value = scanner.nextLine().trim();
- return (value.isEmpty()) ? null : value;
- } catch (IOException e) {
- return null;
- } finally {
- if (scanner != null) {
- scanner.close();
- }
- }
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/ReadElf.java b/common/device-side/util/src/com/android/compatibility/common/util/ReadElf.java
deleted file mode 100644
index feaa9cd..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/ReadElf.java
+++ /dev/null
@@ -1,494 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.RandomAccessFile;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * A poor man's implementation of the readelf command. This program is designed
- * to parse ELF (Executable and Linkable Format) files.
- */
-public class ReadElf implements AutoCloseable {
- /** The magic values for the ELF identification. */
- private static final byte[] ELFMAG = {
- (byte) 0x7F, (byte) 'E', (byte) 'L', (byte) 'F', };
-
- private static final int EI_NIDENT = 16;
-
- private static final int EI_CLASS = 4;
- private static final int EI_DATA = 5;
-
- private static final int EM_386 = 3;
- private static final int EM_MIPS = 8;
- private static final int EM_ARM = 40;
- private static final int EM_X86_64 = 62;
- // http://en.wikipedia.org/wiki/Qualcomm_Hexagon
- private static final int EM_QDSP6 = 164;
- private static final int EM_AARCH64 = 183;
-
- private static final int ELFCLASS32 = 1;
- private static final int ELFCLASS64 = 2;
-
- private static final int ELFDATA2LSB = 1;
- private static final int ELFDATA2MSB = 2;
-
- private static final int EV_CURRENT = 1;
-
- private static final long PT_LOAD = 1;
-
- private static final int SHT_SYMTAB = 2;
- private static final int SHT_STRTAB = 3;
- private static final int SHT_DYNAMIC = 6;
- private static final int SHT_DYNSYM = 11;
-
- public static class Symbol {
- public static final int STB_LOCAL = 0;
- public static final int STB_GLOBAL = 1;
- public static final int STB_WEAK = 2;
- public static final int STB_LOPROC = 13;
- public static final int STB_HIPROC = 15;
-
- public static final int STT_NOTYPE = 0;
- public static final int STT_OBJECT = 1;
- public static final int STT_FUNC = 2;
- public static final int STT_SECTION = 3;
- public static final int STT_FILE = 4;
- public static final int STT_COMMON = 5;
- public static final int STT_TLS = 6;
-
- public final String name;
- public final int bind;
- public final int type;
-
- Symbol(String name, int st_info) {
- this.name = name;
- this.bind = (st_info >> 4) & 0x0F;
- this.type = st_info & 0x0F;
- }
-
- @Override
- public String toString() {
- return "Symbol[" + name + "," + toBind() + "," + toType() + "]";
- }
-
- private String toBind() {
- switch (bind) {
- case STB_LOCAL:
- return "LOCAL";
- case STB_GLOBAL:
- return "GLOBAL";
- case STB_WEAK:
- return "WEAK";
- }
- return "STB_??? (" + bind + ")";
- }
-
- private String toType() {
- switch (type) {
- case STT_NOTYPE:
- return "NOTYPE";
- case STT_OBJECT:
- return "OBJECT";
- case STT_FUNC:
- return "FUNC";
- case STT_SECTION:
- return "SECTION";
- case STT_FILE:
- return "FILE";
- case STT_COMMON:
- return "COMMON";
- case STT_TLS:
- return "TLS";
- }
- return "STT_??? (" + type + ")";
- }
- }
-
- private final String mPath;
- private final RandomAccessFile mFile;
- private final byte[] mBuffer = new byte[512];
- private int mEndian;
- private boolean mIsDynamic;
- private boolean mIsPIE;
- private int mType;
- private int mAddrSize;
-
- /** Symbol Table offset */
- private long mSymTabOffset;
-
- /** Symbol Table size */
- private long mSymTabSize;
-
- /** Dynamic Symbol Table offset */
- private long mDynSymOffset;
-
- /** Dynamic Symbol Table size */
- private long mDynSymSize;
-
- /** Section Header String Table offset */
- private long mShStrTabOffset;
-
- /** Section Header String Table size */
- private long mShStrTabSize;
-
- /** String Table offset */
- private long mStrTabOffset;
-
- /** String Table size */
- private long mStrTabSize;
-
- /** Dynamic String Table offset */
- private long mDynStrOffset;
-
- /** Dynamic String Table size */
- private long mDynStrSize;
-
- /** Symbol Table symbol names */
- private Map<String, Symbol> mSymbols;
-
- /** Dynamic Symbol Table symbol names */
- private Map<String, Symbol> mDynamicSymbols;
-
- public static ReadElf read(File file) throws IOException {
- return new ReadElf(file);
- }
-
- public static void main(String[] args) throws IOException {
- for (String arg : args) {
- ReadElf re = new ReadElf(new File(arg));
- re.getSymbol("x");
- re.getDynamicSymbol("x");
- re.close();
- }
- }
-
- public boolean isDynamic() {
- return mIsDynamic;
- }
-
- public int getType() {
- return mType;
- }
-
- public boolean isPIE() {
- return mIsPIE;
- }
-
- private ReadElf(File file) throws IOException {
- mPath = file.getPath();
- mFile = new RandomAccessFile(file, "r");
-
- if (mFile.length() < EI_NIDENT) {
- throw new IllegalArgumentException("Too small to be an ELF file: " + file);
- }
-
- readHeader();
- }
-
- @Override
- public void close() {
- try {
- mFile.close();
- } catch (IOException ignored) {
- }
- }
-
- @Override
- protected void finalize() throws Throwable {
- try {
- close();
- } finally {
- super.finalize();
- }
- }
-
- private void readHeader() throws IOException {
- mFile.seek(0);
- mFile.readFully(mBuffer, 0, EI_NIDENT);
-
- if (mBuffer[0] != ELFMAG[0] || mBuffer[1] != ELFMAG[1] ||
- mBuffer[2] != ELFMAG[2] || mBuffer[3] != ELFMAG[3]) {
- throw new IllegalArgumentException("Invalid ELF file: " + mPath);
- }
-
- int elfClass = mBuffer[EI_CLASS];
- if (elfClass == ELFCLASS32) {
- mAddrSize = 4;
- } else if (elfClass == ELFCLASS64) {
- mAddrSize = 8;
- } else {
- throw new IOException("Invalid ELF EI_CLASS: " + elfClass + ": " + mPath);
- }
-
- mEndian = mBuffer[EI_DATA];
- if (mEndian == ELFDATA2LSB) {
- } else if (mEndian == ELFDATA2MSB) {
- throw new IOException("Unsupported ELFDATA2MSB file: " + mPath);
- } else {
- throw new IOException("Invalid ELF EI_DATA: " + mEndian + ": " + mPath);
- }
-
- mType = readHalf();
-
- int e_machine = readHalf();
- if (e_machine != EM_386 && e_machine != EM_X86_64 &&
- e_machine != EM_AARCH64 && e_machine != EM_ARM &&
- e_machine != EM_MIPS &&
- e_machine != EM_QDSP6) {
- throw new IOException("Invalid ELF e_machine: " + e_machine + ": " + mPath);
- }
-
- // AbiTest relies on us rejecting any unsupported combinations.
- if ((e_machine == EM_386 && elfClass != ELFCLASS32) ||
- (e_machine == EM_X86_64 && elfClass != ELFCLASS64) ||
- (e_machine == EM_AARCH64 && elfClass != ELFCLASS64) ||
- (e_machine == EM_ARM && elfClass != ELFCLASS32) ||
- (e_machine == EM_QDSP6 && elfClass != ELFCLASS32)) {
- throw new IOException("Invalid e_machine/EI_CLASS ELF combination: " +
- e_machine + "/" + elfClass + ": " + mPath);
- }
-
- long e_version = readWord();
- if (e_version != EV_CURRENT) {
- throw new IOException("Invalid e_version: " + e_version + ": " + mPath);
- }
-
- long e_entry = readAddr();
-
- long ph_off = readOff();
- long sh_off = readOff();
-
- long e_flags = readWord();
- int e_ehsize = readHalf();
- int e_phentsize = readHalf();
- int e_phnum = readHalf();
- int e_shentsize = readHalf();
- int e_shnum = readHalf();
- int e_shstrndx = readHalf();
-
- readSectionHeaders(sh_off, e_shnum, e_shentsize, e_shstrndx);
- readProgramHeaders(ph_off, e_phnum, e_phentsize);
- }
-
- private void readSectionHeaders(long sh_off, int e_shnum, int e_shentsize, int e_shstrndx)
- throws IOException {
- // Read the Section Header String Table offset first.
- {
- mFile.seek(sh_off + e_shstrndx * e_shentsize);
-
- long sh_name = readWord();
- long sh_type = readWord();
- long sh_flags = readX(mAddrSize);
- long sh_addr = readAddr();
- long sh_offset = readOff();
- long sh_size = readX(mAddrSize);
- // ...
-
- if (sh_type == SHT_STRTAB) {
- mShStrTabOffset = sh_offset;
- mShStrTabSize = sh_size;
- }
- }
-
- for (int i = 0; i < e_shnum; ++i) {
- // Don't bother to re-read the Section Header StrTab.
- if (i == e_shstrndx) {
- continue;
- }
-
- mFile.seek(sh_off + i * e_shentsize);
-
- long sh_name = readWord();
- long sh_type = readWord();
- long sh_flags = readX(mAddrSize);
- long sh_addr = readAddr();
- long sh_offset = readOff();
- long sh_size = readX(mAddrSize);
-
- if (sh_type == SHT_SYMTAB || sh_type == SHT_DYNSYM) {
- final String symTabName = readShStrTabEntry(sh_name);
- if (".symtab".equals(symTabName)) {
- mSymTabOffset = sh_offset;
- mSymTabSize = sh_size;
- } else if (".dynsym".equals(symTabName)) {
- mDynSymOffset = sh_offset;
- mDynSymSize = sh_size;
- }
- } else if (sh_type == SHT_STRTAB) {
- final String strTabName = readShStrTabEntry(sh_name);
- if (".strtab".equals(strTabName)) {
- mStrTabOffset = sh_offset;
- mStrTabSize = sh_size;
- } else if (".dynstr".equals(strTabName)) {
- mDynStrOffset = sh_offset;
- mDynStrSize = sh_size;
- }
- } else if (sh_type == SHT_DYNAMIC) {
- mIsDynamic = true;
- }
- }
- }
-
- private void readProgramHeaders(long ph_off, int e_phnum, int e_phentsize) throws IOException {
- for (int i = 0; i < e_phnum; ++i) {
- mFile.seek(ph_off + i * e_phentsize);
-
- long p_type = readWord();
- if (p_type == PT_LOAD) {
- if (mAddrSize == 8) {
- // Only in Elf64_phdr; in Elf32_phdr p_flags is at the end.
- long p_flags = readWord();
- }
- long p_offset = readOff();
- long p_vaddr = readAddr();
- // ...
-
- if (p_vaddr == 0) {
- mIsPIE = true;
- }
- }
- }
- }
-
- private HashMap<String, Symbol> readSymbolTable(long symStrOffset, long symStrSize,
- long tableOffset, long tableSize) throws IOException {
- HashMap<String, Symbol> result = new HashMap<String, Symbol>();
- mFile.seek(tableOffset);
- while (mFile.getFilePointer() < tableOffset + tableSize) {
- long st_name = readWord();
- int st_info;
- if (mAddrSize == 8) {
- st_info = readByte();
- int st_other = readByte();
- int st_shndx = readHalf();
- long st_value = readAddr();
- long st_size = readX(mAddrSize);
- } else {
- long st_value = readAddr();
- long st_size = readWord();
- st_info = readByte();
- int st_other = readByte();
- int st_shndx = readHalf();
- }
- if (st_name == 0) {
- continue;
- }
-
- final String symName = readStrTabEntry(symStrOffset, symStrSize, st_name);
- if (symName != null) {
- Symbol s = new Symbol(symName, st_info);
- result.put(symName, s);
- }
- }
- return result;
- }
-
- private String readShStrTabEntry(long strOffset) throws IOException {
- if (mShStrTabOffset == 0 || strOffset < 0 || strOffset >= mShStrTabSize) {
- return null;
- }
- return readString(mShStrTabOffset + strOffset);
- }
-
- private String readStrTabEntry(long tableOffset, long tableSize, long strOffset)
- throws IOException {
- if (tableOffset == 0 || strOffset < 0 || strOffset >= tableSize) {
- return null;
- }
- return readString(tableOffset + strOffset);
- }
-
- private int readHalf() throws IOException {
- return (int) readX(2);
- }
-
- private long readWord() throws IOException {
- return readX(4);
- }
-
- private long readOff() throws IOException {
- return readX(mAddrSize);
- }
-
- private long readAddr() throws IOException {
- return readX(mAddrSize);
- }
-
- private long readX(int byteCount) throws IOException {
- mFile.readFully(mBuffer, 0, byteCount);
-
- int answer = 0;
- if (mEndian == ELFDATA2LSB) {
- for (int i = byteCount - 1; i >= 0; i--) {
- answer = (answer << 8) | (mBuffer[i] & 0xff);
- }
- } else {
- final int N = byteCount - 1;
- for (int i = 0; i <= N; ++i) {
- answer = (answer << 8) | (mBuffer[i] & 0xff);
- }
- }
-
- return answer;
- }
-
- private String readString(long offset) throws IOException {
- long originalOffset = mFile.getFilePointer();
- mFile.seek(offset);
- mFile.readFully(mBuffer, 0, (int) Math.min(mBuffer.length, mFile.length() - offset));
- mFile.seek(originalOffset);
-
- for (int i = 0; i < mBuffer.length; ++i) {
- if (mBuffer[i] == 0) {
- return new String(mBuffer, 0, i);
- }
- }
-
- return null;
- }
-
- private int readByte() throws IOException {
- return mFile.read() & 0xff;
- }
-
- public Symbol getSymbol(String name) {
- if (mSymbols == null) {
- try {
- mSymbols = readSymbolTable(mStrTabOffset, mStrTabSize, mSymTabOffset, mSymTabSize);
- } catch (IOException e) {
- return null;
- }
- }
- return mSymbols.get(name);
- }
-
- public Symbol getDynamicSymbol(String name) {
- if (mDynamicSymbols == null) {
- try {
- mDynamicSymbols = readSymbolTable(
- mDynStrOffset, mDynStrSize, mDynSymOffset, mDynSymSize);
- } catch (IOException e) {
- return null;
- }
- }
- return mDynamicSymbols.get(name);
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/ReportLogDeviceInfoStore.java b/common/device-side/util/src/com/android/compatibility/common/util/ReportLogDeviceInfoStore.java
deleted file mode 100644
index 538881d..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/ReportLogDeviceInfoStore.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.util;
-
-import android.util.JsonWriter;
-
-import java.io.BufferedReader;
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileReader;
-import java.io.FileWriter;
-import java.io.IOException;
-
-public class ReportLogDeviceInfoStore extends DeviceInfoStore {
-
- private final String mStreamName;
- private File tempJsonFile;
-
- public ReportLogDeviceInfoStore(File jsonFile, String streamName) throws Exception {
- mJsonFile = jsonFile;
- mStreamName = streamName;
- }
-
- /**
- * Creates the writer and starts the JSON Object for the metric stream.
- */
- @Override
- public void open() throws IOException {
- // Write new metrics to a temp file to avoid invalid JSON files due to failed tests.
- BufferedWriter formatWriter;
- tempJsonFile = File.createTempFile(mStreamName, "-temp-report-log");
- formatWriter = new BufferedWriter(new FileWriter(tempJsonFile));
- if (mJsonFile.exists()) {
- BufferedReader jsonReader = new BufferedReader(new FileReader(mJsonFile));
- String currentLine;
- String nextLine = jsonReader.readLine();
- while ((currentLine = nextLine) != null) {
- nextLine = jsonReader.readLine();
- if (nextLine == null && currentLine.charAt(currentLine.length() - 1) == '}') {
- // Reopen overall JSON object to write new metrics.
- currentLine = currentLine.substring(0, currentLine.length() - 1) + ",";
- }
- // Copy to temp file directly to avoid large metrics string in memory.
- formatWriter.write(currentLine, 0, currentLine.length());
- }
- jsonReader.close();
- } else {
- formatWriter.write("{", 0 , 1);
- }
- // Start new JSON object for new metrics.
- formatWriter.write("\"" + mStreamName + "\":", 0, mStreamName.length() + 3);
- formatWriter.flush();
- formatWriter.close();
- mJsonWriter = new JsonWriter(new FileWriter(tempJsonFile, true));
- mJsonWriter.beginObject();
- }
-
- /**
- * Closes the writer.
- */
- @Override
- public void close() throws IOException {
- // Close JSON Writer.
- mJsonWriter.endObject();
- mJsonWriter.close();
- // Close overall JSON Object.
- try (BufferedWriter formatWriter = new BufferedWriter(new FileWriter(tempJsonFile, true))) {
- formatWriter.write("}", 0, 1);
- }
- // Copy metrics from temp file and delete temp file.
- mJsonFile.createNewFile();
- try (
- BufferedReader jsonReader = new BufferedReader(new FileReader(tempJsonFile));
- BufferedWriter metricsWriter = new BufferedWriter(new FileWriter(mJsonFile))
- ) {
- String line;
- while ((line = jsonReader.readLine()) != null) {
- // Copy from temp file directly to avoid large metrics string in memory.
- metricsWriter.write(line, 0, line.length());
- }
- }
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/RequiredFeatureRule.java b/common/device-side/util/src/com/android/compatibility/common/util/RequiredFeatureRule.java
deleted file mode 100644
index 0968ddc..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/RequiredFeatureRule.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import android.support.test.InstrumentationRegistry;
-import android.util.Log;
-
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-
-/**
- * Custom JUnit4 rule that does not run a test case if the device does not have a given feature.
- */
-public class RequiredFeatureRule implements TestRule {
- private static final String TAG = "RequiredFeatureRule";
-
- private final String mFeature;
- private final boolean mHasFeature;
-
- public RequiredFeatureRule(String feature) {
- mFeature = feature;
- mHasFeature = hasFeature(feature);
- }
-
- @Override
- public Statement apply(Statement base, Description description) {
- return new Statement() {
-
- @Override
- public void evaluate() throws Throwable {
- if (!mHasFeature) {
- Log.d(TAG, "skipping "
- + description.getClassName() + "#" + description.getMethodName()
- + " because device does not have feature '" + mFeature + "'");
- return;
- }
- base.evaluate();
- }
- };
- }
-
- public static boolean hasFeature(String feature) {
- return InstrumentationRegistry.getContext().getPackageManager().hasSystemFeature(feature);
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/RequiredServiceRule.java b/common/device-side/util/src/com/android/compatibility/common/util/RequiredServiceRule.java
deleted file mode 100644
index a4359fd..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/RequiredServiceRule.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import android.support.test.InstrumentationRegistry;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-
-/**
- * Custom JUnit4 rule that does not run a test case if the device does not have a given service.
- */
-public class RequiredServiceRule implements TestRule {
- private static final String TAG = "RequiredServiceRule";
-
- private final String mService;
- private final boolean mHasService;
-
- /**
- * Creates a rule for the given service.
- */
- public RequiredServiceRule(@NonNull String service) {
- mService = service;
- mHasService = hasService(service);
- }
-
- @Override
- public Statement apply(@NonNull Statement base, @NonNull Description description) {
- return new Statement() {
-
- @Override
- public void evaluate() throws Throwable {
- if (!mHasService) {
- Log.d(TAG, "skipping "
- + description.getClassName() + "#" + description.getMethodName()
- + " because device does not have service '" + mService + "'");
- return;
- }
- base.evaluate();
- }
- };
- }
-
- /**
- * Checks if the device has the given service.
- */
- public static boolean hasService(@NonNull String service) {
- // TODO: ideally should call SystemServiceManager directly, but we would need to open
- // some @Testing APIs for that.
- String command = "service check " + service;
- try {
- String commandOutput = SystemUtil.runShellCommand(
- InstrumentationRegistry.getInstrumentation(), command);
- return !commandOutput.contains("not found");
- } catch (Exception e) {
- Log.w(TAG, "Exception running '" + command + "': " + e);
- return false;
- }
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/RequiredSystemResourceRule.java b/common/device-side/util/src/com/android/compatibility/common/util/RequiredSystemResourceRule.java
deleted file mode 100644
index ead59943..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/RequiredSystemResourceRule.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import android.content.res.Resources;
-import android.text.TextUtils;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-
-/**
- * Custom JUnit4 rule that does not run a test case if the device does not define the given system
- * resource.
- */
-public class RequiredSystemResourceRule implements TestRule {
-
- private static final String TAG = "RequiredSystemResourceRule";
-
- @NonNull private final String mName;
- private final boolean mHasResource;
-
- /**
- * Creates a rule for the given system resource.
- *
- * @param resourceId resource per se
- * @param name resource name used for debugging purposes
- */
- public RequiredSystemResourceRule(@NonNull String name) {
- mName = name;
- mHasResource = !TextUtils.isEmpty(getSystemResource(name));
- }
-
- @Override
- public Statement apply(Statement base, Description description) {
- return new Statement() {
-
- @Override
- public void evaluate() throws Throwable {
- if (!mHasResource) {
- Log.d(TAG, "skipping "
- + description.getClassName() + "#" + description.getMethodName()
- + " because device does not have system resource '" + mName + "'");
- return;
- }
- base.evaluate();
- }
- };
- }
-
- /**
- * Gets the given system resource.
- */
- @Nullable
- public static String getSystemResource(@NonNull String name) {
- try {
- final int resourceId = Resources.getSystem().getIdentifier(name, "string", "android");
- return Resources.getSystem().getString(resourceId);
- } catch (Exception e) {
- Log.e(TAG, "could not get value of resource '" + name + "': ", e);
- }
- return null;
- }
-
- @Override
- public String toString() {
- return "RequiredSystemResourceRule[" + mName + "]";
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/Result.java b/common/device-side/util/src/com/android/compatibility/common/util/Result.java
deleted file mode 100644
index 0d8a29a..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/Result.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.android.compatibility.common.util;
-
-/**
- * Represents the result of a test.
- */
-public interface Result {
- public static final int RESULT_OK = 1;
- public static final int RESULT_FAIL = 2;
- /**
- * Sets the test result of this object.
- *
- * @param resultCode The test result, either {@code RESULT_OK} or {@code RESULT_FAIL}.
- */
- void setResult(int resultCode);
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/RetryRule.java b/common/device-side/util/src/com/android/compatibility/common/util/RetryRule.java
deleted file mode 100644
index 32dedea..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/RetryRule.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import android.util.Log;
-
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-
-/**
- * Custom JUnit4 rule that retry tests when they fail due to a {@link RetryableException}.
- */
-public class RetryRule implements TestRule {
-
- private static final String TAG = "RetryRule";
- private final int mMaxAttempts;
-
- /**
- * Retries the underlying test when it catches a {@link RetryableException}.
- *
- * @param retries number of retries. Use {@code 0} to disable rule.
- *
- * @throws IllegalArgumentException if {@code retries} is less than {@code 0}.
- */
- public RetryRule(int retries) {
- if (retries < 0) {
- throw new IllegalArgumentException("retries must be more than 0");
- }
- mMaxAttempts = retries + 1;
- }
-
- @Override
- public Statement apply(Statement base, Description description) {
- return new Statement() {
-
- @Override
- public void evaluate() throws Throwable {
- if (mMaxAttempts <= 1) {
- Log.v(TAG, "Executing " + description.getDisplayName()
- + " right away because mMaxAttempts is " + mMaxAttempts);
- base.evaluate();
- return;
- }
-
- final String name = description.getDisplayName();
- Throwable caught = null;
- for (int i = 1; i <= mMaxAttempts; i++) {
- try {
- base.evaluate();
- if (i == 1) {
- Log.v(TAG, "Good News, Everyone! " + name + " passed right away");
- } else {
- Log.d(TAG,
- "Better late than never: " + name + " passed at attempt #" + i);
- }
- return;
- } catch (RetryableException e) {
- final Timeout timeout = e.getTimeout();
- if (timeout != null) {
- long before = timeout.ms();
- timeout.increase();
- Log.d(TAG, "Increased " + timeout.getName() + " from " + before + "ms"
- + " to " + timeout.ms() + "ms");
- }
- caught = e;
- }
- Log.w(TAG, "Arrrr! " + name + " failed at attempt " + i + "/" + mMaxAttempts
- + ": " + caught);
- }
- Log.e(TAG, "D'OH! " + name + ": giving up after " + mMaxAttempts + " attempts");
- throw caught;
- }
- };
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/RetryableException.java b/common/device-side/util/src/com/android/compatibility/common/util/RetryableException.java
deleted file mode 100644
index 1c6c782..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/RetryableException.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import androidx.annotation.Nullable;
-
-/**
- * Exception that cause the {@link RetryRule} to re-try a test.
- */
-public class RetryableException extends RuntimeException {
-
- @Nullable
- private final Timeout mTimeout;
-
- public RetryableException(String msg) {
- this((Timeout) null, msg);
- }
-
- public RetryableException(String format, Object...args) {
- this((Timeout) null, String.format(format, args));
- }
-
- public RetryableException(Throwable cause, String format, Object...args) {
- this((Timeout) null, cause, String.format(format, args), cause);
- }
-
- public RetryableException(@Nullable Timeout timeout, String msg) {
- super(msg);
- this.mTimeout = timeout;
- }
-
- public RetryableException(@Nullable Timeout timeout, String format, Object...args) {
- super(String.format(format, args));
- this.mTimeout = timeout;
- }
-
- public RetryableException(@Nullable Timeout timeout, Throwable cause, String format,
- Object...args) {
- super(String.format(format, args), cause);
- this.mTimeout = timeout;
- }
-
- @Nullable
- public Timeout getTimeout() {
- return mTimeout;
- }
-
- @Override
- public String getMessage() {
- final String superMessage = super.getMessage();
- return mTimeout == null ? superMessage : superMessage + " (timeout=" + mTimeout + ")";
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/SafeCleanerRule.java b/common/device-side/util/src/com/android/compatibility/common/util/SafeCleanerRule.java
deleted file mode 100644
index 806884c..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/SafeCleanerRule.java
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-
-import org.junit.AssumptionViolatedException;
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.Callable;
-
-/**
- * Rule used to safely run clean up code after a test is finished, so that exceptions thrown by
- * the cleanup code don't hide exception thrown by the test body
- */
-public final class SafeCleanerRule implements TestRule {
-
- private static final String TAG = "SafeCleanerRule";
-
- private final List<ThrowingRunnable> mCleaners = new ArrayList<>();
- private final List<Callable<List<Throwable>>> mExtraThrowables = new ArrayList<>();
- private final List<Throwable> mThrowables = new ArrayList<>();
- private Dumper mDumper;
-
- /**
- * Runs {@code cleaner} after the test is finished, catching any {@link Throwable} thrown by it.
- */
- public SafeCleanerRule run(@NonNull ThrowingRunnable cleaner) {
- mCleaners.add(cleaner);
- return this;
- }
-
- /**
- * Adds exceptions directly.
- *
- * <p>Typically used for exceptions caught asychronously during the test execution.
- */
- public SafeCleanerRule add(@NonNull Callable<List<Throwable>> exceptions) {
- mExtraThrowables.add(exceptions);
- return this;
- }
-
- /**
- * Adds exceptions directly.
- *
- * <p>Typically used for exceptions caught during {@code finally} blocks.
- */
- public SafeCleanerRule add(Throwable exception) {
- Log.w(TAG, "Adding exception directly: " + exception);
- mThrowables.add(exception);
- return this;
- }
-
- /**
- * Sets a {@link Dumper} used to log errors.
- */
- public SafeCleanerRule setDumper(@NonNull Dumper dumper) {
- mDumper = dumper;
- return this;
- }
-
- @Override
- public Statement apply(Statement base, Description description) {
- return new Statement() {
- @Override
- public void evaluate() throws Throwable {
- // First run the test
- try {
- base.evaluate();
- } catch (Throwable t) {
- Log.w(TAG, "Adding exception from main test at index 0: " + t);
- mThrowables.add(0, t);
- }
-
- // Then the cleanup runners
- for (ThrowingRunnable runner : mCleaners) {
- try {
- runner.run();
- } catch (Throwable t) {
- Log.w(TAG, "Adding exception from cleaner");
- mThrowables.add(t);
- }
- }
-
- // And finally add the extra exceptions
- for (Callable<List<Throwable>> extraThrowablesCallable : mExtraThrowables) {
- final List<Throwable> extraThrowables = extraThrowablesCallable.call();
- if (extraThrowables != null && !extraThrowables.isEmpty()) {
- Log.w(TAG, "Adding " + extraThrowables.size() + " extra exceptions");
- mThrowables.addAll(extraThrowables);
- }
- }
-
- // Ignore all instances of AssumptionViolatedExceptions
- mThrowables.removeIf(t -> t instanceof AssumptionViolatedException);
-
- // Finally, throw up!
- if (mThrowables.isEmpty()) return;
-
- final int numberExceptions = mThrowables.size();
- if (numberExceptions == 1) {
- fail(description, mThrowables.get(0));
- }
- fail(description, new MultipleExceptions(mThrowables));
- }
-
- };
- }
-
- private void fail(Description description, Throwable t) throws Throwable {
- if (mDumper != null) {
- mDumper.dump(description.getDisplayName(), t);
- }
- throw t;
- }
-
- private static String toMesssage(List<Throwable> throwables) {
- String msg = "D'OH!";
- try {
- try (StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw)) {
- sw.write("Caught " + throwables.size() + " exceptions\n");
- for (int i = 0; i < throwables.size(); i++) {
- sw.write("\n---- Begin of exception #" + (i + 1) + " ----\n");
- final Throwable exception = throwables.get(i);
- exception.printStackTrace(pw);
- sw.write("---- End of exception #" + (i + 1) + " ----\n\n");
- }
- msg = sw.toString();
- }
- } catch (IOException e) {
- // ignore close() errors - should not happen...
- Log.e(TAG, "Exception closing StringWriter: " + e);
- }
- return msg;
- }
-
- // VisibleForTesting
- static class MultipleExceptions extends AssertionError {
- private final List<Throwable> mThrowables;
-
- private MultipleExceptions(List<Throwable> throwables) {
- super(toMesssage(throwables));
-
- this.mThrowables = throwables;
- }
-
- List<Throwable> getThrowables() {
- return mThrowables;
- }
- }
-
- /**
- * Optional interface used to dump an error.
- */
- public interface Dumper {
-
- /**
- * Dumps an error.
- */
- void dump(@NonNull String testName, @NonNull Throwable t);
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/SettingsStateChangerRule.java b/common/device-side/util/src/com/android/compatibility/common/util/SettingsStateChangerRule.java
deleted file mode 100644
index 3e0662a..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/SettingsStateChangerRule.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.util;
-
-import android.content.Context;
-import android.provider.Settings;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-/**
- * JUnit rule used to set a {@link Settings} preference before the test is run.
- *
- * <p>It stores the current value before the test, changes it (if necessary), then restores it after
- * the test (if necessary).
- */
-public class SettingsStateChangerRule extends StateChangerRule<String> {
-
- /**
- * Default constructor for the 'secure' context.
- *
- * @param context context used to retrieve the {@link Settings} provider.
- * @param key prefence key.
- * @param value value to be set before the test is run.
- */
- public SettingsStateChangerRule(@NonNull Context context, @NonNull String key,
- @Nullable String value) {
- this(context, SettingsUtils.NAMESPACE_SECURE, key, value);
- }
-
- /**
- * Default constructor.
- *
- * @param context context used to retrieve the {@link Settings} provider.
- * @param namespace settings namespace.
- * @param key prefence key.
- * @param value value to be set before the test is run.
- */
- public SettingsStateChangerRule(@NonNull Context context, @NonNull String namespace,
- @NonNull String key, @Nullable String value) {
- super(new SettingsStateManager(context, namespace, key), value);
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/SettingsStateKeeperRule.java b/common/device-side/util/src/com/android/compatibility/common/util/SettingsStateKeeperRule.java
deleted file mode 100644
index 18ca88a..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/SettingsStateKeeperRule.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.util;
-
-import android.content.Context;
-import android.provider.Settings;
-
-import androidx.annotation.NonNull;
-
-/**
- * JUnit rule used to restore a {@link Settings} preference after the test is run.
- *
- * <p>It stores the current value before the test, and restores it after the test (if necessary).
- */
-public class SettingsStateKeeperRule extends StateKeeperRule<String> {
-
- /**
- * Default constructor.
- *
- * @param context context used to retrieve the {@link Settings} provider.
- * @param key prefence key.
- */
- public SettingsStateKeeperRule(@NonNull Context context, @NonNull String key) {
- super(new SettingsStateManager(context, SettingsUtils.NAMESPACE_SECURE, key));
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/SettingsStateManager.java b/common/device-side/util/src/com/android/compatibility/common/util/SettingsStateManager.java
deleted file mode 100644
index bab06a6..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/SettingsStateManager.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import android.content.Context;
-import android.provider.Settings;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.google.common.base.Preconditions;
-
-/**
- * Manages the state of a preference backed by {@link Settings}.
- */
-public class SettingsStateManager implements StateManager<String> {
-
- private final Context mContext;
- private final String mNamespace;
- private final String mKey;
-
- /**
- * Default constructor.
- *
- * @param context context used to retrieve the {@link Settings} provider.
- * @param namespace settings namespace.
- * @param key prefence key.
- */
- public SettingsStateManager(@NonNull Context context, @NonNull String namespace,
- @NonNull String key) {
- mContext = Preconditions.checkNotNull(context);
- mNamespace = Preconditions.checkNotNull(namespace);
- mKey = Preconditions.checkNotNull(key);
- }
-
- @Override
- public void set(@Nullable String value) {
- SettingsUtils.syncSet(mContext, mNamespace, mKey, value);
- }
-
- @Override
- @Nullable
- public String get() {
- return SettingsUtils.get(mNamespace, mKey);
- }
-
- @Override
- public String toString() {
- return "SettingsStateManager[namespace=" + mNamespace + ", key=" + mKey + "]";
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/SettingsUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/SettingsUtils.java
deleted file mode 100644
index c13de45..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/SettingsUtils.java
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
-
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import android.content.Context;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-/**
- * Provides utilities to interact with the device's {@link Settings}.
- */
-public final class SettingsUtils {
-
- private static final String TAG = SettingsUtils.class.getSimpleName();
-
- public static final String NAMESPACE_SECURE = "secure";
- public static final String NAMESPACE_GLOBAL = "global";
-
- // TODO(b/123885378): we cannot pass an empty value when using 'cmd settings', so we need
- // to remove the property instead. Once we use the Settings API directly, we can remove this
- // constant and all if() statements that ues it
- static final boolean TMP_HACK_REMOVE_EMPTY_PROPERTIES = true;
-
- /**
- * Uses a Shell command to set the given preference.
- */
- public static void set(@NonNull String namespace, @NonNull String key, @Nullable String value) {
- if (value == null) {
- delete(namespace, key);
- return;
- }
- if (TMP_HACK_REMOVE_EMPTY_PROPERTIES && TextUtils.isEmpty(value)) {
- Log.w(TAG, "Value of " + namespace + ":" + key + " is empty; deleting it instead");
- delete(namespace, key);
- return;
- }
- runShellCommand("settings put %s %s %s default", namespace, key, value);
- }
-
- /**
- * Sets a preference in the {@link #NAMESPACE_SECURE} namespace.
- */
- public static void set(@NonNull String key, @Nullable String value) {
- set(NAMESPACE_SECURE, key, value);
- }
-
- /**
- * Uses a Shell command to set the given preference, and verifies it was correctly set.
- */
- public static void syncSet(@NonNull Context context, @NonNull String namespace,
- @NonNull String key, @Nullable String value) {
- if (value == null) {
- syncDelete(context, namespace, key);
- return;
- }
-
- final String currentValue = get(namespace, key);
- if (value.equals(currentValue)) {
- // Already set, ignore
- return;
- }
-
- final OneTimeSettingsListener observer =
- new OneTimeSettingsListener(context, namespace, key);
- set(namespace, key, value);
- observer.assertCalled();
-
- final String newValue = get(namespace, key);
- if (TMP_HACK_REMOVE_EMPTY_PROPERTIES && TextUtils.isEmpty(value)) {
- assertWithMessage("invalid value for '%s' settings", key).that(newValue)
- .isNull();
- } else {
- assertWithMessage("invalid value for '%s' settings", key).that(newValue)
- .isEqualTo(value);
- }
- }
-
- /**
- * Sets a preference in the {@link #NAMESPACE_SECURE} namespace, using a Settings listener to
- * block until it's set.
- */
- public static void syncSet(@NonNull Context context, @NonNull String key,
- @Nullable String value) {
- syncSet(context, NAMESPACE_SECURE, key, value);
- }
-
- /**
- * Uses a Shell command to delete the given preference.
- */
- public static void delete(@NonNull String namespace, @NonNull String key) {
- runShellCommand("settings delete %s %s", namespace, key);
- }
-
- /**
- * Deletes a preference in the {@link #NAMESPACE_SECURE} namespace.
- */
- public static void delete(@NonNull String key) {
- delete(NAMESPACE_SECURE, key);
- }
-
- /**
- * Uses a Shell command to delete the given preference, and verifies it was correctly deleted.
- */
- public static void syncDelete(@NonNull Context context, @NonNull String namespace,
- @NonNull String key) {
-
- final String currentValue = get(namespace, key);
- if (currentValue == null) {
- // Already set, ignore
- return;
- }
-
- final OneTimeSettingsListener observer = new OneTimeSettingsListener(context, namespace,
- key);
- delete(namespace, key);
- observer.assertCalled();
-
- final String newValue = get(namespace, key);
- assertWithMessage("invalid value for '%s' settings", key).that(newValue).isNull();
- }
-
- /**
- * Deletes a preference in the {@link #NAMESPACE_SECURE} namespace, using a Settings listener to
- * block until it's deleted.
- */
- public static void syncDelete(@NonNull Context context, @NonNull String key) {
- syncDelete(context, NAMESPACE_SECURE, key);
- }
-
- /**
- * Gets the value of a given preference using Shell command.
- */
- @Nullable
- public static String get(@NonNull String namespace, @NonNull String key) {
- final String value = runShellCommand("settings get %s %s", namespace, key);
- if (value == null || value.equals("null")) {
- return null;
- } else {
- return value;
- }
- }
-
- /**
- * Gets the value of a preference in the {@link #NAMESPACE_SECURE} namespace.
- */
- @NonNull
- public static String get(@NonNull String key) {
- return get(NAMESPACE_SECURE, key);
- }
-
- private SettingsUtils() {
- throw new UnsupportedOperationException("contain static methods only");
- }
-
- /**
- * @deprecated - use {@link #set(String, String, String)} with {@link #NAMESPACE_GLOBAL}
- */
- @Deprecated
- public static void putGlobalSetting(String key, String value) {
- set(SettingsUtils.NAMESPACE_GLOBAL, key, value);
-
- }
-
- /**
- * @deprecated - use {@link #set(String, String, String)} with {@link #NAMESPACE_GLOBAL}
- */
- @Deprecated
- public static void putSecureSetting(String key, String value) {
- set(SettingsUtils.NAMESPACE_SECURE, key, value);
-
- }
-
- /**
- * Get a global setting for the current (foreground) user. Trims ending new line.
- */
- public static String getSecureSetting(String key) {
- return SystemUtil.runShellCommand("settings --user current get secure " + key).trim();
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/ShellIdentityUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/ShellIdentityUtils.java
deleted file mode 100644
index f3438ee..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/ShellIdentityUtils.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import android.app.UiAutomation;
-import android.support.test.InstrumentationRegistry;
-
-/**
- * Provides utility methods to invoke system and privileged APIs as the shell user.
- */
-public class ShellIdentityUtils {
-
- /**
- * Utility interface to invoke a method against the target object.
- *
- * @param <T> the type returned by the invoked method.
- * @param <U> the type of the object against which the method is invoked.
- */
- public interface ShellPermissionMethodHelper<T, U> {
- /**
- * Invokes the method against the target object.
- *
- * @param targetObject the object against which the method should be invoked.
- * @return the result of the invoked method.
- */
- T callMethod(U targetObject);
- }
-
- /**
- * Utility interface to invoke a method against the target object.
- *
- * @param <U> the type of the object against which the method is invoked.
- */
- public interface ShellPermissionMethodHelperNoReturn<U> {
- /**
- * Invokes the method against the target object.
- *
- * @param targetObject the object against which the method should be invoked.
- */
- void callMethod(U targetObject);
- }
-
- /**
- * Invokes the specified method on the targetObject as the shell user. The method can be invoked
- * as follows:
- *
- * {@code ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
- * (tm) -> tm.getDeviceId());}
- */
- public static <T, U> T invokeMethodWithShellPermissions(U targetObject,
- ShellPermissionMethodHelper<T, U> methodHelper) {
- final UiAutomation uiAutomation =
- InstrumentationRegistry.getInstrumentation().getUiAutomation();
- try {
- uiAutomation.adoptShellPermissionIdentity();
- return methodHelper.callMethod(targetObject);
- } finally {
- uiAutomation.dropShellPermissionIdentity();
- }
- }
-
- /**
- * Invokes the specified method on the targetObject as the shell user. The method can be invoked
- * as follows:
- *
- * {@code ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
- * (tm) -> tm.getDeviceId());}
- */
- public static <U> void invokeMethodWithShellPermissionsNoReturn(
- U targetObject, ShellPermissionMethodHelperNoReturn<U> methodHelper) {
- final UiAutomation uiAutomation =
- InstrumentationRegistry.getInstrumentation().getUiAutomation();
- try {
- uiAutomation.adoptShellPermissionIdentity();
- methodHelper.callMethod(targetObject);
- } finally {
- uiAutomation.dropShellPermissionIdentity();
- }
- }
-
- /**
- * Utility interface to invoke a static method.
- *
- * @param <T> the type returned by the invoked method.
- */
- public interface StaticShellPermissionMethodHelper<T> {
- /**
- * Invokes the static method.
- *
- * @return the result of the invoked method.
- */
- T callMethod();
- }
-
- /**
- * Invokes the specified static method as the shell user. This method can be invoked as follows:
- *
- * {@code ShellIdentityUtils.invokeStaticMethodWithShellPermissions(Build::getSerial));}
- */
- public static <T> T invokeStaticMethodWithShellPermissions(
- StaticShellPermissionMethodHelper<T> methodHelper) {
- final UiAutomation uiAutomation =
- InstrumentationRegistry.getInstrumentation().getUiAutomation();
- try {
- uiAutomation.adoptShellPermissionIdentity();
- return methodHelper.callMethod();
- } finally {
- uiAutomation.dropShellPermissionIdentity();
- }
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/ShellUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/ShellUtils.java
deleted file mode 100644
index 8cb3290..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/ShellUtils.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
-
-import android.support.test.InstrumentationRegistry;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.View;
-
-import androidx.annotation.NonNull;
-
-/**
- * Provides Shell-based utilities such as running a command.
- */
-public final class ShellUtils {
-
- private static final String TAG = "ShellHelper";
-
- /**
- * Runs a Shell command, returning a trimmed response.
- */
- @NonNull
- public static String runShellCommand(@NonNull String template, Object...args) {
- final String command = String.format(template, args);
- Log.d(TAG, "runShellCommand(): " + command);
- try {
- final String result = SystemUtil
- .runShellCommand(InstrumentationRegistry.getInstrumentation(), command);
- return TextUtils.isEmpty(result) ? "" : result.trim();
- } catch (Exception e) {
- throw new RuntimeException("Command '" + command + "' failed: ", e);
- }
- }
-
- /**
- * Tap on the view center, it may change window focus.
- */
- public static void tap(View view) {
- final int[] xy = new int[2];
- view.getLocationOnScreen(xy);
- final int viewWidth = view.getWidth();
- final int viewHeight = view.getHeight();
- final int x = (int) (xy[0] + (viewWidth / 2.0f));
- final int y = (int) (xy[1] + (viewHeight / 2.0f));
-
- runShellCommand("input touchscreen tap %d %d", x, y);
- }
-
-
- private ShellUtils() {
- throw new UnsupportedOperationException("contain static methods only");
- }
-
- /**
- * Simulates input of key event.
- *
- * @param keyCode key event to fire.
- */
- public static void sendKeyEvent(String keyCode) {
- runShellCommand("input keyevent " + keyCode);
- }
-
- /**
- * Allows an app to draw overlaid windows.
- */
- public static void setOverlayPermissions(@NonNull String packageName, boolean allowed) {
- final String action = allowed ? "allow" : "ignore";
- runShellCommand("appops set %s SYSTEM_ALERT_WINDOW %s", packageName, action);
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/StateChangerRule.java b/common/device-side/util/src/com/android/compatibility/common/util/StateChangerRule.java
deleted file mode 100644
index 4e59f13..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/StateChangerRule.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.google.common.base.Preconditions;
-
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-
-import java.util.Objects;
-
-/**
- * JUnit rule used to prepare a state before the test is run.
- *
- * <p>It stores the current state before the test, changes it (if necessary), then restores it after
- * the test (if necessary).
- *
- * @param <T> value type
- */
-public class StateChangerRule<T> implements TestRule {
-
- private final StateManager<T> mStateManager;
- private final T mValue;
-
- /**
- * Default constructor.
- *
- * @param stateManager abstraction used to mange the state.
- * @param value value to be set before the test is run.
- */
- public StateChangerRule(@NonNull StateManager<T> stateManager, @Nullable T value) {
- mStateManager = Preconditions.checkNotNull(stateManager);
- mValue = value;
- }
-
- @Override
- public Statement apply(Statement base, Description description) {
- return new Statement() {
-
- @Override
- public void evaluate() throws Throwable {
- final T previousValue = mStateManager.get();
- if (!Objects.equals(previousValue, mValue)) {
- mStateManager.set(mValue);
- }
- try {
- base.evaluate();
- } finally {
- final T currentValue = mStateManager.get();
- if (!Objects.equals(currentValue, previousValue)) {
- mStateManager.set(previousValue);
- // TODO: if set() thowns a RuntimeException, JUnit will silently catch it
- // and re-run the test case; it should fail instead.
- }
- }
- }
- };
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/StateKeeperRule.java b/common/device-side/util/src/com/android/compatibility/common/util/StateKeeperRule.java
deleted file mode 100644
index ecc02a2..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/StateKeeperRule.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import androidx.annotation.NonNull;
-
-import com.google.common.base.Preconditions;
-
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-
-import java.util.Objects;
-
-/**
- * JUnit rule used to restore a state after the test is run.
- *
- * <p>It stores the current state before the test, and restores it after the test (if necessary).
- *
- * @param <T> value type
- */
-public class StateKeeperRule<T> implements TestRule {
-
- private final StateManager<T> mStateManager;
-
- /**
- * Default constructor.
- *
- * @param stateManager abstraction used to mange the state.
- */
- public StateKeeperRule(@NonNull StateManager<T> stateManager) {
- mStateManager = Preconditions.checkNotNull(stateManager);
- }
-
- /**
- * Hook for subclasses.
- */
- protected void preEvaluate(@SuppressWarnings("unused") Description description) {
- }
-
- /**
- * Hook for subclasses.
- */
- protected void postEvaluate(@SuppressWarnings("unused") Description description) {
- }
-
- @Override
- public Statement apply(Statement base, Description description) {
- return new Statement() {
-
- @Override
- public void evaluate() throws Throwable {
- final T previousValue = mStateManager.get();
- preEvaluate(description);
- try {
- base.evaluate();
- } finally {
- final T currentValue = mStateManager.get();
- if (!Objects.equals(previousValue, currentValue)) {
- mStateManager.set(previousValue);
- }
- }
- postEvaluate(description);
- }
- };
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/StateManager.java b/common/device-side/util/src/com/android/compatibility/common/util/StateManager.java
deleted file mode 100644
index 2077e08..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/StateManager.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.util;
-
-import androidx.annotation.Nullable;
-
-/**
- * Abstraction for a state that is managed somewhere, like Android Settings.
- *
- * @param <T> value type
- */
-public interface StateManager<T> {
-
- /**
- * Sets a new state.
- */
- void set(@Nullable T value);
-
- /**
- * Gets the current state.
- */
- @Nullable T get();
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/SystemUtil.java b/common/device-side/util/src/com/android/compatibility/common/util/SystemUtil.java
deleted file mode 100644
index fa7f046..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/SystemUtil.java
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import android.app.ActivityManager;
-import android.app.ActivityManager.MemoryInfo;
-import android.app.Instrumentation;
-import android.app.UiAutomation;
-import android.content.Context;
-import android.os.ParcelFileDescriptor;
-import android.os.StatFs;
-import android.support.test.InstrumentationRegistry;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.util.concurrent.Callable;
-import java.util.function.Predicate;
-
-public class SystemUtil {
- private static final String TAG = "CtsSystemUtil";
-
- public static long getFreeDiskSize(Context context) {
- final StatFs statFs = new StatFs(context.getFilesDir().getAbsolutePath());
- return (long)statFs.getAvailableBlocks() * statFs.getBlockSize();
- }
-
- public static long getFreeMemory(Context context) {
- final MemoryInfo info = new MemoryInfo();
- ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryInfo(info);
- return info.availMem;
- }
-
- public static long getTotalMemory(Context context) {
- final MemoryInfo info = new MemoryInfo();
- ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryInfo(info);
- return info.totalMem;
- }
-
- /**
- * Executes a shell command using shell user identity, and return the standard output in string
- * <p>Note: calling this function requires API level 21 or above
- * @param instrumentation {@link Instrumentation} instance, obtained from a test running in
- * instrumentation framework
- * @param cmd the command to run
- * @return the standard output of the command
- * @throws Exception
- */
- public static String runShellCommand(Instrumentation instrumentation, String cmd)
- throws IOException {
- Log.v(TAG, "Running command: " + cmd);
- if (cmd.startsWith("pm grant ") || cmd.startsWith("pm revoke ")) {
- throw new UnsupportedOperationException("Use UiAutomation.grantRuntimePermission() "
- + "or revokeRuntimePermission() directly, which are more robust.");
- }
- ParcelFileDescriptor pfd = instrumentation.getUiAutomation().executeShellCommand(cmd);
- byte[] buf = new byte[512];
- int bytesRead;
- FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
- StringBuffer stdout = new StringBuffer();
- while ((bytesRead = fis.read(buf)) != -1) {
- stdout.append(new String(buf, 0, bytesRead));
- }
- fis.close();
- return stdout.toString();
- }
-
- /**
- * Simpler version of {@link #runShellCommand(Instrumentation, String)}.
- */
- public static String runShellCommand(String cmd) {
- try {
- return runShellCommand(InstrumentationRegistry.getInstrumentation(), cmd);
- } catch (IOException e) {
- fail("Failed reading command output: " + e);
- return "";
- }
- }
-
- /**
- * Same as {@link #runShellCommand(String)}, with optionally
- * check the result using {@code resultChecker}.
- */
- public static String runShellCommand(String cmd, Predicate<String> resultChecker) {
- final String result = runShellCommand(cmd);
- if (resultChecker != null) {
- assertTrue("Assertion failed. Command was: " + cmd + "\n"
- + "Output was:\n" + result,
- resultChecker.test(result));
- }
- return result;
- }
-
- /**
- * Same as {@link #runShellCommand(String)}, but fails if the output is not empty.
- */
- public static String runShellCommandForNoOutput(String cmd) {
- final String result = runShellCommand(cmd);
- assertTrue("Command failed. Command was: " + cmd + "\n"
- + "Didn't expect any output, but the output was:\n" + result,
- result.length() == 0);
- return result;
- }
-
- /**
- * Runs a command and print the result on logcat.
- */
- public static void runCommandAndPrintOnLogcat(String logtag, String cmd) {
- Log.i(logtag, "Executing: " + cmd);
- final String output = runShellCommand(cmd);
- for (String line : output.split("\\n", -1)) {
- Log.i(logtag, line);
- }
- }
-
- /**
- * Runs a command and return the section matching the patterns.
- *
- * @see TextUtils#extractSection
- */
- public static String runCommandAndExtractSection(String cmd,
- String extractionStartRegex, boolean startInclusive,
- String extractionEndRegex, boolean endInclusive) {
- return TextUtils.extractSection(runShellCommand(cmd), extractionStartRegex, startInclusive,
- extractionEndRegex, endInclusive);
- }
-
- /**
- * Runs a {@link ThrowingRunnable} adopting Shell's permissions.
- */
- public static void runWithShellPermissionIdentity(@NonNull ThrowingRunnable runnable) {
- final UiAutomation automan = InstrumentationRegistry.getInstrumentation().getUiAutomation();
- runWithShellPermissionIdentity(automan, runnable);
- }
-
- /**
- * Runs a {@link ThrowingRunnable} adopting a subset of Shell's permissions.
- */
- public static void runWithShellPermissionIdentity(@NonNull ThrowingRunnable runnable,
- String... permissions) {
- final UiAutomation automan = InstrumentationRegistry.getInstrumentation().getUiAutomation();
- runWithShellPermissionIdentity(automan, runnable, permissions);
- }
-
- /**
- * Runs a {@link ThrowingRunnable} adopting Shell's permissions, where you can specify the
- * uiAutomation used.
- */
- public static void runWithShellPermissionIdentity(
- @NonNull UiAutomation automan, @NonNull ThrowingRunnable runnable) {
- runWithShellPermissionIdentity(automan, runnable, null /* permissions */);
- }
-
- /**
- * Runs a {@link ThrowingRunnable} adopting Shell's permissions, where you can specify the
- * uiAutomation used.
- * @param automan UIAutomation to use.
- * @param runnable The code to run with Shell's identity.
- * @param permissions A subset of Shell's permissions. Passing {@code null} will use all
- * available permissions.
- */
- public static void runWithShellPermissionIdentity(@NonNull UiAutomation automan,
- @NonNull ThrowingRunnable runnable, String... permissions) {
- automan.adoptShellPermissionIdentity(permissions);
- try {
- runnable.run();
- } catch (Exception e) {
- throw new RuntimeException("Caught exception", e);
- } finally {
- automan.dropShellPermissionIdentity();
- }
- }
-
- /**
- * Calls a {@link Callable} adopting Shell's permissions.
- */
- public static <T> T callWithShellPermissionIdentity(@NonNull Callable<T> callable)
- throws Exception {
- final UiAutomation automan = InstrumentationRegistry.getInstrumentation().getUiAutomation();
- automan.adoptShellPermissionIdentity();
- try {
- return callable.call();
- } finally {
- automan.dropShellPermissionIdentity();
- }
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/TestNameUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/TestNameUtils.java
deleted file mode 100644
index 2dbeaab..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/TestNameUtils.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-/**
- * Generic helper used to set / get the the name of the test being run.
- *
- * <p>Typically used on {@code @Rule} classes.
- */
-public final class TestNameUtils {
-
- private static String sCurrentTestName;
- private static String sCurrentTestClass;
-
- /**
- * Gets the name of the test current running.
- */
- @NonNull
- public static String getCurrentTestName() {
- if (sCurrentTestName != null) return sCurrentTestName;
- if (sCurrentTestClass != null) return sCurrentTestClass;
- return "(Unknown test)";
- }
-
- /**
- * Sets the name of the test current running
- */
- public static void setCurrentTestName(@Nullable String name) {
- sCurrentTestName = name;
- }
-
- /**
- * Sets the name of the test class current running
- */
- public static void setCurrentTestClass(@Nullable String testClass) {
- sCurrentTestClass = testClass;
- }
-
- /**
- * Checks whether a test is running, based on whether {@link #setCurrentTestName(String)} was
- * called.
- */
- public static boolean isRunningTest() {
- return sCurrentTestName != null;
- }
-
- private TestNameUtils() {
- throw new UnsupportedOperationException("contain static methods only");
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/TestThread.java b/common/device-side/util/src/com/android/compatibility/common/util/TestThread.java
deleted file mode 100644
index 894b9c8..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/TestThread.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.util;
-
-/**
- * Thread class for executing a Runnable containing assertions in a separate thread.
- * Uncaught exceptions in the Runnable are rethrown in the context of the the thread
- * calling the <code>runTest()</code> method.
- */
-public final class TestThread extends Thread {
- private Throwable mThrowable;
- private Runnable mTarget;
-
- public TestThread(Runnable target) {
- mTarget = target;
- }
-
- @Override
- public final void run() {
- try {
- mTarget.run();
- } catch (Throwable t) {
- mThrowable = t;
- }
- }
-
- /**
- * Run the target Runnable object and wait until the test finish or throw
- * out Exception if test fail.
- *
- * @param runTime
- * @throws Throwable
- */
- public void runTest(long runTime) throws Throwable {
- start();
- joinAndCheck(runTime);
- }
-
- /**
- * Get the Throwable object which is thrown when test running
- * @return The Throwable object
- */
- public Throwable getThrowable() {
- return mThrowable;
- }
-
- /**
- * Set the Throwable object which is thrown when test running
- * @param t The Throwable object
- */
- public void setThrowable(Throwable t) {
- mThrowable = t;
- }
-
- /**
- * Wait for the test thread to complete and throw the stored exception if there is one.
- *
- * @param runTime The time to wait for the test thread to complete.
- * @throws Throwable
- */
- public void joinAndCheck(long runTime) throws Throwable {
- this.join(runTime);
- if (this.isAlive()) {
- this.interrupt();
- this.join(runTime);
- throw new Exception("Thread did not finish within allotted time.");
- }
- checkException();
- }
-
- /**
- * Check whether there is an exception when running Runnable object.
- * @throws Throwable
- */
- public void checkException() throws Throwable {
- if (mThrowable != null) {
- throw mThrowable;
- }
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/TestUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/TestUtils.java
deleted file mode 100644
index 3b82cb7..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/TestUtils.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.util;
-
-import static junit.framework.Assert.fail;
-
-import android.os.SystemClock;
-import android.util.Log;
-
-import java.util.function.BooleanSupplier;
-
-public class TestUtils {
- private static final String TAG = "CtsTestUtils";
-
- private TestUtils() {
- }
-
- public static final int DEFAULT_TIMEOUT_SECONDS = 30;
-
- /** Print an error log and fail. */
- public static void failWithLog(String message) {
- Log.e(TAG, message);
- fail(message);
- }
-
- @FunctionalInterface
- public interface BooleanSupplierWithThrow {
- boolean getAsBoolean() throws Exception;
- }
-
- @FunctionalInterface
- public interface RunnableWithThrow {
- void run() throws Exception;
- }
-
- /**
- * Wait until {@code predicate} is satisfied, or fail, with {@link #DEFAULT_TIMEOUT_SECONDS}.
- */
- public static void waitUntil(String message, BooleanSupplierWithThrow predicate)
- throws Exception {
- waitUntil(message, 0, predicate);
- }
-
- /**
- * Wait until {@code predicate} is satisfied, or fail, with a given timeout.
- */
- public static void waitUntil(
- String message, int timeoutSecond, BooleanSupplierWithThrow predicate)
- throws Exception {
- if (timeoutSecond <= 0) {
- timeoutSecond = DEFAULT_TIMEOUT_SECONDS;
- }
- int sleep = 125;
- final long timeout = SystemClock.uptimeMillis() + timeoutSecond * 1000;
- while (SystemClock.uptimeMillis() < timeout) {
- if (predicate.getAsBoolean()) {
- return; // okay
- }
- Thread.sleep(sleep);
- sleep *= 5;
- sleep = Math.min(2000, sleep);
- }
- failWithLog("Timeout: " + message);
- }
-
- /**
- * Run a Runnable {@code r}, and if it throws, also run {@code onFailure}.
- */
- public static void runWithFailureHook(RunnableWithThrow r, RunnableWithThrow onFailure)
- throws Exception {
- if (r == null) {
- throw new NullPointerException("r");
- }
- if (onFailure == null) {
- throw new NullPointerException("onFailure");
- }
- try {
- r.run();
- } catch (Throwable th) {
- Log.e(TAG, "Caught exception: " + th, th);
- onFailure.run();
- throw th;
- }
- }
-
- /**
- * Synchronized wait for a specified condition.
- *
- * @param notifyLock Lock that will be notified when the condition might have changed
- * @param condition The condition to check
- * @param timeoutMs The timeout in millis
- * @param conditionName The name to include in the assertion. If null, will be given a default.
- */
- public static void waitOn(Object notifyLock, BooleanSupplier condition,
- long timeoutMs, String conditionName) {
- if (conditionName == null) conditionName = "condition";
- if (condition.getAsBoolean()) return;
-
- synchronized (notifyLock) {
- try {
- long timeSlept = 0;
- while (!condition.getAsBoolean() && timeSlept < timeoutMs) {
- long sleepStart = SystemClock.uptimeMillis();
- notifyLock.wait(timeoutMs - timeSlept);
- timeSlept += SystemClock.uptimeMillis() - sleepStart;
- }
- if (!condition.getAsBoolean()) {
- throw new AssertionError("Timed out after " + timeSlept
- + "ms waiting for: " + conditionName);
- }
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
- }
-}
-
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/TextUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/TextUtils.java
deleted file mode 100644
index cca2652..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/TextUtils.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import java.util.regex.Pattern;
-
-public class TextUtils {
- private TextUtils() {
- }
-
- /**
- * Return the first section in {@code source} between the line matches
- * {@code extractionStartRegex} and the line matches {@code extractionEndRegex}.
- */
- public static String extractSection(String source,
- String extractionStartRegex, boolean startInclusive,
- String extractionEndRegex, boolean endInclusive) {
-
- final Pattern start = Pattern.compile(extractionStartRegex);
- final Pattern end = Pattern.compile(extractionEndRegex);
-
- final StringBuilder sb = new StringBuilder();
- final String[] lines = source.split("\\n", -1);
-
- int i = 0;
- for (; i < lines.length; i++) {
- final String line = lines[i];
- if (start.matcher(line).matches()) {
- if (startInclusive) {
- sb.append(line);
- sb.append('\n');
- }
- i++;
- break;
- }
- }
-
- for (; i < lines.length; i++) {
- final String line = lines[i];
- if (end.matcher(line).matches()) {
- if (endInclusive) {
- sb.append(line);
- sb.append('\n');
- }
- break;
- }
- sb.append(line);
- sb.append('\n');
- }
- return sb.toString();
- }
-
- /**
- * Creates a string consisted of {@code size} chars {@code c}.
- */
- public static String repeat(char c, int size) {
- StringBuilder builder = new StringBuilder(size);
- for (int i = 1; i <= size; i++) {
- builder.append(c);
- }
- return builder.toString();
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/ThermalUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/ThermalUtils.java
deleted file mode 100644
index a61ffc1..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/ThermalUtils.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import android.util.Log;
-
-/**
- * Device-side utility class for override/reset thermal status.
- */
-public final class ThermalUtils {
- private static final String TAG = "CtsThermalUtils";
-
- private ThermalUtils() {}
-
- /** Make the target device think it's not throttling. */
- public static void overrideThermalNotThrottling() throws Exception {
- overrideThermalStatus(0);
- }
-
- /**
- * Make the target device think it's in given throttling status.
- * @param status thermal status defined in android.os.Temperature
- */
- public static void overrideThermalStatus(int status) throws Exception {
- SystemUtil.runShellCommandForNoOutput("cmd thermalservice override-status " + status);
-
- Log.d(TAG, "override-status " + status);
- }
-
- /** Cancel the thermal override status on target device. */
- public static void resetThermalStatus() throws Exception {
- SystemUtil.runShellCommandForNoOutput("cmd thermalservice reset");
-
- Log.d(TAG, "reset");
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/ThreadUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/ThreadUtils.java
deleted file mode 100644
index 3948628..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/ThreadUtils.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.util;
-
-import android.os.SystemClock;
-
-public final class ThreadUtils {
- private ThreadUtils() {
- }
-
- public static void sleepUntilRealtime(long realtime) throws Exception {
- Thread.sleep(Math.max(0, realtime - SystemClock.elapsedRealtime()));
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/Timeout.java b/common/device-side/util/src/com/android/compatibility/common/util/Timeout.java
deleted file mode 100644
index 9ac6323..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/Timeout.java
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.util;
-
-import android.os.SystemClock;
-import android.text.TextUtils;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.VisibleForTesting;
-
-import java.util.concurrent.Callable;
-
-/**
- * A "smart" timeout that supports exponential backoff.
- */
-//TODO: move to common CTS Code
-public final class Timeout {
-
- private static final String TAG = "Timeout";
- private static final boolean VERBOSE = true;
-
- private final String mName;
- private long mCurrentValue;
- private final float mMultiplier;
- private final long mMaxValue;
-
- private final Sleeper mSleeper;
-
- private static final Sleeper DEFAULT_SLEEPER = (t) -> SystemClock.sleep(t);
-
- /**
- * Default constructor.
- *
- * @param name name to be used for logging purposes.
- * @param initialValue initial timeout value, in ms.
- * @param multiplier multiplier for {@link #increase()}.
- * @param maxValue max timeout value (in ms) set by {@link #increase()}.
- *
- * @throws IllegalArgumentException if {@code name} is {@code null} or empty,
- * {@code initialValue}, {@code multiplir} or {@code maxValue} are less than {@code 1},
- * or if {@code initialValue} is higher than {@code maxValue}
- */
- public Timeout(String name, long initialValue, float multiplier, long maxValue) {
- this(DEFAULT_SLEEPER, name, initialValue, multiplier, maxValue);
- }
-
- @VisibleForTesting
- Timeout(@NonNull Sleeper sleeper, String name, long initialValue, float multiplier,
- long maxValue) {
- if (initialValue < 1 || maxValue < 1 || initialValue > maxValue) {
- throw new IllegalArgumentException(
- "invalid initial and/or max values: " + initialValue + " and " + maxValue);
- }
- if (multiplier <= 1) {
- throw new IllegalArgumentException("multiplier must be higher than 1: " + multiplier);
- }
- if (TextUtils.isEmpty(name)) {
- throw new IllegalArgumentException("no name");
- }
- mSleeper = sleeper;
- mName = name;
- mCurrentValue = initialValue;
- mMultiplier = multiplier;
- mMaxValue = maxValue;
- Log.d(TAG, "Constructor: " + this + " at " + TestNameUtils.getCurrentTestName());
- }
-
- /**
- * Gets the current timeout, in ms.
- */
- public long ms() {
- return mCurrentValue;
- }
-
- /**
- * Gets the max timeout, in ms.
- */
- public long getMaxValue() {
- return mMaxValue;
- }
-
- /**
- * @return the mMultiplier
- */
- public float getMultiplier() {
- return mMultiplier;
- }
-
- /**
- * Gets the user-friendly name of this timeout.
- */
- @NonNull
- public String getName() {
- return mName;
- }
-
- /**
- * Increases the current value by the {@link #getMultiplier()}, up to {@link #getMaxValue()}.
- *
- * @return previous current value.
- */
- public long increase() {
- final long oldValue = mCurrentValue;
- mCurrentValue = Math.min(mMaxValue, (long) (mCurrentValue * mMultiplier));
- if (oldValue != mCurrentValue) {
- Log.w(TAG, mName + " increased from " + oldValue + "ms to " + mCurrentValue + "ms at "
- + TestNameUtils.getCurrentTestName());
- }
- return oldValue;
- }
-
- /**
- * Runs a {@code job} many times before giving up, sleeping between failed attempts up to
- * {@link #ms()}.
- *
- * @param description description of the job for logging purposes.
- * @param job job to be run, must return {@code null} if it failed and should be retried.
- * @throws RetryableException if all attempts failed.
- * @throws IllegalArgumentException if {@code description} is {@code null} or empty, if
- * {@code job} is {@code null}, or if {@code maxAttempts} is less than 1.
- * @throws Exception any other exception thrown by helper methods.
- *
- * @return job's result.
- */
- public <T> T run(String description, Callable<T> job) throws Exception {
- return run(description, 100, job);
- }
-
- /**
- * Runs a {@code job} many times before giving up, sleeping between failed attempts up to
- * {@link #ms()}.
- *
- * @param description description of the job for logging purposes.
- * @param job job to be run, must return {@code null} if it failed and should be retried.
- * @param retryMs how long to sleep between failures.
- * @throws RetryableException if all attempts failed.
- * @throws IllegalArgumentException if {@code description} is {@code null} or empty, if
- * {@code job} is {@code null}, or if {@code maxAttempts} is less than 1.
- * @throws Exception any other exception thrown by helper methods.
- *
- * @return job's result.
- */
- public <T> T run(String description, long retryMs, Callable<T> job) throws Exception {
- if (TextUtils.isEmpty(description)) {
- throw new IllegalArgumentException("no description");
- }
- if (job == null) {
- throw new IllegalArgumentException("no job");
- }
- if (retryMs < 1) {
- throw new IllegalArgumentException("need to sleep at least 1ms, right?");
- }
- long startTime = SystemClock.elapsedRealtime();
- int attempt = 0;
- long totalSlept = 0;
- while (SystemClock.elapsedRealtime() - startTime <= mCurrentValue) {
- final T result = job.call();
- if (result != null) {
- // Good news, everyone: job succeeded on first attempt!
- return result;
- }
- attempt++;
- final long napTime = Math.min(retryMs, mCurrentValue - totalSlept);
- if (VERBOSE) {
- Log.v(TAG, description + " failed at attempt #" + attempt + "; sleeping for "
- + napTime + "ms before trying again");
- }
- mSleeper.sleep(napTime);
- totalSlept += napTime;
-
- retryMs *= mMultiplier;
- }
- Log.w(TAG, description + " failed after " + attempt + " attempts and " + totalSlept + "ms: "
- + this);
- throw new RetryableException(this, description);
- }
-
- @Override
- public String toString() {
- return mName + ": [current=" + mCurrentValue + "ms; multiplier=" + mMultiplier + "x; max="
- + mMaxValue + "ms]";
- }
-
- @VisibleForTesting
- interface Sleeper {
- void sleep(long napTimeMs);
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/Visitor.java b/common/device-side/util/src/com/android/compatibility/common/util/Visitor.java
deleted file mode 100644
index ab4bbfb..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/Visitor.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.util;
-
-import androidx.annotation.NonNull;
-
-/**
- * Implements the Visitor design pattern
- *
- * @param <V> visited object
- */
-public interface Visitor<V> {
-
- /**
- * Visit that object.
- */
- void visit(@NonNull V visited);
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/WatchDog.java b/common/device-side/util/src/com/android/compatibility/common/util/WatchDog.java
deleted file mode 100644
index efcc693..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/WatchDog.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.util;
-
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-
-import android.util.Log;
-
-import junit.framework.Assert;
-
-/**
- * class for checking if rendering function is alive or not.
- * panic if watch-dog is not reset over certain amount of time
- */
-public class WatchDog implements Runnable {
- private static final String TAG = "WatchDog";
- private Thread mThread;
- private Semaphore mSemaphore;
- private volatile boolean mStopRequested;
- private final long mTimeoutInMilliSecs;
- private TimeoutCallback mCallback = null;
-
- public WatchDog(long timeoutInMilliSecs) {
- mTimeoutInMilliSecs = timeoutInMilliSecs;
- }
-
- public WatchDog(long timeoutInMilliSecs, TimeoutCallback callback) {
- this(timeoutInMilliSecs);
- mCallback = callback;
- }
-
- /** start watch-dog */
- public void start() {
- Log.i(TAG, "start");
- mStopRequested = false;
- mSemaphore = new Semaphore(0);
- mThread = new Thread(this);
- mThread.start();
- }
-
- /** stop watch-dog */
- public void stop() {
- Log.i(TAG, "stop");
- if (mThread == null) {
- return; // already finished
- }
- mStopRequested = true;
- mSemaphore.release();
- try {
- mThread.join();
- } catch (InterruptedException e) {
- // ignore
- }
- mThread = null;
- mSemaphore = null;
- }
-
- /** resets watch-dog, thus prevent it from panic */
- public void reset() {
- if (!mStopRequested) { // stop requested, but rendering still on-going
- mSemaphore.release();
- }
- }
-
- @Override
- public void run() {
- while (!mStopRequested) {
- try {
- boolean success = mSemaphore.tryAcquire(mTimeoutInMilliSecs, TimeUnit.MILLISECONDS);
- if (mCallback == null) {
- Assert.assertTrue("Watchdog timed-out", success);
- } else if (!success) {
- mCallback.onTimeout();
- }
- } catch (InterruptedException e) {
- // this thread will not be interrupted,
- // but if it happens, just check the exit condition.
- }
- }
- }
-
- /**
- * Called by the Watchdog when it has timed out.
- */
- public interface TimeoutCallback {
-
- public void onTimeout();
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/WidgetTestUtils.java b/common/device-side/util/src/com/android/compatibility/common/util/WidgetTestUtils.java
deleted file mode 100644
index 2f2b8f7..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/WidgetTestUtils.java
+++ /dev/null
@@ -1,312 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import static android.view.ViewTreeObserver.OnDrawListener;
-import static android.view.ViewTreeObserver.OnGlobalLayoutListener;
-
-import static org.mockito.hamcrest.MockitoHamcrest.argThat;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.support.test.rule.ActivityTestRule;
-import android.text.Editable;
-import android.text.TextUtils;
-import android.view.View;
-import android.view.ViewTreeObserver;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import junit.framework.Assert;
-
-import org.hamcrest.BaseMatcher;
-import org.hamcrest.Description;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/**
- * The useful methods for widget test.
- */
-public class WidgetTestUtils {
- /**
- * Assert that two bitmaps have identical content (same dimensions, same configuration,
- * same pixel content).
- *
- * @param b1 the first bitmap which needs to compare.
- * @param b2 the second bitmap which needs to compare.
- */
- public static void assertEquals(Bitmap b1, Bitmap b2) {
- if (b1 == b2) {
- return;
- }
-
- if (b1 == null || b2 == null) {
- Assert.fail("the bitmaps are not equal");
- }
-
- // b1 and b2 are all not null.
- if (b1.getWidth() != b2.getWidth() || b1.getHeight() != b2.getHeight()
- || b1.getConfig() != b2.getConfig()) {
- Assert.fail("the bitmaps are not equal");
- }
-
- int w = b1.getWidth();
- int h = b1.getHeight();
- int s = w * h;
- int[] pixels1 = new int[s];
- int[] pixels2 = new int[s];
-
- b1.getPixels(pixels1, 0, w, 0, 0, w, h);
- b2.getPixels(pixels2, 0, w, 0, 0, w, h);
-
- for (int i = 0; i < s; i++) {
- if (pixels1[i] != pixels2[i]) {
- Assert.fail("the bitmaps are not equal");
- }
- }
- }
-
- /**
- * Find beginning of the special element.
- * @param parser XmlPullParser will be parsed.
- * @param firstElementName the target element name.
- *
- * @throws XmlPullParserException if XML Pull Parser related faults occur.
- * @throws IOException if I/O-related error occur when parsing.
- */
- public static final void beginDocument(XmlPullParser parser, String firstElementName)
- throws XmlPullParserException, IOException {
- Assert.assertNotNull(parser);
- Assert.assertNotNull(firstElementName);
-
- int type;
- while ((type = parser.next()) != XmlPullParser.START_TAG
- && type != XmlPullParser.END_DOCUMENT) {
- ;
- }
-
- if (!parser.getName().equals(firstElementName)) {
- throw new XmlPullParserException("Unexpected start tag: found " + parser.getName()
- + ", expected " + firstElementName);
- }
- }
-
- /**
- * Compare the expected pixels with actual, scaling for the target context density
- *
- * @throws AssertionFailedError
- */
- public static void assertScaledPixels(int expected, int actual, Context context) {
- Assert.assertEquals(expected * context.getResources().getDisplayMetrics().density,
- actual, 3);
- }
-
- /** Converts dips into pixels using the {@link Context}'s density. */
- public static int convertDipToPixels(Context context, int dip) {
- float density = context.getResources().getDisplayMetrics().density;
- return Math.round(density * dip);
- }
-
- /**
- * Retrieve a bitmap that can be used for comparison on any density
- * @param resources
- * @return the {@link Bitmap} or <code>null</code>
- */
- public static Bitmap getUnscaledBitmap(Resources resources, int resId) {
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inScaled = false;
- return BitmapFactory.decodeResource(resources, resId, options);
- }
-
- /**
- * Retrieve a dithered bitmap that can be used for comparison on any density
- * @param resources
- * @param config the preferred config for the returning bitmap
- * @return the {@link Bitmap} or <code>null</code>
- */
- public static Bitmap getUnscaledAndDitheredBitmap(Resources resources,
- int resId, Bitmap.Config config) {
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inDither = true;
- options.inScaled = false;
- options.inPreferredConfig = config;
- return BitmapFactory.decodeResource(resources, resId, options);
- }
-
- /**
- * Argument matcher for equality check of a CharSequence.
- *
- * @param expected expected CharSequence
- *
- * @return
- */
- public static CharSequence sameCharSequence(final CharSequence expected) {
- return argThat(new BaseMatcher<CharSequence>() {
- @Override
- public boolean matches(Object o) {
- if (o instanceof CharSequence) {
- return TextUtils.equals(expected, (CharSequence) o);
- }
- return false;
- }
-
- @Override
- public void describeTo(Description description) {
- description.appendText("doesn't match " + expected);
- }
- });
- }
-
- /**
- * Argument matcher for equality check of an Editable.
- *
- * @param expected expected Editable
- *
- * @return
- */
- public static Editable sameEditable(final Editable expected) {
- return argThat(new BaseMatcher<Editable>() {
- @Override
- public boolean matches(Object o) {
- if (o instanceof Editable) {
- return TextUtils.equals(expected, (Editable) o);
- }
- return false;
- }
-
- @Override
- public void describeTo(Description description) {
- description.appendText("doesn't match " + expected);
- }
- });
- }
-
- /**
- * Runs the specified {@link Runnable} on the main thread and ensures that the specified
- * {@link View}'s tree is drawn before returning.
- *
- * @param activityTestRule the activity test rule used to run the test
- * @param view the view whose tree should be drawn before returning
- * @param runner the runnable to run on the main thread, or {@code null} to
- * simply force invalidation and a draw pass
- */
- public static void runOnMainAndDrawSync(@NonNull final ActivityTestRule activityTestRule,
- @NonNull final View view, @Nullable final Runnable runner) {
- final CountDownLatch latch = new CountDownLatch(1);
-
- try {
- activityTestRule.runOnUiThread(() -> {
- final OnDrawListener listener = new OnDrawListener() {
- @Override
- public void onDraw() {
- // posting so that the sync happens after the draw that's about to happen
- view.post(() -> {
- activityTestRule.getActivity().getWindow().getDecorView()
- .getViewTreeObserver().removeOnDrawListener(this);
- latch.countDown();
- });
- }
- };
-
- activityTestRule.getActivity().getWindow().getDecorView()
- .getViewTreeObserver().addOnDrawListener(listener);
-
- if (runner != null) {
- runner.run();
- }
- view.invalidate();
- });
-
- Assert.assertTrue("Expected draw pass occurred within 5 seconds",
- latch.await(5, TimeUnit.SECONDS));
- } catch (Throwable t) {
- throw new RuntimeException(t);
- }
- }
-
- /**
- * Runs the specified Runnable on the main thread and ensures that the activity's view tree is
- * laid out before returning.
- *
- * @param activityTestRule the activity test rule used to run the test
- * @param runner the runnable to run on the main thread. {@code null} is
- * allowed, and simply means that there no runnable is required.
- * @param forceLayout true if there should be an explicit call to requestLayout(),
- * false otherwise
- */
- public static void runOnMainAndLayoutSync(@NonNull final ActivityTestRule activityTestRule,
- @Nullable final Runnable runner, boolean forceLayout)
- throws Throwable {
- runOnMainAndLayoutSync(activityTestRule,
- activityTestRule.getActivity().getWindow().getDecorView(), runner, forceLayout);
- }
-
- /**
- * Runs the specified Runnable on the main thread and ensures that the specified view is
- * laid out before returning.
- *
- * @param activityTestRule the activity test rule used to run the test
- * @param view The view
- * @param runner the runnable to run on the main thread. {@code null} is
- * allowed, and simply means that there no runnable is required.
- * @param forceLayout true if there should be an explicit call to requestLayout(),
- * false otherwise
- */
- public static void runOnMainAndLayoutSync(@NonNull final ActivityTestRule activityTestRule,
- @NonNull final View view, @Nullable final Runnable runner, boolean forceLayout)
- throws Throwable {
- final View rootView = view.getRootView();
-
- final CountDownLatch latch = new CountDownLatch(1);
-
- activityTestRule.runOnUiThread(() -> {
- final OnGlobalLayoutListener listener = new ViewTreeObserver.OnGlobalLayoutListener() {
- @Override
- public void onGlobalLayout() {
- rootView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
- // countdown immediately since the layout we were waiting on has happened
- latch.countDown();
- }
- };
-
- rootView.getViewTreeObserver().addOnGlobalLayoutListener(listener);
-
- if (runner != null) {
- runner.run();
- }
-
- if (forceLayout) {
- rootView.requestLayout();
- }
- });
-
- try {
- Assert.assertTrue("Expected layout pass within 5 seconds",
- latch.await(5, TimeUnit.SECONDS));
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
-
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/WifiConfigCreator.java b/common/device-side/util/src/com/android/compatibility/common/util/WifiConfigCreator.java
deleted file mode 100755
index f2d1f65..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/WifiConfigCreator.java
+++ /dev/null
@@ -1,258 +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.compatibility.common.util;
-
-import static android.net.wifi.WifiManager.EXTRA_WIFI_STATE;
-import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.net.ProxyInfo;
-import android.net.Uri;
-import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiManager;
-import android.text.TextUtils;
-import android.util.Log;
-
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/**
- * A simple activity to create and manage wifi configurations.
- */
-public class WifiConfigCreator {
- public static final String ACTION_CREATE_WIFI_CONFIG =
- "com.android.compatibility.common.util.CREATE_WIFI_CONFIG";
- public static final String ACTION_UPDATE_WIFI_CONFIG =
- "com.android.compatibility.common.util.UPDATE_WIFI_CONFIG";
- public static final String ACTION_REMOVE_WIFI_CONFIG =
- "com.android.compatibility.common.util.REMOVE_WIFI_CONFIG";
- public static final String EXTRA_NETID = "extra-netid";
- public static final String EXTRA_SSID = "extra-ssid";
- public static final String EXTRA_SECURITY_TYPE = "extra-security-type";
- public static final String EXTRA_PASSWORD = "extra-password";
-
- public static final int SECURITY_TYPE_NONE = 1;
- public static final int SECURITY_TYPE_WPA = 2;
- public static final int SECURITY_TYPE_WEP = 3;
-
- private static final String TAG = "WifiConfigCreator";
-
- private static final long ENABLE_WIFI_WAIT_SEC = 10L;
-
- private final Context mContext;
- private final WifiManager mWifiManager;
-
- public WifiConfigCreator(Context context) {
- mContext = context;
- mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
- }
-
- /**
- * Adds a new WiFi network.
- * @return network id or -1 in case of error
- */
- public int addNetwork(String ssid, boolean hidden, int securityType,
- String password) throws InterruptedException, SecurityException {
- checkAndEnableWifi();
-
- WifiConfiguration wifiConf = createConfig(ssid, hidden, securityType, password);
-
- int netId = mWifiManager.addNetwork(wifiConf);
-
- if (netId != -1) {
- mWifiManager.enableNetwork(netId, true);
- } else {
- Log.w(TAG, "Unable to add SSID '" + ssid + "': netId = " + netId);
- }
- return netId;
- }
-
- /**
- * Adds a new wifiConfiguration with OPEN security type, and the given pacProxy
- * verifies that the proxy is added by getting the configuration back, and checking it.
- * @return returns the PAC proxy URL after adding the network and getting it from WifiManager
- * @throws IllegalStateException if any of the WifiManager operations fail
- */
- public String addHttpProxyNetworkVerifyAndRemove(String ssid, String pacProxyUrl)
- throws IllegalStateException {
- String retrievedPacProxyUrl = null;
- int netId = -1;
- try {
- WifiConfiguration conf = createConfig(ssid, false, SECURITY_TYPE_NONE, null);
- if (pacProxyUrl != null) {
- conf.setHttpProxy(ProxyInfo.buildPacProxy(Uri.parse(pacProxyUrl)));
- }
- netId = mWifiManager.addNetwork(conf);
- if (netId == -1) {
- throw new IllegalStateException("Failed to addNetwork: " + ssid);
- }
- for (final WifiConfiguration w : mWifiManager.getConfiguredNetworks()) {
- if (w.SSID.equals(ssid)) {
- conf = w;
- break;
- }
- }
- if (conf == null) {
- throw new IllegalStateException("Failed to get WifiConfiguration for: " + ssid);
- }
- Uri pacProxyFileUri = null;
- ProxyInfo httpProxy = conf.getHttpProxy();
- if (httpProxy != null) pacProxyFileUri = httpProxy.getPacFileUrl();
- if (pacProxyFileUri != null) {
- retrievedPacProxyUrl = conf.getHttpProxy().getPacFileUrl().toString();
- }
- if (!mWifiManager.removeNetwork(netId)) {
- throw new IllegalStateException("Failed to remove WifiConfiguration: " + ssid);
- }
- } finally {
- mWifiManager.removeNetwork(netId);
- }
- return retrievedPacProxyUrl;
- }
-
- /**
- * Updates a new WiFi network.
- * @return network id (may differ from original) or -1 in case of error
- */
- public int updateNetwork(WifiConfiguration wifiConf, String ssid, boolean hidden,
- int securityType, String password) throws InterruptedException, SecurityException {
- checkAndEnableWifi();
- if (wifiConf == null) {
- return -1;
- }
-
- WifiConfiguration conf = createConfig(ssid, hidden, securityType, password);
- conf.networkId = wifiConf.networkId;
-
- int newNetId = mWifiManager.updateNetwork(conf);
-
- if (newNetId != -1) {
- mWifiManager.saveConfiguration();
- mWifiManager.enableNetwork(newNetId, true);
- } else {
- Log.w(TAG, "Unable to update SSID '" + ssid + "': netId = " + newNetId);
- }
- return newNetId;
- }
-
- /**
- * Updates a new WiFi network.
- * @return network id (may differ from original) or -1 in case of error
- */
- public int updateNetwork(int netId, String ssid, boolean hidden,
- int securityType, String password) throws InterruptedException, SecurityException {
- checkAndEnableWifi();
-
- WifiConfiguration wifiConf = null;
- List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
- for (WifiConfiguration config : configs) {
- if (config.networkId == netId) {
- wifiConf = config;
- break;
- }
- }
- return updateNetwork(wifiConf, ssid, hidden, securityType, password);
- }
-
- public boolean removeNetwork(int netId) {
- return mWifiManager.removeNetwork(netId);
- }
-
- /**
- * Creates a WifiConfiguration set up according to given parameters
- * @param ssid SSID of the network
- * @param hidden Is SSID not broadcast?
- * @param securityType One of {@link #SECURITY_TYPE_NONE}, {@link #SECURITY_TYPE_WPA} or
- * {@link #SECURITY_TYPE_WEP}
- * @param password Password for WPA or WEP
- * @return Created configuration object
- */
- private WifiConfiguration createConfig(String ssid, boolean hidden, int securityType,
- String password) {
- WifiConfiguration wifiConf = new WifiConfiguration();
- if (!TextUtils.isEmpty(ssid)) {
- wifiConf.SSID = '"' + ssid + '"';
- }
- wifiConf.status = WifiConfiguration.Status.ENABLED;
- wifiConf.hiddenSSID = hidden;
- switch (securityType) {
- case SECURITY_TYPE_NONE:
- wifiConf.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
- break;
- case SECURITY_TYPE_WPA:
- updateForWPAConfiguration(wifiConf, password);
- break;
- case SECURITY_TYPE_WEP:
- updateForWEPConfiguration(wifiConf, password);
- break;
- }
- return wifiConf;
- }
-
- private void updateForWPAConfiguration(WifiConfiguration wifiConf, String wifiPassword) {
- wifiConf.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
- if (!TextUtils.isEmpty(wifiPassword)) {
- wifiConf.preSharedKey = '"' + wifiPassword + '"';
- }
- }
-
- private void updateForWEPConfiguration(WifiConfiguration wifiConf, String password) {
- wifiConf.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
- wifiConf.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
- wifiConf.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
- if (!TextUtils.isEmpty(password)) {
- int length = password.length();
- if ((length == 10 || length == 26
- || length == 58) && password.matches("[0-9A-Fa-f]*")) {
- wifiConf.wepKeys[0] = password;
- } else {
- wifiConf.wepKeys[0] = '"' + password + '"';
- }
- wifiConf.wepTxKeyIndex = 0;
- }
- }
-
- private void checkAndEnableWifi() throws InterruptedException {
- final CountDownLatch enabledLatch = new CountDownLatch(1);
-
- // Register a change receiver first to pick up events between isEnabled and setEnabled
- final BroadcastReceiver watcher = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getIntExtra(EXTRA_WIFI_STATE, -1) == WIFI_STATE_ENABLED) {
- enabledLatch.countDown();
- }
- }
- };
-
- mContext.registerReceiver(watcher, new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION));
- try {
- // In case wifi is not already enabled, wait for it to come up
- if (!mWifiManager.isWifiEnabled()) {
- SystemUtil.runShellCommand("svc wifi enable");
- enabledLatch.await(ENABLE_WIFI_WAIT_SEC, TimeUnit.SECONDS);
- }
- } finally {
- mContext.unregisterReceiver(watcher);
- }
- }
-}
-
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/Within.java b/common/device-side/util/src/com/android/compatibility/common/util/Within.java
deleted file mode 100644
index 4d9ff80..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/Within.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.util;
-
-import android.os.SystemClock;
-
-import org.mockito.Mockito;
-import org.mockito.exceptions.base.MockitoAssertionError;
-import org.mockito.internal.verification.api.VerificationData;
-import org.mockito.invocation.Invocation;
-import org.mockito.verification.VerificationMode;
-
-import java.util.List;
-
-/**
- * Custom verification mode that allows waiting for the specific invocation to happen within
- * a certain time interval. Not that unlike {@link Mockito#timeout(int)}, this mode will not
- * return early and throw exception if the expected method was called with a different set of
- * parameters before the call that we're waiting for.
- */
-public class Within implements VerificationMode {
- private static final long TIME_SLICE = 50;
- private final long mTimeout;
-
- public Within(long timeout) {
- mTimeout = timeout;
- }
-
- @Override
- public void verify(VerificationData data) {
- long timeout = mTimeout;
- MockitoAssertionError errorToRethrow = null;
- // Loop in the same way we do in PollingCheck, sleeping and then testing for the target
- // invocation
- while (timeout > 0) {
- SystemClock.sleep(TIME_SLICE);
-
- try {
- final List<Invocation> actualInvocations = data.getAllInvocations();
- // Iterate over all invocations so far to see if we have a match
- for (Invocation invocation : actualInvocations) {
- if (data.getWanted().matches(invocation)) {
- // Found our match within our timeout. Mark all invocations as verified
- markAllInvocationsAsVerified(data);
- // and return
- return;
- }
- }
- } catch (MockitoAssertionError assertionError) {
- errorToRethrow = assertionError;
- }
-
- timeout -= TIME_SLICE;
- }
-
- if (errorToRethrow != null) {
- throw errorToRethrow;
- }
-
- throw new MockitoAssertionError(
- "Timed out while waiting " + mTimeout + "ms for " + data.getWanted().toString());
- }
-
- // TODO: Uncomment once upgraded to 2.7.13
- // @Override
- public VerificationMode description(String description) {
- // Return this for now.
- // TODO: Return wrapper once upgraded to 2.7.13
- return this;
- }
-
- private void markAllInvocationsAsVerified(VerificationData data) {
- for (Invocation invocation : data.getAllInvocations()) {
- invocation.markVerified();
- data.getWanted().captureArgumentsFrom(invocation);
- }
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/devicepolicy/provisioning/IBooleanCallback.aidl b/common/device-side/util/src/com/android/compatibility/common/util/devicepolicy/provisioning/IBooleanCallback.aidl
deleted file mode 100644
index 2fdb26b..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/devicepolicy/provisioning/IBooleanCallback.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.util.devicepolicy.provisioning;
-
-interface IBooleanCallback {
- oneway void onResult(boolean result);
-}
\ No newline at end of file
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/devicepolicy/provisioning/SilentProvisioningTestManager.java b/common/device-side/util/src/com/android/compatibility/common/util/devicepolicy/provisioning/SilentProvisioningTestManager.java
deleted file mode 100644
index 05edf1a..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/devicepolicy/provisioning/SilentProvisioningTestManager.java
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.util.devicepolicy.provisioning;
-
-import static android.app.admin.DevicePolicyManager.ACTION_MANAGED_PROFILE_PROVISIONED;
-import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE;
-import static android.content.Intent.ACTION_MANAGED_PROFILE_ADDED;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.uiautomator.UiDevice;
-import android.util.Log;
-
-import com.android.compatibility.common.util.BlockingBroadcastReceiver;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.TimeUnit;
-
-public class SilentProvisioningTestManager {
- private static final long TIMEOUT_SECONDS = 120L;
- private static final String TAG = "SilentProvisioningTest";
-
- private final LinkedBlockingQueue<Boolean> mProvisioningResults = new LinkedBlockingQueue(1);
-
- private final IBooleanCallback mProvisioningResultCallback = new IBooleanCallback.Stub() {
- @Override
- public void onResult(boolean result) {
- try {
- mProvisioningResults.put(result);
- } catch (InterruptedException e) {
- Log.e(TAG, "IBooleanCallback.callback", e);
- }
- }
- };
-
- private final Context mContext;
- private Intent mReceivedProfileProvisionedIntent;
-
- public SilentProvisioningTestManager(Context context) {
- mContext = context.getApplicationContext();
- }
-
- public Intent getReceviedProfileProvisionedIntent() {
- return mReceivedProfileProvisionedIntent;
- }
-
- public boolean startProvisioningAndWait(Intent provisioningIntent) throws InterruptedException {
- wakeUpAndDismissInsecureKeyguard();
- mContext.startActivity(getStartIntent(provisioningIntent));
- Log.i(TAG, "startActivity with intent: " + provisioningIntent);
-
- if (ACTION_PROVISION_MANAGED_PROFILE.equals(provisioningIntent.getAction())) {
- return waitManagedProfileProvisioning();
- } else {
- return waitDeviceOwnerProvisioning();
- }
- }
-
- private boolean waitDeviceOwnerProvisioning() throws InterruptedException {
- return pollProvisioningResult();
- }
-
- private boolean waitManagedProfileProvisioning() throws InterruptedException {
- BlockingBroadcastReceiver managedProfileProvisionedReceiver =
- new BlockingBroadcastReceiver(mContext, ACTION_MANAGED_PROFILE_PROVISIONED);
- BlockingBroadcastReceiver managedProfileAddedReceiver =
- new BlockingBroadcastReceiver(mContext, ACTION_MANAGED_PROFILE_ADDED);
- try {
- managedProfileProvisionedReceiver.register();
- managedProfileAddedReceiver.register();
-
- if (!pollProvisioningResult()) {
- return false;
- }
-
- mReceivedProfileProvisionedIntent =
- managedProfileProvisionedReceiver.awaitForBroadcast();
- if (mReceivedProfileProvisionedIntent == null) {
- Log.i(TAG, "managedProfileProvisionedReceiver.awaitForBroadcast(): failed");
- return false;
- }
-
- if (managedProfileAddedReceiver.awaitForBroadcast() == null) {
- Log.i(TAG, "managedProfileAddedReceiver.awaitForBroadcast(): failed");
- return false;
- }
- } finally {
- managedProfileProvisionedReceiver.unregisterQuietly();
- managedProfileAddedReceiver.unregisterQuietly();
- }
- return true;
- }
-
- private boolean pollProvisioningResult() throws InterruptedException {
- Boolean result = mProvisioningResults.poll(TIMEOUT_SECONDS, TimeUnit.SECONDS);
- if (result == null) {
- Log.i(TAG, "ManagedProvisioning doesn't return result within "
- + TIMEOUT_SECONDS + " seconds ");
- return false;
- }
-
- if (!result) {
- Log.i(TAG, "Failed to provision");
- return false;
- }
- return true;
- }
-
- private Intent getStartIntent(Intent intent) {
- final Bundle bundle = new Bundle();
- bundle.putParcelable(Intent.EXTRA_INTENT, intent);
- bundle.putBinder(StartProvisioningActivity.EXTRA_BOOLEAN_CALLBACK,
- mProvisioningResultCallback.asBinder());
- return new Intent(mContext, StartProvisioningActivity.class)
- .putExtras(bundle)
- .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- }
-
- private static void wakeUpAndDismissInsecureKeyguard() {
- try {
- UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
- uiDevice.wakeUp();
- uiDevice.pressMenu();
- } catch (RemoteException e) {
- Log.e(TAG, "wakeUpScreen", e);
- }
- }
-
- private static class BlockingReceiver extends BroadcastReceiver {
-
- private final CountDownLatch mLatch = new CountDownLatch(1);
- private final Context mContext;
- private final String mAction;
- private Intent mReceivedIntent;
-
- private BlockingReceiver(Context context, String action) {
- mContext = context;
- mAction = action;
- mReceivedIntent = null;
- }
-
- public void register() {
- mContext.registerReceiver(this, new IntentFilter(mAction));
- }
-
- public boolean await() throws InterruptedException {
- return mLatch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS);
- }
-
- public Intent getReceivedIntent() {
- return mReceivedIntent;
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- mReceivedIntent = intent;
- mLatch.countDown();
- }
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/devicepolicy/provisioning/StartProvisioningActivity.java b/common/device-side/util/src/com/android/compatibility/common/util/devicepolicy/provisioning/StartProvisioningActivity.java
deleted file mode 100644
index 4a98794..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/devicepolicy/provisioning/StartProvisioningActivity.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.util.devicepolicy.provisioning;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.util.Log;
-import android.view.WindowManager;
-
-/**
- * Must register it in AndroidManifest.xml
- * <activity android:name="com.android.compatibility.common.util.devicepolicy.provisioning.StartProvisioningActivity"></activity>
- */
-public class StartProvisioningActivity extends Activity {
- private static final int REQUEST_CODE = 1;
- private static final String TAG = "StartProvisionActivity";
-
- public static final String EXTRA_BOOLEAN_CALLBACK = "EXTRA_BOOLEAN_CALLBACK";
-
- IBooleanCallback mResultCallback;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- // Reduce flakiness of the test
- // Show activity on top of keyguard
- getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
- // Turn on screen to prevent activity being paused by system
- getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
- getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
-
- mResultCallback = IBooleanCallback.Stub.asInterface(
- getIntent().getExtras().getBinder(EXTRA_BOOLEAN_CALLBACK));
- Log.i(TAG, "result callback class name " + mResultCallback);
-
- // Only provision it if the activity is not re-created
- if (savedInstanceState == null) {
- Intent provisioningIntent = getIntent().getParcelableExtra(Intent.EXTRA_INTENT);
-
- startActivityForResult(provisioningIntent, REQUEST_CODE);
- Log.i(TAG, "Start provisioning intent");
- }
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (requestCode == REQUEST_CODE) {
- try {
- boolean result = resultCode == RESULT_OK;
- mResultCallback.onResult(result);
- Log.i(TAG, "onActivityResult result: " + result);
- } catch (RemoteException e) {
- Log.e(TAG, "onActivityResult", e);
- }
- } else {
- super.onActivityResult(requestCode, resultCode, data);
- }
- }
-}
\ No newline at end of file
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/transition/TargetTracking.java b/common/device-side/util/src/com/android/compatibility/common/util/transition/TargetTracking.java
deleted file mode 100644
index 7c53921..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/transition/TargetTracking.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.util.transition;
-
-import android.graphics.Rect;
-import android.view.View;
-
-import java.util.ArrayList;
-
-public interface TargetTracking {
- ArrayList<View> getTrackedTargets();
- void clearTargets();
- Rect getCapturedEpicenter();
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/transition/TrackingTransition.java b/common/device-side/util/src/com/android/compatibility/common/util/transition/TrackingTransition.java
deleted file mode 100644
index 55b235d..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/transition/TrackingTransition.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.util.transition;
-
-import android.animation.Animator;
-import android.graphics.Rect;
-import android.transition.Transition;
-import android.transition.TransitionValues;
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.util.ArrayList;
-
-/**
- * A transition that tracks which targets are applied to it.
- * It will assume any target that it applies to will have differences
- * between the start and end state, regardless of the differences
- * that actually exist. In other words, it doesn't actually check
- * any size or position differences or any other property of the view.
- * It just records the difference.
- * <p>
- * Both start and end value Views are recorded, but no actual animation
- * is created.
- */
-public class TrackingTransition extends Transition implements TargetTracking {
- public final ArrayList<View> targets = new ArrayList<>();
- private final Rect[] mEpicenter = new Rect[1];
- private static String PROP = "tracking:prop";
- private static String[] PROPS = { PROP };
-
- @Override
- public String[] getTransitionProperties() {
- return PROPS;
- }
-
- @Override
- public void captureStartValues(TransitionValues transitionValues) {
- transitionValues.values.put(PROP, 0);
- }
-
- @Override
- public void captureEndValues(TransitionValues transitionValues) {
- transitionValues.values.put(PROP, 1);
- }
-
- @Override
- public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
- TransitionValues endValues) {
- if (startValues != null) {
- targets.add(startValues.view);
- }
- if (endValues != null) {
- targets.add(endValues.view);
- }
- Rect epicenter = getEpicenter();
- if (epicenter != null) {
- mEpicenter[0] = new Rect(epicenter);
- } else {
- mEpicenter[0] = null;
- }
- return null;
- }
-
- @Override
- public ArrayList<View> getTrackedTargets() {
- return targets;
- }
-
- @Override
- public void clearTargets() {
- targets.clear();
- }
-
- @Override
- public Rect getCapturedEpicenter() {
- return mEpicenter[0];
- }
-}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/transition/TrackingVisibility.java b/common/device-side/util/src/com/android/compatibility/common/util/transition/TrackingVisibility.java
deleted file mode 100644
index 8a5a19e..0000000
--- a/common/device-side/util/src/com/android/compatibility/common/util/transition/TrackingVisibility.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.util.transition;
-
-import android.animation.Animator;
-import android.graphics.Rect;
-import android.transition.TransitionValues;
-import android.transition.Visibility;
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.util.ArrayList;
-
-/**
- * Visibility transition that tracks which targets are applied to it.
- * This transition does no animation.
- */
-public class TrackingVisibility extends Visibility implements TargetTracking {
- public final ArrayList<View> targets = new ArrayList<>();
- private final Rect[] mEpicenter = new Rect[1];
-
- @Override
- public Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues,
- TransitionValues endValues) {
- targets.add(endValues.view);
- Rect epicenter = getEpicenter();
- if (epicenter != null) {
- mEpicenter[0] = new Rect(epicenter);
- } else {
- mEpicenter[0] = null;
- }
- return null;
- }
-
- @Override
- public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues,
- TransitionValues endValues) {
- targets.add(startValues.view);
- Rect epicenter = getEpicenter();
- if (epicenter != null) {
- mEpicenter[0] = new Rect(epicenter);
- } else {
- mEpicenter[0] = null;
- }
- return null;
- }
-
- @Override
- public ArrayList<View> getTrackedTargets() {
- return targets;
- }
-
- @Override
- public void clearTargets() {
- targets.clear();
- }
-
- @Override
- public Rect getCapturedEpicenter() {
- return mEpicenter[0];
- }
-}
diff --git a/common/device-side/util/tests/src/com/android/compatibility/common/util/ApiLevelUtilTest.java b/common/device-side/util/tests/src/com/android/compatibility/common/util/ApiLevelUtilTest.java
deleted file mode 100644
index 13305c3..0000000
--- a/common/device-side/util/tests/src/com/android/compatibility/common/util/ApiLevelUtilTest.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-package com.android.compatibility.common.util;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
-import android.os.Build;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests for {@line ApiLevelUtil}.
- */
-@RunWith(AndroidJUnit4.class)
-public class ApiLevelUtilTest {
-
- @Test
- public void testComparisonByInt() throws Exception {
- int version = Build.VERSION.SDK_INT;
-
- assertFalse(ApiLevelUtil.isBefore(version - 1));
- assertFalse(ApiLevelUtil.isBefore(version));
- assertTrue(ApiLevelUtil.isBefore(version + 1));
-
- assertTrue(ApiLevelUtil.isAfter(version - 1));
- assertFalse(ApiLevelUtil.isAfter(version));
- assertFalse(ApiLevelUtil.isAfter(version + 1));
-
- assertTrue(ApiLevelUtil.isAtLeast(version - 1));
- assertTrue(ApiLevelUtil.isAtLeast(version));
- assertFalse(ApiLevelUtil.isAtLeast(version + 1));
-
- assertFalse(ApiLevelUtil.isAtMost(version - 1));
- assertTrue(ApiLevelUtil.isAtMost(version));
- assertTrue(ApiLevelUtil.isAtMost(version + 1));
- }
-
- @Test
- public void testComparisonByString() throws Exception {
- // test should pass as long as device SDK version is at least 12
- assertTrue(ApiLevelUtil.isAtLeast("HONEYCOMB_MR1"));
- assertTrue(ApiLevelUtil.isAtLeast("12"));
- }
-
- @Test
- public void testResolveVersionString() throws Exception {
- // can only test versions known to the device build
- assertEquals(ApiLevelUtil.resolveVersionString("GINGERBREAD_MR1"), 10);
- assertEquals(ApiLevelUtil.resolveVersionString("10"), 10);
- assertEquals(ApiLevelUtil.resolveVersionString("HONEYCOMB"), 11);
- assertEquals(ApiLevelUtil.resolveVersionString("11"), 11);
- assertEquals(ApiLevelUtil.resolveVersionString("honeycomb_mr1"), 12);
- assertEquals(ApiLevelUtil.resolveVersionString("12"), 12);
- }
-
- @Test(expected = RuntimeException.class)
- public void testResolveMisspelledVersionString() throws Exception {
- ApiLevelUtil.resolveVersionString("GINGERBEARD");
- }
-}
diff --git a/common/device-side/util/tests/src/com/android/compatibility/common/util/BusinessLogicDeviceExecutorTest.java b/common/device-side/util/tests/src/com/android/compatibility/common/util/BusinessLogicDeviceExecutorTest.java
deleted file mode 100644
index 9f3ca47..0000000
--- a/common/device-side/util/tests/src/com/android/compatibility/common/util/BusinessLogicDeviceExecutorTest.java
+++ /dev/null
@@ -1,314 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-package com.android.compatibility.common.util;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertNull;
-import static junit.framework.Assert.assertTrue;
-import static junit.framework.Assert.fail;
-import static org.junit.Assume.assumeTrue;
-
-import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.AssumptionViolatedException;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import junit.framework.AssertionFailedError;
-
-/**
- * Tests for {@line BusinessLogicDeviceExecutor}.
- */
-@RunWith(AndroidJUnit4.class)
-public class BusinessLogicDeviceExecutorTest {
-
- private static final String THIS_CLASS =
- "com.android.compatibility.common.util.BusinessLogicDeviceExecutorTest";
- private static final String METHOD_1 = THIS_CLASS + ".method1";
- private static final String METHOD_2 = THIS_CLASS + ".method2";
- private static final String METHOD_3 = THIS_CLASS + ".method3";
- private static final String METHOD_4 = THIS_CLASS + ".method4";
- private static final String METHOD_5 = THIS_CLASS + ".method5";
- private static final String METHOD_6 = THIS_CLASS + ".method6";
- private static final String METHOD_7 = THIS_CLASS + ".method7";
- private static final String METHOD_8 = THIS_CLASS + ".method8";
- private static final String METHOD_9 = THIS_CLASS + ".method9";
- private static final String METHOD_10 = THIS_CLASS + ".method10";
- private static final String FAKE_METHOD = THIS_CLASS + ".methodDoesntExist";
- private static final String ARG_STRING_1 = "arg1";
- private static final String ARG_STRING_2 = "arg2";
-
- private static final String OTHER_METHOD_1 = THIS_CLASS + "$OtherClass.method1";
-
- private String mInvoked = null;
- private Object[] mArgsUsed = null;
- private Context mContext;
- private BusinessLogicExecutor mExecutor;
-
- @Before
- public void setUp() {
- mContext = InstrumentationRegistry.getTargetContext();
- mExecutor = new BusinessLogicDeviceExecutor(mContext, this);
- // reset the instance variables tracking the method invoked and the args used
- mInvoked = null;
- mArgsUsed = null;
- // reset the OtherClass class variable tracking the method invoked
- OtherClass.otherInvoked = null;
- }
-
- @Test
- public void testInvokeMethodInThisClass() throws Exception {
- mExecutor.invokeMethod(METHOD_1);
- // assert that mInvoked was set for this BusinessLogicDeviceExecutorTest instance
- assertEquals("Failed to invoke method in this class", mInvoked, METHOD_1);
- }
-
- @Test
- public void testInvokeMethodInOtherClass() throws Exception {
- mExecutor.invokeMethod(OTHER_METHOD_1);
- // assert that OtherClass.method1 was invoked, and static field of OtherClass was changed
- assertEquals("Failed to invoke method in other class", OtherClass.otherInvoked,
- OTHER_METHOD_1);
- }
-
- @Test
- public void testInvokeMethodWithStringArgs() throws Exception {
- mExecutor.invokeMethod(METHOD_2, ARG_STRING_1, ARG_STRING_2);
- assertEquals("Failed to invoke method in this class", mInvoked, METHOD_2);
- // assert both String arguments were correctly set for method2
- assertEquals("Failed to set first argument", mArgsUsed[0], ARG_STRING_1);
- assertEquals("Failed to set second argument", mArgsUsed[1], ARG_STRING_2);
- }
-
- @Test
- public void testInvokeMethodWithStringAndContextArgs() throws Exception {
- mExecutor.invokeMethod(METHOD_3, ARG_STRING_1);
- assertEquals("Failed to invoke method in this class", mInvoked, METHOD_3);
- // assert that String arg and Context arg were correctly set for method3
- assertEquals("Failed to set first argument", mArgsUsed[0], ARG_STRING_1);
- assertEquals("Failed to set second argument", mArgsUsed[1], mContext);
- }
-
- @Test
- public void testInvokeMethodWithContextAndStringArgs() throws Exception {
- mExecutor.invokeMethod(METHOD_4, ARG_STRING_1);
- assertEquals("Failed to invoke method in this class", mInvoked, METHOD_4);
- // Like testInvokeMethodWithStringAndContextArgs, but flip the args for method4
- assertEquals("Failed to set first argument", mArgsUsed[0], mContext);
- assertEquals("Failed to set second argument", mArgsUsed[1], ARG_STRING_1);
- }
-
- @Test
- public void testInvokeMethodWithStringArrayArg() throws Exception {
- mExecutor.invokeMethod(METHOD_5, ARG_STRING_1, ARG_STRING_2);
- assertEquals("Failed to invoke method in this class", mInvoked, METHOD_5);
- // assert both String arguments were correctly set for method5
- assertEquals("Failed to set first argument", mArgsUsed[0], ARG_STRING_1);
- assertEquals("Failed to set second argument", mArgsUsed[1], ARG_STRING_2);
- }
-
- @Test
- public void testInvokeMethodWithEmptyStringArrayArg() throws Exception {
- mExecutor.invokeMethod(METHOD_5);
- assertEquals("Failed to invoke method in this class", mInvoked, METHOD_5);
- // assert no String arguments were set for method5
- assertEquals("Incorrectly set args", mArgsUsed.length, 0);
- }
-
- @Test
- public void testInvokeMethodWithStringAndStringArrayArgs() throws Exception {
- mExecutor.invokeMethod(METHOD_6, ARG_STRING_1, ARG_STRING_2);
- assertEquals("Failed to invoke method in this class", mInvoked, METHOD_6);
- // assert both String arguments were correctly set for method6
- assertEquals("Failed to set first argument", mArgsUsed[0], ARG_STRING_1);
- assertEquals("Failed to set second argument", mArgsUsed[1], ARG_STRING_2);
- }
-
- @Test
- public void testInvokeMethodWithAllArgTypes() throws Exception {
- mExecutor.invokeMethod(METHOD_7, ARG_STRING_1, ARG_STRING_2);
- assertEquals("Failed to invoke method in this class", mInvoked, METHOD_7);
- // assert all arguments were correctly set for method7
- assertEquals("Failed to set first argument", mArgsUsed[0], ARG_STRING_1);
- assertEquals("Failed to set second argument", mArgsUsed[1], mContext);
- assertEquals("Failed to set third argument", mArgsUsed[2], ARG_STRING_2);
- }
-
- @Test
- public void testInvokeOverloadedMethodOneArg() throws Exception {
- mExecutor.invokeMethod(METHOD_1, ARG_STRING_1);
- assertEquals("Failed to invoke method in this class", mInvoked, METHOD_1);
- assertEquals("Set wrong number of arguments", mArgsUsed.length, 1);
- assertEquals("Failed to set first argument", mArgsUsed[0], ARG_STRING_1);
- }
-
- @Test
- public void testInvokeOverloadedMethodTwoArgs() throws Exception {
- mExecutor.invokeMethod(METHOD_1, ARG_STRING_1, ARG_STRING_2);
- assertEquals("Failed to invoke method in this class", mInvoked, METHOD_1);
- assertEquals("Set wrong number of arguments", mArgsUsed.length, 2);
- assertEquals("Failed to set first argument", mArgsUsed[0], ARG_STRING_1);
- assertEquals("Failed to set second argument", mArgsUsed[1], ARG_STRING_2);
- }
-
- @Test(expected = RuntimeException.class)
- public void testInvokeNonExistentMethod() throws Exception {
- mExecutor.invokeMethod(FAKE_METHOD, ARG_STRING_1, ARG_STRING_2);
- }
-
- @Test(expected = RuntimeException.class)
- public void testInvokeMethodTooManyArgs() throws Exception {
- mExecutor.invokeMethod(METHOD_3, ARG_STRING_1, ARG_STRING_2);
- }
-
- @Test(expected = RuntimeException.class)
- public void testInvokeMethodTooFewArgs() throws Exception {
- mExecutor.invokeMethod(METHOD_2, ARG_STRING_1);
- }
-
- @Test(expected = RuntimeException.class)
- public void testInvokeMethodIncompatibleArgs() throws Exception {
- mExecutor.invokeMethod(METHOD_8, ARG_STRING_1);
- }
-
- @Test
- public void testExecuteConditionCheckReturnValue() throws Exception {
- assertTrue("Wrong return value",
- mExecutor.executeCondition(METHOD_2, ARG_STRING_1, ARG_STRING_1));
- assertFalse("Wrong return value",
- mExecutor.executeCondition(METHOD_2, ARG_STRING_1, ARG_STRING_2));
- }
-
- @Test(expected = RuntimeException.class)
- public void testExecuteInvalidCondition() throws Exception {
- mExecutor.executeCondition(METHOD_1); // method1 does not return type boolean
- }
-
- @Test
- public void testExecuteAction() throws Exception {
- mExecutor.executeAction(METHOD_2, ARG_STRING_1, ARG_STRING_2);
- assertEquals("Failed to invoke method in this class", mInvoked, METHOD_2);
- // assert both String arguments were correctly set for method2
- assertEquals("Failed to set first argument", mArgsUsed[0], ARG_STRING_1);
- assertEquals("Failed to set second argument", mArgsUsed[1], ARG_STRING_2);
- }
-
- @Test(expected = RuntimeException.class)
- public void testExecuteActionThrowException() throws Exception {
- mExecutor.executeAction(METHOD_9);
- }
-
- @Test
- public void testExecuteActionViolateAssumption() throws Exception {
- try {
- mExecutor.executeAction(METHOD_10);
- // JUnit4 doesn't support expecting AssumptionViolatedException with "expected"
- // attribute on @Test annotation, so test using Assert.fail()
- fail("Expected assumption failure");
- } catch (AssumptionViolatedException e) {
- // expected
- }
- }
-
- public void method1() {
- mInvoked = METHOD_1;
- }
-
- // overloaded method with one arg
- public void method1(String arg1) {
- mInvoked = METHOD_1;
- mArgsUsed = new Object[]{arg1};
- }
-
- // overloaded method with two args
- public void method1(String arg1, String arg2) {
- mInvoked = METHOD_1;
- mArgsUsed = new Object[]{arg1, arg2};
- }
-
- public boolean method2(String arg1, String arg2) {
- mInvoked = METHOD_2;
- mArgsUsed = new Object[]{arg1, arg2};
- return arg1.equals(arg2);
- }
-
- public void method3(String arg1, Context arg2) {
- mInvoked = METHOD_3;
- mArgsUsed = new Object[]{arg1, arg2};
- }
-
- // Same as method3, but flipped args
- public void method4(Context arg1, String arg2) {
- mInvoked = METHOD_4;
- mArgsUsed = new Object[]{arg1, arg2};
- }
-
- public void method5(String... args) {
- mInvoked = METHOD_5;
- mArgsUsed = args;
- }
-
- public void method6(String arg1, String... moreArgs) {
- mInvoked = METHOD_6;
- List<String> allArgs = new ArrayList<>();
- allArgs.add(arg1);
- allArgs.addAll(Arrays.asList(moreArgs));
- mArgsUsed = allArgs.toArray(new String[0]);
- }
-
- public void method7(String arg1, Context arg2, String... moreArgs) {
- mInvoked = METHOD_7;
- List<Object> allArgs = new ArrayList<>();
- allArgs.add(arg1);
- allArgs.add(arg2);
- allArgs.addAll(Arrays.asList(moreArgs));
- mArgsUsed = allArgs.toArray(new Object[0]);
- }
-
- public void method8(String arg1, Integer arg2) {
- // This method should never be successfully invoked, since Integer parameter types are
- // unsupported for the BusinessLogic service
- }
-
- // throw AssertionFailedError
- @Ignore
- public void method9() throws AssertionFailedError {
- assertTrue(false);
- }
-
- // throw AssumptionViolatedException
- public void method10() throws AssumptionViolatedException {
- assumeTrue(false);
- }
-
- public static class OtherClass {
-
- public static String otherInvoked = null;
-
- public void method1() {
- otherInvoked = OTHER_METHOD_1;
- }
- }
-}
diff --git a/common/device-side/util/tests/src/com/android/compatibility/common/util/BusinessLogicTestCaseTest.java b/common/device-side/util/tests/src/com/android/compatibility/common/util/BusinessLogicTestCaseTest.java
deleted file mode 100644
index ad4acbb..0000000
--- a/common/device-side/util/tests/src/com/android/compatibility/common/util/BusinessLogicTestCaseTest.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-package com.android.compatibility.common.util;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertTrue;
-
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Tests for {@line BusinessLogicTestCase}.
- */
-@RunWith(AndroidJUnit4.class)
-public class BusinessLogicTestCaseTest {
-
- private static final String KEY_1 = "key1";
- private static final String KEY_2 = "key2";
- private static final String VALUE_1 = "value1";
- private static final String VALUE_2 = "value2";
-
- DummyTest mDummyTest;
- DummyTest mOtherDummyTest;
-
- @Before
- public void setUp() {
- mDummyTest = new DummyTest();
- mOtherDummyTest = new DummyTest();
- }
-
- @Test
- public void testMapPut() throws Exception {
- mDummyTest.mapPut("instanceMap", KEY_1, VALUE_1);
- assertTrue("mapPut failed for instanceMap", mDummyTest.instanceMap.containsKey(KEY_1));
- assertEquals("mapPut failed for instanceMap", mDummyTest.instanceMap.get(KEY_1), VALUE_1);
- assertTrue("mapPut affected wrong instance", mOtherDummyTest.instanceMap.isEmpty());
- }
-
- @Test
- public void testStaticMapPut() throws Exception {
- mDummyTest.mapPut("staticMap", KEY_2, VALUE_2);
- assertTrue("mapPut failed for staticMap", mDummyTest.staticMap.containsKey(KEY_2));
- assertEquals("mapPut failed for staticMap", mDummyTest.staticMap.get(KEY_2), VALUE_2);
- assertTrue("mapPut on static map should affect all instances",
- mOtherDummyTest.staticMap.containsKey(KEY_2));
- }
-
- public static class DummyTest extends BusinessLogicTestCase {
- public Map<String, String> instanceMap = new HashMap<>();
- public static Map<String, String> staticMap = new HashMap<>();
- }
-}
diff --git a/common/device-side/util/tests/src/com/android/compatibility/common/util/DeviceReportTest.java b/common/device-side/util/tests/src/com/android/compatibility/common/util/DeviceReportTest.java
deleted file mode 100644
index ab42b32..0000000
--- a/common/device-side/util/tests/src/com/android/compatibility/common/util/DeviceReportTest.java
+++ /dev/null
@@ -1,154 +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.compatibility.common.util;
-
-import android.app.Instrumentation;
-import android.os.Bundle;
-import android.os.Environment;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileReader;
-import java.lang.StringBuilder;
-
-import junit.framework.TestCase;
-
-import org.json.JSONObject;
-
-/**
- * Tests for {@line DeviceReportLog}.
- */
-public class DeviceReportTest extends TestCase {
-
- /**
- * A stub of {@link Instrumentation}
- */
- public class TestInstrumentation extends Instrumentation {
-
- private int mResultCode = -1;
- private Bundle mResults = null;
-
- @Override
- public void sendStatus(int resultCode, Bundle results) {
- mResultCode = resultCode;
- mResults = results;
- }
- }
-
- private static final int RESULT_CODE = 2;
- private static final String RESULT_KEY = "COMPATIBILITY_TEST_RESULT";
- private static final String TEST_MESSAGE_1 = "Foo";
- private static final double TEST_VALUE_1 = 3;
- private static final ResultType TEST_TYPE_1 = ResultType.HIGHER_BETTER;
- private static final ResultUnit TEST_UNIT_1 = ResultUnit.SCORE;
- private static final String TEST_MESSAGE_2 = "Bar";
- private static final double TEST_VALUE_2 = 5;
- private static final ResultType TEST_TYPE_2 = ResultType.LOWER_BETTER;
- private static final ResultUnit TEST_UNIT_2 = ResultUnit.COUNT;
- private static final String TEST_MESSAGE_3 = "Sample";
- private static final double TEST_VALUE_3 = 7;
- private static final ResultType TEST_TYPE_3 = ResultType.LOWER_BETTER;
- private static final ResultUnit TEST_UNIT_3 = ResultUnit.COUNT;
- private static final String TEST_MESSAGE_4 = "Message";
- private static final double TEST_VALUE_4 = 9;
- private static final ResultType TEST_TYPE_4 = ResultType.LOWER_BETTER;
- private static final ResultUnit TEST_UNIT_4 = ResultUnit.COUNT;
- private static final String REPORT_NAME_1 = "TestReport1";
- private static final String REPORT_NAME_2 = "TestReport2";
- private static final String STREAM_NAME_1 = "SampleStream1";
- private static final String STREAM_NAME_2 = "SampleStream2";
- private static final String STREAM_NAME_3 = "SampleStream3";
- private static final String STREAM_NAME_4 = "SampleStream4";
-
- public void testSubmit() throws Exception {
- DeviceReportLog log = new DeviceReportLog(REPORT_NAME_1, STREAM_NAME_1);
- log.addValue(TEST_MESSAGE_1, TEST_VALUE_1, TEST_TYPE_1, TEST_UNIT_1);
- log.setSummary(TEST_MESSAGE_2, TEST_VALUE_2, TEST_TYPE_2, TEST_UNIT_2);
- TestInstrumentation inst = new TestInstrumentation();
- log.submit(inst);
- assertEquals("Incorrect result code", RESULT_CODE, inst.mResultCode);
- assertNotNull("Bundle missing", inst.mResults);
- String metrics = inst.mResults.getString(RESULT_KEY);
- assertNotNull("Metrics missing", metrics);
- ReportLog result = ReportLog.parse(metrics);
- assertNotNull("Metrics could not be decoded", result);
- }
-
- public void testFile() throws Exception {
- assertTrue("External storage is not mounted",
- Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED));
- final File dir = new File(Environment.getExternalStorageDirectory(), "report-log-files");
- assertTrue("Report Log directory missing", dir.isDirectory() || dir.mkdirs());
-
- // Remove files from earlier possible runs.
- File[] files = dir.listFiles();
- for (File file : files) {
- file.delete();
- }
-
- TestInstrumentation inst = new TestInstrumentation();
-
- DeviceReportLog log1 = new DeviceReportLog(REPORT_NAME_1, STREAM_NAME_1);
- log1.addValue(TEST_MESSAGE_1, TEST_VALUE_1, TEST_TYPE_1, TEST_UNIT_1);
- log1.setSummary(TEST_MESSAGE_1, TEST_VALUE_1, TEST_TYPE_1, TEST_UNIT_1);
- log1.submit(inst);
-
- DeviceReportLog log2 = new DeviceReportLog(REPORT_NAME_1, STREAM_NAME_2);
- log2.addValue(TEST_MESSAGE_2, TEST_VALUE_2, TEST_TYPE_2, TEST_UNIT_2);
- log2.setSummary(TEST_MESSAGE_2, TEST_VALUE_2, TEST_TYPE_2, TEST_UNIT_2);
- log2.submit(inst);
-
- DeviceReportLog log3 = new DeviceReportLog(REPORT_NAME_2, STREAM_NAME_3);
- log3.addValue(TEST_MESSAGE_3, TEST_VALUE_3, TEST_TYPE_3, TEST_UNIT_3);
- log3.setSummary(TEST_MESSAGE_3, TEST_VALUE_3, TEST_TYPE_3, TEST_UNIT_3);
- log3.submit(inst);
-
- DeviceReportLog log4 = new DeviceReportLog(REPORT_NAME_2, STREAM_NAME_4);
- log4.addValue(TEST_MESSAGE_4, TEST_VALUE_4, TEST_TYPE_4, TEST_UNIT_4);
- log4.setSummary(TEST_MESSAGE_4, TEST_VALUE_4, TEST_TYPE_4, TEST_UNIT_4);
- log4.submit(inst);
-
- File jsonFile1 = new File(dir, REPORT_NAME_1 + ".reportlog.json");
- File jsonFile2 = new File(dir, REPORT_NAME_2 + ".reportlog.json");
- assertTrue("Report Log missing", jsonFile1.exists());
- assertTrue("Report Log missing", jsonFile2.exists());
-
- BufferedReader jsonReader = new BufferedReader(new FileReader(jsonFile1));
- StringBuilder metricsBuilder = new StringBuilder();
- String line;
- while ((line = jsonReader.readLine()) != null) {
- metricsBuilder.append(line);
- }
- String metrics = metricsBuilder.toString().trim();
- JSONObject jsonObject = new JSONObject(metrics);
- assertTrue("Incorrect metrics",
- jsonObject.getJSONObject(STREAM_NAME_1).getDouble(TEST_MESSAGE_1) == TEST_VALUE_1);
- assertTrue("Incorrect metrics",
- jsonObject.getJSONObject(STREAM_NAME_2).getDouble(TEST_MESSAGE_2) == TEST_VALUE_2);
-
- jsonReader = new BufferedReader(new FileReader(jsonFile2));
- metricsBuilder = new StringBuilder();
- while ((line = jsonReader.readLine()) != null) {
- metricsBuilder.append(line);
- }
- metrics = metricsBuilder.toString().trim();
- jsonObject = new JSONObject(metrics);
- assertTrue("Incorrect metrics",
- jsonObject.getJSONObject(STREAM_NAME_3).getDouble(TEST_MESSAGE_3) == TEST_VALUE_3);
- assertTrue("Incorrect metrics",
- jsonObject.getJSONObject(STREAM_NAME_4).getDouble(TEST_MESSAGE_4) == TEST_VALUE_4);
- }
-}
diff --git a/common/device-side/util/tests/src/com/android/compatibility/common/util/RetryRuleTest.java b/common/device-side/util/tests/src/com/android/compatibility/common/util/RetryRuleTest.java
deleted file mode 100644
index 644d95f..0000000
--- a/common/device-side/util/tests/src/com/android/compatibility/common/util/RetryRuleTest.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.testng.Assert.assertThrows;
-import static org.testng.Assert.expectThrows;
-
-import org.junit.Test;
-import org.junit.runner.Description;
-import org.junit.runner.RunWith;
-import org.junit.runners.model.Statement;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnitRunner;
-
-@RunWith(MockitoJUnitRunner.class)
-public class RetryRuleTest {
-
- private final Description mDescription = Description.createSuiteDescription("Whatever");
-
- private static final RetryableException sRetryableException =
- new RetryableException("Y U NO RETRY?");
-
- private static class RetryableStatement<T extends Exception> extends Statement {
- private final int mNumberFailures;
- private int mNumberCalls;
- private final T mException;
-
- RetryableStatement(int numberFailures, T exception) {
- mNumberFailures = numberFailures;
- mException = exception;
- }
-
- @Override
- public void evaluate() throws Throwable {
- mNumberCalls++;
- if (mNumberCalls <= mNumberFailures) {
- throw mException;
- }
- }
-
- @Override
- public String toString() {
- return "RetryableStatement: failures=" + mNumberFailures + ", calls=" + mNumberCalls;
- }
- }
-
- private @Mock Statement mMockStatement;
-
- @Test
- public void testInvalidConstructor() throws Throwable {
- assertThrows(IllegalArgumentException.class, () -> new RetryRule(-1));
- }
-
- @Test
- public void testPassOnRetryableException() throws Throwable {
- final RetryRule rule = new RetryRule(1);
- rule.apply(new RetryableStatement<RetryableException>(1, sRetryableException), mDescription)
- .evaluate();
- }
-
- @Test
- public void testPassOnRetryableExceptionWithTimeout() throws Throwable {
- final Timeout timeout = new Timeout("YOUR TIME IS GONE", 1, 2, 10);
- final RetryableException exception = new RetryableException(timeout, "Y U NO?");
-
- final RetryRule rule = new RetryRule(1);
- rule.apply(new RetryableStatement<RetryableException>(1, exception), mDescription)
- .evaluate();
-
- // Assert timeout was increased
- assertThat(timeout.ms()).isEqualTo(2);
- }
-
- @Test
- public void testFailOnRetryableException() throws Throwable {
- final RetryRule rule = new RetryRule(1);
-
- final RetryableException actualException = expectThrows(RetryableException.class,
- () -> rule.apply(new RetryableStatement<RetryableException>(2, sRetryableException),
- mDescription).evaluate());
-
- assertThat(actualException).isSameAs(sRetryableException);
- }
-
- @Test
- public void testPassWhenDisabledAndStatementPass() throws Throwable {
- final RetryRule rule = new RetryRule(0);
-
- rule.apply(mMockStatement, mDescription).evaluate();
-
- verify(mMockStatement, times(1)).evaluate();
- }
-
- @Test
- public void testFailWhenDisabledAndStatementThrowsRetryableException() throws Throwable {
- final RetryableException exception = new RetryableException("Y U NO?");
- final RetryRule rule = new RetryRule(0);
- doThrow(exception).when(mMockStatement).evaluate();
-
- final RetryableException actualException = expectThrows(RetryableException.class,
- () -> rule.apply(mMockStatement, mDescription).evaluate());
-
- assertThat(actualException).isSameAs(exception);
- verify(mMockStatement, times(1)).evaluate();
- }
-
- @Test
- public void testFailWhenDisabledAndStatementThrowsNonRetryableException() throws Throwable {
- final RuntimeException exception = new RuntimeException("Y U NO?");
- final RetryRule rule = new RetryRule(0);
- doThrow(exception).when(mMockStatement).evaluate();
-
- final RuntimeException actualException = expectThrows(RuntimeException.class,
- () -> rule.apply(mMockStatement, mDescription).evaluate());
-
- assertThat(actualException).isSameAs(exception);
- verify(mMockStatement, times(1)).evaluate();
- }
-}
diff --git a/common/device-side/util/tests/src/com/android/compatibility/common/util/SafeCleanerRuleTest.java b/common/device-side/util/tests/src/com/android/compatibility/common/util/SafeCleanerRuleTest.java
deleted file mode 100644
index a56d7b2..0000000
--- a/common/device-side/util/tests/src/com/android/compatibility/common/util/SafeCleanerRuleTest.java
+++ /dev/null
@@ -1,299 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.verify;
-import static org.testng.Assert.expectThrows;
-
-import com.android.compatibility.common.util.SafeCleanerRule.Dumper;
-
-import com.google.common.collect.ImmutableList;
-
-import org.junit.AssumptionViolatedException;
-import org.junit.Test;
-import org.junit.runner.Description;
-import org.junit.runner.RunWith;
-import org.junit.runners.model.Statement;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnitRunner;
-
-import java.util.List;
-import java.util.concurrent.Callable;
-
-@RunWith(MockitoJUnitRunner.class)
-public class SafeCleanerRuleTest {
-
- private static class FailureStatement extends Statement {
- private final Throwable mThrowable;
-
- FailureStatement(Throwable t) {
- mThrowable = t;
- }
-
- @Override
- public void evaluate() throws Throwable {
- throw mThrowable;
- }
- }
-
- private final Description mDescription = Description.createSuiteDescription("Whatever");
- private final RuntimeException mRuntimeException = new RuntimeException("D'OH!");
-
- @Mock private Dumper mDumper;
-
- // Use mocks for objects that don't throw any exception.
- @Mock private ThrowingRunnable mGoodGuyRunner1;
- @Mock private ThrowingRunnable mGoodGuyRunner2;
- @Mock private Callable<List<Throwable>> mGoodGuyExtraExceptions1;
- @Mock private Callable<List<Throwable>> mGoodGuyExtraExceptions2;
- @Mock private Statement mGoodGuyStatement;
-
- @Test
- public void testEmptyRule_testPass() throws Throwable {
- final SafeCleanerRule rule = new SafeCleanerRule();
- rule.apply(mGoodGuyStatement, mDescription).evaluate();
- }
-
- @Test
- public void testEmptyRule_testFails() throws Throwable {
- final SafeCleanerRule rule = new SafeCleanerRule();
- final Throwable actualException = expectThrows(RuntimeException.class,
- () -> rule.apply(new FailureStatement(mRuntimeException), mDescription).evaluate());
- assertThat(actualException).isSameAs(mRuntimeException);
- }
-
- @Test
- public void testEmptyRule_testFails_withDumper() throws Throwable {
- final SafeCleanerRule rule = new SafeCleanerRule().setDumper(mDumper);
- final Throwable actualException = expectThrows(RuntimeException.class,
- () -> rule.apply(new FailureStatement(mRuntimeException), mDescription).evaluate());
- assertThat(actualException).isSameAs(mRuntimeException);
- verify(mDumper).dump("Whatever", actualException);
- }
-
- @Test
- public void testOnlyTestFails() throws Throwable {
- final SafeCleanerRule rule = new SafeCleanerRule()
- .run(mGoodGuyRunner1)
- .add(mGoodGuyExtraExceptions1);
- final Throwable actualException = expectThrows(RuntimeException.class,
- () -> rule.apply(new FailureStatement(mRuntimeException), mDescription).evaluate());
- assertThat(actualException).isSameAs(mRuntimeException);
- verify(mGoodGuyRunner1).run();
- verify(mGoodGuyExtraExceptions1).call();
- }
-
- @Test
- public void testOnlyTestFails_withDumper() throws Throwable {
- final SafeCleanerRule rule = new SafeCleanerRule()
- .setDumper(mDumper)
- .run(mGoodGuyRunner1)
- .add(mGoodGuyExtraExceptions1);
- final Throwable actualException = expectThrows(RuntimeException.class,
- () -> rule.apply(new FailureStatement(mRuntimeException), mDescription).evaluate());
- assertThat(actualException).isSameAs(mRuntimeException);
- verify(mGoodGuyRunner1).run();
- verify(mGoodGuyExtraExceptions1).call();
- verify(mDumper).dump("Whatever", actualException);
- }
-
- @Test
- public void testTestPass_oneRunnerFails() throws Throwable {
- final SafeCleanerRule rule = new SafeCleanerRule()
- .run(mGoodGuyRunner1)
- .run(() -> { throw mRuntimeException; })
- .run(mGoodGuyRunner2)
- .add(mGoodGuyExtraExceptions1);
- final Throwable actualException = expectThrows(RuntimeException.class,
- () -> rule.apply(mGoodGuyStatement, mDescription).evaluate());
- assertThat(actualException).isSameAs(mRuntimeException);
- verify(mGoodGuyRunner1).run();
- verify(mGoodGuyRunner2).run();
- verify(mGoodGuyExtraExceptions1).call();
- }
-
- @Test
- public void testTestPass_oneRunnerFails_withDumper() throws Throwable {
- final SafeCleanerRule rule = new SafeCleanerRule()
- .setDumper(mDumper)
- .run(mGoodGuyRunner1)
- .run(() -> {
- throw mRuntimeException;
- })
- .run(mGoodGuyRunner2)
- .add(mGoodGuyExtraExceptions1);
- final Throwable actualException = expectThrows(RuntimeException.class,
- () -> rule.apply(mGoodGuyStatement, mDescription).evaluate());
- assertThat(actualException).isSameAs(mRuntimeException);
- verify(mGoodGuyRunner1).run();
- verify(mGoodGuyRunner2).run();
- verify(mGoodGuyExtraExceptions1).call();
- verify(mDumper).dump("Whatever", actualException);
- }
-
- @Test
- public void testTestPass_oneExtraExceptionThrownAsCallable() throws Throwable {
- final SafeCleanerRule rule = new SafeCleanerRule()
- .run(mGoodGuyRunner1)
- .add(mRuntimeException)
- .add(mGoodGuyExtraExceptions1)
- .run(mGoodGuyRunner2);
- final Throwable actualException = expectThrows(RuntimeException.class,
- () -> rule.apply(mGoodGuyStatement, mDescription).evaluate());
- assertThat(actualException).isSameAs(mRuntimeException);
- verify(mGoodGuyRunner1).run();
- verify(mGoodGuyRunner2).run();
- verify(mGoodGuyExtraExceptions1).call();
- }
-
- @Test
- public void testTestPass_oneExtraExceptionThrown() throws Throwable {
- final SafeCleanerRule rule = new SafeCleanerRule()
- .run(mGoodGuyRunner1)
- .add(() -> {
- return ImmutableList.of(mRuntimeException);
- })
- .add(mGoodGuyExtraExceptions1)
- .run(mGoodGuyRunner2);
- final Throwable actualException = expectThrows(RuntimeException.class,
- () -> rule.apply(mGoodGuyStatement, mDescription).evaluate());
- assertThat(actualException).isSameAs(mRuntimeException);
- verify(mGoodGuyRunner1).run();
- verify(mGoodGuyRunner2).run();
- verify(mGoodGuyExtraExceptions1).call();
- }
-
- @Test
- public void testTestPass_oneExtraExceptionThrown_withDumper() throws Throwable {
- final SafeCleanerRule rule = new SafeCleanerRule()
- .setDumper(mDumper)
- .run(mGoodGuyRunner1)
- .add(() -> { return ImmutableList.of(mRuntimeException); })
- .add(mGoodGuyExtraExceptions1)
- .run(mGoodGuyRunner2);
- final Throwable actualException = expectThrows(RuntimeException.class,
- () -> rule.apply(mGoodGuyStatement, mDescription).evaluate());
- assertThat(actualException).isSameAs(mRuntimeException);
- verify(mGoodGuyRunner1).run();
- verify(mGoodGuyRunner2).run();
- verify(mGoodGuyExtraExceptions1).call();
- verify(mDumper).dump("Whatever", actualException);
- }
-
- @Test
- public void testThrowTheKitchenSinkAKAEverybodyThrows() throws Throwable {
- final Exception extra1 = new Exception("1");
- final Exception extra2 = new Exception("2");
- final Exception extra3 = new Exception("3");
- final Error error1 = new Error("one");
- final Error error2 = new Error("two");
- final RuntimeException testException = new RuntimeException("TEST, Y U NO PASS?");
- final SafeCleanerRule rule = new SafeCleanerRule()
- .run(mGoodGuyRunner1)
- .add(mGoodGuyExtraExceptions1)
- .add(mRuntimeException)
- .add(() -> {
- return ImmutableList.of(extra1, extra2);
- })
- .run(() -> {
- throw error1;
- })
- .run(mGoodGuyRunner2)
- .add(() -> {
- return ImmutableList.of(extra3);
- })
- .add(mGoodGuyExtraExceptions2)
- .run(() -> {
- throw error2;
- });
-
- final SafeCleanerRule.MultipleExceptions actualException = expectThrows(
- SafeCleanerRule.MultipleExceptions.class,
- () -> rule.apply(new FailureStatement(testException), mDescription).evaluate());
- assertThat(actualException.getThrowables())
- .containsExactly(testException, mRuntimeException, error1, error2, extra1, extra2,
- extra3)
- .inOrder();
- verify(mGoodGuyRunner1).run();
- verify(mGoodGuyRunner2).run();
- verify(mGoodGuyExtraExceptions1).call();
- }
-
- @Test
- public void testIgnoreAssumptionViolatedException() throws Throwable {
- final AssumptionViolatedException ave = new AssumptionViolatedException(
- "tis an assumption violation");
- final RuntimeException testException = new RuntimeException("TEST, Y U NO PASS?");
- final SafeCleanerRule rule = new SafeCleanerRule()
- .run(mGoodGuyRunner1)
- .add(mRuntimeException)
- .run(() -> {
- throw ave;
- });
-
- final SafeCleanerRule.MultipleExceptions actualException = expectThrows(
- SafeCleanerRule.MultipleExceptions.class,
- () -> rule.apply(new FailureStatement(testException), mDescription).evaluate());
- assertThat(actualException.getThrowables())
- .containsExactly(testException, mRuntimeException)
- .inOrder();
- verify(mGoodGuyRunner1).run();
- }
-
- @Test
- public void testThrowTheKitchenSinkAKAEverybodyThrows_withDumper() throws Throwable {
- final Exception extra1 = new Exception("1");
- final Exception extra2 = new Exception("2");
- final Exception extra3 = new Exception("3");
- final Exception extra4 = new Exception("4");
- final Error error1 = new Error("one");
- final Error error2 = new Error("two");
- final RuntimeException testException = new RuntimeException("TEST, Y U NO PASS?");
- final SafeCleanerRule rule = new SafeCleanerRule()
- .setDumper(mDumper)
- .run(mGoodGuyRunner1)
- .add(mGoodGuyExtraExceptions1)
- .add(() -> {
- return ImmutableList.of(extra1, extra2);
- })
- .run(() -> {
- throw error1;
- })
- .run(mGoodGuyRunner2)
- .add(() -> { return ImmutableList.of(extra3); })
- .add(mGoodGuyExtraExceptions2)
- .run(() -> {
- throw error2;
- })
- .run(() -> {
- throw extra4;
- });
-
- final SafeCleanerRule.MultipleExceptions actualException = expectThrows(
- SafeCleanerRule.MultipleExceptions.class,
- () -> rule.apply(new FailureStatement(testException), mDescription).evaluate());
- assertThat(actualException.getThrowables())
- .containsExactly(testException, error1, error2, extra4, extra1, extra2, extra3)
- .inOrder();
- verify(mGoodGuyRunner1).run();
- verify(mGoodGuyRunner2).run();
- verify(mGoodGuyExtraExceptions1).call();
- verify(mDumper).dump("Whatever", actualException);
- }
-}
diff --git a/common/device-side/util/tests/src/com/android/compatibility/common/util/StateChangerRuleTest.java b/common/device-side/util/tests/src/com/android/compatibility/common/util/StateChangerRuleTest.java
deleted file mode 100644
index 9b1851e..0000000
--- a/common/device-side/util/tests/src/com/android/compatibility/common/util/StateChangerRuleTest.java
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-import static org.testng.Assert.assertThrows;
-import static org.testng.Assert.expectThrows;
-
-import org.junit.Test;
-import org.junit.runner.Description;
-import org.junit.runner.RunWith;
-import org.junit.runners.model.Statement;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnitRunner;
-
-@RunWith(MockitoJUnitRunner.class)
-public class StateChangerRuleTest {
-
- private final RuntimeException mRuntimeException = new RuntimeException("D'OH");
- private final Description mDescription = Description.createSuiteDescription("Whatever");
-
- @Mock
- private StateManager<String> mStateManager;
-
- @Mock
- private Statement mStatement;
-
- @Test
- public void testInvalidConstructor() {
- assertThrows(NullPointerException.class,
- () -> new StateChangerRule<Object>(null, "value"));
- }
-
- @Test
- public void testSetAndRestoreOnSuccess() throws Throwable {
- final StateChangerRule<String> rule = new StateChangerRule<>(mStateManager,
- "newValue");
- when(mStateManager.get()).thenReturn("before", "changed");
-
- rule.apply(mStatement, mDescription).evaluate();
-
- verify(mStatement, times(1)).evaluate();
- verify(mStateManager, times(2)).get(); // Needed because of verifyNoMoreInteractions()
- verify(mStateManager, times(1)).set("newValue");
- verify(mStateManager, times(1)).set("before");
- verifyNoMoreInteractions(mStateManager); // Make sure set() was not called again
- }
-
- @Test
- public void testDontSetIfSameValueOnSuccess() throws Throwable {
- final StateChangerRule<String> rule = new StateChangerRule<>(mStateManager,
- "sameValue");
- when(mStateManager.get()).thenReturn("sameValue");
-
- rule.apply(mStatement, mDescription).evaluate();
-
- verify(mStatement, times(1)).evaluate();
- verify(mStateManager, never()).set(anyString());
- }
-
- @Test
- public void testSetButDontRestoreIfSameValueOnSuccess() throws Throwable {
- final StateChangerRule<String> rule = new StateChangerRule<>(mStateManager,
- "newValue");
- when(mStateManager.get()).thenReturn("before", "before");
-
- rule.apply(mStatement, mDescription).evaluate();
-
- verify(mStatement, times(1)).evaluate();
- verify(mStateManager, times(2)).get(); // Needed because of verifyNoMoreInteractions()
- verify(mStateManager, times(1)).set("newValue");
- verifyNoMoreInteractions(mStateManager); // Make sure set() was not called again
- }
-
- @Test
- public void testDontSetButRestoreIfValueChangedOnSuccess() throws Throwable {
- final StateChangerRule<String> rule = new StateChangerRule<>(mStateManager,
- "sameValue");
- when(mStateManager.get()).thenReturn("sameValue", "changed");
-
- rule.apply(mStatement, mDescription).evaluate();
-
- verify(mStateManager, times(2)).get(); // Needed because of verifyNoMoreInteractions()
- verify(mStatement, times(1)).evaluate();
- verify(mStateManager, times(1)).set("sameValue");
- verifyNoMoreInteractions(mStateManager); // Make sure set() was not called again
- }
-
- @Test
- public void testSetAndRestoreOnFailure() throws Throwable {
- final StateChangerRule<String> rule = new StateChangerRule<>(mStateManager,
- "newValue");
- when(mStateManager.get()).thenReturn("before", "changed");
- doThrow(mRuntimeException).when(mStatement).evaluate();
-
- final RuntimeException actualException = expectThrows(RuntimeException.class,
- () -> rule.apply(mStatement, mDescription).evaluate());
- assertThat(actualException).isSameAs(mRuntimeException);
-
- verify(mStateManager, times(2)).get(); // Needed because of verifyNoMoreInteractions()
- verify(mStateManager, times(1)).set("newValue");
- verify(mStateManager, times(1)).set("before");
- verifyNoMoreInteractions(mStateManager); // Make sure set() was not called again
- }
-
- @Test
- public void testDontSetIfSameValueOnFailure() throws Throwable {
- final StateChangerRule<String> rule = new StateChangerRule<>(mStateManager,
- "sameValue");
- when(mStateManager.get()).thenReturn("sameValue");
- doThrow(mRuntimeException).when(mStatement).evaluate();
-
- final RuntimeException actualException = expectThrows(RuntimeException.class,
- () -> rule.apply(mStatement, mDescription).evaluate());
- assertThat(actualException).isSameAs(mRuntimeException);
-
- verify(mStateManager, never()).set(anyString());
- }
-
- @Test
- public void testSetButDontRestoreIfSameValueOnFailure() throws Throwable {
- final StateChangerRule<String> rule = new StateChangerRule<>(mStateManager,
- "newValue");
- when(mStateManager.get()).thenReturn("before", "before");
- doThrow(mRuntimeException).when(mStatement).evaluate();
-
- final RuntimeException actualException = expectThrows(RuntimeException.class,
- () -> rule.apply(mStatement, mDescription).evaluate());
- assertThat(actualException).isSameAs(mRuntimeException);
-
- verify(mStateManager, times(2)).get(); // Needed because of verifyNoMoreInteractions()
- verify(mStateManager, times(1)).set("newValue");
- verifyNoMoreInteractions(mStateManager); // Make sure set() was not called again
- }
-
- @Test
- public void testDontSetButRestoreIfValueChangedOnFailure() throws Throwable {
- final StateChangerRule<String> rule = new StateChangerRule<>(mStateManager,
- "sameValue");
- when(mStateManager.get()).thenReturn("sameValue", "changed");
- doThrow(mRuntimeException).when(mStatement).evaluate();
-
- final RuntimeException actualException = expectThrows(RuntimeException.class,
- () -> rule.apply(mStatement, mDescription).evaluate());
- assertThat(actualException).isSameAs(mRuntimeException);
-
- verify(mStateManager, times(2)).get(); // Needed because of verifyNoMoreInteractions()
- verify(mStateManager, times(1)).set("sameValue");
- verifyNoMoreInteractions(mStateManager); // Make sure set() was not called again
- }
-}
diff --git a/common/device-side/util/tests/src/com/android/compatibility/common/util/StateKeeperRuleTest.java b/common/device-side/util/tests/src/com/android/compatibility/common/util/StateKeeperRuleTest.java
deleted file mode 100644
index 4599aca..0000000
--- a/common/device-side/util/tests/src/com/android/compatibility/common/util/StateKeeperRuleTest.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-import static org.testng.Assert.assertThrows;
-import static org.testng.Assert.expectThrows;
-
-import org.junit.Test;
-import org.junit.runner.Description;
-import org.junit.runner.RunWith;
-import org.junit.runners.model.Statement;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnitRunner;
-
-@RunWith(MockitoJUnitRunner.class)
-public class StateKeeperRuleTest {
-
- private final RuntimeException mRuntimeException = new RuntimeException("D'OH");
- private final Description mDescription = Description.createSuiteDescription("Whatever");
-
- @Mock
- private StateManager<String> mStateManager;
-
- @Mock
- private Statement mStatement;
-
- @Test
- public void testInvalidConstructor() {
- assertThrows(NullPointerException.class, () -> new StateKeeperRule<Object>(null));
- }
-
- @Test
- public void testRestoreOnSuccess() throws Throwable {
- final StateKeeperRule<String> rule = new StateKeeperRule<>(mStateManager);
- when(mStateManager.get()).thenReturn("before", "changed");
-
- rule.apply(mStatement, mDescription).evaluate();
-
- verify(mStatement, times(1)).evaluate();
- verify(mStateManager, times(2)).get(); // Needed because of verifyNoMoreInteractions()
- verify(mStateManager, times(1)).set("before");
- verifyNoMoreInteractions(mStateManager); // Make sure set() was not called again
- }
-
- @Test
- public void testRestoreOnFailure() throws Throwable {
- final StateKeeperRule<String> rule = new StateKeeperRule<>(mStateManager);
- when(mStateManager.get()).thenReturn("before", "changed");
- doThrow(mRuntimeException).when(mStatement).evaluate();
-
- final RuntimeException actualException = expectThrows(RuntimeException.class,
- () -> rule.apply(mStatement, mDescription).evaluate());
-
- assertThat(actualException).isSameAs(mRuntimeException);
- verify(mStateManager, times(2)).get(); // Needed because of verifyNoMoreInteractions()
- verify(mStateManager, times(1)).set("before");
- verifyNoMoreInteractions(mStateManager); // Make sure set() was not called again
- }
-
- @Test
- public void testDoNotRestoreWhenNotChanged() throws Throwable {
- final StateKeeperRule<String> rule = new StateKeeperRule<>(mStateManager);
- when(mStateManager.get()).thenReturn("not_changed");
-
- rule.apply(mStatement, mDescription).evaluate();
-
- verify(mStatement, times(1)).evaluate();
- verify(mStateManager, never()).set(anyString());
- }
-
- @Test
- public void testDoNotRestoreOnFailure() throws Throwable {
- final StateKeeperRule<String> rule = new StateKeeperRule<>(mStateManager);
- when(mStateManager.get()).thenReturn("not_changed");
- doThrow(mRuntimeException).when(mStatement).evaluate();
-
- final RuntimeException actualException = expectThrows(RuntimeException.class,
- () -> rule.apply(mStatement, mDescription).evaluate());
-
- assertThat(actualException).isSameAs(mRuntimeException);
-
- verify(mStateManager, never()).set(anyString());
- }
-}
diff --git a/common/device-side/util/tests/src/com/android/compatibility/common/util/TimeoutTest.java b/common/device-side/util/tests/src/com/android/compatibility/common/util/TimeoutTest.java
deleted file mode 100644
index 8992d18..0000000
--- a/common/device-side/util/tests/src/com/android/compatibility/common/util/TimeoutTest.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.compatibility.common.util;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.when;
-import static org.testng.Assert.assertThrows;
-import static org.testng.Assert.expectThrows;
-
-import android.os.SystemClock;
-
-import com.android.compatibility.common.util.Timeout.Sleeper;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnitRunner;
-
-import java.util.concurrent.Callable;
-
-@RunWith(MockitoJUnitRunner.class)
-public class TimeoutTest {
-
- private static final String NAME = "TIME, Y U NO OUT?";
- private static final String DESC = "something";
-
- @Mock
- private Callable<Object> mJob;
-
- private final MySleeper mSleeper = new MySleeper();
-
- @Test
- public void testInvalidConstructor() {
- // Invalid name
- assertThrows(IllegalArgumentException.class, ()-> new Timeout(null, 1, 2, 2));
- assertThrows(IllegalArgumentException.class, ()-> new Timeout("", 1, 2, 2));
- // Invalid initial value
- assertThrows(IllegalArgumentException.class, ()-> new Timeout(NAME, -1, 2, 2));
- assertThrows(IllegalArgumentException.class, ()-> new Timeout(NAME, 0, 2, 2));
- // Invalid multiplier
- assertThrows(IllegalArgumentException.class, ()-> new Timeout(NAME, 1, -1, 2));
- assertThrows(IllegalArgumentException.class, ()-> new Timeout(NAME, 1, 0, 2));
- assertThrows(IllegalArgumentException.class, ()-> new Timeout(NAME, 1, 1, 2));
- // Invalid max value
- assertThrows(IllegalArgumentException.class, ()-> new Timeout(NAME, 1, 2, -1));
- assertThrows(IllegalArgumentException.class, ()-> new Timeout(NAME, 1, 2, 0));
- // Max value cannot be less than initial
- assertThrows(IllegalArgumentException.class, ()-> new Timeout(NAME, 2, 2, 1));
- }
-
- @Test
- public void testGetters() {
- final Timeout timeout = new Timeout(NAME, 1, 2, 5);
- assertThat(timeout.ms()).isEqualTo(1);
- assertFloat(timeout.getMultiplier(), 2);
- assertThat(timeout.getMaxValue()).isEqualTo(5);
- assertThat(timeout.getName()).isEqualTo(NAME);
- }
-
- @Test
- public void testIncrease() {
- final Timeout timeout = new Timeout(NAME, 1, 2, 5);
- // Pre-maximum
- assertThat(timeout.increase()).isEqualTo(1);
- assertThat(timeout.ms()).isEqualTo(2);
- assertThat(timeout.increase()).isEqualTo(2);
- assertThat(timeout.ms()).isEqualTo(4);
- // Post-maximum
- assertThat(timeout.increase()).isEqualTo(4);
- assertThat(timeout.ms()).isEqualTo(5);
- assertThat(timeout.increase()).isEqualTo(5);
- assertThat(timeout.ms()).isEqualTo(5);
- }
-
- @Test
- public void testRun_invalidArgs() {
- final Timeout timeout = new Timeout(NAME, 1, 2, 5);
- // Invalid description
- assertThrows(IllegalArgumentException.class, ()-> timeout.run(null, mJob));
- assertThrows(IllegalArgumentException.class, ()-> timeout.run("", mJob));
- // Invalid max attempts
- assertThrows(IllegalArgumentException.class, ()-> timeout.run(DESC, -1, mJob));
- assertThrows(IllegalArgumentException.class, ()-> timeout.run(DESC, 0, mJob));
- // Invalid job
- assertThrows(IllegalArgumentException.class, ()-> timeout.run(DESC, null));
- }
-
- @Test
- public void testRun_successOnFirstAttempt() throws Exception {
- final Timeout timeout = new Timeout(mSleeper, NAME, 100, 2, 500);
- final Object result = new Object();
- when(mJob.call()).thenReturn(result);
- assertThat(timeout.run(DESC, 1, mJob)).isSameAs(result);
- assertThat(mSleeper.totalSleepingTime).isEqualTo(0);
- }
-
- @Test
- public void testRun_successOnSecondAttempt() throws Exception {
- final Timeout timeout = new Timeout(mSleeper, NAME, 100, 2, 500);
- final Object result = new Object();
- when(mJob.call()).thenReturn((Object) null, result);
- assertThat(timeout.run(DESC, 10, mJob)).isSameAs(result);
- assertThat(mSleeper.totalSleepingTime).isEqualTo(10);
- }
-
- @Test
- public void testRun_allAttemptsFailed() throws Exception {
- final Timeout timeout = new Timeout(mSleeper, NAME, 100, 2, 500);
- final RetryableException e = expectThrows(RetryableException.class,
- () -> timeout.run(DESC, 10, mJob));
- assertThat(e.getMessage()).contains(DESC);
- assertThat(e.getTimeout()).isSameAs(timeout);
- assertThat(mSleeper.totalSleepingTime).isEqualTo(100);
- }
-
- private static final class MySleeper implements Sleeper {
- public long totalSleepingTime;
-
- @Override
- public void sleep(long napTimeMs) {
- // We still need to sleep, as the retry is based on ellapsed time. We could use a
- // Mockito spy, but let's keep it simple
- SystemClock.sleep(napTimeMs);
- totalSleepingTime += napTimeMs;
- }
- }
-
- public static void assertFloat(float actualValue, float expectedValue) {
- assertThat(actualValue).isWithin(1.0e-10f).of(expectedValue);
- }
-}
diff --git a/hostsidetests/angle/app/common/src/com/android/angleIntegrationTest/common/AngleIntegrationTestActivity.java b/hostsidetests/angle/app/common/src/com/android/angleIntegrationTest/common/AngleIntegrationTestActivity.java
deleted file mode 100644
index e167176..0000000
--- a/hostsidetests/angle/app/common/src/com/android/angleIntegrationTest/common/AngleIntegrationTestActivity.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.angleIntegrationTest.common;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.util.Log;
-
-import java.lang.Override;
-
-public class AngleIntegrationTestActivity extends Activity {
-
- private final String TAG = this.getClass().getSimpleName();
-
- private GlesView mGlesView;
-
- @Override
- public void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- mGlesView = new GlesView(this);
- setContentView(mGlesView);
-
- Log.i(TAG, "ANGLE Manifest activity complete");
- }
-
- public GlesView getGlesView() {
- return mGlesView;
- }
-}
diff --git a/hostsidetests/angle/app/common/src/com/android/angleIntegrationTest/common/GlesView.java b/hostsidetests/angle/app/common/src/com/android/angleIntegrationTest/common/GlesView.java
index d0fbe58..f1c053f 100644
--- a/hostsidetests/angle/app/common/src/com/android/angleIntegrationTest/common/GlesView.java
+++ b/hostsidetests/angle/app/common/src/com/android/angleIntegrationTest/common/GlesView.java
@@ -19,14 +19,10 @@
import static javax.microedition.khronos.egl.EGL10.EGL_STENCIL_SIZE;
import android.annotation.TargetApi;
-import android.content.Context;
import android.opengl.EGL14;
import android.opengl.GLES20;
-import android.opengl.GLSurfaceView;
import android.os.Build.VERSION_CODES;
import android.util.Log;
-import android.view.SurfaceHolder;
-import android.view.SurfaceView;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
@@ -38,28 +34,21 @@
*/
@TargetApi(VERSION_CODES.GINGERBREAD)
-public class GlesView extends SurfaceView implements SurfaceHolder.Callback2 {
+public class GlesView {
private static final EGL10 EGL = (EGL10) EGLContext.getEGL();
- private EGLConfig eglConfig;
- private EGLDisplay display;
- private SurfaceHolder mSurfaceHolder;
private String mRenderer = "";
private final String TAG = this.getClass().getSimpleName();
- public GlesView(Context context) {
- super(context);
- this.setWillNotDraw(false);
- getHolder().addCallback(this);
- createEGL();
- getHolder().getSurface();
+ public GlesView() {
+ createEGL();
}
@TargetApi(VERSION_CODES.JELLY_BEAN_MR1)
private void createEGL() {
int[] version = new int[2];
- display = EGL.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
+ EGLDisplay display = EGL.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
EGL.eglInitialize(display, version);
int[] numConfigs = new int[1];
@@ -68,7 +57,7 @@
EGL.eglGetConfigs(display, allConfigs, numConfigs[0], numConfigs);
int[] configAttrib =
- new int[] {
+ new int[]{
EGL10.EGL_RENDERABLE_TYPE,
EGL14.EGL_OPENGL_ES2_BIT,
EGL10.EGL_SURFACE_TYPE,
@@ -90,6 +79,7 @@
EGLConfig[] selectedConfig = new EGLConfig[1];
EGL.eglChooseConfig(display, configAttrib, selectedConfig, 1, numConfigs);
+ EGLConfig eglConfig;
if (selectedConfig[0] != null) {
eglConfig = selectedConfig[0];
Log.i(TAG, "Found matching EGL config");
@@ -97,33 +87,35 @@
Log.e(TAG, "Could not find matching EGL config");
throw new RuntimeException("No Matching EGL Config Found");
}
- }
- @TargetApi(VERSION_CODES.JELLY_BEAN_MR1)
- @Override
- public void surfaceCreated(SurfaceHolder holder) {
- EGLSurface surface = EGL.eglCreateWindowSurface(display, eglConfig, this, null);
- int[] contextAttribs = new int[] {EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE};
- EGLContext context = EGL
- .eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, contextAttribs);
+ EGLSurface surface = EGL.eglCreatePbufferSurface(display, eglConfig, null);
+ int[] contextAttribs = new int[]{EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE};
+ EGLContext context = EGL
+ .eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, contextAttribs);
EGL.eglMakeCurrent(display, surface, surface, context);
Log.i(TAG, "CTS ANGLE Test :: GLES GL_VENDOR : " + GLES20.glGetString(GLES20.GL_VENDOR));
Log.i(TAG, "CTS ANGLE Test :: GLES GL_VERSION : " + GLES20.glGetString(GLES20.GL_VERSION));
- Log.i(TAG, "CTS ANGLE Test :: GLES GL_RENDERER : " + GLES20.glGetString(GLES20.GL_RENDERER));
+ Log.i(TAG,
+ "CTS ANGLE Test :: GLES GL_RENDERER : " + GLES20.glGetString(GLES20.GL_RENDERER));
mRenderer = GLES20.glGetString(GLES20.GL_RENDERER);
}
- @Override
- public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}
-
- @Override
- public void surfaceDestroyed(SurfaceHolder holder) {}
-
- @Override
- public void surfaceRedrawNeeded(SurfaceHolder holder) {}
-
public String getRenderer() {
return mRenderer;
}
+
+ public boolean validateDeveloperOption(boolean angleEnabled) {
+ if (angleEnabled) {
+ if (!mRenderer.toLowerCase().contains("angle")) {
+ return false;
+ }
+ } else {
+ if (mRenderer.toLowerCase().contains("angle")) {
+ return false;
+ }
+ }
+
+ return true;
+ }
}
diff --git a/hostsidetests/angle/app/driverTest/src/com/android/angleIntegrationTest/driverTest/AngleDriverTestActivity.java b/hostsidetests/angle/app/driverTest/src/com/android/angleIntegrationTest/driverTest/AngleDriverTestActivity.java
index bb98488..450461a 100644
--- a/hostsidetests/angle/app/driverTest/src/com/android/angleIntegrationTest/driverTest/AngleDriverTestActivity.java
+++ b/hostsidetests/angle/app/driverTest/src/com/android/angleIntegrationTest/driverTest/AngleDriverTestActivity.java
@@ -18,13 +18,10 @@
import static org.junit.Assert.fail;
-import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
-import com.android.angleIntegrationTest.common.AngleIntegrationTestActivity;
import com.android.angleIntegrationTest.common.GlesView;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -33,29 +30,18 @@
private final String TAG = this.getClass().getSimpleName();
- @Rule
- public ActivityTestRule<AngleIntegrationTestActivity> rule =
- new ActivityTestRule<>(AngleIntegrationTestActivity.class);
-
private void validateDeveloperOption(boolean angleEnabled) throws Exception {
- AngleIntegrationTestActivity activity = rule.getActivity();
- GlesView glesView = activity.getGlesView();
- String renderer = glesView.getRenderer();
+ GlesView glesView = new GlesView();
- while(renderer.length() == 0) {
- renderer = glesView.getRenderer();
- }
-
- if (angleEnabled) {
- if (!renderer.toLowerCase().contains("ANGLE".toLowerCase())) {
+ if (!glesView.validateDeveloperOption(angleEnabled)) {
+ if (angleEnabled) {
+ String renderer = glesView.getRenderer();
fail("Failure - ANGLE was not loaded: '" + renderer + "'");
- }
- } else {
- if (renderer.toLowerCase().contains("ANGLE".toLowerCase())) {
+ } else {
+ String renderer = glesView.getRenderer();
fail("Failure - ANGLE was loaded: '" + renderer + "'");
}
}
-
}
@Test
diff --git a/hostsidetests/angle/app/driverTestSecondary/src/com/android/angleIntegrationTest/driverTestSecondary/AngleDriverTestActivity.java b/hostsidetests/angle/app/driverTestSecondary/src/com/android/angleIntegrationTest/driverTestSecondary/AngleDriverTestActivity.java
index ce7e5e8..7b25c39 100644
--- a/hostsidetests/angle/app/driverTestSecondary/src/com/android/angleIntegrationTest/driverTestSecondary/AngleDriverTestActivity.java
+++ b/hostsidetests/angle/app/driverTestSecondary/src/com/android/angleIntegrationTest/driverTestSecondary/AngleDriverTestActivity.java
@@ -18,13 +18,10 @@
import static org.junit.Assert.fail;
-import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
-import com.android.angleIntegrationTest.common.AngleIntegrationTestActivity;
import com.android.angleIntegrationTest.common.GlesView;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -33,25 +30,18 @@
private final String TAG = this.getClass().getSimpleName();
- @Rule
- public ActivityTestRule<AngleIntegrationTestActivity> rule =
- new ActivityTestRule<>(AngleIntegrationTestActivity.class);
-
private void validateDeveloperOption(boolean angleEnabled) throws Exception {
- AngleIntegrationTestActivity activity = rule.getActivity();
- GlesView glesView = activity.getGlesView();
- String renderer = glesView.getRenderer();
+ GlesView glesView = new GlesView();
- if (angleEnabled) {
- if (!renderer.toLowerCase().contains("ANGLE".toLowerCase())) {
+ if (!glesView.validateDeveloperOption(angleEnabled)) {
+ if (angleEnabled) {
+ String renderer = glesView.getRenderer();
fail("Failure - ANGLE was not loaded: '" + renderer + "'");
- }
- } else {
- if (renderer.toLowerCase().contains("ANGLE".toLowerCase())) {
+ } else {
+ String renderer = glesView.getRenderer();
fail("Failure - ANGLE was loaded: '" + renderer + "'");
}
}
-
}
@Test
diff --git a/hostsidetests/angle/src/android/angle/cts/CtsAngleCommon.java b/hostsidetests/angle/src/android/angle/cts/CtsAngleCommon.java
index 85841af..c576253 100644
--- a/hostsidetests/angle/src/android/angle/cts/CtsAngleCommon.java
+++ b/hostsidetests/angle/src/android/angle/cts/CtsAngleCommon.java
@@ -24,6 +24,11 @@
class CtsAngleCommon {
private static final int TEST_WAIT_TIME_MS = 1000;
+ // General
+ static final int NUM_ATTEMPTS = 5;
+ static final int APPLY_SLEEP_MSEC = 500;
+ static final int REATTEMPT_SLEEP_MSEC = 5000;
+
// Settings.Global
static final String SETTINGS_GLOBAL_ALL_USE_ANGLE = "angle_gl_driver_all_angle";
static final String SETTINGS_GLOBAL_DRIVER_PKGS = "angle_gl_driver_selection_pkgs";
diff --git a/hostsidetests/angle/src/android/angle/cts/CtsAngleDeveloperOptionHostTest.java b/hostsidetests/angle/src/android/angle/cts/CtsAngleDeveloperOptionHostTest.java
index 5373a3b..5cff73c 100644
--- a/hostsidetests/angle/src/android/angle/cts/CtsAngleDeveloperOptionHostTest.java
+++ b/hostsidetests/angle/src/android/angle/cts/CtsAngleDeveloperOptionHostTest.java
@@ -45,12 +45,38 @@
setGlobalSetting(getDevice(), SETTINGS_GLOBAL_DRIVER_PKGS, pkgName);
setGlobalSetting(getDevice(), SETTINGS_GLOBAL_DRIVER_VALUES, driverValue);
+ // SETTINGS_GLOBAL_DRIVER_PKGS
+ for (int i = 0; i < NUM_ATTEMPTS; i++)
+ {
+ setGlobalSetting(getDevice(), SETTINGS_GLOBAL_DRIVER_PKGS, pkgName);
+ Thread.sleep(APPLY_SLEEP_MSEC);
+ String devOption = getGlobalSetting(getDevice(), SETTINGS_GLOBAL_DRIVER_PKGS);
+ if (devOption.equals(pkgName))
+ {
+ break;
+ }
+ Thread.sleep(REATTEMPT_SLEEP_MSEC);
+ }
+
String devOption = getGlobalSetting(getDevice(), SETTINGS_GLOBAL_DRIVER_PKGS);
Assert.assertEquals(
"Developer option '" + SETTINGS_GLOBAL_DRIVER_PKGS +
"' was not set correctly: '" + devOption + "'",
pkgName, devOption);
+ // SETTINGS_GLOBAL_DRIVER_VALUES
+ for (int i = 0; i < NUM_ATTEMPTS; i++)
+ {
+ setGlobalSetting(getDevice(), SETTINGS_GLOBAL_DRIVER_VALUES, driverValue);
+ Thread.sleep(APPLY_SLEEP_MSEC);
+ devOption = getGlobalSetting(getDevice(), SETTINGS_GLOBAL_DRIVER_VALUES);
+ if (devOption.equals(driverValue))
+ {
+ break;
+ }
+ Thread.sleep(REATTEMPT_SLEEP_MSEC);
+ }
+
devOption = getGlobalSetting(getDevice(), SETTINGS_GLOBAL_DRIVER_VALUES);
Assert.assertEquals(
"Developer option '" + SETTINGS_GLOBAL_DRIVER_VALUES +
@@ -72,6 +98,18 @@
sDriverTestMethodMap.get(driver));
}
+ private void installApp(String appName) throws Exception {
+ for (int i = 0; i < NUM_ATTEMPTS; i++)
+ {
+ try {
+ installPackage(appName);
+ return;
+ } catch(Exception e) {
+ Thread.sleep(REATTEMPT_SLEEP_MSEC);
+ }
+ }
+ }
+
@Before
public void setUp() throws Exception {
clearSettings(getDevice());
@@ -92,8 +130,8 @@
public void testEnableAngleForAll() throws Exception {
Assume.assumeTrue(isAngleLoadable(getDevice()));
- installPackage(ANGLE_DRIVER_TEST_APP);
- installPackage(ANGLE_DRIVER_TEST_SEC_APP);
+ installApp(ANGLE_DRIVER_TEST_APP);
+ installApp(ANGLE_DRIVER_TEST_SEC_APP);
setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG,
sDriverGlobalSettingMap.get(OpenGlDriverChoice.DEFAULT));
@@ -117,7 +155,7 @@
public void testUseDefaultDriver() throws Exception {
Assume.assumeTrue(isAngleLoadable(getDevice()));
- installPackage(ANGLE_DRIVER_TEST_APP);
+ installApp(ANGLE_DRIVER_TEST_APP);
setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG,
sDriverGlobalSettingMap.get(OpenGlDriverChoice.DEFAULT));
@@ -134,7 +172,7 @@
public void testUseAngleDriver() throws Exception {
Assume.assumeTrue(isAngleLoadable(getDevice()));
- installPackage(ANGLE_DRIVER_TEST_APP);
+ installApp(ANGLE_DRIVER_TEST_APP);
setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG,
sDriverGlobalSettingMap.get(OpenGlDriverChoice.ANGLE));
@@ -151,7 +189,7 @@
public void testUseNativeDriver() throws Exception {
Assume.assumeTrue(isAngleLoadable(getDevice()));
- installPackage(ANGLE_DRIVER_TEST_APP);
+ installApp(ANGLE_DRIVER_TEST_APP);
setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG,
sDriverGlobalSettingMap.get(OpenGlDriverChoice.NATIVE));
@@ -168,8 +206,8 @@
public void testSettingsLengthMismatch() throws Exception {
Assume.assumeTrue(isAngleLoadable(getDevice()));
- installPackage(ANGLE_DRIVER_TEST_APP);
- installPackage(ANGLE_DRIVER_TEST_SEC_APP);
+ installApp(ANGLE_DRIVER_TEST_APP);
+ installApp(ANGLE_DRIVER_TEST_SEC_APP);
setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG + "," +
ANGLE_DRIVER_TEST_SEC_PKG,
@@ -191,7 +229,7 @@
public void testUseInvalidDriver() throws Exception {
Assume.assumeTrue(isAngleLoadable(getDevice()));
- installPackage(ANGLE_DRIVER_TEST_APP);
+ installApp(ANGLE_DRIVER_TEST_APP);
setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG, "timtim");
@@ -207,7 +245,7 @@
public void testUpdateDriverValues() throws Exception {
Assume.assumeTrue(isAngleLoadable(getDevice()));
- installPackage(ANGLE_DRIVER_TEST_APP);
+ installApp(ANGLE_DRIVER_TEST_APP);
for (OpenGlDriverChoice firstDriver : OpenGlDriverChoice.values()) {
for (OpenGlDriverChoice secondDriver : OpenGlDriverChoice.values()) {
@@ -229,8 +267,8 @@
public void testMultipleDevOptionsAngleNative() throws Exception {
Assume.assumeTrue(isAngleLoadable(getDevice()));
- installPackage(ANGLE_DRIVER_TEST_APP);
- installPackage(ANGLE_DRIVER_TEST_SEC_APP);
+ installApp(ANGLE_DRIVER_TEST_APP);
+ installApp(ANGLE_DRIVER_TEST_SEC_APP);
setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG + "," +
ANGLE_DRIVER_TEST_SEC_PKG,
@@ -253,8 +291,8 @@
public void testMultipleUpdateDriverValues() throws Exception {
Assume.assumeTrue(isAngleLoadable(getDevice()));
- installPackage(ANGLE_DRIVER_TEST_APP);
- installPackage(ANGLE_DRIVER_TEST_SEC_APP);
+ installApp(ANGLE_DRIVER_TEST_APP);
+ installApp(ANGLE_DRIVER_TEST_SEC_APP);
// Set the first PKG to always use ANGLE
setAndValidatePkgDriver(ANGLE_DRIVER_TEST_PKG, OpenGlDriverChoice.ANGLE);
@@ -316,7 +354,7 @@
Assume.assumeTrue(isAngleLoadable(getDevice()));
// Install the package so the setting isn't removed because the package isn't present.
- installPackage(ANGLE_DRIVER_TEST_APP);
+ installApp(ANGLE_DRIVER_TEST_APP);
setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG,
sDriverGlobalSettingMap.get(OpenGlDriverChoice.DEFAULT));
@@ -376,8 +414,8 @@
public void testMultipleDevOptionsAngleDefault() throws Exception {
Assume.assumeTrue(isAngleLoadable(getDevice()));
- installPackage(ANGLE_DRIVER_TEST_APP);
- installPackage(ANGLE_DRIVER_TEST_SEC_APP);
+ installApp(ANGLE_DRIVER_TEST_APP);
+ installApp(ANGLE_DRIVER_TEST_SEC_APP);
setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG + "," + ANGLE_DRIVER_TEST_SEC_PKG,
sDriverGlobalSettingMap.get(OpenGlDriverChoice.ANGLE) + "," +
@@ -408,7 +446,7 @@
public void testMultipleDevOptionsAngleNativeUninstall() throws Exception {
Assume.assumeTrue(isAngleLoadable(getDevice()));
- installPackage(ANGLE_DRIVER_TEST_SEC_APP);
+ installApp(ANGLE_DRIVER_TEST_SEC_APP);
setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG + "," + ANGLE_DRIVER_TEST_SEC_PKG,
sDriverGlobalSettingMap.get(OpenGlDriverChoice.ANGLE) + "," +
diff --git a/hostsidetests/angle/src/android/angle/cts/CtsAngleRulesFileTest.java b/hostsidetests/angle/src/android/angle/cts/CtsAngleRulesFileTest.java
index 596630b..50f55b0 100644
--- a/hostsidetests/angle/src/android/angle/cts/CtsAngleRulesFileTest.java
+++ b/hostsidetests/angle/src/android/angle/cts/CtsAngleRulesFileTest.java
@@ -61,22 +61,42 @@
setProperty(getDevice(), PROPERTY_TEMP_RULES_FILE, DEVICE_TEMP_RULES_FILE_PATH);
}
+ private void setAndValidateAngleDevOptionWhitelist(String whiteList) throws Exception {
+ // SETTINGS_GLOBAL_WHITELIST
+ for (int i = 0; i < NUM_ATTEMPTS; i++)
+ {
+ setGlobalSetting(getDevice(), SETTINGS_GLOBAL_WHITELIST, whiteList);
+ Thread.sleep(APPLY_SLEEP_MSEC);
+ String devOption = getGlobalSetting(getDevice(), SETTINGS_GLOBAL_WHITELIST);
+ if (devOption.equals(whiteList))
+ {
+ break;
+ }
+ Thread.sleep(REATTEMPT_SLEEP_MSEC);
+ }
+
+ String devOption = getGlobalSetting(getDevice(), SETTINGS_GLOBAL_WHITELIST);
+ Assert.assertEquals(
+ "Developer option '" + SETTINGS_GLOBAL_WHITELIST +
+ "' was not set correctly: '" + devOption + "'",
+ whiteList, devOption);
+ }
+
@Before
public void setUp() throws Exception {
clearSettings(getDevice());
mWhiteList = getGlobalSetting(getDevice(), SETTINGS_GLOBAL_WHITELIST);
- // Application must be whitelisted to load temp rules
- setGlobalSetting(getDevice(), SETTINGS_GLOBAL_WHITELIST,
- ANGLE_DRIVER_TEST_PKG + "," + ANGLE_DRIVER_TEST_SEC_PKG);
+ final String whitelist = ANGLE_DRIVER_TEST_PKG + "," + ANGLE_DRIVER_TEST_SEC_PKG;
+ setAndValidateAngleDevOptionWhitelist(whitelist);
}
@After
public void tearDown() throws Exception {
clearSettings(getDevice());
- setGlobalSetting(getDevice(), SETTINGS_GLOBAL_WHITELIST, mWhiteList);
+ setAndValidateAngleDevOptionWhitelist(mWhiteList);
FileUtil.deleteFile(mRulesFile);
}
diff --git a/hostsidetests/appbinding/hostside/OWNERS b/hostsidetests/appbinding/hostside/OWNERS
new file mode 100644
index 0000000..77d350b
--- /dev/null
+++ b/hostsidetests/appbinding/hostside/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 533114
+omakoto@google.com
+yamasani@google.com
diff --git a/hostsidetests/appsecurity/OWNERS b/hostsidetests/appsecurity/OWNERS
index f86fd96..ea12403 100644
--- a/hostsidetests/appsecurity/OWNERS
+++ b/hostsidetests/appsecurity/OWNERS
@@ -25,8 +25,10 @@
per-file PermissionsHostTest.java = moltmann@google.com
per-file PkgInstallSignatureVerificationTest.java = cbrubaker@google.com
per-file PrivilegedUpdateTests.java = toddke@google.com
+per-file ReviewPermissionHelper = moltmann@google.com
+per-file RequestsOnlyCalendarApp22.java = moltmann@google.com
per-file ScopedDirectoryAccessTest.java = jsharkey@google.com
per-file SplitTests.java = toddke@google.com
per-file StorageHostTest.java = jsharkey@google.com
per-file UseEmbeddedDexTest.java = victorhsieh@google.com
-per-file UsePermission*.java = moltmann@google.com
+per-file UsePermission*.java = moltmann@google.com
\ No newline at end of file
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/ExceptionUtils.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/ExceptionUtils.java
new file mode 100644
index 0000000..437f2aa
--- /dev/null
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/ExceptionUtils.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.appsecurity.cts;
+
+/**
+ * Utilities to deal with exceptions
+ */
+public class ExceptionUtils {
+ private ExceptionUtils() {}
+
+ /**
+ * Gets the root {@link Throwable#getCause() cause} of {@code t}
+ */
+ public static Throwable getRootCause(Throwable t) {
+ while (t.getCause() != null) t = t.getCause();
+ return t;
+ }
+
+ /**
+ * Appends {@code cause} at the end of the causal chain of {@code t}
+ *
+ * @return {@code t} for convenience
+ */
+ public static <E extends Throwable> E appendCause(E t, Throwable cause) {
+ if (cause != null) {
+ getRootCause(t).initCause(cause);
+ }
+ return t;
+ }
+}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/MatcherUtils.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/MatcherUtils.java
new file mode 100644
index 0000000..3acfd90
--- /dev/null
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/MatcherUtils.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.appsecurity.cts;
+
+import static org.hamcrest.CoreMatchers.both;
+import static org.junit.Assert.assertThat;
+
+import org.hamcrest.CoreMatchers;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+import org.junit.Assert;
+
+import java.util.function.Function;
+
+/**
+ * {@link Matcher} factories and {@link Assert#assertThat assertion} utilities.
+ */
+public class MatcherUtils {
+ private MatcherUtils() {}
+
+ /**
+ * Creates a matcher based on whether object's given property property satisfies
+ * the given matcher.
+ *
+ * This is often preferable over just retrieving the property directly and asserting on it, as
+ * it ensures the enclosing object is included in the error message, providing better context.
+ *
+ * E.g.:
+ * {@code
+ * Matcher<Intent> isExplicit =
+ * propertyMatches("component", Intent::getComponent, notNullValue());
+ * assertThat(myIntent, isExplicit);
+ * // ^ properly includes myIntent in error message, should match fail
+ * }
+ *
+ * @param propName property name to be used in error message
+ * @param propGetter retriever of the property value used for evaluation
+ * @param propCondition condition a property is asserted to satisfy
+ * @param <RECEIVER> type that has the given property
+ * @param <PROP> type of the given property
+ * @return a mather on {@code RECEIVER} type
+ */
+ public static <RECEIVER, PROP> Matcher<RECEIVER> propertyMatches(
+ String propName,
+ Function<? super RECEIVER, ? extends PROP> propGetter,
+ Matcher<? extends PROP> propCondition) {
+ return new TypeSafeMatcher<RECEIVER>() {
+ @Override
+ protected boolean matchesSafely(RECEIVER item) {
+ return propCondition.matches(propGetter.apply(item));
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("has ")
+ .appendText(propName)
+ .appendText(" that ")
+ .appendDescriptionOf(propCondition);
+ }
+ };
+ }
+
+ /**
+ * A more type-safe version of: {@link CoreMatchers#instanceOf} && {@code cond},
+ * with {@code cond} being parametrized on the subtype being tested for as the first step.
+ */
+ public static <SUPER, SUB extends SUPER> Matcher<SUPER> instanceOf(
+ Class<SUB> type, Matcher<? super SUB> cond) {
+ return (Matcher<SUPER>) both(CoreMatchers.instanceOf(type)).and((Matcher) cond);
+ }
+
+ /**
+ * {@link Throwable} matcher based on whether its {@link Throwable::getMessage message} is
+ * matching {@code condition}.
+ */
+ public static Matcher<Throwable> hasMessageThat(Matcher<? super String> condition) {
+ return propertyMatches("message", Throwable::getMessage, condition);
+ }
+
+ /**
+ * Runs {@code action}, and asserts that it throws an exception matching {@code exceptionCond}.
+ */
+ public static void assertThrows(
+ Matcher<? super Throwable> exceptionCond, ThrowingRunnable action) {
+ Throwable thrown;
+ try {
+ action.run();
+ thrown = null;
+ } catch (Throwable t) {
+ thrown = t;
+ }
+ assertOrRethrow(thrown, exceptionCond);
+ }
+
+ /**
+ * Asserts that {@code exception} matches the given {@code condition}, or else
+ * throws the resulting assertion error, with the original exception attached as a cause.
+ */
+ public static <E extends Throwable> void assertOrRethrow(
+ E exception, Matcher<? super E> condition) throws AssertionError {
+ try {
+ assertThat(exception, condition);
+ } catch (AssertionError err) {
+ throw ExceptionUtils.appendCause(err, exception);
+ }
+ }
+}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/OverlayHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/OverlayHostTest.java
index 20978c6..fbf03be 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/OverlayHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/OverlayHostTest.java
@@ -146,7 +146,7 @@
getDevice().executeShellCommand("cmd overlay enable " + overlayPackage);
waitForOverlayState(overlayPackage, STATE_ENABLED);
- runDeviceTests(TEST_APP_PACKAGE, TEST_APP_CLASS, testMethod);
+ runDeviceTests(TEST_APP_PACKAGE, TEST_APP_CLASS, testMethod, false /* instant */);
} finally {
getDevice().uninstallPackage(TARGET_PACKAGE);
getDevice().uninstallPackage(overlayPackage);
@@ -291,7 +291,7 @@
@Test
public void testFrameworkDoesNotDefineOverlayable() throws Exception {
String testMethod = "testFrameworkDoesNotDefineOverlayable";
- runDeviceTests(TEST_APP_PACKAGE, TEST_APP_CLASS, testMethod);
+ runDeviceTests(TEST_APP_PACKAGE, TEST_APP_CLASS, testMethod, false /* instant */);
}
/** Overlays must not overlay assets. */
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java
index d0cc258..2442638 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java
@@ -16,6 +16,12 @@
package android.appsecurity.cts;
+import static android.appsecurity.cts.MatcherUtils.assertThrows;
+import static android.appsecurity.cts.MatcherUtils.hasMessageThat;
+import static android.appsecurity.cts.MatcherUtils.instanceOf;
+
+import static org.hamcrest.CoreMatchers.containsString;
+
import android.platform.test.annotations.AppModeFull;
import android.platform.test.annotations.Presubmit;
@@ -121,31 +127,16 @@
public void testFail() throws Exception {
// Sanity check that remote failure is host failure
assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_23), false, false));
- boolean didThrow = false;
- try {
- runDeviceTests(USES_PERMISSION_PKG, "com.android.cts.usepermission.UsePermissionTest23",
- "testFail");
- } catch (AssertionError expected) {
- didThrow = true;
- }
- if (!didThrow) {
- fail("Expected remote failure");
- }
+ assertThrows(
+ instanceOf(AssertionError.class, hasMessageThat(containsString("Expected"))),
+ () -> runDeviceTests(USES_PERMISSION_PKG,
+ "com.android.cts.usepermission.UsePermissionTest23", "testFail"));
}
public void testKill() throws Exception {
// Sanity check that remote kill is host failure
assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_23), false, false));
- boolean didThrow = false;
- try {
- runDeviceTests(USES_PERMISSION_PKG, "com.android.cts.usepermission.UsePermissionTest23",
- "testKill");
- } catch (AssertionError expected) {
- didThrow = true;
- }
- if (!didThrow) {
- fail("Expected remote failure");
- }
+ runThrowingTest("com.android.cts.usepermission.UsePermissionTest23", "testKill");
}
@AppModeFull(reason = "Instant applications must be at least SDK 26")
@@ -164,16 +155,8 @@
approveReviewPermissionDialog();
- boolean didThrow = false;
- try {
- runDeviceTests(USES_PERMISSION_PKG, "com.android.cts.usepermission.UsePermissionTest22",
- "testCompatRevoked_part1");
- } catch (AssertionError expected) {
- didThrow = true;
- }
- if (!didThrow) {
- fail("App must be killed on a permission revoke");
- }
+ runThrowingTest("com.android.cts.usepermission.UsePermissionTest22",
+ "testCompatRevoked_part1");
runDeviceTests(USES_PERMISSION_PKG, "com.android.cts.usepermission.UsePermissionTest22",
"testCompatRevoked_part2");
}
@@ -256,32 +239,16 @@
public void testRevokeAffectsWholeGroup23() throws Exception {
assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_23), false, false));
- boolean didThrow = false;
- try {
- runDeviceTests(USES_PERMISSION_PKG, "com.android.cts.usepermission.UsePermissionTest23",
- "testRevokeAffectsWholeGroup_part1");
- } catch (AssertionError expected) {
- didThrow = true;
- }
- if (!didThrow) {
- fail("Should have thrown an exception.");
- }
+ runThrowingTest("com.android.cts.usepermission.UsePermissionTest23",
+ "testRevokeAffectsWholeGroup_part1");
runDeviceTests(USES_PERMISSION_PKG, "com.android.cts.usepermission.UsePermissionTest23",
"testRevokeAffectsWholeGroup_part2");
}
public void testGrantPreviouslyRevokedWithPrejudiceShowsPrompt23() throws Exception {
assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_23), false, false));
- boolean didThrow = false;
- try {
- runDeviceTests(USES_PERMISSION_PKG, "com.android.cts.usepermission.UsePermissionTest23",
- "testGrantPreviouslyRevokedWithPrejudiceShowsPrompt_part1");
- } catch (Throwable expected) {
- didThrow = true;
- }
- if (!didThrow) {
- fail("App must be killed on a permission revoke");
- }
+ runThrowingTest("com.android.cts.usepermission.UsePermissionTest23",
+ "testGrantPreviouslyRevokedWithPrejudiceShowsPrompt_part1");
runDeviceTests(USES_PERMISSION_PKG, "com.android.cts.usepermission.UsePermissionTest23",
"testGrantPreviouslyRevokedWithPrejudiceShowsPrompt_part2");
}
@@ -319,15 +286,8 @@
public void testNoDowngradePermissionModel() throws Exception {
assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_23), false, false));
- boolean didThrow = false;
- try {
- assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_22), true, false));
- } catch (AssertionError expected) {
- didThrow = true;
- }
- if (!didThrow) {
- fail("Permission mode downgrade not allowed");
- }
+ assertNotNull("Permission mode downgrade not allowed",
+ getDevice().installPackage(mBuildHelper.getTestFile(APK_22), true, false));
}
public void testNoResidualPermissionsOnUninstall() throws Exception {
@@ -345,16 +305,8 @@
approveReviewPermissionDialog();
- boolean didThrow = false;
- try {
- runDeviceTests(USES_PERMISSION_PKG, "com.android.cts.usepermission.UsePermissionTest22",
- "testRevokePropagatedOnUpgradeOldToNewModel_part1");
- } catch (AssertionError expected) {
- didThrow = true;
- }
- if (!didThrow) {
- fail("App must be killed on a permission revoke");
- }
+ runThrowingTest("com.android.cts.usepermission.UsePermissionTest22",
+ "testRevokePropagatedOnUpgradeOldToNewModel_part1");
assertNull(getDevice().installPackage(mBuildHelper.getTestFile(APK_23), true, false));
runDeviceTests(USES_PERMISSION_PKG, "com.android.cts.usepermission.UsePermissionTest23",
"testRevokePropagatedOnUpgradeOldToNewModel_part2");
@@ -530,4 +482,10 @@
throws DeviceNotAvailableException {
Utils.runDeviceTestsAsCurrentUser(getDevice(), packageName, testClassName, testMethodName);
}
+
+ private void runThrowingTest(String clazz, String testMethod) {
+ assertThrows(
+ instanceOf(AssertionError.class, hasMessageThat(containsString("Process crashed"))),
+ () -> runDeviceTests(USES_PERMISSION_PKG, clazz, testMethod));
+ }
}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/ThrowingRunnable.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/ThrowingRunnable.java
similarity index 94%
rename from common/device-side/util/src/com/android/compatibility/common/util/ThrowingRunnable.java
rename to hostsidetests/appsecurity/src/android/appsecurity/cts/ThrowingRunnable.java
index 0588cff..140ee75 100644
--- a/common/device-side/util/src/com/android/compatibility/common/util/ThrowingRunnable.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/ThrowingRunnable.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.compatibility.common.util;
+package android.appsecurity.cts;
/**
* Similar to {@link Runnable} but has {@code throws Exception}.
diff --git a/hostsidetests/appsecurity/test-apps/DeclareNotRuntimePermissions/OWNERS b/hostsidetests/appsecurity/test-apps/DeclareNotRuntimePermissions/OWNERS
new file mode 100644
index 0000000..8075c9c
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/DeclareNotRuntimePermissions/OWNERS
@@ -0,0 +1,2 @@
+moltmann@google.com
+eugenesusla@google.com
diff --git a/hostsidetests/appsecurity/test-apps/DuplicatePermissionDeclareApp/OWNERS b/hostsidetests/appsecurity/test-apps/DuplicatePermissionDeclareApp/OWNERS
new file mode 100644
index 0000000..8075c9c
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/DuplicatePermissionDeclareApp/OWNERS
@@ -0,0 +1,2 @@
+moltmann@google.com
+eugenesusla@google.com
diff --git a/hostsidetests/appsecurity/test-apps/EscalateToRuntimePermissions/OWNERS b/hostsidetests/appsecurity/test-apps/EscalateToRuntimePermissions/OWNERS
new file mode 100644
index 0000000..8075c9c
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/EscalateToRuntimePermissions/OWNERS
@@ -0,0 +1,2 @@
+moltmann@google.com
+eugenesusla@google.com
diff --git a/hostsidetests/appsecurity/test-apps/MediaStorageApp/src/com/android/cts/mediastorageapp/MediaStorageTest.java b/hostsidetests/appsecurity/test-apps/MediaStorageApp/src/com/android/cts/mediastorageapp/MediaStorageTest.java
index 78f2fff..30291a0 100644
--- a/hostsidetests/appsecurity/test-apps/MediaStorageApp/src/com/android/cts/mediastorageapp/MediaStorageTest.java
+++ b/hostsidetests/appsecurity/test-apps/MediaStorageApp/src/com/android/cts/mediastorageapp/MediaStorageTest.java
@@ -211,6 +211,18 @@
fail("Expected write access to be blocked");
} catch (SecurityException | FileNotFoundException expected) {
}
+
+ // Verify that we can't grant ourselves access
+ for (int flag : new int[] {
+ Intent.FLAG_GRANT_READ_URI_PERMISSION,
+ Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+ }) {
+ try {
+ mContext.grantUriPermission(mContext.getPackageName(), blue, flag);
+ fail("Expected granting to be blocked for flag 0x" + Integer.toHexString(flag));
+ } catch (SecurityException expected) {
+ }
+ }
}
@Test
diff --git a/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/AndroidManifest.xml
index 429ea8b..9188993 100644
--- a/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/AndroidManifest.xml
@@ -92,7 +92,8 @@
android:pathPrefix="/yes"
android:readPermission="com.android.cts.permissionWithSignature"
android:writePermission="com.android.cts.permissionWithSignature" />
- <grant-uri-permission android:pathPattern=".*" />
+ <grant-uri-permission android:pathPattern="/foo.*" />
+ <grant-uri-permission android:pathPattern="/yes.*" />
</provider>
<!-- Target for tests that verify path permissions can restrict access
@@ -112,5 +113,6 @@
android:writePermission="com.android.cts.permissionNormal" />
</provider>
+ <activity android:name=".SendResultActivity" android:exported="true" />
</application>
</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/OWNERS b/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/OWNERS
new file mode 100644
index 0000000..8075c9c
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/OWNERS
@@ -0,0 +1,2 @@
+moltmann@google.com
+eugenesusla@google.com
diff --git a/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/SendResultActivity.java b/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/SendResultActivity.java
new file mode 100644
index 0000000..4fdca81
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/SendResultActivity.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.permissiondeclareapp;
+
+import android.app.Activity;
+import android.content.ClipData;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+public class SendResultActivity extends Activity {
+ private static final String TAG = "SendUriActivity";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // For simplicity, we're willing to grant whatever they asked for
+ final ClipData reqClip = getIntent().getParcelableExtra(Intent.EXTRA_TEXT);
+ final int reqMode = getIntent().getIntExtra(Intent.EXTRA_INDEX, 0);
+
+ final Intent result = new Intent();
+ result.setClipData(reqClip);
+ result.addFlags(reqMode);
+
+ try {
+ Log.d(TAG, "Finishing OK with " + result);
+ setResult(RESULT_OK, result);
+ finish();
+ } catch (SecurityException e) {
+ Log.d(TAG, "Finishing CANCELED due to " + e);
+ setResult(RESULT_CANCELED, null);
+ finish();
+
+ // Make sure we hand control back to whoever started us
+ android.os.Process.killProcess(android.os.Process.myPid());
+ }
+ }
+}
diff --git a/hostsidetests/appsecurity/test-apps/PermissionDeclareAppCompat/OWNERS b/hostsidetests/appsecurity/test-apps/PermissionDeclareAppCompat/OWNERS
new file mode 100644
index 0000000..8075c9c
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/PermissionDeclareAppCompat/OWNERS
@@ -0,0 +1,2 @@
+moltmann@google.com
+eugenesusla@google.com
diff --git a/hostsidetests/appsecurity/test-apps/PermissionPolicy25/OWNERS b/hostsidetests/appsecurity/test-apps/PermissionPolicy25/OWNERS
new file mode 100644
index 0000000..8075c9c
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/PermissionPolicy25/OWNERS
@@ -0,0 +1,2 @@
+moltmann@google.com
+eugenesusla@google.com
diff --git a/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/AndroidManifest.xml
index 7b254dc..30d73f5 100644
--- a/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/AndroidManifest.xml
@@ -19,7 +19,7 @@
package="com.android.cts.reviewpermissionhelper">
<application>
- <activity android:name=".ActivityStarter" />
+ <activity android:name="com.android.compatibility.common.util.FutureResultActivity" />
<uses-library android:name="android.test.runner" />
</application>
diff --git a/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/OWNERS b/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/OWNERS
new file mode 100644
index 0000000..8075c9c
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/OWNERS
@@ -0,0 +1,2 @@
+moltmann@google.com
+eugenesusla@google.com
diff --git a/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/src/com/android/cts/reviewpermissionhelper/ActivityStarter.kt b/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/src/com/android/cts/reviewpermissionhelper/ActivityStarter.kt
deleted file mode 100644
index 90dbca9..0000000
--- a/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/src/com/android/cts/reviewpermissionhelper/ActivityStarter.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.reviewpermissionhelper
-
-import android.app.Activity
-import android.content.Intent
-import android.os.Bundle
-import java.util.concurrent.LinkedBlockingQueue
-
-val installDialogResults = LinkedBlockingQueue<Int>()
-
-class ActivityStarter : Activity() {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- savedInstanceState ?: installDialogResults.clear()
- }
-
- override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
- installDialogResults.offer(resultCode)
- }
-}
diff --git a/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/src/com/android/cts/reviewpermissionhelper/ReviewPermissionsTest.kt b/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/src/com/android/cts/reviewpermissionhelper/ReviewPermissionsTest.kt
index 15fdff3..b07a4bb 100644
--- a/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/src/com/android/cts/reviewpermissionhelper/ReviewPermissionsTest.kt
+++ b/hostsidetests/appsecurity/test-apps/ReviewPermissionHelper/src/com/android/cts/reviewpermissionhelper/ReviewPermissionsTest.kt
@@ -32,11 +32,13 @@
import android.support.test.uiautomator.By
import android.support.test.uiautomator.UiDevice
import android.support.test.uiautomator.Until
+import com.android.compatibility.common.util.FutureResultActivity
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import java.util.concurrent.CompletableFuture
import java.util.concurrent.LinkedBlockingQueue
import java.util.concurrent.TimeUnit
@@ -47,16 +49,22 @@
@RunWith(AndroidJUnit4::class)
class ReviewPermissionsTest {
@get:Rule
- val activityStarter = ActivityTestRule(ActivityStarter::class.java)
+ val activityStarter = ActivityTestRule(FutureResultActivity::class.java)
val instrumentation = InstrumentationRegistry.getInstrumentation()
val uiDevice = UiDevice.getInstance(instrumentation)
- fun startActivityInReviewedAp() {
+ fun startActivityInReviewedAp(): CompletableFuture<Int> {
val startAutoClosingActivity = Intent()
startAutoClosingActivity.component = ComponentName(USE_PERMISSION_PKG,
USE_PERMISSION_PKG + ".AutoClosingActivity")
- activityStarter.activity.startActivityForResult(startAutoClosingActivity, 42)
+ return activityStarter.activity.startActivityForResult(startAutoClosingActivity)
+ }
+
+ private inline fun startActivityInReviewedAp(expectedResult: Int, runAfterStart: () -> Unit) {
+ val activityResult = startActivityInReviewedAp()
+ runAfterStart()
+ assertEquals(expectedResult, activityResult.get(UI_TIMEOUT, TimeUnit.MILLISECONDS))
}
fun clickContinue() {
@@ -66,24 +74,23 @@
@Test
fun approveReviewPermissions() {
- startActivityInReviewedAp()
- clickContinue()
- assertEquals(RESULT_OK, installDialogResults.poll(UI_TIMEOUT, TimeUnit.MILLISECONDS))
+ startActivityInReviewedAp(expectedResult = RESULT_OK) {
+ clickContinue()
+ }
}
@Test
fun cancelReviewPermissions() {
- startActivityInReviewedAp()
-
- uiDevice.wait(Until.findObject(
- By.res("com.android.permissioncontroller:id/cancel_button")), UI_TIMEOUT).click()
- assertEquals(RESULT_CANCELED, installDialogResults.poll(UI_TIMEOUT, TimeUnit.MILLISECONDS))
+ startActivityInReviewedAp(expectedResult = RESULT_CANCELED) {
+ uiDevice.wait(Until.findObject(
+ By.res("com.android.permissioncontroller:id/cancel_button")), UI_TIMEOUT)
+ .click()
+ }
}
@Test
fun assertNoReviewPermissionsNeeded() {
- startActivityInReviewedAp()
- assertEquals(RESULT_OK, installDialogResults.poll(UI_TIMEOUT, TimeUnit.MILLISECONDS))
+ startActivityInReviewedAp(expectedResult = RESULT_OK) {}
}
@Test
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp22/OWNERS b/hostsidetests/appsecurity/test-apps/UsePermissionApp22/OWNERS
new file mode 100644
index 0000000..8075c9c
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp22/OWNERS
@@ -0,0 +1,2 @@
+moltmann@google.com
+eugenesusla@google.com
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/UsePermissionTest22.java b/hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/UsePermissionTest22.java
index 20be2cc..a1bd122 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/UsePermissionTest22.java
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp22/src/com/android/cts/usepermission/UsePermissionTest22.java
@@ -36,7 +36,6 @@
* Runtime permission behavior tests for apps targeting API 22
*/
public class UsePermissionTest22 extends BasePermissionsTest {
- private static final int REQUEST_CODE_PERMISSIONS = 42;
private final Context mContext = getInstrumentation().getContext();
@@ -137,11 +136,9 @@
public void testNoRuntimePrompt() throws Exception {
// Request the permission and do nothing
BasePermissionActivity.Result result = requestPermissions(
- new String[] {Manifest.permission.SEND_SMS}, REQUEST_CODE_PERMISSIONS,
- BasePermissionActivity.class, null);
+ new String[]{Manifest.permission.SEND_SMS}, null);
// Expect the permission is not granted
- assertEquals(REQUEST_CODE_PERMISSIONS, result.requestCode);
assertTrue(Arrays.equals(result.permissions, new String[0]));
assertTrue(Arrays.equals(result.grantResults, new int[0]));
}
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/OWNERS b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/OWNERS
new file mode 100644
index 0000000..8075c9c
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/OWNERS
@@ -0,0 +1,2 @@
+moltmann@google.com
+eugenesusla@google.com
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionActivity.java b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionActivity.java
index cacfa80..48dafef 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionActivity.java
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionActivity.java
@@ -20,23 +20,33 @@
import android.os.Bundle;
import android.view.WindowManager;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
public class BasePermissionActivity extends Activity {
private static final long OPERATION_TIMEOUT_MILLIS = 5000;
- private final SynchronousQueue<Result> mResult = new SynchronousQueue<>();
+ /**
+ * Static to ensure correct behavior if {@link Activity} instance was recreated before
+ * result delivery.
+ *
+ * requestCode -> Future<Result>
+ */
+ private static Map<Integer, CompletableFuture<Result>> sPendingResults =
+ new ConcurrentHashMap<>();
+ private static AtomicInteger sNextRequestCode = new AtomicInteger(0);
+
private final CountDownLatch mOnCreateSync = new CountDownLatch(1);
public static class Result {
- public final int requestCode;
public final String[] permissions;
public final int[] grantResults;
- public Result(int requestCode, String[] permissions, int[] grantResults) {
- this.requestCode = requestCode;
+ public Result(String[] permissions, int[] grantResults) {
this.permissions = permissions;
this.grantResults = grantResults;
}
@@ -53,14 +63,18 @@
mOnCreateSync.countDown();
}
+ public CompletableFuture<Result> requestPermissions(String[] permissions) {
+ int requestCode = sNextRequestCode.getAndIncrement();
+ CompletableFuture<Result> future = new CompletableFuture<>();
+ sPendingResults.put(requestCode, future);
+ requestPermissions(permissions, requestCode);
+ return future;
+ }
+
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions,
int[] grantResults) {
- try {
- mResult.offer(new Result(requestCode, permissions, grantResults), 5, TimeUnit.SECONDS);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
+ sPendingResults.get(requestCode).complete(new Result(permissions, grantResults));
}
public void waitForOnCreate() {
@@ -70,12 +84,4 @@
throw new RuntimeException(e);
}
}
-
- public Result getResult() {
- try {
- return mResult.poll(OPERATION_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
}
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionsTest.java b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionsTest.java
index ba5fd4b..2ec3002 100755
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionsTest.java
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionsTest.java
@@ -16,11 +16,17 @@
package com.android.cts.usepermission;
+import static com.android.compatibility.common.util.UiAutomatorUtils.waitFindObject;
+import static com.android.compatibility.common.util.UiAutomatorUtils.getUiDevice;
+
import static junit.framework.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
import android.Manifest;
import android.app.Activity;
import android.app.Instrumentation;
@@ -51,6 +57,10 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import com.android.compatibility.common.util.ExceptionUtils;
+import com.android.compatibility.common.util.ThrowingRunnable;
+import com.android.compatibility.common.util.UiDumpUtils;
+
import junit.framework.Assert;
import org.junit.Before;
@@ -59,6 +69,8 @@
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.regex.Pattern;
@@ -83,20 +95,16 @@
}
protected static void assertPermissionRequestResult(BasePermissionActivity.Result result,
- int requestCode, String[] permissions, boolean[] granted) {
- assertEquals(requestCode, result.requestCode);
+ String[] permissions, boolean[] granted) {
for (int i = 0; i < permissions.length; i++) {
assertEquals(permissions[i], result.permissions[i]);
- assertEquals(granted[i] ? PackageManager.PERMISSION_GRANTED
+ assertEquals(granted[i]
+ ? PackageManager.PERMISSION_GRANTED
: PackageManager.PERMISSION_DENIED, result.grantResults[i]);
}
}
- protected static UiDevice getUiDevice() {
- return UiDevice.getInstance(getInstrumentation());
- }
-
protected static Activity launchActivity(String packageName,
Class<?> clazz, Bundle extras) {
Intent intent = new Intent(Intent.ACTION_MAIN);
@@ -269,74 +277,67 @@
}
protected BasePermissionActivity.Result requestPermissions(
- String[] permissions, int requestCode, Class<?> clazz, Runnable postRequestAction)
+ String[] permissions, ThrowingRunnable postRequestAction)
throws Exception {
- // Start an activity
- BasePermissionActivity activity = (BasePermissionActivity) launchActivity(
- getInstrumentation().getTargetContext().getPackageName(), clazz, null);
+ return ExceptionUtils.wrappingExceptions(UiDumpUtils::wrapWithUiDump, () -> {
+ // Start an activity
+ BasePermissionActivity activity = (BasePermissionActivity) launchActivity(
+ getInstrumentation().getTargetContext().getPackageName(),
+ BasePermissionActivity.class, null);
- activity.waitForOnCreate();
+ activity.waitForOnCreate();
- // Request the permissions
- activity.requestPermissions(permissions, requestCode);
+ // Request the permissions
+ CompletableFuture<BasePermissionActivity.Result> futureResult =
+ activity.requestPermissions(permissions);
- // Define a more conservative idle criteria
- getInstrumentation().getUiAutomation().waitForIdle(
- IDLE_TIMEOUT_MILLIS, GLOBAL_TIMEOUT_MILLIS);
+ // Define a more conservative idle criteria
+ getInstrumentation().getUiAutomation().waitForIdle(
+ IDLE_TIMEOUT_MILLIS, GLOBAL_TIMEOUT_MILLIS);
- // Perform the post-request action
- if (postRequestAction != null) {
- postRequestAction.run();
- }
+ // Perform the post-request action
+ if (postRequestAction != null) {
+ postRequestAction.run();
+ }
- BasePermissionActivity.Result result = activity.getResult();
- activity.finish();
- return result;
+ BasePermissionActivity.Result result =
+ futureResult.get(GLOBAL_TIMEOUT_MILLIS, MILLISECONDS);
+ activity.finish();
+ return result;
+ });
}
protected void clickAllowButton() throws Exception {
- scrollToBottomIfWatch();
- waitForIdle();
- getUiDevice().wait(Until.findObject(By.res(
- "com.android.permissioncontroller:id/permission_allow_button")),
- GLOBAL_TIMEOUT_MILLIS).click();
+ scrollToBottom();
+ click("com.android.permissioncontroller:id/permission_allow_button");
}
protected void clickAllowAlwaysButton() throws Exception {
- waitForIdle();
- getUiDevice().wait(Until.findObject(By.res(
- "com.android.permissioncontroller:id/permission_allow_always_button")),
- GLOBAL_TIMEOUT_MILLIS).click();
+ click("com.android.permissioncontroller:id/permission_allow_always_button");
}
protected void clickAllowForegroundButton() throws Exception {
- waitForIdle();
- getUiDevice().wait(Until.findObject(By.res(
- "com.android.permissioncontroller:id/permission_allow_foreground_only_button")),
- GLOBAL_TIMEOUT_MILLIS).click();
+ click("com.android.permissioncontroller:id/permission_allow_foreground_only_button");
}
protected void clickDenyButton() throws Exception {
- scrollToBottomIfWatch();
- waitForIdle();
- getUiDevice().wait(Until.findObject(By.res(
- "com.android.permissioncontroller:id/permission_deny_button")),
- GLOBAL_TIMEOUT_MILLIS).click();
+ scrollToBottom();
+ click("com.android.permissioncontroller:id/permission_deny_button");
}
protected void clickDenyAndDontAskAgainButton() throws Exception {
- waitForIdle();
- getUiDevice().wait(Until.findObject(By.res(
- "com.android.permissioncontroller:id/permission_deny_and_dont_ask_again_button")),
- GLOBAL_TIMEOUT_MILLIS).click();
+ scrollToBottom();
+ click("com.android.permissioncontroller:id/permission_deny_and_dont_ask_again_button");
}
protected void clickDontAskAgainButton() throws Exception {
- scrollToBottomIfWatch();
+ scrollToBottom();
+ click("com.android.permissioncontroller:id/permission_deny_dont_ask_again_button");
+ }
+
+ private void click(String resourceName) throws TimeoutException {
waitForIdle();
- getUiDevice().wait(Until.findObject(By.res(
- "com.android.permissioncontroller:id/permission_deny_dont_ask_again_button")),
- GLOBAL_TIMEOUT_MILLIS).click();
+ waitFindObject(By.res(resourceName)).click();
}
protected void grantPermission(String permission) throws Exception {
@@ -355,116 +356,103 @@
setPermissionGrantState(permissions, false, legacyApp);
}
- private void scrollToBottomIfWatch() throws Exception {
- if (mWatch) {
- getUiDevice().wait(Until.findObject(By.clazz(ScrollView.class)), GLOBAL_TIMEOUT_MILLIS);
- UiScrollable scrollable =
- new UiScrollable(new UiSelector().className(ScrollView.class));
- if (scrollable.exists()) {
- scrollable.flingToEnd(10);
- }
+ private void scrollToBottom() throws Exception {
+ waitFindObject(By.clazz(ScrollView.class));
+ UiScrollable scrollable =
+ new UiScrollable(new UiSelector().className(ScrollView.class));
+ if (scrollable.exists()) {
+ scrollable.flingToEnd(10);
}
}
private void setPermissionGrantState(String[] permissions, boolean granted,
boolean legacyApp) throws Exception {
- getUiDevice().pressBack();
- waitForIdle();
- getUiDevice().pressBack();
- waitForIdle();
- getUiDevice().pressBack();
- waitForIdle();
-
- if (isTv()) {
- getUiDevice().pressHome();
+ ExceptionUtils.wrappingExceptions(UiDumpUtils::wrapWithUiDump, () -> {
+ getUiDevice().pressBack();
waitForIdle();
- }
+ getUiDevice().pressBack();
+ waitForIdle();
+ getUiDevice().pressBack();
+ waitForIdle();
- // Open the app details settings
- Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
- intent.addCategory(Intent.CATEGORY_DEFAULT);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.setData(Uri.parse("package:" + mContext.getPackageName()));
- startActivity(intent);
-
- waitForIdle();
-
- // Open the permissions UI
- String label = mContext.getResources().getString(R.string.Permissions);
- AccessibilityNodeInfo permLabelView = getNodeTimed(() -> findByText(label), true);
- Assert.assertNotNull("Permissions label should be present", permLabelView);
-
- AccessibilityNodeInfo permItemView = findCollectionItem(permLabelView);
-
- click(permItemView);
-
- waitForIdle();
-
- for (String permission : permissions) {
- // Find the permission screen
- String permissionLabel = getPermissionLabel(permission);
-
- UiObject2 permissionView = null;
- long start = System.currentTimeMillis();
- while (permissionView == null && start + RETRY_TIMEOUT > System.currentTimeMillis()) {
- permissionView = getUiDevice().wait(Until.findObject(By.text(permissionLabel)),
- IDLE_TIMEOUT_MILLIS);
-
- if (permissionView == null) {
- getUiDevice().findObject(By.scrollable(true))
- .scroll(Direction.DOWN, 1);
- }
+ if (isTv()) {
+ getUiDevice().pressHome();
+ waitForIdle();
}
- permissionView.click();
+ // Open the app details settings
+ Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+ intent.addCategory(Intent.CATEGORY_DEFAULT);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.setData(Uri.parse("package:" + mContext.getPackageName()));
+ startActivity(intent);
+
waitForIdle();
- String denyLabel = mContext.getResources().getString(R.string.Deny);
+ // Open the permissions UI
+ String label = mContext.getResources().getString(R.string.Permissions);
+ AccessibilityNodeInfo permLabelView = getNodeTimed(() -> findByText(label), true);
+ Assert.assertNotNull("Permissions label should be present", permLabelView);
- final boolean wasGranted = !getUiDevice().wait(Until.findObject(By.text(denyLabel)),
- GLOBAL_TIMEOUT_MILLIS).isChecked();
- if (granted != wasGranted) {
- // Toggle the permission
+ AccessibilityNodeInfo permItemView = findCollectionItem(permLabelView);
- if (granted) {
- String allowLabel = mContext.getResources().getString(R.string.Allow);
- getUiDevice().findObject(By.text(allowLabel)).click();
- } else {
- getUiDevice().findObject(By.text(denyLabel)).click();
- }
+ click(permItemView);
+
+ waitForIdle();
+
+ for (String permission : permissions) {
+ // Find the permission screen
+ String permissionLabel = getPermissionLabel(permission);
+
+ waitFindObject(By.text(permissionLabel)).click();
waitForIdle();
- if (wasGranted && legacyApp) {
- scrollToBottomIfWatch();
- Context context = getInstrumentation().getContext();
- String packageName = context.getPackageManager()
- .getPermissionControllerPackageName();
- String resIdName = "com.android.permissioncontroller"
- + ":string/grant_dialog_button_deny_anyway";
- Resources resources = context
- .createPackageContext(packageName, 0).getResources();
- final int confirmResId = resources.getIdentifier(resIdName, null, null);
- String confirmTitle = CaseMap.toUpper().apply(
- resources.getConfiguration().getLocales().get(0),
- resources.getString(confirmResId));
- getUiDevice().wait(Until.findObject(
- byTextStartsWithCaseInsensitive(confirmTitle)),
- GLOBAL_TIMEOUT_MILLIS).click();
+ final boolean wasGranted = !waitFindObject(byText(R.string.Deny)).isChecked();
+ if (granted != wasGranted) {
+ // Toggle the permission
+ if (granted) {
+ waitFindObject(byText(R.string.Allow)).click();
+ } else {
+ waitFindObject(byText(R.string.Deny)).click();
+ }
waitForIdle();
+
+ if (wasGranted && legacyApp) {
+ scrollToBottom();
+ Context context = getInstrumentation().getContext();
+ String packageName = context.getPackageManager()
+ .getPermissionControllerPackageName();
+ String resIdName = "com.android.permissioncontroller"
+ + ":string/grant_dialog_button_deny_anyway";
+ Resources resources = context
+ .createPackageContext(packageName, 0).getResources();
+ final int confirmResId = resources.getIdentifier(resIdName, null, null);
+ String confirmTitle = resources.getString(confirmResId);
+
+ waitFindObject(byTextStartsWithCaseInsensitive(confirmTitle))
+ .click();
+ waitForIdle();
+ }
}
+
+ getUiDevice().pressBack();
+ waitForIdle();
}
getUiDevice().pressBack();
waitForIdle();
- }
-
- getUiDevice().pressBack();
- waitForIdle();
- getUiDevice().pressBack();
- waitForIdle();
+ getUiDevice().pressBack();
+ waitForIdle();
+ });
}
+ private BySelector byText(int stringId) {
+ return By.text(mContext.getResources().getString(stringId));
+ }
+
+
+
private BySelector byTextStartsWithCaseInsensitive(String prefix) {
return By.text(Pattern.compile(String.format("(?i)^%s.*$", Pattern.quote(prefix))));
}
@@ -610,12 +598,14 @@
}
private static void click(AccessibilityNodeInfo node) throws Exception {
- getInstrumentation().getUiAutomation().executeAndWaitForEvent(
- () -> node.performAction(AccessibilityNodeInfo.ACTION_CLICK),
- (AccessibilityEvent event) -> event.getEventType()
- == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
- || event.getEventType() == AccessibilityEvent.TYPE_WINDOWS_CHANGED,
- GLOBAL_TIMEOUT_MILLIS);
+ ExceptionUtils.wrappingExceptions(UiDumpUtils::wrapWithUiDump, () -> {
+ getInstrumentation().getUiAutomation().executeAndWaitForEvent(
+ () -> node.performAction(AccessibilityNodeInfo.ACTION_CLICK),
+ (AccessibilityEvent event) -> event.getEventType()
+ == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
+ || event.getEventType() == AccessibilityEvent.TYPE_WINDOWS_CHANGED,
+ GLOBAL_TIMEOUT_MILLIS);
+ });
}
private static AccessibilityNodeInfo findCollectionItem(AccessibilityNodeInfo current)
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/UsePermissionTest23.java b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/UsePermissionTest23.java
index 9482df0..3dde0fa 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/UsePermissionTest23.java
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/UsePermissionTest23.java
@@ -16,6 +16,7 @@
package com.android.cts.usepermission;
+import static com.android.compatibility.common.util.UiAutomatorUtils.getUiDevice;
import static junit.framework.Assert.assertEquals;
import static org.junit.Assert.fail;
@@ -26,7 +27,6 @@
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
-import android.os.Build;
import android.provider.CalendarContract;
import androidx.test.InstrumentationRegistry;
@@ -41,7 +41,6 @@
* Runtime permission behavior tests for apps targeting API 23
*/
public class UsePermissionTest23 extends BasePermissionsTest {
- private static final int REQUEST_CODE_PERMISSIONS = 42;
private final Context mContext = getInstrumentation().getContext();
@@ -105,21 +104,13 @@
}
// Go through normal grant flow
- BasePermissionActivity.Result result = requestPermissions(new String[] {
+ BasePermissionActivity.Result result = requestPermissions(new String[]{
Manifest.permission.READ_CALENDAR,
- Manifest.permission.WRITE_CALENDAR},
- REQUEST_CODE_PERMISSIONS,
- BasePermissionActivity.class,
- () -> {
- try {
- clickAllowButton();
- getUiDevice().waitForIdle();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- });
+ Manifest.permission.WRITE_CALENDAR}, () -> {
+ clickAllowButton();
+ getUiDevice().waitForIdle();
+ });
- assertEquals(REQUEST_CODE_PERMISSIONS, result.requestCode);
assertEquals(Manifest.permission.READ_CALENDAR, result.permissions[0]);
assertEquals(Manifest.permission.WRITE_CALENDAR, result.permissions[1]);
assertEquals(PackageManager.PERMISSION_GRANTED, result.grantResults[0]);
@@ -147,21 +138,13 @@
String[] permissions = new String[] {Manifest.permission.WRITE_CONTACTS};
// request only one permission from the 'contacts' permission group
- BasePermissionActivity.Result result = requestPermissions(permissions,
- REQUEST_CODE_PERMISSIONS,
- BasePermissionActivity.class,
- () -> {
- try {
- clickAllowButton();
- getUiDevice().waitForIdle();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- });
+ BasePermissionActivity.Result result = requestPermissions(permissions, () -> {
+ clickAllowButton();
+ getUiDevice().waitForIdle();
+ });
// Expect the permission is granted
- assertPermissionRequestResult(result, REQUEST_CODE_PERMISSIONS,
- permissions, new boolean[] {true});
+ assertPermissionRequestResult(result, permissions, new boolean[] {true});
// Make sure no undeclared as used permissions are granted
assertEquals(PackageManager.PERMISSION_DENIED, getInstrumentation().getContext()
@@ -181,21 +164,13 @@
// request only one permission from the 'SMS' permission group at runtime,
// but two from this group are <uses-permission> in the manifest
// request only one permission from the 'contacts' permission group
- BasePermissionActivity.Result result = requestPermissions(permissions,
- REQUEST_CODE_PERMISSIONS,
- BasePermissionActivity.class,
- () -> {
- try {
- clickAllowButton();
- getUiDevice().waitForIdle();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- });
+ BasePermissionActivity.Result result = requestPermissions(permissions, () -> {
+ clickAllowButton();
+ getUiDevice().waitForIdle();
+ });
// Expect the permission is granted
- assertPermissionRequestResult(result, REQUEST_CODE_PERMISSIONS,
- permissions, new boolean[] {true});
+ assertPermissionRequestResult(result, permissions, new boolean[] {true});
// We should now have been granted both of the permissions from this group.
assertEquals(PackageManager.PERMISSION_GRANTED, getInstrumentation().getContext()
@@ -211,21 +186,13 @@
String[] permissions = new String[] {Manifest.permission.WRITE_CONTACTS};
// Request the permission and cancel the request
- BasePermissionActivity.Result result = requestPermissions(permissions,
- REQUEST_CODE_PERMISSIONS,
- BasePermissionActivity.class,
- () -> {
- try {
- clickDenyButton();
- getUiDevice().waitForIdle();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- });
+ BasePermissionActivity.Result result = requestPermissions(permissions, () -> {
+ clickDenyButton();
+ getUiDevice().waitForIdle();
+ });
// Expect the permission is not granted
- assertPermissionRequestResult(result, REQUEST_CODE_PERMISSIONS,
- permissions, new boolean[] {false});
+ assertPermissionRequestResult(result, permissions, new boolean[] {false});
}
@Test
@@ -237,29 +204,20 @@
String[] permissions = new String[] {Manifest.permission.WRITE_CONTACTS};
// Request the permission and allow it
- BasePermissionActivity.Result firstResult = requestPermissions(permissions,
- REQUEST_CODE_PERMISSIONS,
- BasePermissionActivity.class, () -> {
- try {
- clickAllowButton();
- getUiDevice().waitForIdle();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- });
+ BasePermissionActivity.Result firstResult = requestPermissions(permissions, () -> {
+ clickAllowButton();
+ getUiDevice().waitForIdle();
+ });
// Expect the permission is granted
- assertPermissionRequestResult(firstResult, REQUEST_CODE_PERMISSIONS,
- permissions, new boolean[] {true});
+ assertPermissionRequestResult(firstResult, permissions, new boolean[] {true});
// Request the permission and do nothing
- BasePermissionActivity.Result secondResult = requestPermissions(new String[] {
- Manifest.permission.WRITE_CONTACTS}, REQUEST_CODE_PERMISSIONS + 1,
- BasePermissionActivity.class, null);
+ BasePermissionActivity.Result secondResult = requestPermissions(new String[]{
+ Manifest.permission.WRITE_CONTACTS}, null);
// Expect the permission is granted
- assertPermissionRequestResult(secondResult, REQUEST_CODE_PERMISSIONS + 1,
- permissions, new boolean[] {true});
+ assertPermissionRequestResult(secondResult, permissions, new boolean[] {true});
}
@Test
@@ -271,45 +229,30 @@
String[] permissions = new String[] {Manifest.permission.WRITE_CONTACTS};
// Request the permission and deny it
- BasePermissionActivity.Result firstResult = requestPermissions(
- permissions, REQUEST_CODE_PERMISSIONS,
- BasePermissionActivity.class, () -> {
- try {
- clickDenyButton();
- getUiDevice().waitForIdle();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- });
+ BasePermissionActivity.Result firstResult = requestPermissions(permissions, () -> {
+ clickDenyButton();
+ getUiDevice().waitForIdle();
+ });
// Expect the permission is not granted
- assertPermissionRequestResult(firstResult, REQUEST_CODE_PERMISSIONS,
- permissions, new boolean[] {false});
+ assertPermissionRequestResult(firstResult, permissions, new boolean[] {false});
// Request the permission and choose don't ask again
- BasePermissionActivity.Result secondResult = requestPermissions(new String[] {
- Manifest.permission.WRITE_CONTACTS}, REQUEST_CODE_PERMISSIONS + 1,
- BasePermissionActivity.class, () -> {
- try {
- denyWithPrejudice();
- getUiDevice().waitForIdle();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- });
+ BasePermissionActivity.Result secondResult = requestPermissions(new String[]{
+ Manifest.permission.WRITE_CONTACTS}, () -> {
+ denyWithPrejudice();
+ getUiDevice().waitForIdle();
+ });
// Expect the permission is not granted
- assertPermissionRequestResult(secondResult, REQUEST_CODE_PERMISSIONS + 1,
- permissions, new boolean[] {false});
+ assertPermissionRequestResult(secondResult, permissions, new boolean[] {false});
// Request the permission and do nothing
- BasePermissionActivity.Result thirdResult = requestPermissions(new String[] {
- Manifest.permission.WRITE_CONTACTS}, REQUEST_CODE_PERMISSIONS + 2,
- BasePermissionActivity.class, null);
+ BasePermissionActivity.Result thirdResult = requestPermissions(new String[]{
+ Manifest.permission.WRITE_CONTACTS}, null);
// Expect the permission is not granted
- assertPermissionRequestResult(thirdResult, REQUEST_CODE_PERMISSIONS + 2,
- permissions, new boolean[] {false});
+ assertPermissionRequestResult(thirdResult, permissions, new boolean[] {false});
}
@Test
@@ -347,36 +290,23 @@
String[] permissions = new String[] {Manifest.permission.READ_CALENDAR};
// Request the permission and deny it
- BasePermissionActivity.Result firstResult = requestPermissions(
- permissions, REQUEST_CODE_PERMISSIONS,
- BasePermissionActivity.class, () -> {
- try {
- clickDenyButton();
- getUiDevice().waitForIdle();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- });
+ BasePermissionActivity.Result firstResult = requestPermissions(permissions, () -> {
+ clickDenyButton();
+ getUiDevice().waitForIdle();
+ });
// Expect the permission is not granted
- assertPermissionRequestResult(firstResult, REQUEST_CODE_PERMISSIONS,
- permissions, new boolean[] {false});
+ assertPermissionRequestResult(firstResult, permissions, new boolean[] {false});
// Request the permission and choose don't ask again
- BasePermissionActivity.Result secondResult = requestPermissions(new String[] {
- Manifest.permission.READ_CALENDAR}, REQUEST_CODE_PERMISSIONS + 1,
- BasePermissionActivity.class, () -> {
- try {
- denyWithPrejudice();
- getUiDevice().waitForIdle();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- });
+ BasePermissionActivity.Result secondResult = requestPermissions(new String[]{
+ Manifest.permission.READ_CALENDAR}, () -> {
+ denyWithPrejudice();
+ getUiDevice().waitForIdle();
+ });
// Expect the permission is not granted
- assertPermissionRequestResult(secondResult, REQUEST_CODE_PERMISSIONS + 1,
- permissions, new boolean[] {false});
+ assertPermissionRequestResult(secondResult, permissions, new boolean[] {false});
// Clear the denial with prejudice
grantPermission(Manifest.permission.READ_CALENDAR);
@@ -392,20 +322,15 @@
.checkSelfPermission(Manifest.permission.READ_CALENDAR));
// Request the permission and allow it
- BasePermissionActivity.Result thirdResult = requestPermissions(new String[] {
- Manifest.permission.READ_CALENDAR}, REQUEST_CODE_PERMISSIONS + 2,
- BasePermissionActivity.class, () -> {
- try {
- clickAllowButton();
- getUiDevice().waitForIdle();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- });
+ BasePermissionActivity.Result thirdResult = requestPermissions(new String[]{
+ Manifest.permission.READ_CALENDAR}, () -> {
+ clickAllowButton();
+ getUiDevice().waitForIdle();
+ });
// Make sure the permission is granted
- assertPermissionRequestResult(thirdResult, REQUEST_CODE_PERMISSIONS + 2,
- new String[] {Manifest.permission.READ_CALENDAR}, new boolean[] {true});
+ assertPermissionRequestResult(thirdResult, new String[] {Manifest.permission.READ_CALENDAR},
+ new boolean[] {true});
}
@Test
@@ -417,12 +342,10 @@
String[] permissions = new String[] {Manifest.permission.BIND_PRINT_SERVICE};
// Request the permission and do nothing
- BasePermissionActivity.Result result = requestPermissions(permissions,
- REQUEST_CODE_PERMISSIONS, BasePermissionActivity.class, null);
+ BasePermissionActivity.Result result = requestPermissions(permissions, null);
// Expect the permission is not granted
- assertPermissionRequestResult(result, REQUEST_CODE_PERMISSIONS,
- permissions, new boolean[] {false});
+ assertPermissionRequestResult(result, permissions, new boolean[] {false});
}
@Test
@@ -434,12 +357,10 @@
String[] permissions = new String[] {"permission.does.not.exist"};
// Request the permission and do nothing
- BasePermissionActivity.Result result = requestPermissions(permissions,
- REQUEST_CODE_PERMISSIONS, BasePermissionActivity.class, null);
+ BasePermissionActivity.Result result = requestPermissions(permissions, null);
// Expect the permission is not granted
- assertPermissionRequestResult(result, REQUEST_CODE_PERMISSIONS,
- permissions, new boolean[] {false});
+ assertPermissionRequestResult(result, permissions, new boolean[] {false});
}
@Test
@@ -456,8 +377,7 @@
};
// Request the permission and allow it
- BasePermissionActivity.Result result = requestPermissions(permissions,
- REQUEST_CODE_PERMISSIONS, BasePermissionActivity.class, () -> {
+ BasePermissionActivity.Result result = requestPermissions(permissions, () -> {
try {
clickAllowButton();
getUiDevice().waitForIdle();
@@ -469,8 +389,7 @@
});
// Expect the permission are reported as granted
- assertPermissionRequestResult(result, REQUEST_CODE_PERMISSIONS,
- permissions, new boolean[] {true, true});
+ assertPermissionRequestResult(result, permissions, new boolean[] {true, true});
// The permissions are granted
assertEquals(PackageManager.PERMISSION_GRANTED, getInstrumentation().getContext()
@@ -534,20 +453,19 @@
.checkSelfPermission(Manifest.permission.READ_CALENDAR));
// Request the permission and allow it
- BasePermissionActivity.Result thirdResult = requestPermissions(new String[] {
- Manifest.permission.READ_CALENDAR}, REQUEST_CODE_PERMISSIONS,
- BasePermissionActivity.class, () -> {
- try {
- clickAllowButton();
- getUiDevice().waitForIdle();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- });
+ BasePermissionActivity.Result thirdResult = requestPermissions(new String[]{
+ Manifest.permission.READ_CALENDAR}, () -> {
+ try {
+ clickAllowButton();
+ getUiDevice().waitForIdle();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ });
// Make sure the permission is granted
- assertPermissionRequestResult(thirdResult, REQUEST_CODE_PERMISSIONS,
- new String[] {Manifest.permission.READ_CALENDAR}, new boolean[] {true});
+ assertPermissionRequestResult(thirdResult, new String[] {Manifest.permission.READ_CALENDAR},
+ new boolean[] {true});
}
@Test
@@ -571,12 +489,9 @@
// Go through normal grant flow
BasePermissionActivity.Result result = requestPermissions(permissions,
- REQUEST_CODE_PERMISSIONS,
- BasePermissionActivity.class,
() -> { /* empty */ });
- assertPermissionRequestResult(result, REQUEST_CODE_PERMISSIONS,
- permissions, new boolean[] {false});
+ assertPermissionRequestResult(result, permissions, new boolean[] {false});
}
@Test
@@ -595,21 +510,19 @@
};
// Request the permission and allow it
- BasePermissionActivity.Result result = requestPermissions(permissions,
- REQUEST_CODE_PERMISSIONS, BasePermissionActivity.class, () -> {
- try {
- clickAllowButton();
- getUiDevice().waitForIdle();
- clickAllowButton();
- getUiDevice().waitForIdle();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- });
+ BasePermissionActivity.Result result = requestPermissions(permissions, () -> {
+ try {
+ clickAllowButton();
+ getUiDevice().waitForIdle();
+ clickAllowButton();
+ getUiDevice().waitForIdle();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ });
// Expect the permission are reported as granted
- assertPermissionRequestResult(result, REQUEST_CODE_PERMISSIONS,
- permissions, new boolean[] {false, true, false, true});
+ assertPermissionRequestResult(result, permissions, new boolean[] {false, true, false, true});
// The permissions are granted
assertEquals(PackageManager.PERMISSION_GRANTED, getInstrumentation().getContext()
@@ -626,13 +539,10 @@
// Request the permission and allow it
BasePermissionActivity.Result result = requestPermissions(permissions,
- REQUEST_CODE_PERMISSIONS,
- BasePermissionActivity.class,
() -> { /* empty */ });
// Expect the permissions is not granted
- assertPermissionRequestResult(result, REQUEST_CODE_PERMISSIONS,
- permissions, new boolean[] {false});
+ assertPermissionRequestResult(result, permissions, new boolean[] {false});
}
private void assertAllPermissionsRevoked() {
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp25/OWNERS b/hostsidetests/appsecurity/test-apps/UsePermissionApp25/OWNERS
new file mode 100644
index 0000000..8075c9c
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp25/OWNERS
@@ -0,0 +1,2 @@
+moltmann@google.com
+eugenesusla@google.com
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp26/OWNERS b/hostsidetests/appsecurity/test-apps/UsePermissionApp26/OWNERS
new file mode 100644
index 0000000..8075c9c
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp26/OWNERS
@@ -0,0 +1,2 @@
+moltmann@google.com
+eugenesusla@google.com
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp26/src/com/android/cts/usepermission/UsePermissionTest26.java b/hostsidetests/appsecurity/test-apps/UsePermissionApp26/src/com/android/cts/usepermission/UsePermissionTest26.java
index e64838b..81b334e 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp26/src/com/android/cts/usepermission/UsePermissionTest26.java
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp26/src/com/android/cts/usepermission/UsePermissionTest26.java
@@ -16,6 +16,7 @@
package com.android.cts.usepermission;
+import static com.android.compatibility.common.util.UiAutomatorUtils.getUiDevice;
import static junit.framework.Assert.assertEquals;
import android.Manifest;
@@ -42,21 +43,17 @@
// request only one permission from the 'SMS' permission group at runtime,
// but two from this group are <uses-permission> in the manifest
// request only one permission from the 'contacts' permission group
- BasePermissionActivity.Result result = requestPermissions(permissions,
- REQUEST_CODE_PERMISSIONS,
- BasePermissionActivity.class,
- () -> {
- try {
- clickAllowButton();
- getUiDevice().waitForIdle();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- });
+ BasePermissionActivity.Result result = requestPermissions(permissions, () -> {
+ try {
+ clickAllowButton();
+ getUiDevice().waitForIdle();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ });
// Expect the permission is granted
- assertPermissionRequestResult(result, REQUEST_CODE_PERMISSIONS,
- permissions, new boolean[]{true});
+ assertPermissionRequestResult(result, permissions, new boolean[]{true});
assertEquals(PackageManager.PERMISSION_DENIED, getInstrumentation().getTargetContext()
.checkSelfPermission(Manifest.permission.SEND_SMS));
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp28/OWNERS b/hostsidetests/appsecurity/test-apps/UsePermissionApp28/OWNERS
new file mode 100644
index 0000000..8075c9c
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp28/OWNERS
@@ -0,0 +1,2 @@
+moltmann@google.com
+eugenesusla@google.com
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp28/src/com/android/cts/usepermission/UsePermissionTest28.java b/hostsidetests/appsecurity/test-apps/UsePermissionApp28/src/com/android/cts/usepermission/UsePermissionTest28.java
index 1c32d39..4dd5349 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp28/src/com/android/cts/usepermission/UsePermissionTest28.java
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp28/src/com/android/cts/usepermission/UsePermissionTest28.java
@@ -23,9 +23,7 @@
import static junit.framework.Assert.assertEquals;
-import android.Manifest;
import android.content.Context;
-import android.content.pm.PackageManager;
import org.junit.Test;
@@ -46,19 +44,15 @@
// request only foreground permission. This should automatically also add the background
// permission
- BasePermissionActivity.Result result = requestPermissions(permissions,
- REQUEST_CODE_PERMISSIONS,
- BasePermissionActivity.class,
- () -> {
- try {
- clickAllowAlwaysButton();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- });
+ BasePermissionActivity.Result result = requestPermissions(permissions, () -> {
+ try {
+ clickAllowAlwaysButton();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ });
- assertPermissionRequestResult(result, REQUEST_CODE_PERMISSIONS, permissions,
- new boolean[]{true});
+ assertPermissionRequestResult(result, permissions, new boolean[]{true});
assertEquals(PERMISSION_GRANTED, context.checkSelfPermission(ACCESS_FINE_LOCATION));
assertEquals(PERMISSION_GRANTED, context.checkSelfPermission(ACCESS_BACKGROUND_LOCATION));
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp29/OWNERS b/hostsidetests/appsecurity/test-apps/UsePermissionApp29/OWNERS
new file mode 100644
index 0000000..8075c9c
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp29/OWNERS
@@ -0,0 +1,2 @@
+moltmann@google.com
+eugenesusla@google.com
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp29/src/com/android/cts/usepermission/UsePermissionTest29.java b/hostsidetests/appsecurity/test-apps/UsePermissionApp29/src/com/android/cts/usepermission/UsePermissionTest29.java
index e0e21eb..3ced1c4 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp29/src/com/android/cts/usepermission/UsePermissionTest29.java
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp29/src/com/android/cts/usepermission/UsePermissionTest29.java
@@ -21,6 +21,8 @@
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static com.android.compatibility.common.util.UiAutomatorUtils.getUiDevice;
+
import static junit.framework.Assert.assertEquals;
import org.junit.Before;
@@ -48,25 +50,21 @@
private BasePermissionActivity.Result requestPermissions(String[] permissions,
UiInteraction... uiInteractions) throws Exception {
- return super.requestPermissions(permissions,
- REQUEST_CODE_PERMISSIONS,
- BasePermissionActivity.class,
- () -> {
- try {
- for (UiInteraction uiInteraction : uiInteractions) {
- uiInteraction.run();
- getUiDevice().waitForIdle();
- }
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- });
+ return requestPermissions(permissions, () -> {
+ try {
+ for (UiInteraction uiInteraction : uiInteractions) {
+ uiInteraction.run();
+ getUiDevice().waitForIdle();
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ });
}
- private static void assertPermissionRequestResult(BasePermissionActivity.Result result,
+ protected static void assertPermissionRequestResult(BasePermissionActivity.Result result,
String[] permissions, boolean... granted) {
- BasePermissionsTest.assertPermissionRequestResult(result, REQUEST_CODE_PERMISSIONS,
- permissions, granted);
+ BasePermissionsTest.assertPermissionRequestResult(result, permissions, granted);
}
@Before
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionAppLatest/Android.mk b/hostsidetests/appsecurity/test-apps/UsePermissionAppLatest/Android.mk
index 6becbc8..cddfe17 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionAppLatest/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionAppLatest/Android.mk
@@ -23,7 +23,8 @@
androidx.test.rules \
compatibility-device-util-axt \
ctstestrunner-axt \
- ub-uiautomator
+ ub-uiautomator \
+ compatibility-device-util-axt \
LOCAL_SRC_FILES := $(call all-java-files-under, ../UsePermissionApp26/src) \
$(call all-java-files-under, ../UsePermissionApp29/src) \
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionAppLatest/OWNERS b/hostsidetests/appsecurity/test-apps/UsePermissionAppLatest/OWNERS
new file mode 100644
index 0000000..8075c9c
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionAppLatest/OWNERS
@@ -0,0 +1,2 @@
+moltmann@google.com
+eugenesusla@google.com
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/AndroidManifest.xml
index e587e09..18f6096 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/AndroidManifest.xml
@@ -35,6 +35,7 @@
<activity android:name=".ReceiveUriActivity" android:exported="true"
android:launchMode="singleTop" />
<service android:name=".ReceiveUriService" android:exported="true" />
+ <activity android:name=".GetResultActivity" android:exported="true" />
</application>
<instrumentation android:targetPackage="com.android.cts.usespermissiondiffcertapp"
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/OWNERS b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/OWNERS
new file mode 100644
index 0000000..8075c9c
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/OWNERS
@@ -0,0 +1,2 @@
+moltmann@google.com
+eugenesusla@google.com
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
index 30743c9..d999895 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
@@ -16,38 +16,26 @@
package com.android.cts.usespermissiondiffcertapp;
-import static com.android.cts.permissiondeclareapp.UtilsProvider.ACTION_CLEAR_PRIMARY_CLIP;
-import static com.android.cts.permissiondeclareapp.UtilsProvider.ACTION_GRANT_URI;
-import static com.android.cts.permissiondeclareapp.UtilsProvider.ACTION_REVOKE_URI;
-import static com.android.cts.permissiondeclareapp.UtilsProvider.ACTION_SET_PRIMARY_CLIP;
-import static com.android.cts.permissiondeclareapp.UtilsProvider.ACTION_START_ACTIVITY;
-import static com.android.cts.permissiondeclareapp.UtilsProvider.ACTION_START_SERVICE;
-import static com.android.cts.permissiondeclareapp.UtilsProvider.ACTION_VERIFY_OUTGOING_PERSISTED;
-import static com.android.cts.permissiondeclareapp.UtilsProvider.EXTRA_INTENT;
-import static com.android.cts.permissiondeclareapp.UtilsProvider.EXTRA_MODE;
-import static com.android.cts.permissiondeclareapp.UtilsProvider.EXTRA_PACKAGE_NAME;
-import static com.android.cts.permissiondeclareapp.UtilsProvider.EXTRA_URI;
+import static com.android.cts.usespermissiondiffcertapp.Asserts.assertContentUriAllowed;
+import static com.android.cts.usespermissiondiffcertapp.Asserts.assertContentUriNotAllowed;
+import static com.android.cts.usespermissiondiffcertapp.Asserts.assertReadingContentUriAllowed;
+import static com.android.cts.usespermissiondiffcertapp.Asserts.assertReadingContentUriNotAllowed;
+import static com.android.cts.usespermissiondiffcertapp.Asserts.assertWritingContentUriAllowed;
+import static com.android.cts.usespermissiondiffcertapp.Asserts.assertWritingContentUriNotAllowed;
-import android.content.ClipData;
-import android.content.ClipboardManager;
-import android.content.ContentResolver;
-import android.content.ContentValues;
+import static junit.framework.Assert.assertEquals;
+
+import android.content.Context;
import android.content.Intent;
-import android.content.UriPermission;
-import android.database.Cursor;
import android.net.Uri;
-import android.os.Bundle;
import android.provider.CalendarContract;
import android.provider.ContactsContract;
-import android.test.AndroidTestCase;
-import android.util.Log;
import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
-import com.android.cts.permissiondeclareapp.UtilsProvider;
-
-import java.io.IOException;
-import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
/**
* Tests that signature-enforced permissions cannot be accessed by apps signed
@@ -55,209 +43,79 @@
*
* Accesses app cts/tests/appsecurity-tests/test-apps/PermissionDeclareApp/...
*/
-public class AccessPermissionWithDiffSigTest extends AndroidTestCase {
- private static final Uri PERM_URI = Uri.parse("content://ctspermissionwithsignature");
- private static final Uri PERM_URI_GRANTING = Uri.parse("content://ctspermissionwithsignaturegranting");
- private static final Uri PERM_URI_PATH = Uri.parse("content://ctspermissionwithsignaturepath");
- private static final Uri PERM_URI_PATH_RESTRICTING = Uri.parse(
+@RunWith(AndroidJUnit4.class)
+public class AccessPermissionWithDiffSigTest {
+ private static Context getContext() {
+ return InstrumentationRegistry.getTargetContext();
+ }
+
+ static final Uri PERM_URI = Uri.parse("content://ctspermissionwithsignature");
+ static final Uri PERM_URI_GRANTING = Uri.parse("content://ctspermissionwithsignaturegranting");
+ static final Uri PERM_URI_PATH = Uri.parse("content://ctspermissionwithsignaturepath");
+ static final Uri PERM_URI_PATH_RESTRICTING = Uri.parse(
"content://ctspermissionwithsignaturepathrestricting");
- private static final Uri PRIV_URI = Uri.parse("content://ctsprivateprovider");
- private static final Uri PRIV_URI_GRANTING = Uri.parse("content://ctsprivateprovidergranting");
- private static final String EXPECTED_MIME_TYPE = "got/theMIME";
+ static final Uri PRIV_URI = Uri.parse("content://ctsprivateprovider");
+ static final Uri PRIV_URI_GRANTING = Uri.parse("content://ctsprivateprovidergranting");
+ static final String EXPECTED_MIME_TYPE = "got/theMIME";
- private static final Uri AMBIGUOUS_URI_COMPAT = Uri.parse("content://ctsambiguousprovidercompat");
- private static final String EXPECTED_MIME_TYPE_AMBIGUOUS = "got/theUnspecifiedMIME";
- private static final Uri AMBIGUOUS_URI = Uri.parse("content://ctsambiguousprovider");
+ static final Uri AMBIGUOUS_URI_COMPAT = Uri.parse("content://ctsambiguousprovidercompat");
+ static final String EXPECTED_MIME_TYPE_AMBIGUOUS = "got/theUnspecifiedMIME";
+ static final Uri AMBIGUOUS_URI = Uri.parse("content://ctsambiguousprovider");
- private static final Uri[] GRANTABLE = new Uri[] {
+ static final Uri[] GRANTABLE = new Uri[] {
Uri.withAppendedPath(PERM_URI_GRANTING, "foo"),
- Uri.withAppendedPath(PRIV_URI_GRANTING, "foo"),
Uri.withAppendedPath(PERM_URI_PATH, "foo"),
+ Uri.withAppendedPath(PRIV_URI_GRANTING, "foo"),
};
- private static final Uri[] NOT_GRANTABLE = new Uri[] {
- Uri.withAppendedPath(PERM_URI, "foo"),
- Uri.withAppendedPath(PRIV_URI, "foo"),
- Uri.withAppendedPath(PERM_URI_PATH_RESTRICTING, "foo"),
+ static final Uri[] NOT_GRANTABLE = new Uri[] {
+ Uri.withAppendedPath(PERM_URI, "bar"),
+ Uri.withAppendedPath(PERM_URI_GRANTING, "bar"),
+ Uri.withAppendedPath(PERM_URI_PATH, "bar"),
+ Uri.withAppendedPath(PRIV_URI, "bar"),
+ Uri.withAppendedPath(PRIV_URI_GRANTING, "bar"),
+ Uri.withAppendedPath(AMBIGUOUS_URI, "bar"),
CalendarContract.CONTENT_URI,
ContactsContract.AUTHORITY_URI,
};
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
+ static final int[] GRANTABLE_MODES = new int[] {
+ Intent.FLAG_GRANT_READ_URI_PERMISSION,
+ Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
+ };
- // Always dispose, usually to clean up from failed tests
- ReceiveUriActivity.finishCurInstanceSync();
- }
-
- private void assertReadingContentUriNotAllowed(Uri uri, String msg) {
- try {
- getContext().getContentResolver().query(uri, null, null, null, null);
- fail("expected SecurityException reading " + uri + ": " + msg);
- } catch (SecurityException expected) {
- assertNotNull("security exception's error message.", expected.getMessage());
- }
- }
-
- private void assertReadingContentUriAllowed(Uri uri) {
- try {
- getContext().getContentResolver().query(uri, null, null, null, null);
- } catch (SecurityException e) {
- fail("unexpected SecurityException reading " + uri + ": " + e.getMessage());
- }
- }
-
- private void assertReadingClipNotAllowed(ClipData clip) {
- assertReadingClipNotAllowed(clip, null);
- }
-
- private void assertReadingClipNotAllowed(ClipData clip, String msg) {
- for (int i=0; i<clip.getItemCount(); i++) {
- ClipData.Item item = clip.getItemAt(i);
- Uri uri = item.getUri();
- if (uri != null) {
- assertReadingContentUriNotAllowed(uri, msg);
- } else {
- Intent intent = item.getIntent();
- uri = intent.getData();
- if (uri != null) {
- assertReadingContentUriNotAllowed(uri, msg);
- }
- ClipData intentClip = intent.getClipData();
- if (intentClip != null) {
- assertReadingClipNotAllowed(intentClip, msg);
- }
- }
- }
- }
-
- private void assertOpenFileDescriptorModeNotAllowed(Uri uri, String msg, String mode) {
- try {
- getContext().getContentResolver().openFileDescriptor(uri, mode).close();
- fail("expected SecurityException writing " + uri + ": " + msg);
- } catch (IOException e) {
- throw new IllegalStateException(e);
- } catch (SecurityException expected) {
- assertNotNull("security exception's error message.", expected.getMessage());
- }
- }
-
- private void assertContentUriAllowed(Uri uri) {
- assertReadingContentUriAllowed(uri);
- assertWritingContentUriAllowed(uri);
- }
-
- private void assertContentUriNotAllowed(Uri uri, String msg) {
- assertReadingContentUriNotAllowed(uri, msg);
- assertWritingContentUriNotAllowed(uri, msg);
- }
-
- private void assertWritingContentUriNotAllowed(Uri uri, String msg) {
- final ContentResolver resolver = getContext().getContentResolver();
- try {
- resolver.insert(uri, new ContentValues());
- fail("expected SecurityException inserting " + uri + ": " + msg);
- } catch (SecurityException expected) {
- assertNotNull("security exception's error message.", expected.getMessage());
- }
-
- try {
- resolver.update(uri, new ContentValues(), null, null);
- fail("expected SecurityException updating " + uri + ": " + msg);
- } catch (SecurityException expected) {
- assertNotNull("security exception's error message.", expected.getMessage());
- }
-
- try {
- resolver.delete(uri, null, null);
- fail("expected SecurityException deleting " + uri + ": " + msg);
- } catch (SecurityException expected) {
- assertNotNull("security exception's error message.", expected.getMessage());
- }
-
- try {
- getContext().getContentResolver().openOutputStream(uri).close();
- fail("expected SecurityException writing " + uri + ": " + msg);
- } catch (IOException e) {
- throw new IllegalStateException(e);
- } catch (SecurityException expected) {
- assertNotNull("security exception's error message.", expected.getMessage());
- }
-
- assertOpenFileDescriptorModeNotAllowed(uri, msg, "w");
- assertOpenFileDescriptorModeNotAllowed(uri, msg, "wt");
- assertOpenFileDescriptorModeNotAllowed(uri, msg, "wa");
- assertOpenFileDescriptorModeNotAllowed(uri, msg, "rw");
- assertOpenFileDescriptorModeNotAllowed(uri, msg, "rwt");
- }
-
- private void assertWritingContentUriAllowed(Uri uri) {
- final ContentResolver resolver = getContext().getContentResolver();
- try {
- resolver.insert(uri, new ContentValues());
- resolver.update(uri, new ContentValues(), null, null);
- resolver.delete(uri, null, null);
-
- resolver.openOutputStream(uri).close();
- resolver.openFileDescriptor(uri, "w").close();
- resolver.openFileDescriptor(uri, "wt").close();
- resolver.openFileDescriptor(uri, "wa").close();
- resolver.openFileDescriptor(uri, "rw").close();
- resolver.openFileDescriptor(uri, "rwt").close();
- } catch (IOException e) {
- fail("unexpected IOException writing " + uri + ": " + e.getMessage());
- } catch (SecurityException e) {
- fail("unexpected SecurityException writing " + uri + ": " + e.getMessage());
- }
- }
-
- private void assertWritingClipNotAllowed(ClipData clip) {
- assertWritingClipNotAllowed(clip, null);
- }
-
- private void assertWritingClipNotAllowed(ClipData clip, String msg) {
- for (int i=0; i<clip.getItemCount(); i++) {
- ClipData.Item item = clip.getItemAt(i);
- Uri uri = item.getUri();
- if (uri != null) {
- assertWritingContentUriNotAllowed(uri, msg);
- } else {
- Intent intent = item.getIntent();
- uri = intent.getData();
- if (uri != null) {
- assertWritingContentUriNotAllowed(uri, msg);
- }
- ClipData intentClip = intent.getClipData();
- if (intentClip != null) {
- assertWritingClipNotAllowed(intentClip, msg);
- }
- }
- }
- }
+ static final int[] NOT_GRANTABLE_MODES = new int[] {
+ Intent.FLAG_GRANT_READ_URI_PERMISSION,
+ Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION,
+ Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION,
+ Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION,
+ };
/**
* Test that the ctspermissionwithsignature content provider cannot be read,
* since this app lacks the required certs
*/
+ @Test
public void testReadProviderWithDiff() {
- assertReadingContentUriRequiresPermission(PERM_URI,
- "com.android.cts.permissionWithSignature");
+ assertReadingContentUriNotAllowed(PERM_URI, null);
}
/**
* Test that the ctspermissionwithsignature content provider cannot be written,
* since this app lacks the required certs
*/
+ @Test
public void testWriteProviderWithDiff() {
- assertWritingContentUriRequiresPermission(PERM_URI,
- "com.android.cts.permissionWithSignature");
+ assertWritingContentUriNotAllowed(PERM_URI, null);
}
/**
* Test that the ctsprivateprovider content provider cannot be read,
* since it is not exported from its app.
*/
+ @Test
public void testReadProviderWhenPrivate() {
assertReadingContentUriNotAllowed(PRIV_URI, "shouldn't read private provider");
}
@@ -266,6 +124,7 @@
* Test that the ctsambiguousprovider content provider cannot be read,
* since it doesn't have an "exported=" line.
*/
+ @Test
public void testReadProviderWhenAmbiguous() {
assertReadingContentUriNotAllowed(AMBIGUOUS_URI, "shouldn't read ambiguous provider");
}
@@ -276,6 +135,7 @@
* Test that the ctsambiguousprovidercompat content provider can be read for older
* API versions, because it didn't specify either exported=true or exported=false.
*/
+ @Test
public void testReadProviderWhenAmbiguousCompat() {
assertReadingContentUriAllowed(AMBIGUOUS_URI_COMPAT);
}
@@ -286,6 +146,7 @@
* Test that the ctsambiguousprovidercompat content provider can be written for older
* API versions, because it didn't specify either exported=true or exported=false.
*/
+ @Test
public void testWriteProviderWhenAmbiguousCompat() {
assertWritingContentUriAllowed(AMBIGUOUS_URI_COMPAT);
}
@@ -294,6 +155,7 @@
* Test that the ctsprivateprovider content provider cannot be written,
* since it is not exported from its app.
*/
+ @Test
public void testWriteProviderWhenPrivate() {
assertWritingContentUriNotAllowed(PRIV_URI, "shouldn't write private provider");
}
@@ -302,858 +164,16 @@
* Test that the ctsambiguousprovider content provider cannot be written,
* since it doesn't have an exported= line.
*/
+ @Test
public void testWriteProviderWhenAmbiguous() {
assertWritingContentUriNotAllowed(AMBIGUOUS_URI, "shouldn't write ambiguous provider");
}
- private static ClipData makeSingleClipData(Uri uri) {
- return new ClipData("foo", new String[] { "foo/bar" },
- new ClipData.Item(uri));
- }
-
- private static ClipData makeMultiClipData(Uri uri) {
- Uri grantClip1Uri = Uri.withAppendedPath(uri, "clip1");
- Uri grantClip2Uri = Uri.withAppendedPath(uri, "clip2");
- Uri grantClip3Uri = Uri.withAppendedPath(uri, "clip3");
- Uri grantClip4Uri = Uri.withAppendedPath(uri, "clip4");
- Uri grantClip5Uri = Uri.withAppendedPath(uri, "clip5");
- ClipData clip = new ClipData("foo", new String[] { "foo/bar" },
- new ClipData.Item(grantClip1Uri));
- clip.addItem(new ClipData.Item(grantClip2Uri));
- // Intents in the ClipData should allow their data: and clip URIs
- // to be granted, but only respect the grant flags of the top-level
- // Intent.
- clip.addItem(new ClipData.Item(new Intent(Intent.ACTION_VIEW, grantClip3Uri)));
- Intent intent = new Intent(Intent.ACTION_VIEW, grantClip4Uri);
- intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
- | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
- clip.addItem(new ClipData.Item(intent));
- intent = new Intent(Intent.ACTION_VIEW);
- intent.setClipData(new ClipData("foo", new String[] { "foo/bar" },
- new ClipData.Item(grantClip5Uri)));
- clip.addItem(new ClipData.Item(intent));
- return clip;
- }
-
- private static Intent makeClipIntent(ClipData clip, int flags) {
- Intent intent = new Intent();
- intent.setClipData(clip);
- intent.addFlags(flags);
- return intent;
- }
-
- private static Intent makeClipIntent(Uri uri, int flags) {
- return makeClipIntent(makeMultiClipData(uri), flags);
- }
-
- private void doTryGrantUriActivityPermissionToSelf(Uri uri, int mode) {
- Uri grantDataUri = Uri.withAppendedPath(uri, "data");
- Intent grantIntent = new Intent();
- grantIntent.setData(grantDataUri);
- grantIntent.addFlags(mode | Intent.FLAG_ACTIVITY_NEW_TASK);
- grantIntent.setClass(getContext(), ReceiveUriActivity.class);
- try {
- ReceiveUriActivity.clearStarted();
- getContext().startActivity(grantIntent);
- ReceiveUriActivity.waitForStart();
- fail("expected SecurityException granting " + grantDataUri + " to activity");
- } catch (SecurityException e) {
- // This is what we want.
- }
-
- grantIntent = makeClipIntent(uri, mode | Intent.FLAG_ACTIVITY_NEW_TASK);
- grantIntent.setClass(getContext(), ReceiveUriActivity.class);
- try {
- ReceiveUriActivity.clearStarted();
- getContext().startActivity(grantIntent);
- ReceiveUriActivity.waitForStart();
- fail("expected SecurityException granting " + grantIntent.getClipData() + " to activity");
- } catch (SecurityException e) {
- // This is what we want.
- }
- }
-
- /**
- * Test that we can't grant a permission to ourself.
- */
- public void testGrantReadUriActivityPermissionToSelf() {
- doTryGrantUriActivityPermissionToSelf(
- Uri.withAppendedPath(PERM_URI_GRANTING, "foo"),
- Intent.FLAG_GRANT_READ_URI_PERMISSION);
- }
-
- /**
- * Test that we can't grant a permission to ourself.
- */
- public void testGrantWriteUriActivityPermissionToSelf() {
- doTryGrantUriActivityPermissionToSelf(
- Uri.withAppendedPath(PERM_URI_GRANTING, "foo"),
- Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
- }
-
- /**
- * Test that we can't grant a permission to ourself.
- */
- public void testGrantReadUriActivityPrivateToSelf() {
- doTryGrantUriActivityPermissionToSelf(
- Uri.withAppendedPath(PRIV_URI_GRANTING, "foo"),
- Intent.FLAG_GRANT_READ_URI_PERMISSION);
- }
-
- /**
- * Test that we can't grant a permission to ourself.
- */
- public void testGrantWriteUriActivityPrivateToSelf() {
- doTryGrantUriActivityPermissionToSelf(
- Uri.withAppendedPath(PRIV_URI_GRANTING, "foo"),
- Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
- }
-
- private void doTryGrantUriServicePermissionToSelf(Uri uri, int mode) {
- Uri grantDataUri = Uri.withAppendedPath(uri, "data");
- Intent grantIntent = new Intent();
- grantIntent.setData(grantDataUri);
- grantIntent.addFlags(mode);
- grantIntent.setClass(getContext(), ReceiveUriService.class);
- try {
- getContext().startService(grantIntent);
- fail("expected SecurityException granting " + grantDataUri + " to service");
- } catch (SecurityException e) {
- // This is what we want.
- }
-
- grantIntent = makeClipIntent(uri, mode);
- grantIntent.setClass(getContext(), ReceiveUriService.class);
- try {
- getContext().startService(grantIntent);
- fail("expected SecurityException granting " + grantIntent.getClipData() + " to service");
- } catch (SecurityException e) {
- // This is what we want.
- }
- }
-
- /**
- * Test that we can't grant a permission to ourself.
- */
- public void testGrantReadUriServicePermissionToSelf() {
- doTryGrantUriServicePermissionToSelf(
- Uri.withAppendedPath(PERM_URI_GRANTING, "foo"),
- Intent.FLAG_GRANT_READ_URI_PERMISSION);
- }
-
- /**
- * Test that we can't grant a permission to ourself.
- */
- public void testGrantWriteUriServicePermissionToSelf() {
- doTryGrantUriServicePermissionToSelf(
- Uri.withAppendedPath(PERM_URI_GRANTING, "foo"),
- Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
- }
-
- /**
- * Test that we can't grant a permission to ourself.
- */
- public void testGrantReadUriServicePrivateToSelf() {
- doTryGrantUriServicePermissionToSelf(
- Uri.withAppendedPath(PRIV_URI_GRANTING, "foo"),
- Intent.FLAG_GRANT_READ_URI_PERMISSION);
- }
-
- /**
- * Test that we can't grant a permission to ourself.
- */
- public void testGrantWriteUriServicePrivateToSelf() {
- doTryGrantUriServicePermissionToSelf(
- Uri.withAppendedPath(PRIV_URI_GRANTING, "foo"),
- Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
- }
-
- private void grantUriPermissionFail(Uri uri, int mode, boolean service) {
- Uri grantDataUri = Uri.withAppendedPath(uri, "data");
- Intent grantIntent = new Intent();
- grantIntent.setData(grantDataUri);
- grantIntent.addFlags(mode);
- grantIntent.setClass(getContext(),
- service ? ReceiveUriService.class : ReceiveUriActivity.class);
- Intent intent = new Intent();
- intent.setAction(service ? ACTION_START_SERVICE : ACTION_START_ACTIVITY);
- intent.putExtra(EXTRA_INTENT, grantIntent);
- try {
- call(intent);
- fail("Able to grant URI permission to " + grantDataUri + " when should not");
- } catch (Exception expected) {
- }
-
- grantIntent = makeClipIntent(uri, mode);
- grantIntent.setClass(getContext(),
- service ? ReceiveUriService.class : ReceiveUriActivity.class);
- intent = new Intent();
- intent.setAction(service ? ACTION_START_SERVICE : ACTION_START_ACTIVITY);
- intent.putExtra(EXTRA_INTENT, grantIntent);
- try {
- call(intent);
- fail("Able to grant URI permission to " + grantIntent.getClipData()
- + " when should not");
- } catch (Exception expected) {
- }
- }
-
- private void doTestGrantUriPermissionFail(Uri uri) {
- for (boolean service : new boolean[] { false, true }) {
- for (int flags : new int[] {
- Intent.FLAG_GRANT_READ_URI_PERMISSION, Intent.FLAG_GRANT_WRITE_URI_PERMISSION
- }) {
- grantUriPermissionFail(uri,
- flags, service);
- grantUriPermissionFail(uri,
- flags | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION, service);
- grantUriPermissionFail(uri,
- flags | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION, service);
- }
- }
- }
-
- /**
- * Test that the ctspermissionwithsignature content provider can not grant
- * URI permissions to others.
- */
- public void testGrantPermissionNonGrantingFail() {
- doTestGrantUriPermissionFail(PERM_URI);
- }
-
- /**
- * Test that the ctspermissionwithsignaturegranting content provider can not grant
- * URI permissions to paths outside of the grant tree
- */
- public void testGrantPermissionOutsideGrantingFail() {
- doTestGrantUriPermissionFail(PERM_URI_GRANTING);
- doTestGrantUriPermissionFail(Uri.withAppendedPath(PERM_URI_GRANTING, "invalid"));
- }
-
- /**
- * Test that the ctsprivateprovider content provider can not grant
- * URI permissions to others.
- */
- public void testGrantPrivateNonGrantingFail() {
- doTestGrantUriPermissionFail(PRIV_URI);
- }
-
- /**
- * Test that the ctsambiguousprovider content provider can not grant
- * URI permissions to others.
- */
- public void testGrantAmbiguousNonGrantingFail() {
- doTestGrantUriPermissionFail(AMBIGUOUS_URI);
- }
-
- /**
- * Test that the ctsprivateprovidergranting content provider can not grant
- * URI permissions to paths outside of the grant tree
- */
- public void testGrantPrivateOutsideGrantingFail() {
- doTestGrantUriPermissionFail(PRIV_URI_GRANTING);
- doTestGrantUriPermissionFail(Uri.withAppendedPath(PRIV_URI_GRANTING, "invalid"));
- }
-
- private void call(Intent intent) {
- final Bundle extras = new Bundle();
- extras.putParcelable(Intent.EXTRA_INTENT, intent);
- getContext().getContentResolver().call(UtilsProvider.URI, "", "", extras);
- }
-
- private void grantClipUriPermission(ClipData clip, int mode, boolean service) {
- Intent grantIntent = new Intent();
- if (clip.getItemCount() == 1) {
- grantIntent.setData(clip.getItemAt(0).getUri());
- } else {
- grantIntent.setClipData(clip);
- // Make this Intent unique from the one that started it.
- for (int i=0; i<clip.getItemCount(); i++) {
- Uri uri = clip.getItemAt(i).getUri();
- if (uri != null) {
- grantIntent.addCategory(uri.toString());
- }
- }
- }
- grantIntent.addFlags(mode);
- grantIntent.setClass(getContext(),
- service ? ReceiveUriService.class : ReceiveUriActivity.class);
- Intent intent = new Intent();
- intent.setAction(service ? ACTION_START_SERVICE : ACTION_START_ACTIVITY);
- intent.putExtra(EXTRA_INTENT, grantIntent);
- call(intent);
- }
-
- private void grantClipUriPermissionViaContext(Uri uri, int mode) {
- Intent intent = new Intent();
- intent.setAction(ACTION_GRANT_URI);
- intent.putExtra(EXTRA_PACKAGE_NAME, getContext().getPackageName());
- intent.putExtra(EXTRA_URI, uri);
- intent.putExtra(EXTRA_MODE, mode);
- call(intent);
- }
-
- private void revokeClipUriPermissionViaContext(Uri uri, int mode) {
- Intent intent = new Intent();
- intent.setAction(ACTION_REVOKE_URI);
- intent.putExtra(EXTRA_URI, uri);
- intent.putExtra(EXTRA_MODE, mode);
- call(intent);
- }
-
- private void setPrimaryClip(ClipData clip) {
- Intent intent = new Intent();
- intent.setAction(ACTION_SET_PRIMARY_CLIP);
- intent.setClipData(clip);
- call(intent);
- }
-
- private void clearPrimaryClip() {
- Intent intent = new Intent();
- intent.setAction(ACTION_CLEAR_PRIMARY_CLIP);
- call(intent);
- }
-
- private void assertReadingClipAllowed(ClipData clip) {
- for (int i=0; i<clip.getItemCount(); i++) {
- ClipData.Item item = clip.getItemAt(i);
- Uri uri = item.getUri();
- if (uri != null) {
- Cursor c = getContext().getContentResolver().query(uri,
- null, null, null, null);
- if (c != null) {
- c.close();
- }
- } else {
- Intent intent = item.getIntent();
- uri = intent.getData();
- if (uri != null) {
- Cursor c = getContext().getContentResolver().query(uri,
- null, null, null, null);
- if (c != null) {
- c.close();
- }
- }
- ClipData intentClip = intent.getClipData();
- if (intentClip != null) {
- assertReadingClipAllowed(intentClip);
- }
- }
- }
- }
-
- private void doTestGrantActivityUriReadPermission(Uri uri, boolean useClip) {
- final Uri subUri = Uri.withAppendedPath(uri, "foo");
- final Uri subSubUri = Uri.withAppendedPath(subUri, "bar");
- final Uri sub2Uri = Uri.withAppendedPath(uri, "yes");
- final Uri sub2SubUri = Uri.withAppendedPath(sub2Uri, "no");
-
- final ClipData subClip = useClip ? makeMultiClipData(subUri) : makeSingleClipData(subUri);
- final ClipData sub2Clip = useClip ? makeMultiClipData(sub2Uri) : makeSingleClipData(sub2Uri);
-
- // Precondition: no current access.
- assertReadingClipNotAllowed(subClip, "shouldn't read when starting test");
- assertReadingClipNotAllowed(sub2Clip, "shouldn't read when starting test");
-
- // --------------------------------
-
- ReceiveUriActivity.clearStarted();
- grantClipUriPermission(subClip, Intent.FLAG_GRANT_READ_URI_PERMISSION, false);
- ReceiveUriActivity.waitForStart();
-
- // See if we now have access to the provider.
- assertReadingClipAllowed(subClip);
-
- // But not writing.
- assertWritingClipNotAllowed(subClip, "shouldn't write from granted read");
-
- // And not to the base path.
- assertReadingContentUriNotAllowed(uri, "shouldn't read non-granted base URI");
-
- // And not to a sub path.
- assertReadingContentUriNotAllowed(subSubUri, "shouldn't read non-granted sub URI");
-
- // --------------------------------
-
- ReceiveUriActivity.clearNewIntent();
- grantClipUriPermission(sub2Clip, Intent.FLAG_GRANT_READ_URI_PERMISSION, false);
- ReceiveUriActivity.waitForNewIntent();
-
- if (false) {
- synchronized (this) {
- Log.i("**", "******************************* WAITING!!!");
- try {
- wait(10000);
- } catch (InterruptedException e) {
- }
- }
- }
-
- // See if we now have access to the provider.
- assertReadingClipAllowed(sub2Clip);
-
- // And still have access to the original URI.
- assertReadingClipAllowed(subClip);
-
- // But not writing.
- assertWritingClipNotAllowed(sub2Clip, "shouldn't write from granted read");
-
- // And not to the base path.
- assertReadingContentUriNotAllowed(uri, "shouldn't read non-granted base URI");
-
- // And not to a sub path.
- assertReadingContentUriNotAllowed(sub2SubUri, "shouldn't read non-granted sub URI");
-
- // And make sure we can't generate a permission to a running activity.
- doTryGrantUriActivityPermissionToSelf(
- Uri.withAppendedPath(uri, "hah"),
- Intent.FLAG_GRANT_READ_URI_PERMISSION);
- doTryGrantUriActivityPermissionToSelf(
- Uri.withAppendedPath(uri, "hah"),
- Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-
- // --------------------------------
-
- // Dispose of activity.
- ReceiveUriActivity.finishCurInstanceSync();
-
- synchronized (this) {
- Log.i("**", "******************************* WAITING!!!");
- try {
- wait(100);
- } catch (InterruptedException e) {
- }
- }
-
- // Ensure reading no longer allowed.
- assertReadingClipNotAllowed(subClip, "shouldn't read after losing granted URI");
- assertReadingClipNotAllowed(sub2Clip, "shouldn't read after losing granted URI");
- }
-
- private void assertWritingClipAllowed(ClipData clip) {
- for (int i=0; i<clip.getItemCount(); i++) {
- ClipData.Item item = clip.getItemAt(i);
- Uri uri = item.getUri();
- if (uri != null) {
- getContext().getContentResolver().insert(uri, new ContentValues());
- } else {
- Intent intent = item.getIntent();
- uri = intent.getData();
- if (uri != null) {
- getContext().getContentResolver().insert(uri, new ContentValues());
- }
- ClipData intentClip = intent.getClipData();
- if (intentClip != null) {
- assertWritingClipAllowed(intentClip);
- }
- }
- }
- }
-
- private void doTestGrantActivityUriWritePermission(Uri uri, boolean useClip) {
- final Uri subUri = Uri.withAppendedPath(uri, "foo");
- final Uri subSubUri = Uri.withAppendedPath(subUri, "bar");
- final Uri sub2Uri = Uri.withAppendedPath(uri, "yes");
- final Uri sub2SubUri = Uri.withAppendedPath(sub2Uri, "no");
-
- final ClipData subClip = useClip ? makeMultiClipData(subUri) : makeSingleClipData(subUri);
- final ClipData sub2Clip = useClip ? makeMultiClipData(sub2Uri) : makeSingleClipData(sub2Uri);
-
- // Precondition: no current access.
- assertWritingClipNotAllowed(subClip, "shouldn't write when starting test");
- assertWritingClipNotAllowed(sub2Clip, "shouldn't write when starting test");
-
- // --------------------------------
-
- ReceiveUriActivity.clearStarted();
- grantClipUriPermission(subClip, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, false);
- ReceiveUriActivity.waitForStart();
-
- // See if we now have access to the provider.
- assertWritingClipAllowed(subClip);
-
- // But not reading.
- assertReadingClipNotAllowed(subClip, "shouldn't read from granted write");
-
- // And not to the base path.
- assertWritingContentUriNotAllowed(uri, "shouldn't write non-granted base URI");
-
- // And not a sub-path.
- assertWritingContentUriNotAllowed(subSubUri, "shouldn't write non-granted sub URI");
-
- // --------------------------------
-
- ReceiveUriActivity.clearNewIntent();
- grantClipUriPermission(sub2Clip, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, false);
- ReceiveUriActivity.waitForNewIntent();
-
- if (false) {
- synchronized (this) {
- Log.i("**", "******************************* WAITING!!!");
- try {
- wait(10000);
- } catch (InterruptedException e) {
- }
- }
- }
-
- // See if we now have access to the provider.
- assertWritingClipAllowed(sub2Clip);
-
- // And still have access to the original URI.
- assertWritingClipAllowed(subClip);
-
- // But not reading.
- assertReadingClipNotAllowed(sub2Clip, "shouldn't read from granted write");
-
- // And not to the base path.
- assertWritingContentUriNotAllowed(uri, "shouldn't write non-granted base URI");
-
- // And not a sub-path.
- assertWritingContentUriNotAllowed(sub2SubUri, "shouldn't write non-granted sub URI");
-
- // And make sure we can't generate a permission to a running activity.
- doTryGrantUriActivityPermissionToSelf(
- Uri.withAppendedPath(uri, "hah"),
- Intent.FLAG_GRANT_READ_URI_PERMISSION);
- doTryGrantUriActivityPermissionToSelf(
- Uri.withAppendedPath(uri, "hah"),
- Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-
- // --------------------------------
-
- // Dispose of activity.
- ReceiveUriActivity.finishCurInstanceSync();
-
- synchronized (this) {
- Log.i("**", "******************************* WAITING!!!");
- try {
- wait(100);
- } catch (InterruptedException e) {
- }
- }
-
- // Ensure writing no longer allowed.
- assertWritingClipNotAllowed(subClip, "shouldn't write after losing granted URI");
- assertWritingClipNotAllowed(sub2Clip, "shouldn't write after losing granted URI");
- }
-
- /**
- * Test that the ctspermissionwithsignaturegranting content provider can grant a read
- * permission.
- */
- public void testGrantReadPermissionFromStartActivity() {
- doTestGrantActivityUriReadPermission(PERM_URI_GRANTING, false);
- doTestGrantActivityUriReadPermission(PERM_URI_GRANTING, true);
- }
-
- /**
- * Test that the ctspermissionwithsignaturegranting content provider can grant a write
- * permission.
- */
- public void testGrantWritePermissionFromStartActivity() {
- doTestGrantActivityUriWritePermission(PERM_URI_GRANTING, true);
- doTestGrantActivityUriWritePermission(PERM_URI_GRANTING, false);
- }
-
- /**
- * Test that the ctsprivateprovidergranting content provider can grant a read
- * permission.
- */
- public void testGrantReadPrivateFromStartActivity() {
- doTestGrantActivityUriReadPermission(PRIV_URI_GRANTING, false);
- doTestGrantActivityUriReadPermission(PRIV_URI_GRANTING, true);
- }
-
- /**
- * Test that the ctsprivateprovidergranting content provider can grant a write
- * permission.
- */
- public void testGrantWritePrivateFromStartActivity() {
- doTestGrantActivityUriWritePermission(PRIV_URI_GRANTING, true);
- doTestGrantActivityUriWritePermission(PRIV_URI_GRANTING, false);
- }
-
- private void doTestGrantServiceUriReadPermission(Uri uri, boolean useClip) {
- final Uri subUri = Uri.withAppendedPath(uri, "foo");
- final Uri subSubUri = Uri.withAppendedPath(subUri, "bar");
- final Uri sub2Uri = Uri.withAppendedPath(uri, "yes");
- final Uri sub2SubUri = Uri.withAppendedPath(sub2Uri, "no");
-
- ReceiveUriService.stop(getContext());
-
- final ClipData subClip = useClip ? makeMultiClipData(subUri) : makeSingleClipData(subUri);
- final ClipData sub2Clip = useClip ? makeMultiClipData(sub2Uri) : makeSingleClipData(sub2Uri);
-
- // Precondition: no current access.
- assertReadingClipNotAllowed(subClip, "shouldn't read when starting test");
- assertReadingClipNotAllowed(sub2Clip, "shouldn't read when starting test");
-
- // --------------------------------
-
- ReceiveUriService.clearStarted();
- grantClipUriPermission(subClip, Intent.FLAG_GRANT_READ_URI_PERMISSION, true);
- ReceiveUriService.waitForStart();
-
- int firstStartId = ReceiveUriService.getCurStartId();
-
- // See if we now have access to the provider.
- assertReadingClipAllowed(subClip);
-
- // But not writing.
- assertWritingClipNotAllowed(subClip, "shouldn't write from granted read");
-
- // And not to the base path.
- assertReadingContentUriNotAllowed(uri, "shouldn't read non-granted base URI");
-
- // And not to a sub path.
- assertReadingContentUriNotAllowed(subSubUri, "shouldn't read non-granted sub URI");
-
- // --------------------------------
-
- // Send another Intent to it.
- ReceiveUriService.clearStarted();
- grantClipUriPermission(sub2Clip, Intent.FLAG_GRANT_READ_URI_PERMISSION, true);
- ReceiveUriService.waitForStart();
-
- if (false) {
- synchronized (this) {
- Log.i("**", "******************************* WAITING!!!");
- try {
- wait(10000);
- } catch (InterruptedException e) {
- }
- }
- }
-
- // See if we now have access to the provider.
- assertReadingClipAllowed(sub2Clip);
-
- // And still to the previous URI.
- assertReadingClipAllowed(subClip);
-
- // But not writing.
- assertWritingClipNotAllowed(sub2Clip, "shouldn't write from granted read");
-
- // And not to the base path.
- assertReadingContentUriNotAllowed(uri, "shouldn't read non-granted base URI");
-
- // And not to a sub path.
- assertReadingContentUriNotAllowed(sub2SubUri, "shouldn't read non-granted sub URI");
-
- // --------------------------------
-
- // Stop the first command.
- ReceiveUriService.stopCurWithId(firstStartId);
-
- // Ensure reading no longer allowed.
- assertReadingClipNotAllowed(subClip, "shouldn't read after losing granted URI");
-
- // And make sure we can't generate a permission to a running service.
- doTryGrantUriActivityPermissionToSelf(subUri,
- Intent.FLAG_GRANT_READ_URI_PERMISSION);
- doTryGrantUriActivityPermissionToSelf(subUri,
- Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-
- // --------------------------------
-
- // Dispose of service.
- ReceiveUriService.stopSync(getContext());
-
- // Ensure reading no longer allowed.
- assertReadingClipNotAllowed(subClip, "shouldn't read after losing granted URI");
- assertReadingClipNotAllowed(sub2Clip, "shouldn't read after losing granted URI");
- }
-
- private void doTestGrantServiceUriWritePermission(Uri uri, boolean useClip) {
- final Uri subUri = Uri.withAppendedPath(uri, "foo");
- final Uri subSubUri = Uri.withAppendedPath(subUri, "bar");
- final Uri sub2Uri = Uri.withAppendedPath(uri, "yes");
- final Uri sub2SubUri = Uri.withAppendedPath(sub2Uri, "no");
-
- ReceiveUriService.stop(getContext());
-
- final ClipData subClip = useClip ? makeMultiClipData(subUri) : makeSingleClipData(subUri);
- final ClipData sub2Clip = useClip ? makeMultiClipData(sub2Uri) : makeSingleClipData(sub2Uri);
-
- // Precondition: no current access.
- assertReadingClipNotAllowed(subClip, "shouldn't read when starting test");
- assertReadingClipNotAllowed(sub2Clip, "shouldn't read when starting test");
-
- // --------------------------------
-
- ReceiveUriService.clearStarted();
- grantClipUriPermission(subClip, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, true);
- ReceiveUriService.waitForStart();
-
- int firstStartId = ReceiveUriService.getCurStartId();
-
- // See if we now have access to the provider.
- assertWritingClipAllowed(subClip);
-
- // But not reading.
- assertReadingClipNotAllowed(subClip, "shouldn't read from granted write");
-
- // And not to the base path.
- assertWritingContentUriNotAllowed(uri, "shouldn't write non-granted base URI");
-
- // And not a sub-path.
- assertWritingContentUriNotAllowed(subSubUri, "shouldn't write non-granted sub URI");
-
- // --------------------------------
-
- // Send another Intent to it.
- ReceiveUriService.clearStarted();
- grantClipUriPermission(sub2Clip, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, true);
- ReceiveUriService.waitForStart();
-
- // See if we now have access to the provider.
- assertWritingClipAllowed(sub2Clip);
-
- // And still to the previous URI.
- assertWritingClipAllowed(subClip);
-
- // But not reading.
- assertReadingClipNotAllowed(sub2Clip, "shouldn't read from granted write");
-
- // And not to the base path.
- assertWritingContentUriNotAllowed(uri, "shouldn't write non-granted base URI");
-
- // And not a sub-path.
- assertWritingContentUriNotAllowed(sub2SubUri, "shouldn't write non-granted sub URI");
-
- if (false) {
- synchronized (this) {
- Log.i("**", "******************************* WAITING!!!");
- try {
- wait(10000);
- } catch (InterruptedException e) {
- }
- }
- }
-
- // --------------------------------
-
- // Stop the first command.
- ReceiveUriService.stopCurWithId(firstStartId);
-
- // Ensure writing no longer allowed.
- assertWritingClipNotAllowed(subClip, "shouldn't write after losing granted URI");
-
- // And make sure we can't generate a permission to a running service.
- doTryGrantUriActivityPermissionToSelf(subUri,
- Intent.FLAG_GRANT_READ_URI_PERMISSION);
- doTryGrantUriActivityPermissionToSelf(subUri,
- Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-
- // --------------------------------
-
- // Dispose of service.
- ReceiveUriService.stopSync(getContext());
-
- // Ensure writing no longer allowed.
- assertWritingClipNotAllowed(subClip, "shouldn't write after losing granted URI");
- assertWritingClipNotAllowed(sub2Clip, "shouldn't write after losing granted URI");
- }
-
- public void testGrantReadPermissionFromStartService() {
- doTestGrantServiceUriReadPermission(PERM_URI_GRANTING, false);
- doTestGrantServiceUriReadPermission(PERM_URI_GRANTING, true);
- }
-
- public void testGrantWritePermissionFromStartService() {
- doTestGrantServiceUriWritePermission(PERM_URI_GRANTING, false);
- doTestGrantServiceUriWritePermission(PERM_URI_GRANTING, true);
- }
-
- public void testGrantReadPrivateFromStartService() {
- doTestGrantServiceUriReadPermission(PRIV_URI_GRANTING, false);
- doTestGrantServiceUriReadPermission(PRIV_URI_GRANTING, true);
- }
-
- public void testGrantWritePrivateFromStartService() {
- doTestGrantServiceUriWritePermission(PRIV_URI_GRANTING, false);
- doTestGrantServiceUriWritePermission(PRIV_URI_GRANTING, true);
- }
-
- /**
- * Test that ctspermissionwithsignaturepath can't grant read permissions
- * on paths it doesn't have permission to.
- */
- public void testGrantReadUriActivityPathPermissionToSelf() {
- doTryGrantUriActivityPermissionToSelf(PERM_URI_PATH,
- Intent.FLAG_GRANT_READ_URI_PERMISSION);
- }
-
- /**
- * Test that ctspermissionwithsignaturepath can't grant write permissions
- * on paths it doesn't have permission to.
- */
- public void testGrantWriteUriActivityPathPermissionToSelf() {
- doTryGrantUriActivityPermissionToSelf(PERM_URI_PATH,
- Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
- }
-
- /**
- * Test that ctspermissionwithsignaturepath can't grant read permissions
- * on paths it doesn't have permission to.
- */
- public void testGrantReadUriActivitySubPathPermissionToSelf() {
- doTryGrantUriActivityPermissionToSelf(
- Uri.withAppendedPath(PERM_URI_PATH, "foo"),
- Intent.FLAG_GRANT_READ_URI_PERMISSION);
- }
-
- /**
- * Test that ctspermissionwithsignaturepath can't grant write permissions
- * on paths it doesn't have permission to.
- */
- public void testGrantWriteUriActivitySubPathPermissionToSelf() {
- doTryGrantUriActivityPermissionToSelf(
- Uri.withAppendedPath(PERM_URI_PATH, "foo"),
- Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
- }
-
- /**
- * Test that the ctspermissionwithsignaturepath content provider can grant a read
- * permission.
- */
- public void testGrantReadPathPermissionFromStartActivity() {
- doTestGrantActivityUriReadPermission(PERM_URI_PATH, false);
- doTestGrantActivityUriReadPermission(PERM_URI_PATH, true);
- }
-
- /**
- * Test that the ctspermissionwithsignaturepath content provider can grant a write
- * permission.
- */
- public void testGrantWritePathPermissionFromStartActivity() {
- doTestGrantActivityUriWritePermission(PERM_URI_PATH, false);
- doTestGrantActivityUriWritePermission(PERM_URI_PATH, true);
- }
-
- /**
- * Test that the ctspermissionwithsignaturepath content provider can grant a read
- * permission.
- */
- public void testGrantReadPathPermissionFromStartService() {
- doTestGrantServiceUriReadPermission(PERM_URI_PATH, false);
- doTestGrantServiceUriReadPermission(PERM_URI_PATH, true);
- }
-
- /**
- * Test that the ctspermissionwithsignaturepath content provider can grant a write
- * permission.
- */
- public void testGrantWritePathPermissionFromStartService() {
- doTestGrantServiceUriWritePermission(PERM_URI_PATH, false);
- doTestGrantServiceUriWritePermission(PERM_URI_PATH, true);
- }
-
/**
* Verify that we can access paths outside the {@code path-permission}
* protections, which should only rely on {@code provider} permissions.
*/
+ @Test
public void testRestrictingProviderNoMatchingPath() {
assertReadingContentUriAllowed(PERM_URI_PATH_RESTRICTING);
assertWritingContentUriAllowed(PERM_URI_PATH_RESTRICTING);
@@ -1168,6 +188,7 @@
* Verify that paths under {@code path-permission} restriction aren't
* allowed, even though the {@code provider} requires no permissions.
*/
+ @Test
public void testRestrictingProviderMatchingPathDenied() {
// rejected by "foo" prefix
final Uri test1 = PERM_URI_PATH_RESTRICTING.buildUpon().appendPath("foo").build();
@@ -1184,6 +205,7 @@
/**
* Test that shady {@link Uri} are blocked by {@code path-permission}.
*/
+ @Test
public void testRestrictingProviderMatchingShadyPaths() {
assertContentUriAllowed(
Uri.parse("content://ctspermissionwithsignaturepathrestricting/"));
@@ -1205,6 +227,7 @@
* Verify that at least one {@code path-permission} rule will grant access,
* even if the caller doesn't hold another matching {@code path-permission}.
*/
+ @Test
public void testRestrictingProviderMultipleMatchingPath() {
// allowed by narrow "foo/bar" prefix
final Uri test1 = PERM_URI_PATH_RESTRICTING.buildUpon()
@@ -1219,6 +242,7 @@
assertWritingContentUriAllowed(test2);
}
+ @Test
public void testGetMimeTypePermission() {
// Precondition: no current access.
assertReadingContentUriNotAllowed(PERM_URI, "shouldn't read when starting test");
@@ -1228,6 +252,7 @@
assertEquals(getContext().getContentResolver().getType(PERM_URI), EXPECTED_MIME_TYPE);
}
+ @Test
public void testGetMimeTypePrivate() {
// Precondition: no current access.
assertReadingContentUriNotAllowed(PRIV_URI, "shouldn't read when starting test");
@@ -1237,6 +262,7 @@
assertEquals(getContext().getContentResolver().getType(PRIV_URI), EXPECTED_MIME_TYPE);
}
+ @Test
public void testGetMimeTypeAmbiguous() {
// Precondition: no current access.
assertReadingContentUriNotAllowed(AMBIGUOUS_URI, "shouldn't read when starting test");
@@ -1253,425 +279,10 @@
* application, even if that application didn't explicitly declare either
* exported=true or exported=false
*/
+ @Test
public void testGetMimeTypeAmbiguousCompat() {
// All apps should be able to get MIME type even if provider is private.
assertEquals(EXPECTED_MIME_TYPE_AMBIGUOUS,
getContext().getContentResolver().getType(AMBIGUOUS_URI_COMPAT));
}
-
- /**
- * Validate behavior of persistable permission grants.
- */
- public void testGrantPersistableUriPermission() {
- final ContentResolver resolver = getContext().getContentResolver();
-
- final Uri target = Uri.withAppendedPath(PERM_URI_GRANTING, "foo");
- final ClipData clip = makeSingleClipData(target);
-
- // Make sure we can't see the target
- assertReadingClipNotAllowed(clip, "reading should have failed");
- assertWritingClipNotAllowed(clip, "writing should have failed");
-
- // Make sure we can't take a grant we don't have
- try {
- resolver.takePersistableUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION);
- fail("taking read should have failed");
- } catch (SecurityException expected) {
- }
-
- // And since we were just installed, no persisted grants yet
- assertNoPersistedUriPermission();
-
- // Now, let's grant ourselves some access
- ReceiveUriActivity.clearStarted();
- grantClipUriPermission(clip, Intent.FLAG_GRANT_READ_URI_PERMISSION
- | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION, false);
- ReceiveUriActivity.waitForStart();
-
- // We should now have reading access, even before taking the persistable
- // grant. Persisted grants should still be empty.
- assertReadingClipAllowed(clip);
- assertWritingClipNotAllowed(clip, "writing should have failed");
- assertNoPersistedUriPermission();
-
- // Take the read grant and verify we have it!
- long before = System.currentTimeMillis();
- resolver.takePersistableUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION);
- long after = System.currentTimeMillis();
- assertPersistedUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION, before, after);
-
- // Make sure we can't take a grant we don't have
- try {
- resolver.takePersistableUriPermission(target, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
- fail("taking write should have failed");
- } catch (SecurityException expected) {
- }
-
- // Launch again giving ourselves persistable read and write access
- ReceiveUriActivity.clearNewIntent();
- grantClipUriPermission(clip, Intent.FLAG_GRANT_READ_URI_PERMISSION
- | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
- | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION, false);
- ReceiveUriActivity.waitForNewIntent();
-
- // Previous persisted grant should be unchanged
- assertPersistedUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION, before, after);
-
- // We should have both read and write; read is persisted, and write
- // isn't persisted yet.
- assertReadingClipAllowed(clip);
- assertWritingClipAllowed(clip);
-
- // Take again, but still only read; should just update timestamp
- before = System.currentTimeMillis();
- resolver.takePersistableUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION);
- after = System.currentTimeMillis();
- assertPersistedUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION, before, after);
-
- // And take yet again, both read and write
- before = System.currentTimeMillis();
- resolver.takePersistableUriPermission(target,
- Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
- after = System.currentTimeMillis();
- assertPersistedUriPermission(target,
- Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
- before, after);
-
- // Now drop the persisted grant; write first, then read
- resolver.releasePersistableUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION);
- assertPersistedUriPermission(target, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, before, after);
- resolver.releasePersistableUriPermission(target, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
- assertNoPersistedUriPermission();
-
- // And even though we dropped the persistable grants, our activity is
- // still running with the global grants (until reboot).
- assertReadingClipAllowed(clip);
- assertWritingClipAllowed(clip);
-
- ReceiveUriActivity.finishCurInstanceSync();
- }
-
- private void assertNoPersistedUriPermission() {
- assertPersistedUriPermission(null, 0, -1, -1);
- }
-
- private void assertPersistedUriPermission(Uri uri, int flags, long before, long after) {
- // Assert local
- final List<UriPermission> perms = getContext()
- .getContentResolver().getPersistedUriPermissions();
- if (uri != null) {
- assertEquals("expected exactly one permission", 1, perms.size());
-
- final UriPermission perm = perms.get(0);
- assertEquals("unexpected uri", uri, perm.getUri());
-
- final long actual = perm.getPersistedTime();
- if (before != -1) {
- assertTrue("found " + actual + " before " + before, actual >= before);
- }
- if (after != -1) {
- assertTrue("found " + actual + " after " + after, actual <= after);
- }
-
- final boolean expectedRead = (flags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0;
- final boolean expectedWrite = (flags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0;
- assertEquals("unexpected read status", expectedRead, perm.isReadPermission());
- assertEquals("unexpected write status", expectedWrite, perm.isWritePermission());
-
- } else {
- assertEquals("expected zero permissions", 0, perms.size());
- }
-
- // And assert remote
- Intent intent = new Intent();
- intent.setAction(ACTION_VERIFY_OUTGOING_PERSISTED);
- intent.putExtra(EXTRA_URI, uri);
- call(intent);
- }
-
- /**
- * Validate behavior of prefix permission grants.
- */
- public void testGrantPrefixUriPermission() throws Exception {
- final Uri target = Uri.withAppendedPath(PERM_URI_GRANTING, "foo1");
- final Uri targetMeow = Uri.withAppendedPath(target, "meow");
- final Uri targetMeowCat = Uri.withAppendedPath(targetMeow, "cat");
-
- final ClipData clip = makeSingleClipData(target);
- final ClipData clipMeow = makeSingleClipData(targetMeow);
- final ClipData clipMeowCat = makeSingleClipData(targetMeowCat);
-
- // Make sure we can't see the target
- assertReadingClipNotAllowed(clip, "reading should have failed");
- assertWritingClipNotAllowed(clip, "writing should have failed");
-
- // Give ourselves prefix read access
- ReceiveUriActivity.clearStarted();
- grantClipUriPermission(clipMeow, Intent.FLAG_GRANT_READ_URI_PERMISSION
- | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION, false);
- ReceiveUriActivity.waitForStart();
-
- // Verify prefix read access
- assertReadingClipNotAllowed(clip, "reading should have failed");
- assertReadingClipAllowed(clipMeow);
- assertReadingClipAllowed(clipMeowCat);
- assertWritingClipNotAllowed(clip, "writing should have failed");
- assertWritingClipNotAllowed(clipMeow, "writing should have failed");
- assertWritingClipNotAllowed(clipMeowCat, "writing should have failed");
-
- // Now give ourselves exact write access
- ReceiveUriActivity.clearNewIntent();
- grantClipUriPermission(clip, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, false);
- ReceiveUriActivity.waitForNewIntent();
-
- // Verify we have exact write access, but not prefix write
- assertReadingClipNotAllowed(clip, "reading should have failed");
- assertReadingClipAllowed(clipMeow);
- assertReadingClipAllowed(clipMeowCat);
- assertWritingClipAllowed(clip);
- assertWritingClipNotAllowed(clipMeow, "writing should have failed");
- assertWritingClipNotAllowed(clipMeowCat, "writing should have failed");
-
- ReceiveUriActivity.finishCurInstanceSync();
- }
-
- public void testGrantPersistablePrefixUriPermission() {
- final ContentResolver resolver = getContext().getContentResolver();
-
- final Uri target = Uri.withAppendedPath(PERM_URI_GRANTING, "foo2");
- final Uri targetMeow = Uri.withAppendedPath(target, "meow");
-
- final ClipData clip = makeSingleClipData(target);
- final ClipData clipMeow = makeSingleClipData(targetMeow);
-
- // Make sure we can't see the target
- assertReadingClipNotAllowed(clip, "reading should have failed");
-
- // Give ourselves prefix read access
- ReceiveUriActivity.clearStarted();
- grantClipUriPermission(clip, Intent.FLAG_GRANT_READ_URI_PERMISSION
- | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
- | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION, false);
- ReceiveUriActivity.waitForStart();
-
- // Verify prefix read access
- assertReadingClipAllowed(clip);
- assertReadingClipAllowed(clipMeow);
-
- // Verify we can persist direct grant
- long before = System.currentTimeMillis();
- resolver.takePersistableUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION);
- long after = System.currentTimeMillis();
- assertPersistedUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION, before, after);
-
- // But we can't take anywhere under the prefix
- try {
- resolver.takePersistableUriPermission(targetMeow,
- Intent.FLAG_GRANT_READ_URI_PERMISSION);
- fail("taking under prefix should have failed");
- } catch (SecurityException expected) {
- }
-
- // Should still have access regardless of taking
- assertReadingClipAllowed(clip);
- assertReadingClipAllowed(clipMeow);
-
- // And clean up our grants
- resolver.releasePersistableUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION);
- assertNoPersistedUriPermission();
-
- ReceiveUriActivity.finishCurInstanceSync();
- }
-
- /**
- * Validate behavior of directly granting/revoking permission grants.
- */
- public void testDirectGrantRevokeUriPermission() throws Exception {
- final ContentResolver resolver = getContext().getContentResolver();
-
- final Uri target = Uri.withAppendedPath(PERM_URI_GRANTING, "foo3");
- final Uri targetMeow = Uri.withAppendedPath(target, "meow");
- final Uri targetMeowCat = Uri.withAppendedPath(targetMeow, "cat");
-
- final ClipData clip = makeSingleClipData(target);
- final ClipData clipMeow = makeSingleClipData(targetMeow);
- final ClipData clipMeowCat = makeSingleClipData(targetMeowCat);
-
- // Make sure we can't see the target
- assertReadingClipNotAllowed(clipMeow, "reading should have failed");
- assertWritingClipNotAllowed(clipMeow, "writing should have failed");
-
- // Give ourselves some grants:
- // /meow/cat WRITE|PERSISTABLE
- // /meow READ|PREFIX
- // /meow WRITE
- grantClipUriPermissionViaContext(targetMeowCat, Intent.FLAG_GRANT_WRITE_URI_PERMISSION
- | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
- grantClipUriPermissionViaContext(targetMeow, Intent.FLAG_GRANT_READ_URI_PERMISSION
- | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
- grantClipUriPermissionViaContext(targetMeow, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-
- long before = System.currentTimeMillis();
- resolver.takePersistableUriPermission(targetMeowCat, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
- long after = System.currentTimeMillis();
- assertPersistedUriPermission(targetMeowCat, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, before, after);
-
- // Verify they look good
- assertReadingClipNotAllowed(clip, "reading should have failed");
- assertReadingClipAllowed(clipMeow);
- assertReadingClipAllowed(clipMeowCat);
- assertWritingClipNotAllowed(clip, "writing should have failed");
- assertWritingClipAllowed(clipMeow);
- assertWritingClipAllowed(clipMeowCat);
-
- // Revoke anyone with write under meow
- revokeClipUriPermissionViaContext(targetMeow, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-
- // This should have nuked persisted permission at lower level, but it
- // shoulnd't have touched our prefix read.
- assertReadingClipNotAllowed(clip, "reading should have failed");
- assertReadingClipAllowed(clipMeow);
- assertReadingClipAllowed(clipMeowCat);
- assertWritingClipNotAllowed(clip, "writing should have failed");
- assertWritingClipNotAllowed(clipMeow, "writing should have failed");
- assertWritingClipNotAllowed(clipMeowCat, "writing should have failed");
- assertNoPersistedUriPermission();
-
- // Revoking read at top of tree should nuke everything else
- revokeClipUriPermissionViaContext(target, Intent.FLAG_GRANT_READ_URI_PERMISSION);
- assertReadingClipNotAllowed(clip, "reading should have failed");
- assertReadingClipNotAllowed(clipMeow, "reading should have failed");
- assertReadingClipNotAllowed(clipMeowCat, "reading should have failed");
- assertWritingClipNotAllowed(clip, "writing should have failed");
- assertWritingClipNotAllowed(clipMeow, "writing should have failed");
- assertWritingClipNotAllowed(clipMeowCat, "writing should have failed");
- assertNoPersistedUriPermission();
- }
-
- /**
- * Validate behavior of a direct permission grant, where the receiver of
- * that permission revokes it.
- */
- public void testDirectGrantReceiverRevokeUriPermission() throws Exception {
- final ContentResolver resolver = getContext().getContentResolver();
-
- final Uri target = Uri.withAppendedPath(PERM_URI_GRANTING, "foo3");
- final Uri targetMeow = Uri.withAppendedPath(target, "meow");
- final Uri targetMeowCat = Uri.withAppendedPath(targetMeow, "cat");
-
- final ClipData clip = makeSingleClipData(target);
- final ClipData clipMeow = makeSingleClipData(targetMeow);
- final ClipData clipMeowCat = makeSingleClipData(targetMeowCat);
-
- // Make sure we can't see the target
- assertReadingClipNotAllowed(clipMeow, "reading should have failed");
- assertWritingClipNotAllowed(clipMeow, "writing should have failed");
-
- // Give ourselves some grants:
- // /meow/cat WRITE|PERSISTABLE
- // /meow READ|PREFIX
- // /meow WRITE
- grantClipUriPermissionViaContext(targetMeowCat, Intent.FLAG_GRANT_WRITE_URI_PERMISSION
- | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
- grantClipUriPermissionViaContext(targetMeow, Intent.FLAG_GRANT_READ_URI_PERMISSION
- | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
- grantClipUriPermissionViaContext(targetMeow, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-
- long before = System.currentTimeMillis();
- resolver.takePersistableUriPermission(targetMeowCat, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
- long after = System.currentTimeMillis();
- assertPersistedUriPermission(targetMeowCat, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, before, after);
-
- // Verify they look good
- assertReadingClipNotAllowed(clip, "reading should have failed");
- assertReadingClipAllowed(clipMeow);
- assertReadingClipAllowed(clipMeowCat);
- assertWritingClipNotAllowed(clip, "writing should have failed");
- assertWritingClipAllowed(clipMeow);
- assertWritingClipAllowed(clipMeowCat);
-
- // Revoke anyone with write under meow
- getContext().revokeUriPermission(targetMeow, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-
- // This should have nuked persisted permission at lower level, but it
- // shoulnd't have touched our prefix read.
- assertReadingClipNotAllowed(clip, "reading should have failed");
- assertReadingClipAllowed(clipMeow);
- assertReadingClipAllowed(clipMeowCat);
- assertWritingClipNotAllowed(clip, "writing should have failed");
- assertWritingClipNotAllowed(clipMeow, "writing should have failed");
- assertWritingClipNotAllowed(clipMeowCat, "writing should have failed");
- assertNoPersistedUriPermission();
-
- // Revoking read at top of tree should nuke everything else
- getContext().revokeUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION);
- assertReadingClipNotAllowed(clip, "reading should have failed");
- assertReadingClipNotAllowed(clipMeow, "reading should have failed");
- assertReadingClipNotAllowed(clipMeowCat, "reading should have failed");
- assertWritingClipNotAllowed(clip, "writing should have failed");
- assertWritingClipNotAllowed(clipMeow, "writing should have failed");
- assertWritingClipNotAllowed(clipMeowCat, "writing should have failed");
- assertNoPersistedUriPermission();
- }
-
- public void testClipboardWithPermission() throws Exception {
- for (Uri target : GRANTABLE) {
- final ClipData clip = makeSingleClipData(target);
-
- // Normally we can't see the underlying clip data
- assertReadingClipNotAllowed(clip);
- assertWritingClipNotAllowed(clip);
-
- // Use shell's permissions to ensure we can access the clipboard
- InstrumentationRegistry.getInstrumentation().getUiAutomation()
- .adoptShellPermissionIdentity();
- ClipData clipFromClipboard;
- try {
- // But if someone puts it on the clipboard, we can read it
- setPrimaryClip(clip);
- clipFromClipboard = getContext()
- .getSystemService(ClipboardManager.class).getPrimaryClip();
- } finally {
- InstrumentationRegistry.getInstrumentation().getUiAutomation()
- .dropShellPermissionIdentity();
- }
- assertClipDataEquals(clip, clipFromClipboard);
- assertReadingClipAllowed(clipFromClipboard);
- assertWritingClipNotAllowed(clipFromClipboard);
-
- // And if clipboard is cleared, we lose access
- clearPrimaryClip();
- assertReadingClipNotAllowed(clipFromClipboard);
- assertWritingClipNotAllowed(clipFromClipboard);
- }
- }
-
- public void testClipboardWithoutPermission() throws Exception {
- for (Uri target : NOT_GRANTABLE) {
- final ClipData clip = makeSingleClipData(target);
-
- // Can't see it directly
- assertReadingClipNotAllowed(clip);
- assertWritingClipNotAllowed(clip);
-
- // Can't put on clipboard if we don't own it
- try {
- setPrimaryClip(clip);
- fail("Unexpected ability to put protected data " + clip + " on clipboard!");
- } catch (Exception expected) {
- }
- }
- }
-
- private static void assertClipDataEquals(ClipData expected, ClipData actual) {
- assertEquals(expected.getItemCount(), actual.getItemCount());
- for (int i = 0; i < expected.getItemCount(); i++) {
- final ClipData.Item expectedItem = expected.getItemAt(i);
- final ClipData.Item actualItem = actual.getItemAt(i);
- assertEquals(expectedItem.getText(), actualItem.getText());
- assertEquals(expectedItem.getHtmlText(), actualItem.getHtmlText());
- assertEquals(expectedItem.getIntent(), actualItem.getIntent());
- assertEquals(expectedItem.getUri(), actualItem.getUri());
- }
- }
}
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/Asserts.java b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/Asserts.java
new file mode 100644
index 0000000..02483ca
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/Asserts.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.usespermissiondiffcertapp;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.content.ClipData;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.UriPermission;
+import android.database.Cursor;
+import android.net.Uri;
+
+import androidx.test.InstrumentationRegistry;
+
+import java.io.IOException;
+import java.util.List;
+
+public class Asserts {
+ private static Context getContext() {
+ return InstrumentationRegistry.getTargetContext();
+ }
+
+ static void assertAccess(ClipData clip, int mode) {
+ for (int i = 0; i < clip.getItemCount(); i++) {
+ ClipData.Item item = clip.getItemAt(i);
+ Uri uri = item.getUri();
+ if (uri != null) {
+ assertAccess(uri, mode);
+ } else {
+ Intent intent = item.getIntent();
+ uri = intent.getData();
+ if (uri != null) {
+ assertAccess(uri, mode);
+ }
+ ClipData intentClip = intent.getClipData();
+ if (intentClip != null) {
+ assertAccess(intentClip, mode);
+ }
+ }
+ }
+ }
+
+ static void assertAccess(Uri uri, int mode) {
+ if ((mode & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
+ assertReadingContentUriAllowed(uri);
+ } else {
+ assertReadingContentUriNotAllowed(uri, null);
+ }
+ if ((mode & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
+ assertWritingContentUriAllowed(uri);
+ } else {
+ assertWritingContentUriNotAllowed(uri, null);
+ }
+ }
+
+ static void assertReadingContentUriNotAllowed(Uri uri, String msg) {
+ try {
+ getContext().getContentResolver().query(uri, null, null, null, null);
+ fail("expected SecurityException reading " + uri + ": " + msg);
+ } catch (SecurityException expected) {
+ assertNotNull("security exception's error message.", expected.getMessage());
+ }
+ }
+
+ static void assertReadingContentUriAllowed(Uri uri) {
+ try {
+ getContext().getContentResolver().query(uri, null, null, null, null);
+ } catch (SecurityException e) {
+ fail("unexpected SecurityException reading " + uri + ": " + e.getMessage());
+ }
+ }
+
+ static void assertReadingClipNotAllowed(ClipData clip) {
+ assertReadingClipNotAllowed(clip, null);
+ }
+
+ static void assertReadingClipNotAllowed(ClipData clip, String msg) {
+ for (int i=0; i<clip.getItemCount(); i++) {
+ ClipData.Item item = clip.getItemAt(i);
+ Uri uri = item.getUri();
+ if (uri != null) {
+ assertReadingContentUriNotAllowed(uri, msg);
+ } else {
+ Intent intent = item.getIntent();
+ uri = intent.getData();
+ if (uri != null) {
+ assertReadingContentUriNotAllowed(uri, msg);
+ }
+ ClipData intentClip = intent.getClipData();
+ if (intentClip != null) {
+ assertReadingClipNotAllowed(intentClip, msg);
+ }
+ }
+ }
+ }
+
+ static void assertOpenFileDescriptorModeNotAllowed(Uri uri, String msg, String mode) {
+ try {
+ getContext().getContentResolver().openFileDescriptor(uri, mode).close();
+ fail("expected SecurityException writing " + uri + ": " + msg);
+ } catch (IOException e) {
+ throw new IllegalStateException(e);
+ } catch (SecurityException expected) {
+ assertNotNull("security exception's error message.", expected.getMessage());
+ }
+ }
+
+ static void assertContentUriAllowed(Uri uri) {
+ assertReadingContentUriAllowed(uri);
+ assertWritingContentUriAllowed(uri);
+ }
+
+ static void assertContentUriNotAllowed(Uri uri, String msg) {
+ assertReadingContentUriNotAllowed(uri, msg);
+ assertWritingContentUriNotAllowed(uri, msg);
+ }
+
+ static void assertWritingContentUriNotAllowed(Uri uri, String msg) {
+ final ContentResolver resolver = getContext().getContentResolver();
+ try {
+ resolver.insert(uri, new ContentValues());
+ fail("expected SecurityException inserting " + uri + ": " + msg);
+ } catch (SecurityException expected) {
+ assertNotNull("security exception's error message.", expected.getMessage());
+ }
+
+ try {
+ resolver.update(uri, new ContentValues(), null, null);
+ fail("expected SecurityException updating " + uri + ": " + msg);
+ } catch (SecurityException expected) {
+ assertNotNull("security exception's error message.", expected.getMessage());
+ }
+
+ try {
+ resolver.delete(uri, null, null);
+ fail("expected SecurityException deleting " + uri + ": " + msg);
+ } catch (SecurityException expected) {
+ assertNotNull("security exception's error message.", expected.getMessage());
+ }
+
+ try {
+ getContext().getContentResolver().openOutputStream(uri).close();
+ fail("expected SecurityException writing " + uri + ": " + msg);
+ } catch (IOException e) {
+ throw new IllegalStateException(e);
+ } catch (SecurityException expected) {
+ assertNotNull("security exception's error message.", expected.getMessage());
+ }
+
+ assertOpenFileDescriptorModeNotAllowed(uri, msg, "w");
+ assertOpenFileDescriptorModeNotAllowed(uri, msg, "wt");
+ assertOpenFileDescriptorModeNotAllowed(uri, msg, "wa");
+ assertOpenFileDescriptorModeNotAllowed(uri, msg, "rw");
+ assertOpenFileDescriptorModeNotAllowed(uri, msg, "rwt");
+ }
+
+ static void assertWritingContentUriAllowed(Uri uri) {
+ final ContentResolver resolver = getContext().getContentResolver();
+ try {
+ resolver.insert(uri, new ContentValues());
+ resolver.update(uri, new ContentValues(), null, null);
+ resolver.delete(uri, null, null);
+
+ resolver.openOutputStream(uri).close();
+ resolver.openFileDescriptor(uri, "w").close();
+ resolver.openFileDescriptor(uri, "wt").close();
+ resolver.openFileDescriptor(uri, "wa").close();
+ resolver.openFileDescriptor(uri, "rw").close();
+ resolver.openFileDescriptor(uri, "rwt").close();
+ } catch (IOException e) {
+ fail("unexpected IOException writing " + uri + ": " + e.getMessage());
+ } catch (SecurityException e) {
+ fail("unexpected SecurityException writing " + uri + ": " + e.getMessage());
+ }
+ }
+
+ static void assertWritingClipNotAllowed(ClipData clip) {
+ assertWritingClipNotAllowed(clip, null);
+ }
+
+ static void assertWritingClipNotAllowed(ClipData clip, String msg) {
+ for (int i=0; i<clip.getItemCount(); i++) {
+ ClipData.Item item = clip.getItemAt(i);
+ Uri uri = item.getUri();
+ if (uri != null) {
+ assertWritingContentUriNotAllowed(uri, msg);
+ } else {
+ Intent intent = item.getIntent();
+ uri = intent.getData();
+ if (uri != null) {
+ assertWritingContentUriNotAllowed(uri, msg);
+ }
+ ClipData intentClip = intent.getClipData();
+ if (intentClip != null) {
+ assertWritingClipNotAllowed(intentClip, msg);
+ }
+ }
+ }
+ }
+
+ static void assertReadingClipAllowed(ClipData clip) {
+ for (int i=0; i<clip.getItemCount(); i++) {
+ ClipData.Item item = clip.getItemAt(i);
+ Uri uri = item.getUri();
+ if (uri != null) {
+ Cursor c = getContext().getContentResolver().query(uri,
+ null, null, null, null);
+ if (c != null) {
+ c.close();
+ }
+ } else {
+ Intent intent = item.getIntent();
+ uri = intent.getData();
+ if (uri != null) {
+ Cursor c = getContext().getContentResolver().query(uri,
+ null, null, null, null);
+ if (c != null) {
+ c.close();
+ }
+ }
+ ClipData intentClip = intent.getClipData();
+ if (intentClip != null) {
+ assertReadingClipAllowed(intentClip);
+ }
+ }
+ }
+ }
+
+ static void assertWritingClipAllowed(ClipData clip) {
+ for (int i=0; i<clip.getItemCount(); i++) {
+ ClipData.Item item = clip.getItemAt(i);
+ Uri uri = item.getUri();
+ if (uri != null) {
+ getContext().getContentResolver().insert(uri, new ContentValues());
+ } else {
+ Intent intent = item.getIntent();
+ uri = intent.getData();
+ if (uri != null) {
+ getContext().getContentResolver().insert(uri, new ContentValues());
+ }
+ ClipData intentClip = intent.getClipData();
+ if (intentClip != null) {
+ assertWritingClipAllowed(intentClip);
+ }
+ }
+ }
+ }
+
+
+ static void assertClipDataEquals(ClipData expected, ClipData actual) {
+ assertEquals(expected.getItemCount(), actual.getItemCount());
+ for (int i = 0; i < expected.getItemCount(); i++) {
+ final ClipData.Item expectedItem = expected.getItemAt(i);
+ final ClipData.Item actualItem = actual.getItemAt(i);
+ assertEquals(expectedItem.getText(), actualItem.getText());
+ assertEquals(expectedItem.getHtmlText(), actualItem.getHtmlText());
+ assertEquals(expectedItem.getIntent(), actualItem.getIntent());
+ assertEquals(expectedItem.getUri(), actualItem.getUri());
+ }
+ }
+
+ static void assertNoPersistedUriPermission() {
+ assertPersistedUriPermission(null, 0, -1, -1);
+ }
+
+ static void assertPersistedUriPermission(Uri uri, int flags, long before, long after) {
+ // Assert local
+ final List<UriPermission> perms = getContext()
+ .getContentResolver().getPersistedUriPermissions();
+ if (uri != null) {
+ assertEquals("expected exactly one permission", 1, perms.size());
+
+ final UriPermission perm = perms.get(0);
+ assertEquals("unexpected uri", uri, perm.getUri());
+
+ final long actual = perm.getPersistedTime();
+ if (before != -1) {
+ assertTrue("found " + actual + " before " + before, actual >= before);
+ }
+ if (after != -1) {
+ assertTrue("found " + actual + " after " + after, actual <= after);
+ }
+
+ final boolean expectedRead = (flags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0;
+ final boolean expectedWrite = (flags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0;
+ assertEquals("unexpected read status", expectedRead, perm.isReadPermission());
+ assertEquals("unexpected write status", expectedWrite, perm.isWritePermission());
+
+ } else {
+ assertEquals("expected zero permissions", 0, perms.size());
+ }
+
+ Utils.verifyOutgoingPersisted(uri);
+ }
+}
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/GetResultActivity.java b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/GetResultActivity.java
new file mode 100644
index 0000000..71e2cc1
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/GetResultActivity.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.usespermissiondiffcertapp;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.WindowManager;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+public class GetResultActivity extends Activity {
+ private static final String TAG = "GetResultActivity";
+
+ private LinkedBlockingQueue<Result> mResult;
+ private CountDownLatch mDestroyed;
+
+ public static class Result {
+ public final int requestCode;
+ public final int resultCode;
+ public final Intent data;
+
+ public Result(int requestCode, int resultCode, Intent data) {
+ this.requestCode = requestCode;
+ this.resultCode = resultCode;
+ this.data = data;
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+
+ if (mDestroyed != null) {
+ mDestroyed.countDown();
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ Log.d(TAG, "onActivityResult " + data);
+ try {
+ mResult.offer(new Result(requestCode, resultCode, data), 5, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void clearResult() {
+ mResult = new LinkedBlockingQueue<>();
+ mDestroyed = new CountDownLatch(1);
+ }
+
+ public Result getResult() {
+ try {
+ return mResult.poll(5, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void getDestroyed() {
+ try {
+ mDestroyed.await(5, TimeUnit.SECONDS);
+ SystemClock.sleep(200);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsActivityTest.java b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsActivityTest.java
new file mode 100644
index 0000000..26a2eee
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsActivityTest.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.usespermissiondiffcertapp;
+
+import static com.android.cts.usespermissiondiffcertapp.AccessPermissionWithDiffSigTest.GRANTABLE;
+import static com.android.cts.usespermissiondiffcertapp.AccessPermissionWithDiffSigTest.GRANTABLE_MODES;
+import static com.android.cts.usespermissiondiffcertapp.AccessPermissionWithDiffSigTest.NOT_GRANTABLE;
+import static com.android.cts.usespermissiondiffcertapp.AccessPermissionWithDiffSigTest.NOT_GRANTABLE_MODES;
+import static com.android.cts.usespermissiondiffcertapp.Asserts.assertAccess;
+import static com.android.cts.usespermissiondiffcertapp.UriGrantsTest.TAG;
+import static com.android.cts.usespermissiondiffcertapp.Utils.grantClipUriPermission;
+
+import static junit.framework.Assert.fail;
+
+import android.content.ClipData;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.SystemClock;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.function.Function;
+
+@RunWith(AndroidJUnit4.class)
+public class UriGrantsActivityTest {
+ private static Context getContext() {
+ return InstrumentationRegistry.getTargetContext();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ // Always dispose, usually to clean up from failed tests
+ ReceiveUriActivity.finishCurInstanceSync();
+ }
+
+ @Test
+ public void testGrantableToActivity() {
+ for (Uri uri : GRANTABLE) {
+ for (int mode : GRANTABLE_MODES) {
+ Log.d(TAG, "Testing " + uri + " " + mode);
+ assertGrantableToActivity(uri, mode, UriGrantsTest::makeSingleClipData);
+ assertGrantableToActivity(uri, mode, UriGrantsTest::makeMultiClipData);
+ }
+ }
+ }
+
+ private void assertGrantableToActivity(Uri uri, int mode, Function<Uri, ClipData> clipper) {
+ final Uri subUri = Uri.withAppendedPath(uri, "foo");
+ final Uri subSubUri = Uri.withAppendedPath(subUri, "bar");
+ final Uri sub2Uri = Uri.withAppendedPath(uri, "yes");
+ final Uri sub2SubUri = Uri.withAppendedPath(sub2Uri, "no");
+
+ final ClipData subClip = clipper.apply(subUri);
+ final ClipData sub2Clip = clipper.apply(sub2Uri);
+
+ assertAccess(uri, 0);
+ assertAccess(subClip, 0);
+ assertAccess(subUri, 0);
+ assertAccess(subSubUri, 0);
+ assertAccess(sub2Clip, 0);
+ assertAccess(sub2Uri, 0);
+ assertAccess(sub2SubUri, 0);
+
+ // --------------------------------
+
+ ReceiveUriActivity.clearStarted();
+ grantClipUriPermission(subClip, mode, false);
+ ReceiveUriActivity.waitForStart();
+
+ assertAccess(uri, 0);
+ assertAccess(subClip, mode);
+ assertAccess(subUri, mode);
+ assertAccess(subSubUri, 0);
+ assertAccess(sub2Clip, 0);
+ assertAccess(sub2Uri, 0);
+ assertAccess(sub2SubUri, 0);
+
+ // --------------------------------
+
+ ReceiveUriActivity.clearNewIntent();
+ grantClipUriPermission(sub2Clip, mode, false);
+ ReceiveUriActivity.waitForNewIntent();
+
+ assertAccess(uri, 0);
+ assertAccess(subClip, mode);
+ assertAccess(subUri, mode);
+ assertAccess(subSubUri, 0);
+ assertAccess(sub2Clip, mode);
+ assertAccess(sub2Uri, mode);
+ assertAccess(sub2SubUri, 0);
+
+ // And make sure we can't generate a permission to a running activity.
+ assertNotGrantableToActivity(Uri.withAppendedPath(uri, "hah"), mode, clipper);
+
+ // --------------------------------
+
+ // Dispose of activity.
+ ReceiveUriActivity.finishCurInstanceSync();
+ SystemClock.sleep(200);
+
+ assertAccess(uri, 0);
+ assertAccess(subClip, 0);
+ assertAccess(subUri, 0);
+ assertAccess(subSubUri, 0);
+ assertAccess(sub2Clip, 0);
+ assertAccess(sub2Uri, 0);
+ assertAccess(sub2SubUri, 0);
+ }
+
+ @Test
+ public void testNotGrantableToActivity() {
+ for (Uri uri : NOT_GRANTABLE) {
+ for (int mode : NOT_GRANTABLE_MODES) {
+ Log.d(TAG, "Testing " + uri + " " + mode);
+ assertNotGrantableToActivity(uri, mode, UriGrantsTest::makeSingleClipData);
+ assertNotGrantableToActivity(uri, mode, UriGrantsTest::makeMultiClipData);
+ }
+ }
+ }
+
+ private void assertNotGrantableToActivity(Uri uri, int mode, Function<Uri, ClipData> clipper) {
+ final Uri subUri = Uri.withAppendedPath(uri, "foo");
+ final ClipData subClip = clipper.apply(subUri);
+
+ Intent grantIntent = new Intent();
+ grantIntent.setClipData(subClip);
+ grantIntent.addFlags(mode | Intent.FLAG_ACTIVITY_NEW_TASK);
+ grantIntent.setClass(getContext(), ReceiveUriActivity.class);
+ try {
+ ReceiveUriActivity.clearStarted();
+ getContext().startActivity(grantIntent);
+ ReceiveUriActivity.waitForStart();
+ fail("expected SecurityException granting " + subClip + " to activity");
+ } catch (SecurityException e) {
+ // This is what we want.
+ }
+ }
+}
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsClipboardTest.java b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsClipboardTest.java
new file mode 100644
index 0000000..4e6cfb8
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsClipboardTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.usespermissiondiffcertapp;
+
+import static com.android.cts.usespermissiondiffcertapp.AccessPermissionWithDiffSigTest.GRANTABLE;
+import static com.android.cts.usespermissiondiffcertapp.AccessPermissionWithDiffSigTest.NOT_GRANTABLE;
+import static com.android.cts.usespermissiondiffcertapp.Asserts.assertClipDataEquals;
+import static com.android.cts.usespermissiondiffcertapp.Asserts.assertReadingClipAllowed;
+import static com.android.cts.usespermissiondiffcertapp.Asserts.assertReadingClipNotAllowed;
+import static com.android.cts.usespermissiondiffcertapp.Asserts.assertWritingClipNotAllowed;
+import static com.android.cts.usespermissiondiffcertapp.UriGrantsTest.TAG;
+import static com.android.cts.usespermissiondiffcertapp.UriGrantsTest.makeSingleClipData;
+import static com.android.cts.usespermissiondiffcertapp.Utils.clearPrimaryClip;
+import static com.android.cts.usespermissiondiffcertapp.Utils.setPrimaryClip;
+
+import static junit.framework.Assert.fail;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.net.Uri;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class UriGrantsClipboardTest {
+ private static Context getContext() {
+ return InstrumentationRegistry.getTargetContext();
+ }
+
+ @Test
+ public void testClipboardWithPermission() throws Exception {
+ for (Uri target : GRANTABLE) {
+ Log.d(TAG, "Testing " + target);
+ final ClipData clip = makeSingleClipData(target);
+
+ // Normally we can't see the underlying clip data
+ assertReadingClipNotAllowed(clip);
+ assertWritingClipNotAllowed(clip);
+
+ // Use shell's permissions to ensure we can access the clipboard
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .adoptShellPermissionIdentity();
+ ClipData clipFromClipboard;
+ try {
+ // But if someone puts it on the clipboard, we can read it
+ setPrimaryClip(clip);
+ clipFromClipboard = getContext()
+ .getSystemService(ClipboardManager.class).getPrimaryClip();
+ } finally {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .dropShellPermissionIdentity();
+ }
+ assertClipDataEquals(clip, clipFromClipboard);
+ assertReadingClipAllowed(clipFromClipboard);
+ assertWritingClipNotAllowed(clipFromClipboard);
+
+ // And if clipboard is cleared, we lose access
+ clearPrimaryClip();
+ assertReadingClipNotAllowed(clipFromClipboard);
+ assertWritingClipNotAllowed(clipFromClipboard);
+ }
+ }
+
+ @Test
+ public void testClipboardWithoutPermission() throws Exception {
+ for (Uri target : NOT_GRANTABLE) {
+ Log.d(TAG, "Testing " + target);
+ final ClipData clip = makeSingleClipData(target);
+
+ // Can't see it directly
+ assertReadingClipNotAllowed(clip);
+ assertWritingClipNotAllowed(clip);
+
+ // Can't put on clipboard if we don't own it
+ try {
+ setPrimaryClip(clip);
+ fail("Unexpected ability to put protected data " + clip + " on clipboard!");
+ } catch (Exception expected) {
+ }
+ }
+ }
+}
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsResultTest.java b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsResultTest.java
new file mode 100644
index 0000000..a8eea3e
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsResultTest.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.usespermissiondiffcertapp;
+
+import static com.android.cts.usespermissiondiffcertapp.AccessPermissionWithDiffSigTest.GRANTABLE;
+import static com.android.cts.usespermissiondiffcertapp.AccessPermissionWithDiffSigTest.GRANTABLE_MODES;
+import static com.android.cts.usespermissiondiffcertapp.AccessPermissionWithDiffSigTest.NOT_GRANTABLE;
+import static com.android.cts.usespermissiondiffcertapp.AccessPermissionWithDiffSigTest.NOT_GRANTABLE_MODES;
+import static com.android.cts.usespermissiondiffcertapp.Asserts.assertAccess;
+import static com.android.cts.usespermissiondiffcertapp.UriGrantsTest.TAG;
+
+import android.app.Instrumentation;
+import android.content.ClipData;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.function.Function;
+
+@RunWith(AndroidJUnit4.class)
+public class UriGrantsResultTest {
+ private static final int REQUEST_CODE = 42;
+
+ private Instrumentation mInstrumentation;
+ private Context mContext;
+ private GetResultActivity mActivity;
+
+ public void createActivity() throws Exception {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mContext = InstrumentationRegistry.getTargetContext();
+
+ final Intent intent = new Intent(mContext, GetResultActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mActivity = (GetResultActivity) mInstrumentation.startActivitySync(intent);
+ mInstrumentation.waitForIdleSync();
+ mActivity.clearResult();
+ }
+
+ public void destroyActivity() throws Exception {
+ mActivity.finish();
+ }
+
+ @Test
+ public void testGrantableToResult() throws Exception {
+ for (Uri uri : GRANTABLE) {
+ for (int mode : GRANTABLE_MODES) {
+ Log.d(TAG, "Testing " + uri + " " + mode);
+ assertGrantableToResult(uri, mode, UriGrantsTest::makeSingleClipData);
+ assertGrantableToResult(uri, mode, UriGrantsTest::makeMultiClipData);
+ }
+ }
+ }
+
+ private void assertGrantableToResult(Uri uri, int mode,
+ Function<Uri, ClipData> clipper) throws Exception {
+ try {
+ createActivity();
+ assertGrantableToResultInternal(uri, mode, clipper);
+ } finally {
+ destroyActivity();
+ }
+ }
+
+ private void assertGrantableToResultInternal(Uri uri, int mode,
+ Function<Uri, ClipData> clipper) {
+ final Uri subUri = Uri.withAppendedPath(uri, "foo");
+ final Uri subSubUri = Uri.withAppendedPath(subUri, "bar");
+ final ClipData subClip = clipper.apply(subUri);
+
+ assertAccess(uri, 0);
+ assertAccess(subClip, 0);
+ assertAccess(subUri, 0);
+ assertAccess(subSubUri, 0);
+
+ // --------------------------------
+
+ final Intent intent = buildIntent(subClip, mode);
+ mActivity.startActivityForResult(intent, REQUEST_CODE);
+ mActivity.getResult();
+
+ assertAccess(uri, 0);
+ assertAccess(subClip, mode);
+ assertAccess(subUri, mode);
+ assertAccess(subSubUri, 0);
+
+ // --------------------------------
+
+ // Dispose of activity.
+ mActivity.finish();
+ mActivity.getDestroyed();
+
+ assertAccess(uri, 0);
+ assertAccess(subClip, 0);
+ assertAccess(subUri, 0);
+ assertAccess(subSubUri, 0);
+ }
+
+ @Test
+ public void testNotGrantableToResult() throws Exception {
+ for (Uri uri : NOT_GRANTABLE) {
+ for (int mode : NOT_GRANTABLE_MODES) {
+ Log.d(TAG, "Testing " + uri + " " + mode);
+ assertNotGrantableToResult(uri, mode, UriGrantsTest::makeSingleClipData);
+ assertNotGrantableToResult(uri, mode, UriGrantsTest::makeMultiClipData);
+ }
+ }
+ }
+
+ private void assertNotGrantableToResult(Uri uri, int mode,
+ Function<Uri, ClipData> clipper) throws Exception {
+ try {
+ createActivity();
+ assertNotGrantableToResultInternal(uri, mode, clipper);
+ } finally {
+ destroyActivity();
+ }
+ }
+
+ private void assertNotGrantableToResultInternal(Uri uri, int mode,
+ Function<Uri, ClipData> clipper) {
+ final Uri subUri = Uri.withAppendedPath(uri, "foo");
+ final Uri subSubUri = Uri.withAppendedPath(subUri, "bar");
+ final ClipData subClip = clipper.apply(subUri);
+
+ final Intent intent = buildIntent(subClip, mode);
+ mActivity.startActivityForResult(intent, REQUEST_CODE);
+ mActivity.getResult();
+
+ assertAccess(uri, 0);
+ assertAccess(subClip, 0);
+ assertAccess(subUri, 0);
+ assertAccess(subSubUri, 0);
+ }
+
+ private Intent buildIntent(ClipData clip, int mode) {
+ final Intent intent = new Intent();
+ intent.setComponent(new ComponentName("com.android.cts.permissiondeclareapp",
+ "com.android.cts.permissiondeclareapp.SendResultActivity"));
+ intent.putExtra(Intent.EXTRA_TEXT, clip);
+ intent.putExtra(Intent.EXTRA_INDEX, mode);
+ return intent;
+ }
+}
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsServiceTest.java b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsServiceTest.java
new file mode 100644
index 0000000..7be059f
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsServiceTest.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.usespermissiondiffcertapp;
+
+import static com.android.cts.usespermissiondiffcertapp.AccessPermissionWithDiffSigTest.GRANTABLE;
+import static com.android.cts.usespermissiondiffcertapp.AccessPermissionWithDiffSigTest.GRANTABLE_MODES;
+import static com.android.cts.usespermissiondiffcertapp.AccessPermissionWithDiffSigTest.NOT_GRANTABLE;
+import static com.android.cts.usespermissiondiffcertapp.AccessPermissionWithDiffSigTest.NOT_GRANTABLE_MODES;
+import static com.android.cts.usespermissiondiffcertapp.Asserts.assertAccess;
+import static com.android.cts.usespermissiondiffcertapp.UriGrantsTest.TAG;
+import static com.android.cts.usespermissiondiffcertapp.Utils.grantClipUriPermission;
+
+import static junit.framework.Assert.fail;
+
+import android.content.ClipData;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.function.Function;
+
+@RunWith(AndroidJUnit4.class)
+public class UriGrantsServiceTest {
+ private static Context getContext() {
+ return InstrumentationRegistry.getTargetContext();
+ }
+
+ @Test
+ public void testGrantableToService() {
+ for (Uri uri : GRANTABLE) {
+ for (int mode : GRANTABLE_MODES) {
+ Log.d(TAG, "Testing " + uri + " " + mode);
+ assertGrantableToService(uri, mode, UriGrantsTest::makeSingleClipData);
+ assertGrantableToService(uri, mode, UriGrantsTest::makeMultiClipData);
+ }
+ }
+ }
+
+ private void assertGrantableToService(Uri uri, int mode, Function<Uri, ClipData> clipper) {
+ final Uri subUri = Uri.withAppendedPath(uri, "foo");
+ final Uri subSubUri = Uri.withAppendedPath(subUri, "bar");
+ final Uri sub2Uri = Uri.withAppendedPath(uri, "yes");
+ final Uri sub2SubUri = Uri.withAppendedPath(sub2Uri, "no");
+
+ ReceiveUriService.stop(getContext());
+
+ final ClipData subClip = clipper.apply(subUri);
+ final ClipData sub2Clip = clipper.apply(sub2Uri);
+
+ assertAccess(uri, 0);
+ assertAccess(subClip, 0);
+ assertAccess(subUri, 0);
+ assertAccess(subSubUri, 0);
+ assertAccess(sub2Clip, 0);
+ assertAccess(sub2Uri, 0);
+ assertAccess(sub2SubUri, 0);
+
+ // --------------------------------
+
+ ReceiveUriService.clearStarted();
+ grantClipUriPermission(subClip, mode, true);
+ ReceiveUriService.waitForStart();
+
+ int firstStartId = ReceiveUriService.getCurStartId();
+
+ assertAccess(uri, 0);
+ assertAccess(subClip, mode);
+ assertAccess(subUri, mode);
+ assertAccess(subSubUri, 0);
+ assertAccess(sub2Clip, 0);
+ assertAccess(sub2Uri, 0);
+ assertAccess(sub2SubUri, 0);
+
+ // --------------------------------
+
+ // Send another Intent to it.
+ ReceiveUriService.clearStarted();
+ grantClipUriPermission(sub2Clip, mode, true);
+ ReceiveUriService.waitForStart();
+
+ assertAccess(uri, 0);
+ assertAccess(subClip, mode);
+ assertAccess(subUri, mode);
+ assertAccess(subSubUri, 0);
+ assertAccess(sub2Clip, mode);
+ assertAccess(sub2Uri, mode);
+ assertAccess(sub2SubUri, 0);
+
+ // And make sure we can't generate a permission to a running service.
+ assertNotGrantableToService(Uri.withAppendedPath(uri, "hah"), mode, clipper);
+
+ // --------------------------------
+
+ // Stop the first command.
+ ReceiveUriService.stopCurWithId(firstStartId);
+
+ assertAccess(uri, 0);
+ assertAccess(subClip, 0);
+ assertAccess(subUri, 0);
+ assertAccess(subSubUri, 0);
+ assertAccess(sub2Clip, mode);
+ assertAccess(sub2Uri, mode);
+ assertAccess(sub2SubUri, 0);
+
+ // --------------------------------
+
+ // Dispose of service.
+ ReceiveUriService.stopSync(getContext());
+
+ assertAccess(uri, 0);
+ assertAccess(subClip, 0);
+ assertAccess(subUri, 0);
+ assertAccess(subSubUri, 0);
+ assertAccess(sub2Clip, 0);
+ assertAccess(sub2Uri, 0);
+ assertAccess(sub2SubUri, 0);
+ }
+
+ @Test
+ public void testNotGrantableToService() {
+ for (Uri uri : NOT_GRANTABLE) {
+ for (int mode : NOT_GRANTABLE_MODES) {
+ Log.d(TAG, "Testing " + uri + " " + mode);
+ assertNotGrantableToService(uri, mode, UriGrantsTest::makeSingleClipData);
+ assertNotGrantableToService(uri, mode, UriGrantsTest::makeMultiClipData);
+ }
+ }
+ }
+
+ private void assertNotGrantableToService(Uri uri, int mode, Function<Uri, ClipData> clipper) {
+ final Uri subUri = Uri.withAppendedPath(uri, "foo");
+ final ClipData subClip = clipper.apply(subUri);
+
+ Intent grantIntent = new Intent();
+ grantIntent.setClipData(subClip);
+ grantIntent.addFlags(mode);
+ grantIntent.setClass(getContext(), ReceiveUriService.class);
+ try {
+ getContext().startService(grantIntent);
+ fail("expected SecurityException granting " + subClip + " to service");
+ } catch (SecurityException e) {
+ // This is what we want.
+ }
+ }
+}
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsTest.java b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsTest.java
new file mode 100644
index 0000000..a955dbc
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/UriGrantsTest.java
@@ -0,0 +1,402 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.usespermissiondiffcertapp;
+
+import static com.android.cts.usespermissiondiffcertapp.AccessPermissionWithDiffSigTest.PERM_URI_GRANTING;
+import static com.android.cts.usespermissiondiffcertapp.Asserts.assertNoPersistedUriPermission;
+import static com.android.cts.usespermissiondiffcertapp.Asserts.assertPersistedUriPermission;
+import static com.android.cts.usespermissiondiffcertapp.Asserts.assertReadingClipAllowed;
+import static com.android.cts.usespermissiondiffcertapp.Asserts.assertReadingClipNotAllowed;
+import static com.android.cts.usespermissiondiffcertapp.Asserts.assertWritingClipAllowed;
+import static com.android.cts.usespermissiondiffcertapp.Asserts.assertWritingClipNotAllowed;
+import static com.android.cts.usespermissiondiffcertapp.Utils.grantClipUriPermission;
+import static com.android.cts.usespermissiondiffcertapp.Utils.grantClipUriPermissionViaContext;
+import static com.android.cts.usespermissiondiffcertapp.Utils.revokeClipUriPermissionViaContext;
+
+import static junit.framework.Assert.fail;
+
+import android.content.ClipData;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class UriGrantsTest {
+ static final String TAG = "UriGrantsTest";
+
+ private static Context getContext() {
+ return InstrumentationRegistry.getTargetContext();
+ }
+
+ static ClipData makeSingleClipData(Uri uri) {
+ return new ClipData("foo", new String[] { "foo/bar" },
+ new ClipData.Item(uri));
+ }
+
+ static ClipData makeMultiClipData(Uri uri) {
+ Uri grantClip1Uri = uri;
+ Uri grantClip2Uri = Uri.withAppendedPath(uri, "clip2");
+ Uri grantClip3Uri = Uri.withAppendedPath(uri, "clip3");
+ Uri grantClip4Uri = Uri.withAppendedPath(uri, "clip4");
+ Uri grantClip5Uri = Uri.withAppendedPath(uri, "clip5");
+ ClipData clip = new ClipData("foo", new String[] { "foo/bar" },
+ new ClipData.Item(grantClip1Uri));
+ clip.addItem(new ClipData.Item(grantClip2Uri));
+ // Intents in the ClipData should allow their data: and clip URIs
+ // to be granted, but only respect the grant flags of the top-level
+ // Intent.
+ clip.addItem(new ClipData.Item(new Intent(Intent.ACTION_VIEW, grantClip3Uri)));
+ Intent intent = new Intent(Intent.ACTION_VIEW, grantClip4Uri);
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ clip.addItem(new ClipData.Item(intent));
+ intent = new Intent(Intent.ACTION_VIEW);
+ intent.setClipData(new ClipData("foo", new String[] { "foo/bar" },
+ new ClipData.Item(grantClip5Uri)));
+ clip.addItem(new ClipData.Item(intent));
+ return clip;
+ }
+
+ /**
+ * Validate behavior of persistable permission grants.
+ */
+ @Test
+ public void testGrantPersistableUriPermission() {
+ final ContentResolver resolver = getContext().getContentResolver();
+
+ final Uri target = Uri.withAppendedPath(PERM_URI_GRANTING, "foo");
+ final ClipData clip = makeSingleClipData(target);
+
+ // Make sure we can't see the target
+ assertReadingClipNotAllowed(clip, "reading should have failed");
+ assertWritingClipNotAllowed(clip, "writing should have failed");
+
+ // Make sure we can't take a grant we don't have
+ try {
+ resolver.takePersistableUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ fail("taking read should have failed");
+ } catch (SecurityException expected) {
+ }
+
+ // And since we were just installed, no persisted grants yet
+ assertNoPersistedUriPermission();
+
+ // Now, let's grant ourselves some access
+ ReceiveUriActivity.clearStarted();
+ grantClipUriPermission(clip, Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION, false);
+ ReceiveUriActivity.waitForStart();
+
+ // We should now have reading access, even before taking the persistable
+ // grant. Persisted grants should still be empty.
+ assertReadingClipAllowed(clip);
+ assertWritingClipNotAllowed(clip, "writing should have failed");
+ assertNoPersistedUriPermission();
+
+ // Take the read grant and verify we have it!
+ long before = System.currentTimeMillis();
+ resolver.takePersistableUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ long after = System.currentTimeMillis();
+ assertPersistedUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION, before, after);
+
+ // Make sure we can't take a grant we don't have
+ try {
+ resolver.takePersistableUriPermission(target, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ fail("taking write should have failed");
+ } catch (SecurityException expected) {
+ }
+
+ // Launch again giving ourselves persistable read and write access
+ ReceiveUriActivity.clearNewIntent();
+ grantClipUriPermission(clip, Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+ | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION, false);
+ ReceiveUriActivity.waitForNewIntent();
+
+ // Previous persisted grant should be unchanged
+ assertPersistedUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION, before, after);
+
+ // We should have both read and write; read is persisted, and write
+ // isn't persisted yet.
+ assertReadingClipAllowed(clip);
+ assertWritingClipAllowed(clip);
+
+ // Take again, but still only read; should just update timestamp
+ before = System.currentTimeMillis();
+ resolver.takePersistableUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ after = System.currentTimeMillis();
+ assertPersistedUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION, before, after);
+
+ // And take yet again, both read and write
+ before = System.currentTimeMillis();
+ resolver.takePersistableUriPermission(target,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ after = System.currentTimeMillis();
+ assertPersistedUriPermission(target,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
+ before, after);
+
+ // Now drop the persisted grant; write first, then read
+ resolver.releasePersistableUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ assertPersistedUriPermission(target, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, before, after);
+ resolver.releasePersistableUriPermission(target, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ assertNoPersistedUriPermission();
+
+ // And even though we dropped the persistable grants, our activity is
+ // still running with the global grants (until reboot).
+ assertReadingClipAllowed(clip);
+ assertWritingClipAllowed(clip);
+
+ ReceiveUriActivity.finishCurInstanceSync();
+ }
+
+ /**
+ * Validate behavior of prefix permission grants.
+ */
+ @Test
+ public void testGrantPrefixUriPermission() throws Exception {
+ final Uri target = Uri.withAppendedPath(PERM_URI_GRANTING, "foo1");
+ final Uri targetMeow = Uri.withAppendedPath(target, "meow");
+ final Uri targetMeowCat = Uri.withAppendedPath(targetMeow, "cat");
+
+ final ClipData clip = makeSingleClipData(target);
+ final ClipData clipMeow = makeSingleClipData(targetMeow);
+ final ClipData clipMeowCat = makeSingleClipData(targetMeowCat);
+
+ // Make sure we can't see the target
+ assertReadingClipNotAllowed(clip, "reading should have failed");
+ assertWritingClipNotAllowed(clip, "writing should have failed");
+
+ // Give ourselves prefix read access
+ ReceiveUriActivity.clearStarted();
+ grantClipUriPermission(clipMeow, Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION, false);
+ ReceiveUriActivity.waitForStart();
+
+ // Verify prefix read access
+ assertReadingClipNotAllowed(clip, "reading should have failed");
+ assertReadingClipAllowed(clipMeow);
+ assertReadingClipAllowed(clipMeowCat);
+ assertWritingClipNotAllowed(clip, "writing should have failed");
+ assertWritingClipNotAllowed(clipMeow, "writing should have failed");
+ assertWritingClipNotAllowed(clipMeowCat, "writing should have failed");
+
+ // Now give ourselves exact write access
+ ReceiveUriActivity.clearNewIntent();
+ grantClipUriPermission(clip, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, false);
+ ReceiveUriActivity.waitForNewIntent();
+
+ // Verify we have exact write access, but not prefix write
+ assertReadingClipNotAllowed(clip, "reading should have failed");
+ assertReadingClipAllowed(clipMeow);
+ assertReadingClipAllowed(clipMeowCat);
+ assertWritingClipAllowed(clip);
+ assertWritingClipNotAllowed(clipMeow, "writing should have failed");
+ assertWritingClipNotAllowed(clipMeowCat, "writing should have failed");
+
+ ReceiveUriActivity.finishCurInstanceSync();
+ }
+
+ @Test
+ public void testGrantPersistablePrefixUriPermission() {
+ final ContentResolver resolver = getContext().getContentResolver();
+
+ final Uri target = Uri.withAppendedPath(PERM_URI_GRANTING, "foo2");
+ final Uri targetMeow = Uri.withAppendedPath(target, "meow");
+
+ final ClipData clip = makeSingleClipData(target);
+ final ClipData clipMeow = makeSingleClipData(targetMeow);
+
+ // Make sure we can't see the target
+ assertReadingClipNotAllowed(clip, "reading should have failed");
+
+ // Give ourselves prefix read access
+ ReceiveUriActivity.clearStarted();
+ grantClipUriPermission(clip, Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
+ | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION, false);
+ ReceiveUriActivity.waitForStart();
+
+ // Verify prefix read access
+ assertReadingClipAllowed(clip);
+ assertReadingClipAllowed(clipMeow);
+
+ // Verify we can persist direct grant
+ long before = System.currentTimeMillis();
+ resolver.takePersistableUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ long after = System.currentTimeMillis();
+ assertPersistedUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION, before, after);
+
+ // But we can't take anywhere under the prefix
+ try {
+ resolver.takePersistableUriPermission(targetMeow,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ fail("taking under prefix should have failed");
+ } catch (SecurityException expected) {
+ }
+
+ // Should still have access regardless of taking
+ assertReadingClipAllowed(clip);
+ assertReadingClipAllowed(clipMeow);
+
+ // And clean up our grants
+ resolver.releasePersistableUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ assertNoPersistedUriPermission();
+
+ ReceiveUriActivity.finishCurInstanceSync();
+ }
+
+ /**
+ * Validate behavior of directly granting/revoking permission grants.
+ */
+ @Test
+ public void testDirectGrantRevokeUriPermission() throws Exception {
+ final ContentResolver resolver = getContext().getContentResolver();
+
+ final Uri target = Uri.withAppendedPath(PERM_URI_GRANTING, "foo3");
+ final Uri targetMeow = Uri.withAppendedPath(target, "meow");
+ final Uri targetMeowCat = Uri.withAppendedPath(targetMeow, "cat");
+
+ final ClipData clip = makeSingleClipData(target);
+ final ClipData clipMeow = makeSingleClipData(targetMeow);
+ final ClipData clipMeowCat = makeSingleClipData(targetMeowCat);
+
+ // Make sure we can't see the target
+ assertReadingClipNotAllowed(clipMeow, "reading should have failed");
+ assertWritingClipNotAllowed(clipMeow, "writing should have failed");
+
+ // Give ourselves some grants:
+ // /meow/cat WRITE|PERSISTABLE
+ // /meow READ|PREFIX
+ // /meow WRITE
+ grantClipUriPermissionViaContext(targetMeowCat, Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+ | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
+ grantClipUriPermissionViaContext(targetMeow, Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
+ grantClipUriPermissionViaContext(targetMeow, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
+ long before = System.currentTimeMillis();
+ resolver.takePersistableUriPermission(targetMeowCat, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ long after = System.currentTimeMillis();
+ assertPersistedUriPermission(targetMeowCat, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, before, after);
+
+ // Verify they look good
+ assertReadingClipNotAllowed(clip, "reading should have failed");
+ assertReadingClipAllowed(clipMeow);
+ assertReadingClipAllowed(clipMeowCat);
+ assertWritingClipNotAllowed(clip, "writing should have failed");
+ assertWritingClipAllowed(clipMeow);
+ assertWritingClipAllowed(clipMeowCat);
+
+ // Revoke anyone with write under meow
+ revokeClipUriPermissionViaContext(targetMeow, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
+ // This should have nuked persisted permission at lower level, but it
+ // shoulnd't have touched our prefix read.
+ assertReadingClipNotAllowed(clip, "reading should have failed");
+ assertReadingClipAllowed(clipMeow);
+ assertReadingClipAllowed(clipMeowCat);
+ assertWritingClipNotAllowed(clip, "writing should have failed");
+ assertWritingClipNotAllowed(clipMeow, "writing should have failed");
+ assertWritingClipNotAllowed(clipMeowCat, "writing should have failed");
+ assertNoPersistedUriPermission();
+
+ // Revoking read at top of tree should nuke everything else
+ revokeClipUriPermissionViaContext(target, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ assertReadingClipNotAllowed(clip, "reading should have failed");
+ assertReadingClipNotAllowed(clipMeow, "reading should have failed");
+ assertReadingClipNotAllowed(clipMeowCat, "reading should have failed");
+ assertWritingClipNotAllowed(clip, "writing should have failed");
+ assertWritingClipNotAllowed(clipMeow, "writing should have failed");
+ assertWritingClipNotAllowed(clipMeowCat, "writing should have failed");
+ assertNoPersistedUriPermission();
+ }
+
+ /**
+ * Validate behavior of a direct permission grant, where the receiver of
+ * that permission revokes it.
+ */
+ @Test
+ public void testDirectGrantReceiverRevokeUriPermission() throws Exception {
+ final ContentResolver resolver = getContext().getContentResolver();
+
+ final Uri target = Uri.withAppendedPath(PERM_URI_GRANTING, "foo3");
+ final Uri targetMeow = Uri.withAppendedPath(target, "meow");
+ final Uri targetMeowCat = Uri.withAppendedPath(targetMeow, "cat");
+
+ final ClipData clip = makeSingleClipData(target);
+ final ClipData clipMeow = makeSingleClipData(targetMeow);
+ final ClipData clipMeowCat = makeSingleClipData(targetMeowCat);
+
+ // Make sure we can't see the target
+ assertReadingClipNotAllowed(clipMeow, "reading should have failed");
+ assertWritingClipNotAllowed(clipMeow, "writing should have failed");
+
+ // Give ourselves some grants:
+ // /meow/cat WRITE|PERSISTABLE
+ // /meow READ|PREFIX
+ // /meow WRITE
+ grantClipUriPermissionViaContext(targetMeowCat, Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+ | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
+ grantClipUriPermissionViaContext(targetMeow, Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
+ grantClipUriPermissionViaContext(targetMeow, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
+ long before = System.currentTimeMillis();
+ resolver.takePersistableUriPermission(targetMeowCat, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ long after = System.currentTimeMillis();
+ assertPersistedUriPermission(targetMeowCat, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, before, after);
+
+ // Verify they look good
+ assertReadingClipNotAllowed(clip, "reading should have failed");
+ assertReadingClipAllowed(clipMeow);
+ assertReadingClipAllowed(clipMeowCat);
+ assertWritingClipNotAllowed(clip, "writing should have failed");
+ assertWritingClipAllowed(clipMeow);
+ assertWritingClipAllowed(clipMeowCat);
+
+ // Revoke anyone with write under meow
+ getContext().revokeUriPermission(targetMeow, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
+ // This should have nuked persisted permission at lower level, but it
+ // shoulnd't have touched our prefix read.
+ assertReadingClipNotAllowed(clip, "reading should have failed");
+ assertReadingClipAllowed(clipMeow);
+ assertReadingClipAllowed(clipMeowCat);
+ assertWritingClipNotAllowed(clip, "writing should have failed");
+ assertWritingClipNotAllowed(clipMeow, "writing should have failed");
+ assertWritingClipNotAllowed(clipMeowCat, "writing should have failed");
+ assertNoPersistedUriPermission();
+
+ // Revoking read at top of tree should nuke everything else
+ getContext().revokeUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ assertReadingClipNotAllowed(clip, "reading should have failed");
+ assertReadingClipNotAllowed(clipMeow, "reading should have failed");
+ assertReadingClipNotAllowed(clipMeowCat, "reading should have failed");
+ assertWritingClipNotAllowed(clip, "writing should have failed");
+ assertWritingClipNotAllowed(clipMeow, "writing should have failed");
+ assertWritingClipNotAllowed(clipMeowCat, "writing should have failed");
+ assertNoPersistedUriPermission();
+ }
+}
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/Utils.java b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/Utils.java
new file mode 100644
index 0000000..48cac57
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/Utils.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.usespermissiondiffcertapp;
+
+import static com.android.cts.permissiondeclareapp.UtilsProvider.ACTION_CLEAR_PRIMARY_CLIP;
+import static com.android.cts.permissiondeclareapp.UtilsProvider.ACTION_GRANT_URI;
+import static com.android.cts.permissiondeclareapp.UtilsProvider.ACTION_REVOKE_URI;
+import static com.android.cts.permissiondeclareapp.UtilsProvider.ACTION_SET_PRIMARY_CLIP;
+import static com.android.cts.permissiondeclareapp.UtilsProvider.ACTION_START_ACTIVITY;
+import static com.android.cts.permissiondeclareapp.UtilsProvider.ACTION_START_SERVICE;
+import static com.android.cts.permissiondeclareapp.UtilsProvider.ACTION_VERIFY_OUTGOING_PERSISTED;
+import static com.android.cts.permissiondeclareapp.UtilsProvider.EXTRA_INTENT;
+import static com.android.cts.permissiondeclareapp.UtilsProvider.EXTRA_MODE;
+import static com.android.cts.permissiondeclareapp.UtilsProvider.EXTRA_PACKAGE_NAME;
+import static com.android.cts.permissiondeclareapp.UtilsProvider.EXTRA_URI;
+
+import android.content.ClipData;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.cts.permissiondeclareapp.UtilsProvider;
+
+public class Utils {
+ private static Context getContext() {
+ return InstrumentationRegistry.getTargetContext();
+ }
+
+ private static void call(Intent intent) {
+ final Bundle extras = new Bundle();
+ extras.putParcelable(Intent.EXTRA_INTENT, intent);
+ getContext().getContentResolver().call(UtilsProvider.URI, "", "", extras);
+ }
+
+ static void grantClipUriPermission(ClipData clip, int mode, boolean service) {
+ Intent grantIntent = new Intent();
+ if (clip.getItemCount() == 1) {
+ grantIntent.setData(clip.getItemAt(0).getUri());
+ } else {
+ grantIntent.setClipData(clip);
+ // Make this Intent unique from the one that started it.
+ for (int i=0; i<clip.getItemCount(); i++) {
+ Uri uri = clip.getItemAt(i).getUri();
+ if (uri != null) {
+ grantIntent.addCategory(uri.toString());
+ }
+ }
+ }
+ grantIntent.addFlags(mode);
+ grantIntent.setClass(getContext(),
+ service ? ReceiveUriService.class : ReceiveUriActivity.class);
+ Intent intent = new Intent();
+ intent.setAction(service ? ACTION_START_SERVICE : ACTION_START_ACTIVITY);
+ intent.putExtra(EXTRA_INTENT, grantIntent);
+ call(intent);
+ }
+
+ static void grantClipUriPermissionViaContext(Uri uri, int mode) {
+ Intent intent = new Intent();
+ intent.setAction(ACTION_GRANT_URI);
+ intent.putExtra(EXTRA_PACKAGE_NAME, getContext().getPackageName());
+ intent.putExtra(EXTRA_URI, uri);
+ intent.putExtra(EXTRA_MODE, mode);
+ call(intent);
+ }
+
+ static void revokeClipUriPermissionViaContext(Uri uri, int mode) {
+ Intent intent = new Intent();
+ intent.setAction(ACTION_REVOKE_URI);
+ intent.putExtra(EXTRA_URI, uri);
+ intent.putExtra(EXTRA_MODE, mode);
+ call(intent);
+ }
+
+ static void setPrimaryClip(ClipData clip) {
+ Intent intent = new Intent();
+ intent.setAction(ACTION_SET_PRIMARY_CLIP);
+ intent.setClipData(clip);
+ call(intent);
+ }
+
+ static void clearPrimaryClip() {
+ Intent intent = new Intent();
+ intent.setAction(ACTION_CLEAR_PRIMARY_CLIP);
+ call(intent);
+ }
+
+ static void verifyOutgoingPersisted(Uri uri) {
+ Intent intent = new Intent();
+ intent.setAction(ACTION_VERIFY_OUTGOING_PERSISTED);
+ intent.putExtra(EXTRA_URI, uri);
+ call(intent);
+ }
+}
diff --git a/hostsidetests/appsecurity/test-apps/rro/OverlayApp/src/com/android/cts/overlay/app/OverlayableTest.java b/hostsidetests/appsecurity/test-apps/rro/OverlayApp/src/com/android/cts/overlay/app/OverlayableTest.java
index d693691..2b8bad6 100644
--- a/hostsidetests/appsecurity/test-apps/rro/OverlayApp/src/com/android/cts/overlay/app/OverlayableTest.java
+++ b/hostsidetests/appsecurity/test-apps/rro/OverlayApp/src/com/android/cts/overlay/app/OverlayableTest.java
@@ -139,7 +139,7 @@
@Test
public void testFrameworkDoesNotDefineOverlayable() throws Exception {
- assertTrue(InstrumentationRegistry.getTargetContext().getAssets().getOverlayableMap(
+ assertTrue(InstrumentationRegistry.getTargetContext().getAssets().getOverlayablesToString(
"android").isEmpty());
}
diff --git a/hostsidetests/backup/AutoRestoreApp/Android.bp b/hostsidetests/backup/AutoRestoreApp/Android.bp
new file mode 100644
index 0000000..227fe13
--- /dev/null
+++ b/hostsidetests/backup/AutoRestoreApp/Android.bp
@@ -0,0 +1,34 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test_helper_app {
+ name: "CtsAutoRestoreApp",
+ defaults: ["cts_defaults"],
+ static_libs: [
+ "androidx.test.rules",
+ "platform-test-annotations",
+ "testng",
+ "truth-prebuilt",
+ "compatibility-device-util-axt",
+ ],
+ srcs: ["src/**/*.java"],
+ // tag this module as a cts test artifact
+ test_suites: [
+ "arcts",
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+ platform_apis: true,
+}
diff --git a/libs/rollback/testapp/res_v1/values/values.xml b/hostsidetests/backup/AutoRestoreApp/AndroidManifest.xml
similarity index 61%
copy from libs/rollback/testapp/res_v1/values/values.xml
copy to hostsidetests/backup/AutoRestoreApp/AndroidManifest.xml
index 8e71917..de6fa63 100644
--- a/libs/rollback/testapp/res_v1/values/values.xml
+++ b/hostsidetests/backup/AutoRestoreApp/AndroidManifest.xml
@@ -14,6 +14,15 @@
limitations under the License.
-->
-<resources>
- <integer name="app_version">1</integer>
-</resources>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.cts.backup.autorestoreapp">
+
+ <application android:label="AutoRestoreApp" >
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.cts.backup.autorestoreapp" />
+
+</manifest>
diff --git a/hostsidetests/backup/AutoRestoreApp/src/android/cts/backup/autorestoreapp/AutoRestoreTest.java b/hostsidetests/backup/AutoRestoreApp/src/android/cts/backup/autorestoreapp/AutoRestoreTest.java
new file mode 100644
index 0000000..1448f93
--- /dev/null
+++ b/hostsidetests/backup/AutoRestoreApp/src/android/cts/backup/autorestoreapp/AutoRestoreTest.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.cts.backup.autorestoreapp;
+
+import static androidx.test.InstrumentationRegistry.getTargetContext;
+
+import static com.google.common.truth.Truth.assertThat;
+
+
+import android.app.backup.BackupManager;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.platform.test.annotations.AppModeFull;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Device side routines to be invoked by the host side AutoRestoreHostSideTest. These are not
+ * designed to be called in any other way, as they rely on state set up by the host side test.
+ */
+@RunWith(AndroidJUnit4.class)
+@AppModeFull
+public class AutoRestoreTest {
+ private static final String SHARED_PREFERENCES_FILE = "auto_restore_shared_prefs";
+ private static final String PREF_KEY = "auto_restore_pref";
+ private static final int PREF_VALUE = 123;
+
+ private BackupManager mBackupManager;
+ private SharedPreferences mPreferences;
+
+ @Before
+ public void setUp() {
+ Context context = getTargetContext();
+ mBackupManager = new BackupManager(context);
+ mPreferences = context.getSharedPreferences(SHARED_PREFERENCES_FILE, Context.MODE_PRIVATE);
+ }
+
+ @Test
+ public void saveValuesToSharedPrefs() {
+ mPreferences.edit().putInt(PREF_KEY, PREF_VALUE).commit();
+ }
+
+ @Test
+ public void testCheckSharedPrefsExist() {
+ assertThat(mPreferences.getInt(PREF_KEY, 0)).isEqualTo(PREF_VALUE);
+ }
+
+ @Test
+ public void testCheckSharedPrefsDontExist() {
+ assertThat(mPreferences.getInt(PREF_KEY, 0)).isEqualTo(0);
+ }
+
+ @Test
+ public void enableAutoRestore() {
+ setAutoRestoreEnabled(true);
+ }
+
+ @Test
+ public void disableAutoRestore() {
+ setAutoRestoreEnabled(false);
+ }
+
+ private void setAutoRestoreEnabled(boolean enabled) {
+ SystemUtil.runWithShellPermissionIdentity(
+ () -> {
+ mBackupManager.setAutoRestore(enabled);
+ });
+ }
+}
diff --git a/hostsidetests/backup/BackupTransportApp/Android.bp b/hostsidetests/backup/BackupTransportApp/Android.bp
new file mode 100644
index 0000000..240a460
--- /dev/null
+++ b/hostsidetests/backup/BackupTransportApp/Android.bp
@@ -0,0 +1,33 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test_helper_app {
+ name: "CtsBackupTransportApp",
+ defaults: ["cts_defaults"],
+ static_libs: [
+ "androidx.test.rules",
+ "platform-test-annotations",
+ "testng",
+ "truth-prebuilt",
+ ],
+ srcs: ["src/**/*.java"],
+ // tag this module as a cts test artifact
+ test_suites: [
+ "arcts",
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+ sdk_version: "system_current",
+}
diff --git a/libs/rollback/testapp/res_v1/values/values.xml b/hostsidetests/backup/BackupTransportApp/AndroidManifest.xml
similarity index 60%
copy from libs/rollback/testapp/res_v1/values/values.xml
copy to hostsidetests/backup/BackupTransportApp/AndroidManifest.xml
index 8e71917..d3f4e66 100644
--- a/libs/rollback/testapp/res_v1/values/values.xml
+++ b/hostsidetests/backup/BackupTransportApp/AndroidManifest.xml
@@ -14,6 +14,15 @@
limitations under the License.
-->
-<resources>
- <integer name="app_version">1</integer>
-</resources>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.cts.backup.backuptransportapp">
+
+ <application android:label="BackupTransportApp" >
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.cts.backup.backuptransportapp" />
+
+</manifest>
diff --git a/hostsidetests/backup/BackupTransportApp/src/android/cts/backup/backuptransportapp/BackupTransportTest.java b/hostsidetests/backup/BackupTransportApp/src/android/cts/backup/backuptransportapp/BackupTransportTest.java
new file mode 100644
index 0000000..7edcd65
--- /dev/null
+++ b/hostsidetests/backup/BackupTransportApp/src/android/cts/backup/backuptransportapp/BackupTransportTest.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.cts.backup.backuptransportapp;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.app.backup.BackupTransport;
+import android.platform.test.annotations.AppModeFull;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Device side routines to be invoked by the host side BackupTransportHostSideTest. These are not
+ * designed to be called in any other way, as they rely on state set up by the host side test.
+ */
+@RunWith(AndroidJUnit4.class)
+@AppModeFull
+public class BackupTransportTest {
+ private BackupTransport mBackupTransport;
+
+ @Before
+ public void setUp() {
+ mBackupTransport = new BackupTransport();
+ }
+
+ @Test
+ public void testName_throwsException() {
+ assertThrows(UnsupportedOperationException.class, () -> mBackupTransport.name());
+ }
+
+ @Test
+ public void testConfigurationIntent_returnsNull() throws Exception {
+ assertThat(mBackupTransport.configurationIntent()).isNull();
+ }
+
+ @Test
+ public void testCurrentDestinationString_throwsException() {
+ assertThrows(
+ UnsupportedOperationException.class,
+ () -> mBackupTransport.currentDestinationString());
+ }
+
+ @Test
+ public void testDataManagementIntent_returnsNull() {
+ assertThat(mBackupTransport.dataManagementIntent()).isNull();
+ }
+
+ @Test
+ public void testDataManagementIntentLabel_throwsException() {
+ assertThrows(
+ UnsupportedOperationException.class,
+ () -> mBackupTransport.dataManagementIntentLabel());
+ }
+
+ @Test
+ public void testTransportDirName_throwsException() {
+ assertThrows(
+ UnsupportedOperationException.class, () -> mBackupTransport.transportDirName());
+ }
+
+ @Test
+ public void testInitializeDevice_returnsTransportError() {
+ assertThat(mBackupTransport.initializeDevice()).isEqualTo(BackupTransport.TRANSPORT_ERROR);
+ }
+
+ @Test
+ public void testClearBackupData_returnsTransportError() {
+ assertThat(mBackupTransport.clearBackupData(null /* packageInfo */))
+ .isEqualTo(BackupTransport.TRANSPORT_ERROR);
+ }
+
+ @Test
+ public void testFinishBackup_returnsTransportError() {
+ assertThat(mBackupTransport.finishBackup()).isEqualTo(BackupTransport.TRANSPORT_ERROR);
+ }
+
+ @Test
+ public void testRequestBackupTime_returnsZero() {
+ assertThat(mBackupTransport.requestBackupTime()).isEqualTo(0);
+ }
+
+ @Test
+ public void testPerformBackupWithFlags_returnsTransportError() {
+ assertThat(
+ mBackupTransport.performBackup(
+ null /* packageInfo */, null /* inFd */, 0 /* flags */))
+ .isEqualTo(BackupTransport.TRANSPORT_ERROR);
+ }
+
+ @Test
+ public void testPerformBackup_returnsTransportError() {
+ assertThat(mBackupTransport.performBackup(null /* packageInfo */, null /* inFd */))
+ .isEqualTo(BackupTransport.TRANSPORT_ERROR);
+ }
+
+ @Test
+ public void testGetAvailableRestoreSets_returnsNull() {
+ assertThat(mBackupTransport.getAvailableRestoreSets()).isNull();
+ }
+
+ @Test
+ public void testGetCurrentRestoreSet_returnsZero() {
+ assertThat(mBackupTransport.getCurrentRestoreSet()).isEqualTo(0);
+ }
+
+ @Test
+ public void testStartRestore_returnsTransportError() {
+ assertThat(mBackupTransport.startRestore(0 /* token */, null /* packages */))
+ .isEqualTo(BackupTransport.TRANSPORT_ERROR);
+ }
+
+ @Test
+ public void testNextRestorePackage_returnsNull() {
+ assertThat(mBackupTransport.nextRestorePackage()).isNull();
+ }
+
+ @Test
+ public void testGetRestoreData_returnsTransportError() {
+ assertThat(mBackupTransport.getRestoreData(null /* outFd */))
+ .isEqualTo(BackupTransport.TRANSPORT_ERROR);
+ }
+
+ @Test
+ public void testFinishRestore_throwsException() {
+ assertThrows(UnsupportedOperationException.class, () -> mBackupTransport.finishRestore());
+ }
+
+ @Test
+ public void testRequestFullBackupTime_returnsZero() {
+ assertThat(mBackupTransport.requestFullBackupTime()).isEqualTo(0);
+ }
+
+ @Test
+ public void testPerformFullBackupWithFlags_returnsTransportPackageRejected() {
+ assertThat(
+ mBackupTransport.performFullBackup(
+ null /* testPackage */, null /* socket */, 0 /* flags */))
+ .isEqualTo(BackupTransport.TRANSPORT_PACKAGE_REJECTED);
+ }
+
+ @Test
+ public void testPerformFullBackup_returnsTransportPackageRejected() {
+ assertThat(mBackupTransport.performFullBackup(null /* targetPackage */, null /* socket */))
+ .isEqualTo(BackupTransport.TRANSPORT_PACKAGE_REJECTED);
+ }
+
+ @Test
+ public void testCheckFullBackupSize_whenZero_returnsTransportOk() {
+ assertThat(mBackupTransport.checkFullBackupSize(0)).isEqualTo(BackupTransport.TRANSPORT_OK);
+ }
+
+ @Test
+ public void testCheckFullBackupSize_whenMaxLong_returnsTransportOk() {
+ assertThat(mBackupTransport.checkFullBackupSize(Long.MAX_VALUE))
+ .isEqualTo(BackupTransport.TRANSPORT_OK);
+ }
+
+ @Test
+ public void testSendBackupData_returnsTransportError() {
+ assertThat(mBackupTransport.sendBackupData(0 /* numBytes */))
+ .isEqualTo(BackupTransport.TRANSPORT_ERROR);
+ }
+
+ @Test
+ public void testCancelFullBackup_throwsException() {
+ assertThrows(
+ UnsupportedOperationException.class, () -> mBackupTransport.cancelFullBackup());
+ }
+
+ @Test
+ public void testIsAppEligibleForBackup_isFullBackup_returnsTrue() {
+ assertThat(
+ mBackupTransport.isAppEligibleForBackup(
+ null /* targetPackage */, true /* isFullBackup */))
+ .isTrue();
+ }
+
+ @Test
+ public void testIsAppEligibleForBackup_isNotFullBackup_returnsTrue() {
+ assertThat(
+ mBackupTransport.isAppEligibleForBackup(
+ null /* targetPackage */, false /* isFullBackup */))
+ .isTrue();
+ }
+
+ @Test
+ public void testGetBackupQuota_isFullBackup_returnsMaxValue() {
+ assertThat(mBackupTransport.getBackupQuota(null /* packageName */, true /* isFullBackup */))
+ .isEqualTo(Long.MAX_VALUE);
+ }
+
+ @Test
+ public void testGetBackupQuota_isNotFullBackup_returnsMaxValue() {
+ assertThat(
+ mBackupTransport.getBackupQuota(
+ null /* packageName */, false /* isFullBackup */))
+ .isEqualTo(Long.MAX_VALUE);
+ }
+
+ @Test
+ public void testGetNextFullRestoreDataChunk_returnsZero() {
+ assertThat(mBackupTransport.getNextFullRestoreDataChunk(null /* socket */)).isEqualTo(0);
+ }
+
+ @Test
+ public void testAbortFullRestore_returnsTransportOk() {
+ assertThat(mBackupTransport.abortFullRestore()).isEqualTo(BackupTransport.TRANSPORT_OK);
+ }
+
+ @Test
+ public void testGetTransportFlags_returnsZero() {
+ assertThat(mBackupTransport.getTransportFlags()).isEqualTo(0);
+ }
+}
diff --git a/hostsidetests/backup/KeyValueApp/src/android/cts/backup/keyvaluerestoreapp/KeyValueBackupAgent.java b/hostsidetests/backup/KeyValueApp/src/android/cts/backup/keyvaluerestoreapp/KeyValueBackupAgent.java
index f79baa9..85a96b1 100644
--- a/hostsidetests/backup/KeyValueApp/src/android/cts/backup/keyvaluerestoreapp/KeyValueBackupAgent.java
+++ b/hostsidetests/backup/KeyValueApp/src/android/cts/backup/keyvaluerestoreapp/KeyValueBackupAgent.java
@@ -17,6 +17,10 @@
package android.cts.backup.keyvaluerestoreapp;
import android.app.backup.BackupAgentHelper;
+import android.app.backup.BackupDataInput;
+import android.app.backup.BackupDataOutput;
+import android.os.ParcelFileDescriptor;
+import java.io.IOException;
public class KeyValueBackupAgent extends BackupAgentHelper {
@@ -31,4 +35,20 @@
addHelper(KEY_BACKUP_TEST_FILES_PREFIX,
KeyValueBackupRestoreTest.getFileBackupHelper(this));
}
+
+ @Override
+ public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
+ ParcelFileDescriptor newState) throws IOException {
+ // Explicitly override and call super() to help go/android-api-coverage-dashboard pick up
+ // the test coverage (b/113067697).
+ super.onBackup(oldState, data, newState);
+ }
+
+ @Override
+ public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)
+ throws IOException {
+ // Explicitly override and call super() to help go/android-api-coverage-dashboard pick up
+ // the test coverage (b/113067697).
+ super.onRestore(data, appVersionCode, newState);
+ }
}
diff --git a/hostsidetests/backup/OWNERS b/hostsidetests/backup/OWNERS
index c28c4d8..e0e5e22 100644
--- a/hostsidetests/backup/OWNERS
+++ b/hostsidetests/backup/OWNERS
@@ -2,6 +2,7 @@
# Use this reviewer by default.
br-framework-team+reviews@google.com
+alsutton@google.com
anniemeng@google.com
brufino@google.com
nathch@google.com
diff --git a/hostsidetests/backup/RestoreSessionTest/src/android/cts/backup/restoresessionapp/RestoreSessionTest.java b/hostsidetests/backup/RestoreSessionTest/src/android/cts/backup/restoresessionapp/RestoreSessionTest.java
index 06b9ae0..adb7822 100644
--- a/hostsidetests/backup/RestoreSessionTest/src/android/cts/backup/restoresessionapp/RestoreSessionTest.java
+++ b/hostsidetests/backup/RestoreSessionTest/src/android/cts/backup/restoresessionapp/RestoreSessionTest.java
@@ -24,7 +24,6 @@
import static org.junit.Assert.assertNotEquals;
-import android.app.Instrumentation;
import android.app.UiAutomation;
import android.app.backup.BackupManager;
import android.app.backup.BackupManagerMonitor;
@@ -34,10 +33,10 @@
import android.content.Context;
import android.os.Bundle;
+import androidx.annotation.Nullable;
import android.platform.test.annotations.AppModeFull;
import androidx.test.runner.AndroidJUnit4;
-// import com.android.compatibility.common.util.SystemUtil;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -55,15 +54,19 @@
@RunWith(AndroidJUnit4.class)
@AppModeFull
public class RestoreSessionTest {
- private static final String PACKAGE_1 = "android.cts.backup.restoresessionapp1";
- private static final String PACKAGE_2 = "android.cts.backup.restoresessionapp2";
- private static final String PACKAGE_3 = "android.cts.backup.restoresessionapp3";
+ private static final String[] PACKAGES = new String[] {
+ "android.cts.backup.restoresessionapp1",
+ "android.cts.backup.restoresessionapp2",
+ "android.cts.backup.restoresessionapp3"
+ };
+ private static final int PACKAGES_COUNT = 3;
private static final int RESTORE_TIMEOUT_SECONDS = 10;
private BackupManager mBackupManager;
private Set<String> mRestorePackages;
private Set<String> mNonRestorePackages;
+ private CountDownLatch mRestoreSetsLatch;
private CountDownLatch mRestoreObserverLatch;
private RestoreSession mRestoreSession;
private UiAutomation mUiAutomation;
@@ -89,7 +92,7 @@
mRestoreToken = token;
- mRestoreObserverLatch.countDown();
+ mRestoreSetsLatch.countDown();
}
@Override
@@ -129,19 +132,10 @@
Context context = getTargetContext();
mBackupManager = new BackupManager(context);
- mRestorePackages = new HashSet<>();
- mRestorePackages.add(PACKAGE_1);
- mRestorePackages.add(PACKAGE_2);
-
- mNonRestorePackages = new HashSet<>();
- mNonRestorePackages.add(PACKAGE_3);
-
mRestoreToken = 0L;
mUiAutomation = getInstrumentation().getUiAutomation();
mUiAutomation.adoptShellPermissionIdentity();
-
- loadAvailableRestoreSets();
}
@After
@@ -151,11 +145,47 @@
/**
* Restore packages added to mRestorePackages and verify only those packages are restored. Use
+ * {@link RestoreSession#restorePackage(String, RestoreObserver)}
+ */
+
+ @Test
+ public void testRestorePackage() throws InterruptedException {
+ initPackagesToRestore(/* packagesCount */ 1);
+ testRestorePackagesInternal((BackupManagerMonitor monitor) -> {
+ mRestoreSession.restorePackage(
+ mRestorePackages.iterator().next(),
+ mRestoreObserver);
+ }, false);
+ }
+
+ /**
+ * Restore packages added to mRestorePackages and verify only those packages are restored. Use
+ * {@link RestoreSession#restorePackage(String, RestoreObserver, BackupManagerMonitor)}
+ */
+ @Test
+ public void testRestorePackageWithMonitorParam() throws InterruptedException {
+ initPackagesToRestore(/* packagesCount */ 1);
+ testRestorePackagesInternal((BackupManagerMonitor monitor) -> {
+ mRestoreSession.restorePackage(
+ mRestorePackages.iterator().next(),
+ mRestoreObserver,
+ monitor);
+ }, true);
+ }
+
+ /**
+ * Restore packages added to mRestorePackages and verify only those packages are restored. Use
* {@link RestoreSession#restorePackages(long, RestoreObserver, Set)}
*/
@Test
public void testRestorePackages() throws InterruptedException {
- testRestorePackagesInternal(false);
+ initPackagesToRestore(/* packagesCount */ 2);
+ testRestorePackagesInternal((BackupManagerMonitor monitor) -> {
+ mRestoreSession.restorePackages(
+ mRestoreToken,
+ mRestoreObserver,
+ mRestorePackages);
+ }, false);
}
/**
@@ -164,24 +194,30 @@
*/
@Test
public void testRestorePackagesWithMonitorParam() throws InterruptedException {
- testRestorePackagesInternal(true);
+ initPackagesToRestore(/* packagesCount */ 2);
+ testRestorePackagesInternal((BackupManagerMonitor monitor) -> {
+ mRestoreSession.restorePackages(
+ mRestoreToken,
+ mRestoreObserver,
+ mRestorePackages,
+ monitor);
+ }, true);
}
- private void testRestorePackagesInternal(boolean useMonitorParam) throws InterruptedException {
- // Wait for the callbacks from RestoreObserver: one for each package from
- // mRestorePackages plus restoreStarting and restoreFinished.
- mRestoreObserverLatch = new CountDownLatch(mRestorePackages.size() + 2);
+ private void testRestorePackagesInternal(RestoreRunner restoreRunner, boolean useMonitorParam)
+ throws InterruptedException {
CountDownLatch backupMonitorLatch = null;
if (useMonitorParam) {
// Wait for the callbacks from BackupManagerMonitor: one for each package.
backupMonitorLatch = new CountDownLatch(mRestorePackages.size());
- mRestoreSession.restorePackages(
- mRestoreToken,
- mRestoreObserver,
- mRestorePackages,
- new TestBackupMonitor(backupMonitorLatch));
+ BackupManagerMonitor backupMonitor = new TestBackupMonitor(backupMonitorLatch);
+
+ loadAvailableRestoreSets(backupMonitor);
+
+ restoreRunner.runRestore(backupMonitor);
} else {
- mRestoreSession.restorePackages(mRestoreToken, mRestoreObserver, mRestorePackages);
+ loadAvailableRestoreSets(null);
+ restoreRunner.runRestore(null);
}
awaitResultAndAssertSuccess(mRestoreObserverLatch);
@@ -190,13 +226,16 @@
}
}
- private void loadAvailableRestoreSets() throws InterruptedException {
+ private void loadAvailableRestoreSets(@Nullable BackupManagerMonitor monitor)
+ throws InterruptedException {
// Wait for getAvailableRestoreSets to finish and the callback to be fired.
- mRestoreObserverLatch = new CountDownLatch(1);
+ mRestoreSetsLatch = new CountDownLatch(1);
mRestoreSession = mBackupManager.beginRestoreSession();
assertEquals(
- BackupManager.SUCCESS, mRestoreSession.getAvailableRestoreSets(mRestoreObserver));
- awaitResultAndAssertSuccess(mRestoreObserverLatch);
+ BackupManager.SUCCESS, monitor == null
+ ? mRestoreSession.getAvailableRestoreSets(mRestoreObserver)
+ : mRestoreSession.getAvailableRestoreSets(mRestoreObserver, monitor));
+ awaitResultAndAssertSuccess(mRestoreSetsLatch);
assertNotEquals("Restore set not found", 0L, mRestoreToken);
}
@@ -215,6 +254,23 @@
assertTrue("Restore timed out", waitResult);
}
+ private void initPackagesToRestore(int packagesCount) {
+ mRestorePackages = new HashSet<>();
+ mNonRestorePackages = new HashSet<>();
+
+ for (int i = 0; i < PACKAGES_COUNT; i++) {
+ if (i < packagesCount) {
+ mRestorePackages.add(PACKAGES[i]);
+ } else {
+ mNonRestorePackages.add(PACKAGES[i]);
+ }
+ }
+
+ // Wait for the callbacks from RestoreObserver: one for each package from
+ // mRestorePackages plus restoreStarting and restoreFinished.
+ mRestoreObserverLatch = new CountDownLatch(mRestorePackages.size() + 2);
+ }
+
private static class TestBackupMonitor extends BackupManagerMonitor {
private final CountDownLatch mLatch;
@@ -234,4 +290,9 @@
mLatch.countDown();
}
}
+
+ @FunctionalInterface
+ private interface RestoreRunner {
+ void runRestore(BackupManagerMonitor monitor) throws InterruptedException;
+ }
}
diff --git a/hostsidetests/backup/src/android/cts/backup/AutoRestoreHostSideTest.java b/hostsidetests/backup/src/android/cts/backup/AutoRestoreHostSideTest.java
new file mode 100644
index 0000000..99d147c
--- /dev/null
+++ b/hostsidetests/backup/src/android/cts/backup/AutoRestoreHostSideTest.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.cts.backup;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.AppModeFull;
+
+import com.android.compatibility.common.util.BackupUtils;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Optional;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/** Test verifying that {@link BackupManager#setAutoRestore(boolean)} setting is respected. */
+@RunWith(DeviceJUnit4ClassRunner.class)
+@AppModeFull
+public class AutoRestoreHostSideTest extends BaseBackupHostSideTest {
+ private static final String PACKAGE = "android.cts.backup.autorestoreapp";
+ private static final String CLASS = PACKAGE + ".AutoRestoreTest";
+ private static final String APK = "CtsAutoRestoreApp.apk";
+
+ private BackupUtils mBackupUtils;
+ private Optional<Boolean> mWasAutoRestoreEnabled = Optional.empty();
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mBackupUtils = getBackupUtils();
+ mWasAutoRestoreEnabled = Optional.of(isAutoRestoreEnabled());
+
+ installPackage(APK);
+ }
+
+ @After
+ @Override
+ public void tearDown() throws Exception {
+ super.tearDown();
+
+ if (mWasAutoRestoreEnabled.isPresent()) {
+ mBackupUtils.executeShellCommandSync(
+ "bmgr autorestore " + (mWasAutoRestoreEnabled.get() ? "true" : "false"));
+ uninstallPackage(PACKAGE);
+
+ mWasAutoRestoreEnabled = Optional.empty();
+ }
+ }
+
+ /**
+ *
+ *
+ * <ol>
+ * <li>Enable auto restore
+ * <li>Write dummy values to shared preferences
+ * <li>Backup the package
+ * <li>Uninstall the package
+ * <li>Install the package again and verify shared prefs are restored
+ * </ol>
+ */
+ @Test
+ public void testSetAutoRestore_autoRestoresDataWhenEnabled() throws Exception {
+ runDeviceProcedure("enableAutoRestore");
+
+ populateSharedPrefs();
+
+ backupAndReinstallPackage();
+
+ runDeviceProcedure("testCheckSharedPrefsExist");
+ }
+
+ /**
+ *
+ *
+ * <ol>
+ * <li>Disable auto restore
+ * <li>Write dummy values to shared preferences
+ * <li>Backup the package
+ * <li>Uninstall the package
+ * <li>Install the package again and verify shared prefs aren't restored
+ * </ol>
+ */
+ @Test
+ public void testSetAutoRestore_dontAutoRestoresDataWhenDisabled() throws Exception {
+ runDeviceProcedure("disableAutoRestore");
+
+ populateSharedPrefs();
+
+ backupAndReinstallPackage();
+
+ runDeviceProcedure("testCheckSharedPrefsDontExist");
+ }
+
+ private void populateSharedPrefs() throws Exception {
+ runDeviceProcedure("saveValuesToSharedPrefs");
+ runDeviceProcedure("testCheckSharedPrefsExist");
+ }
+
+ private void backupAndReinstallPackage() throws Exception {
+ mBackupUtils.backupNowAndAssertSuccess(PACKAGE);
+ uninstallPackage(PACKAGE);
+ installPackage(APK);
+ }
+
+ private boolean isAutoRestoreEnabled() throws Exception {
+ String output = mBackupUtils.executeShellCommandAndReturnOutput("dumpsys backup");
+ Pattern pattern = Pattern.compile("Auto-restore is (enabled|disabled)");
+ Matcher matcher = pattern.matcher(output.trim());
+
+ assertThat(matcher.find()).isTrue();
+
+ return "enabled".equals(matcher.group(1));
+ }
+
+ private void runDeviceProcedure(String testName) throws Exception {
+ runDeviceTests(PACKAGE, CLASS, testName);
+ }
+}
diff --git a/hostsidetests/backup/src/android/cts/backup/BackupPreparer.java b/hostsidetests/backup/src/android/cts/backup/BackupPreparer.java
index 11a1dbc..a14f0f0 100644
--- a/hostsidetests/backup/src/android/cts/backup/BackupPreparer.java
+++ b/hostsidetests/backup/src/android/cts/backup/BackupPreparer.java
@@ -16,6 +16,10 @@
package android.cts.backup;
+import static org.junit.Assert.fail;
+
+import com.android.compatibility.common.util.BackupHostSideUtils;
+import com.android.compatibility.common.util.BackupUtils;
import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.config.Option;
import com.android.tradefed.config.OptionClass;
@@ -27,7 +31,12 @@
import com.android.tradefed.targetprep.ITargetCleaner;
import com.android.tradefed.targetprep.TargetSetupError;
+import java.io.IOException;
+import java.util.Optional;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CompletionException;
import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -38,6 +47,7 @@
*/
@OptionClass(alias = "backup-preparer")
public class BackupPreparer implements ITargetCleaner {
+ private static final long TRANSPORT_AVAILABLE_TIMEOUT_SECONDS = TimeUnit.MINUTES.toSeconds(5);
@Option(name="enable-backup-if-needed", description=
"Enable backup before all the tests and return to the original state after.")
private boolean mEnableBackup = true;
@@ -52,20 +62,20 @@
private static final String LOCAL_TRANSPORT =
"com.android.localtransport/.LocalTransport";
-
- private static final int BACKUP_PROVISIONING_TIMEOUT_SECONDS = 30;
- private static final int BACKUP_PROVISIONING_POLL_INTERVAL_SECONDS = 1;
+ private final int USER_SYSTEM = 0;
private boolean mIsBackupSupported;
private boolean mWasBackupEnabled;
+ private Optional<Boolean> mWasBackupActivated = Optional.empty();
private String mOldTransport;
private ITestDevice mDevice;
+ private BackupUtils mBackupUtils;
@Override
public void setUp(ITestDevice device, IBuildInfo buildInfo)
throws TargetSetupError, BuildError, DeviceNotAvailableException {
mDevice = device;
-
+ mBackupUtils = BackupHostSideUtils.createBackupUtils(mDevice);
mIsBackupSupported = mDevice.hasFeature("feature:" + FEATURE_BACKUP);
// In case the device was just rebooted, wait for the broadcast queue to get idle to avoid
@@ -73,11 +83,12 @@
waitForBroadcastIdle();
if (mIsBackupSupported) {
+ CLog.i("Activating backup on %s", mDevice.getSerialNumber());
+ mWasBackupActivated = Optional.of(setBackupActive(true));
+
// Enable backup and select local backup transport
- if (!hasBackupTransport(LOCAL_TRANSPORT)) {
- throw new TargetSetupError("Device should have LocalTransport available",
- device.getDeviceDescriptor());
- }
+ waitForTransport(LOCAL_TRANSPORT);
+
if (mEnableBackup) {
CLog.i("Enabling backup on %s", mDevice.getSerialNumber());
mWasBackupEnabled = enableBackup(true);
@@ -87,7 +98,11 @@
mOldTransport = setBackupTransport(LOCAL_TRANSPORT);
CLog.d("Old transport : %s", mOldTransport);
}
- waitForBackupInitialization();
+ try {
+ mBackupUtils.waitForBackupInitialization();
+ } catch (IOException e) {
+ throw new TargetSetupError("Backup not initialized", e);
+ }
}
}
}
@@ -98,29 +113,73 @@
mDevice = device;
if (mIsBackupSupported) {
- if (mEnableBackup) {
- CLog.i("Returning backup to it's previous state on %s", mDevice.getSerialNumber());
- enableBackup(mWasBackupEnabled);
- if (mSelectLocalTransport) {
- CLog.i("Returning selected transport to it's previous value on %s",
+ if (mWasBackupActivated.isPresent()) {
+ setBackupActive(mWasBackupActivated.get());
+
+ if (mEnableBackup) {
+ CLog.i("Returning backup to it's previous state on %s",
mDevice.getSerialNumber());
- setBackupTransport(mOldTransport);
+ enableBackup(mWasBackupEnabled);
+ if (mSelectLocalTransport) {
+ CLog.i("Returning selected transport to it's previous value on %s",
+ mDevice.getSerialNumber());
+ setBackupTransport(mOldTransport);
+ }
}
}
}
}
- // Copied over from BackupQuotaTest
- private boolean hasBackupTransport(String transport) throws DeviceNotAvailableException {
+ private void waitForTransport(String transport) throws TargetSetupError {
+ try {
+ waitUntilWithLastTry(
+ "Local transport didn't become available",
+ TRANSPORT_AVAILABLE_TIMEOUT_SECONDS,
+ lastTry -> uncheck(() -> hasBackupTransport(transport, lastTry)));
+ } catch (InterruptedException e) {
+ throw new TargetSetupError(
+ "Device should have LocalTransport available", mDevice.getDeviceDescriptor());
+ }
+ }
+
+ private boolean hasBackupTransport(
+ String transport, boolean logIfFail) throws DeviceNotAvailableException {
String output = mDevice.executeShellCommand("bmgr list transports");
for (String t : output.split(" ")) {
if (transport.equals(t.trim())) {
return true;
}
}
+ if (logIfFail) {
+ CLog.d("bmgr list transports: " + output);
+ }
return false;
}
+ /**
+ * Calls {@code predicate} with {@code false} until time-out {@code timeoutSeconds} is reached,
+ * if {@code predicate} returns true, method returns. If time-out is reached before that, we
+ * call {@code predicate} with {@code true} one last time, if that last call returns false we
+ * fail with {@code message}.
+ *
+ * TODO: Move to CommonTestUtils
+ */
+ private static void waitUntilWithLastTry(
+ String message, long timeoutSeconds, Function<Boolean, Boolean> predicate)
+ throws InterruptedException {
+ int sleep = 125;
+ final long timeout = System.currentTimeMillis() + timeoutSeconds * 1000;
+ while (System.currentTimeMillis() < timeout) {
+ if (predicate.apply(false)) {
+ return;
+ }
+ Thread.sleep(sleep);
+ }
+ if (!predicate.apply(true)) {
+ fail(message);
+ }
+ }
+
// Copied over from BackupQuotaTest
private boolean enableBackup(boolean enable) throws DeviceNotAvailableException {
boolean previouslyEnabled;
@@ -149,27 +208,6 @@
}
}
- private void waitForBackupInitialization()
- throws TargetSetupError, DeviceNotAvailableException {
- long tryUntilNanos = System.nanoTime()
- + TimeUnit.SECONDS.toNanos(BACKUP_PROVISIONING_TIMEOUT_SECONDS);
- while (System.nanoTime() < tryUntilNanos) {
- String output = mDevice.executeShellCommand("dumpsys backup");
- if (output.matches("(?s)" // DOTALL
- + "^Backup Manager is .* not pending init.*")) {
- return;
- }
- try {
- Thread.sleep(TimeUnit.SECONDS.toMillis(BACKUP_PROVISIONING_POLL_INTERVAL_SECONDS));
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- break;
- }
- }
- throw new TargetSetupError("Timed out waiting for backup initialization",
- mDevice.getDeviceDescriptor());
- }
-
// Copied over from BaseDevicePolicyTest
private void waitForBroadcastIdle() throws DeviceNotAvailableException, TargetSetupError {
CollectingOutputReceiver receiver = new CollectingOutputReceiver();
@@ -190,4 +228,25 @@
}
}
+ private boolean setBackupActive(boolean active) {
+ boolean wasBackupActive;
+ try {
+ wasBackupActive = mBackupUtils.isBackupActivatedForUser(USER_SYSTEM);
+ mBackupUtils.activateBackupForUser(active, USER_SYSTEM);
+ } catch (IOException e) {
+ throw new RuntimeException("Failed set backup active status");
+ }
+
+ return wasBackupActive;
+ }
+
+ private static <T> T uncheck(Callable<T> callable) {
+ try {
+ return callable.call();
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new CompletionException(e);
+ }
+ }
}
diff --git a/hostsidetests/backup/src/android/cts/backup/BackupTransportHostSideTest.java b/hostsidetests/backup/src/android/cts/backup/BackupTransportHostSideTest.java
new file mode 100644
index 0000000..a6a4f3a
--- /dev/null
+++ b/hostsidetests/backup/src/android/cts/backup/BackupTransportHostSideTest.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.cts.backup;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+/** Test verifying the default implementation of {@link BackupTransport} */
+// TODO(b/135920714): Move this test to device-side once it's possible to use both system and test
+// APIs in the same target.
+public class BackupTransportHostSideTest extends BaseBackupHostSideTest {
+ private static final String PACKAGE = "android.cts.backup.backuptransportapp";
+ private static final String CLASS = PACKAGE + ".BackupTransportTest";
+ private static final String APK = "CtsBackupTransportApp.apk";
+
+ @Test
+ public void testBackupTransport() throws Exception {
+ installPackage(APK);
+ assertTrue(runDeviceTests(PACKAGE, CLASS));
+ uninstallPackage(PACKAGE);
+ }
+}
diff --git a/hostsidetests/backup/src/android/cts/backup/BaseMultiUserBackupHostSideTest.java b/hostsidetests/backup/src/android/cts/backup/BaseMultiUserBackupHostSideTest.java
index 81737e9..c1171ac 100644
--- a/hostsidetests/backup/src/android/cts/backup/BaseMultiUserBackupHostSideTest.java
+++ b/hostsidetests/backup/src/android/cts/backup/BaseMultiUserBackupHostSideTest.java
@@ -23,7 +23,7 @@
import android.platform.test.annotations.AppModeFull;
import com.android.compatibility.common.util.BackupUtils;
-import com.android.compatibility.common.util.HostSideTestUtils;
+import com.android.compatibility.common.util.CommonTestUtils;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.log.LogUtil.CLog;
@@ -58,7 +58,7 @@
FULL_BACKUP_TEST_PACKAGE + ".ProfileFullBackupRestoreTest";
protected final BackupUtils mBackupUtils = getBackupUtils();
- protected ITestDevice mDevice;
+ private ITestDevice mDevice;
// Store initial device state as Optional as tearDown() will execute even if we have assumption
// failures in setUp().
@@ -138,13 +138,14 @@
* Selects the local transport as the current transport for user {@code userId}. Returns the
* {@link String} name of the local transport.
*/
- String switchUserToLocalTransportAndAssertSuccess(int userId) throws IOException {
+ String switchUserToLocalTransportAndAssertSuccess(int userId)
+ throws IOException, InterruptedException {
// Make sure the user has the local transport.
String localTransport = mBackupUtils.getLocalTransportName();
// TODO (b/121198010): Update dumpsys or add shell command to query status of transport
// initialization. Transports won't be available until they are initialized/registered.
- HostSideTestUtils.waitUntil("wait for user to have local transport",
+ CommonTestUtils.waitUntil("wait for user to have local transport",
TRANSPORT_INITIALIZATION_TIMEOUT_SECS,
() -> mBackupUtils.userHasBackupTransport(localTransport, userId));
diff --git a/hostsidetests/backup/src/android/cts/backup/MultiUserBackupStateTest.java b/hostsidetests/backup/src/android/cts/backup/MultiUserBackupStateTest.java
index 4fa4469..25c59d6 100644
--- a/hostsidetests/backup/src/android/cts/backup/MultiUserBackupStateTest.java
+++ b/hostsidetests/backup/src/android/cts/backup/MultiUserBackupStateTest.java
@@ -20,7 +20,7 @@
import android.platform.test.annotations.AppModeFull;
-import com.android.compatibility.common.util.HostSideTestUtils;
+import com.android.compatibility.common.util.CommonTestUtils;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import org.junit.After;
@@ -44,7 +44,7 @@
public void setUp() throws Exception {
super.setUp();
- int profileUserId = createProfileUser(mDevice.getCurrentUser(), "MU-State");
+ int profileUserId = createProfileUser(getDevice().getCurrentUser(), "MU-State");
mProfileUserId = Optional.of(profileUserId);
startUser(profileUserId);
}
@@ -54,7 +54,7 @@
@Override
public void tearDown() throws Exception {
if (mProfileUserId.isPresent()) {
- assertTrue(mDevice.removeUser(mProfileUserId.get()));
+ assertTrue(getDevice().removeUser(mProfileUserId.get()));
mProfileUserId = Optional.empty();
}
super.tearDown();
@@ -77,10 +77,10 @@
assertTrue(mBackupUtils.isBackupActivatedForUser(profileUserId));
- assertTrue(mDevice.removeUser(profileUserId));
+ assertTrue(getDevice().removeUser(profileUserId));
mProfileUserId = Optional.empty();
- HostSideTestUtils.waitUntil("wait for backup to be deactivated for removed user",
+ CommonTestUtils.waitUntil("wait for backup to be deactivated for removed user",
TIMEOUT_SECONDS, () -> !mBackupUtils.isBackupActivatedForUser(profileUserId));
}
}
diff --git a/hostsidetests/backup/src/android/cts/backup/ProfileScheduledJobHostSideTest.java b/hostsidetests/backup/src/android/cts/backup/ProfileScheduledJobHostSideTest.java
index 7eff018..874282b 100644
--- a/hostsidetests/backup/src/android/cts/backup/ProfileScheduledJobHostSideTest.java
+++ b/hostsidetests/backup/src/android/cts/backup/ProfileScheduledJobHostSideTest.java
@@ -21,6 +21,9 @@
import android.platform.test.annotations.AppModeFull;
import com.android.compatibility.common.util.BackupUtils;
+import com.android.compatibility.common.util.ProtoUtils;
+import com.android.server.job.JobSchedulerServiceDumpProto;
+import com.android.server.job.JobStatusShortInfoProto;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
@@ -29,10 +32,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.io.IOException;
import java.util.Optional;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
/** Test that key-value and full backup jobs are scheduled in a profile. */
@RunWith(DeviceJUnit4ClassRunner.class)
@@ -60,7 +60,6 @@
private static final int TIMEOUT_FOR_KEY_VALUE_SECONDS = 5 * 60; // 5 minutes.
private static final int TIMEOUT_FOR_FULL_BACKUP_SECONDS = 5 * 60; // 5 minutes.
- private static final String DUMPSYS_JOB_SCHEDULER = "dumpsys jobscheduler";
private static final String JOB_SCHEDULER_RUN_COMMAND = "cmd jobscheduler run -f android";
private final BackupUtils mBackupUtils = getBackupUtils();
@@ -196,10 +195,19 @@
}
/** Returns {@code true} if there is a system job scheduled with the specified parameters. */
- private boolean isSystemJobScheduled(int jobId, String jobName) throws IOException {
- String output = mBackupUtils.executeShellCommandAndReturnOutput(DUMPSYS_JOB_SCHEDULER);
- String jobRegex = String.format("JOB #1000/%d.*%s", jobId, jobName);
- Matcher matcher = Pattern.compile(jobRegex).matcher(output.trim());
- return matcher.find();
+ private boolean isSystemJobScheduled(int jobId, String jobName) throws Exception {
+ // TODO: Look into making a higher level adb command or a system API for this instead.
+ // (e.g. "adb shell cmd jobscheduler is-job-scheduled system JOB-ID JOB-NAME")
+ final JobSchedulerServiceDumpProto dump =
+ ProtoUtils.getProto(mDevice, JobSchedulerServiceDumpProto.parser(),
+ ProtoUtils.DUMPSYS_JOB_SCHEDULER);
+ for (JobSchedulerServiceDumpProto.RegisteredJob job : dump.getRegisteredJobsList()) {
+ final JobStatusShortInfoProto info = job.getInfo();
+ if (info.getCallingUid() == 1000 && info.getJobId() == jobId
+ && jobName.equals(info.getBatteryName())) {
+ return true;
+ }
+ }
+ return false;
}
}
diff --git a/hostsidetests/backup/src/android/cts/backup/RestoreSessionHostSideTest.java b/hostsidetests/backup/src/android/cts/backup/RestoreSessionHostSideTest.java
index 9c5d890..89e2539 100644
--- a/hostsidetests/backup/src/android/cts/backup/RestoreSessionHostSideTest.java
+++ b/hostsidetests/backup/src/android/cts/backup/RestoreSessionHostSideTest.java
@@ -72,10 +72,25 @@
}
}
+ /** Test {@link RestoreSession#restorePackage(RestoreObserver, String)} */
+ @Test
+ public void testRestorePackage() throws Exception {
+ testRestorePackagesInternal("testRestorePackage", /* packagesToRestore */ 1);
+ }
+
+ /**
+ * Test {@link RestoreSession#restorePackage(RestoreObserver, String, BackupManagerMonitor)}
+ */
+ @Test
+ public void testRestorePackageWithMonitorParam() throws Exception {
+ testRestorePackagesInternal("testRestorePackageWithMonitorParam",
+ /* packagesToRestore */ 1);
+ }
+
/** Test {@link RestoreSession#restorePackages(long, RestoreObserver, Set)} */
@Test
public void testRestorePackages() throws Exception {
- testRestorePackagesInternal("testRestorePackages");
+ testRestorePackagesInternal("testRestorePackages", /* packagesToRestore */ 2);
}
/**
@@ -83,7 +98,8 @@
*/
@Test
public void testRestorePackagesWithMonitorParam() throws Exception {
- testRestorePackagesInternal("testRestorePackagesWithMonitorParam");
+ testRestorePackagesInternal("testRestorePackagesWithMonitorParam",
+ /* packagesToRestore */ 2);
}
/**
@@ -94,15 +110,16 @@
* <li>Write dummy values to shared preferences for each package
* <li>Backup each package (adb shell bmgr backupnow)
* <li>Clear shared preferences for each package
- * <li>Run restore for 2 of the packages and verify that only they were restored
- * <li>Verify that shared preferences for the 2 packages are restored correctly
+ * <li>Run restore for the first {@code numPackagesToRestore}, verify only those are restored
+ * <li>Verify that shared preferences for the restored packages are restored correctly
* </ol>
*/
- private void testRestorePackagesInternal(String deviceTestName) throws Exception {
+ private void testRestorePackagesInternal(String deviceTestName, int numPackagesToRestore)
+ throws Exception {
installPackage(getApkNameForTestApp(1));
installPackage(getApkNameForTestApp(2));
installPackage(getApkNameForTestApp(3));
- //
+
// Write dummy value to shared preferences for all test packages.
checkRestoreSessionDeviceTestForAllApps("testSaveValuesToSharedPrefs");
checkRestoreSessionDeviceTestForAllApps("testCheckSharedPrefsExist");
@@ -119,11 +136,15 @@
runRestoreSessionDeviceTestAndAssertSuccess(
MAIN_TEST_APP_PKG, DEVICE_MAIN_TEST_CLASS_NAME, deviceTestName);
- // Check that shared prefs are only restored (and restored correctly) for the first 2
- // packages.
- checkRestoreSessionDeviceTest(1, "testCheckSharedPrefsExist");
- checkRestoreSessionDeviceTest(2, "testCheckSharedPrefsExist");
- checkRestoreSessionDeviceTest(3, "testCheckSharedPrefsDontExist");
+ // Check that shared prefs are only restored (and restored correctly) for the packages
+ // that need to be restored.
+ for (int i = 1; i <= TEST_APPS_COUNT; i++) {
+ if (i <= numPackagesToRestore) {
+ checkRestoreSessionDeviceTest(i, "testCheckSharedPrefsExist");
+ } else {
+ checkRestoreSessionDeviceTest(i, "testCheckSharedPrefsDontExist");
+ }
+ }
uninstallPackage(getPackageNameForTestApp(1));
uninstallPackage(getPackageNameForTestApp(2));
diff --git a/hostsidetests/bootstats/OWNERS b/hostsidetests/bootstats/OWNERS
new file mode 100644
index 0000000..1f99e96
--- /dev/null
+++ b/hostsidetests/bootstats/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 183496
+keunyoung@google.com
diff --git a/hostsidetests/checkpoint/Android.mk b/hostsidetests/checkpoint/Android.mk
index 7f044b0..fc66d79 100644
--- a/hostsidetests/checkpoint/Android.mk
+++ b/hostsidetests/checkpoint/Android.mk
@@ -25,9 +25,7 @@
LOCAL_CTS_TEST_PACKAGE := android.checkpoint
# tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests gts
-
-LOCAL_MIN_SDK_VERSION := 4
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
include $(BUILD_CTS_HOST_JAVA_LIBRARY)
diff --git a/hostsidetests/checkpoint/AndroidTest.xml b/hostsidetests/checkpoint/AndroidTest.xml
index 1b8aba3..54aeaa8 100644
--- a/hostsidetests/checkpoint/AndroidTest.xml
+++ b/hostsidetests/checkpoint/AndroidTest.xml
@@ -15,7 +15,6 @@
-->
<configuration description="Config for the CTS Checkpoint host tests">
<option name="test-suite-tag" value="cts" />
- <option name="test-suite-tag" value="gts" />
<option name="config-descriptor:metadata" key="component" value="systems" />
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
diff --git a/hostsidetests/checkpoint/src/android/checkpoint/cts/CheckpointHostTest.java b/hostsidetests/checkpoint/src/android/checkpoint/cts/CheckpointHostTest.java
index ea876d9..3426443 100644
--- a/hostsidetests/checkpoint/src/android/checkpoint/cts/CheckpointHostTest.java
+++ b/hostsidetests/checkpoint/src/android/checkpoint/cts/CheckpointHostTest.java
@@ -16,7 +16,6 @@
package android.checkpoint.cts;
-import com.android.compatibility.common.util.CtsDownstreamingTest;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.testtype.DeviceTestCase;
import junit.framework.Assert;
@@ -28,7 +27,6 @@
public class CheckpointHostTest extends DeviceTestCase {
private static final String TAG = "CheckpointHostTest";
- @CtsDownstreamingTest
public void testLogEntries() throws Exception {
// Clear buffer to make it easier to find new logs
getDevice().executeShellCommand("logcat --clear");
diff --git a/hostsidetests/classloaders/OWNERS b/hostsidetests/classloaders/OWNERS
new file mode 100644
index 0000000..137abb9
--- /dev/null
+++ b/hostsidetests/classloaders/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 86431
+calin@google.com
+ngeoffray@google.com
+sehr@google.com
diff --git a/hostsidetests/compilation/OWNERS b/hostsidetests/compilation/OWNERS
new file mode 100644
index 0000000..f386ff2
--- /dev/null
+++ b/hostsidetests/compilation/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 86431
+include ../classloaders/OWNERS
diff --git a/hostsidetests/compilation/app/Android.bp b/hostsidetests/compilation/app/Android.bp
index f4954c7..770baea 100644
--- a/hostsidetests/compilation/app/Android.bp
+++ b/hostsidetests/compilation/app/Android.bp
@@ -16,7 +16,7 @@
name: "CtsCompilationApp",
defaults: ["cts_support_defaults"],
srcs: ["src/**/*.java"],
- sdk_version: "current",
+ sdk_version: "29",
// tag this module as a cts test artifact
test_suites: [
"cts",
diff --git a/hostsidetests/devicepolicy/OWNERS b/hostsidetests/devicepolicy/OWNERS
new file mode 100644
index 0000000..12341eb
--- /dev/null
+++ b/hostsidetests/devicepolicy/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 100560
+include /tests/admin/OWNERS
diff --git a/hostsidetests/devicepolicy/TEST_MAPPING b/hostsidetests/devicepolicy/TEST_MAPPING
new file mode 100644
index 0000000..3d86cf3
--- /dev/null
+++ b/hostsidetests/devicepolicy/TEST_MAPPING
@@ -0,0 +1,20 @@
+{
+ "presubmit-devicepolicy": [
+ {
+ "name": "CtsDevicePolicyManagerTestCases",
+ "options": [
+ {
+ "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ },
+ {
+ "exclude-annotation": "android.platform.test.annotations.LargeTest"
+ }
+ ]
+ }
+ ],
+ "postsubmit-devicepolicy": [
+ {
+ "name": "CtsDevicePolicyManagerTestCases"
+ }
+ ]
+}
diff --git a/hostsidetests/devicepolicy/app/CertInstaller/src/com/android/cts/certinstaller/CertSelectionDelegateTest.java b/hostsidetests/devicepolicy/app/CertInstaller/src/com/android/cts/certinstaller/CertSelectionDelegateTest.java
index 609ea1f..f9313ee 100644
--- a/hostsidetests/devicepolicy/app/CertInstaller/src/com/android/cts/certinstaller/CertSelectionDelegateTest.java
+++ b/hostsidetests/devicepolicy/app/CertInstaller/src/com/android/cts/certinstaller/CertSelectionDelegateTest.java
@@ -18,6 +18,8 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
+import static org.junit.Assert.assertNull;
+
import android.app.Activity;
import android.app.admin.DelegatedAdminReceiver;
import android.app.admin.DevicePolicyManager;
@@ -114,6 +116,16 @@
}
}
+ // Tests that if the delegated app returns {@link
+ // android.security.KeyChain.KEY_ALIAS_SELECTION_DENIED}
+ // the caller of {@link android.app.admin.DelegatedAdminReceiver#onChoosePrivateKeyAlias}
+ // receives {@code null}.
+ public void testNotChosenAnyAlias() throws Exception {
+ assertThat(mDpm.getDelegatedScopes(null, mContext.getPackageName())).contains(
+ DevicePolicyManager.DELEGATION_CERT_SELECTION);
+ assertNull(new KeyChainAliasFuture(KeyChain.KEY_ALIAS_SELECTION_DENIED).get());
+ }
+
private class KeyChainAliasFuture implements KeyChainAliasCallback {
private final CountDownLatch mLatch = new CountDownLatch(1);
private String mChosenAlias = null;
diff --git a/hostsidetests/devicepolicy/app/CertInstaller/src/com/android/cts/certinstaller/PreSelectedKeyAccessTest.java b/hostsidetests/devicepolicy/app/CertInstaller/src/com/android/cts/certinstaller/PreSelectedKeyAccessTest.java
new file mode 100644
index 0000000..605a0b4
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/CertInstaller/src/com/android/cts/certinstaller/PreSelectedKeyAccessTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.certinstaller;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.security.KeyChain;
+import android.security.KeyChainException;
+import android.content.Context;
+import android.test.InstrumentationTestCase;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.cert.X509Certificate;
+
+public class PreSelectedKeyAccessTest extends InstrumentationTestCase {
+ private static final String PRE_SELECTED_ALIAS = "pre-selected-rsa";
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ }
+
+ // Test that this app can access pre-granted alias
+ public void testAccessingPreSelectedAliasExpectingSuccess() throws
+ KeyChainException, NoSuchAlgorithmException, InvalidKeyException, SignatureException,
+ InterruptedException {
+ PrivateKey privateKey = KeyChain.getPrivateKey(getContext(), PRE_SELECTED_ALIAS);
+ assertThat(privateKey).isNotNull();
+ String algoIdentifier = "SHA256withRSA";
+
+ byte[] data = new String("hello").getBytes();
+ Signature sign = Signature.getInstance(algoIdentifier);
+ sign.initSign(privateKey);
+ sign.update(data);
+ byte[] signature = sign.sign();
+
+ X509Certificate[] certs = KeyChain.getCertificateChain(getContext(), PRE_SELECTED_ALIAS);
+ assertThat(certs).isNotEmpty();
+
+ PublicKey publicKey = certs[0].getPublicKey();
+ Signature verify = Signature.getInstance(algoIdentifier);
+ verify.initVerify(publicKey);
+ verify.update(data);
+ assertThat(verify.verify(signature)).isTrue();
+ }
+
+ public void testAccessingPreSelectedAliasWithoutGrant() throws
+ KeyChainException, InterruptedException {
+ assertThat(KeyChain.getPrivateKey(getContext(), PRE_SELECTED_ALIAS)).isNull();
+ }
+
+ private Context getContext() {
+ return getInstrumentation().getContext();
+ }
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceAdminPasswordTest.java b/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceAdminPasswordTest.java
index 1e9759c..472f3de 100644
--- a/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceAdminPasswordTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceAdminPasswordTest.java
@@ -67,28 +67,22 @@
}
}
- private boolean waitUntilPasswordState(boolean expected) throws InterruptedException {
+ private boolean getPasswordState() throws InterruptedException {
final int currentQuality = dpm.getPasswordQuality(mAdminComponent);
dpm.setPasswordQuality(mAdminComponent, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
-
- int numTries = 5;
- boolean result = dpm.isActivePasswordSufficient();
- while ((numTries > 0) && (result != expected)) {
- numTries = numTries - 1;
- Thread.sleep(100);
- result = dpm.isActivePasswordSufficient();
+ try {
+ return dpm.isActivePasswordSufficient();
+ } finally {
+ dpm.setPasswordQuality(mAdminComponent, currentQuality);
}
-
- dpm.setPasswordQuality(mAdminComponent, currentQuality);
- return expected == result;
}
private void assertHasPassword() throws InterruptedException {
- assertTrue("No password set", waitUntilPasswordState(true));
+ assertTrue("No password set", getPasswordState());
}
private void assertNoPassword() throws InterruptedException {
- assertTrue("Password is set", waitUntilPasswordState(false));
+ assertFalse("Password is set", getPasswordState());
}
/**
diff --git a/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceOwnerPasswordTest.java b/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceOwnerPasswordTest.java
index fe2621e..1fae525 100644
--- a/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceOwnerPasswordTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceOwnerPasswordTest.java
@@ -60,10 +60,9 @@
assertEquals(10, dpm.getPasswordMinimumLength(mAdminComponent));
assertPasswordSufficiency(true); // length not checked for this quality
- // TODO(ascull): fix resetPassword() logic so these succeed
- assertPasswordFails("1234", caseDescription);
- assertPasswordFails("abcd", caseDescription);
- assertPasswordFails("abcd1234", caseDescription);
+ assertPasswordSucceeds("1234", caseDescription);
+ assertPasswordSucceeds("abcd", caseDescription);
+ assertPasswordSucceeds("abcd1234", caseDescription);
dpm.setPasswordMinimumLength(mAdminComponent, 4);
caseDescription = "minimum password length = 4";
@@ -378,17 +377,6 @@
}
private void assertPasswordSufficiency(boolean expectPasswordSufficient) {
- int retries = 15;
- // isActivePasswordSufficient() gets the result asynchronously so let's retry a few times
- while (retries >= 0 && dpm.isActivePasswordSufficient() != expectPasswordSufficient) {
- retries--;
- try {
- Thread.sleep(200);
- } catch (InterruptedException e) {
- break;
- }
- }
assertEquals(expectPasswordSufficient, dpm.isActivePasswordSufficient());
-
}
}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/BaseDeviceAdminTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/BaseDeviceAdminTest.java
index 8f9b9b5..212d20f 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/BaseDeviceAdminTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/BaseDeviceAdminTest.java
@@ -126,17 +126,6 @@
protected void assertPasswordSufficiency(boolean expectPasswordSufficient) {
waitUntilUserUnlocked();
- int retries = 15;
- // isActivePasswordSufficient() gets the result asynchronously so let's retry a few times
- while (retries >= 0
- && mDevicePolicyManager.isActivePasswordSufficient() != expectPasswordSufficient) {
- retries--;
- try {
- Thread.sleep(200);
- } catch (InterruptedException e) {
- break;
- }
- }
assertEquals(expectPasswordSufficient, mDevicePolicyManager.isActivePasswordSufficient());
}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/DelegatedCertInstallerHelper.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/DelegatedCertInstallerHelper.java
index 756058c..8e8a6d6 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/DelegatedCertInstallerHelper.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/DelegatedCertInstallerHelper.java
@@ -19,6 +19,7 @@
import static android.app.admin.DevicePolicyManager.DELEGATION_CERT_INSTALL;
import android.app.admin.DevicePolicyManager;
+import android.keystore.cts.KeyGenerationUtils;
import java.util.Arrays;
import java.util.List;
@@ -32,6 +33,9 @@
private static final List<String> CERT_INSTALL_SCOPES = Arrays.asList(DELEGATION_CERT_INSTALL);
+ // MUST match the alias in PreSelectedKeyAccessTest
+ private static final String PRE_SELECTED_ALIAS = "pre-selected-rsa";
+
private DevicePolicyManager mDpm;
@Override
@@ -57,4 +61,19 @@
assertFalse(mDpm.getDelegatePackages(ADMIN_RECEIVER_COMPONENT,
DELEGATION_CERT_INSTALL).contains(CERT_INSTALLER_PACKAGE));
}
+
+ public void testManualGenerateKeyAndGrantAccess() {
+ KeyGenerationUtils.generateRsaKey(mDpm, ADMIN_RECEIVER_COMPONENT, PRE_SELECTED_ALIAS);
+ assertTrue(mDpm.grantKeyPairToApp(ADMIN_RECEIVER_COMPONENT, PRE_SELECTED_ALIAS,
+ CERT_INSTALLER_PACKAGE));
+ }
+
+ public void testManualRemoveKeyGrant() {
+ assertTrue(mDpm.revokeKeyPairFromApp(ADMIN_RECEIVER_COMPONENT, PRE_SELECTED_ALIAS,
+ CERT_INSTALLER_PACKAGE));
+ }
+
+ public void testManualClearGeneratedKey() {
+ assertTrue(mDpm.removeKeyPair(ADMIN_RECEIVER_COMPONENT, PRE_SELECTED_ALIAS));
+ }
}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/DevicePolicyLoggingTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/DevicePolicyLoggingTest.java
index d6856a9..4e58d92 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/DevicePolicyLoggingTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/DevicePolicyLoggingTest.java
@@ -50,7 +50,7 @@
public void testPasswordMethodsLogged() {
mDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT,
- DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
+ DevicePolicyManager.PASSWORD_QUALITY_COMPLEX);
mDevicePolicyManager.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, 13);
mDevicePolicyManager.setPasswordMinimumNumeric(ADMIN_RECEIVER_COMPONENT, 14);
mDevicePolicyManager.setPasswordMinimumNonLetter(ADMIN_RECEIVER_COMPONENT, 15);
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PasswordRequirementsTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PasswordRequirementsTest.java
new file mode 100644
index 0000000..f9ce726
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PasswordRequirementsTest.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.deviceandprofileowner;
+
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+
+import static org.testng.Assert.assertThrows;
+
+/**
+ * Class that tests password constraints API preconditions.
+ */
+public class PasswordRequirementsTest extends BaseDeviceAdminTest {
+ private static final int TEST_VALUE = 5;
+ private static final int DEFAULT_NUMERIC = 1;
+ private static final int DEFAULT_LETTERS = 1;
+ private static final int DEFAULT_UPPERCASE = 0;
+ private static final int DEFAULT_LOWERCASE = 0;
+ private static final int DEFAULT_NON_LETTER = 0;
+ private static final int DEFAULT_SYMBOLS = 1;
+ private static final int DEFAULT_LENGTH = 0;
+
+ public void testPasswordConstraintsDoesntThrowAndPreservesValuesPreR() {
+ // Pre-R password restrictions can be set in any order.
+ mDevicePolicyManager.setPasswordQuality(
+ ADMIN_RECEIVER_COMPONENT, PASSWORD_QUALITY_SOMETHING);
+ // These shouldn't throw.
+ mDevicePolicyManager.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, TEST_VALUE);
+ mDevicePolicyManager.setPasswordMinimumNumeric(ADMIN_RECEIVER_COMPONENT, TEST_VALUE);
+ mDevicePolicyManager.setPasswordMinimumLetters(ADMIN_RECEIVER_COMPONENT, TEST_VALUE);
+ mDevicePolicyManager.setPasswordMinimumUpperCase(ADMIN_RECEIVER_COMPONENT, TEST_VALUE);
+ mDevicePolicyManager.setPasswordMinimumLowerCase(ADMIN_RECEIVER_COMPONENT, TEST_VALUE);
+ mDevicePolicyManager.setPasswordMinimumNonLetter(ADMIN_RECEIVER_COMPONENT, TEST_VALUE);
+ mDevicePolicyManager.setPasswordMinimumSymbols(ADMIN_RECEIVER_COMPONENT, TEST_VALUE);
+
+ // Make sure these values are preserved and not reset when quality is set low.
+ mDevicePolicyManager.setPasswordQuality(
+ ADMIN_RECEIVER_COMPONENT, PASSWORD_QUALITY_UNSPECIFIED);
+ assertEquals(TEST_VALUE,
+ mDevicePolicyManager.getPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT));
+ assertEquals(TEST_VALUE,
+ mDevicePolicyManager.getPasswordMinimumNumeric(ADMIN_RECEIVER_COMPONENT));
+ assertEquals(TEST_VALUE,
+ mDevicePolicyManager.getPasswordMinimumLetters(ADMIN_RECEIVER_COMPONENT));
+ assertEquals(TEST_VALUE,
+ mDevicePolicyManager.getPasswordMinimumUpperCase(ADMIN_RECEIVER_COMPONENT));
+ assertEquals(TEST_VALUE,
+ mDevicePolicyManager.getPasswordMinimumLowerCase(ADMIN_RECEIVER_COMPONENT));
+ assertEquals(TEST_VALUE,
+ mDevicePolicyManager.getPasswordMinimumNonLetter(ADMIN_RECEIVER_COMPONENT));
+ assertEquals(TEST_VALUE,
+ mDevicePolicyManager.getPasswordMinimumSymbols(ADMIN_RECEIVER_COMPONENT));
+ }
+
+ public void testSettingConstraintsWithLowQualityThrowsOnRPlus() {
+ // On R and above quality should be set first.
+ mDevicePolicyManager.setPasswordQuality(
+ ADMIN_RECEIVER_COMPONENT, PASSWORD_QUALITY_SOMETHING);
+
+ assertThrows(IllegalStateException.class, () -> mDevicePolicyManager
+ .setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, TEST_VALUE));
+ assertThrows(IllegalStateException.class, () -> mDevicePolicyManager
+ .setPasswordMinimumNumeric(ADMIN_RECEIVER_COMPONENT, TEST_VALUE));
+ assertThrows(IllegalStateException.class, () -> mDevicePolicyManager
+ .setPasswordMinimumLetters(ADMIN_RECEIVER_COMPONENT, TEST_VALUE));
+ assertThrows(IllegalStateException.class, () -> mDevicePolicyManager
+ .setPasswordMinimumUpperCase(ADMIN_RECEIVER_COMPONENT, TEST_VALUE));
+ assertThrows(IllegalStateException.class, () -> mDevicePolicyManager
+ .setPasswordMinimumLowerCase(ADMIN_RECEIVER_COMPONENT, TEST_VALUE));
+ assertThrows(IllegalStateException.class, () -> mDevicePolicyManager
+ .setPasswordMinimumNonLetter(ADMIN_RECEIVER_COMPONENT, TEST_VALUE));
+ assertThrows(IllegalStateException.class, () -> mDevicePolicyManager
+ .setPasswordMinimumSymbols(ADMIN_RECEIVER_COMPONENT, TEST_VALUE));
+ }
+
+ public void testSettingConstraintsWithNumericQualityOnlyLengthAllowedOnRPlus() {
+ // On R and above quality should be set first.
+ mDevicePolicyManager.setPasswordQuality(
+ ADMIN_RECEIVER_COMPONENT, PASSWORD_QUALITY_NUMERIC);
+
+ // This should be allowed now.
+ mDevicePolicyManager.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, TEST_VALUE);
+
+ // These are still not allowed.
+ assertThrows(IllegalStateException.class, () -> mDevicePolicyManager
+ .setPasswordMinimumNumeric(ADMIN_RECEIVER_COMPONENT, TEST_VALUE));
+ assertThrows(IllegalStateException.class, () -> mDevicePolicyManager
+ .setPasswordMinimumLetters(ADMIN_RECEIVER_COMPONENT, TEST_VALUE));
+ assertThrows(IllegalStateException.class, () -> mDevicePolicyManager
+ .setPasswordMinimumUpperCase(ADMIN_RECEIVER_COMPONENT, TEST_VALUE));
+ assertThrows(IllegalStateException.class, () -> mDevicePolicyManager
+ .setPasswordMinimumLowerCase(ADMIN_RECEIVER_COMPONENT, TEST_VALUE));
+ assertThrows(IllegalStateException.class, () -> mDevicePolicyManager
+ .setPasswordMinimumNonLetter(ADMIN_RECEIVER_COMPONENT, TEST_VALUE));
+ assertThrows(IllegalStateException.class, () -> mDevicePolicyManager
+ .setPasswordMinimumSymbols(ADMIN_RECEIVER_COMPONENT, TEST_VALUE));
+ }
+
+ public void testSettingConstraintsWithComplexQualityAndResetWithLowerQuality() {
+ // On R and above when quality is lowered, irrelevant requirements are getting reset.
+ mDevicePolicyManager.setPasswordQuality(
+ ADMIN_RECEIVER_COMPONENT, PASSWORD_QUALITY_COMPLEX);
+ // These shouldn't throw anymore
+ mDevicePolicyManager.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, TEST_VALUE);
+ mDevicePolicyManager.setPasswordMinimumNumeric(ADMIN_RECEIVER_COMPONENT, TEST_VALUE);
+ mDevicePolicyManager.setPasswordMinimumLetters(ADMIN_RECEIVER_COMPONENT, TEST_VALUE);
+ mDevicePolicyManager.setPasswordMinimumUpperCase(ADMIN_RECEIVER_COMPONENT, TEST_VALUE);
+ mDevicePolicyManager.setPasswordMinimumLowerCase(ADMIN_RECEIVER_COMPONENT, TEST_VALUE);
+ mDevicePolicyManager.setPasswordMinimumNonLetter(ADMIN_RECEIVER_COMPONENT, TEST_VALUE);
+ mDevicePolicyManager.setPasswordMinimumSymbols(ADMIN_RECEIVER_COMPONENT, TEST_VALUE);
+
+ // Downgrade to NUMERIC, only length makes sense after that.
+ mDevicePolicyManager.setPasswordQuality(
+ ADMIN_RECEIVER_COMPONENT, PASSWORD_QUALITY_NUMERIC);
+
+ // Length shouldn't be reset
+ assertEquals(TEST_VALUE,
+ mDevicePolicyManager.getPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT));
+
+ // But other requirements should.
+ assertEquals(DEFAULT_NUMERIC,
+ mDevicePolicyManager.getPasswordMinimumNumeric(ADMIN_RECEIVER_COMPONENT));
+ assertEquals(DEFAULT_LETTERS,
+ mDevicePolicyManager.getPasswordMinimumLetters(ADMIN_RECEIVER_COMPONENT));
+ assertEquals(DEFAULT_UPPERCASE,
+ mDevicePolicyManager.getPasswordMinimumUpperCase(ADMIN_RECEIVER_COMPONENT));
+ assertEquals(DEFAULT_LOWERCASE,
+ mDevicePolicyManager.getPasswordMinimumLowerCase(ADMIN_RECEIVER_COMPONENT));
+ assertEquals(DEFAULT_NON_LETTER,
+ mDevicePolicyManager.getPasswordMinimumNonLetter(ADMIN_RECEIVER_COMPONENT));
+ assertEquals(DEFAULT_SYMBOLS,
+ mDevicePolicyManager.getPasswordMinimumSymbols(ADMIN_RECEIVER_COMPONENT));
+
+ // Downgrade to SOMETHING.
+ mDevicePolicyManager.setPasswordQuality(
+ ADMIN_RECEIVER_COMPONENT, PASSWORD_QUALITY_SOMETHING);
+
+ // Now length should also be reset.
+ assertEquals(DEFAULT_LENGTH,
+ mDevicePolicyManager.getPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT));
+
+ }
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PermissionsTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PermissionsTest.java
index 1c633c3..4dc7da0 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PermissionsTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PermissionsTest.java
@@ -15,6 +15,9 @@
*/
package com.android.cts.deviceandprofileowner;
+import static android.Manifest.permission.READ_CONTACTS;
+import static android.Manifest.permission.WRITE_CONTACTS;
+
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -47,7 +50,6 @@
= "com.android.cts.permissionapp";
private static final String SIMPLE_PRE_M_APP_PACKAGE_NAME =
"com.android.cts.launcherapps.simplepremapp";
- private static final String PERMISSION_NAME = "android.permission.READ_CONTACTS";
private static final String CUSTOM_PERM_A_NAME = "com.android.cts.permissionapp.permA";
private static final String CUSTOM_PERM_B_NAME = "com.android.cts.permissionapp.permB";
private static final String DEVELOPMENT_PERMISSION = "android.permission.INTERACT_ACROSS_USERS";
@@ -161,11 +163,19 @@
assertPermissionRequest(PackageManager.PERMISSION_GRANTED);
}
+ public void testAutoGrantMultiplePermissionsInGroup() throws Exception {
+ // set policy to autogrant
+ assertSetPermissionPolicy(DevicePolicyManager.PERMISSION_POLICY_AUTO_GRANT);
+ // both permissions should be granted
+ assertPermissionRequest(READ_CONTACTS, PackageManager.PERMISSION_GRANTED);
+ assertPermissionRequest(WRITE_CONTACTS, PackageManager.PERMISSION_GRANTED);
+ }
+
public void testPermissionGrantOfDisallowedPermissionWhileOtherPermIsGranted() throws Exception {
- assertSetPermissionGrantState(DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED,
- CUSTOM_PERM_A_NAME);
- assertSetPermissionGrantState(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED,
- CUSTOM_PERM_B_NAME);
+ assertSetPermissionGrantState(CUSTOM_PERM_A_NAME,
+ DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED);
+ assertSetPermissionGrantState(CUSTOM_PERM_B_NAME,
+ DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
/*
* CUSTOM_PERM_A_NAME and CUSTOM_PERM_B_NAME are in the same permission group and one is
@@ -174,7 +184,7 @@
* It should not be possible to get the permission that was denied via policy granted by
* requesting it.
*/
- assertPermissionRequest(PackageManager.PERMISSION_DENIED, null, CUSTOM_PERM_B_NAME);
+ assertPermissionRequest(CUSTOM_PERM_B_NAME, PackageManager.PERMISSION_DENIED);
}
@Suppress // Flakey.
@@ -202,14 +212,14 @@
public void testPermissionUpdate_setDeniedState() throws Exception {
assertEquals(mDevicePolicyManager.getPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
- PERMISSION_APP_PACKAGE_NAME, PERMISSION_NAME),
+ PERMISSION_APP_PACKAGE_NAME, READ_CONTACTS),
DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT);
assertSetPermissionGrantState(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
}
public void testPermissionUpdate_setAutoDeniedPolicy() throws Exception {
assertEquals(mDevicePolicyManager.getPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
- PERMISSION_APP_PACKAGE_NAME, PERMISSION_NAME),
+ PERMISSION_APP_PACKAGE_NAME, READ_CONTACTS),
DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT);
assertSetPermissionPolicy(DevicePolicyManager.PERMISSION_POLICY_AUTO_DENY);
assertPermissionRequest(PackageManager.PERMISSION_DENIED);
@@ -222,14 +232,14 @@
public void testPermissionUpdate_setGrantedState() throws Exception {
assertEquals(mDevicePolicyManager.getPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
- PERMISSION_APP_PACKAGE_NAME, PERMISSION_NAME),
+ PERMISSION_APP_PACKAGE_NAME, READ_CONTACTS),
DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT);
assertSetPermissionGrantState(DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED);
}
public void testPermissionUpdate_setAutoGrantedPolicy() throws Exception {
assertEquals(mDevicePolicyManager.getPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
- PERMISSION_APP_PACKAGE_NAME, PERMISSION_NAME),
+ PERMISSION_APP_PACKAGE_NAME, READ_CONTACTS),
DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT);
assertSetPermissionPolicy(DevicePolicyManager.PERMISSION_POLICY_AUTO_GRANT);
assertPermissionRequest(PackageManager.PERMISSION_GRANTED);
@@ -266,14 +276,35 @@
}
private void assertPermissionRequest(int expected) throws Exception {
- assertPermissionRequest(expected, null);
+ assertPermissionRequest(READ_CONTACTS, expected);
}
- private void assertPermissionRequest(int expected, String buttonResource) throws Exception {
- assertPermissionRequest(expected, buttonResource, PERMISSION_NAME);
+ private void assertPermissionRequest(String permission, int expected) throws Exception {
+ assertPermissionRequest(permission, expected, null);
}
- private void assertPermissionRequest(int expected, String buttonResource, String permission)
+ private void assertPermissionRequest(int expected, String buttonResource)
+ throws Exception {
+ assertPermissionRequest(READ_CONTACTS, expected, buttonResource);
+ }
+
+ private void assertSetPermissionGrantState(int value) throws Exception {
+ assertSetPermissionGrantState(READ_CONTACTS, value);
+ }
+
+ private void assertPermissionGrantState(int expected) throws Exception {
+ assertPermissionGrantState(READ_CONTACTS, expected);
+ }
+
+ private void assertCannotSetPermissionGrantStateAppPreM(int value) throws Exception {
+ assertCannotSetPermissionGrantStateAppPreM(READ_CONTACTS, value);
+ }
+
+ private void assertCanSetPermissionGrantStateAppPreM(int value) throws Exception {
+ assertCanSetPermissionGrantStateAppPreM(READ_CONTACTS, value);
+ }
+
+ private void assertPermissionRequest(String permission, int expected, String buttonResource)
throws Exception {
Intent launchIntent = new Intent();
launchIntent.setComponent(new ComponentName(PERMISSION_APP_PACKAGE_NAME,
@@ -288,13 +319,13 @@
PERMISSION_APP_PACKAGE_NAME));
}
- private void assertPermissionGrantState(int expected) throws Exception {
- assertEquals(expected, mPackageManager.checkPermission(PERMISSION_NAME,
+ private void assertPermissionGrantState(String permission, int expected) throws Exception {
+ assertEquals(expected, mPackageManager.checkPermission(permission,
PERMISSION_APP_PACKAGE_NAME));
Intent launchIntent = new Intent();
launchIntent.setComponent(new ComponentName(PERMISSION_APP_PACKAGE_NAME,
PERMISSIONS_ACTIVITY_NAME));
- launchIntent.putExtra(EXTRA_PERMISSION, PERMISSION_NAME);
+ launchIntent.putExtra(EXTRA_PERMISSION, permission);
launchIntent.setAction(ACTION_CHECK_HAS_PERMISSION);
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
mContext.startActivity(launchIntent);
@@ -308,10 +339,7 @@
value);
}
- private void assertSetPermissionGrantState(int value) throws Exception {
- assertSetPermissionGrantState(value, PERMISSION_NAME);
- }
- private void assertSetPermissionGrantState(int value, String permission) throws Exception {
+ private void assertSetPermissionGrantState(String permission, int value) throws Exception {
mDevicePolicyManager.setPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
PERMISSION_APP_PACKAGE_NAME, permission,
value);
@@ -331,33 +359,33 @@
PackageManager.PERMISSION_DENIED);
}
- private void assertCannotSetPermissionGrantStateAppPreM(int value) throws Exception {
+ private void assertCannotSetPermissionGrantStateAppPreM(String permission, int value) throws Exception {
assertFalse(mDevicePolicyManager.setPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
- SIMPLE_PRE_M_APP_PACKAGE_NAME, PERMISSION_NAME,
+ SIMPLE_PRE_M_APP_PACKAGE_NAME, permission,
value));
assertEquals(mDevicePolicyManager.getPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
- SIMPLE_PRE_M_APP_PACKAGE_NAME, PERMISSION_NAME),
+ SIMPLE_PRE_M_APP_PACKAGE_NAME, permission),
DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT);
// Install time permissions should always be granted
PackageInfo packageInfo = mContext.getPackageManager().getPackageInfo(
SIMPLE_PRE_M_APP_PACKAGE_NAME, 0);
assertEquals(PackageManager.PERMISSION_GRANTED,
- PermissionChecker.checkPermissionForDataDelivery(mContext, PERMISSION_NAME,
+ PermissionChecker.checkPermissionForDataDelivery(mContext, permission,
PermissionChecker.PID_UNKNOWN, packageInfo.applicationInfo.uid,
- SIMPLE_PRE_M_APP_PACKAGE_NAME));
+ SIMPLE_PRE_M_APP_PACKAGE_NAME, null /*message*/));
}
- private void assertCanSetPermissionGrantStateAppPreM(int value) throws Exception {
+ private void assertCanSetPermissionGrantStateAppPreM(String permission, int value) throws Exception {
assertTrue(mDevicePolicyManager.setPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
- SIMPLE_PRE_M_APP_PACKAGE_NAME, PERMISSION_NAME,
+ SIMPLE_PRE_M_APP_PACKAGE_NAME, permission,
value));
assertEquals(mDevicePolicyManager.getPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
- SIMPLE_PRE_M_APP_PACKAGE_NAME, PERMISSION_NAME),
+ SIMPLE_PRE_M_APP_PACKAGE_NAME, permission),
value);
// Install time permissions should always be granted
- assertEquals(mPackageManager.checkPermission(PERMISSION_NAME,
+ assertEquals(mPackageManager.checkPermission(permission,
SIMPLE_PRE_M_APP_PACKAGE_NAME),
PackageManager.PERMISSION_GRANTED);
@@ -367,8 +395,8 @@
// For pre-M apps the access to the data might be prevented via app-ops. Hence check that
// they are correctly set
boolean isGranted = (PermissionChecker.checkPermissionForDataDelivery(mContext,
- PERMISSION_NAME, PermissionChecker.PID_UNKNOWN,
- packageInfo.applicationInfo.uid, SIMPLE_PRE_M_APP_PACKAGE_NAME)
+ permission, PermissionChecker.PID_UNKNOWN,
+ packageInfo.applicationInfo.uid, SIMPLE_PRE_M_APP_PACKAGE_NAME, null /*message*/)
== PackageManager.PERMISSION_GRANTED);
switch (value) {
case DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED:
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ResetPasswordWithTokenTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ResetPasswordWithTokenTest.java
index 72f01aa..7ab2e9d 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ResetPasswordWithTokenTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ResetPasswordWithTokenTest.java
@@ -154,26 +154,6 @@
assertPasswordSucceeds("1234", caseDescription);
assertPasswordSucceeds("abcd", caseDescription); // can't change.
assertPasswordSucceeds("abcd1234", caseDescription);
-
- mDevicePolicyManager.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, 10);
- caseDescription = "minimum password length = 10";
- assertEquals(10, mDevicePolicyManager.getPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT));
- assertPasswordSufficiency(true); // length not checked for this quality
-
- // TODO(ascull): fix resetPassword() logic so these succeed
- assertPasswordFails("1234", caseDescription);
- assertPasswordFails("abcd", caseDescription);
- assertPasswordFails("abcd1234", caseDescription);
-
- mDevicePolicyManager.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, 4);
- caseDescription = "minimum password length = 4";
- assertEquals(4, mDevicePolicyManager.getPasswordMinimumLength(
- ADMIN_RECEIVER_COMPONENT));
- assertPasswordSufficiency(true);
-
- assertPasswordSucceeds("1234", caseDescription);
- assertPasswordSucceeds("abcd", caseDescription);
- assertPasswordSucceeds("abcd1234", caseDescription);
}
public void testPasswordQuality_numeric() {
@@ -527,7 +507,6 @@
// First remove device lock
mDevicePolicyManager.setPasswordQuality(ADMIN_RECEIVER_COMPONENT,
DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
- mDevicePolicyManager.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, 0);
assertTrue(mDevicePolicyManager.resetPasswordWithToken(ADMIN_RECEIVER_COMPONENT, null,
TOKEN0, 0));
@@ -557,7 +536,14 @@
}
private void resetComplexPasswordRestrictions() {
+ final int quality = mDevicePolicyManager.getPasswordQuality(ADMIN_RECEIVER_COMPONENT);
+ if (quality < PASSWORD_QUALITY_NUMERIC) {
+ return;
+ }
mDevicePolicyManager.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, 0);
+ if (quality < PASSWORD_QUALITY_COMPLEX) {
+ return;
+ }
mDevicePolicyManager.setPasswordMinimumUpperCase(ADMIN_RECEIVER_COMPONENT, 0);
mDevicePolicyManager.setPasswordMinimumLowerCase(ADMIN_RECEIVER_COMPONENT, 0);
mDevicePolicyManager.setPasswordMinimumLetters(ADMIN_RECEIVER_COMPONENT, 0);
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/DeviceIdentifiersTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/DeviceIdentifiersTest.java
index ea60582..3c53de9 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/DeviceIdentifiersTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/DeviceIdentifiersTest.java
@@ -16,6 +16,7 @@
package com.android.cts.deviceowner;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.Build;
import android.telephony.TelephonyManager;
@@ -72,39 +73,66 @@
// SecurityException when querying for device identifiers.
TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService(
Context.TELEPHONY_SERVICE);
+ // Allow the APIs to also return null if the telephony feature is not supported.
+ boolean hasTelephonyFeature =
+ mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
try {
- telephonyManager.getDeviceId();
- fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "getDeviceId"));
+ String deviceId = telephonyManager.getDeviceId();
+ if (hasTelephonyFeature) {
+ fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "getDeviceId"));
+ } else {
+ assertEquals(null, deviceId);
+ }
} catch (SecurityException expected) {
}
try {
- telephonyManager.getImei();
- fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "getImei"));
+ String imei = telephonyManager.getImei();
+ if (hasTelephonyFeature) {
+ fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "getImei"));
+ } else {
+ assertEquals(null, imei);
+ }
} catch (SecurityException expected) {
}
try {
- telephonyManager.getMeid();
- fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "getMeid"));
+ String meid = telephonyManager.getMeid();
+ if (hasTelephonyFeature) {
+ fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "getMeid"));
+ } else {
+ assertEquals(null, meid);
+ }
} catch (SecurityException expected) {
}
try {
- telephonyManager.getSubscriberId();
- fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "getSubscriberId"));
+ String subscriberId = telephonyManager.getSubscriberId();
+ if (hasTelephonyFeature) {
+ fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "getSubscriberId"));
+ } else {
+ assertEquals(null, subscriberId);
+ }
} catch (SecurityException expected) {
}
try {
- telephonyManager.getSimSerialNumber();
- fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "getSimSerialNumber"));
+ String simSerialNumber = telephonyManager.getSimSerialNumber();
+ if (hasTelephonyFeature) {
+ fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "getSimSerialNumber"));
+ } else {
+ assertEquals(null, simSerialNumber);
+ }
} catch (SecurityException expected) {
}
try {
- Build.getSerial();
- fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "Build#getSerial"));
+ String serial = Build.getSerial();
+ if (hasTelephonyFeature) {
+ fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "Build#getSerial"));
+ } else {
+ assertEquals(null, serial);
+ }
} catch (SecurityException expected) {
}
}
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/DevicePolicyLoggingTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/DevicePolicyLoggingTest.java
index 9aad7b5..3d11999 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/DevicePolicyLoggingTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/DevicePolicyLoggingTest.java
@@ -16,6 +16,13 @@
package com.android.cts.deviceowner;
+import static android.provider.Settings.Global.AUTO_TIME;
+import static android.provider.Settings.Global.AUTO_TIME_ZONE;
+import static android.provider.Settings.Global.DATA_ROAMING;
+import static android.provider.Settings.Global.USB_MASS_STORAGE_ENABLED;
+
+import android.provider.Settings;
+
/**
* Invocations of {@link android.app.admin.DevicePolicyManager} methods which are either not CTS
* tested or the CTS tests call too many other methods. Used to verify that the relevant metrics
@@ -31,4 +38,29 @@
mDevicePolicyManager.setStatusBarDisabled(getWho(), true);
mDevicePolicyManager.setStatusBarDisabled(getWho(), false);
}
+
+ public void testSetGlobalSettingLogged() {
+ final String autoTimeOriginalValue =
+ Settings.Global.getString(mContext.getContentResolver(), AUTO_TIME);
+ final String autoTimeZoneOriginalValue =
+ Settings.Global.getString(mContext.getContentResolver(), AUTO_TIME_ZONE);
+ final String dataRoamingOriginalValue =
+ Settings.Global.getString(mContext.getContentResolver(), DATA_ROAMING);
+ final String usbMassStorageOriginalValue =
+ Settings.Global.getString(mContext.getContentResolver(), USB_MASS_STORAGE_ENABLED);
+
+ try {
+ mDevicePolicyManager.setGlobalSetting(getWho(), AUTO_TIME, "1");
+ mDevicePolicyManager.setGlobalSetting(getWho(), AUTO_TIME_ZONE, "1");
+ mDevicePolicyManager.setGlobalSetting(getWho(), DATA_ROAMING, "1");
+ mDevicePolicyManager.setGlobalSetting(getWho(), USB_MASS_STORAGE_ENABLED, "1");
+ } finally {
+ mDevicePolicyManager.setGlobalSetting(getWho(), AUTO_TIME, autoTimeOriginalValue);
+ mDevicePolicyManager.setGlobalSetting(
+ getWho(), AUTO_TIME_ZONE, autoTimeZoneOriginalValue);
+ mDevicePolicyManager.setGlobalSetting(getWho(), DATA_ROAMING, dataRoamingOriginalValue);
+ mDevicePolicyManager.setGlobalSetting(
+ getWho(), USB_MASS_STORAGE_ENABLED, usbMassStorageOriginalValue);
+ }
+ }
}
diff --git a/hostsidetests/devicepolicy/app/HasLauncherActivityApp/Android.bp b/hostsidetests/devicepolicy/app/HasLauncherActivityApp/Android.bp
index f2c0649..8dc698e 100644
--- a/hostsidetests/devicepolicy/app/HasLauncherActivityApp/Android.bp
+++ b/hostsidetests/devicepolicy/app/HasLauncherActivityApp/Android.bp
@@ -25,6 +25,7 @@
srcs: ["src/**/*.java"],
// tag this module as a cts test artifact
test_suites: [
+ "arcts",
"cts",
"vts",
"general-tests",
@@ -44,6 +45,7 @@
srcs: ["src/**/*.java"],
// tag this module as a cts test artifact
test_suites: [
+ "arcts",
"cts",
"vts",
"general-tests",
@@ -64,6 +66,7 @@
srcs: ["src/**/*.java"],
// tag this module as a cts test artifact
test_suites: [
+ "arcts",
"cts",
"vts",
"general-tests",
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileCalendarTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileCalendarTest.java
index 083848d..5ced0e6 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileCalendarTest.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileCalendarTest.java
@@ -102,9 +102,6 @@
private static final long TEST_VIEW_EVENT_END = 10000;
private static final boolean TEST_VIEW_EVENT_ALL_DAY = false;
private static final int TEST_VIEW_EVENT_FLAG = Intent.FLAG_ACTIVITY_NEW_TASK;
- private static final int TIMEOUT_SEC = 10;
- private static final String ID_TEXTVIEW =
- "com.android.cts.managedprofile:id/view_event_text";
private ContentResolver mResolver;
private DevicePolicyManager mDevicePolicyManager;
@@ -335,29 +332,6 @@
assertThat(cursor.getCount()).isEqualTo(1);
}
- // This test should be run when the test package is whitelisted.
- public void testViewEventCrossProfile_intentReceivedWhenWhitelisted() throws Exception {
- requireRunningOnPrimaryProfile();
-
- // Get UiDevice and start view event activity.
- final UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
- device.wakeUp();
-
- assertThat(CalendarContract.startViewCalendarEventInManagedProfile(mContext,
- TEST_VIEW_EVENT_ID, TEST_VIEW_EVENT_START, TEST_VIEW_EVENT_END,
- TEST_VIEW_EVENT_ALL_DAY, TEST_VIEW_EVENT_FLAG)).isTrue();
- final String textviewString = getViewEventCrossProfileString(TEST_VIEW_EVENT_ID,
- TEST_VIEW_EVENT_START, TEST_VIEW_EVENT_END, TEST_VIEW_EVENT_ALL_DAY,
- TEST_VIEW_EVENT_FLAG);
-
- // Look for the text view to verify that activity is started in work profile.
- UiObject2 textView = device.wait(
- Until.findObject(By.res(ID_TEXTVIEW)),
- TIMEOUT_SEC);
- assertThat(textView).isNotNull();
- assertThat(textView.getText()).isEqualTo(textviewString);
- }
-
// This test should be run when the test package is whitelisted and cross-profile calendar
// is enabled in settings.
public void testPrimaryProfile_getExceptionWhenQueryNonWhitelistedColumns() {
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/DeviceIdentifiersTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/DeviceIdentifiersTest.java
index 7c26fad..14e7b0c 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/DeviceIdentifiersTest.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/DeviceIdentifiersTest.java
@@ -16,6 +16,7 @@
package com.android.cts.managedprofile;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.Build;
import android.telephony.TelephonyManager;
@@ -72,39 +73,66 @@
// SecurityException when querying for device identifiers.
TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService(
Context.TELEPHONY_SERVICE);
+ // Allow the APIs to also return null if the telephony feature is not supported.
+ boolean hasTelephonyFeature =
+ mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
try {
- telephonyManager.getDeviceId();
- fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "getDeviceId"));
+ String deviceId = telephonyManager.getDeviceId();
+ if (hasTelephonyFeature) {
+ fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "getDeviceId"));
+ } else {
+ assertEquals(null, deviceId);
+ }
} catch (SecurityException expected) {
}
try {
- telephonyManager.getImei();
- fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "getImei"));
+ String imei = telephonyManager.getImei();
+ if (hasTelephonyFeature) {
+ fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "getImei"));
+ } else {
+ assertEquals(null, imei);
+ }
} catch (SecurityException expected) {
}
try {
- telephonyManager.getMeid();
- fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "getMeid"));
+ String meid = telephonyManager.getMeid();
+ if (hasTelephonyFeature) {
+ fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "getMeid"));
+ } else {
+ assertEquals(null, meid);
+ }
} catch (SecurityException expected) {
}
try {
- telephonyManager.getSubscriberId();
- fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "getSubscriberId"));
+ String subscriberId = telephonyManager.getSubscriberId();
+ if (hasTelephonyFeature) {
+ fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "getSubscriberId"));
+ } else {
+ assertEquals(null, subscriberId);
+ }
} catch (SecurityException expected) {
}
try {
- telephonyManager.getSimSerialNumber();
- fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "getSimSerialNumber"));
+ String simSerialNumber = telephonyManager.getSimSerialNumber();
+ if (hasTelephonyFeature) {
+ fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "getSimSerialNumber"));
+ } else {
+ assertEquals(null, simSerialNumber);
+ }
} catch (SecurityException expected) {
}
try {
- Build.getSerial();
- fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "Build#getSerial"));
+ String serial = Build.getSerial();
+ if (hasTelephonyFeature) {
+ fail(String.format(NO_SECURITY_EXCEPTION_ERROR_MESSAGE, "Build#getSerial"));
+ } else {
+ assertEquals(null, serial);
+ }
} catch (SecurityException expected) {
}
}
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ParentProfileTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ParentProfileTest.java
index 17cea9b..6cce328 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ParentProfileTest.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ParentProfileTest.java
@@ -78,6 +78,7 @@
.add("getRequiredStrongAuthTimeout")
.add("setRequiredStrongAuthTimeout")
.add("isDeviceIdAttestationSupported")
+ .add("isUniqueDeviceAttestationSupported")
.build();
private static final String LOG_TAG = "ParentProfileTest";
diff --git a/hostsidetests/devicepolicy/app/SimpleApp/AndroidManifest.xml b/hostsidetests/devicepolicy/app/SimpleApp/AndroidManifest.xml
index 9ae0a86..e498048 100644
--- a/hostsidetests/devicepolicy/app/SimpleApp/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/SimpleApp/AndroidManifest.xml
@@ -34,7 +34,11 @@
<activity android:name=".NonLauncherActivity">
android:exported="true">
</activity>
- <activity android:name=".SimpleActivityStartService" android:exported="true" />
+ <activity android:name=".SimpleActivityStartService"
+ android:turnScreenOn="true"
+ android:excludeFromRecents="true"
+ android:exported="true" />
+ <activity android:name=".SimpleActivityStartFgService" android:exported="true" />
<activity android:name=".SimpleActivityImmediateExit" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -47,10 +51,13 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
+
<service android:name=".SimpleService" android:exported="true">
</service>
<service android:name=".SimpleService2" android:exported="true" android:process=":other">
</service>
+ <service android:name=".SimpleService3" android:exported="true" />
+
<receiver android:name=".SimpleReceiverStartService" android:exported="true">
</receiver>
<receiver android:name=".SimpleReceiver" android:exported="true">
diff --git a/hostsidetests/devicepolicy/app/SimpleApp/OWNERS b/hostsidetests/devicepolicy/app/SimpleApp/OWNERS
new file mode 100644
index 0000000..d9a2060
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/SimpleApp/OWNERS
@@ -0,0 +1,2 @@
+ctate@google.com
+hackbod@google.com
diff --git a/hostsidetests/devicepolicy/app/SimpleApp/src/com/android/cts/launcherapps/simpleapp/SimpleActivityStartFgService.java b/hostsidetests/devicepolicy/app/SimpleApp/src/com/android/cts/launcherapps/simpleapp/SimpleActivityStartFgService.java
new file mode 100644
index 0000000..b17b802
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/SimpleApp/src/com/android/cts/launcherapps/simpleapp/SimpleActivityStartFgService.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.launcherapps.simpleapp;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+/**
+ * Test being able to start a service (with no background check restrictions) as soon as
+ * an activity is created.
+ */
+public class SimpleActivityStartFgService extends Activity {
+ private static final String TAG = "SimpleActivityStartFgService";
+
+ static final String ACTION_START_THEN_FG =
+ "com.android.cts.launcherapps.simpleapp.SimpleActivityStartFgService.START_THEN_FG";
+ public static String ACTION_FINISH_EVERYTHING =
+ "com.android.cts.launcherapps.simpleapp.SimpleActivityStartFgService.FINISH_ALL";
+ public static String ACTION_SIMPLE_ACTIVITY_START_FG_SERVICE_RESULT =
+ "com.android.cts.launcherapps.simpleapp.SimpleActivityStartFgService.NOW_FOREGROUND";
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ final String action = getIntent().getAction();
+ Log.i(TAG, "onCreate() with intent " + action);
+ if (ACTION_START_THEN_FG.equals(action)) {
+ if (!attemptStartService()) {
+ // If we couldn't run the test properly, finish immediately
+ Log.i(TAG, " Couldn't start service, finishing immediately!");
+ finish();
+ }
+ } else if (ACTION_FINISH_EVERYTHING.equals(action)) {
+ Log.i(TAG, " *** told to stop service & finish");
+ Intent serviceIntent = getIntent().getParcelableExtra("service");
+ stopService(serviceIntent);
+ finish();
+ }
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ final String action = intent.getAction();
+ if (ACTION_FINISH_EVERYTHING.equals(action)) {
+ Intent serviceIntent = getIntent().getParcelableExtra("service");
+ stopService(serviceIntent);
+ finish();
+ }
+ }
+
+ private boolean attemptStartService() {
+ Intent reply = new Intent(ACTION_SIMPLE_ACTIVITY_START_FG_SERVICE_RESULT);
+ reply.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ Intent serviceIntent = getIntent().getParcelableExtra("service");
+
+ try {
+ final ComponentName svcName = startService(serviceIntent);
+ if (svcName != null) {
+ // Successful start -> the service will send the broadcast once it's
+ // transitioned to the foreground
+ return true;
+ }
+ } catch (Exception e) {
+ // fall through
+ }
+
+ // Failure of some sort: the service isn't going to run as expected,
+ // so report the failure here before exiting.
+ reply.putExtra("result", Activity.RESULT_CANCELED);
+ sendBroadcast(reply);
+ return false;
+ }
+}
diff --git a/hostsidetests/devicepolicy/app/SimpleApp/src/com/android/cts/launcherapps/simpleapp/SimpleActivityStartService.java b/hostsidetests/devicepolicy/app/SimpleApp/src/com/android/cts/launcherapps/simpleapp/SimpleActivityStartService.java
index cda6b6a..0813c24 100644
--- a/hostsidetests/devicepolicy/app/SimpleApp/src/com/android/cts/launcherapps/simpleapp/SimpleActivityStartService.java
+++ b/hostsidetests/devicepolicy/app/SimpleApp/src/com/android/cts/launcherapps/simpleapp/SimpleActivityStartService.java
@@ -17,8 +17,12 @@
package com.android.cts.launcherapps.simpleapp;
import android.app.Activity;
+import android.app.KeyguardManager;
+import android.app.KeyguardManager.KeyguardDismissCallback;
import android.content.Intent;
import android.os.Bundle;
+import android.os.Handler;
+import android.util.Log;
/**
* Test being able to start a service (with no background check restrictions) as soon as
@@ -31,18 +35,34 @@
"com.android.cts.launcherapps.simpleapp.SimpleActivityStartService.RESULT";
@Override
- public void onCreate(Bundle icicle) {
+ protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
- attemptStartService();
- finish();
- }
+ getSystemService(KeyguardManager.class).requestDismissKeyguard(this,
+ new KeyguardDismissCallback() {
+ @Override
+ public void onDismissCancelled() {
+ Log.i(TAG, "onDismissCancelled");
+ }
- @Override
- public void onStart() {
- super.onStart();
+ @Override
+ public void onDismissError() {
+ Log.i(TAG, "onDismissError");
+ }
+
+ @Override
+ public void onDismissSucceeded() {
+ Log.i(TAG, "onDismissSucceeded");
+ }
+ });
+ // No matter if the dismiss was successful or not, continue the test after 2000ms
+ (new Handler()).postDelayed(()-> {
+ attemptStartService();
+ finish();
+ }, 2000);
}
void attemptStartService() {
+ Log.i(TAG, "attemptStartService");
Intent reply = new Intent(ACTION_SIMPLE_ACTIVITY_START_SERVICE_RESULT);
reply.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
Intent serviceIntent = getIntent().getParcelableExtra("service");
diff --git a/hostsidetests/devicepolicy/app/SimpleApp/src/com/android/cts/launcherapps/simpleapp/SimpleService3.java b/hostsidetests/devicepolicy/app/SimpleApp/src/com/android/cts/launcherapps/simpleapp/SimpleService3.java
new file mode 100644
index 0000000..3820825
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/SimpleApp/src/com/android/cts/launcherapps/simpleapp/SimpleService3.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.launcherapps.simpleapp;
+
+import android.app.Activity;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Process;
+import android.os.RemoteException;
+import android.util.Log;
+
+public class SimpleService3 extends Service {
+ private final static String TAG = SimpleService3.class.getSimpleName();
+
+ static final String ACTION_START_THEN_FG = "com.android.test.action.START_THEN_FG";
+ static final String ACTION_STOP_FOREGROUND = "com.android.test.action.STOP_FOREGROUND";
+ static final String ACTION_STOP_SERVICE = "com.android.test.action.STOP";
+
+ public static String ACTION_SIMPLE_ACTIVITY_START_FG_SERVICE_RESULT =
+ SimpleActivityStartFgService.ACTION_SIMPLE_ACTIVITY_START_FG_SERVICE_RESULT;
+
+ static final String CHANNEL_NAME = "SimpleService3";
+
+ final Binder mBinder = new Binder() {
+ @Override
+ protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+ throws RemoteException {
+ switch (code) {
+ case FIRST_CALL_TRANSACTION:
+ Process.killProcess(Process.myPid());
+ return true;
+ }
+ return super.onTransact(code, data, reply, flags);
+ }
+ };
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ Log.i(TAG, "onCreate called");
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ Log.i(TAG, "Starting: " + intent);
+ if (ACTION_START_THEN_FG.equals(intent.getAction())) {
+ // Set the info for the views that show in the notification panel.
+ NotificationManager nm = getSystemService(NotificationManager.class);
+ if (nm.getNotificationChannel(CHANNEL_NAME) == null) {
+ nm.createNotificationChannel(new NotificationChannel(CHANNEL_NAME, CHANNEL_NAME,
+ NotificationManager.IMPORTANCE_DEFAULT));
+ }
+ Notification notification = new Notification.Builder(this, CHANNEL_NAME)
+ .setSmallIcon(android.R.drawable.ic_popup_reminder) // the status icon
+ .setWhen(System.currentTimeMillis()) // the time stamp
+ .setContentTitle("This is a test notification") // the label
+ .setContentText("This is a test notification") // the contents of the entry
+ .build();
+ startForeground(100, notification);
+
+ // And send the broadcast reporting success
+ Intent reply = new Intent(ACTION_SIMPLE_ACTIVITY_START_FG_SERVICE_RESULT);
+ reply.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ reply.putExtra("result", Activity.RESULT_FIRST_USER);
+ sendBroadcast(reply);
+ } else if (ACTION_STOP_FOREGROUND.equals(intent.getAction())) {
+ stopForeground(true);
+ } else if (ACTION_STOP_SERVICE.equals(intent.getAction())) {
+ stopSelf();
+ }
+ return super.onStartCommand(intent, flags, startId);
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ Log.i(TAG, "onBind called");
+ return mBinder;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ Log.i(TAG, "onDestroy called");
+ }
+}
diff --git a/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/TransferDeviceOwnerIncomingTest.java b/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/TransferDeviceOwnerIncomingTest.java
index f39dbbe..7fecb35 100644
--- a/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/TransferDeviceOwnerIncomingTest.java
+++ b/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/TransferDeviceOwnerIncomingTest.java
@@ -20,6 +20,7 @@
import static org.junit.Assert.assertEquals;
import static org.testng.Assert.assertThrows;
+import android.app.admin.DevicePolicyManager;
import android.app.admin.SystemUpdatePolicy;
import androidx.test.filters.SmallTest;
@@ -37,6 +38,9 @@
assertTrue(mDevicePolicyManager.getCameraDisabled(mIncomingComponentName));
assertEquals(Collections.singletonList("test.package"),
mDevicePolicyManager.getKeepUninstalledPackages(mIncomingComponentName));
+ assertEquals(
+ DevicePolicyManager.PASSWORD_QUALITY_NUMERIC,
+ mDevicePolicyManager.getPasswordQuality(mIncomingComponentName));
assertEquals(123, mDevicePolicyManager.getPasswordMinimumLength(mIncomingComponentName));
assertSystemPoliciesEqual(SystemUpdatePolicy.createWindowedInstallPolicy(123, 456),
mDevicePolicyManager.getSystemUpdatePolicy());
diff --git a/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/TransferProfileOwnerIncomingTest.java b/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/TransferProfileOwnerIncomingTest.java
index 4dc9a5b..92c63c0 100644
--- a/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/TransferProfileOwnerIncomingTest.java
+++ b/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/TransferProfileOwnerIncomingTest.java
@@ -36,9 +36,13 @@
assertTrue(mDevicePolicyManager.getCameraDisabled(mIncomingComponentName));
assertTrue(mDevicePolicyManager.getCrossProfileCallerIdDisabled(mIncomingComponentName));
assertEquals(
+ DevicePolicyManager.PASSWORD_QUALITY_NUMERIC,
+ mDevicePolicyManager.getPasswordQuality(mIncomingComponentName));
+ assertEquals(
passwordLength,
mDevicePolicyManager.getPasswordMinimumLength(mIncomingComponentName));
+
DevicePolicyManager targetParentProfileInstance =
mDevicePolicyManager.getParentProfileInstance(mIncomingComponentName);
if (mHasSecureLockScreen) {
diff --git a/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/TransferDeviceOwnerOutgoingTest.java b/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/TransferDeviceOwnerOutgoingTest.java
index b42b2bd..365c154 100644
--- a/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/TransferDeviceOwnerOutgoingTest.java
+++ b/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/TransferDeviceOwnerOutgoingTest.java
@@ -43,6 +43,8 @@
public void testTransferWithPoliciesOutgoing() throws Throwable {
int passwordLength = 123;
mDevicePolicyManager.setCameraDisabled(mOutgoingComponentName, true);
+ mDevicePolicyManager.setPasswordQuality(
+ mOutgoingComponentName, DevicePolicyManager.PASSWORD_QUALITY_NUMERIC);
mDevicePolicyManager.setPasswordMinimumLength(mOutgoingComponentName, passwordLength);
mDevicePolicyManager.setKeepUninstalledPackages(mOutgoingComponentName,
Collections.singletonList("test.package"));
diff --git a/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/TransferProfileOwnerOutgoingTest.java b/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/TransferProfileOwnerOutgoingTest.java
index 157e840..cebeaf1 100644
--- a/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/TransferProfileOwnerOutgoingTest.java
+++ b/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/TransferProfileOwnerOutgoingTest.java
@@ -43,6 +43,8 @@
DevicePolicyManager parentDevicePolicyManager =
mDevicePolicyManager.getParentProfileInstance(mOutgoingComponentName);
mDevicePolicyManager.setCameraDisabled(mOutgoingComponentName, true);
+ mDevicePolicyManager.setPasswordQuality(
+ mOutgoingComponentName, DevicePolicyManager.PASSWORD_QUALITY_NUMERIC);
mDevicePolicyManager.setPasswordMinimumLength(mOutgoingComponentName, passwordLength);
mDevicePolicyManager.setCrossProfileCallerIdDisabled(mOutgoingComponentName, true);
parentDevicePolicyManager.setPasswordExpirationTimeout(
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/AccountCheckHostSideTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/AccountCheckHostSideTest.java
index a79ae11..c279b28 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/AccountCheckHostSideTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/AccountCheckHostSideTest.java
@@ -16,6 +16,8 @@
package com.android.cts.devicepolicy;
+import android.platform.test.annotations.LargeTest;
+
import com.android.tradefed.log.LogUtil.CLog;
import junit.framework.AssertionFailedError;
@@ -147,6 +149,7 @@
return Integer.parseInt(count) > 0;
}
+ @LargeTest
public void testAccountCheck() throws Exception {
if (!mHasFeature) {
return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/AdbProvisioningTests.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/AdbProvisioningTests.java
index 218d6b7..5100aca 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/AdbProvisioningTests.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/AdbProvisioningTests.java
@@ -19,6 +19,7 @@
import static com.android.cts.devicepolicy.DeviceAndProfileOwnerTest.DEVICE_ADMIN_APK;
import static com.android.cts.devicepolicy.DeviceAndProfileOwnerTest.DEVICE_ADMIN_PKG;
import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.assertMetricsLogged;
+import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.isStatsdEnabled;
import com.android.cts.devicepolicy.metrics.DevicePolicyEventWrapper;
import com.android.tradefed.device.DeviceNotAvailableException;
@@ -47,7 +48,7 @@
}
public void testAdbDeviceOwnerLogged() throws Exception {
- if (!mHasFeature) {
+ if (!mHasFeature || !isStatsdEnabled(getDevice())) {
return;
}
assertMetricsLogged(getDevice(), () -> {
@@ -61,7 +62,7 @@
}
public void testAdbProfileOwnerLogged() throws Exception {
- if (!mHasFeature) {
+ if (!mHasFeature || !isStatsdEnabled(getDevice())) {
return;
}
assertMetricsLogged(getDevice(), () -> {
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
index 17bbf06..a1908ef 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
@@ -47,6 +47,8 @@
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import javax.annotation.Nullable;
@@ -643,21 +645,15 @@
protected int getUserSerialNumber(int userId) throws DeviceNotAvailableException{
// TODO: Move this logic to ITestDevice.
- // dumpsys user return lines like "UserInfo{0:Owner:13} serialNo=0"
- String commandOutput = getDevice().executeShellCommand("dumpsys user");
- String[] tokens = commandOutput.split("\\n");
- for (String token : tokens) {
- token = token.trim();
- if (token.contains("UserInfo{" + userId + ":")) {
- String[] split = token.split("serialNo=");
- assertTrue(split.length == 2);
- int serialNumber = Integer.parseInt(split[1]);
- CLog.d("Serial number of user " + userId + ": "
- + serialNumber);
- return serialNumber;
- }
+ // dumpsys user output contains lines like "UserInfo{0:Owner:13} serialNo=0 isPrimary=true"
+ final Pattern pattern =
+ Pattern.compile("UserInfo\\{" + userId + ":[^\\n]*\\sserialNo=(\\d+)\\s");
+ final String commandOutput = getDevice().executeShellCommand("dumpsys user");
+ final Matcher matcher = pattern.matcher(commandOutput);
+ if (matcher.find()) {
+ return Integer.parseInt(matcher.group(1));
}
- fail("Couldn't find user " + userId);
+ fail("Couldn't find serial number for user " + userId);
return -1;
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseManagedProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseManagedProfileTest.java
new file mode 100644
index 0000000..56d5800
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseManagedProfileTest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.devicepolicy;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.log.LogUtil;
+
+public abstract class BaseManagedProfileTest extends BaseDevicePolicyTest {
+ protected static final String MANAGED_PROFILE_PKG = "com.android.cts.managedprofile";
+ protected static final String INTENT_SENDER_PKG = "com.android.cts.intent.sender";
+ protected static final String INTENT_RECEIVER_PKG = "com.android.cts.intent.receiver";
+ protected static final String ADMIN_RECEIVER_TEST_CLASS =
+ MANAGED_PROFILE_PKG + ".BaseManagedProfileTest$BasicAdminReceiver";
+ protected static final String INTENT_SENDER_APK = "CtsIntentSenderApp.apk";
+ protected static final String INTENT_RECEIVER_APK = "CtsIntentReceiverApp.apk";
+ protected static final String SIMPLE_APP_APK = "CtsSimpleApp.apk";
+ private static final String MANAGED_PROFILE_APK = "CtsManagedProfileApp.apk";
+ private static final String NOTIFICATION_PKG =
+ "com.android.cts.managedprofiletests.notificationsender";
+ //The maximum time to wait for user to be unlocked.
+ private static final long USER_UNLOCK_TIMEOUT_NANO = 30_000_000_000L;
+ private static final String USER_STATE_UNLOCKED = "RUNNING_UNLOCKED";
+ protected int mParentUserId;
+ // ID of the profile we'll create. This will always be a profile of the parent.
+ protected int mProfileUserId;
+ protected boolean mHasNfcFeature;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ // We need multi user to be supported in order to create a profile of the user owner.
+ mHasFeature = mHasFeature && hasDeviceFeature("android.software.managed_users");
+ mHasNfcFeature = hasDeviceFeature("android.hardware.nfc")
+ && hasDeviceFeature("android.sofware.nfc.beam");
+
+ if (mHasFeature) {
+ removeTestUsers();
+ mParentUserId = mPrimaryUserId;
+ mProfileUserId = createManagedProfile(mParentUserId);
+ startUser(mProfileUserId);
+
+ // Install the APK on both primary and profile user in one single transaction.
+ // If they were installed separately, the second installation would become an app
+ // update and result in the current running test process being killed.
+ installAppAsUser(MANAGED_PROFILE_APK, USER_ALL);
+ setProfileOwnerOrFail(MANAGED_PROFILE_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS,
+ mProfileUserId);
+ waitForUserUnlock();
+ }
+ }
+
+ private void waitForUserUnlock() throws Exception {
+ final String command = String.format("am get-started-user-state %d", mProfileUserId);
+ final long deadline = System.nanoTime() + USER_UNLOCK_TIMEOUT_NANO;
+ while (System.nanoTime() <= deadline) {
+ if (getDevice().executeShellCommand(command).startsWith(USER_STATE_UNLOCKED)) {
+ return;
+ }
+ Thread.sleep(100);
+ }
+ fail("Profile user is not unlocked.");
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ if (mHasFeature) {
+ removeUser(mProfileUserId);
+ getDevice().uninstallPackage(MANAGED_PROFILE_PKG);
+ getDevice().uninstallPackage(INTENT_SENDER_PKG);
+ getDevice().uninstallPackage(INTENT_RECEIVER_PKG);
+ getDevice().uninstallPackage(NOTIFICATION_PKG);
+ }
+ super.tearDown();
+ }
+
+ protected void disableActivityForUser(String activityName, int userId)
+ throws DeviceNotAvailableException {
+ String command = "am start -W --user " + userId
+ + " --es extra-package " + MANAGED_PROFILE_PKG
+ + " --es extra-class-name " + MANAGED_PROFILE_PKG + "." + activityName
+ + " " + MANAGED_PROFILE_PKG + "/.ComponentDisablingActivity ";
+ LogUtil.CLog.d("Output for command " + command + ": "
+ + getDevice().executeShellCommand(command));
+ }
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsHostSideTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsHostSideTest.java
index b59222e..f30ee5e 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsHostSideTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsHostSideTest.java
@@ -1,7 +1,10 @@
package com.android.cts.devicepolicy;
import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.assertMetricsLogged;
+import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.isStatsdEnabled;
+import android.platform.test.annotations.FlakyTest;
+import android.platform.test.annotations.LargeTest;
import android.stats.devicepolicy.EventId;
import com.android.cts.devicepolicy.metrics.DevicePolicyEventWrapper;
@@ -10,6 +13,7 @@
import java.io.FileNotFoundException;
import java.util.Collections;
import java.util.Map;
+
import javax.annotation.Nullable;
/**
@@ -57,10 +61,14 @@
installAppAsUser(SIMPLE_APP_APK, userId);
}
+ @FlakyTest
+ @LargeTest
public void testPrimaryUserToPrimaryUser() throws Exception {
verifyCrossProfileAppsApi(mPrimaryUserId, mPrimaryUserId, NON_TARGET_USER_TEST_CLASS);
}
+ @FlakyTest
+ @LargeTest
public void testPrimaryUserToManagedProfile() throws Exception {
if (!mHasManagedUserFeature) {
return;
@@ -68,6 +76,7 @@
verifyCrossProfileAppsApi(mPrimaryUserId, mProfileId, TARGET_USER_TEST_CLASS);
}
+ @LargeTest
public void testManagedProfileToPrimaryUser() throws Exception {
if (!mHasManagedUserFeature) {
return;
@@ -75,6 +84,7 @@
verifyCrossProfileAppsApi(mProfileId, mPrimaryUserId, TARGET_USER_TEST_CLASS);
}
+ @LargeTest
public void testStartActivity() throws Exception {
if (!mHasManagedUserFeature) {
return;
@@ -82,6 +92,7 @@
verifyCrossProfileAppsApi(mProfileId, mPrimaryUserId, START_ACTIVITY_TEST_CLASS);
}
+ @LargeTest
public void testPrimaryUserToSecondaryUser() throws Exception {
if (!mCanTestMultiUser) {
return;
@@ -89,6 +100,7 @@
verifyCrossProfileAppsApi(mPrimaryUserId, mSecondaryUserId, NON_TARGET_USER_TEST_CLASS);
}
+ @LargeTest
public void testSecondaryUserToManagedProfile() throws Exception {
if (!mCanTestMultiUser || !mHasManagedUserFeature) {
return;
@@ -97,6 +109,7 @@
}
+ @LargeTest
public void testManagedProfileToSecondaryUser() throws Exception {
if (!mCanTestMultiUser || !mHasManagedUserFeature) {
return;
@@ -104,8 +117,9 @@
verifyCrossProfileAppsApi(mProfileId, mSecondaryUserId, NON_TARGET_USER_TEST_CLASS);
}
+ @LargeTest
public void testStartMainActivity_logged() throws Exception {
- if (!mHasManagedUserFeature) {
+ if (!mHasManagedUserFeature || !isStatsdEnabled(getDevice())) {
return;
}
assertMetricsLogged(
@@ -123,8 +137,9 @@
.build());
}
+ @LargeTest
public void testGetTargetUserProfiles_logged() throws Exception {
- if (!mHasManagedUserFeature) {
+ if (!mHasManagedUserFeature || !isStatsdEnabled(getDevice())) {
return;
}
assertMetricsLogged(
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CustomDeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CustomDeviceOwnerTest.java
index eca6953..f01a88a 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CustomDeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CustomDeviceOwnerTest.java
@@ -16,11 +16,7 @@
package com.android.cts.devicepolicy;
-import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
-import com.android.cts.devicepolicy.BaseDevicePolicyTest.Settings;
-
-import java.io.File;
-import java.lang.Exception;
+import android.platform.test.annotations.FlakyTest;
/**
* This class is used for tests that need to do something special before setting the device
@@ -101,6 +97,7 @@
}
}
+ @FlakyTest
public void testCannotSetDeviceOwnerWhenAccountPresent() throws Exception {
if (!mHasFeature) {
return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAdminHostSideTestApi23.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAdminHostSideTestApi23.java
index 3a74953..7cd15b5 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAdminHostSideTestApi23.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAdminHostSideTestApi23.java
@@ -15,6 +15,8 @@
*/
package com.android.cts.devicepolicy;
+import android.platform.test.annotations.FlakyTest;
+
/**
* BaseDeviceAdminHostSideTest for device admin targeting API level 23.
*/
@@ -27,6 +29,7 @@
/**
* Device admin with no BIND_DEVICE_ADMIN can still be activated, if the target SDK <= 23.
*/
+ @FlakyTest
public void testAdminWithNoProtection() throws Exception {
if (!mHasFeature) {
return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAdminServiceProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAdminServiceProfileOwnerTest.java
index a773737..f48a656 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAdminServiceProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAdminServiceProfileOwnerTest.java
@@ -15,6 +15,8 @@
*/
package com.android.cts.devicepolicy;
+import android.platform.test.annotations.LargeTest;
+
public class DeviceAdminServiceProfileOwnerTest extends BaseDeviceAdminServiceTest {
private int mUserId;
@@ -46,4 +48,10 @@
protected void setAsOwnerOrFail(String component) throws Exception {
setProfileOwnerOrFail(component, getUserId());
}
+
+ @Override
+ @LargeTest
+ public void testAll() throws Throwable {
+ super.testAll();
+ }
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
index ceafda1..05bc832 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
@@ -17,10 +17,14 @@
package com.android.cts.devicepolicy;
import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.assertMetricsLogged;
+import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.isStatsdEnabled;
+import android.platform.test.annotations.FlakyTest;
+import android.platform.test.annotations.LargeTest;
import android.platform.test.annotations.RequiresDevice;
import android.stats.devicepolicy.EventId;
+import com.android.cts.devicepolicy.annotations.LockSettingsTest;
import com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier;
import com.android.cts.devicepolicy.metrics.DevicePolicyEventWrapper;
import com.android.tradefed.device.DeviceNotAvailableException;
@@ -152,7 +156,7 @@
"cmd netpolicy set restrict-background false";
// The following constants were copied from DevicePolicyManager
- private static final int PASSWORD_QUALITY_SOMETHING = 0x10000;
+ private static final int PASSWORD_QUALITY_COMPLEX = 0x60000;
private static final int KEYGUARD_DISABLE_FEATURES_NONE = 0;
private static final int KEYGUARD_DISABLE_FINGERPRINT = 1 << 5;
private static final int KEYGUARD_DISABLE_TRUST_AGENTS = 1 << 4;
@@ -212,7 +216,7 @@
}
public void testInstallCaCertLogged() throws Exception {
- if (!mHasFeature) {
+ if (!mHasFeature || !isStatsdEnabled(getDevice())) {
return;
}
assertMetricsLogged(getDevice(), () -> {
@@ -278,13 +282,15 @@
// The DPC should still be able to manage app restrictions normally.
executeDeviceTestClass(".ApplicationRestrictionsTest");
- assertMetricsLogged(getDevice(), () -> {
- executeDeviceTestMethod(".ApplicationRestrictionsTest",
- "testSetApplicationRestrictions");
- }, new DevicePolicyEventWrapper.Builder(EventId.SET_APPLICATION_RESTRICTIONS_VALUE)
- .setAdminPackageName(DEVICE_ADMIN_PKG)
- .setStrings(APP_RESTRICTIONS_TARGET_APP_PKG)
- .build());
+ if (isStatsdEnabled(getDevice())) {
+ assertMetricsLogged(getDevice(), () -> {
+ executeDeviceTestMethod(".ApplicationRestrictionsTest",
+ "testSetApplicationRestrictions");
+ }, new DevicePolicyEventWrapper.Builder(EventId.SET_APPLICATION_RESTRICTIONS_VALUE)
+ .setAdminPackageName(DEVICE_ADMIN_PKG)
+ .setStrings(APP_RESTRICTIONS_TARGET_APP_PKG)
+ .build());
+ }
} finally {
changeApplicationRestrictionsManagingPackage(null);
}
@@ -541,7 +547,7 @@
@RequiresDevice
public void testAlwaysOnVpnPackageLogged() throws Exception {
- if (!mHasFeature) {
+ if (!mHasFeature || !isStatsdEnabled(getDevice())) {
return;
}
// Will be uninstalled in tearDown().
@@ -564,6 +570,14 @@
executeDeviceTestMethod(".PermissionsTest", "testPermissionPolicy");
}
+ public void testAutoGrantMultiplePermissionsInGroup() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ installAppPermissionAppAsUser();
+ executeDeviceTestMethod(".PermissionsTest", "testAutoGrantMultiplePermissionsInGroup");
+ }
+
public void testPermissionMixedPolicies() throws Exception {
if (!mHasFeature) {
return;
@@ -636,14 +650,16 @@
return;
}
executeDeviceTestClass(".PersistentIntentResolvingTest");
- assertMetricsLogged(getDevice(), () -> {
- executeDeviceTestMethod(".PersistentIntentResolvingTest",
- "testAddPersistentPreferredActivityYieldsReceptionAtTarget");
- }, new DevicePolicyEventWrapper.Builder(EventId.ADD_PERSISTENT_PREFERRED_ACTIVITY_VALUE)
+ if (isStatsdEnabled(getDevice())) {
+ assertMetricsLogged(getDevice(), () -> {
+ executeDeviceTestMethod(".PersistentIntentResolvingTest",
+ "testAddPersistentPreferredActivityYieldsReceptionAtTarget");
+ }, new DevicePolicyEventWrapper.Builder(EventId.ADD_PERSISTENT_PREFERRED_ACTIVITY_VALUE)
.setAdminPackageName(DEVICE_ADMIN_PKG)
.setStrings(DEVICE_ADMIN_PKG,
"com.android.cts.deviceandprofileowner.EXAMPLE_ACTION")
.build());
+ }
}
public void testScreenCaptureDisabled() throws Exception {
@@ -688,17 +704,20 @@
return;
}
executeDeviceTestClass(".SupportMessageTest");
- assertMetricsLogged(getDevice(), () -> {
- executeDeviceTestMethod(
- ".SupportMessageTest", "testShortSupportMessageSetGetAndClear");
- }, new DevicePolicyEventWrapper.Builder(EventId.SET_SHORT_SUPPORT_MESSAGE_VALUE)
+ if (isStatsdEnabled(getDevice())) {
+ assertMetricsLogged(getDevice(), () -> {
+ executeDeviceTestMethod(
+ ".SupportMessageTest", "testShortSupportMessageSetGetAndClear");
+ }, new DevicePolicyEventWrapper.Builder(EventId.SET_SHORT_SUPPORT_MESSAGE_VALUE)
.setAdminPackageName(DEVICE_ADMIN_PKG)
.build());
- assertMetricsLogged(getDevice(), () -> {
- executeDeviceTestMethod(".SupportMessageTest", "testLongSupportMessageSetGetAndClear");
- }, new DevicePolicyEventWrapper.Builder(EventId.SET_LONG_SUPPORT_MESSAGE_VALUE)
+ assertMetricsLogged(getDevice(), () -> {
+ executeDeviceTestMethod(".SupportMessageTest",
+ "testLongSupportMessageSetGetAndClear");
+ }, new DevicePolicyEventWrapper.Builder(EventId.SET_LONG_SUPPORT_MESSAGE_VALUE)
.setAdminPackageName(DEVICE_ADMIN_PKG)
.build());
+ }
}
public void testApplicationHidden() throws Exception {
@@ -707,11 +726,12 @@
}
installAppPermissionAppAsUser();
executeDeviceTestClass(".ApplicationHiddenTest");
- installAppAsUser(PERMISSIONS_APP_APK, mUserId);
- assertMetricsLogged(getDevice(), () -> {
- executeDeviceTestMethod(".ApplicationHiddenTest",
- "testSetApplicationHidden");
- }, new DevicePolicyEventWrapper.Builder(EventId.SET_APPLICATION_HIDDEN_VALUE)
+ if (isStatsdEnabled(getDevice())) {
+ installAppAsUser(PERMISSIONS_APP_APK, mUserId);
+ assertMetricsLogged(getDevice(), () -> {
+ executeDeviceTestMethod(".ApplicationHiddenTest",
+ "testSetApplicationHidden");
+ }, new DevicePolicyEventWrapper.Builder(EventId.SET_APPLICATION_HIDDEN_VALUE)
.setAdminPackageName(DEVICE_ADMIN_PKG)
.setBoolean(false)
.setStrings(PERMISSIONS_APP_PKG, "hidden")
@@ -721,6 +741,7 @@
.setBoolean(false)
.setStrings(PERMISSIONS_APP_PKG, "not_hidden")
.build());
+ }
}
public void testAccountManagement_deviceAndProfileOwnerAlwaysAllowed() throws Exception {
@@ -807,13 +828,15 @@
runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".DelegatedCertInstallerTest", mUserId);
- assertMetricsLogged(getDevice(), () -> {
+ if (isStatsdEnabled(getDevice())) {
+ assertMetricsLogged(getDevice(), () -> {
runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".DelegatedCertInstallerTest",
"testInstallKeyPair", mUserId);
- }, new DevicePolicyEventWrapper.Builder(EventId.SET_CERT_INSTALLER_PACKAGE_VALUE)
- .setAdminPackageName(DEVICE_ADMIN_PKG)
- .setStrings(CERT_INSTALLER_PKG)
- .build());
+ }, new DevicePolicyEventWrapper.Builder(EventId.SET_CERT_INSTALLER_PACKAGE_VALUE)
+ .setAdminPackageName(DEVICE_ADMIN_PKG)
+ .setStrings(CERT_INSTALLER_PKG)
+ .build());
+ }
}
public interface DelegatedCertInstallerTestAction {
@@ -848,6 +871,36 @@
".DirectDelegatedCertInstallerTest", mUserId));
}
+ // This test generates a key pair and validates that an app can be silently granted
+ // access to it.
+ public void testSetKeyGrant() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+
+ // Install an app
+ installAppAsUser(CERT_INSTALLER_APK, mUserId);
+
+ try {
+ // First, generate a key and grant the cert installer access to it.
+ runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".DelegatedCertInstallerHelper",
+ "testManualGenerateKeyAndGrantAccess", mUserId);
+ // Test the key is usable.
+ runDeviceTestsAsUser("com.android.cts.certinstaller",
+ ".PreSelectedKeyAccessTest", "testAccessingPreSelectedAliasExpectingSuccess",
+ mUserId);
+ // Remove the grant
+ runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".DelegatedCertInstallerHelper",
+ "testManualRemoveKeyGrant", mUserId);
+ // Run another test to make sure the app no longer has access to the key.
+ runDeviceTestsAsUser("com.android.cts.certinstaller",
+ ".PreSelectedKeyAccessTest", "testAccessingPreSelectedAliasWithoutGrant", mUserId);
+ } finally {
+ runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".DelegatedCertInstallerHelper",
+ "testManualClearGeneratedKey", mUserId);
+ }
+ }
+
// Sets restrictions and launches non-admin app, that tries to set wallpaper.
// Non-admin apps must not violate any user restriction.
public void testSetWallpaper_disallowed() throws Exception {
@@ -857,6 +910,11 @@
return;
}
+ if (!hasService("wallpaper")) {
+ CLog.d("testSetWallpaper_disallowed(): device does not support wallpapers");
+ return;
+ }
+
installAppAsUser(CUSTOMIZATION_APP_APK, mUserId);
try {
changeUserRestrictionOrFail(DISALLOW_SET_WALLPAPER, true, mUserId);
@@ -873,6 +931,10 @@
if (!mHasFeature) {
return;
}
+ if (!hasService("wallpaper")) {
+ CLog.d("testDisallowSetWallpaper_allowed(): device does not support wallpapers");
+ return;
+ }
executeDeviceTestMethod(".CustomizationRestrictionsTest",
"testDisallowSetWallpaper_allowed");
}
@@ -1042,7 +1104,7 @@
}
public void testDisallowAdjustVolumeMutedLogged() throws Exception {
- if (!mHasFeature) {
+ if (!mHasFeature || !isStatsdEnabled(getDevice())) {
return;
}
assertMetricsLogged(getDevice(), () -> {
@@ -1058,6 +1120,7 @@
.build());
}
+ @FlakyTest(bugId = 132226089)
public void testLockTask() throws Exception {
if (!mHasFeature) {
return;
@@ -1065,14 +1128,17 @@
try {
installAppAsUser(INTENT_RECEIVER_APK, mUserId);
executeDeviceTestClass(".LockTaskTest");
- assertMetricsLogged(
- getDevice(),
- () -> executeDeviceTestMethod(".LockTaskTest", "testStartLockTask"),
- new DevicePolicyEventWrapper.Builder(EventId.SET_LOCKTASK_MODE_ENABLED_VALUE)
- .setAdminPackageName(DEVICE_ADMIN_PKG)
- .setBoolean(true)
- .setStrings(DEVICE_ADMIN_PKG)
- .build());
+ if (isStatsdEnabled(getDevice())) {
+ assertMetricsLogged(
+ getDevice(),
+ () -> executeDeviceTestMethod(".LockTaskTest", "testStartLockTask"),
+ new DevicePolicyEventWrapper.Builder(
+ EventId.SET_LOCKTASK_MODE_ENABLED_VALUE)
+ .setAdminPackageName(DEVICE_ADMIN_PKG)
+ .setBoolean(true)
+ .setStrings(DEVICE_ADMIN_PKG)
+ .build());
+ }
} catch (AssertionError ex) {
// STOPSHIP(b/32771855), remove this once we fixed the bug.
executeShellCommand("dumpsys activity activities");
@@ -1084,6 +1150,7 @@
}
}
+ @LargeTest
public void testLockTaskAfterReboot() throws Exception {
if (!mHasFeature) {
return;
@@ -1104,6 +1171,7 @@
}
}
+ @LargeTest
public void testLockTaskAfterReboot_tryOpeningSettings() throws Exception {
if (!mHasFeature) {
return;
@@ -1163,6 +1231,7 @@
}
}
+ @FlakyTest(bugId = 141314026)
public void testSuspendPackage() throws Exception {
if (!mHasFeature) {
return;
@@ -1190,6 +1259,7 @@
executeDeviceTestMethod(".SuspendPackageTest", "testSuspendNotSuspendablePackages");
}
+ @FlakyTest(bugId = 141314026)
public void testSuspendPackageWithPackageManager() throws Exception {
if (!mHasFeature) {
return;
@@ -1215,6 +1285,7 @@
executeDeviceTestClass(".TrustAgentInfoTest");
}
+ @FlakyTest(bugId = 141161038)
public void testCannotRemoveUserIfRestrictionSet() throws Exception {
// Outside of the primary user, setting DISALLOW_REMOVE_USER would not work.
if (!mHasFeature || !canCreateAdditionalUsers(1) || mUserId != getPrimaryUser()) {
@@ -1270,7 +1341,7 @@
}
public void testSetCameraDisabledLogged() throws Exception {
- if (!mHasFeature) {
+ if (!mHasFeature || !isStatsdEnabled(getDevice())) {
return;
}
assertMetricsLogged(getDevice(), () -> {
@@ -1285,6 +1356,7 @@
.build());
}
+ @LockSettingsTest
public void testResetPasswordWithToken() throws Exception {
if (!mHasFeature || !mHasSecureLockScreen) {
return;
@@ -1306,6 +1378,19 @@
executeDeviceTestClass(".PasswordSufficientInitiallyTest");
}
+ public void testPasswordRequirementsApi() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+
+ executeDeviceTestMethod(".PasswordRequirementsTest",
+ "testSettingConstraintsWithLowQualityThrowsOnRPlus");
+ executeDeviceTestMethod(".PasswordRequirementsTest",
+ "testSettingConstraintsWithNumericQualityOnlyLengthAllowedOnRPlus");
+ executeDeviceTestMethod(".PasswordRequirementsTest",
+ "testSettingConstraintsWithComplexQualityAndResetWithLowerQuality");
+ }
+
public void testGetCurrentFailedPasswordAttempts() throws Exception {
if (!mHasFeature || !mHasSecureLockScreen) {
return;
@@ -1435,7 +1520,7 @@
}
public void testInstallKeyPairLogged() throws Exception {
- if (!mHasFeature) {
+ if (!mHasFeature || !isStatsdEnabled(getDevice())) {
return;
}
@@ -1452,7 +1537,7 @@
}
public void testGenerateKeyPairLogged() throws Exception {
- if (!mHasFeature) {
+ if (!mHasFeature || !isStatsdEnabled(getDevice())) {
return;
}
@@ -1475,7 +1560,7 @@
}
public void testSetKeyPairCertificateLogged() throws Exception {
- if (!mHasFeature) {
+ if (!mHasFeature || !isStatsdEnabled(getDevice())) {
return;
}
@@ -1493,10 +1578,11 @@
}
executeDeviceTestClass(".AccessibilityServicesTest");
- assertMetricsLogged(getDevice(), () -> {
- executeDeviceTestMethod(".AccessibilityServicesTest",
- "testPermittedAccessibilityServices");
- }, new DevicePolicyEventWrapper
+ if (isStatsdEnabled(getDevice())) {
+ assertMetricsLogged(getDevice(), () -> {
+ executeDeviceTestMethod(".AccessibilityServicesTest",
+ "testPermittedAccessibilityServices");
+ }, new DevicePolicyEventWrapper
.Builder(EventId.SET_PERMITTED_ACCESSIBILITY_SERVICES_VALUE)
.setAdminPackageName(DEVICE_ADMIN_PKG)
.setStrings((String[]) null)
@@ -1511,6 +1597,7 @@
.setAdminPackageName(DEVICE_ADMIN_PKG)
.setStrings("com.google.pkg.one", "com.google.pkg.two")
.build());
+ }
}
public void testPermittedInputMethods() throws Exception {
@@ -1519,10 +1606,10 @@
}
executeDeviceTestClass(".InputMethodsTest");
- assertMetricsLogged(getDevice(), () -> {
- executeDeviceTestMethod(".InputMethodsTest",
- "testPermittedInputMethods");
- }, new DevicePolicyEventWrapper.Builder(EventId.SET_PERMITTED_INPUT_METHODS_VALUE)
+ if (isStatsdEnabled(getDevice())) {
+ assertMetricsLogged(getDevice(), () -> {
+ executeDeviceTestMethod(".InputMethodsTest", "testPermittedInputMethods");
+ }, new DevicePolicyEventWrapper.Builder(EventId.SET_PERMITTED_INPUT_METHODS_VALUE)
.setAdminPackageName(DEVICE_ADMIN_PKG)
.setStrings((String[]) null)
.build(),
@@ -1534,6 +1621,7 @@
.setAdminPackageName(DEVICE_ADMIN_PKG)
.setStrings("com.google.pkg.one", "com.google.pkg.two")
.build());
+ }
}
public void testSetStorageEncryption() throws Exception {
@@ -1547,7 +1635,7 @@
}
public void testPasswordMethodsLogged() throws Exception {
- if (!mHasFeature) {
+ if (!mHasFeature || !isStatsdEnabled(getDevice())) {
return;
}
@@ -1555,7 +1643,7 @@
executeDeviceTestMethod(".DevicePolicyLoggingTest", "testPasswordMethodsLogged");
}, new DevicePolicyEventWrapper.Builder(EventId.SET_PASSWORD_QUALITY_VALUE)
.setAdminPackageName(DEVICE_ADMIN_PKG)
- .setInt(PASSWORD_QUALITY_SOMETHING)
+ .setInt(PASSWORD_QUALITY_COMPLEX)
.setBoolean(false)
.build(),
new DevicePolicyEventWrapper.Builder(EventId.SET_PASSWORD_MINIMUM_LENGTH_VALUE)
@@ -1589,7 +1677,7 @@
}
public void testLockNowLogged() throws Exception {
- if (!mHasFeature) {
+ if (!mHasFeature || !isStatsdEnabled(getDevice())) {
return;
}
assertMetricsLogged(getDevice(), () -> {
@@ -1601,7 +1689,7 @@
}
public void testSetKeyguardDisabledFeaturesLogged() throws Exception {
- if (!mHasFeature) {
+ if (!mHasFeature || !isStatsdEnabled(getDevice())) {
return;
}
assertMetricsLogged(getDevice(), () -> {
@@ -1630,7 +1718,7 @@
}
public void testSetUserRestrictionLogged() throws Exception {
- if (!mHasFeature) {
+ if (!mHasFeature || !isStatsdEnabled(getDevice())) {
return;
}
assertMetricsLogged(getDevice(), () -> {
@@ -1664,7 +1752,7 @@
}
public void testSetSecureSettingLogged() throws Exception {
- if (!mHasFeature) {
+ if (!mHasFeature || !isStatsdEnabled(getDevice())) {
return;
}
assertMetricsLogged(getDevice(), () -> {
@@ -1690,7 +1778,7 @@
}
public void testSetPermissionPolicyLogged() throws Exception {
- if (!mHasFeature) {
+ if (!mHasFeature || !isStatsdEnabled(getDevice())) {
return;
}
assertMetricsLogged(getDevice(), () -> {
@@ -1714,7 +1802,7 @@
}
public void testSetPermissionGrantStateLogged() throws Exception {
- if (!mHasFeature) {
+ if (!mHasFeature || !isStatsdEnabled(getDevice())) {
return;
}
installAppPermissionAppAsUser();
@@ -1758,7 +1846,7 @@
}
public void testEnableSystemAppLogged() throws Exception {
- if (!mHasFeature) {
+ if (!mHasFeature || !isStatsdEnabled(getDevice())) {
return;
}
final List<String> enabledSystemPackageNames = getEnabledSystemPackageNames();
@@ -1777,7 +1865,7 @@
}
public void testEnableSystemAppWithIntentLogged() throws Exception {
- if (!mHasFeature) {
+ if (!mHasFeature || !isStatsdEnabled(getDevice())) {
return;
}
final String systemPackageToEnable = getLaunchableSystemPackage();
@@ -1797,7 +1885,7 @@
}
public void testSetUninstallBlockedLogged() throws Exception {
- if (!mHasFeature) {
+ if (!mHasFeature || !isStatsdEnabled(getDevice())) {
return;
}
installAppAsUser(PERMISSIONS_APP_APK, mUserId);
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTestApi25.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTestApi25.java
index cacb226..0ee24a6 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTestApi25.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTestApi25.java
@@ -16,6 +16,7 @@
package com.android.cts.devicepolicy;
+import android.platform.test.annotations.FlakyTest;
import android.platform.test.annotations.RequiresDevice;
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
@@ -72,6 +73,7 @@
}
/** Additional test for resetPassword for FBE-enabled devices. */
+ @FlakyTest(bugId = 141161690)
public void testResetPasswordFbe() throws Exception {
if (!mHasFeature || !mSupportsFbe || !mHasSecureLockScreen) {
return;
@@ -95,6 +97,15 @@
executeDeviceTestMethod(".PermissionsTest", "testPermissionGrantStateAppPreMDeviceAdminPreQ");
}
+ public void testPasswordRequirementsApi() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+
+ executeDeviceTestMethod(".PasswordRequirementsTest",
+ "testPasswordConstraintsDoesntThrowAndPreservesValuesPreR");
+ }
+
protected void executeDeviceTestClass(String className) throws Exception {
runDeviceTestsAsUser(DEVICE_ADMIN_PKG, className, mUserId);
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerPlusProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerPlusProfileOwnerTest.java
index 25168ff..2acb6cb 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerPlusProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerPlusProfileOwnerTest.java
@@ -17,12 +17,16 @@
package com.android.cts.devicepolicy;
import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.assertMetricsLogged;
+import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.isStatsdEnabled;
+
+import android.platform.test.annotations.FlakyTest;
+import android.stats.devicepolicy.EventId;
+import android.platform.test.annotations.LargeTest;
import com.android.cts.devicepolicy.metrics.DevicePolicyEventWrapper;
import com.android.cts.devicepolicy.metrics.DevicePolicyEventWrapper.Builder;
-import java.util.List;
-import android.stats.devicepolicy.EventId;
+import java.util.List;
/**
* Tests for having both device owner and profile owner. Device owner is setup for you in
@@ -93,6 +97,7 @@
/**
* Both device owner and profile are the same package ({@link #COMP_DPC_PKG}).
*/
+ @LargeTest
public void testBindDeviceAdminServiceAsUser_corpOwnedManagedProfile() throws Exception {
if (!mHasFeature) {
return;
@@ -116,6 +121,7 @@
* Same as {@link #testBindDeviceAdminServiceAsUser_corpOwnedManagedProfile} except
* creating managed profile through ManagedProvisioning like normal flow
*/
+ @FlakyTest
public void testBindDeviceAdminServiceAsUser_corpOwnedManagedProfileWithManagedProvisioning()
throws Exception {
if (!mHasFeature) {
@@ -137,6 +143,7 @@
* {@link #testBindDeviceAdminServiceAsUser_corpOwnedManagedProfileWithManagedProvisioning}
* except we don't enable the profile.
*/
+ @FlakyTest
public void testBindDeviceAdminServiceAsUser_canBindEvenIfProfileNotEnabled() throws Exception {
if (!mHasFeature) {
return;
@@ -174,6 +181,7 @@
* Both device owner and profile are the same package ({@link #COMP_DPC_PKG}), as setup
* by createAndManagedUser.
*/
+ @FlakyTest
public void testBindDeviceAdminServiceAsUser_secondaryUser() throws Exception {
if (!mHasFeature || !canCreateAdditionalUsers(1)) {
return;
@@ -195,6 +203,7 @@
* Test that the DO can talk to both a managed profile and managed secondary user at the same
* time.
*/
+ @FlakyTest
public void testBindDeviceAdminServiceAsUser_compPlusSecondaryUser() throws Exception {
if (!mHasFeature || !canCreateAdditionalUsers(2)) {
return;
@@ -229,6 +238,7 @@
assertTrue(getDevice().removeUser(profileUserId));
}
+ @FlakyTest(bugId = 141161038)
public void testCannotRemoveUserIfRestrictionSet() throws Exception {
if (!mHasFeature || !canCreateAdditionalUsers(1)) {
return;
@@ -326,7 +336,7 @@
}
public void testWipeData_managedProfileLogged() throws Exception {
- if (!mHasFeature) {
+ if (!mHasFeature || !isStatsdEnabled(getDevice())) {
return;
}
int profileUserId = setupManagedProfile(COMP_DPC_APK, COMP_DPC_PKG, COMP_DPC_ADMIN);
@@ -353,7 +363,7 @@
}
public void testWipeData_secondaryUserLogged() throws Exception {
- if (!mHasFeature || !canCreateAdditionalUsers(1)) {
+ if (!mHasFeature || !canCreateAdditionalUsers(1) || !isStatsdEnabled(getDevice())) {
return;
}
int secondaryUserId = setupManagedSecondaryUser();
@@ -416,6 +426,7 @@
}
}
+ @FlakyTest
public void testRequestBugreportAvailableIfAffiliated() throws Exception {
if (!mHasFeature) {
return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
index 7c335c2..1c15666 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
@@ -17,7 +17,10 @@
package com.android.cts.devicepolicy;
import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.assertMetricsLogged;
+import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.isStatsdEnabled;
+import android.platform.test.annotations.FlakyTest;
+import android.platform.test.annotations.LargeTest;
import android.stats.devicepolicy.EventId;
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
@@ -100,6 +103,15 @@
/** CreateAndManageUser is available and an additional user can be created. */
private boolean mHasCreateAndManageUserFeature;
+ /**
+ * Copied from {@link android.app.admin.DevicePolicyManager}
+ */
+ private static final String GLOBAL_SETTING_AUTO_TIME = "auto_time";
+ private static final String GLOBAL_SETTING_AUTO_TIME_ZONE = "auto_time_zone";
+ private static final String GLOBAL_SETTING_DATA_ROAMING = "data_roaming";
+ private static final String GLOBAL_SETTING_USB_MASS_STORAGE_ENABLED =
+ "usb_mass_storage_enabled";
+
@Override
protected void setUp() throws Exception {
super.setUp();
@@ -143,23 +155,28 @@
return;
}
executeDeviceOwnerTest("LockScreenInfoTest");
- assertMetricsLogged(getDevice(), () -> {
- executeDeviceTestMethod(".LockScreenInfoTest", "testSetAndGetLockInfo");
- }, new DevicePolicyEventWrapper.Builder(EventId.SET_DEVICE_OWNER_LOCK_SCREEN_INFO_VALUE)
- .setAdminPackageName(DEVICE_OWNER_PKG)
- .build());
+ if (isStatsdEnabled(getDevice())) {
+ assertMetricsLogged(getDevice(), () -> {
+ executeDeviceTestMethod(".LockScreenInfoTest", "testSetAndGetLockInfo");
+ }, new DevicePolicyEventWrapper.Builder(EventId.SET_DEVICE_OWNER_LOCK_SCREEN_INFO_VALUE)
+ .setAdminPackageName(DEVICE_OWNER_PKG)
+ .build());
+ }
}
+ @FlakyTest(bugId = 137088260)
public void testWifi() throws Exception {
if (!mHasFeature || !hasDeviceFeature("android.hardware.wifi")) {
return;
}
executeDeviceOwnerTest("WifiTest");
- assertMetricsLogged(getDevice(), () -> {
- executeDeviceTestMethod(".WifiTest", "testGetWifiMacAddress");
- }, new DevicePolicyEventWrapper.Builder(EventId.GET_WIFI_MAC_ADDRESS_VALUE)
- .setAdminPackageName(DEVICE_OWNER_PKG)
- .build());
+ if (isStatsdEnabled(getDevice())) {
+ assertMetricsLogged(getDevice(), () -> {
+ executeDeviceTestMethod(".WifiTest", "testGetWifiMacAddress");
+ }, new DevicePolicyEventWrapper.Builder(EventId.GET_WIFI_MAC_ADDRESS_VALUE)
+ .setAdminPackageName(DEVICE_OWNER_PKG)
+ .build());
+ }
}
public void testRemoteBugreportWithTwoUsers() throws Exception {
@@ -175,6 +192,7 @@
}
}
+ @FlakyTest(bugId = 137071121)
public void testCreateAndManageUser_LowStorage() throws Exception {
if (!mHasCreateAndManageUserFeature) {
return;
@@ -231,6 +249,7 @@
* to the user.
* {@link android.app.admin.DevicePolicyManager#switchUser} is tested.
*/
+ @FlakyTest(bugId = 131743223)
public void testCreateAndManageUser_SwitchUser() throws Exception {
if (!mHasCreateAndManageUserFeature || !canStartAdditionalUsers(1)) {
return;
@@ -414,6 +433,7 @@
}
}
+ @FlakyTest(bugId = 126955083)
public void testUserAddedOrRemovedBroadcasts() throws Exception {
if (mHasCreateAndManageUserFeature) {
executeDeviceTestMethod(".CreateAndManageUserTest",
@@ -448,6 +468,7 @@
}
}
+ @FlakyTest(bugId = 137093665)
public void testSecurityLoggingWithSingleUser() throws Exception {
if (!mHasFeature) {
return;
@@ -491,7 +512,7 @@
}
public void testSecurityLoggingEnabledLogged() throws Exception {
- if (!mHasFeature) {
+ if (!mHasFeature || !isStatsdEnabled(getDevice())) {
return;
}
assertMetricsLogged(getDevice(), () -> {
@@ -531,6 +552,7 @@
}
}
+ @FlakyTest(bugId = 137092833)
public void testNetworkLoggingWithSingleUser() throws Exception {
if (!mHasFeature) {
return;
@@ -548,6 +570,7 @@
Collections.singletonMap(ARG_NETWORK_LOGGING_BATCH_COUNT, Integer.toString(2)));
}
+ @LargeTest
public void testNetworkLogging_rebootResetsId() throws Exception {
if (!mHasFeature) {
return;
@@ -573,6 +596,7 @@
executeDeviceTestMethod(".AffiliationTest", "testSetAffiliationId_containsEmptyString");
}
+ @LargeTest
public void testSystemUpdatePolicy() throws Exception {
if (!mHasFeature) {
return;
@@ -581,7 +605,7 @@
}
public void testSetSystemUpdatePolicyLogged() throws Exception {
- if (!mHasFeature) {
+ if (!mHasFeature || !isStatsdEnabled(getDevice())) {
return;
}
assertMetricsLogged(getDevice(), () -> {
@@ -610,6 +634,7 @@
.build());
}
+ @FlakyTest(bugId = 127101449)
public void testWifiConfigLockdown() throws Exception {
final boolean hasWifi = hasDeviceFeature("android.hardware.wifi");
if (hasWifi && mHasFeature) {
@@ -698,24 +723,28 @@
"testIsProvisioningAllowedTrueForManagedProfileAction");
}
+ @FlakyTest(bugId = 137096267)
public void testAdminActionBookkeeping() throws Exception {
if (!mHasFeature) {
return;
}
executeDeviceOwnerTest("AdminActionBookkeepingTest");
- assertMetricsLogged(getDevice(), () -> {
- executeDeviceTestMethod(".AdminActionBookkeepingTest", "testRetrieveSecurityLogs");
- }, new DevicePolicyEventWrapper.Builder(EventId.RETRIEVE_SECURITY_LOGS_VALUE)
+ if (isStatsdEnabled(getDevice())) {
+ assertMetricsLogged(getDevice(), () -> {
+ executeDeviceTestMethod(".AdminActionBookkeepingTest", "testRetrieveSecurityLogs");
+ }, new DevicePolicyEventWrapper.Builder(EventId.RETRIEVE_SECURITY_LOGS_VALUE)
.setAdminPackageName(DEVICE_OWNER_PKG)
.build(),
- new DevicePolicyEventWrapper.Builder(EventId.RETRIEVE_PRE_REBOOT_SECURITY_LOGS_VALUE)
+ new DevicePolicyEventWrapper.Builder(
+ EventId.RETRIEVE_PRE_REBOOT_SECURITY_LOGS_VALUE)
.setAdminPackageName(DEVICE_OWNER_PKG)
.build());
- assertMetricsLogged(getDevice(), () -> {
- executeDeviceTestMethod(".AdminActionBookkeepingTest", "testRequestBugreport");
- }, new DevicePolicyEventWrapper.Builder(EventId.REQUEST_BUGREPORT_VALUE)
- .setAdminPackageName(DEVICE_OWNER_PKG)
- .build());
+ assertMetricsLogged(getDevice(), () -> {
+ executeDeviceTestMethod(".AdminActionBookkeepingTest", "testRequestBugreport");
+ }, new DevicePolicyEventWrapper.Builder(EventId.REQUEST_BUGREPORT_VALUE)
+ .setAdminPackageName(DEVICE_OWNER_PKG)
+ .build());
+ }
}
public void testBluetoothRestriction() throws Exception {
@@ -837,6 +866,7 @@
}
}
+ @LargeTest
public void testPackageInstallCache_multiUser() throws Exception {
if (!mHasFeature || !canCreateAdditionalUsers(1)) {
return;
@@ -906,6 +936,7 @@
executeDeviceOwnerTest("OverrideApnTest");
}
+ @FlakyTest(bugId = 134487729)
public void testPrivateDnsPolicy() throws Exception {
if (!mHasFeature) {
return;
@@ -927,7 +958,7 @@
}
public void testInstallUpdateLogged() throws Exception {
- if (!mHasFeature || !isDeviceAb()) {
+ if (!mHasFeature || !isDeviceAb() || !isStatsdEnabled(getDevice())) {
return;
}
pushUpdateFileToDevice("wrongHash.zip");
@@ -961,7 +992,7 @@
}
public void testSetKeyguardDisabledLogged() throws Exception {
- if (!mHasFeature) {
+ if (!mHasFeature || !isStatsdEnabled(getDevice())) {
return;
}
assertMetricsLogged(getDevice(), () -> {
@@ -972,7 +1003,7 @@
}
public void testSetStatusBarDisabledLogged() throws Exception {
- if (!mHasFeature) {
+ if (!mHasFeature || !isStatsdEnabled(getDevice())) {
return;
}
assertMetricsLogged(getDevice(), () -> {
@@ -1011,6 +1042,30 @@
}
}
+ public void testSetGlobalSettingLogged() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ assertMetricsLogged(getDevice(), () -> {
+ executeDeviceTestMethod(".DevicePolicyLoggingTest", "testSetGlobalSettingLogged");
+ }, new DevicePolicyEventWrapper.Builder(EventId.SET_GLOBAL_SETTING_VALUE)
+ .setAdminPackageName(DEVICE_OWNER_PKG)
+ .setStrings(GLOBAL_SETTING_AUTO_TIME, "1")
+ .build(),
+ new DevicePolicyEventWrapper.Builder(EventId.SET_GLOBAL_SETTING_VALUE)
+ .setAdminPackageName(DEVICE_OWNER_PKG)
+ .setStrings(GLOBAL_SETTING_AUTO_TIME_ZONE, "1")
+ .build(),
+ new DevicePolicyEventWrapper.Builder(EventId.SET_GLOBAL_SETTING_VALUE)
+ .setAdminPackageName(DEVICE_OWNER_PKG)
+ .setStrings(GLOBAL_SETTING_DATA_ROAMING, "1")
+ .build(),
+ new DevicePolicyEventWrapper.Builder(EventId.SET_GLOBAL_SETTING_VALUE)
+ .setAdminPackageName(DEVICE_OWNER_PKG)
+ .setStrings(GLOBAL_SETTING_USB_MASS_STORAGE_ENABLED, "1")
+ .build());
+ }
+
private void executeDeviceOwnerTest(String testClassName) throws Exception {
if (!mHasFeature) {
return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsProfileTest.java
index f8a78f0..2b6c245 100755
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsProfileTest.java
@@ -16,8 +16,8 @@
package com.android.cts.devicepolicy;
-import com.android.ddmlib.Log.LogLevel;
-import com.android.tradefed.device.DeviceNotAvailableException;
+import android.platform.test.annotations.FlakyTest;
+
import com.android.tradefed.log.LogUtil.CLog;
import java.util.Collections;
@@ -134,6 +134,7 @@
mParentUserId, Collections.singletonMap(PARAM_TEST_USER, mMainUserSerialNumber));
}
+ @FlakyTest
public void testLauncherCallbackPackageAddedProfile() throws Exception {
if (!mHasFeature) {
return;
@@ -146,6 +147,7 @@
mParentUserId, Collections.singletonMap(PARAM_TEST_USER, mProfileSerialNumber));
}
+ @FlakyTest
public void testLauncherCallbackPackageRemovedProfile() throws Exception {
if (!mHasFeature) {
return;
@@ -159,6 +161,7 @@
mParentUserId, Collections.singletonMap(PARAM_TEST_USER, mProfileSerialNumber));
}
+ @FlakyTest
public void testLauncherCallbackPackageChangedProfile() throws Exception {
if (!mHasFeature) {
return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsSingleUserTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsSingleUserTest.java
index 5d67a46..19a77d3 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsSingleUserTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsSingleUserTest.java
@@ -16,9 +16,7 @@
package com.android.cts.devicepolicy;
-import com.android.ddmlib.Log.LogLevel;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.log.LogUtil.CLog;
+import android.platform.test.annotations.FlakyTest;
import java.util.Collections;
@@ -62,6 +60,7 @@
mCurrentUserId, Collections.singletonMap(PARAM_TEST_USER, mSerialNumber));
}
+ @FlakyTest
public void testLauncherCallbackPackageAddedMainUser() throws Exception {
if (!mHasLauncherApps) {
return;
@@ -75,6 +74,7 @@
mCurrentUserId, Collections.singletonMap(PARAM_TEST_USER, mSerialNumber));
}
+ @FlakyTest
public void testLauncherCallbackPackageRemovedMainUser() throws Exception {
if (!mHasLauncherApps) {
return;
@@ -88,6 +88,7 @@
mCurrentUserId, Collections.singletonMap(PARAM_TEST_USER, mSerialNumber));
}
+ @FlakyTest
public void testLauncherCallbackPackageChangedMainUser() throws Exception {
if (!mHasLauncherApps) {
return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileContactsTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileContactsTest.java
new file mode 100644
index 0000000..bf81c28
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileContactsTest.java
@@ -0,0 +1,420 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.devicepolicy;
+
+import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.assertMetricsLogged;
+import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.isStatsdEnabled;
+
+import android.platform.test.annotations.FlakyTest;
+import android.platform.test.annotations.LargeTest;
+import android.stats.devicepolicy.EventId;
+
+import com.android.cts.devicepolicy.metrics.DevicePolicyEventWrapper;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.log.LogUtil;
+
+import java.util.concurrent.Callable;
+
+public class ManagedProfileContactsTest extends BaseManagedProfileTest {
+ private static final String DIRECTORY_PROVIDER_APK = "CtsContactDirectoryProvider.apk";
+ private static final String DIRECTORY_PROVIDER_PKG
+ = "com.android.cts.contactdirectoryprovider";
+ private static final String PRIMARY_DIRECTORY_PREFIX = "Primary";
+ private static final String MANAGED_DIRECTORY_PREFIX = "Managed";
+ private static final String DIRECTORY_PRIVOIDER_URI
+ = "content://com.android.cts.contact.directory.provider/";
+ private static final String SET_CUSTOM_DIRECTORY_PREFIX_METHOD = "set_prefix";
+
+ @LargeTest
+ public void testManagedContactsUris() throws Exception {
+ runManagedContactsTest(() -> {
+ ContactsTestSet contactsTestSet = new ContactsTestSet(ManagedProfileContactsTest.this,
+ MANAGED_PROFILE_PKG, mParentUserId, mProfileUserId);
+
+ contactsTestSet.setCallerIdEnabled(true);
+ contactsTestSet.setContactsSearchEnabled(true);
+ contactsTestSet.checkIfCanLookupEnterpriseContacts(true);
+ contactsTestSet.checkIfCanFilterEnterpriseContacts(true);
+ contactsTestSet.checkIfCanFilterSelfContacts();
+ return null;
+ });
+ }
+
+ @FlakyTest
+ public void testManagedQuickContacts() throws Exception {
+ runManagedContactsTest(() -> {
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+ "testQuickContact", mParentUserId);
+ return null;
+ });
+ }
+
+ @FlakyTest
+ public void testManagedContactsPolicies() throws Exception {
+ runManagedContactsTest(() -> {
+ ContactsTestSet contactsTestSet = new ContactsTestSet(ManagedProfileContactsTest.this,
+ MANAGED_PROFILE_PKG, mParentUserId, mProfileUserId);
+ try {
+ contactsTestSet.setCallerIdEnabled(true);
+ contactsTestSet.setContactsSearchEnabled(false);
+ contactsTestSet.checkIfCanLookupEnterpriseContacts(true);
+ contactsTestSet.checkIfCanFilterEnterpriseContacts(false);
+ contactsTestSet.checkIfCanFilterSelfContacts();
+ contactsTestSet.setCallerIdEnabled(false);
+ contactsTestSet.setContactsSearchEnabled(true);
+ contactsTestSet.checkIfCanLookupEnterpriseContacts(false);
+ contactsTestSet.checkIfCanFilterEnterpriseContacts(true);
+ contactsTestSet.checkIfCanFilterSelfContacts();
+ contactsTestSet.setCallerIdEnabled(false);
+ contactsTestSet.setContactsSearchEnabled(false);
+ contactsTestSet.checkIfCanLookupEnterpriseContacts(false);
+ contactsTestSet.checkIfCanFilterEnterpriseContacts(false);
+ contactsTestSet.checkIfCanFilterSelfContacts();
+ contactsTestSet.checkIfNoEnterpriseDirectoryFound();
+ if (isStatsdEnabled(getDevice())) {
+ assertMetricsLogged(getDevice(), () -> {
+ contactsTestSet.setCallerIdEnabled(true);
+ contactsTestSet.setCallerIdEnabled(false);
+ }, new DevicePolicyEventWrapper
+ .Builder(EventId.SET_CROSS_PROFILE_CALLER_ID_DISABLED_VALUE)
+ .setAdminPackageName(MANAGED_PROFILE_PKG)
+ .setBoolean(false)
+ .build(),
+ new DevicePolicyEventWrapper
+ .Builder(EventId.SET_CROSS_PROFILE_CALLER_ID_DISABLED_VALUE)
+ .setAdminPackageName(MANAGED_PROFILE_PKG)
+ .setBoolean(true)
+ .build());
+ assertMetricsLogged(getDevice(), () -> {
+ contactsTestSet.setContactsSearchEnabled(true);
+ contactsTestSet.setContactsSearchEnabled(false);
+ }, new DevicePolicyEventWrapper
+ .Builder(EventId.SET_CROSS_PROFILE_CONTACTS_SEARCH_DISABLED_VALUE)
+ .setAdminPackageName(MANAGED_PROFILE_PKG)
+ .setBoolean(false)
+ .build(),
+ new DevicePolicyEventWrapper
+ .Builder(
+ EventId.SET_CROSS_PROFILE_CONTACTS_SEARCH_DISABLED_VALUE)
+ .setAdminPackageName(MANAGED_PROFILE_PKG)
+ .setBoolean(true)
+ .build());
+ }
+ return null;
+ } finally {
+ // reset policies
+ contactsTestSet.setCallerIdEnabled(true);
+ contactsTestSet.setContactsSearchEnabled(true);
+ }
+ });
+ }
+
+ private void setDirectoryPrefix(String directoryName, int userId)
+ throws DeviceNotAvailableException {
+ String command = "content call --uri " + DIRECTORY_PRIVOIDER_URI
+ + " --user " + userId
+ + " --method " + SET_CUSTOM_DIRECTORY_PREFIX_METHOD
+ + " --arg " + directoryName;
+ LogUtil.CLog.d("Output for command " + command + ": "
+ + getDevice().executeShellCommand(command));
+ }
+
+ private void runManagedContactsTest(Callable<Void> callable) throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+
+ try {
+ // Allow cross profile contacts search.
+ // TODO test both on and off.
+ getDevice().executeShellCommand(
+ "settings put --user " + mProfileUserId
+ + " secure managed_profile_contact_remote_search 1");
+
+ // Add test account
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+ "testAddTestAccount", mParentUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+ "testAddTestAccount", mProfileUserId);
+
+ // Install directory provider to both primary and managed profile
+ installAppAsUser(DIRECTORY_PROVIDER_APK, USER_ALL);
+ setDirectoryPrefix(PRIMARY_DIRECTORY_PREFIX, mParentUserId);
+ setDirectoryPrefix(MANAGED_DIRECTORY_PREFIX, mProfileUserId);
+
+ // Check enterprise directory API works
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+ "testGetDirectoryListInPrimaryProfile", mParentUserId);
+
+ // Insert Primary profile Contacts
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+ "testPrimaryProfilePhoneAndEmailLookup_insertedAndfound", mParentUserId);
+ // Insert Managed profile Contacts
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+ "testManagedProfilePhoneAndEmailLookup_insertedAndfound", mProfileUserId);
+ // Insert a primary contact with same phone & email as other
+ // enterprise contacts
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+ "testPrimaryProfileDuplicatedPhoneEmailContact_insertedAndfound",
+ mParentUserId);
+ // Insert a enterprise contact with same phone & email as other
+ // primary contacts
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+ "testManagedProfileDuplicatedPhoneEmailContact_insertedAndfound",
+ mProfileUserId);
+
+ callable.call();
+
+ } finally {
+ // Clean up in managed profile and primary profile
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+ "testCurrentProfileContacts_removeContacts", mProfileUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+ "testCurrentProfileContacts_removeContacts", mParentUserId);
+ getDevice().uninstallPackage(DIRECTORY_PROVIDER_PKG);
+ }
+ }
+
+ /*
+ * Container for running ContactsTest under multi-user environment
+ */
+ private static class ContactsTestSet {
+
+ private ManagedProfileContactsTest mManagedProfileContactsTest;
+ private String mManagedProfilePackage;
+ private int mParentUserId;
+ private int mProfileUserId;
+
+ public ContactsTestSet(ManagedProfileContactsTest managedProfileContactsTest,
+ String managedProfilePackage, int parentUserId, int profileUserId) {
+ mManagedProfileContactsTest = managedProfileContactsTest;
+ mManagedProfilePackage = managedProfilePackage;
+ mParentUserId = parentUserId;
+ mProfileUserId = profileUserId;
+ }
+
+ private void runDeviceTestsAsUser(String pkgName, String testClassName,
+ String testMethodName, Integer userId) throws DeviceNotAvailableException {
+ mManagedProfileContactsTest.runDeviceTestsAsUser(pkgName, testClassName, testMethodName,
+ userId);
+ }
+
+ // Enable / Disable
+ public void setCallerIdEnabled(boolean enabled) throws DeviceNotAvailableException {
+ if (enabled) {
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testSetCrossProfileCallerIdDisabled_false", mProfileUserId);
+ } else {
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testSetCrossProfileCallerIdDisabled_true", mProfileUserId);
+ }
+ }
+
+ // Enable / Disable cross profile contacts search
+ public void setContactsSearchEnabled(boolean enabled) throws DeviceNotAvailableException {
+ if (enabled) {
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testSetCrossProfileContactsSearchDisabled_false", mProfileUserId);
+ } else {
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testSetCrossProfileContactsSearchDisabled_true", mProfileUserId);
+ }
+ }
+
+ public void checkIfCanLookupEnterpriseContacts(boolean expected)
+ throws DeviceNotAvailableException {
+ // Primary user cannot use ordinary phone/email lookup api to access
+ // managed contacts
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testPrimaryProfilePhoneLookup_canNotAccessEnterpriseContact", mParentUserId);
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testPrimaryProfileEmailLookup_canNotAccessEnterpriseContact", mParentUserId);
+ // Primary user can use ENTERPRISE_CONTENT_FILTER_URI to access
+ // primary contacts
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testPrimaryProfileEnterprisePhoneLookup_canAccessPrimaryContact",
+ mParentUserId);
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testPrimaryProfileEnterpriseEmailLookup_canAccessPrimaryContact",
+ mParentUserId);
+ // When there exist contacts with the same phone/email in primary &
+ // enterprise,
+ // primary user can use ENTERPRISE_CONTENT_FILTER_URI to access the
+ // primary contact.
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testPrimaryProfileEnterpriseEmailLookupDuplicated_canAccessPrimaryContact",
+ mParentUserId);
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testPrimaryProfileEnterprisePhoneLookupDuplicated_canAccessPrimaryContact",
+ mParentUserId);
+
+ // Managed user cannot use ordinary phone/email lookup api to access
+ // primary contacts
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testManagedProfilePhoneLookup_canNotAccessPrimaryContact", mProfileUserId);
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testManagedProfileEmailLookup_canNotAccessPrimaryContact", mProfileUserId);
+ // Managed user can use ENTERPRISE_CONTENT_FILTER_URI to access
+ // enterprise contacts
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testManagedProfileEnterprisePhoneLookup_canAccessEnterpriseContact",
+ mProfileUserId);
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testManagedProfileEnterpriseEmailLookup_canAccessEnterpriseContact",
+ mProfileUserId);
+ // Managed user cannot use ENTERPRISE_CONTENT_FILTER_URI to access
+ // primary contacts
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testManagedProfileEnterprisePhoneLookup_canNotAccessPrimaryContact",
+ mProfileUserId);
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testManagedProfileEnterpriseEmailLookup_canNotAccessPrimaryContact",
+ mProfileUserId);
+ // When there exist contacts with the same phone/email in primary &
+ // enterprise,
+ // managed user can use ENTERPRISE_CONTENT_FILTER_URI to access the
+ // enterprise contact.
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testManagedProfileEnterpriseEmailLookupDuplicated_canAccessEnterpriseContact",
+ mProfileUserId);
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testManagedProfileEnterprisePhoneLookupDuplicated_canAccessEnterpriseContact",
+ mProfileUserId);
+
+ // Check if phone lookup can access primary directories
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testPrimaryProfileEnterprisePhoneLookup_canAccessPrimaryDirectories",
+ mParentUserId);
+
+ // Check if email lookup can access primary directories
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testPrimaryProfileEnterpriseEmailLookup_canAccessPrimaryDirectories",
+ mParentUserId);
+
+ if (expected) {
+ // Primary user can use ENTERPRISE_CONTENT_FILTER_URI to access
+ // managed profile contacts
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testPrimaryProfileEnterprisePhoneLookup_canAccessEnterpriseContact",
+ mParentUserId);
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testPrimaryProfileEnterpriseEmailLookup_canAccessEnterpriseContact",
+ mParentUserId);
+
+ // Make sure SIP enterprise lookup works too.
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testPrimaryProfileEnterpriseSipLookup_canAccessEnterpriseContact",
+ mParentUserId);
+
+ // Check if phone lookup can access enterprise directories
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testPrimaryProfileEnterprisePhoneLookup_canAccessManagedDirectories",
+ mParentUserId);
+
+ // Check if email lookup can access enterprise directories
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testPrimaryProfileEnterpriseEmailLookup_canAccessManagedDirectories",
+ mParentUserId);
+ } else {
+ // Primary user cannot use ENTERPRISE_CONTENT_FILTER_URI to
+ // access managed contacts
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testPrimaryProfileEnterprisePhoneLookup_canNotAccessEnterpriseContact",
+ mParentUserId);
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testPrimaryProfileEnterprisePhoneLookup_canNotAccessManagedDirectories",
+ mParentUserId);
+
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testPrimaryProfileEnterpriseEmailLookup_canNotAccessManagedDirectories",
+ mParentUserId);
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testPrimaryProfileEnterprisePhoneLookup_canNotAccessManagedDirectories",
+ mParentUserId);
+ }
+ }
+
+ public void checkIfCanFilterSelfContacts() throws DeviceNotAvailableException {
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testPrimaryProfileEnterpriseCallableFilter_canAccessPrimaryDirectories",
+ mParentUserId);
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testManagedProfileEnterpriseCallableFilter_canAccessManagedDirectories",
+ mProfileUserId);
+
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testPrimaryProfileEnterpriseEmailFilter_canAccessPrimaryDirectories",
+ mParentUserId);
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testEnterpriseProfileEnterpriseEmailFilter_canAccessManagedDirectories",
+ mProfileUserId);
+
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testPrimaryProfileEnterpriseContactFilter_canAccessPrimaryDirectories",
+ mParentUserId);
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testManagedProfileEnterpriseContactFilter_canAccessManagedDirectories",
+ mProfileUserId);
+
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testPrimaryProfileEnterprisePhoneFilter_canAccessPrimaryDirectories",
+ mParentUserId);
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testManagedProfileEnterprisePhoneFilter_canAccessManagedDirectories",
+ mProfileUserId);
+ }
+
+ public void checkIfCanFilterEnterpriseContacts(boolean expected)
+ throws DeviceNotAvailableException {
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testFilterUriWhenDirectoryParamMissing", mParentUserId);
+ if (expected) {
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testPrimaryProfileEnterpriseCallableFilter_canAccessManagedDirectories",
+ mParentUserId);
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testPrimaryProfileEnterpriseEmailFilter_canAccessManagedDirectories",
+ mParentUserId);
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testPrimaryProfileEnterpriseContactFilter_canAccessManagedDirectories",
+ mParentUserId);
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testPrimaryProfileEnterprisePhoneFilter_canAccessManagedDirectories",
+ mParentUserId);
+ } else {
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testPrimaryProfileEnterpriseCallableFilter_canNotAccessManagedDirectories",
+ mParentUserId);
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testPrimaryProfileEnterpriseEmailFilter_canNotAccessManagedDirectories",
+ mParentUserId);
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testPrimaryProfileEnterpriseContactFilter_canNotAccessManagedDirectories",
+ mParentUserId);
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testPrimaryProfileEnterprisePhoneFilter_canNotAccessManagedDirectories",
+ mParentUserId);
+ }
+ }
+
+ public void checkIfNoEnterpriseDirectoryFound() throws DeviceNotAvailableException {
+ runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+ "testPrimaryProfileEnterpriseDirectories_canNotAccessManagedDirectories",
+ mParentUserId);
+ }
+ }
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileCrossProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileCrossProfileTest.java
new file mode 100644
index 0000000..ded287c
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileCrossProfileTest.java
@@ -0,0 +1,511 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.devicepolicy;
+
+import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.assertMetricsLogged;
+import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.isStatsdEnabled;
+
+import android.platform.test.annotations.FlakyTest;
+import android.platform.test.annotations.LargeTest;
+import android.stats.devicepolicy.EventId;
+
+import com.android.cts.devicepolicy.metrics.DevicePolicyEventWrapper;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.log.LogUtil;
+
+import java.util.Collections;
+
+public class ManagedProfileCrossProfileTest extends BaseManagedProfileTest {
+ private static final String NOTIFICATION_APK = "CtsNotificationSenderApp.apk";
+ private static final String WIDGET_PROVIDER_APK = "CtsWidgetProviderApp.apk";
+ private static final String WIDGET_PROVIDER_PKG = "com.android.cts.widgetprovider";
+ private static final String PARAM_PROFILE_ID = "profile-id";
+
+ @LargeTest
+ public void testCrossProfileIntentFilters() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ // Set up activities: ManagedProfileActivity will only be enabled in the managed profile and
+ // PrimaryUserActivity only in the primary one
+ disableActivityForUser("ManagedProfileActivity", mParentUserId);
+ disableActivityForUser("PrimaryUserActivity", mProfileUserId);
+
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG,
+ MANAGED_PROFILE_PKG + ".ManagedProfileTest", mProfileUserId);
+
+ if (isStatsdEnabled(getDevice())) {
+ assertMetricsLogged(getDevice(), () -> {
+ runDeviceTestsAsUser(
+ MANAGED_PROFILE_PKG, MANAGED_PROFILE_PKG + ".ManagedProfileTest",
+ "testAddCrossProfileIntentFilter_all", mProfileUserId);
+ }, new DevicePolicyEventWrapper.Builder(EventId.ADD_CROSS_PROFILE_INTENT_FILTER_VALUE)
+ .setAdminPackageName(MANAGED_PROFILE_PKG)
+ .setInt(1)
+ .setStrings("com.android.cts.managedprofile.ACTION_TEST_ALL_ACTIVITY")
+ .build());
+ }
+
+ // Set up filters from primary to managed profile
+ String command = "am start -W --user " + mProfileUserId + " " + MANAGED_PROFILE_PKG
+ + "/.PrimaryUserFilterSetterActivity";
+ LogUtil.CLog.d("Output for command " + command + ": "
+ + getDevice().executeShellCommand(command));
+ runDeviceTestsAsUser(
+ MANAGED_PROFILE_PKG, MANAGED_PROFILE_PKG + ".PrimaryUserTest", mParentUserId);
+ // TODO: Test with startActivity
+ }
+
+ @FlakyTest
+ public void testCrossProfileContent() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+
+ // Storage permission shouldn't be granted, we check if missing permissions are respected
+ // in ContentTest#testSecurity.
+ installAppAsUser(INTENT_SENDER_APK, false /* grantPermissions */, USER_ALL);
+ installAppAsUser(INTENT_RECEIVER_APK, USER_ALL);
+
+ // Test from parent to managed
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
+ "testRemoveAllFilters", mProfileUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
+ "testAddManagedCanAccessParentFilters", mProfileUserId);
+ runDeviceTestsAsUser(INTENT_SENDER_PKG, ".ContentTest", mParentUserId);
+
+ // Test from managed to parent
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
+ "testRemoveAllFilters", mProfileUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
+ "testAddParentCanAccessManagedFilters", mProfileUserId);
+ runDeviceTestsAsUser(INTENT_SENDER_PKG, ".ContentTest", mProfileUserId);
+
+ }
+
+ @FlakyTest
+ public void testCrossProfileNotificationListeners_EmptyWhitelist() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+
+ installAppAsUser(NOTIFICATION_APK, USER_ALL);
+
+ // Profile owner in the profile sets an empty whitelist
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".NotificationListenerTest",
+ "testSetEmptyWhitelist", mProfileUserId,
+ Collections.singletonMap(PARAM_PROFILE_ID, Integer.toString(mProfileUserId)));
+ // Listener outside the profile can only see personal notifications.
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".NotificationListenerTest",
+ "testCannotReceiveProfileNotifications", mParentUserId,
+ Collections.singletonMap(PARAM_PROFILE_ID, Integer.toString(mProfileUserId)));
+ }
+
+ public void testCrossProfileNotificationListeners_NullWhitelist() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+
+ installAppAsUser(NOTIFICATION_APK, USER_ALL);
+
+ // Profile owner in the profile sets a null whitelist
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".NotificationListenerTest",
+ "testSetNullWhitelist", mProfileUserId,
+ Collections.singletonMap(PARAM_PROFILE_ID, Integer.toString(mProfileUserId)));
+ // Listener outside the profile can see profile and personal notifications
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".NotificationListenerTest",
+ "testCanReceiveNotifications", mParentUserId,
+ Collections.singletonMap(PARAM_PROFILE_ID, Integer.toString(mProfileUserId)));
+ }
+
+ public void testCrossProfileNotificationListeners_InWhitelist() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+
+ installAppAsUser(NOTIFICATION_APK, USER_ALL);
+
+ // Profile owner in the profile adds listener to the whitelist
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".NotificationListenerTest",
+ "testAddListenerToWhitelist", mProfileUserId,
+ Collections.singletonMap(PARAM_PROFILE_ID, Integer.toString(mProfileUserId)));
+ // Listener outside the profile can see profile and personal notifications
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".NotificationListenerTest",
+ "testCanReceiveNotifications", mParentUserId,
+ Collections.singletonMap(PARAM_PROFILE_ID, Integer.toString(mProfileUserId)));
+ }
+
+ public void testCrossProfileNotificationListeners_setAndGet() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ installAppAsUser(NOTIFICATION_APK, USER_ALL);
+
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".NotificationListenerTest",
+ "testSetAndGetPermittedCrossProfileNotificationListeners", mProfileUserId,
+ Collections.singletonMap(PARAM_PROFILE_ID, Integer.toString(mProfileUserId)));
+ }
+
+ @FlakyTest
+ public void testCrossProfileCopyPaste() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ installAppAsUser(INTENT_RECEIVER_APK, USER_ALL);
+ installAppAsUser(INTENT_SENDER_APK, USER_ALL);
+
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
+ "testAllowCrossProfileCopyPaste", mProfileUserId);
+ // Test that managed can see what is copied in the parent.
+ testCrossProfileCopyPasteInternal(mProfileUserId, true);
+ // Test that the parent can see what is copied in managed.
+ testCrossProfileCopyPasteInternal(mParentUserId, true);
+
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
+ "testDisallowCrossProfileCopyPaste", mProfileUserId);
+ // Test that managed can still see what is copied in the parent.
+ testCrossProfileCopyPasteInternal(mProfileUserId, true);
+ // Test that the parent cannot see what is copied in managed.
+ testCrossProfileCopyPasteInternal(mParentUserId, false);
+ }
+
+ private void testCrossProfileCopyPasteInternal(int userId, boolean shouldSucceed)
+ throws DeviceNotAvailableException {
+ final String direction = (userId == mParentUserId)
+ ? "testAddManagedCanAccessParentFilters"
+ : "testAddParentCanAccessManagedFilters";
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
+ "testRemoveAllFilters", mProfileUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
+ direction, mProfileUserId);
+ if (shouldSucceed) {
+ runDeviceTestsAsUser(INTENT_SENDER_PKG, ".CopyPasteTest",
+ "testCanReadAcrossProfiles", userId);
+ } else {
+ runDeviceTestsAsUser(INTENT_SENDER_PKG, ".CopyPasteTest",
+ "testCannotReadAcrossProfiles", userId);
+ }
+ }
+
+ @FlakyTest
+ public void testCrossProfileWidgets() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+
+ try {
+ installAppAsUser(WIDGET_PROVIDER_APK, USER_ALL);
+ getDevice().executeShellCommand("appwidget grantbind --user " + mParentUserId
+ + " --package " + WIDGET_PROVIDER_PKG);
+ setIdleWhitelist(WIDGET_PROVIDER_PKG, true);
+ startWidgetHostService();
+
+ String commandOutput = changeCrossProfileWidgetForUser(WIDGET_PROVIDER_PKG,
+ "add-cross-profile-widget", mProfileUserId);
+ assertTrue("Command was expected to succeed " + commandOutput,
+ commandOutput.contains("Status: ok"));
+
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileWidgetTest",
+ "testCrossProfileWidgetProviderAdded", mProfileUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG,
+ ".CrossProfileWidgetPrimaryUserTest",
+ "testHasCrossProfileWidgetProvider_true", mParentUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG,
+ ".CrossProfileWidgetPrimaryUserTest",
+ "testHostReceivesWidgetUpdates_true", mParentUserId);
+
+ commandOutput = changeCrossProfileWidgetForUser(WIDGET_PROVIDER_PKG,
+ "remove-cross-profile-widget", mProfileUserId);
+ assertTrue("Command was expected to succeed " + commandOutput,
+ commandOutput.contains("Status: ok"));
+
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileWidgetTest",
+ "testCrossProfileWidgetProviderRemoved", mProfileUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG,
+ ".CrossProfileWidgetPrimaryUserTest",
+ "testHasCrossProfileWidgetProvider_false", mParentUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG,
+ ".CrossProfileWidgetPrimaryUserTest",
+ "testHostReceivesWidgetUpdates_false", mParentUserId);
+ } finally {
+ changeCrossProfileWidgetForUser(WIDGET_PROVIDER_PKG, "remove-cross-profile-widget",
+ mProfileUserId);
+ getDevice().uninstallPackage(WIDGET_PROVIDER_PKG);
+ }
+ }
+
+ @FlakyTest
+ public void testCrossProfileWidgetsLogged() throws Exception {
+ if (!mHasFeature || !isStatsdEnabled(getDevice())) {
+ return;
+ }
+
+ try {
+ installAppAsUser(WIDGET_PROVIDER_APK, USER_ALL);
+ getDevice().executeShellCommand("appwidget grantbind --user " + mParentUserId
+ + " --package " + WIDGET_PROVIDER_PKG);
+ setIdleWhitelist(WIDGET_PROVIDER_PKG, true);
+ startWidgetHostService();
+
+ assertMetricsLogged(getDevice(), () -> {
+ changeCrossProfileWidgetForUser(WIDGET_PROVIDER_PKG,
+ "add-cross-profile-widget", mProfileUserId);
+ changeCrossProfileWidgetForUser(WIDGET_PROVIDER_PKG,
+ "remove-cross-profile-widget", mProfileUserId);
+ }, new DevicePolicyEventWrapper
+ .Builder(EventId.ADD_CROSS_PROFILE_WIDGET_PROVIDER_VALUE)
+ .setAdminPackageName(MANAGED_PROFILE_PKG)
+ .build(),
+ new DevicePolicyEventWrapper
+ .Builder(EventId.REMOVE_CROSS_PROFILE_WIDGET_PROVIDER_VALUE)
+ .setAdminPackageName(MANAGED_PROFILE_PKG)
+ .build());
+ } finally {
+ changeCrossProfileWidgetForUser(WIDGET_PROVIDER_PKG, "remove-cross-profile-widget",
+ mProfileUserId);
+ getDevice().uninstallPackage(WIDGET_PROVIDER_PKG);
+ }
+ }
+
+ public void testCrossProfileCalendarPackage() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ assertMetricsLogged(getDevice(), () -> {
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testCrossProfileCalendarPackage", mProfileUserId);
+ }, new DevicePolicyEventWrapper.Builder(EventId.SET_CROSS_PROFILE_CALENDAR_PACKAGES_VALUE)
+ .setAdminPackageName(MANAGED_PROFILE_PKG)
+ .setStrings(MANAGED_PROFILE_PKG)
+ .build());
+ }
+
+ @FlakyTest
+ public void testCrossProfileCalendar() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ runCrossProfileCalendarTestsWhenWhitelistedAndEnabled();
+ runCrossProfileCalendarTestsWhenAllPackagesWhitelisted();
+ runCrossProfileCalendarTestsWhenDisabled();
+ runCrossProfileCalendarTestsWhenNotWhitelisted();
+ }
+
+ @FlakyTest
+ public void testDisallowSharingIntoPersonalFromProfile() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ // Set up activities: PrimaryUserActivity will only be enabled in the personal user
+ // This activity is used to find out the ground truth about the system's cross profile
+ // intent forwarding activity.
+ disableActivityForUser("PrimaryUserActivity", mProfileUserId);
+
+ // Tests from the profile side
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG,
+ ".DisallowSharingIntoProfileTest", "testSharingFromProfile", mProfileUserId);
+ }
+
+ public void testDisallowSharingIntoProfileFromPersonal() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ // Set up activities: ManagedProfileActivity will only be enabled in the managed profile
+ // This activity is used to find out the ground truth about the system's cross profile
+ // intent forwarding activity.
+ disableActivityForUser("ManagedProfileActivity", mParentUserId);
+
+ // Tests from the personal side, which is mostly driven from host side.
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".DisallowSharingIntoProfileTest",
+ "testSetUp", mProfileUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".DisallowSharingIntoProfileTest",
+ "testDisableSharingIntoProfile", mProfileUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".DisallowSharingIntoProfileTest",
+ "testSharingFromPersonalFails", mParentUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".DisallowSharingIntoProfileTest",
+ "testEnableSharingIntoProfile", mProfileUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".DisallowSharingIntoProfileTest",
+ "testSharingFromPersonalSucceeds", mParentUserId);
+ }
+
+ private void runCrossProfileCalendarTestsWhenWhitelistedAndEnabled() throws Exception {
+ try {
+ // Setup. Add the test package into cross-profile calendar whitelist, enable
+ // cross-profile calendar in settings, and insert test data into calendar provider.
+ // All setups should be done in managed profile.
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testWhitelistManagedProfilePackage", mProfileUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testAddTestCalendarDataForWorkProfile", mProfileUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testEnableCrossProfileCalendarSettings", mProfileUserId);
+
+ // Testing.
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testPrimaryProfile_getCorrectWorkCalendarsWhenEnabled", mParentUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testPrimaryProfile_getCorrectWorkEventsWhenEnabled", mParentUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testPrimaryProfile_getCorrectWorkInstancesWhenEnabled", mParentUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testPrimaryProfile_getCorrectWorkInstancesByDayWhenEnabled", mParentUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testPrimaryProfile_canAccessWorkInstancesSearch1", mParentUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testPrimaryProfile_canAccessWorkInstancesSearch2", mParentUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testPrimaryProfile_canAccessWorkInstancesSearchByDay", mParentUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testPrimaryProfile_getExceptionWhenQueryNonWhitelistedColumns", mParentUserId);
+ } finally {
+ // Cleanup.
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testCleanupWhitelist", mProfileUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testCleanupTestCalendarDataForWorkProfile", mProfileUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testDisableCrossProfileCalendarSettings", mProfileUserId);
+ }
+ }
+
+ private void runCrossProfileCalendarTestsWhenAllPackagesWhitelisted() throws Exception {
+ try {
+ // Setup. Allow all packages to access cross-profile calendar APIs by setting
+ // the whitelist to null, enable cross-profile calendar in settings,
+ // and insert test data into calendar provider.
+ // All setups should be done in managed profile.
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testWhitelistAllPackages", mProfileUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testAddTestCalendarDataForWorkProfile", mProfileUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testEnableCrossProfileCalendarSettings", mProfileUserId);
+
+ // Testing.
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testPrimaryProfile_getCorrectWorkCalendarsWhenEnabled", mParentUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testPrimaryProfile_getCorrectWorkEventsWhenEnabled", mParentUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testPrimaryProfile_getCorrectWorkInstancesWhenEnabled", mParentUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testPrimaryProfile_getCorrectWorkInstancesByDayWhenEnabled", mParentUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testPrimaryProfile_canAccessWorkInstancesSearch1", mParentUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testPrimaryProfile_canAccessWorkInstancesSearch2", mParentUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testPrimaryProfile_canAccessWorkInstancesSearchByDay", mParentUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testPrimaryProfile_getExceptionWhenQueryNonWhitelistedColumns", mParentUserId);
+ } finally {
+ // Cleanup.
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testCleanupWhitelist", mProfileUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testCleanupTestCalendarDataForWorkProfile", mProfileUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testDisableCrossProfileCalendarSettings", mProfileUserId);
+ }
+ }
+
+ private void runCrossProfileCalendarTestsWhenDisabled() throws Exception {
+ try {
+ // Setup. Add the test package into cross-profile calendar whitelist,
+ // and insert test data into calendar provider. But disable cross-profile calendar
+ // in settings. Thus cross-profile calendar Uris should not be accessible.
+ // All setups should be done in managed profile.
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testWhitelistManagedProfilePackage", mProfileUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testAddTestCalendarDataForWorkProfile", mProfileUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testDisableCrossProfileCalendarSettings", mProfileUserId);
+
+ // Testing.
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testPrimaryProfile_cannotAccessWorkCalendarsWhenDisabled", mParentUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testPrimaryProfile_cannotAccessWorkEventsWhenDisabled", mParentUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testPrimaryProfile_cannotAccessWorkInstancesWhenDisabled", mParentUserId);
+ } finally {
+ // Cleanup.
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testCleanupWhitelist", mProfileUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testCleanupTestCalendarDataForWorkProfile", mProfileUserId);
+ }
+ }
+
+ private void runCrossProfileCalendarTestsWhenNotWhitelisted() throws Exception {
+ try {
+ // Setup. Enable cross-profile calendar in settings and insert test data into calendar
+ // provider. But make sure that the test package is not whitelisted for cross-profile
+ // calendar. Thus cross-profile calendar Uris should not be accessible.
+ // All setups should be done in managed profile.
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testAddTestCalendarDataForWorkProfile", mProfileUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testEnableCrossProfileCalendarSettings", mProfileUserId);
+
+ // Testing.
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testPrimaryProfile_cannotAccessWorkCalendarsWhenDisabled", mParentUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testPrimaryProfile_cannotAccessWorkEventsWhenDisabled", mParentUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testPrimaryProfile_cannotAccessWorkInstancesWhenDisabled", mParentUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testViewEventCrossProfile_intentFailedWhenNotWhitelisted", mParentUserId);
+ } finally {
+ // Cleanup.
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testCleanupTestCalendarDataForWorkProfile", mProfileUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
+ "testDisableCrossProfileCalendarSettings", mProfileUserId);
+ }
+ }
+
+ private void setIdleWhitelist(String packageName, boolean enabled)
+ throws DeviceNotAvailableException {
+ String command = "cmd deviceidle whitelist " + (enabled ? "+" : "-") + packageName;
+ LogUtil.CLog.d("Output for command " + command + ": "
+ + getDevice().executeShellCommand(command));
+ }
+
+ private String changeCrossProfileWidgetForUser(String packageName, String command, int userId)
+ throws DeviceNotAvailableException {
+ String adbCommand = "am start -W --user " + userId
+ + " -c android.intent.category.DEFAULT "
+ + " --es extra-command " + command
+ + " --es extra-package-name " + packageName
+ + " " + MANAGED_PROFILE_PKG + "/.SetPolicyActivity";
+ String commandOutput = getDevice().executeShellCommand(adbCommand);
+ LogUtil.CLog.d("Output for command " + adbCommand + ": " + commandOutput);
+ return commandOutput;
+ }
+
+ private void startWidgetHostService() throws Exception {
+ String command = "am startservice --user " + mParentUserId
+ + " -a " + WIDGET_PROVIDER_PKG + ".REGISTER_CALLBACK "
+ + "--ei user-extra " + getUserSerialNumber(mProfileUserId)
+ + " " + WIDGET_PROVIDER_PKG + "/.SimpleAppWidgetHostService";
+ LogUtil.CLog.d("Output for command " + command + ": "
+ + getDevice().executeShellCommand(command));
+ }
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfilePasswordTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfilePasswordTest.java
new file mode 100644
index 0000000..ce0bbc0a
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfilePasswordTest.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.devicepolicy;
+
+import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.assertMetricsLogged;
+import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.isStatsdEnabled;
+
+import android.platform.test.annotations.FlakyTest;
+import android.platform.test.annotations.LargeTest;
+import android.stats.devicepolicy.EventId;
+
+import com.android.cts.devicepolicy.annotations.LockSettingsTest;
+import com.android.cts.devicepolicy.metrics.DevicePolicyEventWrapper;
+import com.android.tradefed.device.DeviceNotAvailableException;
+
+import java.util.concurrent.TimeUnit;
+
+public class ManagedProfilePasswordTest extends BaseManagedProfileTest {
+ private static final String USER_STATE_LOCKED = "RUNNING_LOCKED";
+ private static final long TIMEOUT_USER_LOCKED_MILLIS = TimeUnit.MINUTES.toMillis(2);
+ // Password needs to be in sync with ResetPasswordWithTokenTest.PASSWORD1
+ private static final String RESET_PASSWORD_TEST_DEFAULT_PASSWORD = "123456";
+
+ @FlakyTest
+ public void testLockNowWithKeyEviction() throws Exception {
+ if (!mHasFeature || !mSupportsFbe || !mHasSecureLockScreen) {
+ return;
+ }
+ changeUserCredential("1234", null, mProfileUserId);
+ lockProfile();
+ }
+
+ public void testPasswordMinimumRestrictions() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".PasswordMinimumRestrictionsTest",
+ mProfileUserId);
+ }
+
+ @FlakyTest
+ public void testResetPasswordWithTokenBeforeUnlock() throws Exception {
+ if (!mHasFeature || !mSupportsFbe || !mHasSecureLockScreen) {
+ return;
+ }
+
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ResetPasswordWithTokenTest",
+ "testSetupWorkProfile", mProfileUserId);
+ lockProfile();
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ResetPasswordWithTokenTest",
+ "testResetPasswordBeforeUnlock", mProfileUserId);
+ // Password needs to be in sync with ResetPasswordWithTokenTest.PASSWORD1
+ verifyUserCredential(RESET_PASSWORD_TEST_DEFAULT_PASSWORD, mProfileUserId);
+ }
+
+ @FlakyTest
+ public void testClearPasswordWithTokenBeforeUnlock() throws Exception {
+ if (!mHasFeature || !mSupportsFbe || !mHasSecureLockScreen) {
+ return;
+ }
+
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ResetPasswordWithTokenTest",
+ "testSetupWorkProfile", mProfileUserId);
+ lockProfile();
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ResetPasswordWithTokenTest",
+ "testClearPasswordBeforeUnlock", mProfileUserId);
+ // Make sure profile has no password
+ verifyUserCredential("", mProfileUserId);
+ }
+
+ /**
+ * Test password reset token is still functional after the primary user clears and
+ * re-adds back its device lock. This is to detect a regression where the work profile
+ * undergoes an untrusted credential reset (causing synthetic password to change, invalidating
+ * existing password reset token) if it has unified work challenge and the primary user clears
+ * the device lock.
+ */
+ @FlakyTest
+ public void testResetPasswordTokenUsableAfterClearingLock() throws Exception {
+ if (!mHasFeature || !mSupportsFbe || !mHasSecureLockScreen) {
+ return;
+ }
+ final String devicePassword = "1234";
+
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ResetPasswordWithTokenTest",
+ "testSetResetPasswordToken", mProfileUserId);
+ try {
+ changeUserCredential(devicePassword, null, mParentUserId);
+ changeUserCredential(null, devicePassword, mParentUserId);
+ changeUserCredential(devicePassword, null, mParentUserId);
+ lockProfile();
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ResetPasswordWithTokenTest",
+ "testResetPasswordBeforeUnlock", mProfileUserId);
+ verifyUserCredential(RESET_PASSWORD_TEST_DEFAULT_PASSWORD, mProfileUserId);
+ } finally {
+ changeUserCredential(null, devicePassword, mParentUserId);
+ // Cycle the device screen to flush stale password information from keyguard,
+ // otherwise it will still ask for the non-existent password.
+ // return screen to be on for cts test runs
+ executeShellCommand("input keyevent KEYCODE_WAKEUP");
+ executeShellCommand("input keyevent KEYCODE_SLEEP");
+ executeShellCommand("input keyevent KEYCODE_WAKEUP");
+ }
+ }
+
+ @LockSettingsTest
+ public void testIsUsingUnifiedPassword() throws Exception {
+ if (!mHasFeature || !mHasSecureLockScreen) {
+ return;
+ }
+
+ // Freshly created profile has no separate challenge.
+ verifyUnifiedPassword(true);
+
+ // Set separate challenge and verify that the API reports it correctly.
+ changeUserCredential("1234" /* newCredential */, null /* oldCredential */, mProfileUserId);
+ verifyUnifiedPassword(false);
+ }
+
+ @FlakyTest
+ @LargeTest
+ @LockSettingsTest
+ public void testUnlockWorkProfile_deviceWidePassword() throws Exception {
+ if (!mHasFeature || !mSupportsFbe || !mHasSecureLockScreen) {
+ return;
+ }
+ String password = "0000";
+ try {
+ // Add a device password after the work profile has been created.
+ changeUserCredential(password, /* oldCredential= */ null, mPrimaryUserId);
+ // Lock the profile with key eviction.
+ lockProfile();
+ // Turn on work profile, by unlocking the profile with the device password.
+ verifyUserCredential(password, mPrimaryUserId);
+
+ // Verify profile user is running unlocked by running a sanity test on the work profile.
+ installAppAsUser(SIMPLE_APP_APK, mProfileUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".SanityTest", mProfileUserId);
+ } finally {
+ // Clean up
+ changeUserCredential(/* newCredential= */ null, password, mPrimaryUserId);
+ }
+ }
+
+ @FlakyTest
+ @LargeTest
+ @LockSettingsTest
+ public void testRebootDevice_unifiedPassword() throws Exception {
+ if (!mHasFeature || !mHasSecureLockScreen) {
+ return;
+ }
+ // Waiting before rebooting prevents flakiness.
+ waitForBroadcastIdle();
+ String password = "0000";
+ changeUserCredential(password, /* oldCredential= */ null, mPrimaryUserId);
+ try {
+ rebootAndWaitUntilReady();
+ verifyUserCredential(password, mPrimaryUserId);
+ installAppAsUser(SIMPLE_APP_APK, mProfileUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".SanityTest", mProfileUserId);
+ } finally {
+ changeUserCredential(/* newCredential= */ null, password, mPrimaryUserId);
+ // Work-around for http://b/113866275 - password prompt being erroneously shown at the
+ // end.
+ pressPowerButton();
+ }
+ }
+
+ @LargeTest
+ @LockSettingsTest
+ public void testRebootDevice_separatePasswords() throws Exception {
+ if (!mHasFeature || !mHasSecureLockScreen) {
+ return;
+ }
+ // Waiting before rebooting prevents flakiness.
+ waitForBroadcastIdle();
+ String profilePassword = "profile";
+ String primaryPassword = "primary";
+ int managedProfileUserId = getFirstManagedProfileUserId();
+ changeUserCredential(
+ profilePassword, /* oldCredential= */ null, managedProfileUserId);
+ changeUserCredential(primaryPassword, /* oldCredential= */ null, mPrimaryUserId);
+ try {
+ rebootAndWaitUntilReady();
+ verifyUserCredential(profilePassword, managedProfileUserId);
+ verifyUserCredential(primaryPassword, mPrimaryUserId);
+ installAppAsUser(SIMPLE_APP_APK, mProfileUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".SanityTest", mProfileUserId);
+ } finally {
+ changeUserCredential(
+ /* newCredential= */ null, profilePassword, managedProfileUserId);
+ changeUserCredential(/* newCredential= */ null, primaryPassword, mPrimaryUserId);
+ // Work-around for http://b/113866275 - password prompt being erroneously shown at the
+ // end.
+ pressPowerButton();
+ }
+ }
+
+ public void testCreateSeparateChallengeChangedLogged() throws Exception {
+ if (!mHasFeature || !mHasSecureLockScreen || !isStatsdEnabled(getDevice())) {
+ return;
+ }
+ assertMetricsLogged(getDevice(), () -> {
+ changeUserCredential(
+ "1234" /* newCredential */, null /* oldCredential */, mProfileUserId);
+ }, new DevicePolicyEventWrapper.Builder(EventId.SEPARATE_PROFILE_CHALLENGE_CHANGED_VALUE)
+ .setBoolean(true)
+ .build());
+ }
+
+ private void verifyUnifiedPassword(boolean unified) throws DeviceNotAvailableException {
+ final String testMethod =
+ unified ? "testUsingUnifiedPassword" : "testNotUsingUnifiedPassword";
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".IsUsingUnifiedPasswordTest",
+ testMethod, mProfileUserId);
+ }
+
+ private void lockProfile() throws Exception {
+ final String cmd = "am broadcast --receiver-foreground --user " + mProfileUserId
+ + " -a com.android.cts.managedprofile.LOCK_PROFILE"
+ + " com.android.cts.managedprofile/.LockProfileReceiver";
+ getDevice().executeShellCommand(cmd);
+ waitUntilProfileLocked();
+ }
+
+ private void waitUntilProfileLocked() throws Exception {
+ final String cmd = String.format("am get-started-user-state %d", mProfileUserId);
+ tryWaitForSuccess(
+ () -> getDevice().executeShellCommand(cmd).startsWith(USER_STATE_LOCKED),
+ "The managed profile has not been locked after calling "
+ + "lockNow(FLAG_SECURE_USER_DATA)",
+ TIMEOUT_USER_LOCKED_MILLIS);
+ }
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileProvisioningSingleAdminTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileProvisioningSingleAdminTest.java
index b71c4c87..eafb72b 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileProvisioningSingleAdminTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileProvisioningSingleAdminTest.java
@@ -15,6 +15,8 @@
*/
package com.android.cts.devicepolicy;
+import android.platform.test.annotations.FlakyTest;
+
/**
* This class tests the provisioning flow with an APK that declares a single receiver with
* BIND_DEVICE_ADMIN permissions, which was a requirement for the app sending the
@@ -52,6 +54,7 @@
super.tearDown();
}
+ @FlakyTest
public void testEXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME() throws Exception {
if (!mHasFeature) {
return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileProvisioningTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileProvisioningTest.java
index 93a17e0..263c7f0 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileProvisioningTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileProvisioningTest.java
@@ -15,6 +15,8 @@
*/
package com.android.cts.devicepolicy;
+import android.platform.test.annotations.FlakyTest;
+
public class ManagedProfileProvisioningTest extends BaseDevicePolicyTest {
private static final String MANAGED_PROFILE_PKG = "com.android.cts.managedprofile";
private static final String MANAGED_PROFILE_APK = "CtsManagedProfileApp.apk";
@@ -49,7 +51,7 @@
}
super.tearDown();
}
-
+ @FlakyTest(bugId = 141747631)
public void testManagedProfileProvisioning() throws Exception {
if (!mHasFeature) {
return;
@@ -61,6 +63,7 @@
"testIsManagedProfile", mProfileUserId);
}
+ @FlakyTest(bugId = 127275983)
public void testEXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE() throws Exception {
if (!mHasFeature) {
return;
@@ -72,6 +75,7 @@
"testVerifyAdminExtraBundle", mProfileUserId);
}
+ @FlakyTest(bugId = 141747631)
public void testVerifySuccessfulIntentWasReceived() throws Exception {
if (!mHasFeature) {
return;
@@ -83,6 +87,7 @@
"testVerifySuccessfulIntentWasReceived", mProfileUserId);
}
+ @FlakyTest(bugId = 141747631)
public void testAccountMigration() throws Exception {
if (!mHasFeature) {
return;
@@ -97,6 +102,7 @@
"testAccountNotExist", mParentUserId);
}
+ @FlakyTest(bugId = 141747631)
public void testAccountCopy() throws Exception {
if (!mHasFeature) {
return;
@@ -111,6 +117,7 @@
"testAccountExist", mParentUserId);
}
+ @FlakyTest(bugId = 141747631)
public void testWebview() throws Exception {
if (!mHasFeature) {
return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileRingtoneTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileRingtoneTest.java
new file mode 100644
index 0000000..03af77a
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileRingtoneTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.devicepolicy;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.log.LogUtil.CLog;
+
+public class ManagedProfileRingtoneTest extends BaseManagedProfileTest {
+ public void testRingtoneSync() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ givePackageWriteSettingsPermission(mProfileUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".RingtoneSyncTest",
+ "testRingtoneSync", mProfileUserId);
+ }
+
+ // Test if setting RINGTONE disables sync
+ public void testRingtoneSyncAutoDisableRingtone() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ givePackageWriteSettingsPermission(mProfileUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".RingtoneSyncTest",
+ "testRingtoneDisableSync", mProfileUserId);
+ }
+
+ // Test if setting NOTIFICATION disables sync
+ public void testRingtoneSyncAutoDisableNotification() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ givePackageWriteSettingsPermission(mProfileUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".RingtoneSyncTest",
+ "testNotificationDisableSync", mProfileUserId);
+ }
+
+ // Test if setting ALARM disables sync
+ public void testRingtoneSyncAutoDisableAlarm() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ givePackageWriteSettingsPermission(mProfileUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".RingtoneSyncTest",
+ "testAlarmDisableSync", mProfileUserId);
+ }
+
+ private void givePackageWriteSettingsPermission(int userId) throws DeviceNotAvailableException {
+ // Allow app to write to settings (for RingtoneManager.setActualDefaultUri to work)
+ String command = "appops set --user " + userId + " " + MANAGED_PROFILE_PKG
+ + " android:write_settings allow";
+ CLog.d("Output for command " + command + ": " + getDevice().executeShellCommand(command));
+ }
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
index 3f7bd9d..433255b 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
@@ -13,12 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package com.android.cts.devicepolicy;
import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.assertMetricsLogged;
+import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.isStatsdEnabled;
-
+import android.platform.test.annotations.LargeTest;
import android.stats.devicepolicy.EventId;
import com.android.cts.devicepolicy.metrics.DevicePolicyEventWrapper;
@@ -28,130 +28,21 @@
import junit.framework.AssertionFailedError;
-import java.util.Collections;
-import java.util.concurrent.Callable;
-import java.util.concurrent.TimeUnit;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
/**
* Set of tests for Managed Profile use cases.
*/
-public class ManagedProfileTest extends BaseDevicePolicyTest {
+public class ManagedProfileTest extends BaseManagedProfileTest {
- private static final String MANAGED_PROFILE_PKG = "com.android.cts.managedprofile";
- private static final String MANAGED_PROFILE_APK = "CtsManagedProfileApp.apk";
-
+ private static final String FEATURE_TELEPHONY = "android.hardware.telephony";
+ private static final String FEATURE_BLUETOOTH = "android.hardware.bluetooth";
+ private static final String FEATURE_CAMERA = "android.hardware.camera";
+ private static final String FEATURE_WIFI = "android.hardware.wifi";
+ private static final String FEATURE_CONNECTION_SERVICE = "android.software.connectionservice";
private static final String DEVICE_OWNER_PKG = "com.android.cts.deviceowner";
private static final String DEVICE_OWNER_APK = "CtsDeviceOwnerApp.apk";
private static final String DEVICE_OWNER_ADMIN =
DEVICE_OWNER_PKG + ".BaseDeviceOwnerTest$BasicAdminReceiver";
- private static final String INTENT_SENDER_PKG = "com.android.cts.intent.sender";
- private static final String INTENT_SENDER_APK = "CtsIntentSenderApp.apk";
-
- private static final String INTENT_RECEIVER_PKG = "com.android.cts.intent.receiver";
- private static final String INTENT_RECEIVER_APK = "CtsIntentReceiverApp.apk";
-
- private static final String WIDGET_PROVIDER_APK = "CtsWidgetProviderApp.apk";
- private static final String WIDGET_PROVIDER_PKG = "com.android.cts.widgetprovider";
-
- private static final String DIRECTORY_PROVIDER_APK = "CtsContactDirectoryProvider.apk";
- private static final String DIRECTORY_PROVIDER_PKG
- = "com.android.cts.contactdirectoryprovider";
- private static final String PRIMARY_DIRECTORY_PREFIX = "Primary";
- private static final String MANAGED_DIRECTORY_PREFIX = "Managed";
- private static final String DIRECTORY_PRIVOIDER_URI
- = "content://com.android.cts.contact.directory.provider/";
- private static final String SET_CUSTOM_DIRECTORY_PREFIX_METHOD = "set_prefix";
-
- private static final String NOTIFICATION_APK = "CtsNotificationSenderApp.apk";
- private static final String NOTIFICATION_PKG =
- "com.android.cts.managedprofiletests.notificationsender";
-
- private static final String ADMIN_RECEIVER_TEST_CLASS =
- MANAGED_PROFILE_PKG + ".BaseManagedProfileTest$BasicAdminReceiver";
-
- private static final String FEATURE_BLUETOOTH = "android.hardware.bluetooth";
- private static final String FEATURE_CAMERA = "android.hardware.camera";
- private static final String FEATURE_WIFI = "android.hardware.wifi";
- private static final String FEATURE_TELEPHONY = "android.hardware.telephony";
- private static final String FEATURE_CONNECTION_SERVICE = "android.software.connectionservice";
-
- private static final String SIMPLE_APP_APK = "CtsSimpleApp.apk";
-
- private static final long TIMEOUT_USER_LOCKED_MILLIS = TimeUnit.MINUTES.toMillis(2);
-
- private static final String PARAM_PROFILE_ID = "profile-id";
-
- // Password needs to be in sync with ResetPasswordWithTokenTest.PASSWORD1
- private static final String RESET_PASSWORD_TEST_DEFAULT_PASSWORD = "123456";
-
- private static final String PROFILE_CREDENTIAL = "1234";
- // This should be sufficiently larger than ProfileTimeoutTestHelper.TIMEOUT_MS
- private static final int PROFILE_TIMEOUT_DELAY_MS = 40_000;
-
- //The maximum time to wait for user to be unlocked.
- private static final long USER_UNLOCK_TIMEOUT_NANO = 30_000_000_000L;
-
- private static final String USER_UNLOCKED_SHELL_OUTPUT = "RUNNING_UNLOCKED";
-
- private int mParentUserId;
-
- // ID of the profile we'll create. This will always be a profile of the parent.
- private int mProfileUserId;
-
- private boolean mHasNfcFeature;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
- // We need multi user to be supported in order to create a profile of the user owner.
- mHasFeature = mHasFeature && hasDeviceFeature("android.software.managed_users");
- mHasNfcFeature = hasDeviceFeature("android.hardware.nfc")
- && hasDeviceFeature("android.sofware.nfc.beam");
-
- if (mHasFeature) {
- removeTestUsers();
- mParentUserId = mPrimaryUserId;
- mProfileUserId = createManagedProfile(mParentUserId);
- startUser(mProfileUserId);
-
- // Install the APK on both primary and profile user in one single transaction.
- // If they were installed separately, the second installation would become an app
- // update and result in the current running test process being killed.
- installAppAsUser(MANAGED_PROFILE_APK, USER_ALL);
- setProfileOwnerOrFail(MANAGED_PROFILE_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS,
- mProfileUserId);
- waitForUserUnlock();
- }
- }
-
- private void waitForUserUnlock() throws Exception {
- final String command = String.format("am get-started-user-state %d", mProfileUserId);
- final long deadline = System.nanoTime() + USER_UNLOCK_TIMEOUT_NANO;
- while (System.nanoTime() <= deadline) {
- if (getDevice().executeShellCommand(command).startsWith(USER_UNLOCKED_SHELL_OUTPUT)) {
- return;
- }
- Thread.sleep(100);
- }
- fail("Profile user is not unlocked.");
- }
-
- @Override
- protected void tearDown() throws Exception {
- if (mHasFeature) {
- removeUser(mProfileUserId);
- getDevice().uninstallPackage(MANAGED_PROFILE_PKG);
- getDevice().uninstallPackage(INTENT_SENDER_PKG);
- getDevice().uninstallPackage(INTENT_RECEIVER_PKG);
- getDevice().uninstallPackage(NOTIFICATION_PKG);
- }
- super.tearDown();
- }
-
public void testManagedProfilesSupportedWithLockScreenOnly() throws Exception {
if (mHasFeature) {
// Managed profiles should be only supported if the device supports the secure lock
@@ -169,210 +60,6 @@
mProfileUserId);
}
- public void testWipeDataWithReason() throws Exception {
- if (!mHasFeature) {
- return;
- }
- assertTrue(listUsers().contains(mProfileUserId));
- sendWipeProfileBroadcast("com.android.cts.managedprofile.WIPE_DATA_WITH_REASON");
- // Note: the managed profile is removed by this test, which will make removeUserCommand in
- // tearDown() to complain, but that should be OK since its result is not asserted.
- assertUserGetsRemoved(mProfileUserId);
- // testWipeDataWithReason() removes the managed profile,
- // so it needs to separated from other tests.
- // Check and clear the notification is presented after work profile got removed, so profile
- // user no longer exists, verification should be run in primary user.
- runDeviceTestsAsUser(
- MANAGED_PROFILE_PKG,
- ".WipeDataNotificationTest",
- "testWipeDataWithReasonVerification",
- mParentUserId);
- }
-
- public void testWipeDataLogged() throws Exception {
- if (!mHasFeature) {
- return;
- }
- assertTrue(listUsers().contains(mProfileUserId));
- assertMetricsLogged(getDevice(), () -> {
- sendWipeProfileBroadcast("com.android.cts.managedprofile.WIPE_DATA_WITH_REASON");
- }, new DevicePolicyEventWrapper.Builder(EventId.WIPE_DATA_WITH_REASON_VALUE)
- .setAdminPackageName(MANAGED_PROFILE_PKG)
- .setInt(0)
- .build());
- // Check and clear the notification is presented after work profile got removed, so profile
- // user no longer exists, verification should be run in primary user.
- runDeviceTestsAsUser(
- MANAGED_PROFILE_PKG,
- ".WipeDataNotificationTest",
- "testWipeDataWithReasonVerification",
- mParentUserId);
- }
-
- public void testWipeDataWithoutReason() throws Exception {
- if (!mHasFeature) {
- return;
- }
- assertTrue(listUsers().contains(mProfileUserId));
- sendWipeProfileBroadcast("com.android.cts.managedprofile.WIPE_DATA_WITHOUT_REASON");
- // Note: the managed profile is removed by this test, which will make removeUserCommand in
- // tearDown() to complain, but that should be OK since its result is not asserted.
- assertUserGetsRemoved(mProfileUserId);
- // testWipeDataWithoutReason() removes the managed profile,
- // so it needs to separated from other tests.
- // Check the notification is not presented after work profile got removed, so profile user
- // no longer exists, verification should be run in primary user.
- runDeviceTestsAsUser(
- MANAGED_PROFILE_PKG,
- ".WipeDataNotificationTest",
- "testWipeDataWithoutReasonVerification",
- mParentUserId);
- }
-
- /**
- * wipeData() test removes the managed profile, so it needs to be separated from other tests.
- */
- public void testWipeData() throws Exception {
- if (!mHasFeature) {
- return;
- }
- assertTrue(listUsers().contains(mProfileUserId));
- sendWipeProfileBroadcast("com.android.cts.managedprofile.WIPE_DATA");
- // Note: the managed profile is removed by this test, which will make removeUserCommand in
- // tearDown() to complain, but that should be OK since its result is not asserted.
- assertUserGetsRemoved(mProfileUserId);
- }
-
- private void sendWipeProfileBroadcast(String action) throws Exception {
- final String cmd = "am broadcast --receiver-foreground --user " + mProfileUserId
- + " -a " + action
- + " com.android.cts.managedprofile/.WipeDataReceiver";
- getDevice().executeShellCommand(cmd);
- }
-
- public void testLockNowWithKeyEviction() throws Exception {
- if (!mHasFeature || !mSupportsFbe || !mHasSecureLockScreen) {
- return;
- }
- changeUserCredential("1234", null, mProfileUserId);
- lockProfile();
- }
-
- private void lockProfile() throws Exception {
- final String cmd = "am broadcast --receiver-foreground --user " + mProfileUserId
- + " -a com.android.cts.managedprofile.LOCK_PROFILE"
- + " com.android.cts.managedprofile/.LockProfileReceiver";
- getDevice().executeShellCommand(cmd);
- waitUntilProfileLocked();
- }
-
- private void waitUntilProfileLocked() throws Exception {
- final String cmd = "dumpsys activity | grep 'User #" + mProfileUserId + ": state='";
- final Pattern p = Pattern.compile("state=([\\p{Upper}_]+)$");
- SuccessCondition userLocked = () -> {
- final String activityDump = getDevice().executeShellCommand(cmd);
- final Matcher m = p.matcher(activityDump);
- return m.find() && m.group(1).equals("RUNNING_LOCKED");
- };
- tryWaitForSuccess(
- userLocked,
- "The managed profile has not been locked after calling "
- + "lockNow(FLAG_SECURE_USER_DATA)",
- TIMEOUT_USER_LOCKED_MILLIS);
- }
-
- /** Profile should get locked if it is not in foreground no matter what. */
- public void testWorkProfileTimeoutBackground() throws Exception {
- if (!mHasFeature) {
- return;
- }
- setUpWorkProfileTimeout();
-
- startDummyActivity(mPrimaryUserId, true);
- simulateUserInteraction(PROFILE_TIMEOUT_DELAY_MS);
-
- verifyOnlyProfileLocked(true);
- }
-
- /** Profile should get locked if it is in foreground but with no user activity. */
- public void testWorkProfileTimeoutIdleActivity() throws Exception {
- if (!mHasFeature) {
- return;
- }
- setUpWorkProfileTimeout();
-
- startDummyActivity(mProfileUserId, false);
- Thread.sleep(PROFILE_TIMEOUT_DELAY_MS);
-
- verifyOnlyProfileLocked(true);
- }
-
- /** User activity in profile should prevent it from locking. */
- public void testWorkProfileTimeoutUserActivity() throws Exception {
- if (!mHasFeature) {
- return;
- }
- setUpWorkProfileTimeout();
-
- startDummyActivity(mProfileUserId, false);
- simulateUserInteraction(PROFILE_TIMEOUT_DELAY_MS);
-
- verifyOnlyProfileLocked(false);
- }
-
- /** Keep screen on window flag in the profile should prevent it from locking. */
- public void testWorkProfileTimeoutKeepScreenOnWindow() throws Exception {
- if (!mHasFeature) {
- return;
- }
- setUpWorkProfileTimeout();
-
- startDummyActivity(mProfileUserId, true);
- Thread.sleep(PROFILE_TIMEOUT_DELAY_MS);
-
- verifyOnlyProfileLocked(false);
- }
-
- private void setUpWorkProfileTimeout() throws DeviceNotAvailableException {
- // Set separate challenge.
- changeUserCredential(PROFILE_CREDENTIAL, null, mProfileUserId);
-
- // Make sure the profile is not prematurely locked.
- verifyUserCredential(PROFILE_CREDENTIAL, mProfileUserId);
- verifyOnlyProfileLocked(false);
- // Set profile timeout to 5 seconds.
- runProfileTimeoutTest("testSetWorkProfileTimeout", mProfileUserId);
- }
-
- private void verifyOnlyProfileLocked(boolean locked) throws DeviceNotAvailableException {
- final String expectedResultTest = locked ? "testDeviceLocked" : "testDeviceNotLocked";
- runProfileTimeoutTest(expectedResultTest, mProfileUserId);
- // Primary profile shouldn't be locked.
- runProfileTimeoutTest("testDeviceNotLocked", mPrimaryUserId);
- }
-
- private void simulateUserInteraction(int timeMs) throws Exception {
- final long endTime = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(timeMs);
- final UserActivityEmulator helper = new UserActivityEmulator(getDevice());
- while (System.nanoTime() < endTime) {
- helper.tapScreenCenter();
- // Just in case to prevent busy loop.
- Thread.sleep(100);
- }
- }
-
- private void runProfileTimeoutTest(String method, int userId)
- throws DeviceNotAvailableException {
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, MANAGED_PROFILE_PKG + ".ProfileTimeoutTestHelper",
- method, userId);
- }
-
- private void startDummyActivity(int profileUserId, boolean keepScreenOn) throws Exception {
- getDevice().executeShellCommand(String.format(
- "am start-activity -W --user %d --ez keep_screen_on %s %s/.TimeoutActivity",
- profileUserId, keepScreenOn, MANAGED_PROFILE_PKG));
- }
-
public void testMaxOneManagedProfile() throws Exception {
int newUserId = -1;
try {
@@ -414,74 +101,7 @@
MANAGED_PROFILE_PKG, ".WifiTest", "testCannotGetWifiMacAddress", mProfileUserId);
}
- public void testCrossProfileIntentFilters() throws Exception {
- if (!mHasFeature) {
- return;
- }
- // Set up activities: ManagedProfileActivity will only be enabled in the managed profile and
- // PrimaryUserActivity only in the primary one
- disableActivityForUser("ManagedProfileActivity", mParentUserId);
- disableActivityForUser("PrimaryUserActivity", mProfileUserId);
-
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG,
- MANAGED_PROFILE_PKG + ".ManagedProfileTest", mProfileUserId);
-
- assertMetricsLogged(getDevice(), () -> {
- runDeviceTestsAsUser(
- MANAGED_PROFILE_PKG, MANAGED_PROFILE_PKG + ".ManagedProfileTest",
- "testAddCrossProfileIntentFilter_all", mProfileUserId);
- }, new DevicePolicyEventWrapper.Builder(EventId.ADD_CROSS_PROFILE_INTENT_FILTER_VALUE)
- .setAdminPackageName(MANAGED_PROFILE_PKG)
- .setInt(1)
- .setStrings("com.android.cts.managedprofile.ACTION_TEST_ALL_ACTIVITY")
- .build());
-
- // Set up filters from primary to managed profile
- String command = "am start -W --user " + mProfileUserId + " " + MANAGED_PROFILE_PKG
- + "/.PrimaryUserFilterSetterActivity";
- CLog.d("Output for command " + command + ": "
- + getDevice().executeShellCommand(command));
- runDeviceTestsAsUser(
- MANAGED_PROFILE_PKG, MANAGED_PROFILE_PKG + ".PrimaryUserTest", mParentUserId);
- // TODO: Test with startActivity
- }
-
- public void testDisallowSharingIntoProfileFromProfile() throws Exception {
- if (!mHasFeature) {
- return;
- }
- // Set up activities: PrimaryUserActivity will only be enabled in the personal user
- // This activity is used to find out the ground truth about the system's cross profile
- // intent forwarding activity.
- disableActivityForUser("PrimaryUserActivity", mProfileUserId);
-
- // Tests from the profile side
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG,
- ".DisallowSharingIntoProfileTest", "testSharingFromProfile", mProfileUserId);
- }
-
- public void testDisallowSharingIntoProfileFromPersonal() throws Exception {
- if (!mHasFeature) {
- return;
- }
- // Set up activities: ManagedProfileActivity will only be enabled in the managed profile
- // This activity is used to find out the ground truth about the system's cross profile
- // intent forwarding activity.
- disableActivityForUser("ManagedProfileActivity", mParentUserId);
-
- // Tests from the personal side, which is mostly driven from host side.
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".DisallowSharingIntoProfileTest",
- "testSetUp", mProfileUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".DisallowSharingIntoProfileTest",
- "testDisableSharingIntoProfile", mProfileUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".DisallowSharingIntoProfileTest",
- "testSharingFromPersonalFails", mParentUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".DisallowSharingIntoProfileTest",
- "testEnableSharingIntoProfile", mProfileUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".DisallowSharingIntoProfileTest",
- "testSharingFromPersonalSucceeds", mParentUserId);
- }
-
+ @LargeTest
public void testAppLinks_verificationStatus() throws Exception {
if (!mHasFeature) {
return;
@@ -518,6 +138,7 @@
assertAppLinkResult("testReceivedByAppLinkActivityInManaged");
}
+ @LargeTest
public void testAppLinks_enabledStatus() throws Exception {
if (!mHasFeature) {
return;
@@ -580,134 +201,6 @@
mProfileUserId);
}
- public void testCrossProfileContent() throws Exception {
- if (!mHasFeature) {
- return;
- }
-
- // Storage permission shouldn't be granted, we check if missing permissions are respected
- // in ContentTest#testSecurity.
- installAppAsUser(INTENT_SENDER_APK, false /* grantPermissions */, USER_ALL);
- installAppAsUser(INTENT_RECEIVER_APK, USER_ALL);
-
- // Test from parent to managed
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
- "testRemoveAllFilters", mProfileUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
- "testAddManagedCanAccessParentFilters", mProfileUserId);
- runDeviceTestsAsUser(INTENT_SENDER_PKG, ".ContentTest", mParentUserId);
-
- // Test from managed to parent
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
- "testRemoveAllFilters", mProfileUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
- "testAddParentCanAccessManagedFilters", mProfileUserId);
- runDeviceTestsAsUser(INTENT_SENDER_PKG, ".ContentTest", mProfileUserId);
-
- }
-
- public void testCrossProfileNotificationListeners_EmptyWhitelist() throws Exception {
- if (!mHasFeature) {
- return;
- }
-
- installAppAsUser(NOTIFICATION_APK, USER_ALL);
-
- // Profile owner in the profile sets an empty whitelist
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".NotificationListenerTest",
- "testSetEmptyWhitelist", mProfileUserId,
- Collections.singletonMap(PARAM_PROFILE_ID, Integer.toString(mProfileUserId)));
- // Listener outside the profile can only see personal notifications.
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".NotificationListenerTest",
- "testCannotReceiveProfileNotifications", mParentUserId,
- Collections.singletonMap(PARAM_PROFILE_ID, Integer.toString(mProfileUserId)));
- }
-
- public void testCrossProfileNotificationListeners_NullWhitelist() throws Exception {
- if (!mHasFeature) {
- return;
- }
-
- installAppAsUser(NOTIFICATION_APK, USER_ALL);
-
- // Profile owner in the profile sets a null whitelist
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".NotificationListenerTest",
- "testSetNullWhitelist", mProfileUserId,
- Collections.singletonMap(PARAM_PROFILE_ID, Integer.toString(mProfileUserId)));
- // Listener outside the profile can see profile and personal notifications
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".NotificationListenerTest",
- "testCanReceiveNotifications", mParentUserId,
- Collections.singletonMap(PARAM_PROFILE_ID, Integer.toString(mProfileUserId)));
- }
-
- public void testCrossProfileNotificationListeners_InWhitelist() throws Exception {
- if (!mHasFeature) {
- return;
- }
-
- installAppAsUser(NOTIFICATION_APK, USER_ALL);
-
- // Profile owner in the profile adds listener to the whitelist
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".NotificationListenerTest",
- "testAddListenerToWhitelist", mProfileUserId,
- Collections.singletonMap(PARAM_PROFILE_ID, Integer.toString(mProfileUserId)));
- // Listener outside the profile can see profile and personal notifications
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".NotificationListenerTest",
- "testCanReceiveNotifications", mParentUserId,
- Collections.singletonMap(PARAM_PROFILE_ID, Integer.toString(mProfileUserId)));
- }
-
- public void testCrossProfileNotificationListeners_setAndGet() throws Exception {
- if (!mHasFeature) {
- return;
- }
- installAppAsUser(NOTIFICATION_APK, USER_ALL);
-
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".NotificationListenerTest",
- "testSetAndGetPermittedCrossProfileNotificationListeners", mProfileUserId,
- Collections.singletonMap(PARAM_PROFILE_ID, Integer.toString(mProfileUserId)));
- }
-
- public void testCrossProfileCopyPaste() throws Exception {
- if (!mHasFeature) {
- return;
- }
- installAppAsUser(INTENT_RECEIVER_APK, USER_ALL);
- installAppAsUser(INTENT_SENDER_APK, USER_ALL);
-
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
- "testAllowCrossProfileCopyPaste", mProfileUserId);
- // Test that managed can see what is copied in the parent.
- testCrossProfileCopyPasteInternal(mProfileUserId, true);
- // Test that the parent can see what is copied in managed.
- testCrossProfileCopyPasteInternal(mParentUserId, true);
-
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
- "testDisallowCrossProfileCopyPaste", mProfileUserId);
- // Test that managed can still see what is copied in the parent.
- testCrossProfileCopyPasteInternal(mProfileUserId, true);
- // Test that the parent cannot see what is copied in managed.
- testCrossProfileCopyPasteInternal(mParentUserId, false);
- }
-
- private void testCrossProfileCopyPasteInternal(int userId, boolean shouldSucceed)
- throws DeviceNotAvailableException {
- final String direction = (userId == mParentUserId)
- ? "testAddManagedCanAccessParentFilters"
- : "testAddParentCanAccessManagedFilters";
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
- "testRemoveAllFilters", mProfileUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
- direction, mProfileUserId);
- if (shouldSucceed) {
- runDeviceTestsAsUser(INTENT_SENDER_PKG, ".CopyPasteTest",
- "testCanReadAcrossProfiles", userId);
- } else {
- runDeviceTestsAsUser(INTENT_SENDER_PKG, ".CopyPasteTest",
- "testCannotReadAcrossProfiles", userId);
- }
- }
-
/** Tests for the API helper class. */
public void testCurrentApiHelper() throws Exception {
if (!mHasFeature) {
@@ -793,94 +286,6 @@
}
}
-
- public void testManagedContactsUris() throws Exception {
- runManagedContactsTest(new Callable<Void>() {
- @Override
- public Void call() throws Exception {
- ContactsTestSet contactsTestSet = new ContactsTestSet(ManagedProfileTest.this,
- MANAGED_PROFILE_PKG, mParentUserId, mProfileUserId);
-
- contactsTestSet.setCallerIdEnabled(true);
- contactsTestSet.setContactsSearchEnabled(true);
- contactsTestSet.checkIfCanLookupEnterpriseContacts(true);
- contactsTestSet.checkIfCanFilterEnterpriseContacts(true);
- contactsTestSet.checkIfCanFilterSelfContacts();
- return null;
- }
- });
- }
-
- public void testManagedQuickContacts() throws Exception {
- runManagedContactsTest(new Callable<Void>() {
- @Override
- public Void call() throws Exception {
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
- "testQuickContact", mParentUserId);
- return null;
- }
- });
- }
-
- public void testManagedContactsPolicies() throws Exception {
- runManagedContactsTest(new Callable<Void>() {
- @Override
- public Void call() throws Exception {
- ContactsTestSet contactsTestSet = new ContactsTestSet(ManagedProfileTest.this,
- MANAGED_PROFILE_PKG, mParentUserId, mProfileUserId);
- try {
- contactsTestSet.setCallerIdEnabled(true);
- contactsTestSet.setContactsSearchEnabled(false);
- contactsTestSet.checkIfCanLookupEnterpriseContacts(true);
- contactsTestSet.checkIfCanFilterEnterpriseContacts(false);
- contactsTestSet.checkIfCanFilterSelfContacts();
- contactsTestSet.setCallerIdEnabled(false);
- contactsTestSet.setContactsSearchEnabled(true);
- contactsTestSet.checkIfCanLookupEnterpriseContacts(false);
- contactsTestSet.checkIfCanFilterEnterpriseContacts(true);
- contactsTestSet.checkIfCanFilterSelfContacts();
- contactsTestSet.setCallerIdEnabled(false);
- contactsTestSet.setContactsSearchEnabled(false);
- contactsTestSet.checkIfCanLookupEnterpriseContacts(false);
- contactsTestSet.checkIfCanFilterEnterpriseContacts(false);
- contactsTestSet.checkIfCanFilterSelfContacts();
- contactsTestSet.checkIfNoEnterpriseDirectoryFound();
- assertMetricsLogged(getDevice(), () -> {
- contactsTestSet.setCallerIdEnabled(true);
- contactsTestSet.setCallerIdEnabled(false);
- }, new DevicePolicyEventWrapper
- .Builder(EventId.SET_CROSS_PROFILE_CALLER_ID_DISABLED_VALUE)
- .setAdminPackageName(MANAGED_PROFILE_PKG)
- .setBoolean(false)
- .build(),
- new DevicePolicyEventWrapper
- .Builder(EventId.SET_CROSS_PROFILE_CALLER_ID_DISABLED_VALUE)
- .setAdminPackageName(MANAGED_PROFILE_PKG)
- .setBoolean(true)
- .build());
- assertMetricsLogged(getDevice(), () -> {
- contactsTestSet.setContactsSearchEnabled(true);
- contactsTestSet.setContactsSearchEnabled(false);
- }, new DevicePolicyEventWrapper
- .Builder(EventId.SET_CROSS_PROFILE_CONTACTS_SEARCH_DISABLED_VALUE)
- .setAdminPackageName(MANAGED_PROFILE_PKG)
- .setBoolean(false)
- .build(),
- new DevicePolicyEventWrapper
- .Builder(EventId.SET_CROSS_PROFILE_CONTACTS_SEARCH_DISABLED_VALUE)
- .setAdminPackageName(MANAGED_PROFILE_PKG)
- .setBoolean(true)
- .build());
- return null;
- } finally {
- // reset policies
- contactsTestSet.setCallerIdEnabled(true);
- contactsTestSet.setContactsSearchEnabled(true);
- }
- }
- });
- }
-
public void testOrganizationInfo() throws Exception {
if (!mHasFeature) {
return;
@@ -891,21 +296,15 @@
"testDefaultOrganizationNameIsNull", mProfileUserId);
runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".OrganizationInfoTest",
mProfileUserId);
- assertMetricsLogged(getDevice(), () -> {
- runDeviceTestsAsUser(
- MANAGED_PROFILE_PKG, MANAGED_PROFILE_PKG + ".OrganizationInfoTest",
- "testSetOrganizationColor", mProfileUserId);
- }, new DevicePolicyEventWrapper.Builder(EventId.SET_ORGANIZATION_COLOR_VALUE)
- .setAdminPackageName(MANAGED_PROFILE_PKG)
- .build());
- }
-
- public void testPasswordMinimumRestrictions() throws Exception {
- if (!mHasFeature) {
- return;
+ if (isStatsdEnabled(getDevice())) {
+ assertMetricsLogged(getDevice(), () -> {
+ runDeviceTestsAsUser(
+ MANAGED_PROFILE_PKG, MANAGED_PROFILE_PKG + ".OrganizationInfoTest",
+ "testSetOrganizationColor", mProfileUserId);
+ }, new DevicePolicyEventWrapper.Builder(EventId.SET_ORGANIZATION_COLOR_VALUE)
+ .setAdminPackageName(MANAGED_PROFILE_PKG)
+ .build());
}
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".PasswordMinimumRestrictionsTest",
- mProfileUserId);
}
public void testDevicePolicyManagerParentSupport() throws Exception {
@@ -950,6 +349,7 @@
/*expectFailure*/ true));
}
+ @LargeTest
public void testCannotSetDeviceOwnerWhenProfilePresent() throws Exception {
if (!mHasFeature) {
return;
@@ -985,84 +385,6 @@
"testNfcShareEnabled", mParentUserId);
}
- public void testCrossProfileWidgets() throws Exception {
- if (!mHasFeature) {
- return;
- }
-
- try {
- installAppAsUser(WIDGET_PROVIDER_APK, USER_ALL);
- getDevice().executeShellCommand("appwidget grantbind --user " + mParentUserId
- + " --package " + WIDGET_PROVIDER_PKG);
- setIdleWhitelist(WIDGET_PROVIDER_PKG, true);
- startWidgetHostService();
-
- String commandOutput = changeCrossProfileWidgetForUser(WIDGET_PROVIDER_PKG,
- "add-cross-profile-widget", mProfileUserId);
- assertTrue("Command was expected to succeed " + commandOutput,
- commandOutput.contains("Status: ok"));
-
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileWidgetTest",
- "testCrossProfileWidgetProviderAdded", mProfileUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG,
- ".CrossProfileWidgetPrimaryUserTest",
- "testHasCrossProfileWidgetProvider_true", mParentUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG,
- ".CrossProfileWidgetPrimaryUserTest",
- "testHostReceivesWidgetUpdates_true", mParentUserId);
-
- commandOutput = changeCrossProfileWidgetForUser(WIDGET_PROVIDER_PKG,
- "remove-cross-profile-widget", mProfileUserId);
- assertTrue("Command was expected to succeed " + commandOutput,
- commandOutput.contains("Status: ok"));
-
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileWidgetTest",
- "testCrossProfileWidgetProviderRemoved", mProfileUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG,
- ".CrossProfileWidgetPrimaryUserTest",
- "testHasCrossProfileWidgetProvider_false", mParentUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG,
- ".CrossProfileWidgetPrimaryUserTest",
- "testHostReceivesWidgetUpdates_false", mParentUserId);
- } finally {
- changeCrossProfileWidgetForUser(WIDGET_PROVIDER_PKG, "remove-cross-profile-widget",
- mProfileUserId);
- getDevice().uninstallPackage(WIDGET_PROVIDER_PKG);
- }
- }
-
- public void testCrossProfileWidgetsLogged() throws Exception {
- if (!mHasFeature) {
- return;
- }
-
- try {
- installAppAsUser(WIDGET_PROVIDER_APK, USER_ALL);
- getDevice().executeShellCommand("appwidget grantbind --user " + mParentUserId
- + " --package " + WIDGET_PROVIDER_PKG);
- setIdleWhitelist(WIDGET_PROVIDER_PKG, true);
- startWidgetHostService();
-
- assertMetricsLogged(getDevice(), () -> {
- changeCrossProfileWidgetForUser(WIDGET_PROVIDER_PKG,
- "add-cross-profile-widget", mProfileUserId);
- changeCrossProfileWidgetForUser(WIDGET_PROVIDER_PKG,
- "remove-cross-profile-widget", mProfileUserId);
- }, new DevicePolicyEventWrapper
- .Builder(EventId.ADD_CROSS_PROFILE_WIDGET_PROVIDER_VALUE)
- .setAdminPackageName(MANAGED_PROFILE_PKG)
- .build(),
- new DevicePolicyEventWrapper
- .Builder(EventId.REMOVE_CROSS_PROFILE_WIDGET_PROVIDER_VALUE)
- .setAdminPackageName(MANAGED_PROFILE_PKG)
- .build());
- } finally {
- changeCrossProfileWidgetForUser(WIDGET_PROVIDER_PKG, "remove-cross-profile-widget",
- mProfileUserId);
- getDevice().uninstallPackage(WIDGET_PROVIDER_PKG);
- }
- }
-
public void testIsProvisioningAllowed() throws DeviceNotAvailableException {
if (!mHasFeature) {
return;
@@ -1077,16 +399,6 @@
"testIsProvisioningAllowedTrue", mParentUserId);
}
- private void setDirectoryPrefix(String directoryName, int userId)
- throws DeviceNotAvailableException {
- String command = "content call --uri " + DIRECTORY_PRIVOIDER_URI
- + " --user " + userId
- + " --method " + SET_CUSTOM_DIRECTORY_PREFIX_METHOD
- + " --arg " + directoryName;
- CLog.d("Output for command " + command + ": "
- + getDevice().executeShellCommand(command));
- }
-
public void testPhoneAccountVisibility() throws Exception {
if (!mHasFeature) {
return;
@@ -1127,6 +439,7 @@
}
}
+ @LargeTest
public void testManagedCall() throws Exception {
if (!mHasFeature) {
return;
@@ -1177,52 +490,6 @@
mParentUserId);
}
- private void givePackageWriteSettingsPermission(int userId, String pkg) throws Exception {
- // Allow app to write to settings (for RingtoneManager.setActualDefaultUri to work)
- String command = "appops set --user " + userId + " " + pkg
- + " android:write_settings allow";
- CLog.d("Output for command " + command + ": " + getDevice().executeShellCommand(command));
- }
-
- public void testRingtoneSync() throws Exception {
- if (!mHasFeature) {
- return;
- }
- givePackageWriteSettingsPermission(mProfileUserId, MANAGED_PROFILE_PKG);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".RingtoneSyncTest",
- "testRingtoneSync", mProfileUserId);
- }
-
- // Test if setting RINGTONE disables sync
- public void testRingtoneSyncAutoDisableRingtone() throws Exception {
- if (!mHasFeature) {
- return;
- }
- givePackageWriteSettingsPermission(mProfileUserId, MANAGED_PROFILE_PKG);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".RingtoneSyncTest",
- "testRingtoneDisableSync", mProfileUserId);
- }
-
- // Test if setting NOTIFICATION disables sync
- public void testRingtoneSyncAutoDisableNotification() throws Exception {
- if (!mHasFeature) {
- return;
- }
- givePackageWriteSettingsPermission(mProfileUserId, MANAGED_PROFILE_PKG);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".RingtoneSyncTest",
- "testNotificationDisableSync", mProfileUserId);
- }
-
- // Test if setting ALARM disables sync
- public void testRingtoneSyncAutoDisableAlarm() throws Exception {
- if (!mHasFeature) {
- return;
- }
- givePackageWriteSettingsPermission(mProfileUserId, MANAGED_PROFILE_PKG);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".RingtoneSyncTest",
- "testAlarmDisableSync", mProfileUserId);
- }
-
public void testTrustAgentInfo() throws Exception {
if (!mHasFeature || !mHasSecureLockScreen) {
return;
@@ -1298,332 +565,8 @@
"testProfileOwnerCannotGetDeviceIdentifiersWithoutPermission", mProfileUserId);
}
- public void testResetPasswordWithTokenBeforeUnlock() throws Exception {
- if (!mHasFeature || !mSupportsFbe || !mHasSecureLockScreen) {
- return;
- }
-
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ResetPasswordWithTokenTest",
- "testSetupWorkProfile", mProfileUserId);
- lockProfile();
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ResetPasswordWithTokenTest",
- "testResetPasswordBeforeUnlock", mProfileUserId);
- // Password needs to be in sync with ResetPasswordWithTokenTest.PASSWORD1
- verifyUserCredential(RESET_PASSWORD_TEST_DEFAULT_PASSWORD, mProfileUserId);
- }
-
- public void testClearPasswordWithTokenBeforeUnlock() throws Exception {
- if (!mHasFeature || !mSupportsFbe || !mHasSecureLockScreen) {
- return;
- }
-
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ResetPasswordWithTokenTest",
- "testSetupWorkProfile", mProfileUserId);
- lockProfile();
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ResetPasswordWithTokenTest",
- "testClearPasswordBeforeUnlock", mProfileUserId);
- // Make sure profile has no password
- verifyUserCredential("", mProfileUserId);
- }
-
- /**
- * Test password reset token is still functional after the primary user clears and
- * re-adds back its device lock. This is to detect a regression where the work profile
- * undergoes an untrusted credential reset (causing synthetic password to change, invalidating
- * existing password reset token) if it has unified work challenge and the primary user clears
- * the device lock.
- */
- public void testResetPasswordTokenUsableAfterClearingLock() throws Exception {
- if (!mHasFeature || !mSupportsFbe || !mHasSecureLockScreen) {
- return;
- }
- final String devicePassword = "1234";
-
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ResetPasswordWithTokenTest",
- "testSetResetPasswordToken", mProfileUserId);
- try {
- changeUserCredential(devicePassword, null, mParentUserId);
- changeUserCredential(null, devicePassword, mParentUserId);
- changeUserCredential(devicePassword, null, mParentUserId);
- lockProfile();
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ResetPasswordWithTokenTest",
- "testResetPasswordBeforeUnlock", mProfileUserId);
- verifyUserCredential(RESET_PASSWORD_TEST_DEFAULT_PASSWORD, mProfileUserId);
- } finally {
- changeUserCredential(null, devicePassword, mParentUserId);
- // Cycle the device screen to flush stale password information from keyguard,
- // otherwise it will still ask for the non-existent password.
- // return screen to be on for cts test runs
- executeShellCommand("input keyevent KEYCODE_WAKEUP");
- executeShellCommand("input keyevent KEYCODE_SLEEP");
- executeShellCommand("input keyevent KEYCODE_WAKEUP");
- }
- }
-
- public void testIsUsingUnifiedPassword() throws Exception {
- if (!mHasFeature || !mHasSecureLockScreen) {
- return;
- }
-
- // Freshly created profile has no separate challenge.
- verifyUnifiedPassword(true);
-
- // Set separate challenge and verify that the API reports it correctly.
- changeUserCredential("1234" /* newCredential */, null /* oldCredential */, mProfileUserId);
- verifyUnifiedPassword(false);
- }
-
- public void testUnlockWorkProfile_deviceWidePassword() throws Exception {
- if (!mHasFeature || !mSupportsFbe || !mHasSecureLockScreen) {
- return;
- }
- String password = "0000";
- try {
- // Add a device password after the work profile has been created.
- changeUserCredential(password, /* oldCredential= */ null, mPrimaryUserId);
- // Lock the profile with key eviction.
- lockProfile();
- // Turn on work profile, by unlocking the profile with the device password.
- verifyUserCredential(password, mPrimaryUserId);
-
- // Verify profile user is running unlocked by running a sanity test on the work profile.
- installAppAsUser(SIMPLE_APP_APK, mProfileUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".SanityTest", mProfileUserId);
- } finally {
- // Clean up
- changeUserCredential(/* newCredential= */ null, password, mPrimaryUserId);
- }
- }
-
- public void testRebootDevice_unifiedPassword() throws Exception {
- if (!mHasFeature || !mHasSecureLockScreen) {
- return;
- }
- // Waiting before rebooting prevents flakiness.
- waitForBroadcastIdle();
- String password = "0000";
- changeUserCredential(password, /* oldCredential= */ null, mPrimaryUserId);
- try {
- rebootAndWaitUntilReady();
- verifyUserCredential(password, mPrimaryUserId);
- installAppAsUser(SIMPLE_APP_APK, mProfileUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".SanityTest", mProfileUserId);
- } finally {
- changeUserCredential(/* newCredential= */ null, password, mPrimaryUserId);
- // Work-around for http://b/113866275 - password prompt being erroneously shown at the
- // end.
- pressPowerButton();
- }
- }
-
- public void testRebootDevice_separatePasswords() throws Exception {
- if (!mHasFeature || !mHasSecureLockScreen) {
- return;
- }
- // Waiting before rebooting prevents flakiness.
- waitForBroadcastIdle();
- String profilePassword = "profile";
- String primaryPassword = "primary";
- int managedProfileUserId = getFirstManagedProfileUserId();
- changeUserCredential(
- profilePassword, /* oldCredential= */ null, managedProfileUserId);
- changeUserCredential(primaryPassword, /* oldCredential= */ null, mPrimaryUserId);
- try {
- rebootAndWaitUntilReady();
- verifyUserCredential(profilePassword, managedProfileUserId);
- verifyUserCredential(primaryPassword, mPrimaryUserId);
- installAppAsUser(SIMPLE_APP_APK, mProfileUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".SanityTest", mProfileUserId);
- } finally {
- changeUserCredential(
- /* newCredential= */ null, profilePassword, managedProfileUserId);
- changeUserCredential(/* newCredential= */ null, primaryPassword, mPrimaryUserId);
- // Work-around for http://b/113866275 - password prompt being erroneously shown at the
- // end.
- pressPowerButton();
- }
- }
-
- public void testCrossProfileCalendarPackage() throws Exception {
- if (!mHasFeature) {
- return;
- }
- assertMetricsLogged(getDevice(), () -> {
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testCrossProfileCalendarPackage", mProfileUserId);
- }, new DevicePolicyEventWrapper.Builder(EventId.SET_CROSS_PROFILE_CALENDAR_PACKAGES_VALUE)
- .setAdminPackageName(MANAGED_PROFILE_PKG)
- .setStrings(MANAGED_PROFILE_PKG)
- .build());
- }
-
- public void testCrossProfileCalendar() throws Exception {
- if (!mHasFeature) {
- return;
- }
- runCrossProfileCalendarTestsWhenWhitelistedAndEnabled();
- runCrossProfileCalendarTestsWhenAllPackagesWhitelisted();
- runCrossProfileCalendarTestsWhenDisabled();
- runCrossProfileCalendarTestsWhenNotWhitelisted();
- }
-
- private void runCrossProfileCalendarTestsWhenWhitelistedAndEnabled() throws Exception {
- try {
- // Setup. Add the test package into cross-profile calendar whitelist, enable
- // cross-profile calendar in settings, and insert test data into calendar provider.
- // All setups should be done in managed profile.
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testWhitelistManagedProfilePackage", mProfileUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testAddTestCalendarDataForWorkProfile", mProfileUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testEnableCrossProfileCalendarSettings", mProfileUserId);
-
- // Testing.
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testPrimaryProfile_getCorrectWorkCalendarsWhenEnabled", mParentUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testPrimaryProfile_getCorrectWorkEventsWhenEnabled", mParentUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testPrimaryProfile_getCorrectWorkInstancesWhenEnabled", mParentUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testPrimaryProfile_getCorrectWorkInstancesByDayWhenEnabled", mParentUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testPrimaryProfile_canAccessWorkInstancesSearch1", mParentUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testPrimaryProfile_canAccessWorkInstancesSearch2", mParentUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testPrimaryProfile_canAccessWorkInstancesSearchByDay", mParentUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testPrimaryProfile_getExceptionWhenQueryNonWhitelistedColumns", mParentUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testViewEventCrossProfile_intentReceivedWhenWhitelisted", mParentUserId);
- } finally {
- // Cleanup.
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testCleanupWhitelist", mProfileUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testCleanupTestCalendarDataForWorkProfile", mProfileUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testDisableCrossProfileCalendarSettings", mProfileUserId);
- }
- }
-
- private void runCrossProfileCalendarTestsWhenAllPackagesWhitelisted() throws Exception {
- try {
- // Setup. Allow all packages to access cross-profile calendar APIs by setting
- // the whitelist to null, enable cross-profile calendar in settings,
- // and insert test data into calendar provider.
- // All setups should be done in managed profile.
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testWhitelistAllPackages", mProfileUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testAddTestCalendarDataForWorkProfile", mProfileUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testEnableCrossProfileCalendarSettings", mProfileUserId);
-
- // Testing.
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testPrimaryProfile_getCorrectWorkCalendarsWhenEnabled", mParentUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testPrimaryProfile_getCorrectWorkEventsWhenEnabled", mParentUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testPrimaryProfile_getCorrectWorkInstancesWhenEnabled", mParentUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testPrimaryProfile_getCorrectWorkInstancesByDayWhenEnabled", mParentUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testPrimaryProfile_canAccessWorkInstancesSearch1", mParentUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testPrimaryProfile_canAccessWorkInstancesSearch2", mParentUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testPrimaryProfile_canAccessWorkInstancesSearchByDay", mParentUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testPrimaryProfile_getExceptionWhenQueryNonWhitelistedColumns", mParentUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testViewEventCrossProfile_intentReceivedWhenWhitelisted", mParentUserId);
- } finally {
- // Cleanup.
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testCleanupWhitelist", mProfileUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testCleanupTestCalendarDataForWorkProfile", mProfileUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testDisableCrossProfileCalendarSettings", mProfileUserId);
- }
- }
-
- private void runCrossProfileCalendarTestsWhenDisabled() throws Exception {
- try {
- // Setup. Add the test package into cross-profile calendar whitelist,
- // and insert test data into calendar provider. But disable cross-profile calendar
- // in settings. Thus cross-profile calendar Uris should not be accessible.
- // All setups should be done in managed profile.
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testWhitelistManagedProfilePackage", mProfileUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testAddTestCalendarDataForWorkProfile", mProfileUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testDisableCrossProfileCalendarSettings", mProfileUserId);
-
- // Testing.
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testPrimaryProfile_cannotAccessWorkCalendarsWhenDisabled", mParentUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testPrimaryProfile_cannotAccessWorkEventsWhenDisabled", mParentUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testPrimaryProfile_cannotAccessWorkInstancesWhenDisabled", mParentUserId);
- } finally {
- // Cleanup.
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testCleanupWhitelist", mProfileUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testCleanupTestCalendarDataForWorkProfile", mProfileUserId);
- }
- }
-
- private void runCrossProfileCalendarTestsWhenNotWhitelisted() throws Exception {
- try {
- // Setup. Enable cross-profile calendar in settings and insert test data into calendar
- // provider. But make sure that the test package is not whitelisted for cross-profile
- // calendar. Thus cross-profile calendar Uris should not be accessible.
- // All setups should be done in managed profile.
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testAddTestCalendarDataForWorkProfile", mProfileUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testEnableCrossProfileCalendarSettings", mProfileUserId);
-
- // Testing.
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testPrimaryProfile_cannotAccessWorkCalendarsWhenDisabled", mParentUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testPrimaryProfile_cannotAccessWorkEventsWhenDisabled", mParentUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testPrimaryProfile_cannotAccessWorkInstancesWhenDisabled", mParentUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testViewEventCrossProfile_intentFailedWhenNotWhitelisted", mParentUserId);
- } finally {
- // Cleanup.
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testCleanupTestCalendarDataForWorkProfile", mProfileUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testDisableCrossProfileCalendarSettings", mProfileUserId);
- }
- }
-
- public void testCreateSeparateChallengeChangedLogged() throws Exception {
- if (!mHasFeature || !mHasSecureLockScreen) {
- return;
- }
- assertMetricsLogged(getDevice(), () -> {
- changeUserCredential(
- "1234" /* newCredential */, null /* oldCredential */, mProfileUserId);
- }, new DevicePolicyEventWrapper.Builder(EventId.SEPARATE_PROFILE_CHALLENGE_CHANGED_VALUE)
- .setBoolean(true)
- .build());
- }
-
public void testSetProfileNameLogged() throws Exception {
- if (!mHasFeature) {
+ if (!mHasFeature || !isStatsdEnabled(getDevice())) {
return;
}
assertMetricsLogged(getDevice(), () -> {
@@ -1635,23 +578,6 @@
.build());
}
- private void verifyUnifiedPassword(boolean unified) throws DeviceNotAvailableException {
- final String testMethod =
- unified ? "testUsingUnifiedPassword" : "testNotUsingUnifiedPassword";
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".IsUsingUnifiedPasswordTest",
- testMethod, mProfileUserId);
- }
-
- private void disableActivityForUser(String activityName, int userId)
- throws DeviceNotAvailableException {
- String command = "am start -W --user " + userId
- + " --es extra-package " + MANAGED_PROFILE_PKG
- + " --es extra-class-name " + MANAGED_PROFILE_PKG + "." + activityName
- + " " + MANAGED_PROFILE_PKG + "/.ComponentDisablingActivity ";
- CLog.d("Output for command " + command + ": "
- + getDevice().executeShellCommand(command));
- }
-
private void changeUserRestrictionOrFail(String key, boolean value, int userId)
throws DeviceNotAvailableException {
changeUserRestrictionOrFail(key, value, userId, MANAGED_PROFILE_PKG);
@@ -1662,25 +588,6 @@
return changeUserRestriction(key, value, userId, MANAGED_PROFILE_PKG);
}
- private void setIdleWhitelist(String packageName, boolean enabled)
- throws DeviceNotAvailableException {
- String command = "cmd deviceidle whitelist " + (enabled ? "+" : "-") + packageName;
- CLog.d("Output for command " + command + ": "
- + getDevice().executeShellCommand(command));
- }
-
- private String changeCrossProfileWidgetForUser(String packageName, String command, int userId)
- throws DeviceNotAvailableException {
- String adbCommand = "am start -W --user " + userId
- + " -c android.intent.category.DEFAULT "
- + " --es extra-command " + command
- + " --es extra-package-name " + packageName
- + " " + MANAGED_PROFILE_PKG + "/.SetPolicyActivity";
- String commandOutput = getDevice().executeShellCommand(adbCommand);
- CLog.d("Output for command " + adbCommand + ": " + commandOutput);
- return commandOutput;
- }
-
// status should be one of never, undefined, ask, always
private void changeVerificationStatus(int userId, String packageName, String status)
throws DeviceNotAvailableException {
@@ -1689,15 +596,6 @@
+ getDevice().executeShellCommand(command));
}
- protected void startWidgetHostService() throws Exception {
- String command = "am startservice --user " + mParentUserId
- + " -a " + WIDGET_PROVIDER_PKG + ".REGISTER_CALLBACK "
- + "--ei user-extra " + getUserSerialNumber(mProfileUserId)
- + " " + WIDGET_PROVIDER_PKG + "/.SimpleAppWidgetHostService";
- CLog.d("Output for command " + command + ": "
- + getDevice().executeShellCommand(command));
- }
-
private void assertAppLinkResult(String methodName) throws DeviceNotAvailableException {
runDeviceTestsAsUser(INTENT_SENDER_PKG, ".AppLinkTest", methodName,
mProfileUserId);
@@ -1706,290 +604,4 @@
private boolean shouldRunTelecomTest() throws DeviceNotAvailableException {
return hasDeviceFeature(FEATURE_TELEPHONY) && hasDeviceFeature(FEATURE_CONNECTION_SERVICE);
}
-
- private void runManagedContactsTest(Callable<Void> callable) throws Exception {
- if (!mHasFeature) {
- return;
- }
-
- try {
- // Allow cross profile contacts search.
- // TODO test both on and off.
- getDevice().executeShellCommand(
- "settings put --user " + mProfileUserId
- + " secure managed_profile_contact_remote_search 1");
-
- // Add test account
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
- "testAddTestAccount", mParentUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
- "testAddTestAccount", mProfileUserId);
-
- // Install directory provider to both primary and managed profile
- installAppAsUser(DIRECTORY_PROVIDER_APK, USER_ALL);
- setDirectoryPrefix(PRIMARY_DIRECTORY_PREFIX, mParentUserId);
- setDirectoryPrefix(MANAGED_DIRECTORY_PREFIX, mProfileUserId);
-
- // Check enterprise directory API works
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
- "testGetDirectoryListInPrimaryProfile", mParentUserId);
-
- // Insert Primary profile Contacts
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
- "testPrimaryProfilePhoneAndEmailLookup_insertedAndfound", mParentUserId);
- // Insert Managed profile Contacts
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
- "testManagedProfilePhoneAndEmailLookup_insertedAndfound", mProfileUserId);
- // Insert a primary contact with same phone & email as other
- // enterprise contacts
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
- "testPrimaryProfileDuplicatedPhoneEmailContact_insertedAndfound",
- mParentUserId);
- // Insert a enterprise contact with same phone & email as other
- // primary contacts
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
- "testManagedProfileDuplicatedPhoneEmailContact_insertedAndfound",
- mProfileUserId);
-
- callable.call();
-
- } finally {
- // Clean up in managed profile and primary profile
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
- "testCurrentProfileContacts_removeContacts", mProfileUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
- "testCurrentProfileContacts_removeContacts", mParentUserId);
- getDevice().uninstallPackage(DIRECTORY_PROVIDER_PKG);
- }
- }
-
-
- /*
- * Container for running ContactsTest under multi-user environment
- */
- private static class ContactsTestSet {
-
- private ManagedProfileTest mManagedProfileTest;
- private String mManagedProfilePackage;
- private int mParentUserId;
- private int mProfileUserId;
-
- public ContactsTestSet(ManagedProfileTest managedProfileTest, String managedProfilePackage,
- int parentUserId, int profileUserId) {
- mManagedProfileTest = managedProfileTest;
- mManagedProfilePackage = managedProfilePackage;
- mParentUserId = parentUserId;
- mProfileUserId = profileUserId;
- }
-
- private void runDeviceTestsAsUser(String pkgName, String testClassName,
- String testMethodName, Integer userId) throws DeviceNotAvailableException {
- mManagedProfileTest.runDeviceTestsAsUser(pkgName, testClassName, testMethodName,
- userId);
- }
-
- // Enable / Disable
- public void setCallerIdEnabled(boolean enabled) throws DeviceNotAvailableException {
- if (enabled) {
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testSetCrossProfileCallerIdDisabled_false", mProfileUserId);
- } else {
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testSetCrossProfileCallerIdDisabled_true", mProfileUserId);
- }
- }
-
- // Enable / Disable cross profile contacts search
- public void setContactsSearchEnabled(boolean enabled) throws DeviceNotAvailableException {
- if (enabled) {
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testSetCrossProfileContactsSearchDisabled_false", mProfileUserId);
- } else {
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testSetCrossProfileContactsSearchDisabled_true", mProfileUserId);
- }
- }
-
- public void checkIfCanLookupEnterpriseContacts(boolean expected)
- throws DeviceNotAvailableException {
- // Primary user cannot use ordinary phone/email lookup api to access
- // managed contacts
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testPrimaryProfilePhoneLookup_canNotAccessEnterpriseContact", mParentUserId);
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testPrimaryProfileEmailLookup_canNotAccessEnterpriseContact", mParentUserId);
- // Primary user can use ENTERPRISE_CONTENT_FILTER_URI to access
- // primary contacts
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testPrimaryProfileEnterprisePhoneLookup_canAccessPrimaryContact",
- mParentUserId);
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testPrimaryProfileEnterpriseEmailLookup_canAccessPrimaryContact",
- mParentUserId);
- // When there exist contacts with the same phone/email in primary &
- // enterprise,
- // primary user can use ENTERPRISE_CONTENT_FILTER_URI to access the
- // primary contact.
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testPrimaryProfileEnterpriseEmailLookupDuplicated_canAccessPrimaryContact",
- mParentUserId);
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testPrimaryProfileEnterprisePhoneLookupDuplicated_canAccessPrimaryContact",
- mParentUserId);
-
- // Managed user cannot use ordinary phone/email lookup api to access
- // primary contacts
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testManagedProfilePhoneLookup_canNotAccessPrimaryContact", mProfileUserId);
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testManagedProfileEmailLookup_canNotAccessPrimaryContact", mProfileUserId);
- // Managed user can use ENTERPRISE_CONTENT_FILTER_URI to access
- // enterprise contacts
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testManagedProfileEnterprisePhoneLookup_canAccessEnterpriseContact",
- mProfileUserId);
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testManagedProfileEnterpriseEmailLookup_canAccessEnterpriseContact",
- mProfileUserId);
- // Managed user cannot use ENTERPRISE_CONTENT_FILTER_URI to access
- // primary contacts
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testManagedProfileEnterprisePhoneLookup_canNotAccessPrimaryContact",
- mProfileUserId);
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testManagedProfileEnterpriseEmailLookup_canNotAccessPrimaryContact",
- mProfileUserId);
- // When there exist contacts with the same phone/email in primary &
- // enterprise,
- // managed user can use ENTERPRISE_CONTENT_FILTER_URI to access the
- // enterprise contact.
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testManagedProfileEnterpriseEmailLookupDuplicated_canAccessEnterpriseContact",
- mProfileUserId);
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testManagedProfileEnterprisePhoneLookupDuplicated_canAccessEnterpriseContact",
- mProfileUserId);
-
- // Check if phone lookup can access primary directories
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testPrimaryProfileEnterprisePhoneLookup_canAccessPrimaryDirectories",
- mParentUserId);
-
- // Check if email lookup can access primary directories
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testPrimaryProfileEnterpriseEmailLookup_canAccessPrimaryDirectories",
- mParentUserId);
-
- if (expected) {
- // Primary user can use ENTERPRISE_CONTENT_FILTER_URI to access
- // managed profile contacts
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testPrimaryProfileEnterprisePhoneLookup_canAccessEnterpriseContact",
- mParentUserId);
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testPrimaryProfileEnterpriseEmailLookup_canAccessEnterpriseContact",
- mParentUserId);
-
- // Make sure SIP enterprise lookup works too.
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testPrimaryProfileEnterpriseSipLookup_canAccessEnterpriseContact",
- mParentUserId);
-
- // Check if phone lookup can access enterprise directories
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testPrimaryProfileEnterprisePhoneLookup_canAccessManagedDirectories",
- mParentUserId);
-
- // Check if email lookup can access enterprise directories
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testPrimaryProfileEnterpriseEmailLookup_canAccessManagedDirectories",
- mParentUserId);
- } else {
- // Primary user cannot use ENTERPRISE_CONTENT_FILTER_URI to
- // access managed contacts
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testPrimaryProfileEnterprisePhoneLookup_canNotAccessEnterpriseContact",
- mParentUserId);
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testPrimaryProfileEnterprisePhoneLookup_canNotAccessManagedDirectories",
- mParentUserId);
-
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testPrimaryProfileEnterpriseEmailLookup_canNotAccessManagedDirectories",
- mParentUserId);
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testPrimaryProfileEnterprisePhoneLookup_canNotAccessManagedDirectories",
- mParentUserId);
- }
- }
-
- public void checkIfCanFilterSelfContacts() throws DeviceNotAvailableException {
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testPrimaryProfileEnterpriseCallableFilter_canAccessPrimaryDirectories",
- mParentUserId);
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testManagedProfileEnterpriseCallableFilter_canAccessManagedDirectories",
- mProfileUserId);
-
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testPrimaryProfileEnterpriseEmailFilter_canAccessPrimaryDirectories",
- mParentUserId);
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testEnterpriseProfileEnterpriseEmailFilter_canAccessManagedDirectories",
- mProfileUserId);
-
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testPrimaryProfileEnterpriseContactFilter_canAccessPrimaryDirectories",
- mParentUserId);
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testManagedProfileEnterpriseContactFilter_canAccessManagedDirectories",
- mProfileUserId);
-
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testPrimaryProfileEnterprisePhoneFilter_canAccessPrimaryDirectories",
- mParentUserId);
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testManagedProfileEnterprisePhoneFilter_canAccessManagedDirectories",
- mProfileUserId);
- }
-
- public void checkIfCanFilterEnterpriseContacts(boolean expected)
- throws DeviceNotAvailableException {
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testFilterUriWhenDirectoryParamMissing", mParentUserId);
- if (expected) {
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testPrimaryProfileEnterpriseCallableFilter_canAccessManagedDirectories",
- mParentUserId);
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testPrimaryProfileEnterpriseEmailFilter_canAccessManagedDirectories",
- mParentUserId);
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testPrimaryProfileEnterpriseContactFilter_canAccessManagedDirectories",
- mParentUserId);
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testPrimaryProfileEnterprisePhoneFilter_canAccessManagedDirectories",
- mParentUserId);
- } else {
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testPrimaryProfileEnterpriseCallableFilter_canNotAccessManagedDirectories",
- mParentUserId);
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testPrimaryProfileEnterpriseEmailFilter_canNotAccessManagedDirectories",
- mParentUserId);
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testPrimaryProfileEnterpriseContactFilter_canNotAccessManagedDirectories",
- mParentUserId);
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testPrimaryProfileEnterprisePhoneFilter_canNotAccessManagedDirectories",
- mParentUserId);
- }
- }
-
- public void checkIfNoEnterpriseDirectoryFound() throws DeviceNotAvailableException {
- runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
- "testPrimaryProfileEnterpriseDirectories_canNotAccessManagedDirectories",
- mParentUserId);
- }
- }
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTimeoutTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTimeoutTest.java
new file mode 100644
index 0000000..341912c
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTimeoutTest.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.devicepolicy;
+
+import android.platform.test.annotations.FlakyTest;
+import android.platform.test.annotations.LargeTest;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+
+import java.util.concurrent.TimeUnit;
+
+public class ManagedProfileTimeoutTest extends BaseManagedProfileTest {
+ private static final String PROFILE_CREDENTIAL = "1234";
+ // This should be sufficiently larger than ProfileTimeoutTestHelper.TIMEOUT_MS
+ private static final int PROFILE_TIMEOUT_DELAY_MS = 40_000;
+
+ /** Profile should get locked if it is not in foreground no matter what. */
+ @FlakyTest
+ public void testWorkProfileTimeoutBackground() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ setUpWorkProfileTimeout();
+
+ startDummyActivity(mPrimaryUserId, true);
+ simulateUserInteraction(PROFILE_TIMEOUT_DELAY_MS);
+
+ verifyOnlyProfileLocked(true);
+ }
+
+ /** Profile should get locked if it is in foreground but with no user activity. */
+ @LargeTest
+ public void testWorkProfileTimeoutIdleActivity() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ setUpWorkProfileTimeout();
+
+ startDummyActivity(mProfileUserId, false);
+ Thread.sleep(PROFILE_TIMEOUT_DELAY_MS);
+
+ verifyOnlyProfileLocked(true);
+ }
+
+ /** User activity in profile should prevent it from locking. */
+ @FlakyTest
+ public void testWorkProfileTimeoutUserActivity() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ setUpWorkProfileTimeout();
+
+ startDummyActivity(mProfileUserId, false);
+ simulateUserInteraction(PROFILE_TIMEOUT_DELAY_MS);
+
+ verifyOnlyProfileLocked(false);
+ }
+
+ /** Keep screen on window flag in the profile should prevent it from locking. */
+ @FlakyTest
+ public void testWorkProfileTimeoutKeepScreenOnWindow() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ setUpWorkProfileTimeout();
+
+ startDummyActivity(mProfileUserId, true);
+ Thread.sleep(PROFILE_TIMEOUT_DELAY_MS);
+
+ verifyOnlyProfileLocked(false);
+ }
+
+ private void setUpWorkProfileTimeout() throws DeviceNotAvailableException {
+ // Set separate challenge.
+ changeUserCredential(PROFILE_CREDENTIAL, null, mProfileUserId);
+
+ // Make sure the profile is not prematurely locked.
+ verifyUserCredential(PROFILE_CREDENTIAL, mProfileUserId);
+ verifyOnlyProfileLocked(false);
+ // Set profile timeout to 5 seconds.
+ runProfileTimeoutTest("testSetWorkProfileTimeout", mProfileUserId);
+ }
+
+ private void verifyOnlyProfileLocked(boolean locked) throws DeviceNotAvailableException {
+ final String expectedResultTest = locked ? "testDeviceLocked" : "testDeviceNotLocked";
+ runProfileTimeoutTest(expectedResultTest, mProfileUserId);
+ // Primary profile shouldn't be locked.
+ runProfileTimeoutTest("testDeviceNotLocked", mPrimaryUserId);
+ }
+
+ private void simulateUserInteraction(int timeMs) throws Exception {
+ final long endTime = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(timeMs);
+ final UserActivityEmulator helper = new UserActivityEmulator(getDevice());
+ while (System.nanoTime() < endTime) {
+ helper.tapScreenCenter();
+ // Just in case to prevent busy loop.
+ Thread.sleep(100);
+ }
+ }
+
+ private void runProfileTimeoutTest(String method, int userId)
+ throws DeviceNotAvailableException {
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, MANAGED_PROFILE_PKG + ".ProfileTimeoutTestHelper",
+ method, userId);
+ }
+
+ private void startDummyActivity(int profileUserId, boolean keepScreenOn) throws Exception {
+ getDevice().executeShellCommand(String.format(
+ "am start-activity -W --user %d --ez keep_screen_on %s %s/.TimeoutActivity",
+ profileUserId, keepScreenOn, MANAGED_PROFILE_PKG));
+ }
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileWipeTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileWipeTest.java
new file mode 100644
index 0000000..5d62c83
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileWipeTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.devicepolicy;
+
+import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.assertMetricsLogged;
+import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.isStatsdEnabled;
+
+import android.platform.test.annotations.FlakyTest;
+import android.stats.devicepolicy.EventId;
+
+import com.android.cts.devicepolicy.metrics.DevicePolicyEventWrapper;
+
+public class ManagedProfileWipeTest extends BaseManagedProfileTest {
+ @FlakyTest
+ public void testWipeDataWithReason() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ assertTrue(listUsers().contains(mProfileUserId));
+ sendWipeProfileBroadcast("com.android.cts.managedprofile.WIPE_DATA_WITH_REASON");
+ // Note: the managed profile is removed by this test, which will make removeUserCommand in
+ // tearDown() to complain, but that should be OK since its result is not asserted.
+ assertUserGetsRemoved(mProfileUserId);
+ // testWipeDataWithReason() removes the managed profile,
+ // so it needs to separated from other tests.
+ // Check and clear the notification is presented after work profile got removed, so profile
+ // user no longer exists, verification should be run in primary user.
+ runDeviceTestsAsUser(
+ MANAGED_PROFILE_PKG,
+ ".WipeDataNotificationTest",
+ "testWipeDataWithReasonVerification",
+ mParentUserId);
+ }
+
+ @FlakyTest
+ public void testWipeDataLogged() throws Exception {
+ if (!mHasFeature || !isStatsdEnabled(getDevice())) {
+ return;
+ }
+ assertTrue(listUsers().contains(mProfileUserId));
+ assertMetricsLogged(getDevice(), () -> {
+ sendWipeProfileBroadcast("com.android.cts.managedprofile.WIPE_DATA_WITH_REASON");
+ }, new DevicePolicyEventWrapper.Builder(EventId.WIPE_DATA_WITH_REASON_VALUE)
+ .setAdminPackageName(MANAGED_PROFILE_PKG)
+ .setInt(0)
+ .build());
+ // Check and clear the notification is presented after work profile got removed, so profile
+ // user no longer exists, verification should be run in primary user.
+ runDeviceTestsAsUser(
+ MANAGED_PROFILE_PKG,
+ ".WipeDataNotificationTest",
+ "testWipeDataWithReasonVerification",
+ mParentUserId);
+ }
+
+ @FlakyTest
+ public void testWipeDataWithoutReason() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ assertTrue(listUsers().contains(mProfileUserId));
+ sendWipeProfileBroadcast("com.android.cts.managedprofile.WIPE_DATA_WITHOUT_REASON");
+ // Note: the managed profile is removed by this test, which will make removeUserCommand in
+ // tearDown() to complain, but that should be OK since its result is not asserted.
+ assertUserGetsRemoved(mProfileUserId);
+ // testWipeDataWithoutReason() removes the managed profile,
+ // so it needs to separated from other tests.
+ // Check the notification is not presented after work profile got removed, so profile user
+ // no longer exists, verification should be run in primary user.
+ runDeviceTestsAsUser(
+ MANAGED_PROFILE_PKG,
+ ".WipeDataNotificationTest",
+ "testWipeDataWithoutReasonVerification",
+ mParentUserId);
+ }
+
+ /**
+ * wipeData() test removes the managed profile, so it needs to be separated from other tests.
+ */
+ public void testWipeData() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ assertTrue(listUsers().contains(mProfileUserId));
+ sendWipeProfileBroadcast("com.android.cts.managedprofile.WIPE_DATA");
+ // Note: the managed profile is removed by this test, which will make removeUserCommand in
+ // tearDown() to complain, but that should be OK since its result is not asserted.
+ assertUserGetsRemoved(mProfileUserId);
+ }
+
+ private void sendWipeProfileBroadcast(String action) throws Exception {
+ final String cmd = "am broadcast --receiver-foreground --user " + mProfileUserId
+ + " -a " + action
+ + " com.android.cts.managedprofile/.WipeDataReceiver";
+ getDevice().executeShellCommand(cmd);
+ }
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerHostSideTransferTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerHostSideTransferTest.java
index fa0e60b..dbe8e52 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerHostSideTransferTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerHostSideTransferTest.java
@@ -15,6 +15,8 @@
*/
package com.android.cts.devicepolicy;
+import android.platform.test.annotations.LargeTest;
+
/**
* Tests the DPC transfer functionality for device owner. Testing is done by having two dummy DPCs,
* CtsTransferOwnerOutgoingApp and CtsTransferOwnerIncomingApp. The former is the current DPC
@@ -49,6 +51,7 @@
}
}
+ @LargeTest
public void testTransferAffiliatedProfileOwnershipCompleteCallback() throws Exception {
if (!mHasFeature || !hasDeviceFeature("android.software.managed_users")) {
return;
@@ -72,6 +75,7 @@
mUserId);
}
+ @LargeTest
public void testTransferAffiliatedProfileOwnershipInComp() throws Exception {
if (!mHasFeature || !hasDeviceFeature("android.software.managed_users")) {
return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java
index 6f3c07c..3e3a723 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java
@@ -16,6 +16,7 @@
package com.android.cts.devicepolicy;
+import android.platform.test.annotations.FlakyTest;
import android.stats.devicepolicy.EventId;
import com.android.cts.devicepolicy.metrics.DevicePolicyEventWrapper;
@@ -66,8 +67,10 @@
final int userId = createSecondaryUserAsProfileOwner();
runDeviceTestsAsUser(
- DEVICE_ADMIN_PKG, ".AffiliationTest",
- "testLockTaskMethodsThrowExceptionIfUnaffiliated", userId);
+ DEVICE_ADMIN_PKG,
+ ".AffiliationTest",
+ "testLockTaskMethodsThrowExceptionIfUnaffiliated",
+ userId);
setUserAsAffiliatedUserToPrimary(userId);
runDeviceTestsAsUser(
@@ -77,6 +80,7 @@
userId);
}
+ @FlakyTest(bugId = 127270520)
public void testLockTask_affiliatedSecondaryUser() throws Exception {
if (!mHasFeature || !canCreateAdditionalUsers(1)) {
return;
@@ -98,6 +102,30 @@
"testGenerateKeyPairWithDeviceIdAttestationExpectingSuccess", mUserId));
}
+ @FlakyTest
+ @Override
+ public void testCaCertManagement() throws Exception {
+ super.testCaCertManagement();
+ }
+
+ @FlakyTest(bugId = 141161038)
+ @Override
+ public void testCannotRemoveUserIfRestrictionSet() throws Exception {
+ super.testCannotRemoveUserIfRestrictionSet();
+ }
+
+ @FlakyTest
+ @Override
+ public void testInstallCaCertLogged() throws Exception {
+ super.testInstallCaCertLogged();
+ }
+
+ @Override
+ @FlakyTest(bugId = 138721077)
+ public void testLockTask_defaultDialer() throws Exception {
+ super.testLockTask_defaultDialer();
+ }
+
@Override
Map<String, DevicePolicyEventWrapper[]> getAdditionalDelegationTests() {
final Map<String, DevicePolicyEventWrapper[]> result = new HashMap<>();
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTest.java
index b3b48b4..ca0fb4a 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTest.java
@@ -16,6 +16,11 @@
package com.android.cts.devicepolicy;
+import android.platform.test.annotations.FlakyTest;
+import android.platform.test.annotations.LargeTest;
+
+import com.android.cts.devicepolicy.annotations.LockSettingsTest;
+import com.android.cts.devicepolicy.annotations.PermissionsTest;
import com.android.tradefed.device.DeviceNotAvailableException;
/**
@@ -67,6 +72,7 @@
* Verify that screenshots are still possible for activities in the primary user when the policy
* is set on the profile owner.
*/
+ @LargeTest
public void testScreenCaptureDisabled_allowedPrimaryUser() throws Exception {
if (!mHasFeature) {
return;
@@ -80,6 +86,7 @@
executeDeviceTestMethod(".ScreenCaptureDisabledTest", "testScreenCapturePossible");
}
+ @FlakyTest
public void testScreenCaptureDisabled_assist_allowedPrimaryUser() throws Exception {
if (!mHasFeature) {
return;
@@ -122,6 +129,7 @@
}
/** VPN tests don't require physical device for managed profile, thus overriding. */
+ @FlakyTest
@Override
public void testAlwaysOnVpn() throws Exception {
super.testAlwaysOnVpn();
@@ -135,6 +143,7 @@
/** VPN tests don't require physical device for managed profile, thus overriding. */
@Override
+ @LargeTest
public void testAlwaysOnVpnAcrossReboot() throws Exception {
super.testAlwaysOnVpnAcrossReboot();
}
@@ -158,6 +167,7 @@
}
@Override
+ @LockSettingsTest
public void testResetPasswordWithToken() throws Exception {
if (!mHasFeature || !mHasSecureLockScreen) {
return;
@@ -220,6 +230,75 @@
"testSucceedsWithProfileOwnerIdsGrant", mUserId);
}
+ @FlakyTest
+ @Override
+ public void testCaCertManagement() throws Exception {
+ super.testCaCertManagement();
+ }
+
+ @FlakyTest
+ @Override
+ public void testDelegatedCertInstaller() throws Exception {
+ super.testDelegatedCertInstaller();
+ }
+
+ @FlakyTest
+ @Override
+ public void testPackageInstallUserRestrictions() throws Exception {
+ super.testPackageInstallUserRestrictions();
+ }
+
+ @Override
+ @FlakyTest
+ @PermissionsTest
+ public void testPermissionGrant() throws Exception {
+ super.testPermissionGrant();
+ }
+
+ @Override
+ @FlakyTest
+ @PermissionsTest
+ public void testPermissionMixedPolicies() throws Exception {
+ super.testPermissionMixedPolicies();
+ }
+
+ @FlakyTest
+ @Override
+ public void testScreenCaptureDisabled_assist() throws Exception {
+ super.testScreenCaptureDisabled_assist();
+ }
+
+ @Override
+ @PermissionsTest
+ public void testPermissionPolicy() throws Exception {
+ super.testPermissionPolicy();
+ }
+
+ @FlakyTest
+ @Override
+ public void testSetMeteredDataDisabledPackages() throws Exception {
+ super.testSetMeteredDataDisabledPackages();
+ }
+
+ @Override
+ @PermissionsTest
+ public void testPermissionAppUpdate() throws Exception {
+ super.testPermissionAppUpdate();
+ }
+
+ @Override
+ @PermissionsTest
+ public void testPermissionGrantPreMApp() throws Exception {
+ super.testPermissionGrantPreMApp();
+ }
+
+ @Override
+ @PermissionsTest
+ public void testPermissionGrantOfDisallowedPermissionWhileOtherPermIsGranted()
+ throws Exception {
+ super.testPermissionGrantOfDisallowedPermissionWhileOtherPermIsGranted();
+ }
+
@Override
public void testLockTask() {
// Managed profiles are not allowed to use lock task
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTestApi25.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTestApi25.java
index 7623b48..8e26ae4 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTestApi25.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTestApi25.java
@@ -16,6 +16,11 @@
package com.android.cts.devicepolicy;
+import android.platform.test.annotations.FlakyTest;
+import android.platform.test.annotations.LargeTest;
+
+import com.android.cts.devicepolicy.annotations.PermissionsTest;
+
/**
* Set of tests for managed profile owner use cases that also apply to device owners.
* Tests that should be run identically in both cases are added in DeviceAndProfileOwnerTestApi25.
@@ -74,6 +79,8 @@
* unlocked, and cannot remove the password even when FBE is unlocked.
*/
@Override
+ @LargeTest
+ @FlakyTest(bugId = 141161690)
public void testResetPasswordFbe() throws Exception {
if (!mHasFeature || !mSupportsFbe || !mHasSecureLockScreen) {
return;
@@ -94,4 +101,10 @@
executeDeviceTestMethod(FBE_HELPER_CLASS, "testUnlockFbe");
executeDeviceTestMethod(RESET_PASSWORD_TEST_CLASS, "testResetPasswordManagedProfile");
}
+
+ @Override
+ @PermissionsTest
+ public void testPermissionGrantPreMApp() throws Exception {
+ super.testPermissionGrantPreMApp();
+ }
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTest.java
index e1d50bd..36aee24 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTest.java
@@ -16,6 +16,9 @@
package com.android.cts.devicepolicy;
+import android.platform.test.annotations.FlakyTest;
+import android.platform.test.annotations.LargeTest;
+
/**
* Set of tests for pure (non-managed) profile owner use cases that also apply to device owners.
* Tests that should be run identically in both cases are added in DeviceAndProfileOwnerTest.
@@ -48,4 +51,46 @@
}
super.tearDown();
}
+
+ @Override
+ @FlakyTest
+ public void testCaCertManagement() throws Exception {
+ super.testCaCertManagement();
+ }
+
+ @Override
+ @FlakyTest
+ public void testInstallCaCertLogged() throws Exception {
+ super.testInstallCaCertLogged();
+ }
+
+ @Override
+ @LargeTest
+ public void testPackageInstallUserRestrictions() throws Exception {
+ super.testPackageInstallUserRestrictions();
+ }
+
+ @Override
+ @FlakyTest(bugId = 138721077)
+ public void testLockTask_defaultDialer() throws Exception {
+ super.testLockTask_defaultDialer();
+ }
+
+ @Override
+ @FlakyTest(bugId = 140932104)
+ public void testLockTaskAfterReboot() throws Exception {
+ super.testLockTaskAfterReboot();
+ }
+
+ @Override
+ @FlakyTest(bugId = 140932104)
+ public void testLockTaskAfterReboot_tryOpeningSettings() throws Exception {
+ super.testLockTaskAfterReboot_tryOpeningSettings();
+ }
+
+ @Override
+ @FlakyTest(bugId = 140932104)
+ public void testLockTask_exitIfNoLongerWhitelisted() throws Exception {
+ super.testLockTask_exitIfNoLongerWhitelisted();
+ }
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/QuietModeHostsideTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/QuietModeHostsideTest.java
index fd9a0d6..a9b7a9c 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/QuietModeHostsideTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/QuietModeHostsideTest.java
@@ -1,5 +1,7 @@
package com.android.cts.devicepolicy;
+import android.platform.test.annotations.LargeTest;
+
import java.util.HashMap;
import java.util.Map;
@@ -49,6 +51,7 @@
super.tearDown();
}
+ @LargeTest
public void testQuietMode_defaultForegroundLauncher() throws Exception {
if (!mHasFeature) {
return;
@@ -61,6 +64,7 @@
createParams(mProfileId));
}
+ @LargeTest
public void testQuietMode_notForegroundLauncher() throws Exception {
if (!mHasFeature) {
return;
@@ -73,6 +77,7 @@
createParams(mProfileId));
}
+ @LargeTest
public void testQuietMode_notDefaultLauncher() throws Exception {
if (!mHasFeature) {
return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/UserRestrictionsTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/UserRestrictionsTest.java
index fdf063ea..7130e3b 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/UserRestrictionsTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/UserRestrictionsTest.java
@@ -15,6 +15,8 @@
*/
package com.android.cts.devicepolicy;
+import android.platform.test.annotations.FlakyTest;
+
import com.android.tradefed.device.DeviceNotAvailableException;
import javax.annotation.Nonnull;
@@ -128,6 +130,7 @@
}
// Checks restrictions for managed profile.
+ @FlakyTest
public void testUserRestrictions_managedProfileOwnerOnly() throws Exception {
if (!mHasFeature || !mSupportsMultiUser || !mHasManagedUserFeature) {
return;
@@ -150,6 +153,7 @@
/**
* DO + PO combination. Make sure global DO restrictions are visible on secondary users.
*/
+ @FlakyTest
public void testUserRestrictions_layering() throws Exception {
if (!mHasFeature || !mSupportsMultiUser) {
return;
@@ -216,6 +220,7 @@
* DO sets profile global restrictions (only ENSURE_VERIFY_APPS), should affect all
* users (not a particularly special case but to be sure).
*/
+ @FlakyTest
public void testUserRestrictions_profileGlobalRestrictionsAsDo() throws Exception {
if (!mHasFeature || !mSupportsMultiUser) {
return;
@@ -236,6 +241,7 @@
* Managed profile owner sets profile global restrictions (only ENSURE_VERIFY_APPS), should
* affect all users.
*/
+ @FlakyTest
public void testUserRestrictions_ProfileGlobalRestrictionsAsPo() throws Exception {
if (!mHasFeature || !mSupportsMultiUser || !mHasManagedUserFeature) {
return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/annotations/LockSettingsTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/annotations/LockSettingsTest.java
new file mode 100644
index 0000000..413df07
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/annotations/LockSettingsTest.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.devicepolicy.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Denotes that a test depends on screenlock functionality (e.g. setting user password, unlocking
+ * the user, etc.) and should be run in presubmit when changes are made to the relevant code.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD, ElementType.TYPE})
+public @interface LockSettingsTest {}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/annotations/PermissionsTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/annotations/PermissionsTest.java
new file mode 100644
index 0000000..04a8351
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/annotations/PermissionsTest.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.devicepolicy.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Denotes that a test depends on permissions functionality and should be run in presubmit when
+ * permissions code changes are made.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD, ElementType.TYPE})
+public @interface PermissionsTest {}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/metrics/DevicePolicyEventLogVerifier.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/metrics/DevicePolicyEventLogVerifier.java
index 264bd08..16e9e7f 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/metrics/DevicePolicyEventLogVerifier.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/metrics/DevicePolicyEventLogVerifier.java
@@ -19,6 +19,7 @@
import com.android.os.AtomsProto.Atom;
import com.android.os.StatsLog.EventMetricData;
+import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import java.util.ArrayList;
import java.util.List;
@@ -37,12 +38,14 @@
/**
* Asserts that <code>expectedLogs</code> were logged as a result of executing
- * <code>action</code>, in the same order.
+ * <code>action</code>, in the same order. Note that {@link Action#apply() } is always
+ * invoked on the <code>action</code> parameter, even if statsd logs are disabled.
*/
public static void assertMetricsLogged(ITestDevice device, Action action,
DevicePolicyEventWrapper... expectedLogs) throws Exception {
final AtomMetricTester logVerifier = new AtomMetricTester(device);
if (logVerifier.isStatsdDisabled()) {
+ action.apply();
return;
}
try {
@@ -61,6 +64,11 @@
}
}
+ public static boolean isStatsdEnabled(ITestDevice device) throws DeviceNotAvailableException {
+ final AtomMetricTester logVerifier = new AtomMetricTester(device);
+ return !logVerifier.isStatsdDisabled();
+ }
+
private static void assertExpectedMetricLogged(List<EventMetricData> data,
DevicePolicyEventWrapper expectedLog) {
final List<DevicePolicyEventWrapper> closestMatches = new ArrayList<>();
diff --git a/hostsidetests/dumpsys/Android.bp b/hostsidetests/dumpsys/Android.bp
index cdcd366..d26c8c4 100644
--- a/hostsidetests/dumpsys/Android.bp
+++ b/hostsidetests/dumpsys/Android.bp
@@ -20,6 +20,7 @@
libs: [
"cts-tradefed",
"tradefed",
+ "truth-prebuilt",
],
// tag this module as a cts test artifact
test_suites: [
diff --git a/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java b/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java
index 5aaef193..6c651e2 100644
--- a/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java
+++ b/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java
@@ -16,6 +16,8 @@
package android.dumpsys.cts;
+import static com.google.common.truth.Truth.assertThat;
+
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
import com.android.tradefed.log.LogUtil.CLog;
@@ -360,13 +362,16 @@
}
private void checkJobCompletion(String[] parts) {
- assertEquals(10, parts.length);
+ // This line contains a number for each job cancel reason.
+ // (See JobParameters.JOB_STOP_REASON_CODES), and future mainline updates may introudce
+ // more codes, so we have no upper bound for the number of columns.
+ assertThat(parts.length).isAtLeast(11);
assertNotNull(parts[4]); // job
- assertInteger(parts[5]); // reason_canceled
- assertInteger(parts[6]); // reason_constraints_not_satisfied
- assertInteger(parts[7]); // reason_preempt
- assertInteger(parts[8]); // reason_timeout
- assertInteger(parts[9]); // reason_device_idle
+
+ // Values for each of JOB_STOP_REASON_CODES.
+ for (int i = 5; i < parts.length; i++) {
+ assertInteger(parts[i]);
+ }
}
private void checkJobsDeferred(String[] parts) {
@@ -575,7 +580,7 @@
}
private void checkDataConnection(String[] parts) {
- assertEquals(26, parts.length);
+ assertEquals(27, parts.length);
assertInteger(parts[4]); // none
assertInteger(parts[5]); // gprs
assertInteger(parts[6]); // edge
@@ -597,7 +602,8 @@
assertInteger(parts[22]); // iwlan
assertInteger(parts[23]); // lte_ca
assertInteger(parts[24]); // nr
- assertInteger(parts[25]); // other
+ assertInteger(parts[25]); // emngcy
+ assertInteger(parts[26]); // other
}
private void checkWifiState(String[] parts) {
diff --git a/hostsidetests/incident/OWNERS b/hostsidetests/incident/OWNERS
index 2d6787a..1165b42 100644
--- a/hostsidetests/incident/OWNERS
+++ b/hostsidetests/incident/OWNERS
@@ -1,2 +1,5 @@
# Bug component: 329246
-joeo@google.com
\ No newline at end of file
+joeo@google.com
+jreck@google.com
+kwekua@google.com
+yamasani@google.com
diff --git a/hostsidetests/incident/src/com/android/server/cts/ActivityManagerIncidentTest.java b/hostsidetests/incident/src/com/android/server/cts/ActivityManagerIncidentTest.java
index 342dc7f..acf1bd4 100644
--- a/hostsidetests/incident/src/com/android/server/cts/ActivityManagerIncidentTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/ActivityManagerIncidentTest.java
@@ -97,7 +97,7 @@
assertTrue(activeServices.getServicesByUsersCount() > 0);
for (ServicesByUser perUserServices : activeServices.getServicesByUsersList()) {
- assertTrue(perUserServices.getServiceRecordsCount() > 0);
+ assertTrue(perUserServices.getServiceRecordsCount() >= 0);
for (ServiceRecordProto service : perUserServices.getServiceRecordsList()) {
assertFalse(service.getShortName().isEmpty());
assertFalse(service.getPackageName().isEmpty());
diff --git a/hostsidetests/incident/src/com/android/server/cts/AlarmManagerIncidentTest.java b/hostsidetests/incident/src/com/android/server/cts/AlarmManagerIncidentTest.java
index 35d1bb6..8093dc3 100644
--- a/hostsidetests/incident/src/com/android/server/cts/AlarmManagerIncidentTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/AlarmManagerIncidentTest.java
@@ -23,8 +23,8 @@
import com.android.server.BroadcastStatsProto;
import com.android.server.ConstantsProto;
import com.android.server.FilterStatsProto;
-import com.android.server.ForceAppStandbyTrackerProto;
-import com.android.server.ForceAppStandbyTrackerProto.RunAnyInBackgroundRestrictedPackages;
+import com.android.server.AppStateTrackerProto;
+import com.android.server.AppStateTrackerProto.RunAnyInBackgroundRestrictedPackages;
import com.android.server.IdleDispatchEntryProto;
import com.android.server.InFlightProto;
import com.android.server.WakeupEventProto;
@@ -58,19 +58,19 @@
assertTrue(0 < settings.getAllowWhileIdleLongDurationMs());
assertTrue(0 < settings.getAllowWhileIdleWhitelistDurationMs());
- // ForceAppStandbyTrackerProto
- ForceAppStandbyTrackerProto forceAppStandbyTracker = dump.getForceAppStandbyTracker();
- for (int uid : forceAppStandbyTracker.getForegroundUidsList()) {
+ // AppStateTrackerProto
+ AppStateTrackerProto appStateTracker = dump.getAppStateTracker();
+ for (int uid : appStateTracker.getForegroundUidsList()) {
// 0 is technically a valid UID.
assertTrue(0 <= uid);
}
- for (int aid : forceAppStandbyTracker.getPowerSaveWhitelistAppIdsList()) {
+ for (int aid : appStateTracker.getPowerSaveWhitelistAppIdsList()) {
assertTrue(0 <= aid);
}
- for (int aid : forceAppStandbyTracker.getTempPowerSaveWhitelistAppIdsList()) {
+ for (int aid : appStateTracker.getTempPowerSaveWhitelistAppIdsList()) {
assertTrue(0 <= aid);
}
- for (RunAnyInBackgroundRestrictedPackages r : forceAppStandbyTracker.getRunAnyInBackgroundRestrictedPackagesList()) {
+ for (RunAnyInBackgroundRestrictedPackages r : appStateTracker.getRunAnyInBackgroundRestrictedPackagesList()) {
assertTrue(0 <= r.getUid());
}
diff --git a/hostsidetests/incident/src/com/android/server/cts/JobSchedulerIncidentTest.java b/hostsidetests/incident/src/com/android/server/cts/JobSchedulerIncidentTest.java
index 9c8363c..1c2a1fe 100644
--- a/hostsidetests/incident/src/com/android/server/cts/JobSchedulerIncidentTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/JobSchedulerIncidentTest.java
@@ -69,7 +69,7 @@
for (JobSchedulerServiceDumpProto.PendingJob pj : dump.getPendingJobsList()) {
testJobStatusShortInfoProto(pj.getInfo(), filterLevel);
testJobStatusDumpProto(pj.getDump());
- assertTrue(0 <= pj.getEnqueuedDurationMs());
+ assertTrue(0 <= pj.getPendingDurationMs());
}
for (JobSchedulerServiceDumpProto.ActiveJob aj : dump.getActiveJobsList()) {
@@ -109,10 +109,6 @@
assertTrue(0 <= c.getMaxWorkRescheduleCount());
assertTrue(0 <= c.getMinLinearBackoffTimeMs());
assertTrue(0 <= c.getMinExpBackoffTimeMs());
- assertTrue(0 <= c.getStandbyHeartbeatTimeMs());
- for (int sb : c.getStandbyBeatsList()) {
- assertTrue(0 <= sb);
- }
}
private static void testDataSetProto(DataSetProto ds) throws Exception {
@@ -186,7 +182,8 @@
assertTrue(0 <= ji.getTriggerContentMaxDelayMs());
testNetworkRequestProto(ji.getRequiredNetwork());
// JobInfo.NETWORK_BYTES_UNKNOWN (= -1) is a valid value.
- assertTrue(-1 <= ji.getTotalNetworkBytes());
+ assertTrue(-1 <= ji.getTotalNetworkDownloadBytes());
+ assertTrue(-1 <= ji.getTotalNetworkUploadBytes());
assertTrue(0 <= ji.getMinLatencyMs());
assertTrue(0 <= ji.getMaxExecutionDelayMs());
JobStatusDumpProto.JobInfo.Backoff bp = ji.getBackoffPolicy();
diff --git a/hostsidetests/media/OWNERS b/hostsidetests/media/OWNERS
index 9884b18..8d6b291e 100644
--- a/hostsidetests/media/OWNERS
+++ b/hostsidetests/media/OWNERS
@@ -1,4 +1,4 @@
-rachad@google.com
+# Bug component: 1344
elaurent@google.com
lajos@google.com
marcone@google.com
diff --git a/hostsidetests/multiuser/Android.bp b/hostsidetests/multiuser/Android.bp
index cc4bd81..ce653dd 100644
--- a/hostsidetests/multiuser/Android.bp
+++ b/hostsidetests/multiuser/Android.bp
@@ -21,6 +21,7 @@
"cts-tradefed",
"tradefed",
"platform-test-annotations-host",
+ "compatibility-host-util",
],
// tag this module as a cts test artifact
test_suites: [
diff --git a/hostsidetests/multiuser/OWNERS b/hostsidetests/multiuser/OWNERS
new file mode 100644
index 0000000..2b344eb
--- /dev/null
+++ b/hostsidetests/multiuser/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 71510
+include /tests/app/OWNERS
+
+bookatz@google.com
diff --git a/hostsidetests/multiuser/src/android/host/multiuser/CreateUsersPermissionTest.java b/hostsidetests/multiuser/src/android/host/multiuser/CreateUsersPermissionTest.java
index 41cbeab..fc385b1 100644
--- a/hostsidetests/multiuser/src/android/host/multiuser/CreateUsersPermissionTest.java
+++ b/hostsidetests/multiuser/src/android/host/multiuser/CreateUsersPermissionTest.java
@@ -17,6 +17,7 @@
import static com.android.tradefed.log.LogUtil.CLog;
+import com.android.compatibility.common.util.CddTest;
import com.android.ddmlib.Log;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
@@ -69,6 +70,7 @@
+ "command output: " + output, isErrorOutput);
}
+ @CddTest(requirement="9.5/A-1-3")
@Test
public void testCanCreateGuestUserWhenUserLimitReached() throws Exception {
if (!isAutomotiveDevice()) {
diff --git a/hostsidetests/multiuser/src/android/host/multiuser/SecondaryUsersTest.java b/hostsidetests/multiuser/src/android/host/multiuser/SecondaryUsersTest.java
index 1f4ab36..d4a6ef2 100644
--- a/hostsidetests/multiuser/src/android/host/multiuser/SecondaryUsersTest.java
+++ b/hostsidetests/multiuser/src/android/host/multiuser/SecondaryUsersTest.java
@@ -15,6 +15,7 @@
*/
package android.host.multiuser;
+import com.android.compatibility.common.util.CddTest;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import java.util.concurrent.TimeUnit;
@@ -31,6 +32,7 @@
private static final long POLL_INTERVAL_MS = 100;
+ @CddTest(requirement="9.5/A-1-2")
@Test
public void testSwitchToSecondaryUserBeforeBootComplete() throws Exception {
if (!isAutomotiveDevice() || !mSupportsMultiUser) {
@@ -58,4 +60,4 @@
}
Assert.assertTrue("Must switch to secondary user before boot complete", isUserSecondary);
}
-}
\ No newline at end of file
+}
diff --git a/hostsidetests/net/AndroidTest.xml b/hostsidetests/net/AndroidTest.xml
index dbff179..5479c51 100644
--- a/hostsidetests/net/AndroidTest.xml
+++ b/hostsidetests/net/AndroidTest.xml
@@ -31,4 +31,9 @@
<option name="jar" value="CtsHostsideNetworkTests.jar" />
<option name="runtime-hint" value="3m56s" />
</test>
+
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="directory-keys" value="/sdcard/CtsHostsideNetworkTests" />
+ <option name="collect-on-run-ended-only" value="true" />
+ </metrics_collector>
</configuration>
diff --git a/hostsidetests/net/app/Android.bp b/hostsidetests/net/app/Android.bp
index d66b71b..e988ea4 100644
--- a/hostsidetests/net/app/Android.bp
+++ b/hostsidetests/net/app/Android.bp
@@ -20,6 +20,8 @@
//sdk_version: "current",
platform_apis: true,
static_libs: [
+ "androidx.test.rules",
+ "androidx.test.ext.junit",
"compatibility-device-util-axt",
"ctstestrunner-axt",
"ub-uiautomator",
diff --git a/hostsidetests/net/app/AndroidManifest.xml b/hostsidetests/net/app/AndroidManifest.xml
index f54b5c1..3b00713 100644
--- a/hostsidetests/net/app/AndroidManifest.xml
+++ b/hostsidetests/net/app/AndroidManifest.xml
@@ -26,8 +26,9 @@
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
- <application>
+ <application android:requestLegacyExternalStorage="true" >
<uses-library android:name="android.test.runner" />
<activity android:name=".MyActivity" />
<service android:name=".MyVpnService"
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
index 55bd406..d06ab13 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
@@ -16,20 +16,27 @@
package com.android.cts.net.hostside;
+import static com.android.cts.net.hostside.Property.APP_STANDBY_MODE;
+import static com.android.cts.net.hostside.Property.BATTERY_SAVER_MODE;
+
+import static org.junit.Assert.assertEquals;
+
import android.os.SystemClock;
-import android.util.Log;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
/**
* Base class for metered and non-metered tests on idle apps.
*/
+@RequiredProperties({APP_STANDBY_MODE})
abstract class AbstractAppIdleTestCase extends AbstractRestrictBackgroundNetworkTestCase {
- @Override
- protected final void setUp() throws Exception {
+ @Before
+ public final void setUp() throws Exception {
super.setUp();
- if (!isSupported()) return;
-
// Set initial state.
removePowerSaveModeWhitelist(TEST_APP2_PKG);
removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
@@ -39,41 +46,16 @@
registerBroadcastReceiver();
}
- @Override
- protected final void tearDown() throws Exception {
+ @After
+ public final void tearDown() throws Exception {
super.tearDown();
- if (!isSupported()) return;
-
- try {
- tearDownMeteredNetwork();
- } finally {
- turnBatteryOff();
- setAppIdle(false);
- }
+ turnBatteryOff();
+ setAppIdle(false);
}
- @Override
- protected boolean isSupported() throws Exception {
- boolean supported = isDozeModeEnabled();
- if (!supported) {
- Log.i(TAG, "Skipping " + getClass() + "." + getName()
- + "() because device does not support Doze Mode");
- }
- return supported;
- }
-
- /**
- * Resets the (non) metered network state.
- *
- * <p>By default is empty - it's up to subclasses to override.
- */
- protected void tearDownMeteredNetwork() throws Exception {
- }
-
+ @Test
public void testBackgroundNetworkAccess_enabled() throws Exception {
- if (!isSupported()) return;
-
setAppIdle(true);
assertBackgroundNetworkAccess(false);
@@ -98,9 +80,8 @@
assertBackgroundNetworkAccess(false);
}
+ @Test
public void testBackgroundNetworkAccess_whitelisted() throws Exception {
- if (!isSupported()) return;
-
setAppIdle(true);
assertBackgroundNetworkAccess(false);
@@ -127,9 +108,8 @@
assertBackgroundNetworkAccess(false);
}
+ @Test
public void testBackgroundNetworkAccess_tempWhitelisted() throws Exception {
- if (!isSupported()) return;
-
setAppIdle(true);
assertBackgroundNetworkAccess(false);
@@ -140,23 +120,17 @@
assertBackgroundNetworkAccess(false);
}
+ @Test
public void testBackgroundNetworkAccess_disabled() throws Exception {
- if (!isSupported()) return;
-
assertBackgroundNetworkAccess(true);
assertsForegroundAlwaysHasNetworkAccess();
assertBackgroundNetworkAccess(true);
}
+ @RequiredProperties({BATTERY_SAVER_MODE})
+ @Test
public void testAppIdleNetworkAccess_whenCharging() throws Exception {
- if (!isBatterySaverSupported()) {
- Log.i(TAG, "Skipping " + getClass() + "." + getName()
- + "() because device does not support Battery saver mode");
- return;
- }
- if (!isSupported()) return;
-
// Check that app is paroled when charging
setAppIdle(true);
assertBackgroundNetworkAccess(false);
@@ -180,9 +154,8 @@
assertBackgroundNetworkAccess(true);
}
+ @Test
public void testAppIdleNetworkAccess_idleWhitelisted() throws Exception {
- if (!isSupported()) return;
-
setAppIdle(true);
assertAppIdle(true);
assertBackgroundNetworkAccess(false);
@@ -199,9 +172,8 @@
removeAppIdleWhitelist(mUid + 1);
}
+ @Test
public void testAppIdle_toast() throws Exception {
- if (!isSupported()) return;
-
setAppIdle(true);
assertAppIdle(true);
assertEquals("Shown", showToast());
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractBatterySaverModeTestCase.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractBatterySaverModeTestCase.java
index 931376b..04d054d 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractBatterySaverModeTestCase.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractBatterySaverModeTestCase.java
@@ -16,20 +16,22 @@
package com.android.cts.net.hostside;
-import android.text.TextUtils;
-import android.util.Log;
+import static com.android.cts.net.hostside.Property.BATTERY_SAVER_MODE;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
/**
* Base class for metered and non-metered Battery Saver Mode tests.
*/
+@RequiredProperties({BATTERY_SAVER_MODE})
abstract class AbstractBatterySaverModeTestCase extends AbstractRestrictBackgroundNetworkTestCase {
- @Override
- protected final void setUp() throws Exception {
+ @Before
+ public final void setUp() throws Exception {
super.setUp();
- if (!isSupported()) return;
-
// Set initial state.
removePowerSaveModeWhitelist(TEST_APP2_PKG);
removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
@@ -38,55 +40,15 @@
registerBroadcastReceiver();
}
- @Override
- protected final void tearDown() throws Exception {
+ @After
+ public final void tearDown() throws Exception {
super.tearDown();
- if (!isSupported()) return;
-
- try {
- tearDownMeteredNetwork();
- } finally {
- setBatterySaverMode(false);
- }
+ setBatterySaverMode(false);
}
- @Override
- protected boolean isSupported() throws Exception {
- String unSupported = "";
- if (!isDozeModeEnabled()) {
- unSupported += "Doze mode,";
- }
- if (!isBatterySaverSupported()) {
- unSupported += "Battery saver mode,";
- }
- if (!TextUtils.isEmpty(unSupported)) {
- Log.i(TAG, "Skipping " + getClass() + "." + getName()
- + "() because device does not support " + unSupported);
- return false;
- }
- return true;
- }
-
- /**
- * Sets the initial (non) metered network state.
- *
- * <p>By default is empty - it's up to subclasses to override.
- */
- protected void setUpMeteredNetwork() throws Exception {
- }
-
- /**
- * Resets the (non) metered network state.
- *
- * <p>By default is empty - it's up to subclasses to override.
- */
- protected void tearDownMeteredNetwork() throws Exception {
- }
-
+ @Test
public void testBackgroundNetworkAccess_enabled() throws Exception {
- if (!isSupported()) return;
-
setBatterySaverMode(true);
assertBackgroundNetworkAccess(false);
@@ -118,9 +80,8 @@
assertBackgroundNetworkAccess(false);
}
+ @Test
public void testBackgroundNetworkAccess_whitelisted() throws Exception {
- if (!isSupported()) return;
-
setBatterySaverMode(true);
assertBackgroundNetworkAccess(false);
@@ -140,9 +101,8 @@
assertBackgroundNetworkAccess(false);
}
+ @Test
public void testBackgroundNetworkAccess_disabled() throws Exception {
- if (!isSupported()) return;
-
assertBackgroundNetworkAccess(true);
assertsForegroundAlwaysHasNetworkAccess();
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java
index f20f1d1..6f32c56 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java
@@ -16,20 +16,25 @@
package com.android.cts.net.hostside;
+import static com.android.cts.net.hostside.Property.DOZE_MODE;
+import static com.android.cts.net.hostside.Property.NOT_LOW_RAM_DEVICE;
+
import android.os.SystemClock;
-import android.util.Log;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
/**
* Base class for metered and non-metered Doze Mode tests.
*/
+@RequiredProperties({DOZE_MODE})
abstract class AbstractDozeModeTestCase extends AbstractRestrictBackgroundNetworkTestCase {
- @Override
- protected final void setUp() throws Exception {
+ @Before
+ public final void setUp() throws Exception {
super.setUp();
- if (!isSupported()) return;
-
// Set initial state.
removePowerSaveModeWhitelist(TEST_APP2_PKG);
removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
@@ -38,48 +43,15 @@
registerBroadcastReceiver();
}
- @Override
- protected final void tearDown() throws Exception {
+ @After
+ public final void tearDown() throws Exception {
super.tearDown();
- if (!isSupported()) return;
-
- try {
- tearDownMeteredNetwork();
- } finally {
- setDozeMode(false);
- }
+ setDozeMode(false);
}
- @Override
- protected boolean isSupported() throws Exception {
- boolean supported = isDozeModeEnabled();
- if (!supported) {
- Log.i(TAG, "Skipping " + getClass() + "." + getName()
- + "() because device does not support Doze Mode");
- }
- return supported;
- }
-
- /**
- * Sets the initial (non) metered network state.
- *
- * <p>By default is empty - it's up to subclasses to override.
- */
- protected void setUpMeteredNetwork() throws Exception {
- }
-
- /**
- * Resets the (non) metered network state.
- *
- * <p>By default is empty - it's up to subclasses to override.
- */
- protected void tearDownMeteredNetwork() throws Exception {
- }
-
+ @Test
public void testBackgroundNetworkAccess_enabled() throws Exception {
- if (!isSupported()) return;
-
setDozeMode(true);
assertBackgroundNetworkAccess(false);
@@ -96,9 +68,8 @@
assertBackgroundNetworkAccess(false);
}
+ @Test
public void testBackgroundNetworkAccess_whitelisted() throws Exception {
- if (!isSupported()) return;
-
setDozeMode(true);
assertBackgroundNetworkAccess(false);
@@ -118,19 +89,18 @@
assertBackgroundNetworkAccess(false);
}
+ @Test
public void testBackgroundNetworkAccess_disabled() throws Exception {
- if (!isSupported()) return;
-
assertBackgroundNetworkAccess(true);
assertsForegroundAlwaysHasNetworkAccess();
assertBackgroundNetworkAccess(true);
}
+ @RequiredProperties({NOT_LOW_RAM_DEVICE})
+ @Test
public void testBackgroundNetworkAccess_enabledButWhitelistedOnNotificationAction()
throws Exception {
- if (!isSupported() || isLowRamDevice()) return;
-
setPendingIntentWhitelistDuration(NETWORK_TIMEOUT_MS);
try {
registerNotificationListenerService();
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
index 40d7e34..57b7bb4 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
@@ -17,14 +17,22 @@
package com.android.cts.net.hostside;
import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
-import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
-import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
-import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED;
import static android.os.BatteryManager.BATTERY_PLUGGED_AC;
import static android.os.BatteryManager.BATTERY_PLUGGED_USB;
import static android.os.BatteryManager.BATTERY_PLUGGED_WIRELESS;
-import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.executeShellCommand;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.getConnectivityManager;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.getContext;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.getInstrumentation;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.getWifiManager;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isDozeModeSupported;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.restrictBackgroundValueToString;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import android.app.ActivityManager;
import android.app.Instrumentation;
@@ -34,9 +42,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkInfo.State;
import android.net.wifi.WifiManager;
@@ -44,24 +50,27 @@
import android.os.Binder;
import android.os.Bundle;
import android.os.SystemClock;
-import android.os.SystemProperties;
import android.provider.Settings;
import android.service.notification.NotificationListenerService;
-import android.test.InstrumentationTestCase;
-import android.text.TextUtils;
import android.util.Log;
-import com.android.compatibility.common.util.BatteryUtils;
+import org.junit.Rule;
+import org.junit.rules.RuleChain;
+import org.junit.runner.RunWith;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
/**
* Superclass for tests related to background network restrictions.
*/
-abstract class AbstractRestrictBackgroundNetworkTestCase extends InstrumentationTestCase {
- protected static final String TAG = "RestrictBackgroundNetworkTests";
+@RunWith(AndroidJUnit4.class)
+public abstract class AbstractRestrictBackgroundNetworkTestCase {
+ public static final String TAG = "RestrictBackgroundNetworkTests";
protected static final String TEST_PKG = "com.android.cts.net.hostside";
protected static final String TEST_APP2_PKG = "com.android.cts.net.hostside.app2";
@@ -98,8 +107,6 @@
static final int NETWORK_TIMEOUT_MS = 15 * SECOND_IN_MS;
private static int PROCESS_STATE_FOREGROUND_SERVICE;
- private static final int PROCESS_STATE_TOP = 2;
-
private static final String KEY_NETWORK_STATE_OBSERVER = TEST_PKG + ".observer";
protected static final int TYPE_COMPONENT_ACTIVTIY = 0;
@@ -126,22 +133,23 @@
protected WifiManager mWfm;
protected int mUid;
private int mMyUid;
- private String mMeteredWifi;
private MyServiceClient mServiceClient;
private String mDeviceIdleConstantsSetting;
- private boolean mSupported;
private boolean mIsLocationOn;
- @Override
+ @Rule
+ public final RuleChain mRuleChain = RuleChain.outerRule(new DumpOnFailureRule())
+ .around(new RequiredPropertiesRule())
+ .around(new MeterednessConfigurationRule());
+
protected void setUp() throws Exception {
- super.setUp();
PROCESS_STATE_FOREGROUND_SERVICE = (Integer) ActivityManager.class
.getDeclaredField("PROCESS_STATE_FOREGROUND_SERVICE").get(null);
mInstrumentation = getInstrumentation();
- mContext = mInstrumentation.getContext();
- mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
- mWfm = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+ mContext = getContext();
+ mCm = getConnectivityManager();
+ mWfm = getWifiManager();
mUid = getUid(TEST_APP2_PKG);
mMyUid = getUid(mContext.getPackageName());
mServiceClient = new MyServiceClient(mContext);
@@ -151,10 +159,9 @@
if (!mIsLocationOn) {
enableLocation();
}
- mSupported = setUpActiveNetworkMeteringState();
setAppIdle(false);
- Log.i(TAG, "Apps status on " + getName() + ":\n"
+ Log.i(TAG, "Apps status:\n"
+ "\ttest app: uid=" + mMyUid + ", state=" + getProcessStateByUid(mMyUid) + "\n"
+ "\tapp2: uid=" + mUid + ", state=" + getProcessStateByUid(mUid));
@@ -165,16 +172,13 @@
final String currentConstants =
executeShellCommand("settings get global app_idle_constants");
assertEquals(appIdleConstants, currentConstants);
- }
+ }
- @Override
protected void tearDown() throws Exception {
if (!mIsLocationOn) {
disableLocation();
}
mServiceClient.unbind();
-
- super.tearDown();
}
private void enableLocation() throws Exception {
@@ -259,23 +263,8 @@
protected void assertRestrictBackgroundStatus(int expectedStatus) throws Exception {
final String status = mServiceClient.getRestrictBackgroundStatus();
assertNotNull("didn't get API status from app2", status);
- final String actualStatus = toString(Integer.parseInt(status));
- assertEquals("wrong status", toString(expectedStatus), actualStatus);
- }
-
- protected void assertMyRestrictBackgroundStatus(int expectedStatus) throws Exception {
- final int actualStatus = mCm.getRestrictBackgroundStatus();
- assertEquals("Wrong status", toString(expectedStatus), toString(actualStatus));
- }
-
- protected boolean isMyRestrictBackgroundStatus(int expectedStatus) throws Exception {
- final int actualStatus = mCm.getRestrictBackgroundStatus();
- if (expectedStatus != actualStatus) {
- Log.d(TAG, "Expected: " + toString(expectedStatus)
- + " but actual: " + toString(actualStatus));
- return false;
- }
- return true;
+ assertEquals(restrictBackgroundValueToString(expectedStatus),
+ restrictBackgroundValueToString(Integer.parseInt(status)));
}
protected void assertBackgroundNetworkAccess(boolean expectAllowed) throws Exception {
@@ -298,28 +287,6 @@
}
/**
- * Whether this device suport this type of test.
- *
- * <p>Should be overridden when necessary (but always calling
- * {@code super.isSupported()} first), and explicitly used before each test
- * Example:
- *
- * <pre><code>
- * public void testSomething() {
- * if (!isSupported()) return;
- * </code></pre>
- *
- * @return {@code true} by default.
- */
- protected boolean isSupported() throws Exception {
- return mSupported;
- }
-
- protected boolean isBatterySaverSupported() {
- return BatteryUtils.isBatterySaverSupported();
- }
-
- /**
* Asserts that an app always have access while on foreground or running a foreground service.
*
* <p>This method will launch an activity and a foreground service to make the assertion, but
@@ -388,23 +355,6 @@
}
/**
- * As per CDD requirements, if the device doesn't support data saver mode then
- * ConnectivityManager.getRestrictBackgroundStatus() will always return
- * RESTRICT_BACKGROUND_STATUS_DISABLED. So, enable the data saver mode and check if
- * ConnectivityManager.getRestrictBackgroundStatus() for an app in background returns
- * RESTRICT_BACKGROUND_STATUS_DISABLED or not.
- */
- protected boolean isDataSaverSupported() throws Exception {
- assertMyRestrictBackgroundStatus(RESTRICT_BACKGROUND_STATUS_DISABLED);
- try {
- setRestrictBackground(true);
- return !isMyRestrictBackgroundStatus(RESTRICT_BACKGROUND_STATUS_DISABLED);
- } finally {
- setRestrictBackground(false);
- }
- }
-
- /**
* Returns whether an app state should be considered "background" for restriction purposes.
*/
protected boolean isBackground(int state) {
@@ -443,40 +393,10 @@
// Exponential back-off.
timeoutMs = Math.min(timeoutMs*2, NETWORK_TIMEOUT_MS);
}
- dumpOnFailure();
fail("Invalid state for expectAvailable=" + expectAvailable + " after " + maxTries
+ " attempts.\nLast error: " + error);
}
- private void dumpOnFailure() throws Exception {
- dumpAllNetworkRules();
- Log.d(TAG, "Usagestats dump: " + getUsageStatsDump());
- executeShellCommand("settings get global app_idle_constants");
- }
-
- private void dumpAllNetworkRules() throws Exception {
- final String networkManagementDump = runShellCommand(mInstrumentation,
- "dumpsys network_management").trim();
- final String networkPolicyDump = runShellCommand(mInstrumentation,
- "dumpsys netpolicy").trim();
- TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter('\n');
- splitter.setString(networkManagementDump);
- String next;
- Log.d(TAG, ">>> Begin network_management dump");
- while (splitter.hasNext()) {
- next = splitter.next();
- Log.d(TAG, next);
- }
- Log.d(TAG, "<<< End network_management dump");
- splitter.setString(networkPolicyDump);
- Log.d(TAG, ">>> Begin netpolicy dump");
- while (splitter.hasNext()) {
- next = splitter.next();
- Log.d(TAG, next);
- }
- Log.d(TAG, "<<< End netpolicy dump");
- }
-
/**
* Checks whether the network is available as expected.
*
@@ -528,22 +448,10 @@
return errors.toString();
}
- protected boolean isLowRamDevice() {
- final ActivityManager am = (ActivityManager) mContext.getSystemService(
- Context.ACTIVITY_SERVICE);
- return am.isLowRamDevice();
- }
-
- protected String executeShellCommand(String command) throws Exception {
- final String result = runShellCommand(mInstrumentation, command).trim();
- if (DEBUG) Log.d(TAG, "Command '" + command + "' returned '" + result + "'");
- return result;
- }
-
/**
* Runs a Shell command which is not expected to generate output.
*/
- protected void executeSilentShellCommand(String command) throws Exception {
+ protected void executeSilentShellCommand(String command) {
final String result = executeShellCommand(command);
assertTrue("Command '" + command + "' failed: " + result, result.trim().isEmpty());
}
@@ -572,10 +480,6 @@
});
}
- protected void assertDelayedShellCommand(String command, ExpectResultChecker checker)
- throws Exception {
- assertDelayedShellCommand(command, 5, 1, checker);
- }
protected void assertDelayedShellCommand(String command, int maxTries, int napTimeSeconds,
ExpectResultChecker checker) throws Exception {
String result = "";
@@ -592,159 +496,6 @@
+ " attempts. Last result: '" + result + "'");
}
- /**
- * Sets the initial metering state for the active network.
- *
- * <p>It's called on setup and by default does nothing - it's up to the
- * subclasses to override.
- *
- * @return whether the tests in the subclass are supported on this device.
- */
- protected boolean setUpActiveNetworkMeteringState() throws Exception {
- return true;
- }
-
- /**
- * Makes sure the active network is not metered.
- *
- * <p>If the device does not supoprt un-metered networks (for example if it
- * only has cellular data but not wi-fi), it should return {@code false};
- * otherwise, it should return {@code true} (or fail if the un-metered
- * network could not be set).
- *
- * @return {@code true} if the network is now unmetered.
- */
- protected boolean setUnmeteredNetwork() throws Exception {
- final NetworkInfo info = mCm.getActiveNetworkInfo();
- assertNotNull("Could not get active network", info);
- if (!mCm.isActiveNetworkMetered()) {
- Log.d(TAG, "Active network is not metered: " + info);
- } else if (info.getType() == ConnectivityManager.TYPE_WIFI) {
- Log.i(TAG, "Setting active WI-FI network as not metered: " + info );
- setWifiMeteredStatus(false);
- } else {
- Log.d(TAG, "Active network cannot be set to un-metered: " + info);
- return false;
- }
- assertActiveNetworkMetered(false); // Sanity check.
- return true;
- }
-
- /**
- * Enables metering on the active network if supported.
- *
- * <p>If the device does not support metered networks it should return
- * {@code false}; otherwise, it should return {@code true} (or fail if the
- * metered network could not be set).
- *
- * @return {@code true} if the network is now metered.
- */
- protected boolean setMeteredNetwork() throws Exception {
- final NetworkInfo info = mCm.getActiveNetworkInfo();
- final boolean metered = mCm.isActiveNetworkMetered();
- if (metered) {
- Log.d(TAG, "Active network already metered: " + info);
- return true;
- } else if (info.getType() != ConnectivityManager.TYPE_WIFI) {
- Log.w(TAG, "Active network does not support metering: " + info);
- return false;
- } else {
- Log.w(TAG, "Active network not metered: " + info);
- }
- final String netId = setWifiMeteredStatus(true);
-
- // Set flag so status is reverted on resetMeteredNetwork();
- mMeteredWifi = netId;
- // Sanity check.
- assertWifiMeteredStatus(netId, true);
- assertActiveNetworkMetered(true);
- return true;
- }
-
- /**
- * Resets the device metering state to what it was before the test started.
- *
- * <p>This reverts any metering changes made by {@code setMeteredNetwork}.
- */
- protected void resetMeteredNetwork() throws Exception {
- if (mMeteredWifi != null) {
- Log.i(TAG, "resetMeteredNetwork(): SID '" + mMeteredWifi
- + "' was set as metered by test case; resetting it");
- setWifiMeteredStatus(mMeteredWifi, false);
- assertActiveNetworkMetered(false); // Sanity check.
- }
- }
-
- private void assertActiveNetworkMetered(boolean expected) throws Exception {
- final int maxTries = 5;
- NetworkInfo info = null;
- for (int i = 1; i <= maxTries; i++) {
- info = mCm.getActiveNetworkInfo();
- if (info == null) {
- Log.v(TAG, "No active network info on attempt #" + i
- + "; sleeping 1s before polling again");
- } else if (mCm.isActiveNetworkMetered() != expected) {
- Log.v(TAG, "Wrong metered status for active network " + info + "; expected="
- + expected + "; sleeping 1s before polling again");
- } else {
- break;
- }
- Thread.sleep(SECOND_IN_MS);
- }
- assertNotNull("No active network after " + maxTries + " attempts", info);
- assertEquals("Wrong metered status for active network " + info, expected,
- mCm.isActiveNetworkMetered());
- }
-
- private String setWifiMeteredStatus(boolean metered) throws Exception {
- // We could call setWifiEnabled() here, but it might take sometime to be in a consistent
- // state (for example, if one of the saved network is not properly authenticated), so it's
- // better to let the hostside test take care of that.
- assertTrue("wi-fi is disabled", mWfm.isWifiEnabled());
- // TODO: if it's not guaranteed the device has wi-fi, we need to change the tests
- // to make the actual verification of restrictions optional.
- final String ssid = mWfm.getConnectionInfo().getSSID();
- return setWifiMeteredStatus(ssid, metered);
- }
-
- private String setWifiMeteredStatus(String ssid, boolean metered) throws Exception {
- assertNotNull("null SSID", ssid);
- final String netId = ssid.trim().replaceAll("\"", ""); // remove quotes, if any.
- assertFalse("empty SSID", ssid.isEmpty());
-
- Log.i(TAG, "Setting wi-fi network " + netId + " metered status to " + metered);
- final String setCommand = "cmd netpolicy set metered-network " + netId + " " + metered;
- assertDelayedShellCommand(setCommand, "");
-
- return netId;
- }
-
- private void assertWifiMeteredStatus(String netId, boolean status) throws Exception {
- final String command = "cmd netpolicy list wifi-networks";
- final String expectedLine = netId + ";" + status;
- assertDelayedShellCommand(command, new ExpectResultChecker() {
-
- @Override
- public boolean isExpected(String result) {
- return result.contains(expectedLine);
- }
-
- @Override
- public String getExpected() {
- return "line containing " + expectedLine;
- }
- });
- }
-
- protected void setRestrictBackground(boolean enabled) throws Exception {
- executeShellCommand("cmd netpolicy set restrict-background " + enabled);
- final String output = executeShellCommand("cmd netpolicy get restrict-background ");
- final String expectedSuffix = enabled ? "enabled" : "disabled";
- // TODO: use MoreAsserts?
- assertTrue("output '" + output + "' should end with '" + expectedSuffix + "'",
- output.endsWith(expectedSuffix));
- }
-
protected void addRestrictBackgroundWhitelist(int uid) throws Exception {
executeShellCommand("cmd netpolicy add restrict-background-whitelist " + uid);
assertRestrictBackgroundWhitelist(uid, true);
@@ -924,7 +675,7 @@
protected void setDozeMode(boolean enabled) throws Exception {
// Sanity check, since tests should check beforehand....
- assertTrue("Device does not support Doze Mode", isDozeModeEnabled());
+ assertTrue("Device does not support Doze Mode", isDozeModeSupported());
Log.i(TAG, "Setting Doze Mode to " + enabled);
if (enabled) {
@@ -944,43 +695,16 @@
assertDelayedShellCommand("dumpsys deviceidle get deep", enabled ? "IDLE" : "ACTIVE");
}
- protected boolean isDozeModeEnabled() throws Exception {
- final String result = executeShellCommand("cmd deviceidle enabled deep").trim();
- return result.equals("1");
- }
-
protected void setAppIdle(boolean enabled) throws Exception {
Log.i(TAG, "Setting app idle to " + enabled);
executeSilentShellCommand("am set-inactive " + TEST_APP2_PKG + " " + enabled );
assertAppIdle(enabled); // Sanity check
}
- private String getUsageStatsDump() throws Exception {
- final String output = runShellCommand(mInstrumentation, "dumpsys usagestats").trim();
- final StringBuilder sb = new StringBuilder();
- final TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter('\n');
- splitter.setString(output);
- String str;
- while (splitter.hasNext()) {
- str = splitter.next();
- if (str.contains("package=")
- && !str.contains(TEST_PKG) && !str.contains(TEST_APP2_PKG)) {
- continue;
- }
- if (str.trim().startsWith("config=") || str.trim().startsWith("time=")) {
- continue;
- }
- sb.append(str).append('\n');
- }
- return sb.toString();
- }
-
protected void assertAppIdle(boolean enabled) throws Exception {
try {
assertDelayedShellCommand("am get-inactive " + TEST_APP2_PKG, 15, 2, "Idle=" + enabled);
} catch (Throwable e) {
- Log.d(TAG, "UsageStats dump:\n" + getUsageStatsDump());
- executeShellCommand("settings get global app_idle_constants");
throw e;
}
}
@@ -1061,12 +785,10 @@
// App didn't come to foreground when the activity is started, so try again.
assertForegroundNetworkAccess();
} else {
- dumpOnFailure();
fail("Network is not available for app2 (" + mUid + "): " + errors[0]);
}
}
} else {
- dumpOnFailure();
fail("Timed out waiting for network availability status from app2 (" + mUid + ")");
}
} else {
@@ -1150,19 +872,6 @@
}
}
- private String toString(int status) {
- switch (status) {
- case RESTRICT_BACKGROUND_STATUS_DISABLED:
- return "DISABLED";
- case RESTRICT_BACKGROUND_STATUS_WHITELISTED:
- return "WHITELISTED";
- case RESTRICT_BACKGROUND_STATUS_ENABLED:
- return "ENABLED";
- default:
- return "UNKNOWN_STATUS_" + status;
- }
- }
-
private ProcessState getProcessStateByUid(int uid) throws Exception {
return new ProcessState(executeShellCommand("cmd activity get-uid-state " + uid));
}
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/AppIdleMeteredTest.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/AppIdleMeteredTest.java
index 622d993..f1858d6 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/AppIdleMeteredTest.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/AppIdleMeteredTest.java
@@ -16,15 +16,8 @@
package com.android.cts.net.hostside;
+import static com.android.cts.net.hostside.Property.METERED_NETWORK;
+
+@RequiredProperties({METERED_NETWORK})
public class AppIdleMeteredTest extends AbstractAppIdleTestCase {
-
- @Override
- protected boolean setUpActiveNetworkMeteringState() throws Exception {
- return setMeteredNetwork();
- }
-
- @Override
- protected void tearDownMeteredNetwork() throws Exception {
- resetMeteredNetwork();
- }
}
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/AppIdleNonMeteredTest.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/AppIdleNonMeteredTest.java
index bde71f9..e737a6d 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/AppIdleNonMeteredTest.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/AppIdleNonMeteredTest.java
@@ -16,9 +16,8 @@
package com.android.cts.net.hostside;
+import static com.android.cts.net.hostside.Property.NON_METERED_NETWORK;
+
+@RequiredProperties({NON_METERED_NETWORK})
public class AppIdleNonMeteredTest extends AbstractAppIdleTestCase {
- @Override
- protected boolean setUpActiveNetworkMeteringState() throws Exception {
- return setUnmeteredNetwork();
- }
}
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/BatterySaverModeMeteredTest.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/BatterySaverModeMeteredTest.java
index 3071cfe..c78ca2e 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/BatterySaverModeMeteredTest.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/BatterySaverModeMeteredTest.java
@@ -16,15 +16,8 @@
package com.android.cts.net.hostside;
+import static com.android.cts.net.hostside.Property.METERED_NETWORK;
+
+@RequiredProperties({METERED_NETWORK})
public class BatterySaverModeMeteredTest extends AbstractBatterySaverModeTestCase {
-
- @Override
- protected boolean setUpActiveNetworkMeteringState() throws Exception {
- return setMeteredNetwork();
- }
-
- @Override
- protected void tearDownMeteredNetwork() throws Exception {
- resetMeteredNetwork();
- }
}
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/BatterySaverModeNonMeteredTest.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/BatterySaverModeNonMeteredTest.java
index 6d3076f..fb52a54 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/BatterySaverModeNonMeteredTest.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/BatterySaverModeNonMeteredTest.java
@@ -16,10 +16,9 @@
package com.android.cts.net.hostside;
-public class BatterySaverModeNonMeteredTest extends AbstractBatterySaverModeTestCase {
- @Override
- protected boolean setUpActiveNetworkMeteringState() throws Exception {
- return setUnmeteredNetwork();
- }
+import static com.android.cts.net.hostside.Property.NON_METERED_NETWORK;
+
+@RequiredProperties({NON_METERED_NETWORK})
+public class BatterySaverModeNonMeteredTest extends AbstractBatterySaverModeTestCase {
}
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/DataSaverModeTest.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/DataSaverModeTest.java
index cfe6a73..aa2c914 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/DataSaverModeTest.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/DataSaverModeTest.java
@@ -20,24 +20,33 @@
import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED;
-import android.util.Log;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.setRestrictBackground;
+import static com.android.cts.net.hostside.Property.DATA_SAVER_MODE;
+import static com.android.cts.net.hostside.Property.METERED_NETWORK;
+import static com.android.cts.net.hostside.Property.NO_DATA_SAVER_MODE;
+
+import static org.junit.Assert.fail;
import com.android.compatibility.common.util.CddTest;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import androidx.test.filters.LargeTest;
+
+@RequiredProperties({DATA_SAVER_MODE, METERED_NETWORK})
+@LargeTest
public class DataSaverModeTest extends AbstractRestrictBackgroundNetworkTestCase {
private static final String[] REQUIRED_WHITELISTED_PACKAGES = {
"com.android.providers.downloads"
};
- private boolean mIsDataSaverSupported;
-
- @Override
+ @Before
public void setUp() throws Exception {
super.setUp();
- mIsDataSaverSupported = isDataSaverSupported();
-
// Set initial state.
setRestrictBackground(false);
removeRestrictBackgroundWhitelist(mUid);
@@ -47,36 +56,15 @@
assertRestrictBackgroundChangedReceived(0);
}
- @Override
- protected void tearDown() throws Exception {
+ @After
+ public void tearDown() throws Exception {
super.tearDown();
- if (!isSupported()) return;
-
- try {
- resetMeteredNetwork();
- } finally {
- setRestrictBackground(false);
- }
+ setRestrictBackground(false);
}
- @Override
- protected boolean setUpActiveNetworkMeteringState() throws Exception {
- return setMeteredNetwork();
- }
-
- @Override
- protected boolean isSupported() throws Exception {
- if (!mIsDataSaverSupported) {
- Log.i(TAG, "Skipping " + getClass() + "." + getName()
- + "() because device does not support Data Saver Mode");
- }
- return mIsDataSaverSupported && super.isSupported();
- }
-
+ @Test
public void testGetRestrictBackgroundStatus_disabled() throws Exception {
- if (!isSupported()) return;
-
assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_DISABLED);
// Sanity check: make sure status is always disabled, never whitelisted
@@ -88,9 +76,8 @@
assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_DISABLED);
}
+ @Test
public void testGetRestrictBackgroundStatus_whitelisted() throws Exception {
- if (!isSupported()) return;
-
setRestrictBackground(true);
assertRestrictBackgroundChangedReceived(1);
assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
@@ -107,9 +94,8 @@
assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
}
+ @Test
public void testGetRestrictBackgroundStatus_enabled() throws Exception {
- if (!isSupported()) return;
-
setRestrictBackground(true);
assertRestrictBackgroundChangedReceived(1);
assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
@@ -142,9 +128,8 @@
assertBackgroundNetworkAccess(false);
}
+ @Test
public void testGetRestrictBackgroundStatus_blacklisted() throws Exception {
- if (!isSupported()) return;
-
addRestrictBackgroundBlacklist(mUid);
assertRestrictBackgroundChangedReceived(1);
assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
@@ -180,9 +165,8 @@
assertsForegroundAlwaysHasNetworkAccess();
}
+ @Test
public void testGetRestrictBackgroundStatus_requiredWhitelistedPackages() throws Exception {
- if (!isSupported()) return;
-
final StringBuilder error = new StringBuilder();
for (String packageName : REQUIRED_WHITELISTED_PACKAGES) {
int uid = -1;
@@ -202,10 +186,10 @@
}
}
+ @RequiredProperties({NO_DATA_SAVER_MODE})
@CddTest(requirement="7.4.7/C-2-2")
+ @Test
public void testBroadcastNotSentOnUnsupportedDevices() throws Exception {
- if (isSupported()) return;
-
setRestrictBackground(true);
assertRestrictBackgroundChangedReceived(0);
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/DozeModeMeteredTest.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/DozeModeMeteredTest.java
index e4189af..4306c99 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/DozeModeMeteredTest.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/DozeModeMeteredTest.java
@@ -16,15 +16,8 @@
package com.android.cts.net.hostside;
+import static com.android.cts.net.hostside.Property.METERED_NETWORK;
+
+@RequiredProperties({METERED_NETWORK})
public class DozeModeMeteredTest extends AbstractDozeModeTestCase {
-
- @Override
- protected boolean setUpActiveNetworkMeteringState() throws Exception {
- return setMeteredNetwork();
- }
-
- @Override
- protected void tearDownMeteredNetwork() throws Exception {
- resetMeteredNetwork();
- }
}
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/DozeModeNonMeteredTest.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/DozeModeNonMeteredTest.java
index edbbb9e..1e89f15 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/DozeModeNonMeteredTest.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/DozeModeNonMeteredTest.java
@@ -16,10 +16,8 @@
package com.android.cts.net.hostside;
-public class DozeModeNonMeteredTest extends AbstractDozeModeTestCase {
+import static com.android.cts.net.hostside.Property.NON_METERED_NETWORK;
- @Override
- protected boolean setUpActiveNetworkMeteringState() throws Exception {
- return setUnmeteredNetwork();
- }
+@RequiredProperties({NON_METERED_NETWORK})
+public class DozeModeNonMeteredTest extends AbstractDozeModeTestCase {
}
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/DumpOnFailureRule.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/DumpOnFailureRule.java
new file mode 100644
index 0000000..cedd62a
--- /dev/null
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/DumpOnFailureRule.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.net.hostside;
+
+import static com.android.cts.net.hostside.AbstractRestrictBackgroundNetworkTestCase.TAG;
+import static com.android.cts.net.hostside.AbstractRestrictBackgroundNetworkTestCase.TEST_PKG;
+
+import android.os.Environment;
+import android.os.FileUtils;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import com.android.compatibility.common.util.OnFailureRule;
+
+import org.junit.AssumptionViolatedException;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+public class DumpOnFailureRule extends OnFailureRule {
+ private File mDumpDir = new File(Environment.getExternalStorageDirectory(),
+ "CtsHostsideNetworkTests");
+
+ @Override
+ public void onTestFailure(Statement base, Description description, Throwable throwable) {
+ final String testName = description.getClassName() + "_" + description.getMethodName();
+
+ if (throwable instanceof AssumptionViolatedException) {
+ Log.d(TAG, "Skipping test " + testName + ": " + throwable);
+ return;
+ }
+
+ prepareDumpRootDir();
+ final File dumpFile = new File(mDumpDir, "dump-" + testName);
+ Log.i(TAG, "Dumping debug info for " + description + ": " + dumpFile.getPath());
+ try (FileOutputStream out = new FileOutputStream(dumpFile)) {
+ for (String cmd : new String[] {
+ "dumpsys netpolicy",
+ "dumpsys network_management",
+ "dumpsys usagestats " + TEST_PKG,
+ "dumpsys usagestats appstandby",
+ }) {
+ dumpCommandOutput(out, cmd);
+ }
+ } catch (FileNotFoundException e) {
+ Log.e(TAG, "Error opening file: " + dumpFile, e);
+ } catch (IOException e) {
+ Log.e(TAG, "Error closing file: " + dumpFile, e);
+ }
+ }
+
+ void dumpCommandOutput(FileOutputStream out, String cmd) {
+ final ParcelFileDescriptor pfd = InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation().executeShellCommand(cmd);
+ try (FileInputStream in = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
+ out.write(("Output of '" + cmd + "':\n").getBytes(StandardCharsets.UTF_8));
+ FileUtils.copy(in, out);
+ out.write("\n\n=================================================================\n\n"
+ .getBytes(StandardCharsets.UTF_8));
+ } catch (IOException e) {
+ Log.e(TAG, "Error dumping '" + cmd + "'", e);
+ }
+ }
+
+ void prepareDumpRootDir() {
+ if (!mDumpDir.exists() && !mDumpDir.mkdir()) {
+ Log.e(TAG, "Error creating " + mDumpDir);
+ }
+ }
+}
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/MeterednessConfigurationRule.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/MeterednessConfigurationRule.java
new file mode 100644
index 0000000..8fadf9e
--- /dev/null
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/MeterednessConfigurationRule.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.net.hostside;
+
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.resetMeteredNetwork;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.setupMeteredNetwork;
+import static com.android.cts.net.hostside.Property.METERED_NETWORK;
+import static com.android.cts.net.hostside.Property.NON_METERED_NETWORK;
+
+import android.util.ArraySet;
+import android.util.Pair;
+
+import com.android.compatibility.common.util.BeforeAfterRule;
+
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+public class MeterednessConfigurationRule extends BeforeAfterRule {
+ private Pair<String, Boolean> mSsidAndInitialMeteredness;
+
+ @Override
+ public void onBefore(Statement base, Description description) throws Throwable {
+ final ArraySet<Property> requiredProperties
+ = RequiredPropertiesRule.getRequiredProperties();
+ if (requiredProperties.contains(METERED_NETWORK)) {
+ configureNetworkMeteredness(true);
+ } else if (requiredProperties.contains(NON_METERED_NETWORK)) {
+ configureNetworkMeteredness(false);
+ }
+ }
+
+ @Override
+ public void onAfter(Statement base, Description description) throws Throwable {
+ resetNetworkMeteredness();
+ }
+
+ public void configureNetworkMeteredness(boolean metered) throws Exception {
+ mSsidAndInitialMeteredness = setupMeteredNetwork(metered);
+ }
+
+ public void resetNetworkMeteredness() throws Exception {
+ if (mSsidAndInitialMeteredness != null) {
+ resetMeteredNetwork(mSsidAndInitialMeteredness.first,
+ mSsidAndInitialMeteredness.second);
+ }
+ }
+}
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/MixedModesTest.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/MixedModesTest.java
index b1a2186..c9edda6 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/MixedModesTest.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/MixedModesTest.java
@@ -15,9 +15,21 @@
*/
package com.android.cts.net.hostside;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.setRestrictBackground;
+import static com.android.cts.net.hostside.Property.APP_STANDBY_MODE;
+import static com.android.cts.net.hostside.Property.BATTERY_SAVER_MODE;
+import static com.android.cts.net.hostside.Property.DATA_SAVER_MODE;
+import static com.android.cts.net.hostside.Property.DOZE_MODE;
+import static com.android.cts.net.hostside.Property.METERED_NETWORK;
+import static com.android.cts.net.hostside.Property.NON_METERED_NETWORK;
+
import android.os.SystemClock;
import android.util.Log;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
/**
* Test cases for the more complex scenarios where multiple restrictions (like Battery Saver Mode
* and Data Saver Mode) are applied simultaneously.
@@ -29,12 +41,10 @@
public class MixedModesTest extends AbstractRestrictBackgroundNetworkTestCase {
private static final String TAG = "MixedModesTest";
- @Override
+ @Before
public void setUp() throws Exception {
super.setUp();
- if (!isSupported()) return;
-
// Set initial state.
removeRestrictBackgroundWhitelist(mUid);
removeRestrictBackgroundBlacklist(mUid);
@@ -44,12 +54,10 @@
registerBroadcastReceiver();
}
- @Override
- protected void tearDown() throws Exception {
+ @After
+ public void tearDown() throws Exception {
super.tearDown();
- if (!isSupported()) return;
-
try {
setRestrictBackground(false);
} finally {
@@ -57,34 +65,15 @@
}
}
- @Override
- public boolean isSupported() throws Exception {
- if (!isDozeModeEnabled()) {
- Log.i(TAG, "Skipping " + getClass() + "." + getName()
- + "() because device does not support Doze Mode");
- return false;
- }
- return true;
- }
-
/**
* Tests all DS ON and BS ON scenarios from network-policy-restrictions.md on metered networks.
*/
+ @RequiredProperties({DATA_SAVER_MODE, BATTERY_SAVER_MODE, METERED_NETWORK})
+ @Test
public void testDataAndBatterySaverModes_meteredNetwork() throws Exception {
- if (!isBatterySaverSupported()) {
- Log.i(TAG, "Skipping " + getClass() + "." + getName()
- + "() because device does not support Battery saver mode");
- return;
- }
- if (!isSupported()) return;
-
- Log.i(TAG, "testDataAndBatterySaverModes_meteredNetwork() tests");
- if (!setMeteredNetwork()) {
- Log.w(TAG, "testDataAndBatterySaverModes_meteredNetwork() skipped because "
- + "device cannot use a metered network");
- return;
- }
-
+ final MeterednessConfigurationRule meterednessConfiguration
+ = new MeterednessConfigurationRule();
+ meterednessConfiguration.configureNetworkMeteredness(true);
try {
setRestrictBackground(true);
setBatterySaverMode(true);
@@ -137,7 +126,7 @@
removeRestrictBackgroundBlacklist(mUid);
removePowerSaveModeWhitelist(TEST_APP2_PKG);
} finally {
- resetMeteredNetwork();
+ meterednessConfiguration.resetNetworkMeteredness();
}
}
@@ -145,86 +134,75 @@
* Tests all DS ON and BS ON scenarios from network-policy-restrictions.md on non-metered
* networks.
*/
+ @RequiredProperties({DATA_SAVER_MODE, BATTERY_SAVER_MODE, NON_METERED_NETWORK})
+ @Test
public void testDataAndBatterySaverModes_nonMeteredNetwork() throws Exception {
- if (!isBatterySaverSupported()) {
- Log.i(TAG, "Skipping " + getClass() + "." + getName()
- + "() because device does not support Battery saver mode");
- return;
+ final MeterednessConfigurationRule meterednessConfiguration
+ = new MeterednessConfigurationRule();
+ meterednessConfiguration.configureNetworkMeteredness(false);
+ try {
+ setRestrictBackground(true);
+ setBatterySaverMode(true);
+
+ Log.v(TAG, "Not whitelisted for any.");
+ assertBackgroundNetworkAccess(false);
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(false);
+
+ Log.v(TAG, "Whitelisted for Data Saver but not for Battery Saver.");
+ addRestrictBackgroundWhitelist(mUid);
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(false);
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(false);
+ removeRestrictBackgroundWhitelist(mUid);
+
+ Log.v(TAG, "Whitelisted for Battery Saver but not for Data Saver.");
+ addPowerSaveModeWhitelist(TEST_APP2_PKG);
+ removeRestrictBackgroundWhitelist(mUid);
+ assertBackgroundNetworkAccess(true);
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(true);
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+
+ Log.v(TAG, "Whitelisted for both.");
+ addRestrictBackgroundWhitelist(mUid);
+ addPowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(true);
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(true);
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(false);
+ removeRestrictBackgroundWhitelist(mUid);
+
+ Log.v(TAG, "Blacklisted for Data Saver, not whitelisted for Battery Saver.");
+ addRestrictBackgroundBlacklist(mUid);
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(false);
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(false);
+ removeRestrictBackgroundBlacklist(mUid);
+
+ Log.v(TAG, "Blacklisted for Data Saver, whitelisted for Battery Saver.");
+ addRestrictBackgroundBlacklist(mUid);
+ addPowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(true);
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(true);
+ removeRestrictBackgroundBlacklist(mUid);
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ } finally {
+ meterednessConfiguration.resetNetworkMeteredness();
}
- if (!isSupported()) return;
-
- if (!setUnmeteredNetwork()) {
- Log.w(TAG, "testDataAndBatterySaverModes_nonMeteredNetwork() skipped because network"
- + " is metered");
- return;
- }
- Log.i(TAG, "testDataAndBatterySaverModes_nonMeteredNetwork() tests");
- setRestrictBackground(true);
- setBatterySaverMode(true);
-
- Log.v(TAG, "Not whitelisted for any.");
- assertBackgroundNetworkAccess(false);
- assertsForegroundAlwaysHasNetworkAccess();
- assertBackgroundNetworkAccess(false);
-
- Log.v(TAG, "Whitelisted for Data Saver but not for Battery Saver.");
- addRestrictBackgroundWhitelist(mUid);
- removePowerSaveModeWhitelist(TEST_APP2_PKG);
- assertBackgroundNetworkAccess(false);
- assertsForegroundAlwaysHasNetworkAccess();
- assertBackgroundNetworkAccess(false);
- removeRestrictBackgroundWhitelist(mUid);
-
- Log.v(TAG, "Whitelisted for Battery Saver but not for Data Saver.");
- addPowerSaveModeWhitelist(TEST_APP2_PKG);
- removeRestrictBackgroundWhitelist(mUid);
- assertBackgroundNetworkAccess(true);
- assertsForegroundAlwaysHasNetworkAccess();
- assertBackgroundNetworkAccess(true);
- removePowerSaveModeWhitelist(TEST_APP2_PKG);
-
- Log.v(TAG, "Whitelisted for both.");
- addRestrictBackgroundWhitelist(mUid);
- addPowerSaveModeWhitelist(TEST_APP2_PKG);
- assertBackgroundNetworkAccess(true);
- assertsForegroundAlwaysHasNetworkAccess();
- assertBackgroundNetworkAccess(true);
- removePowerSaveModeWhitelist(TEST_APP2_PKG);
- assertBackgroundNetworkAccess(false);
- removeRestrictBackgroundWhitelist(mUid);
-
- Log.v(TAG, "Blacklisted for Data Saver, not whitelisted for Battery Saver.");
- addRestrictBackgroundBlacklist(mUid);
- removePowerSaveModeWhitelist(TEST_APP2_PKG);
- assertBackgroundNetworkAccess(false);
- assertsForegroundAlwaysHasNetworkAccess();
- assertBackgroundNetworkAccess(false);
- removeRestrictBackgroundBlacklist(mUid);
-
- Log.v(TAG, "Blacklisted for Data Saver, whitelisted for Battery Saver.");
- addRestrictBackgroundBlacklist(mUid);
- addPowerSaveModeWhitelist(TEST_APP2_PKG);
- assertBackgroundNetworkAccess(true);
- assertsForegroundAlwaysHasNetworkAccess();
- assertBackgroundNetworkAccess(true);
- removeRestrictBackgroundBlacklist(mUid);
- removePowerSaveModeWhitelist(TEST_APP2_PKG);
}
/**
* Tests that powersave whitelists works as expected when doze and battery saver modes
* are enabled.
*/
+ @RequiredProperties({DOZE_MODE, BATTERY_SAVER_MODE})
+ @Test
public void testDozeAndBatterySaverMode_powerSaveWhitelists() throws Exception {
- if (!isBatterySaverSupported()) {
- Log.i(TAG, "Skipping " + getClass() + "." + getName()
- + "() because device does not support Battery saver mode");
- return;
- }
- if (!isSupported()) {
- return;
- }
-
setBatterySaverMode(true);
setDozeMode(true);
@@ -250,11 +228,9 @@
* Tests that powersave whitelists works as expected when doze and appIdle modes
* are enabled.
*/
+ @RequiredProperties({DOZE_MODE, APP_STANDBY_MODE})
+ @Test
public void testDozeAndAppIdle_powerSaveWhitelists() throws Exception {
- if (!isSupported()) {
- return;
- }
-
setDozeMode(true);
setAppIdle(true);
@@ -276,11 +252,9 @@
}
}
+ @RequiredProperties({APP_STANDBY_MODE, DOZE_MODE})
+ @Test
public void testAppIdleAndDoze_tempPowerSaveWhitelists() throws Exception {
- if (!isSupported()) {
- return;
- }
-
setDozeMode(true);
setAppIdle(true);
@@ -299,16 +273,9 @@
}
}
+ @RequiredProperties({APP_STANDBY_MODE, BATTERY_SAVER_MODE})
+ @Test
public void testAppIdleAndBatterySaver_tempPowerSaveWhitelists() throws Exception {
- if (!isBatterySaverSupported()) {
- Log.i(TAG, "Skipping " + getClass() + "." + getName()
- + "() because device does not support Battery saver mode");
- return;
- }
- if (!isSupported()) {
- return;
- }
-
setBatterySaverMode(true);
setAppIdle(true);
@@ -330,11 +297,9 @@
/**
* Tests that the app idle whitelist works as expected when doze and appIdle mode are enabled.
*/
+ @RequiredProperties({DOZE_MODE, APP_STANDBY_MODE})
+ @Test
public void testDozeAndAppIdle_appIdleWhitelist() throws Exception {
- if (!isSupported()) {
- return;
- }
-
setDozeMode(true);
setAppIdle(true);
@@ -353,11 +318,9 @@
}
}
+ @RequiredProperties({APP_STANDBY_MODE, DOZE_MODE})
+ @Test
public void testAppIdleAndDoze_tempPowerSaveAndAppIdleWhitelists() throws Exception {
- if (!isSupported()) {
- return;
- }
-
setDozeMode(true);
setAppIdle(true);
@@ -380,16 +343,9 @@
}
}
+ @RequiredProperties({APP_STANDBY_MODE, BATTERY_SAVER_MODE})
+ @Test
public void testAppIdleAndBatterySaver_tempPowerSaveAndAppIdleWhitelists() throws Exception {
- if (!isBatterySaverSupported()) {
- Log.i(TAG, "Skipping " + getClass() + "." + getName()
- + "() because device does not support Battery saver mode");
- return;
- }
- if (!isSupported()) {
- return;
- }
-
setBatterySaverMode(true);
setAppIdle(true);
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java
index 24dde9d..ed397b9 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java
@@ -16,15 +16,26 @@
package com.android.cts.net.hostside;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.setRestrictBackground;
+import static com.android.cts.net.hostside.Property.BATTERY_SAVER_MODE;
+import static com.android.cts.net.hostside.Property.DATA_SAVER_MODE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+
import android.net.Network;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
import java.util.Objects;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
public class NetworkCallbackTest extends AbstractRestrictBackgroundNetworkTestCase {
- private boolean mIsDataSaverSupported;
private Network mNetwork;
private final TestNetworkCallback mTestNetworkCallback = new TestNetworkCallback();
@@ -132,108 +143,122 @@
}
}
- @Override
+ @Before
public void setUp() throws Exception {
super.setUp();
- mIsDataSaverSupported = isDataSaverSupported();
-
mNetwork = mCm.getActiveNetwork();
- // Set initial state.
- setBatterySaverMode(false);
registerBroadcastReceiver();
- if (!mIsDataSaverSupported) return;
- setRestrictBackground(false);
removeRestrictBackgroundWhitelist(mUid);
removeRestrictBackgroundBlacklist(mUid);
assertRestrictBackgroundChangedReceived(0);
}
- @Override
- protected void tearDown() throws Exception {
+ @After
+ public void tearDown() throws Exception {
super.tearDown();
- if (!mIsDataSaverSupported) return;
+ setRestrictBackground(false);
+ setBatterySaverMode(false);
+ }
+ @RequiredProperties({DATA_SAVER_MODE})
+ @Test
+ public void testOnBlockedStatusChanged_dataSaver() throws Exception {
+ // Initial state
+ setBatterySaverMode(false);
+ setRestrictBackground(false);
+
+ final MeterednessConfigurationRule meterednessConfiguration
+ = new MeterednessConfigurationRule();
+ meterednessConfiguration.configureNetworkMeteredness(true);
try {
- resetMeteredNetwork();
+ // Register callback
+ registerNetworkCallback((INetworkCallback.Stub) mTestNetworkCallback);
+ mTestNetworkCallback.expectAvailableCallback(mNetwork);
+ mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+
+ // Enable restrict background
+ setRestrictBackground(true);
+ assertBackgroundNetworkAccess(false);
+ mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
+
+ // Add to whitelist
+ addRestrictBackgroundWhitelist(mUid);
+ assertBackgroundNetworkAccess(true);
+ mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+
+ // Remove from whitelist
+ removeRestrictBackgroundWhitelist(mUid);
+ assertBackgroundNetworkAccess(false);
+ mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
} finally {
+ meterednessConfiguration.resetNetworkMeteredness();
+ }
+
+ // Set to non-metered network
+ meterednessConfiguration.configureNetworkMeteredness(false);
+ try {
+ assertBackgroundNetworkAccess(true);
+ mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+
+ // Disable restrict background, should not trigger callback
setRestrictBackground(false);
+ assertBackgroundNetworkAccess(true);
+ mTestNetworkCallback.assertNoCallback();
+ } finally {
+ meterednessConfiguration.resetNetworkMeteredness();
}
}
- public void testOnBlockedStatusChanged_data_saver() throws Exception {
- if (!mIsDataSaverSupported) return;
-
- // Prepare metered wifi
- if (!setMeteredNetwork()) return;
-
- // Register callback
- registerNetworkCallback((INetworkCallback.Stub) mTestNetworkCallback);
- mTestNetworkCallback.expectAvailableCallback(mNetwork);
- mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
-
- // Enable restrict background
- setRestrictBackground(true);
- assertBackgroundNetworkAccess(false);
- mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
-
- // Add to whitelist
- addRestrictBackgroundWhitelist(mUid);
- assertBackgroundNetworkAccess(true);
- mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
-
- // Remove from whitelist
- removeRestrictBackgroundWhitelist(mUid);
- assertBackgroundNetworkAccess(false);
- mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
-
- // Set to non-metered network
- setUnmeteredNetwork();
- assertBackgroundNetworkAccess(true);
- mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
-
- // Disable restrict background, should not trigger callback
+ @RequiredProperties({BATTERY_SAVER_MODE})
+ @Test
+ public void testOnBlockedStatusChanged_powerSaver() throws Exception {
+ // Set initial state.
+ setBatterySaverMode(false);
setRestrictBackground(false);
- assertBackgroundNetworkAccess(true);
- mTestNetworkCallback.assertNoCallback();
- }
+ final MeterednessConfigurationRule meterednessConfiguration
+ = new MeterednessConfigurationRule();
+ meterednessConfiguration.configureNetworkMeteredness(true);
+ try {
+ // Register callback
+ registerNetworkCallback((INetworkCallback.Stub) mTestNetworkCallback);
+ mTestNetworkCallback.expectAvailableCallback(mNetwork);
+ mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
- public void testOnBlockedStatusChanged_power_saver() throws Exception {
- // Prepare metered wifi
- if (!setMeteredNetwork()) return;
+ // Enable Power Saver
+ setBatterySaverMode(true);
+ assertBackgroundNetworkAccess(false);
+ mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
- // Register callback
- registerNetworkCallback((INetworkCallback.Stub) mTestNetworkCallback);
- mTestNetworkCallback.expectAvailableCallback(mNetwork);
- mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
-
- // Enable Power Saver
- setBatterySaverMode(true);
- assertBackgroundNetworkAccess(false);
- mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
-
- // Disable Power Saver
- setBatterySaverMode(false);
- assertBackgroundNetworkAccess(true);
- mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+ // Disable Power Saver
+ setBatterySaverMode(false);
+ assertBackgroundNetworkAccess(true);
+ mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+ } finally {
+ meterednessConfiguration.resetNetworkMeteredness();
+ }
// Set to non-metered network
- setUnmeteredNetwork();
- mTestNetworkCallback.assertNoCallback();
+ meterednessConfiguration.configureNetworkMeteredness(false);
+ try {
+ mTestNetworkCallback.assertNoCallback();
- // Enable Power Saver
- setBatterySaverMode(true);
- assertBackgroundNetworkAccess(false);
- mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
+ // Enable Power Saver
+ setBatterySaverMode(true);
+ assertBackgroundNetworkAccess(false);
+ mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
- // Disable Power Saver
- setBatterySaverMode(false);
- assertBackgroundNetworkAccess(true);
- mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+ // Disable Power Saver
+ setBatterySaverMode(false);
+ assertBackgroundNetworkAccess(true);
+ mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+ } finally {
+ meterednessConfiguration.resetNetworkMeteredness();
+ }
}
// TODO: 1. test against VPN lockdown.
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/NetworkPolicyTestUtils.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/NetworkPolicyTestUtils.java
new file mode 100644
index 0000000..ca2864c
--- /dev/null
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/NetworkPolicyTestUtils.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.net.hostside;
+
+import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
+import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
+import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+import static com.android.cts.net.hostside.AbstractRestrictBackgroundNetworkTestCase.TAG;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.app.ActivityManager;
+import android.app.Instrumentation;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.wifi.WifiManager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.compatibility.common.util.AppStandbyUtils;
+import com.android.compatibility.common.util.BatteryUtils;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+public class NetworkPolicyTestUtils {
+
+ private static final int TIMEOUT_CHANGE_METEREDNESS_MS = 5000;
+
+ private static ConnectivityManager mCm;
+ private static WifiManager mWm;
+
+ private static Boolean mBatterySaverSupported;
+ private static Boolean mDataSaverSupported;
+ private static Boolean mDozeModeSupported;
+ private static Boolean mAppStandbySupported;
+
+ private NetworkPolicyTestUtils() {}
+
+ public static boolean isBatterySaverSupported() {
+ if (mBatterySaverSupported == null) {
+ mBatterySaverSupported = BatteryUtils.isBatterySaverSupported();
+ }
+ return mBatterySaverSupported;
+ }
+
+ /**
+ * As per CDD requirements, if the device doesn't support data saver mode then
+ * ConnectivityManager.getRestrictBackgroundStatus() will always return
+ * RESTRICT_BACKGROUND_STATUS_DISABLED. So, enable the data saver mode and check if
+ * ConnectivityManager.getRestrictBackgroundStatus() for an app in background returns
+ * RESTRICT_BACKGROUND_STATUS_DISABLED or not.
+ */
+ public static boolean isDataSaverSupported() {
+ if (mDataSaverSupported == null) {
+ assertMyRestrictBackgroundStatus(RESTRICT_BACKGROUND_STATUS_DISABLED);
+ try {
+ setRestrictBackground(true);
+ mDataSaverSupported = !isMyRestrictBackgroundStatus(
+ RESTRICT_BACKGROUND_STATUS_DISABLED);
+ } finally {
+ setRestrictBackground(false);
+ }
+ }
+ return mDataSaverSupported;
+ }
+
+ public static boolean isDozeModeSupported() {
+ if (mDozeModeSupported == null) {
+ final String result = executeShellCommand("cmd deviceidle enabled deep");
+ mDozeModeSupported = result.equals("1");
+ }
+ return mDozeModeSupported;
+ }
+
+ public static boolean isAppStandbySupported() {
+ if (mAppStandbySupported == null) {
+ mAppStandbySupported = AppStandbyUtils.isAppStandbyEnabled();
+ }
+ return mAppStandbySupported;
+ }
+
+ public static boolean isLowRamDevice() {
+ final ActivityManager am = (ActivityManager) getContext().getSystemService(
+ Context.ACTIVITY_SERVICE);
+ return am.isLowRamDevice();
+ }
+
+ public static boolean isActiveNetworkMetered(boolean metered) {
+ return getConnectivityManager().isActiveNetworkMetered() == metered;
+ }
+
+ public static boolean canChangeActiveNetworkMeteredness() {
+ final Network activeNetwork = getConnectivityManager().getActiveNetwork();
+ final NetworkCapabilities networkCapabilities
+ = getConnectivityManager().getNetworkCapabilities(activeNetwork);
+ return networkCapabilities.hasTransport(TRANSPORT_WIFI);
+ }
+
+ public static Pair<String, Boolean> setupMeteredNetwork(boolean metered) throws Exception {
+ if (isActiveNetworkMetered(metered)) {
+ return null;
+ }
+ final String ssid = unquoteSSID(getWifiManager().getConnectionInfo().getSSID());
+ setWifiMeteredStatus(ssid, metered);
+ return Pair.create(ssid, !metered);
+ }
+
+ public static void resetMeteredNetwork(String ssid, boolean metered) throws Exception {
+ setWifiMeteredStatus(ssid, metered);
+ }
+
+ public static void setWifiMeteredStatus(String ssid, boolean metered) throws Exception {
+ assertFalse("SSID should not be empty", TextUtils.isEmpty(ssid));
+ final String cmd = "cmd netpolicy set metered-network " + ssid + " " + metered;
+ executeShellCommand(cmd);
+ assertWifiMeteredStatus(ssid, metered);
+ assertActiveNetworkMetered(metered);
+ }
+
+ public static void assertWifiMeteredStatus(String ssid, boolean expectedMeteredStatus) {
+ final String result = executeShellCommand("cmd netpolicy list wifi-networks");
+ final String expectedLine = ssid + ";" + expectedMeteredStatus;
+ assertTrue("Expected line: " + expectedLine + "; Actual result: " + result,
+ result.contains(expectedLine));
+ }
+
+ // Copied from cts/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
+ public static void assertActiveNetworkMetered(boolean expectedMeteredStatus) throws Exception {
+ final CountDownLatch latch = new CountDownLatch(1);
+ final NetworkCallback networkCallback = new NetworkCallback() {
+ @Override
+ public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) {
+ final boolean metered = !nc.hasCapability(NET_CAPABILITY_NOT_METERED);
+ if (metered == expectedMeteredStatus) {
+ latch.countDown();
+ }
+ }
+ };
+ // Registering a callback here guarantees onCapabilitiesChanged is called immediately
+ // with the current setting. Therefore, if the setting has already been changed,
+ // this method will return right away, and if not it will wait for the setting to change.
+ getConnectivityManager().registerDefaultNetworkCallback(networkCallback);
+ if (!latch.await(TIMEOUT_CHANGE_METEREDNESS_MS, TimeUnit.MILLISECONDS)) {
+ fail("Timed out waiting for active network metered status to change to "
+ + expectedMeteredStatus + " ; network = "
+ + getConnectivityManager().getActiveNetwork());
+ }
+ getConnectivityManager().unregisterNetworkCallback(networkCallback);
+ }
+
+ public static void setRestrictBackground(boolean enabled) {
+ executeShellCommand("cmd netpolicy set restrict-background " + enabled);
+ final String output = executeShellCommand("cmd netpolicy get restrict-background");
+ final String expectedSuffix = enabled ? "enabled" : "disabled";
+ assertTrue("output '" + output + "' should end with '" + expectedSuffix + "'",
+ output.endsWith(expectedSuffix));
+ }
+
+ public static boolean isMyRestrictBackgroundStatus(int expectedStatus) {
+ final int actualStatus = getConnectivityManager().getRestrictBackgroundStatus();
+ if (expectedStatus != actualStatus) {
+ Log.d(TAG, "MyRestrictBackgroundStatus: "
+ + "Expected: " + restrictBackgroundValueToString(expectedStatus)
+ + "; Actual: " + restrictBackgroundValueToString(actualStatus));
+ return false;
+ }
+ return true;
+ }
+
+ // Copied from cts/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
+ private static String unquoteSSID(String ssid) {
+ // SSID is returned surrounded by quotes if it can be decoded as UTF-8.
+ // Otherwise it's guaranteed not to start with a quote.
+ if (ssid.charAt(0) == '"') {
+ return ssid.substring(1, ssid.length() - 1);
+ } else {
+ return ssid;
+ }
+ }
+
+ public static String restrictBackgroundValueToString(int status) {
+ switch (status) {
+ case RESTRICT_BACKGROUND_STATUS_DISABLED:
+ return "DISABLED";
+ case RESTRICT_BACKGROUND_STATUS_WHITELISTED:
+ return "WHITELISTED";
+ case RESTRICT_BACKGROUND_STATUS_ENABLED:
+ return "ENABLED";
+ default:
+ return "UNKNOWN_STATUS_" + status;
+ }
+ }
+
+ public static String executeShellCommand(String command) {
+ final String result = runShellCommand(command).trim();
+ Log.d(TAG, "Output of '" + command + "': '" + result + "'");
+ return result;
+ }
+
+ public static void assertMyRestrictBackgroundStatus(int expectedStatus) {
+ final int actualStatus = getConnectivityManager().getRestrictBackgroundStatus();
+ assertEquals(restrictBackgroundValueToString(expectedStatus),
+ restrictBackgroundValueToString(actualStatus));
+ }
+
+ public static ConnectivityManager getConnectivityManager() {
+ if (mCm == null) {
+ mCm = (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
+ }
+ return mCm;
+ }
+
+ public static WifiManager getWifiManager() {
+ if (mWm == null) {
+ mWm = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
+ }
+ return mWm;
+ }
+
+ public static Context getContext() {
+ return getInstrumentation().getContext();
+ }
+
+ public static Instrumentation getInstrumentation() {
+ return InstrumentationRegistry.getInstrumentation();
+ }
+}
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/Property.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/Property.java
new file mode 100644
index 0000000..18805f9
--- /dev/null
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/Property.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.net.hostside;
+
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.canChangeActiveNetworkMeteredness;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isActiveNetworkMetered;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isAppStandbySupported;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isBatterySaverSupported;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isDataSaverSupported;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isDozeModeSupported;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isLowRamDevice;
+
+public enum Property {
+ BATTERY_SAVER_MODE(1 << 0) {
+ public boolean isSupported() { return isBatterySaverSupported(); }
+ },
+
+ DATA_SAVER_MODE(1 << 1) {
+ public boolean isSupported() { return isDataSaverSupported(); }
+ },
+
+ NO_DATA_SAVER_MODE(~DATA_SAVER_MODE.getValue()) {
+ public boolean isSupported() { return !isDataSaverSupported(); }
+ },
+
+ DOZE_MODE(1 << 2) {
+ public boolean isSupported() { return isDozeModeSupported(); }
+ },
+
+ APP_STANDBY_MODE(1 << 3) {
+ public boolean isSupported() { return isAppStandbySupported(); }
+ },
+
+ NOT_LOW_RAM_DEVICE(1 << 4) {
+ public boolean isSupported() { return !isLowRamDevice(); }
+ },
+
+ METERED_NETWORK(1 << 5) {
+ public boolean isSupported() {
+ return isActiveNetworkMetered(true) || canChangeActiveNetworkMeteredness();
+ }
+ },
+
+ NON_METERED_NETWORK(~METERED_NETWORK.getValue()) {
+ public boolean isSupported() {
+ return isActiveNetworkMetered(false) || canChangeActiveNetworkMeteredness();
+ }
+ };
+
+ private int mValue;
+
+ Property(int value) { mValue = value; }
+
+ public int getValue() { return mValue; }
+
+ abstract boolean isSupported();
+}
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/RequiredProperties.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/RequiredProperties.java
new file mode 100644
index 0000000..96838bb
--- /dev/null
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/RequiredProperties.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.net.hostside;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+@Retention(RUNTIME)
+@Target({METHOD, TYPE})
+@Inherited
+public @interface RequiredProperties {
+ Property[] value();
+}
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/RequiredPropertiesRule.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/RequiredPropertiesRule.java
new file mode 100644
index 0000000..1e33320
--- /dev/null
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/RequiredPropertiesRule.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.net.hostside;
+
+import static com.android.cts.net.hostside.AbstractRestrictBackgroundNetworkTestCase.TAG;
+
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.compatibility.common.util.BeforeAfterRule;
+
+import org.junit.Assume;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+public class RequiredPropertiesRule extends BeforeAfterRule {
+
+ private static ArraySet<Property> mRequiredProperties;
+
+ @Override
+ public void onBefore(Statement base, Description description) {
+ mRequiredProperties = getAllRequiredProperties(description);
+
+ final String testName = description.getClassName() + "#" + description.getMethodName();
+ assertTestIsValid(testName, mRequiredProperties);
+ Log.i(TAG, "Running test " + testName + " with required properties: "
+ + propertiesToString(mRequiredProperties));
+ }
+
+ private ArraySet<Property> getAllRequiredProperties(Description description) {
+ final ArraySet<Property> allRequiredProperties = new ArraySet<>();
+ RequiredProperties requiredProperties = description.getAnnotation(RequiredProperties.class);
+ if (requiredProperties != null) {
+ Collections.addAll(allRequiredProperties, requiredProperties.value());
+ }
+
+ for (Class<?> clazz = description.getTestClass();
+ clazz != null; clazz = clazz.getSuperclass()) {
+ requiredProperties = clazz.getDeclaredAnnotation(RequiredProperties.class);
+ if (requiredProperties == null) {
+ continue;
+ }
+ for (Property requiredProperty : requiredProperties.value()) {
+ if (!allRequiredProperties.contains(~requiredProperty.getValue())) {
+ allRequiredProperties.add(requiredProperty);
+ }
+ }
+ }
+ return allRequiredProperties;
+ }
+
+ private void assertTestIsValid(String testName, ArraySet<Property> requiredProperies) {
+ if (requiredProperies == null) {
+ return;
+ }
+ final ArrayList<Property> unsupportedProperties = new ArrayList<>();
+ for (Property property : requiredProperies) {
+ if (!property.isSupported()) {
+ unsupportedProperties.add(property);
+ }
+ }
+ Assume.assumeTrue("Unsupported properties: "
+ + propertiesToString(unsupportedProperties), unsupportedProperties.isEmpty());
+ }
+
+ public static ArraySet<Property> getRequiredProperties() {
+ return mRequiredProperties;
+ }
+
+ private static String propertiesToString(Iterable<Property> properties) {
+ return "[" + TextUtils.join(",", properties) + "]";
+ }
+}
diff --git a/hostsidetests/net/src/com/android/cts/net/HostsideNetworkCallbackTests.java b/hostsidetests/net/src/com/android/cts/net/HostsideNetworkCallbackTests.java
index 8d6c4ac..1312085 100644
--- a/hostsidetests/net/src/com/android/cts/net/HostsideNetworkCallbackTests.java
+++ b/hostsidetests/net/src/com/android/cts/net/HostsideNetworkCallbackTests.java
@@ -29,14 +29,14 @@
uninstallPackage(TEST_APP2_PKG, true);
}
- public void testOnBlockedStatusChanged_data_saver() throws Exception {
+ public void testOnBlockedStatusChanged_dataSaver() throws Exception {
runDeviceTests(TEST_PKG,
- TEST_PKG + ".NetworkCallbackTest", "testOnBlockedStatusChanged_data_saver");
+ TEST_PKG + ".NetworkCallbackTest", "testOnBlockedStatusChanged_dataSaver");
}
- public void testOnBlockedStatusChanged_power_saver() throws Exception {
+ public void testOnBlockedStatusChanged_powerSaver() throws Exception {
runDeviceTests(TEST_PKG,
- TEST_PKG + ".NetworkCallbackTest", "testOnBlockedStatusChanged_power_saver");
+ TEST_PKG + ".NetworkCallbackTest", "testOnBlockedStatusChanged_powerSaver");
}
}
diff --git a/hostsidetests/net/src/com/android/cts/net/HostsideNetworkTestCase.java b/hostsidetests/net/src/com/android/cts/net/HostsideNetworkTestCase.java
index a2443b3..ce20379 100644
--- a/hostsidetests/net/src/com/android/cts/net/HostsideNetworkTestCase.java
+++ b/hostsidetests/net/src/com/android/cts/net/HostsideNetworkTestCase.java
@@ -79,7 +79,8 @@
protected void installPackage(String apk) throws FileNotFoundException,
DeviceNotAvailableException {
CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild);
- assertNull(getDevice().installPackage(buildHelper.getTestFile(apk), false));
+ assertNull(getDevice().installPackage(buildHelper.getTestFile(apk),
+ false /* reinstall */, true /* grantPermissions */));
}
protected void uninstallPackage(String packageName, boolean shouldSucceed)
diff --git a/hostsidetests/numberblocking/OWNERS b/hostsidetests/numberblocking/OWNERS
new file mode 100644
index 0000000..0cfd686
--- /dev/null
+++ b/hostsidetests/numberblocking/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 151185
+tgunn@google.com
\ No newline at end of file
diff --git a/hostsidetests/numberblocking/src/com/android/cts/numberblocking/hostside/NumberBlockingTest.java b/hostsidetests/numberblocking/src/com/android/cts/numberblocking/hostside/NumberBlockingTest.java
index 1bf1aef..12b2dca 100644
--- a/hostsidetests/numberblocking/src/com/android/cts/numberblocking/hostside/NumberBlockingTest.java
+++ b/hostsidetests/numberblocking/src/com/android/cts/numberblocking/hostside/NumberBlockingTest.java
@@ -27,6 +27,8 @@
import com.android.tradefed.testtype.IBuildReceiver;
import java.io.File;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* Multi-user tests for number blocking.
@@ -237,15 +239,15 @@
// TODO: Replace this with API in ITestDevice once it is available.
private int getUserSerialNumber(int userId) throws DeviceNotAvailableException {
+ Pattern pattern = Pattern.compile("^.*UserInfo\\{" + userId + "\\:.*serialNo=(\\d+).*$");
// dumpsys user return lines like "UserInfo{0:Owner:13} serialNo=0"
String commandOutput = getDevice().executeShellCommand("dumpsys user");
String[] tokens = commandOutput.split("\\n");
for (String token : tokens) {
token = token.trim();
- if (token.contains("UserInfo{" + userId + ":")) {
- String[] split = token.split("serialNo=");
- assertTrue(split.length == 2);
- int serialNumber = Integer.parseInt(split[1]);
+ Matcher matcher = pattern.matcher(token);
+ if (matcher.matches()) {
+ int serialNumber = Integer.parseInt(matcher.group(1));
LogUtil.CLog.logAndDisplay(
Log.LogLevel.INFO,
String.format("Serial number of user %d : %d", userId, serialNumber));
diff --git a/hostsidetests/os/app/AndroidManifest.xml b/hostsidetests/os/app/AndroidManifest.xml
index fa9d9ae..88791ea 100755
--- a/hostsidetests/os/app/AndroidManifest.xml
+++ b/hostsidetests/os/app/AndroidManifest.xml
@@ -19,9 +19,16 @@
package="android.os.app"
android:targetSandboxVersion="2">
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
+
<application>
<activity android:name=".TestNonExported"
android:exported="false" />
+
+ <service android:name=".TestFgService"
+ android:exported="true" />
+
</application>
+
</manifest>
diff --git a/hostsidetests/os/app/src/android/os/app/TestFgService.java b/hostsidetests/os/app/src/android/os/app/TestFgService.java
new file mode 100644
index 0000000..3548105
--- /dev/null
+++ b/hostsidetests/os/app/src/android/os/app/TestFgService.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.app;
+
+import android.app.Notification;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.Process;
+import android.util.Log;
+
+public class TestFgService extends Service {
+ private static final String TAG = "TestFgService";
+
+ // intentionally invalid resource configuration
+ private static final int NOTIFICATION_ID = 5038;
+ private static final String CHANNEL = "fgservice";
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ Log.i(TAG, "onStartCommand() called");
+ Notification notification = new Notification.Builder(this, CHANNEL)
+ .setContentTitle("Foreground service")
+ .setContentText("Ongoing test app foreground service is live")
+ .setSmallIcon(NOTIFICATION_ID)
+ .build();
+
+ Log.i(TAG, "TestFgService starting foreground: pid=" + Process.myPid());
+ startForeground(NOTIFICATION_ID, notification);
+
+ return START_NOT_STICKY;
+ }
+}
\ No newline at end of file
diff --git a/hostsidetests/os/src/android/os/cts/OsHostTests.java b/hostsidetests/os/src/android/os/cts/OsHostTests.java
index c557c9e..8e3f5c5 100644
--- a/hostsidetests/os/src/android/os/cts/OsHostTests.java
+++ b/hostsidetests/os/src/android/os/cts/OsHostTests.java
@@ -22,14 +22,17 @@
import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.device.CollectingOutputReceiver;
import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.result.InputStreamSource;
import com.android.tradefed.testtype.DeviceTestCase;
import com.android.tradefed.testtype.IAbi;
import com.android.tradefed.testtype.IAbiReceiver;
import com.android.tradefed.testtype.IBuildReceiver;
import com.android.tradefed.util.AbiUtils;
+import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
+import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Scanner;
@@ -38,12 +41,19 @@
public class OsHostTests extends DeviceTestCase implements IBuildReceiver, IAbiReceiver {
private static final String TEST_APP_PACKAGE = "android.os.app";
- private static final String TEST_NON_EXPORTED_ACTIVITY_CLASS = "TestNonExported";
+ private static final String TEST_NON_EXPORTED_ACTIVITY_CLASS = "TestNonExported";
private static final String START_NON_EXPORTED_ACTIVITY_COMMAND = String.format(
"am start -n %s/%s.%s",
TEST_APP_PACKAGE, TEST_APP_PACKAGE, TEST_NON_EXPORTED_ACTIVITY_CLASS);
+ private static final String TEST_FG_SERVICE_CLASS = "TestFgService";
+ private static final String START_FG_SERVICE_COMMAND = String.format(
+ "am start-foreground-service -n %s/%s.%s",
+ TEST_APP_PACKAGE, TEST_APP_PACKAGE, TEST_FG_SERVICE_CLASS);
+ private static final String FILTER_FG_SERVICE_REGEXP =
+ "TestFgService starting foreground: pid=([0-9]*)";
+
// Testing the intent filter verification mechanism
private static final String HOST_VERIFICATION_APK = "CtsHostLinkVerificationApp.apk";
private static final String HOST_VERIFICATION_PKG = "com.android.cts.openlinksskeleton";
@@ -104,6 +114,40 @@
}
}
+ /**
+ * Test behavior of malformed Notifications w.r.t. foreground services
+ * @throws Exception
+ */
+ @AppModeFull(reason = "Instant apps may not start foreground services")
+ public void testForegroundServiceBadNotification() throws Exception {
+ final Pattern pattern = Pattern.compile(FILTER_FG_SERVICE_REGEXP);
+
+ mDevice.clearLogcat();
+ mDevice.executeShellCommand(START_FG_SERVICE_COMMAND);
+ Thread.sleep(2500);
+
+ String pid = null;
+ try (InputStreamSource logSource = mDevice.getLogcat()) {
+ InputStreamReader streamReader = new InputStreamReader(logSource.createInputStream());
+ BufferedReader logReader = new BufferedReader(streamReader);
+
+ String line;
+ while ((line = logReader.readLine()) != null) {
+ Matcher matcher = pattern.matcher(line);
+ if (matcher.find()) {
+ pid = matcher.group(1);
+ break;
+ }
+ }
+ }
+ assertTrue("Didn't find test service statement in logcat", pid != null);
+
+ final String procStr = "/proc/" + pid;
+ final String lsOut = mDevice.executeShellCommand("ls -d " + procStr).trim();
+ assertTrue("Looking for nonexistence of service process " + pid,
+ lsOut.contains("No such file"));
+ }
+
public void testIntentFilterHostValidation() throws Exception {
String line = null;
try {
diff --git a/hostsidetests/rollback/Android.bp b/hostsidetests/rollback/Android.bp
index c3dbe18..7c323b2 100644
--- a/hostsidetests/rollback/Android.bp
+++ b/hostsidetests/rollback/Android.bp
@@ -17,19 +17,29 @@
defaults: ["cts_defaults"],
srcs: ["src/**/*.java"],
libs: ["cts-tradefed", "tradefed", "truth-prebuilt"],
- data: [":CtsRollbackManagerHostTestHelperApp"],
+ data: [":CtsRollbackManagerHostTestHelperApp", ":CtsRollbackManagerHostTestHelperApp2"],
test_suites: ["cts", "general-tests"],
}
android_test_helper_app {
name: "CtsRollbackManagerHostTestHelperApp",
srcs: ["app/src/**/*.java"],
- static_libs: ["androidx.test.rules", "cts-rollback-lib"],
+ static_libs: ["androidx.test.rules", "cts-rollback-lib", "cts-install-lib"],
manifest : "app/AndroidManifest.xml",
java_resources: [
+ ":ApexKeyRotationTestV2_SignedBobRot",
":StagedInstallTestApexV2",
":StagedInstallTestApexV3",
],
sdk_version: "test_current",
test_suites: ["device-tests"],
}
+
+android_test_helper_app {
+ name: "CtsRollbackManagerHostTestHelperApp2",
+ srcs: ["app2/src/**/*.java"],
+ static_libs: ["androidx.test.rules", "cts-rollback-lib", "cts-install-lib"],
+ manifest : "app2/AndroidManifest.xml",
+ sdk_version: "test_current",
+ test_suites: ["device-tests"],
+}
diff --git a/hostsidetests/rollback/AndroidTest.xml b/hostsidetests/rollback/AndroidTest.xml
index 9f0ae07..23e362f 100644
--- a/hostsidetests/rollback/AndroidTest.xml
+++ b/hostsidetests/rollback/AndroidTest.xml
@@ -21,6 +21,7 @@
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsRollbackManagerHostTestHelperApp.apk" />
+ <option name="test-file-name" value="CtsRollbackManagerHostTestHelperApp2.apk" />
</target_preparer>
<test class="com.android.tradefed.testtype.HostTest" >
<option name="class" value="com.android.cts.rollback.host.RollbackManagerHostTest" />
diff --git a/hostsidetests/rollback/OWNERS b/hostsidetests/rollback/OWNERS
new file mode 100644
index 0000000..e84df8e
--- /dev/null
+++ b/hostsidetests/rollback/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 557916
+ruhler@google.com
+*
\ No newline at end of file
diff --git a/hostsidetests/rollback/app/AndroidManifest.xml b/hostsidetests/rollback/app/AndroidManifest.xml
index 2aac999..421ceff 100644
--- a/hostsidetests/rollback/app/AndroidManifest.xml
+++ b/hostsidetests/rollback/app/AndroidManifest.xml
@@ -18,7 +18,7 @@
package="com.android.cts.rollback.host.app" >
<application>
- <receiver android:name="com.android.cts.rollback.lib.LocalIntentSender"
+ <receiver android:name="com.android.cts.install.lib.LocalIntentSender"
android:exported="true" />
<uses-library android:name="android.test.runner" />
</application>
diff --git a/hostsidetests/rollback/app/src/com/android/cts/rollback/host/app/HostTestHelper.java b/hostsidetests/rollback/app/src/com/android/cts/rollback/host/app/HostTestHelper.java
index 1e8040d..5cdfe86 100644
--- a/hostsidetests/rollback/app/src/com/android/cts/rollback/host/app/HostTestHelper.java
+++ b/hostsidetests/rollback/app/src/com/android/cts/rollback/host/app/HostTestHelper.java
@@ -23,12 +23,11 @@
import android.Manifest;
import android.content.rollback.RollbackInfo;
-import androidx.test.InstrumentationRegistry;
-
-import com.android.cts.rollback.lib.Install;
+import com.android.cts.install.lib.Install;
+import com.android.cts.install.lib.InstallUtils;
+import com.android.cts.install.lib.TestApp;
import com.android.cts.rollback.lib.Rollback;
-import com.android.cts.rollback.lib.TestApp;
-import com.android.cts.rollback.lib.Utils;
+import com.android.cts.rollback.lib.RollbackUtils;
import org.junit.After;
import org.junit.Before;
@@ -43,14 +42,16 @@
*/
@RunWith(JUnit4.class)
public class HostTestHelper {
+ private static final TestApp Apex2SignedBobRot = new TestApp(
+ "Apex2SignedBobRot", TestApp.Apex, 2, /*isApex*/true,
+ "com.android.apex.cts.shim.v2_signed_bob_rot.apex");
/**
* Adopts common permissions needed to test rollbacks.
*/
@Before
public void setup() throws InterruptedException, IOException {
- InstrumentationRegistry.getInstrumentation().getUiAutomation()
- .adoptShellPermissionIdentity(
+ InstallUtils.adoptShellPermissionIdentity(
Manifest.permission.INSTALL_PACKAGES,
Manifest.permission.DELETE_PACKAGES,
Manifest.permission.TEST_MANAGE_ROLLBACKS);
@@ -61,8 +62,7 @@
*/
@After
public void teardown() throws InterruptedException, IOException {
- InstrumentationRegistry.getInstrumentation().getUiAutomation()
- .dropShellPermissionIdentity();
+ InstallUtils.dropShellPermissionIdentity();
}
@@ -72,7 +72,7 @@
*/
@Test
public void testApkOnlyEnableRollback() throws Exception {
- assertThat(Utils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
Install.single(TestApp.A1).commit();
Install.single(TestApp.A2).setStaged().setEnableRollback().commit();
@@ -88,15 +88,17 @@
*/
@Test
public void testApkOnlyCommitRollback() throws Exception {
- assertThat(Utils.getInstalledVersion(TestApp.A)).isEqualTo(2);
- RollbackInfo available = Utils.getAvailableRollback(TestApp.A);
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
+ InstallUtils.processUserData(TestApp.A);
+
+ RollbackInfo available = RollbackUtils.getAvailableRollback(TestApp.A);
assertThat(available).isStaged();
assertThat(available).packagesContainsExactly(
Rollback.from(TestApp.A2).to(TestApp.A1));
- assertThat(Utils.getCommittedRollback(TestApp.A)).isNull();
+ assertThat(RollbackUtils.getCommittedRollback(TestApp.A)).isNull();
- Utils.rollback(available.getRollbackId(), TestApp.A2);
- RollbackInfo committed = Utils.getCommittedRollback(TestApp.A);
+ RollbackUtils.rollback(available.getRollbackId(), TestApp.A2);
+ RollbackInfo committed = RollbackUtils.getCommittedRollback(TestApp.A);
assertThat(committed).hasRollbackId(available.getRollbackId());
assertThat(committed).isStaged();
assertThat(committed).packagesContainsExactly(
@@ -106,8 +108,8 @@
// Note: The app is not rolled back until after the rollback is staged
// and the device has been rebooted.
- Utils.waitForSessionReady(committed.getCommittedSessionId());
- assertThat(Utils.getInstalledVersion(TestApp.A)).isEqualTo(2);
+ InstallUtils.waitForSessionReady(committed.getCommittedSessionId());
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
// At this point, the host test driver will reboot the device and run
// testApkOnlyConfirmRollback().
@@ -119,9 +121,10 @@
*/
@Test
public void testApkOnlyConfirmRollback() throws Exception {
- assertThat(Utils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+ InstallUtils.processUserData(TestApp.A);
- RollbackInfo committed = Utils.getCommittedRollback(TestApp.A);
+ RollbackInfo committed = RollbackUtils.getCommittedRollback(TestApp.A);
assertThat(committed).isStaged();
assertThat(committed).packagesContainsExactly(
Rollback.from(TestApp.A2).to(TestApp.A1));
@@ -133,12 +136,11 @@
* Test rollbacks of staged installs involving only apex.
* Install first version phase.
*
- * <p> We can't rollback to version 1, which is already installed, so we start by installing
- * version 2. The test ultimately rolls back from 3 to 2.
+ * <p> We start by installing version 2. The test ultimately rolls back from 3 to 2.
*/
@Test
public void testApexOnlyInstallFirstVersion() throws Exception {
- assertThat(Utils.getInstalledVersion(TestApp.Apex)).isEqualTo(1);
+ assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(1);
Install.single(TestApp.Apex2).setStaged().commit();
@@ -152,7 +154,7 @@
*/
@Test
public void testApexOnlyEnableRollback() throws Exception {
- assertThat(Utils.getInstalledVersion(TestApp.Apex)).isEqualTo(2);
+ assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(2);
Install.single(TestApp.Apex3).setStaged().setEnableRollback().commit();
// At this point, the host test driver will reboot the device and run
@@ -165,14 +167,14 @@
*/
@Test
public void testApexOnlyCommitRollback() throws Exception {
- assertThat(Utils.getInstalledVersion(TestApp.Apex)).isEqualTo(3);
- RollbackInfo available = Utils.getAvailableRollback(TestApp.Apex);
+ assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(3);
+ RollbackInfo available = RollbackUtils.getAvailableRollback(TestApp.Apex);
assertThat(available).isStaged();
assertThat(available).packagesContainsExactly(
Rollback.from(TestApp.Apex3).to(TestApp.Apex2));
- Utils.rollback(available.getRollbackId(), TestApp.Apex3);
- RollbackInfo committed = Utils.getCommittedRollbackById(available.getRollbackId());
+ RollbackUtils.rollback(available.getRollbackId(), TestApp.Apex3);
+ RollbackInfo committed = RollbackUtils.getCommittedRollbackById(available.getRollbackId());
assertThat(committed).isNotNull();
assertThat(committed).isStaged();
assertThat(committed).packagesContainsExactly(
@@ -182,8 +184,8 @@
// Note: The app is not rolled back until after the rollback is staged
// and the device has been rebooted.
- Utils.waitForSessionReady(committed.getCommittedSessionId());
- assertThat(Utils.getInstalledVersion(TestApp.Apex)).isEqualTo(3);
+ InstallUtils.waitForSessionReady(committed.getCommittedSessionId());
+ assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(3);
// At this point, the host test driver will reboot the device and run
// testApexOnlyConfirmRollback().
@@ -195,7 +197,7 @@
*/
@Test
public void testApexOnlyConfirmRollback() throws Exception {
- assertThat(Utils.getInstalledVersion(TestApp.Apex)).isEqualTo(2);
+ assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(2);
// Rollback data for shim apex will remain in storage since the apex cannot be completely
// removed and thus the rollback data won't be expired. Unfortunately, we can't also delete
@@ -204,6 +206,42 @@
// At this point, the host test driver will reboot the device to complete the uninstall.
}
+ /**
+ * Test rollback to system version involving apex only
+ */
+ @Test
+ public void testApexOnlySystemVersion_EnableRollback() throws Exception {
+ assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(1);
+ Install.single(TestApp.Apex2).setStaged().setEnableRollback().commit();
+ }
+
+ @Test
+ public void testApexOnlySystemVersion_CommitRollback() throws Exception {
+ assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(2);
+ RollbackInfo available = RollbackUtils.getAvailableRollback(TestApp.Apex);
+ assertThat(available).isStaged();
+ assertThat(available).packagesContainsExactly(
+ Rollback.from(TestApp.Apex2).to(TestApp.Apex1));
+
+ RollbackUtils.rollback(available.getRollbackId(), TestApp.Apex2);
+ RollbackInfo committed = RollbackUtils.getCommittedRollbackById(available.getRollbackId());
+ assertThat(committed).isNotNull();
+ assertThat(committed).isStaged();
+ assertThat(committed).packagesContainsExactly(
+ Rollback.from(TestApp.Apex2).to(TestApp.Apex1));
+ assertThat(committed).causePackagesContainsExactly(TestApp.Apex2);
+ assertThat(committed.getCommittedSessionId()).isNotEqualTo(-1);
+
+ // Note: The app is not rolled back until after the rollback is staged
+ // and the device has been rebooted.
+ InstallUtils.waitForSessionReady(committed.getCommittedSessionId());
+ assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(2);
+ }
+
+ @Test
+ public void testApexOnlySystemVersion_ConfirmRollback() throws Exception {
+ assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(1);
+ }
/**
* Test rollbacks of staged installs involving apex and apk.
@@ -213,8 +251,8 @@
*/
@Test
public void testApexAndApkInstallFirstVersion() throws Exception {
- assertThat(Utils.getInstalledVersion(TestApp.Apex)).isEqualTo(1);
- assertThat(Utils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
+ assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(1);
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
Install.multi(TestApp.Apex2, TestApp.A1).setStaged().commit();
@@ -228,8 +266,8 @@
*/
@Test
public void testApexAndApkEnableRollback() throws Exception {
- assertThat(Utils.getInstalledVersion(TestApp.Apex)).isEqualTo(2);
- assertThat(Utils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+ assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(2);
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
Install.multi(TestApp.Apex3, TestApp.A2).setStaged().setEnableRollback().commit();
// At this point, the host test driver will reboot the device and run
@@ -242,16 +280,18 @@
*/
@Test
public void testApexAndApkCommitRollback() throws Exception {
- assertThat(Utils.getInstalledVersion(TestApp.Apex)).isEqualTo(3);
- assertThat(Utils.getInstalledVersion(TestApp.A)).isEqualTo(2);
- RollbackInfo available = Utils.getAvailableRollback(TestApp.Apex);
+ assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(3);
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
+ InstallUtils.processUserData(TestApp.A);
+
+ RollbackInfo available = RollbackUtils.getAvailableRollback(TestApp.Apex);
assertThat(available).isStaged();
assertThat(available).packagesContainsExactly(
Rollback.from(TestApp.Apex3).to(TestApp.Apex2),
Rollback.from(TestApp.A2).to(TestApp.A1));
- Utils.rollback(available.getRollbackId(), TestApp.Apex3, TestApp.A2);
- RollbackInfo committed = Utils.getCommittedRollback(TestApp.A);
+ RollbackUtils.rollback(available.getRollbackId(), TestApp.Apex3, TestApp.A2);
+ RollbackInfo committed = RollbackUtils.getCommittedRollback(TestApp.A);
assertThat(committed).isNotNull();
assertThat(committed).isStaged();
assertThat(committed).packagesContainsExactly(
@@ -262,9 +302,9 @@
// Note: The app is not rolled back until after the rollback is staged
// and the device has been rebooted.
- Utils.waitForSessionReady(committed.getCommittedSessionId());
- assertThat(Utils.getInstalledVersion(TestApp.Apex)).isEqualTo(3);
- assertThat(Utils.getInstalledVersion(TestApp.A)).isEqualTo(2);
+ InstallUtils.waitForSessionReady(committed.getCommittedSessionId());
+ assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(3);
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
// At this point, the host test driver will reboot the device and run
// testApexOnlyConfirmRollback().
@@ -276,9 +316,11 @@
*/
@Test
public void testApexAndApkConfirmRollback() throws Exception {
- assertThat(Utils.getInstalledVersion(TestApp.Apex)).isEqualTo(2);
+ assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(2);
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+ InstallUtils.processUserData(TestApp.A);
- RollbackInfo committed = Utils.getCommittedRollback(TestApp.A);
+ RollbackInfo committed = RollbackUtils.getCommittedRollback(TestApp.A);
assertThat(committed).isStaged();
assertThat(committed).packagesContainsExactly(
Rollback.from(TestApp.Apex3).to(TestApp.Apex2),
@@ -299,7 +341,7 @@
*/
@Test
public void testApexRollbackExpirationEnableRollback() throws Exception {
- assertThat(Utils.getInstalledVersion(TestApp.Apex)).isEqualTo(1);
+ assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(1);
Install.single(TestApp.Apex2).setStaged().setEnableRollback().commit();
@@ -313,8 +355,8 @@
*/
@Test
public void testApexRollbackExpirationUpdateApex() throws Exception {
- assertThat(Utils.getInstalledVersion(TestApp.Apex)).isEqualTo(2);
- assertThat(Utils.getAvailableRollback(TestApp.Apex)).isNotNull();
+ assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(2);
+ assertThat(RollbackUtils.getAvailableRollback(TestApp.Apex)).isNotNull();
Install.single(TestApp.Apex3).setStaged().commit();
// At this point, the host test driver will reboot the device and run
@@ -327,7 +369,50 @@
*/
@Test
public void testApexRollbackExpirationConfirmExpiration() throws Exception {
- assertThat(Utils.getInstalledVersion(TestApp.Apex)).isEqualTo(3);
- assertThat(Utils.getAvailableRollback(TestApp.Apex)).isNull();
+ assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(3);
+ assertThat(RollbackUtils.getAvailableRollback(TestApp.Apex)).isNull();
+ }
+
+ /**
+ * Test rollback with key downgrade for apex only
+ */
+ @Test
+ public void testApexKeyRotation_EnableRollback() throws Exception {
+ assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(1);
+ Install.single(Apex2SignedBobRot).setStaged().setEnableRollback().commit();
+ }
+
+ @Test
+ public void testApexKeyRotation_CommitRollback() throws Exception {
+ assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(2);
+ RollbackInfo available = RollbackUtils.getAvailableRollback(TestApp.Apex);
+ assertThat(available).isStaged();
+ assertThat(available).packagesContainsExactly(
+ Rollback.from(Apex2SignedBobRot).to(TestApp.Apex1));
+
+ RollbackUtils.rollback(available.getRollbackId(), Apex2SignedBobRot);
+ RollbackInfo committed = RollbackUtils.getCommittedRollbackById(available.getRollbackId());
+ assertThat(committed).isNotNull();
+ assertThat(committed).isStaged();
+ assertThat(committed).packagesContainsExactly(
+ Rollback.from(Apex2SignedBobRot).to(TestApp.Apex1));
+ assertThat(committed).causePackagesContainsExactly(Apex2SignedBobRot);
+ assertThat(committed.getCommittedSessionId()).isNotEqualTo(-1);
+
+ // Note: The app is not rolled back until after the rollback is staged
+ // and the device has been rebooted.
+ InstallUtils.waitForSessionReady(committed.getCommittedSessionId());
+ assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(2);
+ }
+
+ @Test
+ public void testApexKeyRotation_CofirmRollback() throws Exception {
+ assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(1);
+ }
+
+ @Test
+ public void testInstallTestAppA_EnableRollback() throws Exception {
+ Install.single(TestApp.A1).commit();
+ Install.single(TestApp.A2).setEnableRollback().commit();
}
}
diff --git a/hostsidetests/rollback/app2/AndroidManifest.xml b/hostsidetests/rollback/app2/AndroidManifest.xml
new file mode 100644
index 0000000..be6d483
--- /dev/null
+++ b/hostsidetests/rollback/app2/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.cts.rollback.host.app2" >
+
+ <application>
+ <receiver android:name="com.android.cts.install.lib.LocalIntentSender"
+ android:exported="true" />
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.cts.rollback.host.app2"
+ android:label="Helper for CTS host tests of RollbackManager"/>
+
+</manifest>
diff --git a/hostsidetests/rollback/app2/src/com/android/cts/rollback/host/app2/HostTestHelper.java b/hostsidetests/rollback/app2/src/com/android/cts/rollback/host/app2/HostTestHelper.java
new file mode 100644
index 0000000..56e9251
--- /dev/null
+++ b/hostsidetests/rollback/app2/src/com/android/cts/rollback/host/app2/HostTestHelper.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.rollback.host.app2;
+
+import static com.android.cts.rollback.lib.RollbackInfoSubject.assertThat;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.Manifest;
+import android.content.rollback.RollbackInfo;
+
+import com.android.cts.install.lib.InstallUtils;
+import com.android.cts.install.lib.TestApp;
+import com.android.cts.rollback.lib.Rollback;
+import com.android.cts.rollback.lib.RollbackUtils;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.IOException;
+
+/**
+ * On-device helper test methods used for host-driven rollback tests.
+ */
+@RunWith(JUnit4.class)
+public class HostTestHelper {
+ /**
+ * Adopts common permissions needed to test rollbacks.
+ */
+ @Before
+ public void setup() throws InterruptedException, IOException {
+ InstallUtils.adoptShellPermissionIdentity(
+ Manifest.permission.INSTALL_PACKAGES,
+ Manifest.permission.DELETE_PACKAGES,
+ Manifest.permission.TEST_MANAGE_ROLLBACKS);
+ }
+
+ /**
+ * Drops adopted shell permissions.
+ */
+ @After
+ public void teardown() throws InterruptedException, IOException {
+ InstallUtils.dropShellPermissionIdentity();
+ }
+
+ @Test
+ public void testRollbackTestAppA() throws Exception {
+ RollbackInfo rollbackA = RollbackUtils.waitForAvailableRollback(TestApp.A);
+ assertThat(rollbackA).packagesContainsExactly(Rollback.from(TestApp.A2).to(TestApp.A1));
+ RollbackUtils.rollback(rollbackA.getRollbackId());
+ // TODO(b/127452064): fix the rollback bug and enable the assertion
+ // Rollback should fail since TestApp.A is installed by another installer.
+ // assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
+ }
+}
diff --git a/hostsidetests/rollback/src/com/android/cts/rollback/host/RollbackManagerHostTest.java b/hostsidetests/rollback/src/com/android/cts/rollback/host/RollbackManagerHostTest.java
index 60e154c..41350d0 100644
--- a/hostsidetests/rollback/src/com/android/cts/rollback/host/RollbackManagerHostTest.java
+++ b/hostsidetests/rollback/src/com/android/cts/rollback/host/RollbackManagerHostTest.java
@@ -20,6 +20,7 @@
import static org.junit.Assume.assumeTrue;
+import com.android.ddmlib.Log;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
@@ -35,8 +36,10 @@
@RunWith(DeviceJUnit4ClassRunner.class)
public class RollbackManagerHostTest extends BaseHostJUnit4Test {
+ private static final String TAG = "RollbackManagerHostTest";
+
private static final String SHIM_APEX_PACKAGE_NAME = "com.android.apex.cts.shim";
- private static final String TEST_APK_PACKAGE_NAME = "com.android.cts.rollback.lib.testapp.A";
+ private static final String TEST_APK_PACKAGE_NAME = "com.android.cts.install.lib.testapp.A";
/**
* Runs the helper app test method on device.
@@ -51,6 +54,16 @@
}
/**
+ * Runs the helper app test method on device targeted for
+ * com.android.cts.rollback.host.app2.HostTestHelper.
+ */
+ private void run2(String method) throws Exception {
+ assertThat(runDeviceTests("com.android.cts.rollback.host.app2",
+ "com.android.cts.rollback.host.app2.HostTestHelper",
+ method)).isTrue();
+ }
+
+ /**
* Return {@code true} if and only if device supports updating apex.
*/
private boolean isApexUpdateSupported() throws Exception {
@@ -68,17 +81,16 @@
// Device doesn't support updating apex. Nothing to uninstall.
return;
}
- final ITestDevice.ApexInfo shimApex = getShimApex();
- if (shimApex.versionCode == 1) {
- // System version is active, skipping uninstalling active apex and rebooting the device.
- return;
- }
- // Non system version is active, need to uninstall it and reboot the device.
final String errorMessage = getDevice().uninstallPackage(SHIM_APEX_PACKAGE_NAME);
- if (errorMessage != null) {
- throw new AssertionError("Failed to uninstall " + shimApex);
+ if (errorMessage == null) {
+ Log.i(TAG, "Uninstalling shim apex");
+ getDevice().reboot();
+ } else {
+ // Most likely we tried to uninstall system version and failed. It should be fine to
+ // continue tests.
+ // TODO(b/140813980): use ApexInfo.sourceDir to decide whenever to issue an uninstall.
+ Log.w(TAG, "Failed to uninstall shim APEX : " + errorMessage);
}
- getDevice().reboot();
assertThat(getShimApex().versionCode).isEqualTo(1L);
}
@@ -133,6 +145,20 @@
}
/**
+ * Tests staged rollbacks to system version involving only apex.
+ */
+ @Test
+ public void testApexOnlySystemVersionStagedRollback() throws Exception {
+ assumeTrue("Device does not support updating APEX", isApexUpdateSupported());
+
+ run("testApexOnlySystemVersion_EnableRollback");
+ getDevice().reboot();
+ run("testApexOnlySystemVersion_CommitRollback");
+ getDevice().reboot();
+ run("testApexOnlySystemVersion_ConfirmRollback");
+ }
+
+ /**
* Tests staged rollbacks involving only apex.
*/
@Test
@@ -163,4 +189,27 @@
run("testApexRollbackExpirationConfirmExpiration");
}
+ /**
+ * Tests staged rollbacks involving apex with rotated keys.
+ */
+ @Test
+ public void testApexKeyRotationStagedRollback() throws Exception {
+ assumeTrue("Device does not support updating APEX", isApexUpdateSupported());
+
+ run("testApexKeyRotation_EnableRollback");
+ getDevice().reboot();
+ run("testApexKeyRotation_CommitRollback");
+ getDevice().reboot();
+ run("testApexKeyRotation_CofirmRollback");
+ }
+
+ /**
+ * Tests installer B can't rollback a package installed by A.
+ */
+ @Test
+ public void testApkRollbackByAnotherInstaller() throws Exception {
+ run("testInstallTestAppA_EnableRollback");
+ run2("testRollbackTestAppA");
+ }
+
}
diff --git a/hostsidetests/sample/OWNERS b/hostsidetests/sample/OWNERS
new file mode 100644
index 0000000..84828b2
--- /dev/null
+++ b/hostsidetests/sample/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 346961
+include /tests/sample/OWNERS
diff --git a/hostsidetests/security/AndroidTest.xml b/hostsidetests/security/AndroidTest.xml
index ac79c0e..0f03245 100755
--- a/hostsidetests/security/AndroidTest.xml
+++ b/hostsidetests/security/AndroidTest.xml
@@ -18,6 +18,7 @@
<option name="config-descriptor:metadata" key="component" value="security" />
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
<test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
<option name="jar" value="CtsSecurityHostTestCases.jar" />
diff --git a/hostsidetests/security/OWNERS b/hostsidetests/security/OWNERS
new file mode 100644
index 0000000..94522e3
--- /dev/null
+++ b/hostsidetests/security/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 36824
+include /tests/tests/security/OWNERS
diff --git a/hostsidetests/security/src/android/cts/security/KernelConfigTest.java b/hostsidetests/security/src/android/cts/security/KernelConfigTest.java
index 113ec76..5ff9802 100644
--- a/hostsidetests/security/src/android/cts/security/KernelConfigTest.java
+++ b/hostsidetests/security/src/android/cts/security/KernelConfigTest.java
@@ -31,6 +31,7 @@
import java.io.InputStreamReader;
import java.lang.String;
import java.util.stream.Collectors;
+import java.util.Set;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -236,4 +237,61 @@
configSet.contains("CONFIG_PAGE_TABLE_ISOLATION=y"));
}
}
+
+ /**
+ * Test that the kernel enables static usermodehelper and sets
+ * the path to a whitelisted path.
+ *
+ * @throws Exception
+ */
+ @CddTest(requirement="9.7")
+ public void testConfigDisableUsermodehelper() throws Exception {
+ if (PropertyUtil.getFirstApiLevel(mDevice) < 29) {
+ return;
+ }
+
+ final String ENABLE_CONFIG = "CONFIG_STATIC_USERMODEHELPER=y";
+ final String PATH_CONFIG = "CONFIG_STATIC_USERMODEHELPER_PATH=";
+
+ final Set<String> ALLOWED_PATH_PREFIXES = new HashSet<String>();
+ ALLOWED_PATH_PREFIXES.add("/vendor/");
+ ALLOWED_PATH_PREFIXES.add("/system/");
+
+ assertTrue("Linux kernel must enable static usermodehelper: " + ENABLE_CONFIG,
+ configSet.contains(ENABLE_CONFIG));
+
+ String configPath = null;
+
+ for (String option : configSet) {
+ if (option.startsWith(PATH_CONFIG)) {
+ configPath = option;
+ }
+ }
+
+ int index = configPath.indexOf('=');
+ String path = configPath.substring(index+1);
+
+ assertTrue("Linux kernel must specify an absolute path for static usermodehelper path",
+ configPath.contains("..") == false);
+
+ boolean pathIsWhitelisted = false;
+
+ for (String allowedPath : ALLOWED_PATH_PREFIXES) {
+ if (path.startsWith(allowedPath)) {
+ pathIsWhitelisted = true;
+ break;
+ }
+ }
+
+ // Specifying no path, which disables usermodehelper, is also
+ // valid.
+ pathIsWhitelisted |= path.isEmpty();
+
+ String whitelistedPathPrefixExample = "'" +
+ String.join("', '", ALLOWED_PATH_PREFIXES) + "'";
+
+ assertTrue("Linux kernel must specify a whitelisted static usermodehelper path, "
+ + "and it must be empty or start with one of the following "
+ + "prefixes: " + whitelistedPathPrefixExample, pathIsWhitelisted);
+ }
}
diff --git a/hostsidetests/securitybulletin/OWNERS b/hostsidetests/securitybulletin/OWNERS
new file mode 100644
index 0000000..ca82a6a
--- /dev/null
+++ b/hostsidetests/securitybulletin/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 36824
+mspector@google.com
+samschumacher@google.com
diff --git a/hostsidetests/securitybulletin/securityPatch/Bug-115739809/poc.cpp b/hostsidetests/securitybulletin/securityPatch/Bug-115739809/poc.cpp
index 83227af..73251cd 100755
--- a/hostsidetests/securitybulletin/securityPatch/Bug-115739809/poc.cpp
+++ b/hostsidetests/securitybulletin/securityPatch/Bug-115739809/poc.cpp
@@ -108,6 +108,10 @@
outMsg->body.motion.xPrecision = msg.body.motion.xPrecision;
// float yPrecision
outMsg->body.motion.yPrecision = msg.body.motion.yPrecision;
+ // float xCursorPosition
+ outMsg->body.motion.xCursorPosition = msg.body.motion.xCursorPosition;
+ // float yCursorPosition
+ outMsg->body.motion.yCursorPosition = msg.body.motion.yCursorPosition;
// uint32_t pointerCount
outMsg->body.motion.pointerCount = msg.body.motion.pointerCount;
//struct Pointer pointers[MAX_POINTERS]
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2016-3746/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2016-3746/poc.cpp
index 7b67095..1e55834 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2016-3746/poc.cpp
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2016-3746/poc.cpp
@@ -80,7 +80,7 @@
sp<MemoryDealer> dealerIn = new MemoryDealer(inSize);
IOMX::buffer_id inBufferId = 0;
memory = dealerIn->allocate(inSize);
- if (memory.get() == nullptr || memory->pointer() == nullptr) {
+ if (memory.get() == nullptr || memory->unsecurePointer() == nullptr) {
ALOGE("memory allocate failed for port index 0, err: %d", err);
mOMXNode->freeNode();
client.disconnect();
@@ -93,7 +93,7 @@
*/
OMXBuffer omxInBuf(memory);
- memset(memory->pointer(), 0xCF, inSize);
+ memset(memory->unsecurePointer(), 0xCF, inSize);
err = mOMXNode->useBuffer(0, omxInBuf, &inBufferId);
ALOGI("useBuffer, port index 0, err: %d", err);
@@ -106,7 +106,7 @@
sp<MemoryDealer> dealerOut = new MemoryDealer(outSize);
IOMX::buffer_id outBufferId = 0;
memory = dealerOut->allocate(outSize);
- if (memory.get() == nullptr || memory->pointer() == nullptr) {
+ if (memory.get() == nullptr || memory->unsecurePointer() == nullptr) {
ALOGE("memory allocate failed for port index 1, err: %d", err);
mOMXNode->freeNode();
client.disconnect();
diff --git a/hostsidetests/shortcuts/deviceside/upgrade/Android.bp b/hostsidetests/shortcuts/deviceside/upgrade/Android.bp
index a0e435f..f386a71 100644
--- a/hostsidetests/shortcuts/deviceside/upgrade/Android.bp
+++ b/hostsidetests/shortcuts/deviceside/upgrade/Android.bp
@@ -27,6 +27,9 @@
"vts",
"general-tests",
],
+ static_libs: [
+ "compatibility-device-util-axt",
+ ],
}
android_test_helper_app {
diff --git a/hostsidetests/signedconfig/hostside/OWNERS b/hostsidetests/signedconfig/hostside/OWNERS
new file mode 100644
index 0000000..f968305
--- /dev/null
+++ b/hostsidetests/signedconfig/hostside/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 533114
+mathewi@google.com
+*
diff --git a/hostsidetests/stagedinstall/Android.bp b/hostsidetests/stagedinstall/Android.bp
index 49f7f87..b3c2bfa 100644
--- a/hostsidetests/stagedinstall/Android.bp
+++ b/hostsidetests/stagedinstall/Android.bp
@@ -45,54 +45,35 @@
manifest : "app/AndroidManifest.xml",
java_resources: [
+ ":ApexKeyRotationTestV2_SignedBob",
+ ":ApexKeyRotationTestV2_SignedBobRot",
+ ":ApexKeyRotationTestV2_SignedEve",
+ ":ApexKeyRotationTestV3_SignedBob",
+ ":ApexKeyRotationTestV3_SignedBobRot",
":StagedInstallTestApexV1_NotPreInstalled",
+ ":StagedInstallTestApexV1",
":StagedInstallTestApexV2",
":StagedInstallTestApexV2_AdditionalFile",
":StagedInstallTestApexV2_AdditionalFolder",
+ ":StagedInstallTestApexV2_DifferentCertificate",
":StagedInstallTestApexV2_WithPostInstallHook",
":StagedInstallTestApexV2_WithPreInstallHook",
":StagedInstallTestApexV2_WrongSha",
":StagedInstallTestApexV3",
- ":StagedInstallTestAppAv1",
- ":StagedInstallTestAppAv2",
- ":StagedInstallTestAppBv1",
":StagedInstallTestAppSamePackageNameAsApex",
],
static_libs: [
"androidx.test.runner",
"androidx.test.core",
"truth-prebuilt",
+ "cts-install-lib",
],
- sdk_version: "system_current",
+ sdk_version: "test_current",
test_suites: ["device-tests"],
}
android_test_helper_app {
- name: "StagedInstallTestAppAv1",
-
- srcs: ["testdata/apk/src/**/*java"],
-
- manifest: "testdata/apk/Av1.xml",
-}
-
-android_test_helper_app {
- name: "StagedInstallTestAppAv2",
-
- srcs: ["testdata/apk/src/**/*java"],
-
- manifest: "testdata/apk/Av2.xml",
-}
-
-android_test_helper_app {
- name: "StagedInstallTestAppBv1",
-
- srcs: ["testdata/apk/src/**/*java"],
-
- manifest: "testdata/apk/Bv1.xml",
-}
-
-android_test_helper_app {
name: "StagedInstallTestAppSamePackageNameAsApex",
srcs: ["testdata/apk/src/**/*java"],
@@ -101,6 +82,48 @@
}
prebuilt_apex {
+ name: "ApexKeyRotationTestV2_SignedBob",
+ src: "testdata/apex/com.android.apex.cts.shim.v2_signed_bob.apex",
+ filename: "com.android.apex.cts.shim.v2_signed_bob.apex",
+ installable: false,
+}
+
+prebuilt_apex {
+ name: "ApexKeyRotationTestV2_SignedBobRot",
+ src: "testdata/apex/com.android.apex.cts.shim.v2_signed_bob_rot.apex",
+ filename: "com.android.apex.cts.shim.v2_signed_bob_rot.apex",
+ installable: false,
+}
+
+prebuilt_apex {
+ name: "ApexKeyRotationTestV2_SignedEve",
+ src: "testdata/apex/com.android.apex.cts.shim.v2_signed_eve.apex",
+ filename: "com.android.apex.cts.shim.v2_signed_eve.apex",
+ installable: false,
+}
+
+prebuilt_apex {
+ name: "ApexKeyRotationTestV3_SignedBob",
+ src: "testdata/apex/com.android.apex.cts.shim.v3_signed_bob.apex",
+ filename: "com.android.apex.cts.shim.v3_signed_bob.apex",
+ installable: false,
+}
+
+prebuilt_apex {
+ name: "ApexKeyRotationTestV3_SignedBobRot",
+ src: "testdata/apex/com.android.apex.cts.shim.v3_signed_bob_rot.apex",
+ filename: "com.android.apex.cts.shim.v3_signed_bob_rot.apex",
+ installable: false,
+}
+
+prebuilt_apex {
+ name: "StagedInstallTestApexV1",
+ src: "testdata/apex/com.android.apex.cts.shim.v1.apex",
+ filename: "com.android.apex.cts.shim.v1.apex",
+ installable: false,
+}
+
+prebuilt_apex {
name: "StagedInstallTestApexV2",
src: "testdata/apex/com.android.apex.cts.shim.v2.apex",
filename: "com.android.apex.cts.shim.v2.apex",
@@ -155,3 +178,10 @@
filename: "com.android.apex.cts.shim_not_pre_installed.apex",
installable: false,
}
+
+prebuilt_apex {
+ name: "StagedInstallTestApexV2_DifferentCertificate",
+ src: "testdata/apex/com.android.apex.cts.shim.v2_different_certificate.apex",
+ filename: "com.android.apex.cts.shim.v2_different_certificate.apex",
+ installable: false,
+}
diff --git a/hostsidetests/stagedinstall/AndroidTest.xml b/hostsidetests/stagedinstall/AndroidTest.xml
index ad944b0..64d7495 100644
--- a/hostsidetests/stagedinstall/AndroidTest.xml
+++ b/hostsidetests/stagedinstall/AndroidTest.xml
@@ -24,10 +24,10 @@
<option name="test-file-name" value="StagedInstallTest.apk" />
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
- <option name="run-command" value="pm uninstall com.android.tests.stagedinstall.testapp.A" />
- <option name="run-command" value="pm uninstall com.android.tests.stagedinstall.testapp.B" />
- <option name="teardown-command" value="pm uninstall com.android.tests.stagedinstall.testapp.A" />
- <option name="teardown-command" value="pm uninstall com.android.tests.stagedinstall.testapp.B" />
+ <option name="run-command" value="pm uninstall com.android.cts.install.lib.testapp.A" />
+ <option name="run-command" value="pm uninstall com.android.cts.install.lib.testapp.B" />
+ <option name="teardown-command" value="pm uninstall com.android.cts.install.lib.testapp.A" />
+ <option name="teardown-command" value="pm uninstall com.android.cts.install.lib.testapp.B" />
</target_preparer>
<test class="com.android.tradefed.testtype.HostTest" >
<option name="class" value="com.android.tests.stagedinstall.host.StagedInstallTest" />
diff --git a/hostsidetests/stagedinstall/OWNERS b/hostsidetests/stagedinstall/OWNERS
new file mode 100644
index 0000000..2dd1076
--- /dev/null
+++ b/hostsidetests/stagedinstall/OWNERS
@@ -0,0 +1,6 @@
+# Bug component: 36137
+dariofreni@google.com
+toddke@google.com
+narayan@google.com
+patb@google.com
+ioffe@google.com
diff --git a/hostsidetests/stagedinstall/TEST_MAPPING b/hostsidetests/stagedinstall/TEST_MAPPING
index c487fa3..8a1c753 100644
--- a/hostsidetests/stagedinstall/TEST_MAPPING
+++ b/hostsidetests/stagedinstall/TEST_MAPPING
@@ -1,6 +1,16 @@
{
"presubmit": [
{
+ "name": "CtsStagedInstallHostTestCases",
+ "options": [
+ {
+ "exclude-annotation": "android.platform.test.annotations.LargeTest"
+ }
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
"name": "CtsStagedInstallHostTestCases"
}
]
diff --git a/hostsidetests/stagedinstall/app/AndroidManifest.xml b/hostsidetests/stagedinstall/app/AndroidManifest.xml
index c327b79..51c2ec4 100644
--- a/hostsidetests/stagedinstall/app/AndroidManifest.xml
+++ b/hostsidetests/stagedinstall/app/AndroidManifest.xml
@@ -18,7 +18,7 @@
package="com.android.tests.stagedinstall" >
<application>
- <receiver android:name="com.android.tests.stagedinstall.LocalIntentSender"
+ <receiver android:name="com.android.cts.install.lib.LocalIntentSender"
android:exported="true" />
<receiver android:name="com.android.tests.stagedinstall.SessionUpdateBroadcastReceiver">
<intent-filter>
@@ -32,4 +32,4 @@
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.tests.stagedinstall"
android:label="StagedInstall Test"/>
-</manifest>
\ No newline at end of file
+</manifest>
diff --git a/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/ApexShimValidationTest.java b/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/ApexShimValidationTest.java
index f9c4ba8..c36c4f7 100644
--- a/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/ApexShimValidationTest.java
+++ b/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/ApexShimValidationTest.java
@@ -21,22 +21,22 @@
import static com.google.common.truth.Truth.assertThat;
import android.Manifest;
-import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
-import android.content.pm.PackageManager;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.cts.install.lib.Install;
+import com.android.cts.install.lib.InstallUtils;
+import com.android.cts.install.lib.LocalIntentSender;
+import com.android.cts.install.lib.TestApp;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-import java.io.InputStream;
-import java.io.OutputStream;
import java.util.concurrent.TimeUnit;
/**
@@ -120,63 +120,17 @@
@Test
public void testInstallRejected_VerifyPostReboot() throws Exception {
- assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1);
- }
-
- private static PackageInstaller getPackageInstaller() {
- return InstrumentationRegistry.getInstrumentation().getContext().getPackageManager()
- .getPackageInstaller();
- }
-
- private static long getInstalledVersion(String packageName) {
- Context context = InstrumentationRegistry.getInstrumentation().getContext();
- PackageManager pm = context.getPackageManager();
- try {
- PackageInfo info = pm.getPackageInfo(packageName, PackageManager.MATCH_APEX);
- return info.getLongVersionCode();
- } catch (PackageManager.NameNotFoundException e) {
- return -1;
- }
+ assertThat(InstallUtils.getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1);
}
private static int stageApex(String apexFileName) throws Exception {
- PackageInstaller installer = getPackageInstaller();
-
- PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
- PackageInstaller.SessionParams.MODE_FULL_INSTALL);
- params.setInstallAsApex();
- params.setStaged();
- int sessionId = installer.createSession(params);
- PackageInstaller.Session session = installer.openSession(sessionId);
- writeApex(session, apexFileName);
+ TestApp apexTestApp = new TestApp("ShimApex", SHIM_APEX_PACKAGE_NAME, 2,
+ true, apexFileName);
+ int sessionId = Install.single(apexTestApp).setStaged().createSession();
+ PackageInstaller.Session session = InstallUtils.openPackageInstallerSession(sessionId);
session.commit(LocalIntentSender.getIntentSender());
Intent result = LocalIntentSender.getIntentSenderResult();
- assertStatusSuccess(result);
+ InstallUtils.assertStatusSuccess(result);
return sessionId;
}
-
- private static void writeApex(PackageInstaller.Session session, String apkFileName)
- throws Exception {
- try (OutputStream packageInSession = session.openWrite(apkFileName, 0, -1);
- InputStream is =
- ApexShimValidationTest.class.getClassLoader().getResourceAsStream(
- apkFileName)) {
- byte[] buffer = new byte[4096];
- int n;
- while ((n = is.read(buffer)) >= 0) {
- packageInSession.write(buffer, 0, n);
- }
- }
- }
-
- private static void assertStatusSuccess(Intent result) {
- int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
- PackageInstaller.STATUS_FAILURE);
- if (status == -1) {
- throw new AssertionError("PENDING USER ACTION");
- } else if (status > 0) {
- String message = result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
- throw new AssertionError(message == null ? "UNKNOWN FAILURE" : message);
- }
- }
}
diff --git a/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java b/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java
index 968960a..748e45e 100644
--- a/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java
+++ b/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java
@@ -16,6 +16,7 @@
package com.android.tests.stagedinstall;
+import static com.android.cts.install.lib.InstallUtils.getPackageInstaller;
import static com.android.tests.stagedinstall.PackageInstallerSessionInfoSubject.assertThat;
import static com.google.common.truth.Truth.assertThat;
@@ -26,16 +27,22 @@
import android.Manifest;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
-import android.util.Pair;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.cts.install.lib.Install;
+import com.android.cts.install.lib.InstallUtils;
+import com.android.cts.install.lib.LocalIntentSender;
+import com.android.cts.install.lib.TestApp;
+import com.android.cts.install.lib.Uninstall;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -47,6 +54,7 @@
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
+import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
@@ -82,12 +90,6 @@
private static final String TAG = "StagedInstallTest";
- private static final String TEST_APP_A = "com.android.tests.stagedinstall.testapp.A";
- private static final String TEST_APP_B = "com.android.tests.stagedinstall.testapp.B";
- private static final String SHIM_APEX_PACKAGE_NAME = "com.android.apex.cts.shim";
- private static final String NOT_PRE_INSTALLED_SHIM_APEX_PACKAGE_NAME =
- "com.android.apex.cts.shim_not_pre_installed";
-
private File mTestStateFile = new File(
InstrumentationRegistry.getInstrumentation().getContext().getFilesDir(),
"ctsstagedinstall_state");
@@ -95,6 +97,29 @@
private static final Duration WAIT_FOR_SESSION_REMOVED_TTL = Duration.ofSeconds(10);
private static final Duration SLEEP_DURATION = Duration.ofMillis(200);
+ private static final String SHIM_PACKAGE_NAME = "com.android.apex.cts.shim";
+ private static final TestApp TESTAPP_SAME_NAME_AS_APEX = new TestApp(
+ "TestAppSamePackageNameAsApex", SHIM_PACKAGE_NAME, 1, /*isApex*/ false,
+ "StagedInstallTestAppSamePackageNameAsApex.apk");
+ public static final TestApp Apex2DifferentCertificate = new TestApp(
+ "Apex2DifferentCertificate", SHIM_PACKAGE_NAME, 2, /*isApex*/true,
+ "com.android.apex.cts.shim.v2_different_certificate.apex");
+ private static final TestApp Apex2SignedBob = new TestApp(
+ "Apex2SignedBob", SHIM_PACKAGE_NAME, 2, /*isApex*/true,
+ "com.android.apex.cts.shim.v2_signed_bob.apex");
+ private static final TestApp Apex2SignedBobRot = new TestApp(
+ "Apex2SignedBobRot", SHIM_PACKAGE_NAME, 2, /*isApex*/true,
+ "com.android.apex.cts.shim.v2_signed_bob_rot.apex");
+ private static final TestApp Apex2SignedEve = new TestApp(
+ "Apex2SignedEve", SHIM_PACKAGE_NAME, 2, /*isApex*/true,
+ "com.android.apex.cts.shim.v2_signed_eve.apex");
+ private static final TestApp Apex3SignedBob = new TestApp(
+ "Apex3SignedBob", SHIM_PACKAGE_NAME, 3, /*isApex*/true,
+ "com.android.apex.cts.shim.v3_signed_bob.apex");
+ private static final TestApp Apex3SignedBobRot = new TestApp(
+ "Apex3SignedBobRot", SHIM_PACKAGE_NAME, 3, /*isApex*/true,
+ "com.android.apex.cts.shim.v3_signed_bob_rot.apex");
+
@Before
public void adoptShellPermissions() {
InstrumentationRegistry
@@ -133,8 +158,7 @@
Log.e(TAG, "Failed to abandon session " + sessionInfo.getSessionId(), e);
}
}
- uninstall(TEST_APP_A);
- uninstall(TEST_APP_B);
+ Uninstall.packages(TestApp.A, TestApp.B);
Files.deleteIfExists(mTestStateFile.toPath());
}
@@ -156,20 +180,19 @@
@Test
public void testInstallStagedApk_Commit() throws Exception {
- int sessionId = stageSingleApk(
- "StagedInstallTestAppAv1.apk").assertSuccessful().getSessionId();
- assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(-1);
+ int sessionId = stageSingleApk(TestApp.A1).assertSuccessful().getSessionId();
+ assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1);
waitForIsReadyBroadcast(sessionId);
assertSessionReady(sessionId);
storeSessionId(sessionId);
- assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(-1);
+ assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1);
}
@Test
public void testInstallStagedApk_VerifyPostReboot() throws Exception {
int sessionId = retrieveLastSessionId();
assertSessionApplied(sessionId);
- assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(1);
+ assertThat(getInstalledVersion(TestApp.A)).isEqualTo(1);
}
@Test
@@ -184,33 +207,30 @@
@Test
public void testInstallMultipleStagedApks_Commit() throws Exception {
- int sessionId = stageMultipleApks(
- "StagedInstallTestAppAv1.apk",
- "StagedInstallTestAppBv1.apk")
+ int sessionId = stageMultipleApks(TestApp.A1, TestApp.B1)
.assertSuccessful().getSessionId();
- assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(-1);
- assertThat(getInstalledVersion(TEST_APP_B)).isEqualTo(-1);
+ assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1);
+ assertThat(getInstalledVersion(TestApp.B)).isEqualTo(-1);
waitForIsReadyBroadcast(sessionId);
assertSessionReady(sessionId);
storeSessionId(sessionId);
- assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(-1);
- assertThat(getInstalledVersion(TEST_APP_B)).isEqualTo(-1);
+ assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1);
+ assertThat(getInstalledVersion(TestApp.B)).isEqualTo(-1);
}
@Test
public void testInstallMultipleStagedApks_VerifyPostReboot() throws Exception {
int sessionId = retrieveLastSessionId();
assertSessionApplied(sessionId);
- assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(1);
- assertThat(getInstalledVersion(TEST_APP_B)).isEqualTo(1);
+ assertThat(getInstalledVersion(TestApp.A)).isEqualTo(1);
+ assertThat(getInstalledVersion(TestApp.B)).isEqualTo(1);
}
@Test
public void testFailInstallAnotherSessionAlreadyInProgress_BothSinglePackage()
throws Exception {
- int sessionId = stageSingleApk(
- "StagedInstallTestAppAv1.apk").assertSuccessful().getSessionId();
- StageSessionResult failedSessionResult = stageSingleApk("StagedInstallTestAppAv1.apk");
+ int sessionId = stageSingleApk(TestApp.A1).assertSuccessful().getSessionId();
+ StageSessionResult failedSessionResult = stageSingleApk(TestApp.A1);
assertThat(failedSessionResult.getErrorMessage()).contains(
"There is already in-progress committed staged session");
getPackageInstaller().abandonSession(sessionId);
@@ -219,11 +239,8 @@
@Test
public void testFailInstallAnotherSessionAlreadyInProgress_SinglePackageMultiPackage()
throws Exception {
- int sessionId = stageSingleApk(
- "StagedInstallTestAppAv1.apk").assertSuccessful().getSessionId();
- StageSessionResult failedSessionResult = stageMultipleApks(
- "StagedInstallTestAppAv1.apk",
- "StagedInstallTestAppBv1.apk");
+ int sessionId = stageSingleApk(TestApp.A1).assertSuccessful().getSessionId();
+ StageSessionResult failedSessionResult = stageMultipleApks(TestApp.A1, TestApp.B1);
assertThat(failedSessionResult.getErrorMessage()).contains(
"There is already in-progress committed staged session");
getPackageInstaller().abandonSession(sessionId);
@@ -232,11 +249,9 @@
@Test
public void testFailInstallAnotherSessionAlreadyInProgress_MultiPackageSinglePackage()
throws Exception {
- int sessionId = stageMultipleApks(
- "StagedInstallTestAppAv1.apk",
- "StagedInstallTestAppBv1.apk").assertSuccessful().getSessionId();
- StageSessionResult failedSessionResult = stageSingleApk(
- "StagedInstallTestAppAv1.apk");
+ int sessionId = stageMultipleApks(TestApp.A1, TestApp.B1)
+ .assertSuccessful().getSessionId();
+ StageSessionResult failedSessionResult = stageSingleApk(TestApp.A1);
assertThat(failedSessionResult.getErrorMessage()).contains(
"There is already in-progress committed staged session");
getPackageInstaller().abandonSession(sessionId);
@@ -245,12 +260,9 @@
@Test
public void testFailInstallAnotherSessionAlreadyInProgress_BothMultiPackage()
throws Exception {
- int sessionId = stageMultipleApks(
- "StagedInstallTestAppAv1.apk",
- "StagedInstallTestAppBv1.apk").assertSuccessful().getSessionId();
- StageSessionResult failedSessionResult = stageMultipleApks(
- "StagedInstallTestAppAv1.apk",
- "StagedInstallTestAppBv1.apk");
+ int sessionId = stageMultipleApks(TestApp.A1, TestApp.B1)
+ .assertSuccessful().getSessionId();
+ StageSessionResult failedSessionResult = stageMultipleApks(TestApp.A1, TestApp.B1);
assertThat(failedSessionResult.getErrorMessage()).contains(
"There is already in-progress committed staged session");
getPackageInstaller().abandonSession(sessionId);
@@ -258,9 +270,8 @@
@Test
public void testAbandonStagedApkBeforeReboot_CommitAndAbandon() throws Exception {
- int sessionId = stageSingleApk(
- "StagedInstallTestAppAv1.apk").assertSuccessful().getSessionId();
- assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(-1);
+ int sessionId = stageSingleApk(TestApp.A1).assertSuccessful().getSessionId();
+ assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1);
waitForIsReadyBroadcast(sessionId);
PackageInstaller.SessionInfo session = getStagedSessionInfo(sessionId);
assertSessionReady(sessionId);
@@ -287,14 +298,13 @@
@Test
public void testAbandonStagedApkBeforeReboot_VerifyPostReboot() throws Exception {
- assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(-1);
+ assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1);
}
@Test
public void testGetActiveStagedSession() throws Exception {
PackageInstaller packageInstaller = getPackageInstaller();
- int sessionId = stageSingleApk(
- "StagedInstallTestAppAv1.apk").assertSuccessful().getSessionId();
+ int sessionId = stageSingleApk(TestApp.A1).assertSuccessful().getSessionId();
PackageInstaller.SessionInfo session = packageInstaller.getActiveStagedSession();
assertThat(session.getSessionId()).isEqualTo(sessionId);
}
@@ -308,9 +318,7 @@
@Test
public void testGetGetActiveStagedSession_MultiApkSession() throws Exception {
- int sessionId = stageMultipleApks(
- "StagedInstallTestAppAv1.apk",
- "StagedInstallTestAppBv1.apk")
+ int sessionId = stageMultipleApks(TestApp.A1, TestApp.B1)
.assertSuccessful().getSessionId();
PackageInstaller.SessionInfo session = getPackageInstaller().getActiveStagedSession();
assertThat(session.getSessionId()).isEqualTo(sessionId);
@@ -318,22 +326,20 @@
@Test
public void testStagedInstallDowngrade_DowngradeNotRequested_Fails_Commit() throws Exception {
- assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(-1);
- installNonStaged("StagedInstallTestAppAv2.apk");
- int sessionId = stageSingleApk(
- "StagedInstallTestAppAv1.apk").assertSuccessful().getSessionId();
- assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(2);
+ assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1);
+ Install.single(TestApp.A2).commit();
+ int sessionId = stageSingleApk(TestApp.A1).assertSuccessful().getSessionId();
+ assertThat(getInstalledVersion(TestApp.A)).isEqualTo(2);
PackageInstaller.SessionInfo sessionInfo = waitForBroadcast(sessionId);
assertThat(sessionInfo).isStagedSessionFailed();
}
@Test
public void testStagedInstallDowngrade_DowngradeRequested_Commit() throws Exception {
- assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(-1);
- installNonStaged("StagedInstallTestAppAv2.apk");
- int sessionId = stageDowngradeSingleApk(
- "StagedInstallTestAppAv1.apk").assertSuccessful().getSessionId();
- assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(2);
+ assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1);
+ Install.single(TestApp.A2).commit();
+ int sessionId = stageDowngradeSingleApk(TestApp.A1).assertSuccessful().getSessionId();
+ assertThat(getInstalledVersion(TestApp.A)).isEqualTo(2);
waitForIsReadyBroadcast(sessionId);
assertSessionReady(sessionId);
storeSessionId(sessionId);
@@ -341,11 +347,10 @@
@Test
public void testStagedInstallDowngrade_DowngradeRequested_Fails_Commit() throws Exception {
- assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(-1);
- installNonStaged("StagedInstallTestAppAv2.apk");
- int sessionId = stageDowngradeSingleApk(
- "StagedInstallTestAppAv1.apk").assertSuccessful().getSessionId();
- assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(2);
+ assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1);
+ Install.single(TestApp.A2).commit();
+ int sessionId = stageDowngradeSingleApk(TestApp.A1).assertSuccessful().getSessionId();
+ assertThat(getInstalledVersion(TestApp.A)).isEqualTo(2);
PackageInstaller.SessionInfo sessionInfo = waitForBroadcast(sessionId);
assertThat(sessionInfo).isStagedSessionFailed();
}
@@ -356,49 +361,47 @@
int sessionId = retrieveLastSessionId();
assertSessionApplied(sessionId);
// App should be downgraded.
- assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(1);
+ assertThat(getInstalledVersion(TestApp.A)).isEqualTo(1);
}
@Test
public void testInstallStagedApex_Commit() throws Exception {
- assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1);
- int sessionId = stageSingleApk(
- "com.android.apex.cts.shim.v2.apex").assertSuccessful().getSessionId();
+ assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1);
+ int sessionId = stageSingleApk(TestApp.Apex2).assertSuccessful().getSessionId();
waitForIsReadyBroadcast(sessionId);
assertSessionReady(sessionId);
storeSessionId(sessionId);
// Version shouldn't change before reboot.
- assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1);
+ assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1);
}
@Test
public void testInstallStagedApex_VerifyPostReboot() throws Exception {
int sessionId = retrieveLastSessionId();
assertSessionApplied(sessionId);
- assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(2);
+ assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(2);
}
@Test
public void testInstallStagedApexAndApk_Commit() throws Exception {
- assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1);
- assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(-1);
- int sessionId = stageMultipleApks(
- "com.android.apex.cts.shim.v2.apex",
- "StagedInstallTestAppAv1.apk").assertSuccessful().getSessionId();
+ assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1);
+ assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1);
+ int sessionId = stageMultipleApks(TestApp.Apex2, TestApp.A1)
+ .assertSuccessful().getSessionId();
waitForIsReadyBroadcast(sessionId);
assertSessionReady(sessionId);
storeSessionId(sessionId);
// Version shouldn't change before reboot.
- assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1);
- assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(-1);
+ assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1);
+ assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1);
}
@Test
public void testInstallStagedApexAndApk_VerifyPostReboot() throws Exception {
int sessionId = retrieveLastSessionId();
assertSessionApplied(sessionId);
- assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(2);
- assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(1);
+ assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(2);
+ assertThat(getInstalledVersion(TestApp.A)).isEqualTo(1);
}
@Test
@@ -418,9 +421,9 @@
@Test
public void testInstallStagedNonPreInstalledApex_Fails() throws Exception {
- assertThat(getInstalledVersion(NOT_PRE_INSTALLED_SHIM_APEX_PACKAGE_NAME)).isEqualTo(-1);
+ assertThat(getInstalledVersion(TestApp.NotPreInstalledApex)).isEqualTo(-1);
int sessionId = stageSingleApk(
- "com.android.apex.cts.shim_not_pre_installed.apex")
+ TestApp.ApexNotPreInstalled)
.assertSuccessful().getSessionId();
PackageInstaller.SessionInfo sessionInfo = waitForBroadcast(sessionId);
assertThat(sessionInfo).isStagedSessionFailed();
@@ -428,9 +431,9 @@
@Test
public void testStageApkWithSameNameAsApexShouldFail_Commit() throws Exception {
- assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1);
- int sessionId = stageSingleApk(
- "StagedInstallTestAppSamePackageNameAsApex.apk").assertSuccessful().getSessionId();
+ assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1);
+ int sessionId = stageSingleApk(TESTAPP_SAME_NAME_AS_APEX)
+ .assertSuccessful().getSessionId();
waitForIsReadyBroadcast(sessionId);
assertSessionReady(sessionId);
storeSessionId(sessionId);
@@ -440,28 +443,51 @@
public void testStageApkWithSameNameAsApexShouldFail_VerifyPostReboot() throws Exception {
int sessionId = retrieveLastSessionId();
assertSessionFailed(sessionId);
- assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1);
+ assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1);
}
@Test
public void testNonStagedInstallApkWithSameNameAsApexShouldFail() throws Exception {
- assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1);
- PackageInstaller packageInstaller = getPackageInstaller();
- PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams(
- PackageInstaller.SessionParams.MODE_FULL_INSTALL);
- int sessionId = packageInstaller.createSession(sessionParams);
- PackageInstaller.Session session = packageInstaller.openSession(sessionId);
- writeApk(session, "StagedInstallTestAppSamePackageNameAsApex.apk");
- session.commit(LocalIntentSender.getIntentSender());
- final String errorMessage = extractErrorMessage(LocalIntentSender.getIntentSenderResult());
- assertThat(errorMessage).contains("is an APEX package and can't be installed as an APK");
- assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1);
+ assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1);
+ InstallUtils.commitExpectingFailure(AssertionError.class,
+ "is an APEX package and can't be installed as an APK",
+ Install.single(TESTAPP_SAME_NAME_AS_APEX));
+ assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1);
+ }
+
+ @Test
+ public void testInstallV2Apex_Commit() throws Exception {
+ int sessionId = stageSingleApk(TestApp.Apex2).assertSuccessful().getSessionId();
+ waitForIsReadyBroadcast(sessionId);
+ assertSessionReady(sessionId);
+ storeSessionId(sessionId);
+ }
+
+ @Test
+ public void testInstallV2Apex_VerifyPostReboot() throws Exception {
+ int sessionId = retrieveLastSessionId();
+ assertSessionApplied(sessionId);
+ assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(2);
+ }
+
+ @Test
+ public void testInstallV2SignedBobApex_Commit() throws Exception {
+ int sessionId = stageSingleApk(Apex2SignedBobRot).assertSuccessful().getSessionId();
+ waitForIsReadyBroadcast(sessionId);
+ assertSessionReady(sessionId);
+ storeSessionId(sessionId);
+ }
+
+ @Test
+ public void testInstallV2SignedBobApex_VerifyPostReboot() throws Exception {
+ int sessionId = retrieveLastSessionId();
+ assertSessionApplied(sessionId);
+ assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(2);
}
@Test
public void testInstallV3Apex_Commit() throws Exception {
- int sessionId = stageSingleApk(
- "com.android.apex.cts.shim.v3.apex").assertSuccessful().getSessionId();
+ int sessionId = stageSingleApk(TestApp.Apex3).assertSuccessful().getSessionId();
waitForIsReadyBroadcast(sessionId);
assertSessionReady(sessionId);
storeSessionId(sessionId);
@@ -471,15 +497,29 @@
public void testInstallV3Apex_VerifyPostReboot() throws Exception {
int sessionId = retrieveLastSessionId();
assertSessionApplied(sessionId);
- assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(3);
+ assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(3);
+ }
+
+ @Test
+ public void testInstallV3SignedBobApex_Commit() throws Exception {
+ int sessionId = stageSingleApk(Apex2SignedBobRot).assertSuccessful().getSessionId();
+ waitForIsReadyBroadcast(sessionId);
+ assertSessionReady(sessionId);
+ storeSessionId(sessionId);
+ }
+
+ @Test
+ public void testInstallV3SignedBobApex_VerifyPostReboot() throws Exception {
+ int sessionId = retrieveLastSessionId();
+ assertSessionApplied(sessionId);
+ assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(2);
}
@Test
public void testStagedInstallDowngradeApex_DowngradeNotRequested_Fails_Commit()
throws Exception {
- assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(3);
- int sessionId = stageSingleApk(
- "com.android.apex.cts.shim.v2.apex").assertSuccessful().getSessionId();
+ assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(3);
+ int sessionId = stageSingleApk(TestApp.Apex2).assertSuccessful().getSessionId();
PackageInstaller.SessionInfo sessionInfo = waitForBroadcast(sessionId);
assertThat(sessionInfo).isStagedSessionFailed();
// Also verify that correct session info is reported by PackageManager.
@@ -493,15 +533,14 @@
int sessionId = retrieveLastSessionId();
assertSessionFailed(sessionId);
// INSTALL_REQUEST_DOWNGRADE wasn't set, so apex shouldn't be downgraded.
- assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(3);
+ assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(3);
}
@Test
public void testStagedInstallDowngradeApex_DowngradeRequested_DebugBuild_Commit()
throws Exception {
- assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(3);
- int sessionId = stageDowngradeSingleApk(
- "com.android.apex.cts.shim.v2.apex").assertSuccessful().getSessionId();
+ assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(3);
+ int sessionId = stageDowngradeSingleApk(TestApp.Apex2).assertSuccessful().getSessionId();
waitForIsReadyBroadcast(sessionId);
assertSessionReady(sessionId);
storeSessionId(sessionId);
@@ -513,15 +552,14 @@
int sessionId = retrieveLastSessionId();
assertSessionApplied(sessionId);
// Apex should be downgraded.
- assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(2);
+ assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(2);
}
@Test
public void testStagedInstallDowngradeApex_DowngradeRequested_UserBuild_Fails_Commit()
throws Exception {
- assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(3);
- int sessionId = stageDowngradeSingleApk(
- "com.android.apex.cts.shim.v2.apex").assertSuccessful().getSessionId();
+ assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(3);
+ int sessionId = stageDowngradeSingleApk(TestApp.Apex2).assertSuccessful().getSessionId();
PackageInstaller.SessionInfo sessionInfo = waitForBroadcast(sessionId);
assertThat(sessionInfo).isStagedSessionFailed();
// Also verify that correct session info is reported by PackageManager.
@@ -535,25 +573,39 @@
int sessionId = retrieveLastSessionId();
assertSessionFailed(sessionId);
// Apex shouldn't be downgraded.
- assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(3);
+ assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(3);
+ }
+
+ @Test
+ public void testStagedInstallDowngradeApexToSystemVersion_DebugBuild_Commit()
+ throws Exception {
+ assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(2);
+ int sessionId = stageDowngradeSingleApk(TestApp.Apex1).assertSuccessful().getSessionId();
+ waitForIsReadyBroadcast(sessionId);
+ assertSessionReady(sessionId);
+ storeSessionId(sessionId);
+ }
+
+ @Test
+ public void testStagedInstallDowngradeApexToSystemVersion_DebugBuild_VerifyPostReboot()
+ throws Exception {
+ int sessionId = retrieveLastSessionId();
+ assertSessionApplied(sessionId);
+ // Apex should be downgraded.
+ assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1);
}
@Test
public void testInstallApex_DeviceDoesNotSupportApex_Fails() throws Exception {
- try {
- stageSingleApk("com.android.apex.cts.shim.v2.apex");
- fail("IllegalArgumentException expected");
- } catch (IllegalArgumentException expected) {
- assertThat(expected.getMessage()).contains(
- "This device doesn't support the installation of APEX files");
- }
+ InstallUtils.commitExpectingFailure(IllegalArgumentException.class,
+ "This device doesn't support the installation of APEX files",
+ Install.single(TestApp.Apex2).setStaged());
}
@Test
public void testFailsInvalidApexInstall_Commit() throws Exception {
- assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1);
- int sessionId = stageSingleApk(
- "com.android.apex.cts.shim.v2_wrong_sha.apex").assertSuccessful()
+ assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1);
+ int sessionId = stageSingleApk(TestApp.ApexWrongSha2).assertSuccessful()
.getSessionId();
waitForIsFailedBroadcast(sessionId);
assertSessionFailed(sessionId);
@@ -603,13 +655,12 @@
};
Context context = InstrumentationRegistry.getInstrumentation().getContext();
- PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
+ PackageInstaller packageInstaller = getPackageInstaller();
packageInstaller.registerSessionCallback(callback, handler);
- int sessionId = stageSingleApk(
- "StagedInstallTestAppAv1.apk").assertSuccessful().getSessionId();
+ int sessionId = stageSingleApk(TestApp.A1).assertSuccessful().getSessionId();
- assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(-1);
+ assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1);
waitForIsReadyBroadcast(sessionId);
assertSessionReady(sessionId);
@@ -627,9 +678,150 @@
packageInstaller.abandonSession(sessionId);
}
- private static PackageInstaller getPackageInstaller() {
- return InstrumentationRegistry.getInstrumentation().getContext().getPackageManager()
- .getPackageInstaller();
+ @Test
+ public void testInstallStagedApexWithoutApexSuffix_Commit() throws Exception {
+ assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1);
+
+ int sessionId = stageSingleApk("com.android.apex.cts.shim.v2.apex", "package")
+ .assertSuccessful().getSessionId();
+ waitForIsReadyBroadcast(sessionId);
+ assertSessionReady(sessionId);
+ storeSessionId(sessionId);
+ // Version shouldn't change before reboot.
+ assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1);
+ }
+
+ @Test
+ public void testInstallStagedApexWithoutApexSuffix_VerifyPostReboot() throws Exception {
+ int sessionId = retrieveLastSessionId();
+ assertSessionApplied(sessionId);
+ assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(2);
+ }
+
+ @Test
+ public void testRejectsApexDifferentCertificate() throws Exception {
+ int sessionId = stageSingleApk(Apex2DifferentCertificate)
+ .assertSuccessful().getSessionId();
+ PackageInstaller.SessionInfo info =
+ SessionUpdateBroadcastReceiver.sessionBroadcasts.poll(60, TimeUnit.SECONDS);
+ assertThat(info.getSessionId()).isEqualTo(sessionId);
+ assertThat(info).isStagedSessionFailed();
+ assertThat(info.getStagedSessionErrorMessage()).contains("is not compatible with the one "
+ + "currently installed on device");
+ }
+
+ /**
+ * Tests for staged install involving rotated keys.
+ *
+ * Here alice means the original default key that cts.shim.v1 package was signed with and
+ * bob is the new key alice rotates to. Where ambiguous, we will refer keys as alice and bob
+ * instead of "old key" and "new key".
+ */
+
+ // The update should fail if it is signed with a different non-rotated key
+ @Test
+ public void testUpdateWithDifferentKeyButNoRotation() throws Exception {
+ int sessionId = stageSingleApk(Apex2SignedBob).assertSuccessful().getSessionId();
+ PackageInstaller.SessionInfo info =
+ SessionUpdateBroadcastReceiver.sessionBroadcasts.poll(60, TimeUnit.SECONDS);
+ assertThat(info.getSessionId()).isEqualTo(sessionId);
+ assertThat(info).isStagedSessionFailed();
+ }
+
+ // The update should pass if it is signed with a proper rotated key
+ @Test
+ public void testUpdateWithDifferentKey_Commit() throws Exception {
+ int sessionId = stageSingleApk(Apex2SignedBobRot).assertSuccessful().getSessionId();
+ PackageInstaller.SessionInfo info =
+ SessionUpdateBroadcastReceiver.sessionBroadcasts.poll(60, TimeUnit.SECONDS);
+ assertThat(info.getSessionId()).isEqualTo(sessionId);
+ assertThat(info).isStagedSessionReady();
+ }
+
+ @Test
+ public void testUpdateWithDifferentKey_VerifyPostReboot() throws Exception {
+ assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(2);
+ }
+
+ // Once updated with a new rotated key (bob), further updates with old key (alice) should fail
+ @Test
+ public void testAfterRotationOldKeyIsRejected() throws Exception {
+ assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(2);
+ int sessionId = stageSingleApk(TestApp.Apex3).assertSuccessful().getSessionId();
+ PackageInstaller.SessionInfo info =
+ SessionUpdateBroadcastReceiver.sessionBroadcasts.poll(60, TimeUnit.SECONDS);
+ assertThat(info.getSessionId()).isEqualTo(sessionId);
+ assertThat(info).isStagedSessionFailed();
+ }
+
+ // Once updated with a new rotated key (bob), further updates with new key (bob) should pass
+ @Test
+ public void testAfterRotationNewKeyCanUpdateFurther_CommitPostReboot() throws Exception {
+ assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(2);
+ int sessionId = stageSingleApk(Apex3SignedBobRot).assertSuccessful().getSessionId();
+ PackageInstaller.SessionInfo info =
+ SessionUpdateBroadcastReceiver.sessionBroadcasts.poll(60, TimeUnit.SECONDS);
+ assertThat(info.getSessionId()).isEqualTo(sessionId);
+ assertThat(info).isStagedSessionReady();
+ }
+
+ @Test
+ public void testAfterRotationNewKeyCanUpdateFurther_VerifyPostReboot() throws Exception {
+ assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(3);
+ }
+
+ // Once updated with a new rotated key (bob), further updates can be done with key only
+ @Test
+ public void testAfterRotationNewKeyCanUpdateFurtherWithoutLineage()
+ throws Exception {
+ assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(2);
+ int sessionId = stageSingleApk(Apex3SignedBob).assertSuccessful().getSessionId();
+ PackageInstaller.SessionInfo info =
+ SessionUpdateBroadcastReceiver.sessionBroadcasts.poll(60, TimeUnit.SECONDS);
+ assertThat(info.getSessionId()).isEqualTo(sessionId);
+ assertThat(info).isStagedSessionReady();
+ }
+
+ // Key downgrade should fail if new key is not ancestor of current key
+ @Test
+ public void testKeyDowngradeFailIfMismatch()
+ throws Exception {
+ assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(2);
+ int sessionId = stageDowngradeSingleApk(Apex2SignedEve).assertSuccessful().getSessionId();
+ PackageInstaller.SessionInfo info =
+ SessionUpdateBroadcastReceiver.sessionBroadcasts.poll(60, TimeUnit.SECONDS);
+ assertThat(info.getSessionId()).isEqualTo(sessionId);
+ assertThat(info).isStagedSessionFailed();
+ assertThat(info.getStagedSessionErrorMessage()).contains("is not compatible with the one "
+ + "currently installed on device");
+ }
+
+ @Test
+ public void testSamegradeSystemApex_Commit() throws Exception {
+ final PackageInfo shim = InstrumentationRegistry.getInstrumentation().getContext()
+ .getPackageManager().getPackageInfo(SHIM_PACKAGE_NAME, PackageManager.MATCH_APEX);
+ assertThat(shim.getLongVersionCode()).isEqualTo(1);
+ assertThat(shim.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM).isEqualTo(
+ ApplicationInfo.FLAG_SYSTEM);
+ assertThat(shim.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED).isEqualTo(
+ ApplicationInfo.FLAG_INSTALLED);
+ int sessionId = stageDowngradeSingleApk(TestApp.Apex1).assertSuccessful().getSessionId();
+ waitForIsReadyBroadcast(sessionId);
+ assertSessionReady(sessionId);
+ storeSessionId(sessionId);
+ }
+
+ @Test
+ public void testSamegradeSystemApex_VerifyPostReboot() throws Exception {
+ int sessionId = retrieveLastSessionId();
+ assertSessionApplied(sessionId);
+ final PackageInfo shim = InstrumentationRegistry.getInstrumentation().getContext()
+ .getPackageManager().getPackageInfo(SHIM_PACKAGE_NAME, PackageManager.MATCH_APEX);
+ assertThat(shim.getLongVersionCode()).isEqualTo(1);
+ // Check that APEX on /data wins.
+ assertThat(shim.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM).isEqualTo(0);
+ assertThat(shim.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED).isEqualTo(
+ ApplicationInfo.FLAG_INSTALLED);
}
private static long getInstalledVersion(String packageName) {
@@ -646,73 +838,52 @@
// It becomes harder to maintain this variety of install-related helper methods.
// TODO(ioffe): refactor install-related helper methods into a separate utility.
private static int createStagedSession() throws Exception {
- return createStagedSession(getPackageInstaller(), false, false, false);
+ return Install.single(TestApp.A1).setStaged().createSession();
}
- private static int createStagedSession(
- PackageInstaller packageInstaller,
- boolean multiPackage, boolean isDowngrade, boolean isApexSession) throws Exception {
- PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams(
- PackageInstaller.SessionParams.MODE_FULL_INSTALL);
- if (multiPackage) {
- sessionParams.setMultiPackage();
- }
- sessionParams.setStaged();
- sessionParams.setRequestDowngrade(isDowngrade);
- if (isApexSession) {
- sessionParams.setInstallAsApex();
- }
-
- return packageInstaller.createSession(sessionParams);
+ private static void commitSession(int sessionId) throws IOException {
+ InstallUtils.openPackageInstallerSession(sessionId)
+ .commit(LocalIntentSender.getIntentSender());
}
- private static StageSessionResult stageDowngradeSingleApk(String apkFileName) throws Exception {
- Log.i(TAG, "Staging a downgrade of " + apkFileName);
- PackageInstaller packageInstaller = getPackageInstaller();
-
- Pair<Integer, PackageInstaller.Session> sessionPair =
- prepareSingleApkStagedSession(packageInstaller, apkFileName, true);
+ private static StageSessionResult stageDowngradeSingleApk(TestApp testApp) throws Exception {
+ Log.i(TAG, "Staging a downgrade of " + testApp);
+ int sessionId = Install.single(testApp).setStaged().setRequestDowngrade().createSession();
// Commit the session (this will start the installation workflow).
- Log.i(TAG, "Committing downgrade session for apk: " + apkFileName);
- sessionPair.second.commit(LocalIntentSender.getIntentSender());
- return new StageSessionResult(sessionPair.first, LocalIntentSender.getIntentSenderResult());
+ Log.i(TAG, "Committing downgrade session for apk: " + testApp);
+ commitSession(sessionId);
+ return new StageSessionResult(sessionId, LocalIntentSender.getIntentSenderResult());
}
- private static StageSessionResult stageSingleApk(String apkFileName) throws Exception {
+ private static StageSessionResult stageSingleApk(String apkFileName, String outputFileName)
+ throws Exception {
Log.i(TAG, "Staging an install of " + apkFileName);
- PackageInstaller packageInstaller = getPackageInstaller();
-
- Pair<Integer, PackageInstaller.Session> sessionPair =
- prepareSingleApkStagedSession(packageInstaller, apkFileName, false);
+ // this is a trick to open an empty install session so we can manually write the package
+ // using writeApk
+ TestApp empty = new TestApp(null, null, -1,
+ apkFileName.endsWith(".apex"));
+ int sessionId = Install.single(empty).setStaged().createSession();
+ PackageInstaller.Session session = InstallUtils.openPackageInstallerSession(sessionId);
+ writeApk(session, apkFileName, outputFileName);
// Commit the session (this will start the installation workflow).
Log.i(TAG, "Committing session for apk: " + apkFileName);
- sessionPair.second.commit(LocalIntentSender.getIntentSender());
- return new StageSessionResult(sessionPair.first, LocalIntentSender.getIntentSenderResult());
+ commitSession(sessionId);
+ return new StageSessionResult(sessionId, LocalIntentSender.getIntentSenderResult());
}
- private static Pair<Integer, PackageInstaller.Session>
- prepareSingleApkStagedSession(PackageInstaller packageInstaller, String apkFileName,
- boolean isDowngrade)
- throws Exception {
- int sessionId = createStagedSession(packageInstaller, false, isDowngrade,
- apkFileName.endsWith(".apex"));
- PackageInstaller.Session session = packageInstaller.openSession(sessionId);
- writeApk(session, apkFileName);
- return new Pair<>(sessionId, session);
+ private static StageSessionResult stageSingleApk(TestApp testApp) throws Exception {
+ Log.i(TAG, "Staging an install of " + testApp);
+ int sessionId = Install.single(testApp).setStaged().createSession();
+ // Commit the session (this will start the installation workflow).
+ Log.i(TAG, "Committing session for apk: " + testApp);
+ commitSession(sessionId);
+ return new StageSessionResult(sessionId, LocalIntentSender.getIntentSenderResult());
}
- private static StageSessionResult stageMultipleApks(String... apkFileNames) throws Exception {
- Log.i(TAG, "Staging an install of " + Arrays.toString(apkFileNames));
- PackageInstaller packageInstaller = getPackageInstaller();
- int multiPackageSessionId = createStagedSession(packageInstaller, true, false, false);
- PackageInstaller.Session multiPackageSession = packageInstaller.openSession(
- multiPackageSessionId);
- for (String apkFileName : apkFileNames) {
- Pair<Integer, PackageInstaller.Session> sessionPair =
- prepareSingleApkStagedSession(packageInstaller, apkFileName, false);
- multiPackageSession.addChildSessionId(sessionPair.first);
- }
- multiPackageSession.commit(LocalIntentSender.getIntentSender());
+ private static StageSessionResult stageMultipleApks(TestApp... testApps) throws Exception {
+ Log.i(TAG, "Staging an install of " + Arrays.toString(testApps));
+ int multiPackageSessionId = Install.multi(testApps).setStaged().createSession();
+ commitSession(multiPackageSessionId);
return new StageSessionResult(
multiPackageSessionId, LocalIntentSender.getIntentSenderResult());
}
@@ -764,20 +935,10 @@
}
}
- private static void installNonStaged(String apkFileName) throws Exception {
- PackageInstaller packageInstaller = getPackageInstaller();
- PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams(
- PackageInstaller.SessionParams.MODE_FULL_INSTALL);
- int sessionId = packageInstaller.createSession(sessionParams);
- PackageInstaller.Session session = packageInstaller.openSession(sessionId);
- writeApk(session, apkFileName);
- session.commit(LocalIntentSender.getIntentSender());
- assertStatusSuccess(LocalIntentSender.getIntentSenderResult());
- }
-
- private static void writeApk(PackageInstaller.Session session, String apkFileName)
+ private static void writeApk(PackageInstaller.Session session, String apkFileName,
+ String outputFileName)
throws Exception {
- try (OutputStream packageInSession = session.openWrite(apkFileName, 0, -1);
+ try (OutputStream packageInSession = session.openWrite(outputFileName, 0, -1);
InputStream is =
StagedInstallTest.class.getClassLoader().getResourceAsStream(apkFileName)) {
byte[] buffer = new byte[4096];
@@ -845,19 +1006,6 @@
return getPackageInstaller().getSessionInfo(sessionId);
}
- private static void uninstall(String packageName) throws Exception {
- // No need to uninstall if the package isn't installed.
- if (getInstalledVersion(packageName) == -1) {
- return;
- }
-
- Context context = InstrumentationRegistry.getInstrumentation().getContext();
- PackageManager packageManager = context.getPackageManager();
- PackageInstaller packageInstaller = packageManager.getPackageInstaller();
- packageInstaller.uninstall(packageName, LocalIntentSender.getIntentSender());
- assertStatusSuccess(LocalIntentSender.getIntentSenderResult());
- }
-
private static void assertStatusSuccess(Intent result) {
int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
PackageInstaller.STATUS_FAILURE);
diff --git a/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/ApexShimValidationTest.java b/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/ApexShimValidationTest.java
index 9182ee9..094fd8d 100644
--- a/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/ApexShimValidationTest.java
+++ b/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/ApexShimValidationTest.java
@@ -21,6 +21,8 @@
import static org.junit.Assume.assumeThat;
+import android.platform.test.annotations.LargeTest;
+
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
@@ -87,6 +89,7 @@
}
@Test
+ @LargeTest
public void testRejectsApexWithAdditionalFile() throws Exception {
runPhase("testRejectsApexWithAdditionalFile_Commit");
getDevice().reboot();
@@ -94,6 +97,7 @@
}
@Test
+ @LargeTest
public void testRejectsApexWithAdditionalFolder() throws Exception {
runPhase("testRejectsApexWithAdditionalFolder_Commit");
getDevice().reboot();
@@ -101,6 +105,7 @@
}
@Test
+ @LargeTest
public void testRejectsApexWithPostInstallHook() throws Exception {
runPhase("testRejectsApexWithPostInstallHook_Commit");
getDevice().reboot();
@@ -108,6 +113,7 @@
}
@Test
+ @LargeTest
public void testRejectsApexWithPreInstallHook() throws Exception {
runPhase("testRejectsApexWithPreInstallHook_Commit");
getDevice().reboot();
@@ -115,6 +121,7 @@
}
@Test
+ @LargeTest
public void testRejectsApexWrongSHA() throws Exception {
runPhase("testRejectsApexWrongSHA_Commit");
getDevice().reboot();
diff --git a/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java b/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java
index 41ad7ee..2f421c3 100644
--- a/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java
+++ b/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java
@@ -24,6 +24,8 @@
import static org.junit.Assume.assumeThat;
import static org.junit.Assume.assumeTrue;
+import android.platform.test.annotations.LargeTest;
+
import com.android.ddmlib.Log;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
@@ -73,9 +75,10 @@
}
/**
- * Tests staged install involving only one apk.
+ * Tests for staged install involving only one apk.
*/
@Test
+ @LargeTest
public void testInstallStagedApk() throws Exception {
runPhase("testInstallStagedApk_Commit");
getDevice().reboot();
@@ -109,6 +112,7 @@
}
@Test
+ @LargeTest
public void testAbandonStagedApkBeforeReboot() throws Exception {
runPhase("testAbandonStagedApkBeforeReboot_CommitAndAbandon");
getDevice().reboot();
@@ -116,6 +120,7 @@
}
@Test
+ @LargeTest
public void testInstallMultipleStagedApks() throws Exception {
runPhase("testInstallMultipleStagedApks_Commit");
getDevice().reboot();
@@ -143,6 +148,7 @@
}
@Test
+ @LargeTest
public void testStagedInstallDowngrade_DowngradeRequested_DebugBuild() throws Exception {
assumeThat(getDevice().getBuildFlavor(), not(endsWith("-user")));
@@ -166,6 +172,7 @@
}
@Test
+ @LargeTest
public void testInstallStagedApex() throws Exception {
assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
@@ -198,6 +205,7 @@
}
@Test
+ @LargeTest
public void testStageApkWithSameNameAsApexShouldFail() throws Exception {
assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
@@ -213,6 +221,7 @@
}
@Test
+ @LargeTest
public void testStagedInstallDowngradeApex_DowngradeNotRequested_Fails() throws Exception {
assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
@@ -223,6 +232,7 @@
}
@Test
+ @LargeTest
public void testStagedInstallDowngradeApex_DowngradeRequested_DebugBuild() throws Exception {
assumeThat(getDevice().getBuildFlavor(), not(endsWith("-user")));
assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
@@ -234,6 +244,7 @@
}
@Test
+ @LargeTest
public void testStagedInstallDowngradeApex_DowngradeRequested_UserBuild_Fails()
throws Exception {
assumeThat(getDevice().getBuildFlavor(), endsWith("-user"));
@@ -247,6 +258,19 @@
}
@Test
+ @LargeTest
+ public void testStagedInstallDowngradeApexToSystemVersion_DebugBuild() throws Exception {
+ assumeThat(getDevice().getBuildFlavor(), not(endsWith("-user")));
+ assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
+
+ installV2Apex();
+ runPhase("testStagedInstallDowngradeApexToSystemVersion_DebugBuild_Commit");
+ getDevice().reboot();
+ runPhase("testStagedInstallDowngradeApexToSystemVersion_DebugBuild_VerifyPostReboot");
+ }
+
+ @Test
+ @LargeTest
public void testInstallStagedApex_SameGrade() throws Exception {
assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
@@ -261,12 +285,30 @@
runPhase("testInstallApex_DeviceDoesNotSupportApex_Fails");
}
+ private void installV2Apex()throws Exception {
+ runPhase("testInstallV2Apex_Commit");
+ getDevice().reboot();
+ runPhase("testInstallV2Apex_VerifyPostReboot");
+ }
+
+ private void installV2SignedBobApex() throws Exception {
+ runPhase("testInstallV2SignedBobApex_Commit");
+ getDevice().reboot();
+ runPhase("testInstallV2SignedBobApex_VerifyPostReboot");
+ }
+
private void installV3Apex()throws Exception {
runPhase("testInstallV3Apex_Commit");
getDevice().reboot();
runPhase("testInstallV3Apex_VerifyPostReboot");
}
+ private void installV3SignedBobApex() throws Exception {
+ runPhase("testInstallV3SignedBobApex_Commit");
+ getDevice().reboot();
+ runPhase("testInstallV3SignedBobApex_VerifyPostReboot");
+ }
+
@Test
public void testFailsInvalidApexInstall() throws Exception {
assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
@@ -274,16 +316,103 @@
runPhase("testFailsInvalidApexInstall_AbandonSessionIsNoop");
}
- private boolean isUpdatingApexSupported() throws Exception {
- final String updatable = getDevice().getProperty("ro.apex.updatable");
- return updatable != null && updatable.equals("true");
- }
-
@Test
public void testStagedApkSessionCallbacks() throws Exception {
runPhase("testStagedApkSessionCallbacks");
}
+ @Test
+ @LargeTest
+ public void testInstallStagedApexWithoutApexSuffix() throws Exception {
+ assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
+
+ runPhase("testInstallStagedApexWithoutApexSuffix_Commit");
+ getDevice().reboot();
+ runPhase("testInstallStagedApexWithoutApexSuffix_VerifyPostReboot");
+ }
+
+ @Test
+ public void testRejectsApexDifferentCertificate() throws Exception {
+ runPhase("testRejectsApexDifferentCertificate");
+ }
+
+ /**
+ * Tests for staged install involving rotated keys.
+ *
+ * Here alice means the original default key that cts.shim.v1 package was signed with and
+ * bob is the new key alice rotates to. Where ambiguous, we will refer keys as alice and bob
+ * instead of "old key" and "new key".
+ */
+ @Test
+ public void testUpdateWithDifferentKeyButNoRotation() throws Exception {
+ assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
+
+ runPhase("testUpdateWithDifferentKeyButNoRotation");
+ }
+
+ @Test
+ @LargeTest
+ public void testUpdateWithDifferentKey() throws Exception {
+ assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
+
+ runPhase("testUpdateWithDifferentKey_Commit");
+ getDevice().reboot();
+ runPhase("testUpdateWithDifferentKey_VerifyPostReboot");
+ }
+
+ @Test
+ @LargeTest
+ public void testAfterRotationOldKeyIsRejected() throws Exception {
+ assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
+
+ installV2SignedBobApex();
+ runPhase("testAfterRotationOldKeyIsRejected");
+ }
+
+ @Test
+ @LargeTest
+ public void testAfterRotationNewKeyCanUpdateFurther() throws Exception {
+ assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
+
+ installV2SignedBobApex();
+ runPhase("testAfterRotationNewKeyCanUpdateFurther_CommitPostReboot");
+ getDevice().reboot();
+ runPhase("testAfterRotationNewKeyCanUpdateFurther_VerifyPostReboot");
+ }
+
+ @Test
+ @LargeTest
+ public void testAfterRotationNewKeyCanUpdateFurtherWithoutLineage() throws Exception {
+ assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
+
+ installV2SignedBobApex();
+ runPhase("testAfterRotationNewKeyCanUpdateFurtherWithoutLineage");
+ }
+
+ @Test
+ @LargeTest
+ public void testKeyDowngradeFailIfMismatch() throws Exception {
+ assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
+
+ installV3SignedBobApex();
+ runPhase("testKeyDowngradeFailIfMismatch");
+ }
+
+ @Test
+ @LargeTest
+ public void testSamegradeSystemApex() throws Exception {
+ assumeTrue("Device does not support updating APEX", isUpdatingApexSupported());
+
+ runPhase("testSamegradeSystemApex_Commit");
+ getDevice().reboot();
+ runPhase("testSamegradeSystemApex_VerifyPostReboot");
+ }
+
+ private boolean isUpdatingApexSupported() throws Exception {
+ final String updatable = getDevice().getProperty("ro.apex.updatable");
+ return updatable != null && updatable.equals("true");
+ }
+
/**
* Uninstalls a shim apex only if it's latest version is installed on /data partition (i.e.
* it has a version higher than {@code 1}).
@@ -297,18 +426,18 @@
// Device doesn't support updating apex. Nothing to uninstall.
return;
}
- final ITestDevice.ApexInfo shimApex = getShimApex();
- if (shimApex.versionCode == 1) {
- // System version is active, skipping uninstalling active apex and rebooting the device.
- return;
- }
// Non system version is active, need to uninstall it and reboot the device.
final String errorMessage = getDevice().uninstallPackage(SHIM_APEX_PACKAGE_NAME);
- Log.i(TAG, "Uninstalling shim apex " + shimApex);
- if (errorMessage != null) {
- throw new AssertionError("Failed to uninstall " + shimApex);
+ if (errorMessage == null) {
+ Log.i(TAG, "Uninstalling shim apex");
+ getDevice().reboot();
+ } else {
+ // Most likely we tried to uninstall system version and failed. It should be fine to
+ // continue tests.
+ // TODO(ioffe): extend getDevice().getActiveApexes() to include isInstalled/isFactory
+ // and remove this terrible hack.
+ Log.w(TAG, "Failed to uninstall shim APEX : " + errorMessage);
}
- getDevice().reboot();
assertThat(getShimApex().versionCode).isEqualTo(1L);
}
diff --git a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v1.apex b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v1.apex
new file mode 100644
index 0000000..efa3d87
--- /dev/null
+++ b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v1.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2.apex b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2.apex
index 9dc33de..6f3bc3c 100644
--- a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_additional_file.apex b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_additional_file.apex
index 0ba9d99..f6bb67a 100644
--- a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_additional_file.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_additional_file.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_additional_folder.apex b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_additional_folder.apex
index 1d3fcf5..a99d938 100644
--- a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_additional_folder.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_additional_folder.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_different_certificate.apex b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_different_certificate.apex
new file mode 100644
index 0000000..117cbda
--- /dev/null
+++ b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_different_certificate.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_signed_bob.apex b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_signed_bob.apex
new file mode 100644
index 0000000..72d22e8
--- /dev/null
+++ b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_signed_bob.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_signed_bob_rot.apex b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_signed_bob_rot.apex
new file mode 100644
index 0000000..bdca0e5
--- /dev/null
+++ b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_signed_bob_rot.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_signed_eve.apex b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_signed_eve.apex
new file mode 100644
index 0000000..2cf3d09
--- /dev/null
+++ b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_signed_eve.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_with_post_install_hook.apex b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_with_post_install_hook.apex
index c32b139..3c5aad1 100644
--- a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_with_post_install_hook.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_with_post_install_hook.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_with_pre_install_hook.apex b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_with_pre_install_hook.apex
index 6a0fb0d..a5512ef 100644
--- a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_with_pre_install_hook.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_with_pre_install_hook.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_wrong_sha.apex b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_wrong_sha.apex
index 2388490..21b282d 100644
--- a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_wrong_sha.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_wrong_sha.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v3.apex b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v3.apex
index 57ab7b9..d3575f4 100644
--- a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v3.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v3.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v3_signed_bob.apex b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v3_signed_bob.apex
new file mode 100644
index 0000000..2a68a26
--- /dev/null
+++ b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v3_signed_bob.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v3_signed_bob_rot.apex b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v3_signed_bob_rot.apex
new file mode 100644
index 0000000..abd87a7
--- /dev/null
+++ b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v3_signed_bob_rot.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim_not_pre_installed.apex b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim_not_pre_installed.apex
index 45715a2..b11deee 100644
--- a/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim_not_pre_installed.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim_not_pre_installed.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apk/Av1.xml b/hostsidetests/stagedinstall/testdata/apk/Av1.xml
deleted file mode 100644
index 234448c..0000000
--- a/hostsidetests/stagedinstall/testdata/apk/Av1.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<!--
- ~ Copyright (C) 2019 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.tests.stagedinstall.testapp.A"
- android:versionCode="1"
- android:versionName="1.0" >
-
-
- <uses-sdk android:minSdkVersion="19" />
-
- <application android:label="StagedInstall Test App A v1">
- <activity android:name="com.android.tests.stagedinstall.testapp.MainActivity">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
-</manifest>
diff --git a/hostsidetests/stagedinstall/testdata/apk/Av2.xml b/hostsidetests/stagedinstall/testdata/apk/Av2.xml
deleted file mode 100644
index dd5e512..0000000
--- a/hostsidetests/stagedinstall/testdata/apk/Av2.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<!--
- ~ Copyright (C) 2019 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.tests.stagedinstall.testapp.A"
- android:versionCode="2"
- android:versionName="2.0" >
-
-
- <uses-sdk android:minSdkVersion="19" />
-
- <application android:label="StagedInstall Test App A v2">
- <activity android:name="com.android.tests.stagedinstall.testapp.MainActivity">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
-</manifest>
diff --git a/hostsidetests/stagedinstall/testdata/apk/Bv1.xml b/hostsidetests/stagedinstall/testdata/apk/Bv1.xml
deleted file mode 100644
index 2a108b9..0000000
--- a/hostsidetests/stagedinstall/testdata/apk/Bv1.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<!--
- ~ Copyright (C) 2019 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.tests.stagedinstall.testapp.B"
- android:versionCode="1"
- android:versionName="1.0" >
-
-
- <uses-sdk android:minSdkVersion="19" />
-
- <application android:label="StagedInstall Test App B v1">
- <activity android:name="com.android.tests.stagedinstall.testapp.MainActivity">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
-</manifest>
diff --git a/hostsidetests/statsd/Android.bp b/hostsidetests/statsd/Android.bp
index 68a2d15..5f2d712 100644
--- a/hostsidetests/statsd/Android.bp
+++ b/hostsidetests/statsd/Android.bp
@@ -32,5 +32,8 @@
"platformprotos",
"truth-host-prebuilt",
],
- data: ["**/*.pbtxt"],
+ data: [
+ "**/*.pbtxt",
+ ":CtsStatsdApp",
+ ],
}
diff --git a/hostsidetests/statsd/apps/statsdapp/Android.bp b/hostsidetests/statsd/apps/statsdapp/Android.bp
index 01b07ec..b24d1c3 100644
--- a/hostsidetests/statsd/apps/statsdapp/Android.bp
+++ b/hostsidetests/statsd/apps/statsdapp/Android.bp
@@ -46,12 +46,6 @@
"androidx.test.rules",
],
jni_libs: ["liblmkhelper"],
- // tag this module as a cts test artifact
- test_suites: [
- "cts",
- "vts",
- "general-tests",
- ],
compile_multilib: "both",
}
diff --git a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java
index c679341..fc1b57e 100644
--- a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java
+++ b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java
@@ -18,7 +18,7 @@
import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertWithMessage;
import android.accounts.Account;
import android.accounts.AccountManager;
@@ -325,7 +325,7 @@
Context context = InstrumentationRegistry.getContext();
JobScheduler js = context.getSystemService(JobScheduler.class);
- assertTrue("JobScheduler service not available", js != null);
+ assertWithMessage("JobScheduler service not available").that(js).isNotNull();
JobInfo.Builder builder = new JobInfo.Builder(1, name);
builder.setOverrideDeadline(0);
diff --git a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/Checkers.java b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/Checkers.java
index 1db1c0a..3728cef 100644
--- a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/Checkers.java
+++ b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/Checkers.java
@@ -16,7 +16,7 @@
package com.android.server.cts.device.statsd;
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
import android.net.wifi.WifiManager;
import android.os.Vibrator;
@@ -34,12 +34,12 @@
@Test
public void checkVibratorSupported() {
Vibrator v = InstrumentationRegistry.getContext().getSystemService(Vibrator.class);
- assertTrue(v.hasVibrator());
+ assertThat(v.hasVibrator()).isTrue();
}
@Test
public void checkWifiEnhancedPowerReportingSupported() {
WifiManager wm = InstrumentationRegistry.getContext().getSystemService(WifiManager.class);
- assertTrue(wm.isEnhancedPowerReportingSupported());
+ assertThat(wm.isEnhancedPowerReportingSupported()).isTrue();
}
}
diff --git a/hostsidetests/statsd/src/android/cts/statsd/alarm/AlarmTests.java b/hostsidetests/statsd/src/android/cts/statsd/alarm/AlarmTests.java
index 4983d06..00930cd 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/alarm/AlarmTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/alarm/AlarmTests.java
@@ -15,6 +15,8 @@
*/
package android.cts.statsd.alarm;
+import static com.google.common.truth.Truth.assertThat;
+
import android.cts.statsd.atom.AtomTestCase;
import com.android.internal.os.StatsdConfigProto;
@@ -59,7 +61,7 @@
String markTime = getCurrentLogcatDate();
Thread.sleep(9_000);
- if (INCIDENTD_TESTS_ENABLED) assertTrue("No incident", didIncidentdFireSince(markTime));
+ if (INCIDENTD_TESTS_ENABLED) assertThat(didIncidentdFireSince(markTime)).isTrue();
}
diff --git a/hostsidetests/statsd/src/android/cts/statsd/alert/AnomalyDetectionTests.java b/hostsidetests/statsd/src/android/cts/statsd/alert/AnomalyDetectionTests.java
index 3109138..5e9eac6 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/alert/AnomalyDetectionTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/alert/AnomalyDetectionTests.java
@@ -15,6 +15,9 @@
*/
package android.cts.statsd.alert;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
import android.cts.statsd.atom.AtomTestCase;
import com.android.internal.os.StatsdConfigProto;
@@ -94,29 +97,28 @@
// count(label=6) -> 1 (not an anomaly, since not "greater than 2")
doAppBreadcrumbReportedStart(6);
Thread.sleep(500);
- assertEquals("Premature anomaly", 0, getEventMetricDataList().size());
- if (INCIDENTD_TESTS_ENABLED) assertFalse("Incident", didIncidentdFireSince(markTime));
+ assertWithMessage("Premature anomaly").that(getEventMetricDataList()).isEmpty();
+ if (INCIDENTD_TESTS_ENABLED) assertThat(didIncidentdFireSince(markTime)).isFalse();
// count(label=6) -> 2 (not an anomaly, since not "greater than 2")
doAppBreadcrumbReportedStart(6);
Thread.sleep(500);
- assertEquals("Premature anomaly", 0, getEventMetricDataList().size());
- if (INCIDENTD_TESTS_ENABLED) assertFalse("Incident", didIncidentdFireSince(markTime));
+ assertWithMessage("Premature anomaly").that(getEventMetricDataList()).isEmpty();
+ if (INCIDENTD_TESTS_ENABLED) assertThat(didIncidentdFireSince(markTime)).isFalse();
// count(label=12) -> 1 (not an anomaly, since not "greater than 2")
doAppBreadcrumbReportedStart(12);
Thread.sleep(1000);
- assertEquals("Premature anomaly", 0, getEventMetricDataList().size());
- if (INCIDENTD_TESTS_ENABLED) assertFalse("Incident", didIncidentdFireSince(markTime));
+ assertWithMessage("Premature anomaly").that(getEventMetricDataList()).isEmpty();
+ if (INCIDENTD_TESTS_ENABLED) assertThat(didIncidentdFireSince(markTime)).isFalse();
doAppBreadcrumbReportedStart(6); // count(label=6) -> 3 (anomaly, since "greater than 2"!)
Thread.sleep(WAIT_AFTER_BREADCRUMB_MS);
List<EventMetricData> data = getEventMetricDataList();
- assertEquals("Expected 1 anomaly", 1, data.size());
- AnomalyDetected a = data.get(0).getAtom().getAnomalyDetected();
- assertEquals("Wrong alert_id", ALERT_ID, a.getAlertId());
- if (INCIDENTD_TESTS_ENABLED) assertTrue("No incident", didIncidentdFireSince(markTime));
+ assertWithMessage("Expected anomaly").that(data).hasSize(1);
+ assertThat(data.get(0).getAtom().getAnomalyDetected().getAlertId()).isEqualTo(ALERT_ID);
+ if (INCIDENTD_TESTS_ENABLED) assertThat(didIncidentdFireSince(markTime)).isTrue();
}
// Tests that anomaly detection for duration works.
@@ -149,18 +151,18 @@
String markTime = getCurrentLogcatDate();
doAppBreadcrumbReportedStart(1);
Thread.sleep(6_000); // Recorded duration at end: 6s
- assertEquals("Premature anomaly,", 0, getEventMetricDataList().size());
+ assertWithMessage("Premature anomaly").that(getEventMetricDataList()).isEmpty();
doAppBreadcrumbReportedStop(1);
Thread.sleep(4_000); // Recorded duration at end: 6s
- assertEquals("Premature anomaly,", 0, getEventMetricDataList().size());
+ assertWithMessage("Premature anomaly").that(getEventMetricDataList()).isEmpty();
// Test that alarm does fire when it is supposed to (after 4s, plus up to 5s alarm delay).
doAppBreadcrumbReportedStart(1);
Thread.sleep(9_000); // Recorded duration at end: 13s
List<EventMetricData> data = getEventMetricDataList();
- assertEquals("Expected an anomaly,", 1, data.size());
- assertEquals(ALERT_ID, data.get(0).getAtom().getAnomalyDetected().getAlertId());
+ assertWithMessage("Expected anomaly").that(data).hasSize(1);
+ assertThat(data.get(0).getAtom().getAnomalyDetected().getAlertId()).isEqualTo(ALERT_ID);
// Now test that the refractory period is obeyed.
markTime = getCurrentLogcatDate();
@@ -168,7 +170,7 @@
doAppBreadcrumbReportedStart(1);
Thread.sleep(3_000); // Recorded duration at end: 13s
// NB: the previous getEventMetricDataList also removes the report, so size is back to 0.
- assertEquals("Expected only 1 anomaly,", 0, getEventMetricDataList().size());
+ assertWithMessage("Premature anomaly").that(getEventMetricDataList()).isEmpty();
// Test that detection works again after refractory period finishes.
doAppBreadcrumbReportedStop(1);
@@ -177,9 +179,9 @@
Thread.sleep(15_000); // Recorded duration at end: 15s
// We can do an incidentd test now that all the timing issues are done.
data = getEventMetricDataList();
- assertEquals("Expected another anomaly,", 1, data.size());
- assertEquals(ALERT_ID, data.get(0).getAtom().getAnomalyDetected().getAlertId());
- if (INCIDENTD_TESTS_ENABLED) assertTrue("No incident", didIncidentdFireSince(markTime));
+ assertWithMessage("Expected anomaly").that(data).hasSize(1);
+ assertThat(data.get(0).getAtom().getAnomalyDetected().getAlertId()).isEqualTo(ALERT_ID);
+ if (INCIDENTD_TESTS_ENABLED) assertThat(didIncidentdFireSince(markTime)).isTrue();
doAppBreadcrumbReportedStop(1);
}
@@ -212,7 +214,7 @@
Thread.sleep(5_000);
doAppBreadcrumbReportedStop(1);
Thread.sleep(2_000);
- assertEquals("Premature anomaly,", 0, getEventMetricDataList().size());
+ assertWithMessage("Premature anomaly").that(getEventMetricDataList()).isEmpty();
// Test that alarm does fire when it is supposed to.
// The anomaly occurs in 1s, but alarms won't fire that quickly.
@@ -228,9 +230,8 @@
// Although we expect that the alarm won't fire, we certainly cannot demand that.
CLog.w(TAG, "The anomaly was detected twice. Presumably the alarm did manage to fire.");
}
- assertTrue("Expected 1 (or possibly 2) anomalies, instead of " + data.size(),
- 1 == data.size() || 2 == data.size());
- assertEquals(ALERT_ID, data.get(0).getAtom().getAnomalyDetected().getAlertId());
+ assertThat(data.size()).isAnyOf(1, 2);
+ assertThat(data.get(0).getAtom().getAnomalyDetected().getAlertId()).isEqualTo(ALERT_ID);
}
// Tests that anomaly detection for value works.
@@ -256,17 +257,16 @@
String markTime = getCurrentLogcatDate();
doAppBreadcrumbReportedStart(6); // value = 6, which is NOT > trigger
Thread.sleep(WAIT_AFTER_BREADCRUMB_MS);
- assertEquals("Premature anomaly", 0, getEventMetricDataList().size());
- if (INCIDENTD_TESTS_ENABLED) assertFalse("Incident", didIncidentdFireSince(markTime));
+ assertWithMessage("Premature anomaly").that(getEventMetricDataList()).isEmpty();
+ if (INCIDENTD_TESTS_ENABLED) assertThat(didIncidentdFireSince(markTime)).isFalse();
doAppBreadcrumbReportedStart(14); // value = 14 > trigger
Thread.sleep(WAIT_AFTER_BREADCRUMB_MS);
List<EventMetricData> data = getEventMetricDataList();
- assertEquals("Expected 1 anomaly", 1, data.size());
- AnomalyDetected a = data.get(0).getAtom().getAnomalyDetected();
- assertEquals("Wrong alert_id", ALERT_ID, a.getAlertId());
- if (INCIDENTD_TESTS_ENABLED) assertTrue("No incident", didIncidentdFireSince(markTime));
+ assertWithMessage("Expected anomaly").that(data).hasSize(1);
+ assertThat(data.get(0).getAtom().getAnomalyDetected().getAlertId()).isEqualTo(ALERT_ID);
+ if (INCIDENTD_TESTS_ENABLED) assertThat(didIncidentdFireSince(markTime)).isTrue();
}
// Test that anomaly detection integrates with perfetto properly.
@@ -306,17 +306,28 @@
String markTime = getCurrentLogcatDate();
doAppBreadcrumbReportedStart(6); // value = 6, which is NOT > trigger
Thread.sleep(WAIT_AFTER_BREADCRUMB_MS);
- assertEquals("Premature anomaly", 0, getEventMetricDataList().size());
- if (PERFETTO_TESTS_ENABLED) assertFalse(isSystemTracingEnabled());
+ assertWithMessage("Premature anomaly").that(getEventMetricDataList()).isEmpty();
+ if (PERFETTO_TESTS_ENABLED) assertThat(isSystemTracingEnabled()).isFalse();
doAppBreadcrumbReportedStart(14); // value = 14 > trigger
Thread.sleep(WAIT_AFTER_BREADCRUMB_MS);
List<EventMetricData> data = getEventMetricDataList();
- assertEquals("Expected 1 anomaly", 1, data.size());
- AnomalyDetected a = data.get(0).getAtom().getAnomalyDetected();
- assertEquals("Wrong alert_id", ALERT_ID, a.getAlertId());
- if (PERFETTO_TESTS_ENABLED) assertTrue(isSystemTracingEnabled());
+ assertWithMessage("Expected anomaly").that(data).hasSize(1);
+ assertThat(data.get(0).getAtom().getAnomalyDetected().getAlertId()).isEqualTo(ALERT_ID);
+
+ // Pool a few times to allow for statsd <-> traced <-> traced_probes communication to happen.
+ if (PERFETTO_TESTS_ENABLED) {
+ boolean tracingEnabled = false;
+ for (int i = 0; i < 5; i++) {
+ if (isSystemTracingEnabled()) {
+ tracingEnabled = true;
+ break;
+ }
+ Thread.sleep(1000);
+ }
+ assertThat(tracingEnabled).isTrue();
+ }
}
// Tests that anomaly detection for gauge works.
@@ -343,18 +354,17 @@
String markTime = getCurrentLogcatDate();
doAppBreadcrumbReportedStart(6); // gauge = 6, which is NOT > trigger
Thread.sleep(Math.max(WAIT_AFTER_BREADCRUMB_MS, 1_100)); // Must be >1s to push next bucket.
- assertEquals("Premature anomaly", 0, getEventMetricDataList().size());
- if (INCIDENTD_TESTS_ENABLED) assertFalse("Incident", didIncidentdFireSince(markTime));
+ assertWithMessage("Premature anomaly").that(getEventMetricDataList()).isEmpty();
+ if (INCIDENTD_TESTS_ENABLED) assertThat(didIncidentdFireSince(markTime)).isFalse();
// We waited for >1s above, so we are now in the next bucket (which is essential).
doAppBreadcrumbReportedStart(14); // gauge = 14 > trigger
Thread.sleep(WAIT_AFTER_BREADCRUMB_MS);
List<EventMetricData> data = getEventMetricDataList();
- assertEquals("Expected 1 anomaly", 1, data.size());
- AnomalyDetected a = data.get(0).getAtom().getAnomalyDetected();
- assertEquals("Wrong alert_id", ALERT_ID, a.getAlertId());
- if (INCIDENTD_TESTS_ENABLED) assertTrue("No incident", didIncidentdFireSince(markTime));
+ assertWithMessage("Expected anomaly").that(data).hasSize(1);
+ assertThat(data.get(0).getAtom().getAnomalyDetected().getAlertId()).isEqualTo(ALERT_ID);
+ if (INCIDENTD_TESTS_ENABLED) assertThat(didIncidentdFireSince(markTime)).isTrue();
}
// Test that anomaly detection for pulled metrics work.
@@ -399,9 +409,8 @@
List<EventMetricData> data = getEventMetricDataList();
// There will likely be many anomalies (one for each dimension). There must be at least one.
- assertTrue("Expected >=1 anomaly", data.size() >= 1);
- AnomalyDetected a = data.get(0).getAtom().getAnomalyDetected();
- assertEquals("Wrong alert_id", ALERT_ID, a.getAlertId());
+ assertThat(data.size()).isAtLeast(1);
+ assertThat(data.get(0).getAtom().getAnomalyDetected().getAlertId()).isEqualTo(ALERT_ID);
}
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java b/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java
index 5b5711c..4ec6142 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java
@@ -18,6 +18,9 @@
import static android.cts.statsd.atom.DeviceAtomTestCase.DEVICE_SIDE_TEST_APK;
import static android.cts.statsd.atom.DeviceAtomTestCase.DEVICE_SIDE_TEST_PACKAGE;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
import android.os.BatteryStatsProto;
import android.os.StatsDataDumpProto;
import android.service.battery.BatteryServiceDumpProto;
@@ -54,6 +57,7 @@
import com.android.tradefed.util.CommandResult;
import com.android.tradefed.util.CommandStatus;
+import com.google.common.collect.Range;
import com.google.common.io.Files;
import com.google.protobuf.ByteString;
@@ -255,7 +259,7 @@
*/
protected List<EventMetricData> getEventMetricDataList(ConfigMetricsReportList reportList)
throws Exception {
- assertTrue("Expected one report", reportList.getReportsCount() == 1);
+ assertThat(reportList.getReportsCount()).isEqualTo(1);
ConfigMetricsReport report = reportList.getReports(0);
List<EventMetricData> data = new ArrayList<>();
@@ -273,15 +277,16 @@
protected List<Atom> getGaugeMetricDataList() throws Exception {
ConfigMetricsReportList reportList = getReportList();
- assertTrue("Expected one report.", reportList.getReportsCount() == 1);
+ assertThat(reportList.getReportsCount()).isEqualTo(1);
+
// only config
ConfigMetricsReport report = reportList.getReports(0);
- assertEquals("Expected one metric in the report.", 1, report.getMetricsCount());
+ assertThat(report.getMetricsCount()).isEqualTo(1);
List<Atom> data = new ArrayList<>();
for (GaugeMetricData gaugeMetricData :
report.getMetrics(0).getGaugeMetrics().getDataList()) {
- assertTrue("Expected one bucket.", gaugeMetricData.getBucketInfoCount() == 1);
+ assertThat(gaugeMetricData.getBucketInfoCount()).isEqualTo(1);
for (Atom atom : gaugeMetricData.getBucketInfo(0).getAtomList()) {
data.add(atom);
}
@@ -300,7 +305,7 @@
*/
protected List<DurationMetricData> getDurationMetricDataList() throws Exception {
ConfigMetricsReportList reportList = getReportList();
- assertTrue("Expected one report", reportList.getReportsCount() == 1);
+ assertThat(reportList.getReportsCount()).isEqualTo(1);
ConfigMetricsReport report = reportList.getReports(0);
List<DurationMetricData> data = new ArrayList<>();
@@ -321,7 +326,7 @@
*/
protected List<CountMetricData> getCountMetricDataList() throws Exception {
ConfigMetricsReportList reportList = getReportList();
- assertTrue("Expected one report", reportList.getReportsCount() == 1);
+ assertThat(reportList.getReportsCount()).isEqualTo(1);
ConfigMetricsReport report = reportList.getReports(0);
List<CountMetricData> data = new ArrayList<>();
@@ -342,7 +347,7 @@
*/
protected List<ValueMetricData> getValueMetricDataList() throws Exception {
ConfigMetricsReportList reportList = getReportList();
- assertTrue("Expected one report", reportList.getReportsCount() == 1);
+ assertThat(reportList.getReportsCount()).isEqualTo(1);
ConfigMetricsReport report = reportList.getReports(0);
List<ValueMetricData> data = new ArrayList<>();
@@ -359,14 +364,14 @@
protected StatsLogReport getStatsLogReport() throws Exception {
ConfigMetricsReport report = getConfigMetricsReport();
- assertTrue(report.hasUidMap());
- assertEquals(1, report.getMetricsCount());
+ assertThat(report.hasUidMap()).isTrue();
+ assertThat(report.getMetricsCount()).isEqualTo(1);
return report.getMetrics(0);
}
protected ConfigMetricsReport getConfigMetricsReport() throws Exception {
ConfigMetricsReportList reportList = getReportList();
- assertEquals(1, reportList.getReportsCount());
+ assertThat(reportList.getReportsCount()).isEqualTo(1);
return reportList.getReports(0);
}
@@ -624,7 +629,7 @@
int wait, Function<Atom, Integer> getStateFromAtom) {
// Sometimes, there are more events than there are states.
// Eg: When the screen turns off, it may go into OFF and then DOZE immediately.
- assertTrue("Too few states found (" + data.size() + ")", data.size() >= stateSets.size());
+ assertWithMessage("Too few states found").that(data.size()).isAtLeast(stateSets.size());
int stateSetIndex = 0; // Tracks which state set we expect the data to be in.
for (int dataIndex = 0; dataIndex < data.size(); dataIndex++) {
Atom atom = data.get(dataIndex).getAtom();
@@ -641,19 +646,18 @@
LogUtil.CLog.i("Assert that the following atom at dataIndex=" + dataIndex + " is"
+ " in stateSetIndex " + stateSetIndex + ":\n"
+ data.get(dataIndex).getAtom().toString());
- assertTrue("Missed first state", dataIndex != 0); // should not be on first data
- assertTrue("Too many states (" + (stateSetIndex + 1) + ")",
- stateSetIndex < stateSets.size());
- assertTrue("Is in wrong state (" + state + ")",
- stateSets.get(stateSetIndex).contains(state));
+ assertWithMessage("Missed first state").that(dataIndex).isNotEqualTo(0);
+ assertWithMessage("Too many states").that(stateSetIndex)
+ .isLessThan(stateSets.size());
+ assertWithMessage(String.format("Is in wrong state (%d)", state))
+ .that(stateSets.get(stateSetIndex)).contains(state);
if (wait > 0) {
assertTimeDiffBetween(data.get(dataIndex - 1), data.get(dataIndex),
wait / 2, wait * 5);
}
}
}
- assertTrue("Too few states (" + (stateSetIndex + 1) + ")",
- stateSetIndex == stateSets.size() - 1);
+ assertWithMessage("Too few states").that(stateSetIndex).isEqualTo(stateSets.size() - 1);
}
/**
@@ -899,8 +903,8 @@
public static void assertTimeDiffBetween(EventMetricData d0, EventMetricData d1,
int minDiffMs, int maxDiffMs) {
long diffMs = (d1.getElapsedTimestampNanos() - d0.getElapsedTimestampNanos()) / 1_000_000;
- assertTrue("Illegal time difference (" + diffMs + "ms)", minDiffMs <= diffMs);
- assertTrue("Illegal time difference (" + diffMs + "ms)", diffMs <= maxDiffMs);
+ assertWithMessage("Illegal time difference")
+ .that(diffMs).isIn(Range.closed((long) minDiffMs, (long) maxDiffMs));
}
protected String getCurrentLogcatDate() throws Exception {
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/BaseTestCase.java b/hostsidetests/statsd/src/android/cts/statsd/atom/BaseTestCase.java
index 94d5fdc..b110fff 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/BaseTestCase.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/BaseTestCase.java
@@ -16,6 +16,9 @@
package android.cts.statsd.atom;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
import android.cts.statsd.validation.ValidationTestUtil;
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
@@ -52,7 +55,7 @@
@Override
protected void setUp() throws Exception {
super.setUp();
- assertNotNull(mCtsBuild);
+ assertThat(mCtsBuild).isNotNull();
}
@Override
@@ -104,7 +107,8 @@
CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild);
final String result = getDevice().installPackage(
buildHelper.getTestFile(appFileName), true, grantPermissions);
- assertNull("Failed to install " + appFileName + ": " + result, result);
+ assertWithMessage(String.format("Failed to install %s: %s", appFileName, result))
+ .that(result).isNull();
}
protected CompatibilityBuildHelper getBuildHelper() {
@@ -135,7 +139,7 @@
}
CollectingTestListener listener = new CollectingTestListener();
- assertTrue(getDevice().runInstrumentationTests(testRunner, listener));
+ assertThat(getDevice().runInstrumentationTests(testRunner, listener)).isTrue();
final TestRunResult result = listener.getCurrentRunResults();
if (result.isRunFailure()) {
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/DeviceAtomTestCase.java b/hostsidetests/statsd/src/android/cts/statsd/atom/DeviceAtomTestCase.java
index ec6291d..8394006 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/DeviceAtomTestCase.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/DeviceAtomTestCase.java
@@ -15,6 +15,9 @@
*/
package android.cts.statsd.atom;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
import com.android.internal.os.StatsdConfigProto.FieldValueMatcher;
import com.android.internal.os.StatsdConfigProto.MessageMatcher;
import com.android.internal.os.StatsdConfigProto.Position;
@@ -80,9 +83,9 @@
List<EventMetricData> data = doDeviceMethod(methodName, conf);
if (demandExactlyTwo) {
- assertEquals(2, data.size());
+ assertThat(data).hasSize(2);
} else {
- assertTrue("data.size() [" + data.size() + "] should be >= 2", data.size() >= 2);
+ assertThat(data.size()).isAtLeast(2);
}
assertTimeDiffBetween(data.get(0), data.get(1), minTimeDiffMs, maxTimeDiffMs);
return data;
@@ -165,9 +168,9 @@
+ currentUser + " " + DEVICE_SIDE_TEST_PACKAGE);
String[] uidLineParts = uidLine.split(":");
// 3rd entry is package uid
- assertTrue(uidLineParts.length > 2);
+ assertThat(uidLineParts.length).isGreaterThan(2);
int uid = Integer.parseInt(uidLineParts[2].trim());
- assertTrue(uid > 10000);
+ assertThat(uid).isGreaterThan(10000);
return uid;
}
@@ -287,8 +290,10 @@
protected void rebootDeviceAndWaitUntilReady() throws Exception {
rebootDevice();
// Wait for 2 mins.
- assertTrue("Device failed to boot", getDevice().waitForBootComplete(120_000));
- assertTrue("Stats service failed to start", waitForStatsServiceStart(60_000));
+ assertWithMessage("Device failed to boot")
+ .that(getDevice().waitForBootComplete(120_000)).isTrue();
+ assertWithMessage("Stats service failed to start")
+ .that(waitForStatsServiceStart(60_000)).isTrue();
Thread.sleep(2_000);
}
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/HostAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/HostAtomTests.java
index c22fd8b..1bdaf7c 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/HostAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/HostAtomTests.java
@@ -16,6 +16,7 @@
package android.cts.statsd.atom;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import android.os.BatteryPluggedStateEnum;
import android.os.BatteryStatusEnum;
@@ -32,6 +33,8 @@
import com.android.os.StatsLog.ConfigMetricsReportList;
import com.android.os.StatsLog.EventMetricData;
+import com.google.common.collect.Range;
+
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
@@ -349,11 +352,12 @@
List<Atom> data = getGaugeMetricDataList();
- assertTrue(data.size() > 0);
+ assertThat(data).isNotEmpty();
Atom atom = data.get(0);
- assertTrue(atom.getRemainingBatteryCapacity().hasChargeMicroAmpereHour());
+ assertThat(atom.getRemainingBatteryCapacity().hasChargeMicroAmpereHour()).isTrue();
if (hasBattery()) {
- assertTrue(atom.getRemainingBatteryCapacity().getChargeMicroAmpereHour() > 0);
+ assertThat(atom.getRemainingBatteryCapacity().getChargeMicroAmpereHour())
+ .isGreaterThan(0);
}
}
@@ -375,11 +379,11 @@
List<Atom> data = getGaugeMetricDataList();
- assertTrue(data.size() > 0);
+ assertThat(data).isNotEmpty();
Atom atom = data.get(0);
- assertTrue(atom.getFullBatteryCapacity().hasCapacityMicroAmpereHour());
+ assertThat(atom.getFullBatteryCapacity().hasCapacityMicroAmpereHour()).isTrue();
if (hasBattery()) {
- assertTrue(atom.getFullBatteryCapacity().getCapacityMicroAmpereHour() > 0);
+ assertThat(atom.getFullBatteryCapacity().getCapacityMicroAmpereHour()).isGreaterThan(0);
}
}
@@ -399,11 +403,11 @@
List<Atom> data = getGaugeMetricDataList();
- assertTrue(data.size() > 0);
+ assertThat(data).isNotEmpty();
Atom atom = data.get(0);
- assertTrue(atom.getBatteryVoltage().hasVoltageMillivolt());
+ assertThat(atom.getBatteryVoltage().hasVoltageMillivolt()).isTrue();
if (hasBattery()) {
- assertTrue(atom.getBatteryVoltage().getVoltageMillivolt() > 0);
+ assertThat(atom.getBatteryVoltage().getVoltageMillivolt()).isGreaterThan(0);
}
}
@@ -424,12 +428,11 @@
List<Atom> data = getGaugeMetricDataList();
- assertTrue(data.size() > 0);
+ assertThat(data).isNotEmpty();
Atom atom = data.get(0);
- assertTrue(atom.getBatteryLevel().hasBatteryLevel());
+ assertThat(atom.getBatteryLevel().hasBatteryLevel()).isTrue();
if (hasBattery()) {
- assertTrue(atom.getBatteryLevel().getBatteryLevel() > 0);
- assertTrue(atom.getBatteryLevel().getBatteryLevel() <= 100);
+ assertThat(atom.getBatteryLevel().getBatteryLevel()).isIn(Range.openClosed(0, 100));
}
}
@@ -450,11 +453,11 @@
List<Atom> data = getGaugeMetricDataList();
- assertTrue(data.size() > 0);
+ assertThat(data).isNotEmpty();
Atom atom = data.get(0);
- assertTrue(atom.getBatteryCycleCount().hasCycleCount());
+ assertThat(atom.getBatteryCycleCount().hasCycleCount()).isTrue();
if (hasBattery()) {
- assertTrue(atom.getBatteryCycleCount().getCycleCount() >= 0);
+ assertThat(atom.getBatteryCycleCount().getCycleCount()).isAtLeast(0);
}
}
@@ -474,11 +477,11 @@
List<Atom> data = getGaugeMetricDataList();
Atom atom = data.get(0);
- assertTrue(!atom.getKernelWakelock().getName().equals(""));
- assertTrue(atom.getKernelWakelock().hasCount());
- assertTrue(atom.getKernelWakelock().hasVersion());
- assertTrue(atom.getKernelWakelock().getVersion() > 0);
- assertTrue(atom.getKernelWakelock().hasTimeMicros());
+ assertThat(atom.getKernelWakelock().getName()).isNotEmpty();
+ assertThat(atom.getKernelWakelock().hasCount()).isTrue();
+ assertThat(atom.getKernelWakelock().hasVersion()).isTrue();
+ assertThat(atom.getKernelWakelock().getVersion()).isGreaterThan(0);
+ assertThat(atom.getKernelWakelock().hasTimeMicros()).isTrue();
}
// Returns true iff either |WAKE_LOCK_FILE| or |WAKE_SOURCES_FILE| exists.
@@ -510,12 +513,12 @@
List<Atom> dataList = getGaugeMetricDataList();
for (Atom atom: dataList) {
- assertTrue(atom.getWifiActivityInfo().getTimestampMillis() > 0);
- assertTrue(atom.getWifiActivityInfo().getStackState() >= 0);
- assertTrue(atom.getWifiActivityInfo().getControllerIdleTimeMillis() > 0);
- assertTrue(atom.getWifiActivityInfo().getControllerTxTimeMillis() >= 0);
- assertTrue(atom.getWifiActivityInfo().getControllerRxTimeMillis() >= 0);
- assertTrue(atom.getWifiActivityInfo().getControllerEnergyUsed() >= 0);
+ assertThat(atom.getWifiActivityInfo().getTimestampMillis()).isGreaterThan(0L);
+ assertThat(atom.getWifiActivityInfo().getStackState()).isAtLeast(0);
+ assertThat(atom.getWifiActivityInfo().getControllerIdleTimeMillis()).isGreaterThan(0L);
+ assertThat(atom.getWifiActivityInfo().getControllerTxTimeMillis()).isAtLeast(0L);
+ assertThat(atom.getWifiActivityInfo().getControllerRxTimeMillis()).isAtLeast(0L);
+ assertThat(atom.getWifiActivityInfo().getControllerEnergyUsed()).isAtLeast(0L);
}
}
@@ -533,16 +536,17 @@
Thread.sleep(WAIT_TIME_LONG);
List<Atom> data = getGaugeMetricDataList();
- assertTrue(data.size() > 0);
+ assertThat(data).isNotEmpty();
BuildInformation atom = data.get(0).getBuildInformation();
- assertEquals(getProperty("ro.product.brand"), atom.getBrand());
- assertEquals(getProperty("ro.product.name"), atom.getProduct());
- assertEquals(getProperty("ro.product.device"), atom.getDevice());
- assertEquals(getProperty("ro.build.version.release"), atom.getVersionRelease());
- assertEquals(getProperty("ro.build.id"), atom.getId());
- assertEquals(getProperty("ro.build.version.incremental"), atom.getVersionIncremental());
- assertEquals(getProperty("ro.build.type"), atom.getType());
- assertEquals(getProperty("ro.build.tags"), atom.getTags());
+ assertThat(getProperty("ro.product.brand")).isEqualTo(atom.getBrand());
+ assertThat(getProperty("ro.product.name")).isEqualTo(atom.getProduct());
+ assertThat(getProperty("ro.product.device")).isEqualTo(atom.getDevice());
+ assertThat(getProperty("ro.build.version.release")).isEqualTo(atom.getVersionRelease());
+ assertThat(getProperty("ro.build.id")).isEqualTo(atom.getId());
+ assertThat(getProperty("ro.build.version.incremental"))
+ .isEqualTo(atom.getVersionIncremental());
+ assertThat(getProperty("ro.build.type")).isEqualTo(atom.getType());
+ assertThat(getProperty("ro.build.tags")).isEqualTo(atom.getTags());
}
public void testOnDevicePowerMeasurement() throws Exception {
@@ -563,8 +567,9 @@
List<Atom> dataList = getGaugeMetricDataList();
for (Atom atom: dataList) {
- assertTrue(atom.getOnDevicePowerMeasurement().getMeasurementTimestampMillis() >= 0);
- assertTrue(atom.getOnDevicePowerMeasurement().getEnergyMicrowattSecs() >= 0);
+ assertThat(atom.getOnDevicePowerMeasurement().getMeasurementTimestampMillis())
+ .isAtLeast(0L);
+ assertThat(atom.getOnDevicePowerMeasurement().getEnergyMicrowattSecs()).isAtLeast(0L);
}
}
@@ -582,8 +587,8 @@
List<EventMetricData> data = getEventMetricDataList();
AppBreadcrumbReported atom = data.get(0).getAtom().getAppBreadcrumbReported();
- assertTrue(atom.getLabel() == 1);
- assertTrue(atom.getState().getNumber() == AppBreadcrumbReported.State.START_VALUE);
+ assertThat(atom.getLabel()).isEqualTo(1);
+ assertThat(atom.getState().getNumber()).isEqualTo(AppBreadcrumbReported.State.START_VALUE);
}
// Test dumpsys stats --proto.
@@ -600,7 +605,7 @@
// Get the stats incident section.
List<ConfigMetricsReportList> listList = getReportsFromStatsDataDumpProto();
- assertTrue(listList.size() > 0);
+ assertThat(listList).isNotEmpty();
// Extract the relevent report from the incident section.
ConfigMetricsReportList ourList = null;
@@ -612,14 +617,14 @@
break;
}
}
- assertNotNull("Could not find list for uid=" + hostUid
- + " id=" + CONFIG_ID, ourList);
+ assertWithMessage(String.format("Could not find list for uid=%d id=%d", hostUid, CONFIG_ID))
+ .that(ourList).isNotNull();
// Make sure that the report is correct.
List<EventMetricData> data = getEventMetricDataList(ourList);
AppBreadcrumbReported atom = data.get(0).getAtom().getAppBreadcrumbReported();
- assertTrue(atom.getLabel() == 1);
- assertTrue(atom.getState().getNumber() == AppBreadcrumbReported.State.START_VALUE);
+ assertThat(atom.getLabel()).isEqualTo(1);
+ assertThat(atom.getState().getNumber()).isEqualTo(AppBreadcrumbReported.State.START_VALUE);
}
public void testConnectivityStateChange() throws Exception {
@@ -656,6 +661,7 @@
foundConnectEvent = true;
}
}
- assertTrue(foundConnectEvent && foundDisconnectEvent);
+ assertThat(foundConnectEvent).isTrue();
+ assertThat(foundDisconnectEvent).isTrue();
}
}
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/ProcStateAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/ProcStateAtomTests.java
index 5fecde5..070d3f4 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/ProcStateAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/ProcStateAtomTests.java
@@ -15,6 +15,8 @@
*/
package android.cts.statsd.atom;
+import static com.google.common.truth.Truth.assertWithMessage;
+
import android.app.ProcessStateEnum; // From enums.proto for atoms.proto's UidProcessStateChanged.
import com.android.os.AtomsProto.Atom;
@@ -248,8 +250,8 @@
if (statsdDisabled()) {
return;
}
- assertFalse("UNKNOWN_TO_PROTO should not be a valid state",
- ALL_STATES.contains(ProcessStateEnum.PROCESS_STATE_UNKNOWN_TO_PROTO_VALUE));
+ assertWithMessage("UNKNOWN_TO_PROTO should not be a valid state")
+ .that(ALL_STATES).doesNotContain(ProcessStateEnum.PROCESS_STATE_UNKNOWN_TO_PROTO_VALUE);
}
/** Returns the a set containing elements of a that are not elements of b. */
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
index 045b4a1..e6d8e43 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
@@ -15,6 +15,9 @@
*/
package android.cts.statsd.atom;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
import android.net.wifi.WifiModeEnum;
import android.os.WakeLockLevelEnum;
import android.server.ErrorSource;
@@ -41,10 +44,10 @@
import com.android.os.AtomsProto.LooperStats;
import com.android.os.AtomsProto.LmkKillOccurred;
import com.android.os.AtomsProto.MediaCodecStateChanged;
-import com.android.os.AtomsProto.NativeProcessMemoryState;
import com.android.os.AtomsProto.OverlayStateChanged;
import com.android.os.AtomsProto.PictureInPictureStateChanged;
import com.android.os.AtomsProto.ProcessMemoryHighWaterMark;
+import com.android.os.AtomsProto.ProcessMemorySnapshot;
import com.android.os.AtomsProto.ProcessMemoryState;
import com.android.os.AtomsProto.ScheduledJobStateChanged;
import com.android.os.AtomsProto.SyncStateChanged;
@@ -58,6 +61,8 @@
import com.android.os.StatsLog.EventMetricData;
import com.android.tradefed.log.LogUtil;
+import com.google.common.collect.Range;
+
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
@@ -102,12 +107,12 @@
// Sorted list of events in order in which they occurred.
List<EventMetricData> data = getEventMetricDataList();
- assertEquals(1, data.size());
- assertTrue(data.get(0).getAtom().hasLmkKillOccurred());
+ assertThat(data).hasSize(1);
+ assertThat(data.get(0).getAtom().hasLmkKillOccurred()).isTrue();
LmkKillOccurred atom = data.get(0).getAtom().getLmkKillOccurred();
- assertEquals(getUid(), atom.getUid());
- assertEquals(DEVICE_SIDE_TEST_PACKAGE, atom.getProcessName());
- assertTrue(500 <= atom.getOomAdjScore());
+ assertThat(atom.getUid()).isEqualTo(getUid());
+ assertThat(atom.getProcessName()).isEqualTo(DEVICE_SIDE_TEST_PACKAGE);
+ assertThat(atom.getOomAdjScore()).isAtLeast(500);
}
public void testAppCrashOccurred() throws Exception {
@@ -125,11 +130,12 @@
List<EventMetricData> data = getEventMetricDataList();
AppCrashOccurred atom = data.get(0).getAtom().getAppCrashOccurred();
- assertEquals("crash", atom.getEventType());
- assertEquals(AppCrashOccurred.InstantApp.FALSE_VALUE, atom.getIsInstantApp().getNumber());
- assertEquals(AppCrashOccurred.ForegroundState.FOREGROUND_VALUE,
- atom.getForegroundState().getNumber());
- assertEquals("com.android.server.cts.device.statsd", atom.getPackageName());
+ assertThat(atom.getEventType()).isEqualTo("crash");
+ assertThat(atom.getIsInstantApp().getNumber())
+ .isEqualTo(AppCrashOccurred.InstantApp.FALSE_VALUE);
+ assertThat(atom.getForegroundState().getNumber())
+ .isEqualTo(AppCrashOccurred.ForegroundState.FOREGROUND_VALUE);
+ assertThat(atom.getPackageName()).isEqualTo("com.android.server.cts.device.statsd");
}
public void testAppStartOccurred() throws Exception {
@@ -147,12 +153,12 @@
List<EventMetricData> data = getEventMetricDataList();
AppStartOccurred atom = data.get(0).getAtom().getAppStartOccurred();
- assertEquals("com.android.server.cts.device.statsd", atom.getPkgName());
- assertEquals("com.android.server.cts.device.statsd.StatsdCtsForegroundActivity",
- atom.getActivityName());
- assertFalse(atom.getIsInstantApp());
- assertTrue(atom.getActivityStartMillis() > 0);
- assertTrue(atom.getTransitionDelayMillis() > 0);
+ assertThat(atom.getPkgName()).isEqualTo("com.android.server.cts.device.statsd");
+ assertThat(atom.getActivityName())
+ .isEqualTo("com.android.server.cts.device.statsd.StatsdCtsForegroundActivity");
+ assertThat(atom.getIsInstantApp()).isFalse();
+ assertThat(atom.getActivityStartMillis()).isGreaterThan(0L);
+ assertThat(atom.getTransitionDelayMillis()).isGreaterThan(0);
}
public void testAudioState() throws Exception {
@@ -204,8 +210,8 @@
BleScanStateChanged a0 = data.get(0).getAtom().getBleScanStateChanged();
BleScanStateChanged a1 = data.get(1).getAtom().getBleScanStateChanged();
- assertTrue(a0.getState().getNumber() == stateOn);
- assertTrue(a1.getState().getNumber() == stateOff);
+ assertThat(a0.getState().getNumber()).isEqualTo(stateOn);
+ assertThat(a1.getState().getNumber()).isEqualTo(stateOff);
}
public void testBleUnoptimizedScan() throws Exception {
@@ -225,15 +231,15 @@
stateOn, stateOff, minTimeDiffMillis, maxTimeDiffMillis, true);
BleScanStateChanged a0 = data.get(0).getAtom().getBleScanStateChanged();
- assertTrue(a0.getState().getNumber() == stateOn);
- assertFalse(a0.getIsFiltered());
- assertFalse(a0.getIsFirstMatch());
- assertFalse(a0.getIsOpportunistic());
+ assertThat(a0.getState().getNumber()).isEqualTo(stateOn);
+ assertThat(a0.getIsFiltered()).isFalse();
+ assertThat(a0.getIsFirstMatch()).isFalse();
+ assertThat(a0.getIsOpportunistic()).isFalse();
BleScanStateChanged a1 = data.get(1).getAtom().getBleScanStateChanged();
- assertTrue(a1.getState().getNumber() == stateOff);
- assertFalse(a1.getIsFiltered());
- assertFalse(a1.getIsFirstMatch());
- assertFalse(a1.getIsOpportunistic());
+ assertThat(a1.getState().getNumber()).isEqualTo(stateOff);
+ assertThat(a1.getIsFiltered()).isFalse();
+ assertThat(a1.getIsFirstMatch()).isFalse();
+ assertThat(a1.getIsOpportunistic()).isFalse();
// Now repeat the test for opportunistic scanning and make sure it is reported correctly.
@@ -241,15 +247,15 @@
stateOn, stateOff, minTimeDiffMillis, maxTimeDiffMillis, true);
a0 = data.get(0).getAtom().getBleScanStateChanged();
- assertTrue(a0.getState().getNumber() == stateOn);
- assertFalse(a0.getIsFiltered());
- assertFalse(a0.getIsFirstMatch());
- assertTrue(a0.getIsOpportunistic()); // This scan is opportunistic.
+ assertThat(a0.getState().getNumber()).isEqualTo(stateOn);
+ assertThat(a0.getIsFiltered()).isFalse();
+ assertThat(a0.getIsFirstMatch()).isFalse();
+ assertThat(a0.getIsOpportunistic()).isTrue(); // This scan is opportunistic.
a1 = data.get(1).getAtom().getBleScanStateChanged();
- assertTrue(a1.getState().getNumber() == stateOff);
- assertFalse(a1.getIsFiltered());
- assertFalse(a1.getIsFirstMatch());
- assertTrue(a1.getIsOpportunistic());
+ assertThat(a1.getState().getNumber()).isEqualTo(stateOff);
+ assertThat(a1.getIsFiltered()).isFalse();
+ assertThat(a1.getIsFirstMatch()).isFalse();
+ assertThat(a1.getIsOpportunistic()).isTrue();
}
public void testBleScanResult() throws Exception {
@@ -265,9 +271,9 @@
addAtomEvent(conf, atom, createFvm(field).setGteInt(0));
List<EventMetricData> data = doDeviceMethod("testBleScanResult", conf);
- assertTrue(data.size() >= 1);
+ assertThat(data.size()).isAtLeast(1);
BleScanResultReceived a0 = data.get(0).getAtom().getBleScanResultReceived();
- assertTrue(a0.getNumResults() >= 1);
+ assertThat(a0.getNumResults()).isAtLeast(1);
}
public void testHiddenApiUsed() throws Exception {
@@ -289,15 +295,15 @@
List<EventMetricData> data = getEventMetricDataList();
- assertTrue(data.size() == 1);
+ assertThat(data).hasSize(1);
HiddenApiUsed atom = data.get(0).getAtom().getHiddenApiUsed();
int uid = getUid();
- assertEquals(uid, atom.getUid());
- assertFalse(atom.getAccessDenied());
- assertEquals("Landroid/app/Activity;->mWindow:Landroid/view/Window;",
- atom.getSignature());
+ assertThat(atom.getUid()).isEqualTo(uid);
+ assertThat(atom.getAccessDenied()).isFalse();
+ assertThat(atom.getSignature())
+ .isEqualTo("Landroid/app/Activity;->mWindow:Landroid/view/Window;");
} finally {
if (!oldRate.equals("null")) {
getDevice().executeShellCommand(
@@ -359,11 +365,11 @@
for (Atom atom : atomList) {
if (atom.getCpuTimePerUid().getUid() == uid) {
found = true;
- assertTrue(atom.getCpuTimePerUid().getUserTimeMicros() > 0);
- assertTrue(atom.getCpuTimePerUid().getSysTimeMicros() > 0);
+ assertThat(atom.getCpuTimePerUid().getUserTimeMicros()).isGreaterThan(0L);
+ assertThat(atom.getCpuTimePerUid().getSysTimeMicros()).isGreaterThan(0L);
}
}
- assertTrue("found uid " + uid, found);
+ assertWithMessage(String.format("did not find uid %d", uid)).that(found).isTrue();
}
public void testDeviceCalculatedPowerUse() throws Exception {
@@ -384,7 +390,8 @@
Thread.sleep(WAIT_TIME_LONG);
Atom atom = getGaugeMetricDataList().get(0);
- assertTrue(atom.getDeviceCalculatedPowerUse().getComputedPowerNanoAmpSecs() > 0);
+ assertThat(atom.getDeviceCalculatedPowerUse().getComputedPowerNanoAmpSecs())
+ .isGreaterThan(0L);
}
@@ -413,13 +420,15 @@
for (Atom atom : atomList) {
DeviceCalculatedPowerBlameUid item = atom.getDeviceCalculatedPowerBlameUid();
if (item.getUid() == uid) {
- assertFalse("Found multiple power values for uid " + uid, uidFound);
+ assertWithMessage(String.format("Found multiple power values for uid %d", uid))
+ .that(uidFound).isFalse();
uidFound = true;
uidPower = item.getPowerNanoAmpSecs();
}
}
- assertTrue("No power value for uid " + uid, uidFound);
- assertTrue("Non-positive power value for uid " + uid, uidPower > 0);
+ assertWithMessage(String.format("No power value for uid %d", uid)).that(uidFound).isTrue();
+ assertWithMessage(String.format("Non-positive power value for uid %d", uid))
+ .that(uidPower).isGreaterThan(0L);
}
public void testDavey() throws Exception {
@@ -435,12 +444,10 @@
runActivity("DaveyActivity", null, null);
List<EventMetricData> data = getEventMetricDataList();
- assertTrue(data.size() == 1);
+ assertThat(data).hasSize(1);
long duration = data.get(0).getAtom().getDaveyOccurred().getJankDurationMillis();
- assertTrue("Jank duration of " + duration + "ms was less than " + MIN_DURATION + "ms",
- duration >= MIN_DURATION);
- assertTrue("Jank duration of " + duration + "ms was longer than " + MAX_DURATION + "ms",
- duration <= MAX_DURATION);
+ assertWithMessage("Incorrect jank duration")
+ .that(duration).isIn(Range.closed(MIN_DURATION, MAX_DURATION));
}
public void testFlashlightState() throws Exception {
@@ -526,8 +533,8 @@
GpsScanStateChanged a0 = data.get(0).getAtom().getGpsScanStateChanged();
GpsScanStateChanged a1 = data.get(1).getAtom().getGpsScanStateChanged();
- assertTrue(a0.getState().getNumber() == stateOn);
- assertTrue(a1.getState().getNumber() == stateOff);
+ assertThat(a0.getState().getNumber()).isEqualTo(stateOn);
+ assertThat(a1.getState().getNumber()).isEqualTo(stateOff);
} finally {
if ("null".equals(origWhitelist) || "".equals(origWhitelist)) {
getDevice().executeShellCommand(
@@ -657,7 +664,8 @@
atom -> atom.getScheduledJobStateChanged().getState().getNumber());
for (EventMetricData e : data) {
- assertTrue(e.getAtom().getScheduledJobStateChanged().getJobName().equals(expectedName));
+ assertThat(e.getAtom().getScheduledJobStateChanged().getJobName())
+ .isEqualTo(expectedName);
}
}
@@ -783,10 +791,8 @@
for (EventMetricData event: data) {
String tag = event.getAtom().getWakelockStateChanged().getTag();
WakeLockLevelEnum type = event.getAtom().getWakelockStateChanged().getType();
- assertTrue("Expected tag: " + EXPECTED_TAG + ", but got tag: " + tag,
- tag.equals(EXPECTED_TAG));
- assertTrue("Expected wakelock type: " + EXPECTED_LEVEL + ", but got level: " + type,
- type == EXPECTED_LEVEL);
+ assertThat(tag).isEqualTo(EXPECTED_TAG);
+ assertThat(type).isEqualTo(EXPECTED_LEVEL);
}
}
@@ -803,11 +809,11 @@
addAtomEvent(config, atomTag, true); // True: uses attribution.
List<EventMetricData> data = doDeviceMethod("testWakeupAlarm", config);
- assertTrue(data.size() >= 1);
+ assertThat(data.size()).isAtLeast(1);
for (int i = 0; i < data.size(); i++) {
WakeupAlarmOccurred wao = data.get(i).getAtom().getWakeupAlarmOccurred();
- assertEquals("*walarm*:android.cts.statsd.testWakeupAlarm", wao.getTag());
- assertEquals(DEVICE_SIDE_TEST_PACKAGE, wao.getPackageName());
+ assertThat(wao.getTag()).isEqualTo("*walarm*:android.cts.statsd.testWakeupAlarm");
+ assertThat(wao.getPackageName()).isEqualTo(DEVICE_SIDE_TEST_PACKAGE);
}
}
@@ -836,8 +842,8 @@
atom -> atom.getWifiLockStateChanged().getState().getNumber());
for (EventMetricData event : data) {
- assertEquals(WifiModeEnum.WIFI_MODE_FULL_HIGH_PERF,
- event.getAtom().getWifiLockStateChanged().getMode());
+ assertThat(event.getAtom().getWifiLockStateChanged().getMode())
+ .isEqualTo(WifiModeEnum.WIFI_MODE_FULL_HIGH_PERF);
}
}
@@ -866,8 +872,8 @@
atom -> atom.getWifiLockStateChanged().getState().getNumber());
for (EventMetricData event : data) {
- assertEquals(WifiModeEnum.WIFI_MODE_FULL_LOW_LATENCY,
- event.getAtom().getWifiLockStateChanged().getMode());
+ assertThat(event.getAtom().getWifiLockStateChanged().getMode())
+ .isEqualTo(WifiModeEnum.WIFI_MODE_FULL_LOW_LATENCY);
}
}
@@ -901,7 +907,7 @@
for (EventMetricData event: data) {
String tag = event.getAtom().getWifiMulticastLockStateChanged().getTag();
- assertEquals("Wrong tag.", EXPECTED_TAG, tag);
+ assertThat(tag).isEqualTo(EXPECTED_TAG);
}
}
@@ -922,12 +928,11 @@
List<EventMetricData> data = doDeviceMethodOnOff("testWifiScan", atom, key,
stateOn, stateOff, minTimeDiffMillis, maxTimeDiffMillis, demandExactlyTwo);
- assertTrue(data.size() >= 2);
- assertTrue(data.size() <= 4);
+ assertThat(data.size()).isIn(Range.closed(2, 4));
WifiScanStateChanged a0 = data.get(0).getAtom().getWifiScanStateChanged();
WifiScanStateChanged a1 = data.get(1).getAtom().getWifiScanStateChanged();
- assertTrue(a0.getState().getNumber() == stateOn);
- assertTrue(a1.getState().getNumber() == stateOff);
+ assertThat(a0.getState().getNumber()).isEqualTo(stateOn);
+ assertThat(a1.getState().getNumber()).isEqualTo(stateOff);
}
public void testBinderStats() throws Exception {
@@ -963,20 +968,16 @@
if (calls.getUid() == uid && classMatches && methodMatches) {
found = true;
- assertTrue("Call count should not be negative or equal to 0.",
- calls.getRecordedCallCount() > 0);
- assertTrue("Call count should not be negative or equal to 0.",
- calls.getCallCount() > 0);
- assertTrue("Wrong latency",
- calls.getRecordedTotalLatencyMicros() > 0
- && calls.getRecordedTotalLatencyMicros() < 1000000);
- assertTrue("Wrong cpu usage",
- calls.getRecordedTotalCpuMicros() > 0
- && calls.getRecordedTotalCpuMicros() < 1000000);
+ assertThat(calls.getRecordedCallCount()).isGreaterThan(0L);
+ assertThat(calls.getCallCount()).isGreaterThan(0L);
+ assertThat(calls.getRecordedTotalLatencyMicros())
+ .isIn(Range.open(0L, 1000000L));
+ assertThat(calls.getRecordedTotalCpuMicros()).isIn(Range.open(0L, 1000000L));
}
}
- assertTrue("Did not find a matching atom for uid " + uid, found);
+ assertWithMessage(String.format("Did not find a matching atom for uid %d", uid))
+ .that(found).isTrue();
} finally {
disableBinderStats();
@@ -1017,34 +1018,21 @@
notificationServiceFullName + "$EnqueueNotificationRunnable");
if (atom.getLooperStats().getUid() == uid && handlerMatches && messageMatches) {
found = true;
- assertTrue(stats.getMessageCount() > 0);
- assertTrue("Message count should be non-negative.",
- stats.getMessageCount() > 0);
- assertTrue("Recorded message count should be non-negative.",
- stats.getRecordedMessageCount() > 0);
- assertTrue("Wrong latency",
- stats.getRecordedTotalLatencyMicros() > 0
- && stats.getRecordedTotalLatencyMicros() < 1000000);
- assertTrue("Wrong cpu usage",
- stats.getRecordedTotalCpuMicros() > 0
- && stats.getRecordedTotalCpuMicros() < 1000000);
- assertTrue("Wrong max latency",
- stats.getRecordedMaxLatencyMicros() > 0
- && stats.getRecordedMaxLatencyMicros() < 1000000);
- assertTrue("Wrong max cpu usage",
- stats.getRecordedMaxCpuMicros() > 0
- && stats.getRecordedMaxCpuMicros() < 1000000);
- assertTrue("Recorded delay message count should be non-negative.",
- stats.getRecordedDelayMessageCount() > 0);
- assertTrue("Wrong delay",
- stats.getRecordedTotalDelayMillis() >= 0
- && stats.getRecordedTotalDelayMillis() < 5000);
- assertTrue("Wrong max delay",
- stats.getRecordedMaxDelayMillis() >= 0
- && stats.getRecordedMaxDelayMillis() < 5000);
+ assertThat(stats.getMessageCount()).isGreaterThan(0L);
+ assertThat(stats.getRecordedMessageCount()).isGreaterThan(0L);
+ assertThat(stats.getRecordedTotalLatencyMicros())
+ .isIn(Range.open(0L, 1000000L));
+ assertThat(stats.getRecordedTotalCpuMicros()).isIn(Range.open(0L, 1000000L));
+ assertThat(stats.getRecordedMaxLatencyMicros()).isIn(Range.open(0L, 1000000L));
+ assertThat(stats.getRecordedMaxCpuMicros()).isIn(Range.open(0L, 1000000L));
+ assertThat(stats.getRecordedDelayMessageCount()).isGreaterThan(0L);
+ assertThat(stats.getRecordedTotalDelayMillis())
+ .isIn(Range.closedOpen(0L, 5000L));
+ assertThat(stats.getRecordedMaxDelayMillis()).isIn(Range.closedOpen(0L, 5000L));
}
}
- assertTrue("Did not find a matching atom for uid " + uid, found);
+ assertWithMessage(String.format("Did not find a matching atom for uid %d", uid))
+ .that(found).isTrue();
} finally {
cleanUpLooperStats();
plugInAc();
@@ -1081,53 +1069,16 @@
continue;
}
found = true;
- assertEquals(DEVICE_SIDE_TEST_PACKAGE, state.getProcessName());
- assertTrue("oom_score should not be negative", state.getOomAdjScore() >= 0);
- assertTrue("page_fault should not be negative", state.getPageFault() >= 0);
- assertTrue("page_major_fault should not be negative", state.getPageMajorFault() >= 0);
- assertTrue("rss_in_bytes should be positive", state.getRssInBytes() > 0);
- assertTrue("cache_in_bytes should not be negative", state.getCacheInBytes() >= 0);
- assertTrue("swap_in_bytes should not be negative", state.getSwapInBytes() >= 0);
- assertTrue("start_time_nanos should be positive", state.getStartTimeNanos() > 0);
- assertTrue("start_time_nanos should be in the past",
- state.getStartTimeNanos() < System.nanoTime());
+ assertThat(state.getProcessName()).isEqualTo(DEVICE_SIDE_TEST_PACKAGE);
+ assertThat(state.getOomAdjScore()).isAtLeast(0);
+ assertThat(state.getPageFault()).isAtLeast(0L);
+ assertThat(state.getPageMajorFault()).isAtLeast(0L);
+ assertThat(state.getRssInBytes()).isGreaterThan(0L);
+ assertThat(state.getCacheInBytes()).isAtLeast(0L);
+ assertThat(state.getSwapInBytes()).isAtLeast(0L);
}
- assertTrue("Did not find a matching atom for uid=" + uid, found);
- }
-
- public void testNativeProcessMemoryState() throws Exception {
- if (statsdDisabled()) {
- return;
- }
-
- // Get NativeProcessState as a simple gauge metric.
- StatsdConfig.Builder config = getPulledConfig();
- addGaugeAtomWithDimensions(config, Atom.NATIVE_PROCESS_MEMORY_STATE_FIELD_NUMBER, null);
- uploadConfig(config);
- Thread.sleep(WAIT_TIME_SHORT);
-
- // Trigger new pull.
- setAppBreadcrumbPredicate();
- Thread.sleep(WAIT_TIME_LONG);
-
- // Assert about NativeProcessMemoryState for statsd.
- List<Atom> atoms = getGaugeMetricDataList();
- boolean found = false;
- for (Atom atom : atoms) {
- NativeProcessMemoryState state = atom.getNativeProcessMemoryState();
- if (!state.getProcessName().contains("/statsd")) {
- continue;
- }
- found = true;
- assertTrue("uid is below 10000", state.getUid() < 10000);
- assertTrue("page_fault should not be negative", state.getPageFault() >= 0);
- assertTrue("page_major_fault should not be negative", state.getPageMajorFault() >= 0);
- assertTrue("rss_in_bytes should be positive", state.getRssInBytes() > 0);
- assertTrue("start_time_nanos should be positive", state.getStartTimeNanos() > 0);
- assertTrue("start_time_nanos should be in the past",
- state.getStartTimeNanos() < System.nanoTime());
- }
- assertTrue("Did not find a matching atom for statsd", found);
+ assertWithMessage(String.format("Did not find a matching atom for uid %d", uid))
+ .that(found).isTrue();
}
public void testProcessMemoryHighWaterMark() throws Exception {
@@ -1135,13 +1086,13 @@
return;
}
- // Get ProcessMemoryState as a simple gauge metric.
+ // Get ProcessMemoryHighWaterMark as a simple gauge metric.
StatsdConfig.Builder config = getPulledConfig();
addGaugeAtomWithDimensions(config, Atom.PROCESS_MEMORY_HIGH_WATER_MARK_FIELD_NUMBER, null);
uploadConfig(config);
Thread.sleep(WAIT_TIME_SHORT);
- // Start test app and trigger a pull while its running.
+ // Start test app and trigger a pull while it is running.
try (AutoCloseable a = withActivity("StatsdCtsForegroundActivity", "action",
"action.show_notification")) {
setAppBreadcrumbPredicate();
@@ -1158,22 +1109,71 @@
ProcessMemoryHighWaterMark state = atom.getProcessMemoryHighWaterMark();
if (state.getUid() == uid) {
foundTestApp = true;
- assertEquals(DEVICE_SIDE_TEST_PACKAGE, state.getProcessName());
- assertTrue("rss_high_water_mark_in_bytes should be positive",
- state.getRssHighWaterMarkInBytes() > 0);
+ assertThat(state.getProcessName()).isEqualTo(DEVICE_SIDE_TEST_PACKAGE);
+ assertThat(state.getRssHighWaterMarkInBytes()).isGreaterThan(0L);
} else if (state.getProcessName().contains("/statsd")) {
foundStatsd = true;
- assertTrue("rss_high_water_mark_in_bytes should be positive",
- state.getRssHighWaterMarkInBytes() > 0);
+ assertThat(state.getRssHighWaterMarkInBytes()).isGreaterThan(0L);
} else if (state.getProcessName().equals("system")) {
foundSystemServer = true;
- assertTrue("rss_high_water_mark_in_bytes should be positive",
- state.getRssHighWaterMarkInBytes() > 0);
+ assertThat(state.getRssHighWaterMarkInBytes()).isGreaterThan(0L);
}
}
- assertTrue("Did not find a matching atom for test app uid=" + uid, foundTestApp);
- assertTrue("Did not find a matching atom for statsd", foundStatsd);
- assertTrue("Did not find a matching atom for system server", foundSystemServer);
+ assertWithMessage(String.format("Did not find a matching atom for test app uid=%d",uid))
+ .that(foundTestApp).isTrue();
+ assertWithMessage("Did not find a matching atom for statsd").that(foundStatsd).isTrue();
+ assertWithMessage("Did not find a matching atom for system server")
+ .that(foundSystemServer).isTrue();
+ }
+
+ public void testProcessMemorySnapshot() throws Exception {
+ if (statsdDisabled()) {
+ return;
+ }
+
+ // Get ProcessMemorySnapshot as a simple gauge metric.
+ StatsdConfig.Builder config = getPulledConfig();
+ addGaugeAtomWithDimensions(config, Atom.PROCESS_MEMORY_SNAPSHOT_FIELD_NUMBER, null);
+ uploadConfig(config);
+ Thread.sleep(WAIT_TIME_SHORT);
+
+ // Start test app and trigger a pull while it is running.
+ try (AutoCloseable a = withActivity("StatsdCtsForegroundActivity", "action",
+ "action.show_notification")) {
+ setAppBreadcrumbPredicate();
+ Thread.sleep(WAIT_TIME_LONG);
+ }
+
+ // Assert about ProcessMemorySnapshot for the test app, statsd and system server.
+ List<Atom> atoms = getGaugeMetricDataList();
+ int uid = getUid();
+ boolean foundTestApp = false;
+ boolean foundStatsd = false;
+ boolean foundSystemServer = false;
+ for (Atom atom : atoms) {
+ ProcessMemorySnapshot snapshot = atom.getProcessMemorySnapshot();
+ if (snapshot.getUid() == uid) {
+ foundTestApp = true;
+ assertThat(snapshot.getProcessName()).isEqualTo(DEVICE_SIDE_TEST_PACKAGE);
+ } else if (snapshot.getProcessName().contains("/statsd")) {
+ foundStatsd = true;
+ } else if (snapshot.getProcessName().equals("system")) {
+ foundSystemServer = true;
+ }
+
+ assertThat(snapshot.getPid()).isGreaterThan(0);
+ assertThat(snapshot.getAnonRssAndSwapInKilobytes()).isGreaterThan(0);
+ assertThat(snapshot.getAnonRssAndSwapInKilobytes()).isEqualTo(
+ snapshot.getAnonRssInKilobytes() + snapshot.getSwapInKilobytes());
+ assertThat(snapshot.getRssInKilobytes()).isAtLeast(0);
+ assertThat(snapshot.getAnonRssInKilobytes()).isAtLeast(0);
+ assertThat(snapshot.getSwapInKilobytes()).isAtLeast(0);
+ }
+ assertWithMessage(String.format("Did not find a matching atom for test app uid=%d",uid))
+ .that(foundTestApp).isTrue();
+ assertWithMessage("Did not find a matching atom for statsd").that(foundStatsd).isTrue();
+ assertWithMessage("Did not find a matching atom for system server")
+ .that(foundSystemServer).isTrue();
}
/**
@@ -1216,20 +1216,20 @@
for (Atom atom : getGaugeMetricDataList()) {
AtomsProto.RoleHolder roleHolder = atom.getRoleHolder();
- assertNotNull(roleHolder.getPackageName());
- assertTrue(roleHolder.getUid() >= 0);
- assertNotNull(roleHolder.getRole());
+ assertThat(roleHolder.getPackageName()).isNotNull();
+ assertThat(roleHolder.getUid()).isAtLeast(0);
+ assertThat(roleHolder.getRole()).isNotNull();
if (roleHolder.getPackageName().equals(DEVICE_SIDE_TEST_PACKAGE)) {
- assertEquals(testAppId, getAppId(roleHolder.getUid()));
- assertEquals(DEVICE_SIDE_TEST_PACKAGE, roleHolder.getPackageName());
- assertEquals(callScreenAppRole, roleHolder.getRole());
+ assertThat(getAppId(roleHolder.getUid())).isEqualTo(testAppId);
+ assertThat(roleHolder.getPackageName()).isEqualTo(DEVICE_SIDE_TEST_PACKAGE);
+ assertThat(roleHolder.getRole()).isEqualTo(callScreenAppRole);
verifiedKnowRoleState = true;
}
}
- assertTrue(verifiedKnowRoleState);
+ assertThat(verifiedKnowRoleState).isTrue();
}
public void testDangerousPermissionState() throws Exception {
@@ -1257,26 +1257,27 @@
for (Atom atom : getGaugeMetricDataList()) {
DangerousPermissionState permissionState = atom.getDangerousPermissionState();
- assertNotNull(permissionState.getPermissionName());
- assertTrue(permissionState.getUid() >= 0);
- assertNotNull(permissionState.getPackageName());
+ assertThat(permissionState.getPermissionName()).isNotNull();
+ assertThat(permissionState.getUid()).isAtLeast(0);
+ assertThat(permissionState.getPackageName()).isNotNull();
if (permissionState.getPackageName().equals(DEVICE_SIDE_TEST_PACKAGE)) {
- assertEquals(testAppId, getAppId(permissionState.getUid()));
+ assertThat(getAppId(permissionState.getUid())).isEqualTo(testAppId);
if (permissionState.getPermissionName().equals(
"android.permission.ACCESS_FINE_LOCATION")) {
- assertTrue(permissionState.getIsGranted());
- assertEquals(0, permissionState.getPermissionFlags() & (~(
+ assertThat(permissionState.getIsGranted()).isTrue();
+ assertThat(permissionState.getPermissionFlags() & (~(
FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED
- | FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED)));
+ | FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED)))
+ .isEqualTo(0);
verifiedKnowPermissionState = true;
}
}
}
- assertTrue(verifiedKnowPermissionState);
+ assertThat(verifiedKnowPermissionState).isTrue();
}
public void testANROccurred() throws Exception {
@@ -1297,14 +1298,15 @@
// Sorted list of events in order in which they occurred.
List<EventMetricData> data = getEventMetricDataList();
- assertEquals(1, data.size());
- assertTrue(data.get(0).getAtom().hasAnrOccurred());
+ assertThat(data).hasSize(1);
+ assertThat(data.get(0).getAtom().hasAnrOccurred()).isTrue();
ANROccurred atom = data.get(0).getAtom().getAnrOccurred();
- assertEquals(ANROccurred.InstantApp.FALSE_VALUE, atom.getIsInstantApp().getNumber());
- assertEquals(ANROccurred.ForegroundState.FOREGROUND_VALUE,
- atom.getForegroundState().getNumber());
- assertEquals(ErrorSource.DATA_APP, atom.getErrorSource());
- assertEquals(DEVICE_SIDE_TEST_PACKAGE, atom.getPackageName());
+ assertThat(atom.getIsInstantApp().getNumber())
+ .isEqualTo(ANROccurred.InstantApp.FALSE_VALUE);
+ assertThat(atom.getForegroundState().getNumber())
+ .isEqualTo(ANROccurred.ForegroundState.FOREGROUND_VALUE);
+ assertThat(atom.getErrorSource()).isEqualTo(ErrorSource.DATA_APP);
+ assertThat(atom.getPackageName()).isEqualTo(DEVICE_SIDE_TEST_PACKAGE);
}
public void testWriteRawTestAtom() throws Exception {
@@ -1320,80 +1322,71 @@
Thread.sleep(WAIT_TIME_SHORT);
// Sorted list of events in order in which they occurred.
List<EventMetricData> data = getEventMetricDataList();
- assertEquals(data.size(), 4);
+ assertThat(data).hasSize(4);
TestAtomReported atom = data.get(0).getAtom().getTestAtomReported();
List<AttributionNode> attrChain = atom.getAttributionNodeList();
- assertEquals(2, attrChain.size());
- assertEquals(1234, attrChain.get(0).getUid());
- assertEquals("tag1", attrChain.get(0).getTag());
- assertEquals(getUid(), attrChain.get(1).getUid());
- assertEquals("tag2", attrChain.get(1).getTag());
+ assertThat(attrChain).hasSize(2);
+ assertThat(attrChain.get(0).getUid()).isEqualTo(1234);
+ assertThat(attrChain.get(0).getTag()).isEqualTo("tag1");
+ assertThat(attrChain.get(1).getUid()).isEqualTo(getUid());
+ assertThat(attrChain.get(1).getTag()).isEqualTo("tag2");
- assertEquals(42, atom.getIntField());
- assertEquals(Long.MAX_VALUE, atom.getLongField());
- assertEquals(3.14f, atom.getFloatField());
- assertEquals("This is a basic test!", atom.getStringField());
- assertEquals(false, atom.getBooleanField());
- assertEquals(TestAtomReported.State.ON_VALUE, atom.getState().getNumber());
- List<Long> expIds = atom.getBytesField().getExperimentIdList();
- assertEquals(3, expIds.size());
- assertEquals(1L, (long) expIds.get(0));
- assertEquals(2L, (long) expIds.get(1));
- assertEquals(3L, (long) expIds.get(2));
+ assertThat(atom.getIntField()).isEqualTo(42);
+ assertThat(atom.getLongField()).isEqualTo(Long.MAX_VALUE);
+ assertThat(atom.getFloatField()).isEqualTo(3.14f);
+ assertThat(atom.getStringField()).isEqualTo("This is a basic test!");
+ assertThat(atom.getBooleanField()).isFalse();
+ assertThat(atom.getState().getNumber()).isEqualTo(TestAtomReported.State.ON_VALUE);
+ assertThat(atom.getBytesField().getExperimentIdList())
+ .containsExactly(1L, 2L, 3L).inOrder();
+
atom = data.get(1).getAtom().getTestAtomReported();
attrChain = atom.getAttributionNodeList();
- assertEquals(2, attrChain.size());
- assertEquals(9999, attrChain.get(0).getUid());
- assertEquals("tag9999", attrChain.get(0).getTag());
- assertEquals(getUid(), attrChain.get(1).getUid());
- assertEquals("", attrChain.get(1).getTag());
+ assertThat(attrChain).hasSize(2);
+ assertThat(attrChain.get(0).getUid()).isEqualTo(9999);
+ assertThat(attrChain.get(0).getTag()).isEqualTo("tag9999");
+ assertThat(attrChain.get(1).getUid()).isEqualTo(getUid());
+ assertThat(attrChain.get(1).getTag()).isEmpty();
- assertEquals(100, atom.getIntField());
- assertEquals(Long.MIN_VALUE, atom.getLongField());
- assertEquals(-2.5f, atom.getFloatField());
- assertEquals("Test null uid", atom.getStringField());
- assertEquals(true, atom.getBooleanField());
- assertEquals(TestAtomReported.State.UNKNOWN_VALUE, atom.getState().getNumber());
- expIds = atom.getBytesField().getExperimentIdList();
- assertEquals(3, expIds.size());
- assertEquals(1L, (long) expIds.get(0));
- assertEquals(2L, (long) expIds.get(1));
- assertEquals(3L, (long) expIds.get(2));
+ assertThat(atom.getIntField()).isEqualTo(100);
+ assertThat(atom.getLongField()).isEqualTo(Long.MIN_VALUE);
+ assertThat(atom.getFloatField()).isEqualTo(-2.5f);
+ assertThat(atom.getStringField()).isEqualTo("Test null uid");
+ assertThat(atom.getBooleanField()).isTrue();
+ assertThat(atom.getState().getNumber()).isEqualTo(TestAtomReported.State.UNKNOWN_VALUE);
+ assertThat(atom.getBytesField().getExperimentIdList())
+ .containsExactly(1L, 2L, 3L).inOrder();
atom = data.get(2).getAtom().getTestAtomReported();
attrChain = atom.getAttributionNodeList();
- assertEquals(1, attrChain.size());
- assertEquals(getUid(), attrChain.get(0).getUid());
- assertEquals("tag1", attrChain.get(0).getTag());
+ assertThat(attrChain).hasSize(1);
+ assertThat(attrChain.get(0).getUid()).isEqualTo(getUid());
+ assertThat(attrChain.get(0).getTag()).isEqualTo("tag1");
- assertEquals(-256, atom.getIntField());
- assertEquals(-1234567890L, atom.getLongField());
- assertEquals(42.01f, atom.getFloatField());
- assertEquals("Test non chained", atom.getStringField());
- assertEquals(true, atom.getBooleanField());
- assertEquals(TestAtomReported.State.OFF_VALUE, atom.getState().getNumber());
- expIds = atom.getBytesField().getExperimentIdList();
- assertEquals(3, expIds.size());
- assertEquals(1L, (long) expIds.get(0));
- assertEquals(2L, (long) expIds.get(1));
- assertEquals(3L, (long) expIds.get(2));
+ assertThat(atom.getIntField()).isEqualTo(-256);
+ assertThat(atom.getLongField()).isEqualTo(-1234567890L);
+ assertThat(atom.getFloatField()).isEqualTo(42.01f);
+ assertThat(atom.getStringField()).isEqualTo("Test non chained");
+ assertThat(atom.getBooleanField()).isTrue();
+ assertThat(atom.getState().getNumber()).isEqualTo(TestAtomReported.State.OFF_VALUE);
+ assertThat(atom.getBytesField().getExperimentIdList())
+ .containsExactly(1L, 2L, 3L).inOrder();
atom = data.get(3).getAtom().getTestAtomReported();
attrChain = atom.getAttributionNodeList();
- assertEquals(1, attrChain.size());
- assertEquals(getUid(), attrChain.get(0).getUid());
- assertEquals("", attrChain.get(0).getTag());
+ assertThat(attrChain).hasSize(1);
+ assertThat(attrChain.get(0).getUid()).isEqualTo(getUid());
+ assertThat(attrChain.get(0).getTag()).isEmpty();
- assertEquals(0, atom.getIntField());
- assertEquals(0L, atom.getLongField());
- assertEquals(0f, atom.getFloatField());
- assertEquals("", atom.getStringField());
- assertEquals(true, atom.getBooleanField());
- assertEquals(TestAtomReported.State.OFF_VALUE, atom.getState().getNumber());
- expIds = atom.getBytesField().getExperimentIdList();
- assertEquals(0, expIds.size());
+ assertThat(atom.getIntField()).isEqualTo(0);
+ assertThat(atom.getLongField()).isEqualTo(0L);
+ assertThat(atom.getFloatField()).isEqualTo(0f);
+ assertThat(atom.getStringField()).isEmpty();
+ assertThat(atom.getBooleanField()).isTrue();
+ assertThat(atom.getState().getNumber()).isEqualTo(TestAtomReported.State.OFF_VALUE);
+ assertThat(atom.getBytesField().getExperimentIdList()).isEmpty();
}
}
diff --git a/hostsidetests/statsd/src/android/cts/statsd/metadata/MetadataTests.java b/hostsidetests/statsd/src/android/cts/statsd/metadata/MetadataTests.java
index e92e7b2..4c0e4e6 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/metadata/MetadataTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/metadata/MetadataTests.java
@@ -15,6 +15,8 @@
*/
package android.cts.statsd.metadata;
+import static com.google.common.truth.Truth.assertWithMessage;
+
import android.cts.statsd.atom.AtomTestCase;
import com.android.internal.os.StatsdConfigProto;
@@ -61,13 +63,14 @@
for (ConfigStats stats: report.getConfigStatsList()) {
if (stats.getId() == CONFIG_ID && stats.getUid() == getHostUid()) {
if(!stats.hasDeletionTimeSec()) {
- assertTrue("Found multiple active CTS configs!", foundActiveConfig == false);
+ assertWithMessage("Found multiple active CTS configs!")
+ .that(foundActiveConfig).isFalse();
foundActiveConfig = true;
creationTime = stats.getCreationTimeSec();
}
}
}
- assertTrue("Did not find an active CTS config", foundActiveConfig);
+ assertWithMessage("Did not find an active CTS config").that(foundActiveConfig).isTrue();
while(System.currentTimeMillis() - startTime < 8_000) {
Thread.sleep(10);
@@ -81,26 +84,29 @@
if (stats.getId() == CONFIG_ID && stats.getUid() == getHostUid()) {
// Original config should be TTL'd
if (stats.getCreationTimeSec() == creationTime) {
- assertTrue("Config should have TTL'd but is still active",
- stats.hasDeletionTimeSec());
- assertTrue("Config deletion time should be about " + TTL_TIME_SEC +
- " after creation",
- Math.abs(stats.getDeletionTimeSec() - expectedTime) <= 2);
+ assertWithMessage("Config should have TTL'd but is still active")
+ .that(stats.hasDeletionTimeSec()).isTrue();
+ assertWithMessage(
+ "Config deletion time should be about %s after creation", TTL_TIME_SEC
+ ).that(Math.abs(stats.getDeletionTimeSec() - expectedTime)).isAtMost(2);
}
// There should still be one active config, that is marked as reset.
if(!stats.hasDeletionTimeSec()) {
- assertTrue("Found multiple active CTS configs!", foundActiveConfig == false);
+ assertWithMessage("Found multiple active CTS configs!")
+ .that(foundActiveConfig).isFalse();
foundActiveConfig = true;
creationTime = stats.getCreationTimeSec();
- assertTrue("Active config after TTL should be marked as reset",
- stats.hasResetTimeSec());
- assertEquals("Reset time and creation time should be equal for TTl'd configs",
- stats.getResetTimeSec(), stats.getCreationTimeSec());
- assertTrue("Reset config should be created when the original config TTL'd",
- Math.abs(stats.getCreationTimeSec() - expectedTime) <= 2);
+ assertWithMessage("Active config after TTL should be marked as reset")
+ .that(stats.hasResetTimeSec()).isTrue();
+ assertWithMessage("Reset and creation time should be equal for TTl'd configs")
+ .that(stats.getResetTimeSec()).isEqualTo(stats.getCreationTimeSec());
+ assertWithMessage(
+ "Reset config should be created when the original config TTL'd"
+ ).that(Math.abs(stats.getCreationTimeSec() - expectedTime)).isAtMost(2);
}
}
}
- assertTrue("Did not find an active CTS config after the TTL", foundActiveConfig);
+ assertWithMessage("Did not find an active CTS config after the TTL")
+ .that(foundActiveConfig).isTrue();
}
}
diff --git a/hostsidetests/statsd/src/android/cts/statsd/metric/CountMetricsTests.java b/hostsidetests/statsd/src/android/cts/statsd/metric/CountMetricsTests.java
index 70777ef..4eeb74c 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/metric/CountMetricsTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/metric/CountMetricsTests.java
@@ -15,6 +15,9 @@
*/
package android.cts.statsd.metric;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
import android.cts.statsd.atom.DeviceAtomTestCase;
import com.android.internal.os.StatsdConfigProto;
@@ -53,13 +56,13 @@
StatsLogReport metricReport = getStatsLogReport();
LogUtil.CLog.d("Got the following stats log report: \n" + metricReport.toString());
- assertEquals(MetricsUtils.COUNT_METRIC_ID, metricReport.getMetricId());
- assertTrue(metricReport.hasCountMetrics());
+ assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.COUNT_METRIC_ID);
+ assertThat(metricReport.hasCountMetrics()).isTrue();
StatsLogReport.CountMetricDataWrapper countData = metricReport.getCountMetrics();
- assertTrue(countData.getDataCount() > 0);
- assertEquals(2, countData.getData(0).getBucketInfo(0).getCount());
+ assertThat(countData.getDataCount()).isGreaterThan(0);
+ assertThat(countData.getData(0).getBucketInfo(0).getCount()).isEqualTo(2);
}
public void testEventCountWithCondition() throws Exception {
if (statsdDisabled()) {
@@ -112,13 +115,13 @@
Thread.sleep(2000); // Wait for the metrics to propagate to statsd.
StatsLogReport metricReport = getStatsLogReport();
- assertEquals(MetricsUtils.COUNT_METRIC_ID, metricReport.getMetricId());
- assertTrue(metricReport.hasCountMetrics());
+ assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.COUNT_METRIC_ID);
+ assertThat(metricReport.hasCountMetrics()).isTrue();
StatsLogReport.CountMetricDataWrapper countData = metricReport.getCountMetrics();
- assertTrue(countData.getDataCount() > 0);
- assertEquals(1, countData.getData(0).getBucketInfo(0).getCount());
+ assertThat(countData.getDataCount()).isGreaterThan(0);
+ assertThat(countData.getData(0).getBucketInfo(0).getCount()).isEqualTo(1);
}
public void testEventCountWithConditionAndActivation() throws Exception {
@@ -236,16 +239,16 @@
Thread.sleep(2000);
StatsLogReport metricReport = getStatsLogReport();
- assertEquals(MetricsUtils.COUNT_METRIC_ID, metricReport.getMetricId());
+ assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.COUNT_METRIC_ID);
LogUtil.CLog.d("Received the following data: " + metricReport.toString());
- assertTrue(metricReport.hasCountMetrics());
- assertFalse(metricReport.getIsActive());
+ assertThat(metricReport.hasCountMetrics()).isTrue();
+ assertThat(metricReport.getIsActive()).isFalse();
StatsLogReport.CountMetricDataWrapper countData = metricReport.getCountMetrics();
- assertEquals(1, countData.getDataCount());
- assertEquals(2, countData.getData(0).getBucketInfoCount());
- assertEquals(2, countData.getData(0).getBucketInfo(0).getCount());
- assertEquals(1, countData.getData(0).getBucketInfo(1).getCount());
+ assertThat(countData.getDataCount()).isEqualTo(1);
+ assertThat(countData.getData(0).getBucketInfoCount()).isEqualTo(2);
+ assertThat(countData.getData(0).getBucketInfo(0).getCount()).isEqualTo(2);
+ assertThat(countData.getData(0).getBucketInfo(1).getCount()).isEqualTo(1);
}
public void testPartialBucketCountMetric() throws Exception {
@@ -273,17 +276,14 @@
ConfigMetricsReportList reports = getReportList();
LogUtil.CLog.d("Got following report list: " + reports.toString());
- assertEquals("Expected 2 reports, got " + reports.getReportsCount(),
- 2, reports.getReportsCount());
+ assertThat(reports.getReportsCount()).isEqualTo(2);
boolean inOrder = reports.getReports(0).getCurrentReportWallClockNanos() <
reports.getReports(1).getCurrentReportWallClockNanos();
// Only 1 metric, so there should only be 1 StatsLogReport.
for (ConfigMetricsReport report : reports.getReportsList()) {
- assertEquals("Expected 1 StatsLogReport in each ConfigMetricsReport",
- 1, report.getMetricsCount());
- assertEquals("Expected 1 CountMetricData in each report",
- 1, report.getMetrics(0).getCountMetrics().getDataCount());
+ assertThat(report.getMetricsCount()).isEqualTo(1);
+ assertThat(report.getMetrics(0).getCountMetrics().getDataCount()).isEqualTo(1);
}
CountMetricData data1 =
reports.getReports(inOrder? 0 : 1).getMetrics(0).getCountMetrics().getData(0);
@@ -291,19 +291,20 @@
reports.getReports(inOrder? 1 : 0).getMetrics(0).getCountMetrics().getData(0);
// Data1 should have only 1 bucket, and it should be a partial bucket.
// The count should be 1.
- assertEquals("First report should only have 1 bucket", 1, data1.getBucketInfoCount());
+ assertThat(data1.getBucketInfoCount()).isEqualTo(1);
CountBucketInfo bucketInfo = data1.getBucketInfo(0);
- assertEquals("First report should have a count of 1", 1, bucketInfo.getCount());
- assertTrue("First report's bucket should be less than 1 day",
- bucketInfo.getEndBucketElapsedNanos() <
- (bucketInfo.getStartBucketElapsedNanos() + 1_000_000_000L * 60L * 60L * 24L));
+ assertThat(bucketInfo.getCount()).isEqualTo(1);
+ assertWithMessage("First report's bucket should be less than 1 day")
+ .that(bucketInfo.getEndBucketElapsedNanos())
+ .isLessThan(bucketInfo.getStartBucketElapsedNanos() +
+ 1_000_000_000L * 60L * 60L * 24L);
//Second report should have a count of 2.
- assertTrue("Second report should have at most 2 buckets", data2.getBucketInfoCount() < 3);
+ assertThat(data2.getBucketInfoCount()).isAtMost(2);
int totalCount = 0;
for (CountBucketInfo bucket : data2.getBucketInfoList()) {
totalCount += bucket.getCount();
}
- assertEquals("Second report should have a count of 2", 2, totalCount);
+ assertThat(totalCount).isEqualTo(2);
}
}
diff --git a/hostsidetests/statsd/src/android/cts/statsd/metric/GaugeMetricsTests.java b/hostsidetests/statsd/src/android/cts/statsd/metric/GaugeMetricsTests.java
index 4bd5a2a..16c3baf 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/metric/GaugeMetricsTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/metric/GaugeMetricsTests.java
@@ -122,30 +122,30 @@
StatsLogReport metricReport = getStatsLogReport();
LogUtil.CLog.d("Got the following gauge metric data: " + metricReport.toString());
- assertEquals(MetricsUtils.GAUGE_METRIC_ID, metricReport.getMetricId());
- assertTrue(metricReport.hasGaugeMetrics());
+ assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.GAUGE_METRIC_ID);
+ assertThat(metricReport.hasGaugeMetrics()).isTrue();
StatsLogReport.GaugeMetricDataWrapper gaugeData = metricReport.getGaugeMetrics();
- assertEquals(gaugeData.getDataCount(), 1);
+ assertThat(gaugeData.getDataCount()).isEqualTo(1);
int bucketCount = gaugeData.getData(0).getBucketInfoCount();
GaugeMetricData data = gaugeData.getData(0);
- assertTrue(bucketCount > 2);
+ assertThat(bucketCount).isGreaterThan(2);
MetricsUtils.assertBucketTimePresent(data.getBucketInfo(0));
- assertEquals(data.getBucketInfo(0).getAtomCount(), 1);
- assertEquals(data.getBucketInfo(0).getAtom(0).getAppBreadcrumbReported().getLabel(), 0);
- assertEquals(data.getBucketInfo(0).getAtom(0).getAppBreadcrumbReported().getState(),
- AppBreadcrumbReported.State.START);
+ assertThat(data.getBucketInfo(0).getAtomCount()).isEqualTo(1);
+ assertThat(data.getBucketInfo(0).getAtom(0).getAppBreadcrumbReported().getLabel())
+ .isEqualTo(0);
+ assertThat(data.getBucketInfo(0).getAtom(0).getAppBreadcrumbReported().getState())
+ .isEqualTo(AppBreadcrumbReported.State.START);
MetricsUtils.assertBucketTimePresent(data.getBucketInfo(1));
- assertEquals(data.getBucketInfo(1).getAtomCount(), 1);
+ assertThat(data.getBucketInfo(1).getAtomCount()).isEqualTo(1);
MetricsUtils.assertBucketTimePresent(data.getBucketInfo(bucketCount-1));
- assertEquals(data.getBucketInfo(bucketCount - 1).getAtomCount(), 1);
- assertEquals(
- data.getBucketInfo(bucketCount - 1).getAtom(0).getAppBreadcrumbReported().getLabel(), 2);
- assertEquals(
- data.getBucketInfo(bucketCount - 1).getAtom(0).getAppBreadcrumbReported().getState(),
- AppBreadcrumbReported.State.STOP);
+ assertThat(data.getBucketInfo(bucketCount-1).getAtomCount()).isEqualTo(1);
+ assertThat(data.getBucketInfo(bucketCount-1).getAtom(0).getAppBreadcrumbReported().getLabel())
+ .isEqualTo(2);
+ assertThat(data.getBucketInfo(bucketCount-1).getAtom(0).getAppBreadcrumbReported().getState())
+ .isEqualTo(AppBreadcrumbReported.State.STOP);
}
public void testPulledGaugeMetricWithActivation() throws Exception {
@@ -197,8 +197,8 @@
StatsLogReport metricReport = getStatsLogReport();
LogUtil.CLog.d("Got the following gauge metric data: " + metricReport.toString());
- assertEquals(MetricsUtils.GAUGE_METRIC_ID, metricReport.getMetricId());
- assertFalse(metricReport.hasGaugeMetrics());
+ assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.GAUGE_METRIC_ID);
+ assertThat(metricReport.hasGaugeMetrics()).isFalse();
}
public void testPulledGaugeMetricWithConditionAndActivation() throws Exception {
diff --git a/hostsidetests/statsd/src/android/cts/statsd/metric/MetricActivationTests.java b/hostsidetests/statsd/src/android/cts/statsd/metric/MetricActivationTests.java
index 803dd2e..b976678 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/metric/MetricActivationTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/metric/MetricActivationTests.java
@@ -15,6 +15,8 @@
*/
package android.cts.statsd.metric;
+import static com.google.common.truth.Truth.assertThat;
+
import android.cts.statsd.atom.DeviceAtomTestCase;
import com.android.internal.os.StatsdConfigProto;
@@ -374,7 +376,7 @@
ConfigMetricsReportList reportList = getReportList();
List<ConfigMetricsReport> reports = getSortedConfigMetricsReports(reportList);
- assertEquals(3, reports.size());
+ assertThat(reports).hasSize(3);
// Report before restart.
ConfigMetricsReport report = reports.get(0);
@@ -510,7 +512,7 @@
ConfigMetricsReportList reportList = getReportList();
List<ConfigMetricsReport> reports = getSortedConfigMetricsReports(reportList);
- assertEquals(3, reports.size());
+ assertThat(reports).hasSize(3);
// Report before restart.
ConfigMetricsReport report = reports.get(0);
@@ -538,7 +540,7 @@
private void verifyMetrics(ConfigMetricsReport report, int metric1Count, int metric2Count,
int metric3Count) throws Exception {
- assertEquals(3, report.getMetricsCount());
+ assertThat(report.getMetricsCount()).isEqualTo(3);
verifyMetric(
report.getMetrics(0), // StatsLogReport
@@ -563,17 +565,14 @@
private void verifyMetric(StatsLogReport metricReport, long metricId, int metricMatcherLabel,
int dataCount) {
LogUtil.CLog.d("Got the following event metric data: " + metricReport.toString());
- assertEquals(metricId, metricReport.getMetricId());
- if (dataCount > 0) {
- assertTrue(metricReport.hasEventMetrics());
- } else {
- assertFalse(metricReport.hasEventMetrics());
- }
+ assertThat(metricReport.getMetricId()).isEqualTo(metricId);
+ assertThat(metricReport.hasEventMetrics()).isEqualTo(dataCount > 0);
+
StatsLogReport.EventMetricDataWrapper eventData = metricReport.getEventMetrics();
- assertEquals(dataCount, eventData.getDataCount());
+ assertThat(eventData.getDataCount()).isEqualTo(dataCount);
for (int i = 0; i < eventData.getDataCount(); i++) {
AppBreadcrumbReported atom = eventData.getData(i).getAtom().getAppBreadcrumbReported();
- assertEquals(metricMatcherLabel, atom.getLabel());
+ assertThat(atom.getLabel()).isEqualTo(metricMatcherLabel);
}
}
}
diff --git a/hostsidetests/statsd/src/android/cts/statsd/metric/MetricsUtils.java b/hostsidetests/statsd/src/android/cts/statsd/metric/MetricsUtils.java
index 2677634..7097587 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/metric/MetricsUtils.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/metric/MetricsUtils.java
@@ -15,6 +15,8 @@
*/
package android.cts.statsd.metric;
+import static com.google.common.truth.Truth.assertWithMessage;
+
import com.android.internal.os.StatsdConfigProto;
import com.android.internal.os.StatsdConfigProto.AtomMatcher;
import com.android.internal.os.StatsdConfigProto.EventActivation;
@@ -26,8 +28,6 @@
import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
-import static org.junit.Assert.assertTrue;
-
public class MetricsUtils {
public static final long COUNT_METRIC_ID = 3333;
public static final long DURATION_METRIC_ID = 4444;
@@ -156,7 +156,8 @@
endMillis != null && bucketInfo.hasField(endMillis)) {
found = true;
}
- assertTrue("Bucket info did not have either bucket num or start and end elapsed millis",
- found);
+ assertWithMessage(
+ "Bucket info did not have either bucket num or start and end elapsed millis"
+ ).that(found).isTrue();
}
}
diff --git a/hostsidetests/statsd/src/android/cts/statsd/metric/ValueMetricsTests.java b/hostsidetests/statsd/src/android/cts/statsd/metric/ValueMetricsTests.java
index 25c06e3..2b2eee7 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/metric/ValueMetricsTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/metric/ValueMetricsTests.java
@@ -94,22 +94,22 @@
StatsLogReport metricReport = getStatsLogReport();
LogUtil.CLog.d("Got the following value metric data: " + metricReport.toString());
- assertEquals(MetricsUtils.VALUE_METRIC_ID, metricReport.getMetricId());
- assertTrue(metricReport.hasValueMetrics());
+ assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.VALUE_METRIC_ID);
+ assertThat(metricReport.hasValueMetrics()).isTrue();
StatsLogReport.ValueMetricDataWrapper valueData = metricReport.getValueMetrics();
- assertEquals(1, valueData.getDataCount());
+ assertThat(valueData.getDataCount()).isEqualTo(1);
int bucketCount = valueData.getData(0).getBucketInfoCount();
- assertTrue(bucketCount > 1);
+ assertThat(bucketCount).isGreaterThan(1);
ValueMetricData data = valueData.getData(0);
int totalValue = 0;
for (ValueBucketInfo bucketInfo : data.getBucketInfoList()) {
MetricsUtils.assertBucketTimePresent(bucketInfo);
- assertEquals(1, bucketInfo.getValuesCount());
- assertEquals(0, bucketInfo.getValues(0).getIndex());
+ assertThat(bucketInfo.getValuesCount()).isEqualTo(1);
+ assertThat(bucketInfo.getValues(0).getIndex()).isEqualTo(0);
totalValue += (int) bucketInfo.getValues(0).getValueLong();
}
- assertEquals(8, totalValue);
+ assertThat(totalValue).isEqualTo(8);
}
// Test value metric with pulled atoms and across multiple buckets
@@ -172,24 +172,24 @@
StatsLogReport metricReport = getStatsLogReport();
LogUtil.CLog.d("Got the following value metric data: " + metricReport.toString());
- assertEquals(MetricsUtils.VALUE_METRIC_ID, metricReport.getMetricId());
- assertTrue(metricReport.hasValueMetrics());
+ assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.VALUE_METRIC_ID);
+ assertThat(metricReport.hasValueMetrics()).isTrue();
StatsLogReport.ValueMetricDataWrapper valueData = metricReport.getValueMetrics();
- assertEquals(valueData.getDataCount(), 1);
+ assertThat(valueData.getDataCount()).isEqualTo(1);
int bucketCount = valueData.getData(0).getBucketInfoCount();
// should have at least 2 buckets
- assertTrue(bucketCount >= 2);
+ assertThat(bucketCount).isAtLeast(2);
ValueMetricData data = valueData.getData(0);
int totalValue = 0;
for (ValueBucketInfo bucketInfo : data.getBucketInfoList()) {
MetricsUtils.assertBucketTimePresent(bucketInfo);
- assertEquals(1, bucketInfo.getValuesCount());
- assertEquals(0, bucketInfo.getValues(0).getIndex());
+ assertThat(bucketInfo.getValuesCount()).isEqualTo(1);
+ assertThat(bucketInfo.getValues(0).getIndex()).isEqualTo(0);
totalValue += (int) bucketInfo.getValues(0).getValueLong();
}
// At most we lose one full min bucket
- assertTrue(totalValue > (130_000 - 60_000));
+ assertThat(totalValue).isGreaterThan(130_000 - 60_000);
}
// Test value metric with pulled atoms and across multiple buckets
@@ -256,24 +256,24 @@
StatsLogReport metricReport = getStatsLogReport();
LogUtil.CLog.d("Got the following value metric data: " + metricReport.toString());
- assertEquals(MetricsUtils.VALUE_METRIC_ID, metricReport.getMetricId());
- assertTrue(metricReport.hasValueMetrics());
+ assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.VALUE_METRIC_ID);
+ assertThat(metricReport.hasValueMetrics()).isTrue();
StatsLogReport.ValueMetricDataWrapper valueData = metricReport.getValueMetrics();
- assertEquals(valueData.getDataCount(), 1);
+ assertThat(valueData.getDataCount()).isEqualTo(1);
int bucketCount = valueData.getData(0).getBucketInfoCount();
// should have at least 2 buckets
- assertTrue(bucketCount >= 2);
+ assertThat(bucketCount).isAtLeast(2);
ValueMetricData data = valueData.getData(0);
int totalValue = 0;
for (ValueBucketInfo bucketInfo : data.getBucketInfoList()) {
MetricsUtils.assertBucketTimePresent(bucketInfo);
- assertEquals(1, bucketInfo.getValuesCount());
- assertEquals(0, bucketInfo.getValues(0).getIndex());
+ assertThat(bucketInfo.getValuesCount()).isEqualTo(1);
+ assertThat(bucketInfo.getValues(0).getIndex()).isEqualTo(0);
totalValue += (int) bucketInfo.getValues(0).getValueLong();
}
// At most we lose one full min bucket
- assertTrue(totalValue > (GAP_INTERVAL*NUM_EVENTS - 60_000));
+ assertThat((long) totalValue).isGreaterThan(GAP_INTERVAL * NUM_EVENTS - 60_000);
}
// Test value metric with pulled atoms and across multiple buckets
@@ -329,8 +329,8 @@
StatsLogReport metricReport = getStatsLogReport();
LogUtil.CLog.d("Got the following value metric data: " + metricReport.toString());
- assertEquals(MetricsUtils.VALUE_METRIC_ID, metricReport.getMetricId());
- assertFalse(metricReport.hasValueMetrics());
+ assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.VALUE_METRIC_ID);
+ assertThat(metricReport.hasValueMetrics()).isFalse();
}
public void testValueMetricWithConditionAndActivation() throws Exception {
diff --git a/hostsidetests/statsd/src/android/cts/statsd/subscriber/ShellSubscriberTest.java b/hostsidetests/statsd/src/android/cts/statsd/subscriber/ShellSubscriberTest.java
index f8aa3b7..f239f0e 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/subscriber/ShellSubscriberTest.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/subscriber/ShellSubscriberTest.java
@@ -15,6 +15,8 @@
*/
package android.cts.statsd.subscriber;
+import static com.google.common.truth.Truth.assertThat;
+
import com.android.compatibility.common.util.CpuFeatures;
import com.android.internal.os.StatsdConfigProto;
import com.android.os.AtomsProto;
@@ -79,7 +81,7 @@
startSubscription(config, receiver, 10);
byte[] output = receiver.getOutput();
// There should be at lease some data returned.
- assertTrue(output.length > sizetBytes);
+ assertThat(output.length).isGreaterThan(sizetBytes);
int atomCount = 0;
int i = 0;
@@ -98,8 +100,8 @@
ShellDataProto.ShellData data =
ShellDataProto.ShellData.parseFrom(
Arrays.copyOfRange(output, i + sizetBytes, i + sizetBytes + len));
- assertTrue(data.getAtomCount() > 0);
- assertTrue(data.getAtom(0).hasSystemUptime());
+ assertThat(data.getAtomCount()).isGreaterThan(0);
+ assertThat(data.getAtom(0).hasSystemUptime()).isTrue();
atomCount++;
LogUtil.CLog.d("Received " + data.toString());
} catch (InvalidProtocolBufferException e) {
@@ -108,7 +110,7 @@
i += (sizetBytes + len);
}
- assertTrue(atomCount > 0);
+ assertThat(atomCount).isGreaterThan(0);
}
private void startSubscription(ShellConfig.ShellSubscription config,
diff --git a/hostsidetests/statsd/src/android/cts/statsd/uidmap/UidMapTests.java b/hostsidetests/statsd/src/android/cts/statsd/uidmap/UidMapTests.java
index bf599d3..dab7f0f 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/uidmap/UidMapTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/uidmap/UidMapTests.java
@@ -15,7 +15,7 @@
*/
package android.cts.statsd.uidmap;
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
import android.cts.statsd.atom.DeviceAtomTestCase;
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
@@ -40,15 +40,15 @@
createAndUploadConfig(AtomsProto.Atom.UID_PROCESS_STATE_CHANGED_FIELD_NUMBER);
ConfigMetricsReportList reports = getReportList();
- assertTrue(reports.getReportsCount() > 0);
+ assertThat(reports.getReportsCount()).isGreaterThan(0);
for (ConfigMetricsReport report : reports.getReportsList()) {
UidMapping uidmap = report.getUidMap();
- assertTrue(uidmap.getSnapshotsCount() > 0);
+ assertThat(uidmap.getSnapshotsCount()).isGreaterThan(0);
for (PackageInfoSnapshot snapshot : uidmap.getSnapshotsList()) {
// There must be at least one element in each snapshot (at least one package is
// installed).
- assertTrue(snapshot.getPackageInfoCount() > 0);
+ assertThat(snapshot.getPackageInfoCount()).isGreaterThan(0);
}
}
}
@@ -81,7 +81,7 @@
Thread.sleep(WAIT_TIME_SHORT);
ConfigMetricsReportList reports = getReportList();
- assertTrue(reports.getReportsCount() > 0);
+ assertThat(reports.getReportsCount()).isGreaterThan(0);
boolean found = false;
int uid = getUid();
@@ -91,7 +91,7 @@
found = true;
}
}
- assertTrue(found);
+ assertThat(found).isTrue();
}
// We check that a re-installation gives a change event (similar to an app upgrade).
@@ -108,7 +108,7 @@
Thread.sleep(WAIT_TIME_SHORT);
ConfigMetricsReportList reports = getReportList();
- assertTrue(reports.getReportsCount() > 0);
+ assertThat(reports.getReportsCount()).isGreaterThan(0);
boolean found = false;
int uid = getUid();
@@ -118,7 +118,7 @@
found = true;
}
}
- assertTrue(found);
+ assertThat(found).isTrue();
}
public void testChangeFromUninstall() throws Exception {
@@ -134,7 +134,7 @@
Thread.sleep(WAIT_TIME_SHORT);
ConfigMetricsReportList reports = getReportList();
- assertTrue(reports.getReportsCount() > 0);
+ assertThat(reports.getReportsCount()).isGreaterThan(0);
boolean found = false;
for (ConfigMetricsReport report : reports.getReportsList()) {
@@ -143,6 +143,6 @@
found = true;
}
}
- assertTrue(found);
+ assertThat(found).isTrue();
}
}
diff --git a/hostsidetests/statsd/src/android/cts/statsd/validation/BatteryStatsValidationTests.java b/hostsidetests/statsd/src/android/cts/statsd/validation/BatteryStatsValidationTests.java
index 7b95933..dc3e23b 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/validation/BatteryStatsValidationTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/validation/BatteryStatsValidationTests.java
@@ -15,8 +15,8 @@
*/
package android.cts.statsd.validation;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import android.cts.statsd.atom.DeviceAtomTestCase;
import android.os.BatteryStatsProto;
@@ -70,11 +70,11 @@
BatteryStatsProto batterystatsProto = getBatteryStatsProto();
List<CountMetricData> countMetricData = getCountMetricDataList();
- assertEquals(1, countMetricData.size());
- assertEquals(1, countMetricData.get(0).getBucketInfoCount());
- assertTrue(countMetricData.get(0).getBucketInfo(0).getCount() >= 2);
- assertEquals(batterystatsProto.getSystem().getMisc().getNumConnectivityChanges(),
- countMetricData.get(0).getBucketInfo(0).getCount());
+ assertThat(countMetricData).hasSize(1);
+ assertThat(countMetricData.get(0).getBucketInfoCount()).isEqualTo(1);
+ assertThat(countMetricData.get(0).getBucketInfo(0).getCount()).isAtLeast(2L);
+ assertThat(countMetricData.get(0).getBucketInfo(0).getCount()).isEqualTo(
+ (long) batterystatsProto.getSystem().getMisc().getNumConnectivityChanges());
}
public void testPowerUse() throws Exception {
@@ -104,19 +104,16 @@
// Extract statsd data
Atom atom = atomList.get(0);
long statsdPowerNas = atom.getDeviceCalculatedPowerUse().getComputedPowerNanoAmpSecs();
- assertTrue("Statsd: Non-positive power value.", statsdPowerNas > 0);
+ assertThat(statsdPowerNas).isGreaterThan(0L);
// Extract BatteryStats data
double bsPowerNas = batterystatsProto.getSystem().getPowerUseSummary().getComputedPowerMah()
* 1_000_000L * 3600L; /* mAh to nAs */
- assertTrue("BatteryStats: Non-positive power value.", bsPowerNas > 0);
+ assertThat(bsPowerNas).isGreaterThan(0d);
- assertTrue(
- String.format("Statsd (%d) < Batterystats (%f)", statsdPowerNas, bsPowerNas),
- statsdPowerNas > ALLOWED_FRACTIONAL_DIFFERENCE * bsPowerNas);
- assertTrue(
- String.format("Batterystats (%f) < Statsd (%d)", bsPowerNas, statsdPowerNas),
- bsPowerNas > ALLOWED_FRACTIONAL_DIFFERENCE * statsdPowerNas);
+ assertThat((double) statsdPowerNas)
+ .isGreaterThan(ALLOWED_FRACTIONAL_DIFFERENCE * bsPowerNas);
+ assertThat(bsPowerNas).isGreaterThan(ALLOWED_FRACTIONAL_DIFFERENCE * statsdPowerNas);
}
public void testPowerBlameUid() throws Exception {
@@ -151,13 +148,15 @@
for (Atom atom : atomList) {
DeviceCalculatedPowerBlameUid item = atom.getDeviceCalculatedPowerBlameUid();
if (item.getUid() == uid) {
- assertFalse("Found multiple power values for uid " + uid, uidFound);
+ assertWithMessage("Found multiple power values for uid %s", uid)
+ .that(uidFound).isFalse();
uidFound = true;
statsdUidPowerNas = item.getPowerNanoAmpSecs();
}
}
- assertTrue("Statsd: No power value for uid " + uid, uidFound);
- assertTrue("Statsd: Non-positive power value for uid " + uid, statsdUidPowerNas > 0);
+ assertWithMessage("Statsd: No power value for uid %s", uid).that(uidFound).isTrue();
+ assertWithMessage("Statsd: Non-positive power value for uid %s", uid)
+ .that(statsdUidPowerNas).isGreaterThan(0L);
// Extract batterystats data
double bsUidPowerNas = -1;
@@ -169,15 +168,13 @@
* 1_000_000L * 3600L; /* mAh to nAs */;
}
}
- assertTrue("Batterystats: No power value for uid " + uid, hadUid);
- assertTrue("BatteryStats: Non-positive power value for uid " + uid, bsUidPowerNas > 0);
+ assertWithMessage("Batterystats: No power value for uid %s", uid).that(hadUid).isTrue();
+ assertWithMessage("BatteryStats: Non-positive power value for uid %s", uid)
+ .that(bsUidPowerNas).isGreaterThan(0d);
- assertTrue(
- String.format("Statsd (%d) < Batterystats (%f).", statsdUidPowerNas, bsUidPowerNas),
- statsdUidPowerNas > ALLOWED_FRACTIONAL_DIFFERENCE * bsUidPowerNas);
- assertTrue(
- String.format("Batterystats (%f) < Statsd (%d).", bsUidPowerNas, statsdUidPowerNas),
- bsUidPowerNas > ALLOWED_FRACTIONAL_DIFFERENCE * statsdUidPowerNas);
+ assertThat((double) statsdUidPowerNas)
+ .isGreaterThan(ALLOWED_FRACTIONAL_DIFFERENCE * bsUidPowerNas);
+ assertThat(bsUidPowerNas).isGreaterThan(ALLOWED_FRACTIONAL_DIFFERENCE * statsdUidPowerNas);
}
public void testServiceStartCount() throws Exception {
@@ -192,16 +189,17 @@
BatteryStatsProto batterystatsProto = getBatteryStatsProto();
List<CountMetricData> countMetricData = getCountMetricDataList();
- assertTrue(countMetricData.size() > 0);
+ assertThat(countMetricData).isNotEmpty();
int uid = getUid();
long countFromStatsd = 0;
for (CountMetricData data : countMetricData) {
List<DimensionsValue> dims = data.getDimensionLeafValuesInWhatList();
if (dims.get(0).getValueInt() == uid) {
- assertEquals(DEVICE_SIDE_TEST_PACKAGE, dims.get(1).getValueStr());
- assertEquals(dims.get(2).getValueStr(), DEVICE_SIDE_TEST_FOREGROUND_SERVICE_NAME);
+ assertThat(dims.get(1).getValueStr()).isEqualTo(DEVICE_SIDE_TEST_PACKAGE);
+ assertThat(dims.get(2).getValueStr())
+ .isEqualTo(DEVICE_SIDE_TEST_FOREGROUND_SERVICE_NAME);
countFromStatsd = data.getBucketInfo(0).getCount();
- assertTrue(countFromStatsd > 0);
+ assertThat(countFromStatsd).isGreaterThan(0L);
}
}
long countFromBS = 0;
@@ -212,16 +210,16 @@
for (Service svc : pkg.getServicesList()) {
if (svc.getName().equals(DEVICE_SIDE_TEST_FOREGROUND_SERVICE_NAME)) {
countFromBS = svc.getStartCount();
- assertTrue(countFromBS > 0);
+ assertThat(countFromBS).isGreaterThan(0L);
}
}
}
}
}
}
- assertTrue(countFromStatsd > 0);
- assertTrue(countFromBS > 0);
- assertEquals(countFromBS, countFromStatsd);
+ assertThat(countFromStatsd).isGreaterThan(0L);
+ assertThat(countFromBS).isGreaterThan(0L);
+ assertThat(countFromBS).isEqualTo(countFromStatsd);
}
public void testServiceLaunchCount() throws Exception {
@@ -236,16 +234,17 @@
BatteryStatsProto batterystatsProto = getBatteryStatsProto();
List<CountMetricData> countMetricData = getCountMetricDataList();
- assertTrue(countMetricData.size() > 0);
+ assertThat(countMetricData).isNotEmpty();
int uid = getUid();
long countFromStatsd = 0;
for (CountMetricData data : countMetricData) {
List<DimensionsValue> dims = data.getDimensionLeafValuesInWhatList();
if (dims.get(0).getValueInt() == uid) {
- assertEquals(DEVICE_SIDE_TEST_PACKAGE, dims.get(1).getValueStr());
- assertEquals(DEVICE_SIDE_TEST_FOREGROUND_SERVICE_NAME, dims.get(2).getValueStr());
+ assertThat(dims.get(1).getValueStr()).isEqualTo(DEVICE_SIDE_TEST_PACKAGE);
+ assertThat(dims.get(2).getValueStr())
+ .isEqualTo(DEVICE_SIDE_TEST_FOREGROUND_SERVICE_NAME);
countFromStatsd = data.getBucketInfo(0).getCount();
- assertTrue(countFromStatsd > 0);
+ assertThat(countFromStatsd).isGreaterThan(0L);
}
}
long countFromBS = 0;
@@ -256,15 +255,15 @@
for (Service svc : pkg.getServicesList()) {
if (svc.getName().equals(DEVICE_SIDE_TEST_FOREGROUND_SERVICE_NAME)) {
countFromBS = svc.getLaunchCount();
- assertTrue(countFromBS > 0);
+ assertThat(countFromBS).isGreaterThan(0L);
}
}
}
}
}
}
- assertTrue(countFromStatsd > 0);
- assertTrue(countFromBS > 0);
- assertEquals(countFromBS, countFromStatsd);
+ assertThat(countFromStatsd).isGreaterThan(0L);
+ assertThat(countFromBS).isGreaterThan(0L);
+ assertThat(countFromBS).isEqualTo(countFromStatsd);
}
}
diff --git a/hostsidetests/statsd/src/android/cts/statsd/validation/ProcStatsValidationTests.java b/hostsidetests/statsd/src/android/cts/statsd/validation/ProcStatsValidationTests.java
index cfa0a37..9cb198c 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/validation/ProcStatsValidationTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/validation/ProcStatsValidationTests.java
@@ -15,7 +15,7 @@
*/
package android.cts.statsd.validation;
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
import android.cts.statsd.atom.ProcStateTestCase;
import android.service.procstats.ProcessState;
@@ -113,8 +113,8 @@
}
}
// Verify that duration is within 1% difference
- assertTrue(Math.abs(durationInTopStatsd - durationInTopProcStats) / durationInTopProcStats
- < 0.1);
+ assertThat(Math.abs(durationInTopStatsd - durationInTopProcStats) / durationInTopProcStats)
+ .isLessThan(0.01);
}
public void testProcessStateCachedEmptyDuration() throws Exception {
@@ -178,8 +178,8 @@
}
}
// Verify that duration is within 1% difference
- assertTrue(Math.abs(durationInStatsd - durationInProcStats) / durationInProcStats
- < 0.1);
+ assertThat(Math.abs(durationInStatsd - durationInProcStats) / durationInProcStats)
+ .isLessThan(0.01);
}
public void testProcessStatePssValue() throws Exception {
@@ -240,8 +240,8 @@
}
}
}
- assertTrue(valueInProcStats > 0);
- assertTrue(valueInStatsd == valueInProcStats);
+ assertThat(valueInProcStats).isGreaterThan(0d);
+ assertThat(valueInStatsd).isWithin(1e-10).of(valueInProcStats);
}
public void testProcessStateByPulling() throws Exception {
@@ -342,11 +342,11 @@
}
LogUtil.CLog.d("avg pss from procstats is " + pssAvgProcstats);
- assertTrue(durationStatsd > 0);
- assertTrue(durationStatsd == durationProcstats);
- assertTrue(pssAvgStatsd == pssAvgProcstats);
- assertTrue(ussAvgStatsd == ussAvgProcstats);
- assertTrue(rssAvgStatsd == rssAvgProcstats);
+ assertThat(durationStatsd).isGreaterThan(0L);
+ assertThat(durationStatsd).isEqualTo(durationProcstats);
+ assertThat(pssAvgStatsd).isEqualTo(pssAvgProcstats);
+ assertThat(ussAvgStatsd).isEqualTo(ussAvgProcstats);
+ assertThat(rssAvgStatsd).isEqualTo(rssAvgProcstats);
}
public void testProcStatsPkgProcStats() throws Exception {
@@ -390,8 +390,11 @@
Thread.sleep(WAIT_TIME_SHORT);
List<Atom> statsdData = getGaugeMetricDataList();
- assertTrue(statsdData.size() > 0);
- assertTrue(statsdData.get(0).getProcStatsPkgProc().getProcStatsSection().getAvailablePagesList().size() > 0);
+ assertThat(statsdData).isNotEmpty();
+ assertThat(
+ statsdData.get(0).getProcStatsPkgProc().getProcStatsSection()
+ .getAvailablePagesList()
+ ).isNotEmpty();
List<ProcessStatsPackageProto> processStatsPackageProtoList = getAllProcStatsProto();
@@ -421,8 +424,8 @@
}
}
}
- assertTrue(pkg.getServiceStatsCount() == 0);
- assertTrue(pkg.getAssociationStatsCount() == 0);
+ assertThat(pkg.getServiceStatsCount()).isEqualTo(0L);
+ assertThat(pkg.getAssociationStatsCount()).isEqualTo(0L);
}
}
@@ -453,14 +456,14 @@
serviceStatsCount += pkg.getServiceStatsCount();
associationStatsCount += pkg.getAssociationStatsCount();
}
- assertTrue(serviceStatsCount > 0);
- assertTrue(associationStatsCount > 0);
+ assertThat(serviceStatsCount).isGreaterThan(0);
+ assertThat(associationStatsCount).isGreaterThan(0);
LogUtil.CLog.d("avg pss from procstats is " + pssAvgProcstats);
- assertTrue(durationStatsd > 0);
- assertTrue(durationStatsd == durationProcstats);
- assertTrue(pssAvgStatsd == pssAvgProcstats);
- assertTrue(ussAvgStatsd == ussAvgProcstats);
- assertTrue(rssAvgStatsd == rssAvgProcstats);
+ assertThat(durationStatsd).isGreaterThan(0L);
+ assertThat(durationStatsd).isEqualTo(durationProcstats);
+ assertThat(pssAvgStatsd).isEqualTo(pssAvgProcstats);
+ assertThat(ussAvgStatsd).isEqualTo(ussAvgProcstats);
+ assertThat(rssAvgStatsd).isEqualTo(rssAvgProcstats);
}
}
diff --git a/hostsidetests/statsd/src/android/cts/statsd/validation/ValidationTests.java b/hostsidetests/statsd/src/android/cts/statsd/validation/ValidationTests.java
index ae40111..d4815b4 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/validation/ValidationTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/validation/ValidationTests.java
@@ -15,7 +15,8 @@
*/
package android.cts.statsd.validation;
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import android.cts.statsd.atom.DeviceAtomTestCase;
import android.os.BatteryPluggedStateEnum;
@@ -48,6 +49,8 @@
import com.android.os.StatsLog.StatsLogReport;
import com.android.tradefed.log.LogUtil.CLog;
+import com.google.common.collect.Range;
+
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
@@ -118,10 +121,8 @@
for (EventMetricData event : data) {
String tag = event.getAtom().getWakelockStateChanged().getTag();
WakeLockLevelEnum type = event.getAtom().getWakelockStateChanged().getType();
- assertTrue("Expected tag: " + EXPECTED_TAG + ", but got tag: " + tag,
- tag.equals(EXPECTED_TAG));
- assertTrue("Expected wakelock level: " + EXPECTED_LEVEL + ", but got level: " + type,
- type == EXPECTED_LEVEL);
+ assertThat(tag).isEqualTo(EXPECTED_TAG);
+ assertThat(type).isEqualTo(EXPECTED_LEVEL);
}
//=================== verify that batterystats is correct ===============//
@@ -129,12 +130,10 @@
android.os.TimerProto wl =
getBatteryStatsPartialWakelock(batterystatsProto, uid, EXPECTED_TAG);
- assertNotNull(wl);
- assertTrue(wl.getDurationMs() > 0);
- assertTrue(wl.getMaxDurationMs() >= 400);
- assertTrue(wl.getMaxDurationMs() < 700);
- assertTrue(wl.getTotalDurationMs() >= 400);
- assertTrue(wl.getTotalDurationMs() < 700);
+ assertThat(wl).isNotNull();
+ assertThat(wl.getDurationMs()).isGreaterThan(0L);
+ assertThat(wl.getMaxDurationMs()).isIn(Range.closedOpen(400L, 700L));
+ assertThat(wl.getTotalDurationMs()).isIn(Range.closedOpen(400L, 700L));
setAodState(aodState); // restores AOD to initial state.
}
@@ -174,36 +173,34 @@
// Get the batterystats wakelock time and make sure it's reasonable.
android.os.TimerProto bsWakelock =
getBatteryStatsPartialWakelock(batterystatsProto, EXPECTED_UID, EXPECTED_TAG);
- assertNotNull("Could not find any partial wakelocks with uid " + EXPECTED_UID +
- " and tag " + EXPECTED_TAG + " in BatteryStats", bsWakelock);
+ assertWithMessage(
+ "No partial wakelocks with uid %s and tag %s in BatteryStats",
+ EXPECTED_UID, EXPECTED_TAG
+ ).that(bsWakelock).isNotNull();
long bsDurationMs = bsWakelock.getTotalDurationMs();
- assertTrue("Wakelock in batterystats with uid " + EXPECTED_UID + " and tag "
- + EXPECTED_TAG + "was too short. Expected " + MIN_DURATION +
- ", received " + bsDurationMs, bsDurationMs >= MIN_DURATION);
- assertTrue("Wakelock in batterystats with uid " + EXPECTED_UID + " and tag "
- + EXPECTED_TAG + "was too long. Expected " + MAX_DURATION +
- ", received " + bsDurationMs, bsDurationMs <= MAX_DURATION);
+ assertWithMessage(
+ "Wakelock in batterystats with uid %s and tag %s was too short or too long",
+ EXPECTED_UID, EXPECTED_TAG
+ ).that(bsDurationMs).isIn(Range.closed((long) MIN_DURATION, (long) MAX_DURATION));
// Get the statsd wakelock time and make sure it's reasonable.
- assertTrue("Could not find any wakelocks with uid " + EXPECTED_UID + " in statsd",
- statsdWakelockData.containsKey(EXPECTED_UID));
- assertTrue("Did not find any wakelocks with tag " + EXPECTED_TAG + " in statsd",
- statsdWakelockData.get(EXPECTED_UID).containsKey(EXPECTED_TAG_HASH));
+ assertWithMessage("No wakelocks with uid %s in statsd", EXPECTED_UID)
+ .that(statsdWakelockData).containsKey(EXPECTED_UID);
+ assertWithMessage("No wakelocks with tag %s in statsd", EXPECTED_TAG)
+ .that(statsdWakelockData.get(EXPECTED_UID)).containsKey(EXPECTED_TAG_HASH);
long statsdDurationMs = statsdWakelockData.get(EXPECTED_UID)
.get(EXPECTED_TAG_HASH) / 1_000_000;
- assertTrue("Wakelock in statsd with uid " + EXPECTED_UID + " and tag " + EXPECTED_TAG +
- "was too short. Expected " + MIN_DURATION + ", received " +
- statsdDurationMs,
- statsdDurationMs >= MIN_DURATION);
- assertTrue("Wakelock in statsd with uid " + EXPECTED_UID + " and tag " + EXPECTED_TAG +
- "was too long. Expected " + MAX_DURATION + ", received " + statsdDurationMs,
- statsdDurationMs <= MAX_DURATION);
+ assertWithMessage(
+ "Wakelock in statsd with uid %s and tag %s was too short or too long",
+ EXPECTED_UID, EXPECTED_TAG
+ ).that(statsdDurationMs).isIn(Range.closed((long) MIN_DURATION, (long) MAX_DURATION));
// Compare batterystats with statsd.
long difference = Math.abs(statsdDurationMs - bsDurationMs);
- assertTrue("For uid=" + EXPECTED_UID + " tag=" + EXPECTED_TAG + " had " +
- "BatteryStats=" + bsDurationMs + "ms but statsd=" + statsdDurationMs + "ms",
- difference <= Math.max(bsDurationMs / 10, 10L));
+ assertWithMessage(
+ "For uid=%s tag=%s had BatteryStats=%s ms but statsd=%s ms",
+ EXPECTED_UID, EXPECTED_TAG, bsDurationMs, statsdDurationMs
+ ).that(difference).isAtMost(Math.max(bsDurationMs / 10, 10L));
setAodState(aodState); // restores AOD to initial state.
}
@@ -304,7 +301,7 @@
for (DurationMetricData data : report.getDurationMetrics().getDataList()) {
// Gets tag and uid.
List<DimensionsValue> dims = data.getDimensionLeafValuesInWhatList();
- assertTrue("Expected 2 dimensions, received " + dims.size(), dims.size() == 2);
+ assertThat(dims).hasSize(2);
boolean hasTag = false;
long tag = 0;
int uid = -1;
@@ -317,8 +314,8 @@
tag = dim.getValueStrHash();
}
}
- assertTrue("Did not receive a tag for the wakelock", hasTag);
- assertTrue("Did not receive a uid for the wakelock", uid != -1);
+ assertWithMessage("Did not receive a tag for the wakelock").that(hasTag).isTrue();
+ assertWithMessage("Did not receive a uid for the wakelock").that(uid).isNotEqualTo(-1);
// Gets duration.
for (DurationBucketInfo bucketInfo : data.getBucketInfoList()) {
diff --git a/hostsidetests/theme/OWNERS b/hostsidetests/theme/OWNERS
index ae2c27b..b43358c 100644
--- a/hostsidetests/theme/OWNERS
+++ b/hostsidetests/theme/OWNERS
@@ -1,2 +1,3 @@
# Bug component: 25700
-alanv@google.com
\ No newline at end of file
+alanv@google.com
+aurimas@google.com
\ No newline at end of file
diff --git a/hostsidetests/theme/assets/29/140dpi.zip b/hostsidetests/theme/assets/29/140dpi.zip
index cb385f1..48de32b 100644
--- a/hostsidetests/theme/assets/29/140dpi.zip
+++ b/hostsidetests/theme/assets/29/140dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/29/180dpi.zip b/hostsidetests/theme/assets/29/180dpi.zip
index b034a7f..4def986 100644
--- a/hostsidetests/theme/assets/29/180dpi.zip
+++ b/hostsidetests/theme/assets/29/180dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/29/200dpi.zip b/hostsidetests/theme/assets/29/200dpi.zip
index 9a0ca5e..fe2495e 100644
--- a/hostsidetests/theme/assets/29/200dpi.zip
+++ b/hostsidetests/theme/assets/29/200dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/29/220dpi.zip b/hostsidetests/theme/assets/29/220dpi.zip
index b65a035..a2c2ea2 100644
--- a/hostsidetests/theme/assets/29/220dpi.zip
+++ b/hostsidetests/theme/assets/29/220dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/29/260dpi.zip b/hostsidetests/theme/assets/29/260dpi.zip
index 9cdefe7..74890a2 100644
--- a/hostsidetests/theme/assets/29/260dpi.zip
+++ b/hostsidetests/theme/assets/29/260dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/29/280dpi.zip b/hostsidetests/theme/assets/29/280dpi.zip
index 2e39de5..83fd290 100644
--- a/hostsidetests/theme/assets/29/280dpi.zip
+++ b/hostsidetests/theme/assets/29/280dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/29/300dpi.zip b/hostsidetests/theme/assets/29/300dpi.zip
index fba9c6c..25334d8 100644
--- a/hostsidetests/theme/assets/29/300dpi.zip
+++ b/hostsidetests/theme/assets/29/300dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/29/340dpi.zip b/hostsidetests/theme/assets/29/340dpi.zip
index 72e6f8f..2092326 100644
--- a/hostsidetests/theme/assets/29/340dpi.zip
+++ b/hostsidetests/theme/assets/29/340dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/29/360dpi.zip b/hostsidetests/theme/assets/29/360dpi.zip
index 3970139..c13ad5c 100644
--- a/hostsidetests/theme/assets/29/360dpi.zip
+++ b/hostsidetests/theme/assets/29/360dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/29/400dpi.zip b/hostsidetests/theme/assets/29/400dpi.zip
index 510eb94d..1df1a95 100644
--- a/hostsidetests/theme/assets/29/400dpi.zip
+++ b/hostsidetests/theme/assets/29/400dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/29/420dpi.zip b/hostsidetests/theme/assets/29/420dpi.zip
index a457bda..d58d418 100644
--- a/hostsidetests/theme/assets/29/420dpi.zip
+++ b/hostsidetests/theme/assets/29/420dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/29/440dpi.zip b/hostsidetests/theme/assets/29/440dpi.zip
index 07355d1..535102f 100644
--- a/hostsidetests/theme/assets/29/440dpi.zip
+++ b/hostsidetests/theme/assets/29/440dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/29/560dpi.zip b/hostsidetests/theme/assets/29/560dpi.zip
index 6a85ad8..20f1c7b 100644
--- a/hostsidetests/theme/assets/29/560dpi.zip
+++ b/hostsidetests/theme/assets/29/560dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/29/hdpi.zip b/hostsidetests/theme/assets/29/hdpi.zip
index e1a534a..582989d 100644
--- a/hostsidetests/theme/assets/29/hdpi.zip
+++ b/hostsidetests/theme/assets/29/hdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/29/ldpi.zip b/hostsidetests/theme/assets/29/ldpi.zip
index 5475608..3035146 100644
--- a/hostsidetests/theme/assets/29/ldpi.zip
+++ b/hostsidetests/theme/assets/29/ldpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/29/mdpi.zip b/hostsidetests/theme/assets/29/mdpi.zip
index 67c5c03..a831e7b 100644
--- a/hostsidetests/theme/assets/29/mdpi.zip
+++ b/hostsidetests/theme/assets/29/mdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/29/tvdpi.zip b/hostsidetests/theme/assets/29/tvdpi.zip
index 60f5afe..bd4bf75 100644
--- a/hostsidetests/theme/assets/29/tvdpi.zip
+++ b/hostsidetests/theme/assets/29/tvdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/29/xhdpi.zip b/hostsidetests/theme/assets/29/xhdpi.zip
index d2895a1..0dd72e2 100644
--- a/hostsidetests/theme/assets/29/xhdpi.zip
+++ b/hostsidetests/theme/assets/29/xhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/29/xxhdpi.zip b/hostsidetests/theme/assets/29/xxhdpi.zip
index 637649a..64fc846 100644
--- a/hostsidetests/theme/assets/29/xxhdpi.zip
+++ b/hostsidetests/theme/assets/29/xxhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/29/xxxhdpi.zip b/hostsidetests/theme/assets/29/xxxhdpi.zip
index 9ab19b1..87beb9a 100644
--- a/hostsidetests/theme/assets/29/xxxhdpi.zip
+++ b/hostsidetests/theme/assets/29/xxxhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java b/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java
index f31b081..2fcbb5b 100644
--- a/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java
+++ b/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java
@@ -295,7 +295,7 @@
private static int getDensityForDevice(ITestDevice device) throws DeviceNotAvailableException {
final String densityProp;
- if (device.getSerialNumber().startsWith("emulator-")) {
+ if (isEmulator(device)) {
densityProp = DENSITY_PROP_EMULATOR;
} else {
densityProp = DENSITY_PROP_DEVICE;
@@ -308,4 +308,9 @@
|| hardwareTypeString.contains("android.hardware.type.television")
|| hardwareTypeString.contains("android.hardware.type.automotive");
}
+
+ private static boolean isEmulator(ITestDevice device) {
+ // Expecting something like "emulator-XXXX" or "EMULATORXXXX".
+ return device.getSerialNumber().toLowerCase().startsWith("emulator");
+ }
}
diff --git a/hostsidetests/trustedvoice/OWNERS b/hostsidetests/trustedvoice/OWNERS
new file mode 100644
index 0000000..f96bd04
--- /dev/null
+++ b/hostsidetests/trustedvoice/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 174421
+sharmneha@google.com
\ No newline at end of file
diff --git a/hostsidetests/wifibroadcasts/src/android/wifibroadcasts/cts/WifiBroadcastsHostJUnit4Test.java b/hostsidetests/wifibroadcasts/src/android/wifibroadcasts/cts/WifiBroadcastsHostJUnit4Test.java
index e01d682..8dcc4d3 100644
--- a/hostsidetests/wifibroadcasts/src/android/wifibroadcasts/cts/WifiBroadcastsHostJUnit4Test.java
+++ b/hostsidetests/wifibroadcasts/src/android/wifibroadcasts/cts/WifiBroadcastsHostJUnit4Test.java
@@ -71,9 +71,14 @@
private static final String PROHIBITED_STRING = "UNEXPECTED WIFI BROADCAST RECEIVED";
/**
- * The maximim number of times to attempt a ping
+ * The maximum total number of times to attempt a ping.
*/
- private static final int MAXIMUM_PING_TRIES = 30;
+ private static final int MAXIMUM_PING_TRIES = 180;
+
+ /**
+ * The maximum number of times to attempt a ping before toggling wifi.
+ */
+ private static final int MAXIMUM_PING_TRIES_PER_CONNECTION = 60;
/**
* Name for wifi feature test
@@ -121,6 +126,10 @@
CommandResult pingCommandResult = null;
boolean pingSucceeded = false;
for (int tries = 0; tries < MAXIMUM_PING_TRIES; tries++) {
+ if (tries > 0 && tries % MAXIMUM_PING_TRIES_PER_CONNECTION == 0) {
+ // if we have been trying for a while, toggle wifi off and then on.
+ device.executeShellCommand("svc wifi disable; sleep 1; svc wifi enable; sleep 3");
+ }
// We don't require internet connectivity, just a configured address
pingCommandResult = device.executeShellV2Command("ping -c 4 -W 2 -t 1 8.8.8.8");
pingResult = String.join("/", pingCommandResult.getStdout(),
diff --git a/libs/deviceutillegacy/Android.bp b/libs/deviceutillegacy/Android.bp
index bbdd399..220b2ab 100644
--- a/libs/deviceutillegacy/Android.bp
+++ b/libs/deviceutillegacy/Android.bp
@@ -13,23 +13,6 @@
// limitations under the License.
java_library_static {
- name: "ctsdeviceutillegacy",
-
- static_libs: [
- "compatibility-device-util",
- "junit",
- ],
-
- libs: ["android.test.base.stubs"],
-
- srcs: ["src/**/*.java"],
-
- sdk_version: "test_current",
-
-}
-
-// A variant of ctsdeviceutillegacy that depends on androidx.test instead of android.support.test
-java_library_static {
name: "ctsdeviceutillegacy-axt",
static_libs: [
diff --git a/libs/deviceutillegacy/src/android/webkit/cts/WebViewOnUiThread.java b/libs/deviceutillegacy/src/android/webkit/cts/WebViewOnUiThread.java
index 06425c1..6e64204 100644
--- a/libs/deviceutillegacy/src/android/webkit/cts/WebViewOnUiThread.java
+++ b/libs/deviceutillegacy/src/android/webkit/cts/WebViewOnUiThread.java
@@ -490,9 +490,7 @@
}
public void evaluateJavascript(final String script, final ValueCallback<String> result) {
- WebkitUtils.onMainThreadSync(() -> {
- mWebView.evaluateJavascript(script, result);
- });
+ WebkitUtils.onMainThread(() -> mWebView.evaluateJavascript(script, result));
}
public void saveWebArchive(final String basename, final boolean autoname,
diff --git a/libs/install/Android.bp b/libs/install/Android.bp
new file mode 100644
index 0000000..5ca15ae
--- /dev/null
+++ b/libs/install/Android.bp
@@ -0,0 +1,97 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+android_test_helper_app {
+ name: "TestAppAv1",
+ manifest: "testapp/Av1.xml",
+ sdk_version: "current",
+ srcs: ["testapp/src/**/*.java"],
+ resource_dirs: ["testapp/res_v1"],
+}
+
+android_test_helper_app {
+ name: "TestAppAv2",
+ manifest: "testapp/Av2.xml",
+ sdk_version: "current",
+ srcs: ["testapp/src/**/*.java"],
+ resource_dirs: ["testapp/res_v2"],
+}
+
+android_test_helper_app {
+ name: "TestAppAv3",
+ manifest: "testapp/Av3.xml",
+ sdk_version: "current",
+ srcs: ["testapp/src/**/*.java"],
+ resource_dirs: ["testapp/res_v3"],
+}
+
+android_test_helper_app {
+ name: "TestAppACrashingV2",
+ manifest: "testapp/ACrashingV2.xml",
+ sdk_version: "current",
+ srcs: ["testapp/src/**/*.java"],
+ resource_dirs: ["testapp/res_v2"],
+}
+
+android_test_helper_app {
+ name: "TestAppBv1",
+ manifest: "testapp/Bv1.xml",
+ sdk_version: "current",
+ srcs: ["testapp/src/**/*.java"],
+ resource_dirs: ["testapp/res_v1"],
+}
+
+android_test_helper_app {
+ name: "TestAppBv2",
+ manifest: "testapp/Bv2.xml",
+ sdk_version: "current",
+ srcs: ["testapp/src/**/*.java"],
+ resource_dirs: ["testapp/res_v2"],
+}
+
+android_test_helper_app {
+ name: "TestAppASplitV1",
+ manifest: "testapp/Av1.xml",
+ sdk_version: "current",
+ srcs: ["testapp/src/**/*.java"],
+ resource_dirs: ["testapp/res_v1"],
+ package_splits: ["anydpi"],
+}
+
+android_test_helper_app {
+ name: "TestAppASplitV2",
+ manifest: "testapp/Av2.xml",
+ sdk_version: "current",
+ srcs: ["testapp/src/**/*.java"],
+ resource_dirs: ["testapp/res_v2"],
+ package_splits: ["anydpi"],
+}
+
+java_library {
+ name: "cts-install-lib",
+ srcs: ["src/**/*.java"],
+ static_libs: ["androidx.test.rules", "truth-prebuilt"],
+ sdk_version: "test_current",
+ java_resources: [
+ ":TestAppAv1",
+ ":TestAppAv2",
+ ":TestAppAv3",
+ ":TestAppBv1",
+ ":TestAppBv2",
+ ":TestAppACrashingV2",
+ ":TestAppASplitV1",
+ ":TestAppASplitV2",
+ ],
+}
diff --git a/libs/install/TEST_MAPPING b/libs/install/TEST_MAPPING
new file mode 100644
index 0000000..9d0ffe9
--- /dev/null
+++ b/libs/install/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "imports": [
+ {
+ "path": "cts/tests/tests/util"
+ }
+ ]
+}
diff --git a/libs/install/src/com/android/cts/install/lib/Install.java b/libs/install/src/com/android/cts/install/lib/Install.java
new file mode 100644
index 0000000..11fbffe
--- /dev/null
+++ b/libs/install/src/com/android/cts/install/lib/Install.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.install.lib;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Intent;
+import android.content.pm.PackageInstaller;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Builder class for installing test apps and creating install sessions.
+ */
+public class Install {
+ // The collection of apps to be installed with parameters inherited from parent Install object.
+ private final TestApp[] mTestApps;
+ // The collection of apps to be installed with parameters independent of parent Install object.
+ private final Install[] mChildInstalls;
+ // Indicates whether Install represents a multiPackage install.
+ private final boolean mIsMultiPackage;
+ // PackageInstaller.Session parameters.
+ private boolean mIsStaged = false;
+ private boolean mIsDowngrade = false;
+ private boolean mEnableRollback = false;
+ private int mSessionMode = PackageInstaller.SessionParams.MODE_FULL_INSTALL;
+ private int mInstallFlags = 0;
+
+ private Install(boolean isMultiPackage, TestApp... testApps) {
+ mIsMultiPackage = isMultiPackage;
+ mTestApps = testApps;
+ mChildInstalls = new Install[0];
+ }
+
+ private Install(boolean isMultiPackage, Install... installs) {
+ mIsMultiPackage = isMultiPackage;
+ mTestApps = new TestApp[0];
+ mChildInstalls = installs;
+ }
+
+ /**
+ * Creates an Install builder to install a single package.
+ */
+ public static Install single(TestApp testApp) {
+ return new Install(false, testApp);
+ }
+
+ /**
+ * Creates an Install builder to install using multiPackage.
+ */
+ public static Install multi(TestApp... testApps) {
+ return new Install(true, testApps);
+ }
+
+ /**
+ * Creates an Install builder from separate Install builders. The newly created builder
+ * will be responsible for building the parent session, while each one of the other builders
+ * will be responsible for building one of the child sessions.
+ *
+ * <p>Modifications to the parent install are not propagated to the child installs,
+ * and vice versa. This gives more control over a multi install session,
+ * e.g. can setStaged on a subset of the child sessions or setStaged on a child session but
+ * not on the parent session.
+ *
+ * <p>It's encouraged to use {@link #multi} that receives {@link TestApp}s
+ * instead of {@link Install}s. This variation of {@link #multi} should be used only if it's
+ * necessary to modify parameters in a subset of the installed sessions.
+ */
+ public static Install multi(Install... installs) {
+ for (Install childInstall : installs) {
+ assertThat(childInstall.isMultiPackage()).isFalse();
+ }
+ Install install = new Install(true, installs);
+ return install;
+ }
+
+ /**
+ * Makes the install a staged install.
+ */
+ public Install setStaged() {
+ mIsStaged = true;
+ return this;
+ }
+
+ /**
+ * Marks the install as a downgrade.
+ */
+ public Install setRequestDowngrade() {
+ mIsDowngrade = true;
+ return this;
+ }
+
+ /**
+ * Enables rollback for the install.
+ */
+ public Install setEnableRollback() {
+ mEnableRollback = true;
+ return this;
+ }
+
+ /**
+ * Sets the session mode {@link PackageInstaller.SessionParams#MODE_INHERIT_EXISTING}.
+ * If it's not set, then the default session mode is
+ * {@link PackageInstaller.SessionParams#MODE_FULL_INSTALL}
+ */
+ public Install setSessionMode(int sessionMode) {
+ mSessionMode = sessionMode;
+ return this;
+ }
+
+ /**
+ * Sets the session params.
+ */
+ public Install addInstallFlags(int installFlags) {
+ mInstallFlags |= installFlags;
+ return this;
+ }
+
+ /**
+ * Commits the install.
+ *
+ * @return the session id of the install session, if the session is successful.
+ * @throws AssertionError if the install doesn't succeed.
+ */
+ public int commit() throws IOException, InterruptedException {
+ int sessionId = createSession();
+ PackageInstaller.Session session = InstallUtils.openPackageInstallerSession(sessionId);
+ session.commit(LocalIntentSender.getIntentSender());
+ Intent result = LocalIntentSender.getIntentSenderResult();
+ int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+ PackageInstaller.STATUS_FAILURE);
+ if (status == -1) {
+ throw new AssertionError("PENDING USER ACTION");
+ } else if (status > 0) {
+ String message = result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
+ throw new AssertionError(message == null ? "UNKNOWN FAILURE" : message);
+ }
+
+ if (mIsStaged) {
+ InstallUtils.waitForSessionReady(sessionId);
+ }
+ return sessionId;
+ }
+
+ /**
+ * Kicks off an install flow by creating an install session
+ * and, in the case of a multiPackage install, child install sessions.
+ *
+ * @return the session id of the install session, if the session is successful.
+ */
+ public int createSession() throws IOException {
+ int sessionId;
+ if (isMultiPackage()) {
+ PackageInstaller.Session session;
+ sessionId = createEmptyInstallSession(/*multiPackage*/ true, /*isApex*/false);
+ session = InstallUtils.openPackageInstallerSession(sessionId);
+ for (Install subInstall : mChildInstalls) {
+ session.addChildSessionId(subInstall.createSession());
+ }
+ for (TestApp testApp : mTestApps) {
+ session.addChildSessionId(createSingleInstallSession(testApp));
+ }
+ } else {
+ assert mTestApps.length == 1;
+ sessionId = createSingleInstallSession(mTestApps[0]);
+ }
+ return sessionId;
+ }
+
+ /**
+ * Creates an empty install session with appropriate install params set.
+ *
+ * @return the session id of the newly created session
+ */
+ private int createEmptyInstallSession(boolean multiPackage, boolean isApex)
+ throws IOException {
+ PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(mSessionMode);
+ if (multiPackage) {
+ params.setMultiPackage();
+ }
+ if (isApex) {
+ params.setInstallAsApex();
+ }
+ if (mIsStaged) {
+ params.setStaged();
+ }
+ params.setRequestDowngrade(mIsDowngrade);
+ params.setEnableRollback(mEnableRollback);
+ if (mInstallFlags != 0) {
+ InstallUtils.mutateInstallFlags(params, mInstallFlags);
+ }
+ return InstallUtils.getPackageInstaller().createSession(params);
+ }
+
+ /**
+ * Creates an install session for the given test app.
+ *
+ * @return the session id of the newly created session.
+ */
+ private int createSingleInstallSession(TestApp app) throws IOException {
+ int sessionId = createEmptyInstallSession(/*multiPackage*/false, app.isApex());
+ PackageInstaller.Session session = InstallUtils.getPackageInstaller()
+ .openSession(sessionId);
+
+ ClassLoader loader = TestApp.class.getClassLoader();
+ for (String resourceName : app.getResourceNames()) {
+ try (OutputStream os = session.openWrite(resourceName, 0, -1);
+ InputStream is = loader.getResourceAsStream(resourceName);) {
+ if (is == null) {
+ throw new IOException("Resource " + resourceName + " not found");
+ }
+ byte[] buffer = new byte[4096];
+ int n;
+ while ((n = is.read(buffer)) >= 0) {
+ os.write(buffer, 0, n);
+ }
+ }
+ }
+ session.close();
+ return sessionId;
+ }
+
+ private boolean isMultiPackage() {
+ return mIsMultiPackage;
+ }
+
+}
diff --git a/libs/install/src/com/android/cts/install/lib/InstallUtils.java b/libs/install/src/com/android/cts/install/lib/InstallUtils.java
new file mode 100644
index 0000000..6969398
--- /dev/null
+++ b/libs/install/src/com/android/cts/install/lib/InstallUtils.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.install.lib;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.os.Handler;
+import android.os.HandlerThread;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.google.common.annotations.VisibleForTesting;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+/**
+ * Utilities to facilitate installation in tests.
+ */
+public class InstallUtils {
+ /**
+ * Adopts the given shell permissions.
+ */
+ public static void adoptShellPermissionIdentity(String... permissions) {
+ InstrumentationRegistry
+ .getInstrumentation()
+ .getUiAutomation()
+ .adoptShellPermissionIdentity(permissions);
+ }
+
+ /**
+ * Drops all shell permissions.
+ */
+ public static void dropShellPermissionIdentity() {
+ InstrumentationRegistry
+ .getInstrumentation()
+ .getUiAutomation()
+ .dropShellPermissionIdentity();
+ }
+ /**
+ * Returns the version of the given package installed on device.
+ * Returns -1 if the package is not currently installed.
+ */
+ public static long getInstalledVersion(String packageName) {
+ Context context = InstrumentationRegistry.getContext();
+ PackageManager pm = context.getPackageManager();
+ try {
+ PackageInfo info = pm.getPackageInfo(packageName, PackageManager.MATCH_APEX);
+ return info.getLongVersionCode();
+ } catch (PackageManager.NameNotFoundException e) {
+ return -1;
+ }
+ }
+
+ /**
+ * Waits for the given session to be marked as ready.
+ * Throws an assertion if the session fails.
+ */
+ public static void waitForSessionReady(int sessionId) {
+ BlockingQueue<PackageInstaller.SessionInfo> sessionStatus = new LinkedBlockingQueue<>();
+ BroadcastReceiver sessionUpdatedReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ PackageInstaller.SessionInfo info =
+ intent.getParcelableExtra(PackageInstaller.EXTRA_SESSION);
+ if (info != null && info.getSessionId() == sessionId) {
+ if (info.isStagedSessionReady() || info.isStagedSessionFailed()) {
+ try {
+ sessionStatus.put(info);
+ } catch (InterruptedException e) {
+ throw new AssertionError(e);
+ }
+ }
+ }
+ }
+ };
+ IntentFilter sessionUpdatedFilter =
+ new IntentFilter(PackageInstaller.ACTION_SESSION_UPDATED);
+
+ Context context = InstrumentationRegistry.getContext();
+ context.registerReceiver(sessionUpdatedReceiver, sessionUpdatedFilter);
+
+ PackageInstaller installer = getPackageInstaller();
+ PackageInstaller.SessionInfo info = installer.getSessionInfo(sessionId);
+
+ try {
+ if (info.isStagedSessionReady() || info.isStagedSessionFailed()) {
+ sessionStatus.put(info);
+ }
+
+ info = sessionStatus.take();
+ context.unregisterReceiver(sessionUpdatedReceiver);
+ if (info.isStagedSessionFailed()) {
+ throw new AssertionError(info.getStagedSessionErrorMessage());
+ }
+ } catch (InterruptedException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ /**
+ * Returns the info for the given package name.
+ */
+ public static PackageInfo getPackageInfo(String packageName) {
+ Context context = InstrumentationRegistry.getContext();
+ PackageManager pm = context.getPackageManager();
+ try {
+ return pm.getPackageInfo(packageName, PackageManager.MATCH_APEX);
+ } catch (PackageManager.NameNotFoundException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the PackageInstaller instance of the current {@code Context}
+ */
+ public static PackageInstaller getPackageInstaller() {
+ return InstrumentationRegistry.getContext().getPackageManager().getPackageInstaller();
+ }
+
+ /**
+ * Returns an existing session to actively perform work.
+ * {@see PackageInstaller#openSession}
+ */
+ public static PackageInstaller.Session openPackageInstallerSession(int sessionId)
+ throws IOException {
+ return getPackageInstaller().openSession(sessionId);
+ }
+
+ /**
+ * Asserts that {@code result} intent has a success status.
+ */
+ public static void assertStatusSuccess(Intent result) {
+ int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+ PackageInstaller.STATUS_FAILURE);
+ if (status == -1) {
+ throw new AssertionError("PENDING USER ACTION");
+ } else if (status > 0) {
+ String message = result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
+ throw new AssertionError(message == null ? "UNKNOWN FAILURE" : message);
+ }
+ }
+
+ /**
+ * Commits {@link Install} but expects to fail.
+ *
+ * @param expectedThrowableClass class or superclass of the expected throwable.
+ *
+ */
+ public static void commitExpectingFailure(Class expectedThrowableClass,
+ String expectedFailMessage, Install install) {
+ assertThrows(expectedThrowableClass, expectedFailMessage, () -> install.commit());
+ }
+
+ /**
+ * Mutates {@code installFlags} field of {@code params} by adding {@code
+ * additionalInstallFlags} to it.
+ */
+ @VisibleForTesting
+ public static void mutateInstallFlags(PackageInstaller.SessionParams params,
+ int additionalInstallFlags) {
+ final Class<?> clazz = params.getClass();
+ Field installFlagsField;
+ try {
+ installFlagsField = clazz.getDeclaredField("installFlags");
+ } catch (NoSuchFieldException e) {
+ throw new AssertionError("Unable to reflect over SessionParams.installFlags", e);
+ }
+
+ try {
+ int flags = installFlagsField.getInt(params);
+ flags |= additionalInstallFlags;
+ installFlagsField.setAccessible(true);
+ installFlagsField.setInt(params, flags);
+ } catch (IllegalAccessException e) {
+ throw new AssertionError("Unable to reflect over SessionParams.installFlags", e);
+ }
+ }
+
+ private static final String NO_RESPONSE = "NO RESPONSE";
+
+ /**
+ * Calls into the test app to process user data.
+ * Asserts if the user data could not be processed or was version
+ * incompatible with the previously processed user data.
+ */
+ public static void processUserData(String packageName) {
+ Intent intent = new Intent();
+ intent.setComponent(new ComponentName(packageName,
+ "com.android.cts.install.lib.testapp.ProcessUserData"));
+ Context context = InstrumentationRegistry.getContext();
+
+ HandlerThread handlerThread = new HandlerThread("RollbackTestHandlerThread");
+ handlerThread.start();
+
+ // It can sometimes take a while after rollback before the app will
+ // receive this broadcast, so try a few times in a loop.
+ String result = NO_RESPONSE;
+ for (int i = 0; result.equals(NO_RESPONSE) && i < 5; ++i) {
+ BlockingQueue<String> resultQueue = new LinkedBlockingQueue<>();
+ context.sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (getResultCode() == 1) {
+ resultQueue.add("OK");
+ } else {
+ // If the test app doesn't receive the broadcast or
+ // fails to set the result data, then getResultData
+ // here returns the initial NO_RESPONSE data passed to
+ // the sendOrderedBroadcast call.
+ resultQueue.add(getResultData());
+ }
+ }
+ }, new Handler(handlerThread.getLooper()), 0, NO_RESPONSE, null);
+
+ try {
+ result = resultQueue.take();
+ } catch (InterruptedException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ assertThat(result).isEqualTo("OK");
+ }
+
+ /**
+ * Checks whether the given package is installed on /system and was not updated.
+ */
+ static boolean isSystemAppWithoutUpdate(String packageName) {
+ PackageInfo pi = getPackageInfo(packageName);
+ if (pi == null) {
+ return false;
+ } else {
+ return ((pi.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0)
+ && ((pi.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0);
+ }
+ }
+
+ /**
+ * A functional interface representing an operation that takes no arguments,
+ * returns no arguments and might throw a {@link Throwable} of any kind.
+ */
+ @FunctionalInterface
+ private interface Operation {
+ /**
+ * This is the method that gets called for any object that implements this interface.
+ */
+ void run() throws Throwable;
+ }
+
+ /**
+ * Runs {@link Operation} and expects a {@link Throwable} of the given class to be thrown.
+ *
+ * @param expectedThrowableClass class or superclass of the expected throwable.
+ */
+ private static void assertThrows(Class expectedThrowableClass, String expectedFailMessage,
+ Operation operation) {
+ try {
+ operation.run();
+ } catch (Throwable expected) {
+ assertThat(expectedThrowableClass.isAssignableFrom(expected.getClass())).isTrue();
+ assertThat(expected.getMessage()).containsMatch(expectedFailMessage);
+ return;
+ }
+ fail("Operation was expected to fail!");
+ }
+}
diff --git a/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/LocalIntentSender.java b/libs/install/src/com/android/cts/install/lib/LocalIntentSender.java
similarity index 88%
rename from hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/LocalIntentSender.java
rename to libs/install/src/com/android/cts/install/lib/LocalIntentSender.java
index d4b3f48..cf2abe8 100644
--- a/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/LocalIntentSender.java
+++ b/libs/install/src/com/android/cts/install/lib/LocalIntentSender.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.tests.stagedinstall;
+package com.android.cts.install.lib;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
@@ -29,8 +29,13 @@
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
+/**
+ * Helper for making IntentSenders whose results are sent back to the test
+ * app.
+ */
public class LocalIntentSender extends BroadcastReceiver {
- private static final String TAG = "StagedInstallTest";
+ private static final String TAG = "cts.install.lib";
+
private static final BlockingQueue<Intent> sIntentSenderResults = new LinkedBlockingQueue<>();
@Override
@@ -42,7 +47,7 @@
/**
* Get a LocalIntentSender.
*/
- static IntentSender getIntentSender() {
+ public static IntentSender getIntentSender() {
Context context = InstrumentationRegistry.getContext();
Intent intent = new Intent(context, LocalIntentSender.class);
PendingIntent pending = PendingIntent.getBroadcast(context, 0, intent, 0);
@@ -52,7 +57,7 @@
/**
* Returns the most recent Intent sent by a LocalIntentSender.
*/
- static Intent getIntentSenderResult() throws InterruptedException {
+ public static Intent getIntentSenderResult() throws InterruptedException {
Intent intent = sIntentSenderResults.take();
Log.i(TAG, "Taking intent " + prettyPrint(intent));
return intent;
diff --git a/libs/install/src/com/android/cts/install/lib/TestApp.java b/libs/install/src/com/android/cts/install/lib/TestApp.java
new file mode 100644
index 0000000..5704887
--- /dev/null
+++ b/libs/install/src/com/android/cts/install/lib/TestApp.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.install.lib;
+
+import android.content.pm.VersionedPackage;
+
+/**
+ * Collection of dummy apps used in tests.
+ */
+public class TestApp {
+ public static final String A = "com.android.cts.install.lib.testapp.A";
+ public static final String B = "com.android.cts.install.lib.testapp.B";
+ public static final String Apex = "com.android.apex.cts.shim";
+ public static final String NotPreInstalledApex = "com.android.apex.cts.shim_not_pre_installed";
+
+ // Apk collection
+ public static final TestApp A1 = new TestApp("Av1", A, 1, /*isApex*/false,
+ "TestAppAv1.apk");
+ public static final TestApp A2 = new TestApp("Av2", A, 2, /*isApex*/false,
+ "TestAppAv2.apk");
+ public static final TestApp A3 = new TestApp("Av3", A, 3, /*isApex*/false,
+ "TestAppAv3.apk");
+ public static final TestApp ACrashing2 = new TestApp("ACrashingV2", A, 2, /*isApex*/false,
+ "TestAppACrashingV2.apk");
+ public static final TestApp ASplit1 = new TestApp("ASplitV1", A, 1, /*isApex*/false,
+ "TestAppASplitV1.apk", "TestAppASplitV1_anydpi.apk");
+ public static final TestApp ASplit2 = new TestApp("ASplitV2", A, 2, /*isApex*/false,
+ "TestAppASplitV2.apk", "TestAppASplitV2_anydpi.apk");
+
+ public static final TestApp B1 = new TestApp("Bv1", B, 1, /*isApex*/false,
+ "TestAppBv1.apk");
+ public static final TestApp B2 = new TestApp("Bv2", B, 2, /*isApex*/false,
+ "TestAppBv2.apk");
+
+ // Apex collection
+ public static final TestApp Apex1 =
+ new TestApp("Apex1", Apex, 1, /*isApex*/true,
+ "com.android.apex.cts.shim.v1.apex");
+ public static final TestApp Apex2 =
+ new TestApp("Apex2", Apex, 2, /*isApex*/true,
+ "com.android.apex.cts.shim.v2.apex");
+ public static final TestApp ApexWrongSha2 = new TestApp(
+ "ApexWrongSha2", Apex, 2, /*isApex*/true,
+ "com.android.apex.cts.shim.v2_wrong_sha.apex");
+ public static final TestApp Apex3 =
+ new TestApp("Apex3", Apex, 3, /*isApex*/true,
+ "com.android.apex.cts.shim.v3.apex");
+ public static final TestApp ApexNotPreInstalled =
+ new TestApp("ApexNotPreInstalled", NotPreInstalledApex, 3, /*isApex*/true,
+ "com.android.apex.cts.shim_not_pre_installed.apex");
+
+ private final String mName;
+ private final String mPackageName;
+ private final long mVersionCode;
+ private final String[] mResourceNames;
+ private final boolean mIsApex;
+
+ public TestApp(String name, String packageName, long versionCode, boolean isApex,
+ String... resourceNames) {
+ mName = name;
+ mPackageName = packageName;
+ mVersionCode = versionCode;
+ mResourceNames = resourceNames;
+ mIsApex = isApex;
+ }
+
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ public long getVersionCode() {
+ return mVersionCode;
+ }
+
+ public VersionedPackage getVersionedPackage() {
+ return new VersionedPackage(mPackageName, mVersionCode);
+ }
+
+ @Override
+ public String toString() {
+ return mName;
+ }
+
+ boolean isApex() {
+ return mIsApex;
+ }
+
+ String[] getResourceNames() {
+ return mResourceNames;
+ }
+}
diff --git a/libs/install/src/com/android/cts/install/lib/Uninstall.java b/libs/install/src/com/android/cts/install/lib/Uninstall.java
new file mode 100644
index 0000000..0444130
--- /dev/null
+++ b/libs/install/src/com/android/cts/install/lib/Uninstall.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.install.lib;
+
+import android.content.Context;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+
+import androidx.test.InstrumentationRegistry;
+
+/**
+ * Helper class for uninstalling test apps.
+ */
+public class Uninstall {
+ /**
+ * Uninstalls all of the given packages.
+ * Does nothing if the package is not installed or installed on /system
+ */
+ public static void packages(String... packageNames) throws InterruptedException {
+ for (String pkg : packageNames) {
+ uninstallSinglePackage(pkg);
+ }
+ }
+
+ private static void uninstallSinglePackage(String packageName) throws InterruptedException {
+ // No need to uninstall if the package isn't installed.
+ if (InstallUtils.getInstalledVersion(packageName) == -1
+ || InstallUtils.isSystemAppWithoutUpdate(packageName)) {
+ return;
+ }
+
+ Context context = InstrumentationRegistry.getContext();
+ PackageManager packageManager = context.getPackageManager();
+ PackageInstaller packageInstaller = packageManager.getPackageInstaller();
+ packageInstaller.uninstall(packageName, LocalIntentSender.getIntentSender());
+ InstallUtils.assertStatusSuccess(LocalIntentSender.getIntentSenderResult());
+ }
+}
diff --git a/libs/rollback/testapp/A2.xml b/libs/install/testapp/ACrashingV2.xml
similarity index 63%
copy from libs/rollback/testapp/A2.xml
copy to libs/install/testapp/ACrashingV2.xml
index d879a91..338a5b9 100644
--- a/libs/rollback/testapp/A2.xml
+++ b/libs/install/testapp/ACrashingV2.xml
@@ -15,18 +15,21 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.cts.rollback.lib.testapp.A"
+ package="com.android.cts.install.lib.testapp.A"
android:versionCode="2"
android:versionName="2.0" >
<uses-sdk android:minSdkVersion="19" />
- <application android:label="Rollback Test App A2">
- <activity android:name="com.android.cts.rollback.lib.testapp.MainActivity">
+ <application android:label="Test App A v2">
+ <receiver android:name="com.android.cts.install.lib.testapp.ProcessUserData"
+ android:exported="true" />
+ <activity android:name="com.android.cts.install.lib.testapp.CrashingMainActivity">
<intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT"/>
+ <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
diff --git a/libs/rollback/testapp/A1.xml b/libs/install/testapp/Av1.xml
similarity index 77%
copy from libs/rollback/testapp/A1.xml
copy to libs/install/testapp/Av1.xml
index 2cd1825..e9714fc 100644
--- a/libs/rollback/testapp/A1.xml
+++ b/libs/install/testapp/Av1.xml
@@ -15,15 +15,17 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.cts.rollback.lib.testapp.A"
+ package="com.android.cts.install.lib.testapp.A"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="19" />
- <application android:label="Rollback Test App A1">
- <activity android:name="com.android.cts.rollback.lib.testapp.MainActivity">
+ <application android:label="Test App A1">
+ <receiver android:name="com.android.cts.install.lib.testapp.ProcessUserData"
+ android:exported="true" />
+ <activity android:name="com.android.cts.install.lib.testapp.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
diff --git a/libs/rollback/testapp/A2.xml b/libs/install/testapp/Av2.xml
similarity index 78%
rename from libs/rollback/testapp/A2.xml
rename to libs/install/testapp/Av2.xml
index d879a91..fd8afa0 100644
--- a/libs/rollback/testapp/A2.xml
+++ b/libs/install/testapp/Av2.xml
@@ -15,15 +15,17 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.cts.rollback.lib.testapp.A"
+ package="com.android.cts.install.lib.testapp.A"
android:versionCode="2"
android:versionName="2.0" >
<uses-sdk android:minSdkVersion="19" />
- <application android:label="Rollback Test App A2">
- <activity android:name="com.android.cts.rollback.lib.testapp.MainActivity">
+ <application android:label="Test App A2">
+ <receiver android:name="com.android.cts.install.lib.testapp.ProcessUserData"
+ android:exported="true" />
+ <activity android:name="com.android.cts.install.lib.testapp.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
diff --git a/libs/rollback/testapp/A1.xml b/libs/install/testapp/Av3.xml
similarity index 73%
copy from libs/rollback/testapp/A1.xml
copy to libs/install/testapp/Av3.xml
index 2cd1825..a7839e3 100644
--- a/libs/rollback/testapp/A1.xml
+++ b/libs/install/testapp/Av3.xml
@@ -15,15 +15,17 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.cts.rollback.lib.testapp.A"
- android:versionCode="1"
- android:versionName="1.0" >
+ package="com.android.cts.install.lib.testapp.A"
+ android:versionCode="3"
+ android:versionName="3.0" >
<uses-sdk android:minSdkVersion="19" />
- <application android:label="Rollback Test App A1">
- <activity android:name="com.android.cts.rollback.lib.testapp.MainActivity">
+ <application android:label="Test App A3">
+ <receiver android:name="com.android.cts.install.lib.testapp.ProcessUserData"
+ android:exported="true" />
+ <activity android:name="com.android.cts.install.lib.testapp.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
diff --git a/libs/rollback/testapp/A1.xml b/libs/install/testapp/Bv1.xml
similarity index 78%
rename from libs/rollback/testapp/A1.xml
rename to libs/install/testapp/Bv1.xml
index 2cd1825..403e7e2 100644
--- a/libs/rollback/testapp/A1.xml
+++ b/libs/install/testapp/Bv1.xml
@@ -15,15 +15,17 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.cts.rollback.lib.testapp.A"
+ package="com.android.cts.install.lib.testapp.B"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="19" />
- <application android:label="Rollback Test App A1">
- <activity android:name="com.android.cts.rollback.lib.testapp.MainActivity">
+ <application android:label="Test App B1">
+ <receiver android:name="com.android.cts.install.lib.testapp.ProcessUserData"
+ android:exported="true" />
+ <activity android:name="com.android.cts.install.lib.testapp.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
diff --git a/libs/rollback/testapp/A2.xml b/libs/install/testapp/Bv2.xml
similarity index 78%
copy from libs/rollback/testapp/A2.xml
copy to libs/install/testapp/Bv2.xml
index d879a91..f030c3f 100644
--- a/libs/rollback/testapp/A2.xml
+++ b/libs/install/testapp/Bv2.xml
@@ -15,15 +15,17 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.cts.rollback.lib.testapp.A"
+ package="com.android.cts.install.lib.testapp.B"
android:versionCode="2"
android:versionName="2.0" >
<uses-sdk android:minSdkVersion="19" />
- <application android:label="Rollback Test App A2">
- <activity android:name="com.android.cts.rollback.lib.testapp.MainActivity">
+ <application android:label="Test App B2">
+ <receiver android:name="com.android.cts.install.lib.testapp.ProcessUserData"
+ android:exported="true" />
+ <activity android:name="com.android.cts.install.lib.testapp.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
diff --git a/libs/rollback/testapp/res_v1/values/values.xml b/libs/install/testapp/res_v1/values-anydpi/values.xml
similarity index 93%
rename from libs/rollback/testapp/res_v1/values/values.xml
rename to libs/install/testapp/res_v1/values-anydpi/values.xml
index 8e71917..90d3da2 100644
--- a/libs/rollback/testapp/res_v1/values/values.xml
+++ b/libs/install/testapp/res_v1/values-anydpi/values.xml
@@ -15,5 +15,5 @@
-->
<resources>
- <integer name="app_version">1</integer>
+ <integer name="split_version">1</integer>
</resources>
diff --git a/libs/rollback/testapp/res_v1/values/values.xml b/libs/install/testapp/res_v1/values/values.xml
similarity index 93%
copy from libs/rollback/testapp/res_v1/values/values.xml
copy to libs/install/testapp/res_v1/values/values.xml
index 8e71917..0447c74 100644
--- a/libs/rollback/testapp/res_v1/values/values.xml
+++ b/libs/install/testapp/res_v1/values/values.xml
@@ -16,4 +16,5 @@
<resources>
<integer name="app_version">1</integer>
+ <integer name="split_version">0</integer>
</resources>
diff --git a/libs/rollback/testapp/res_v1/values/values.xml b/libs/install/testapp/res_v2/values-anydpi/values.xml
similarity index 93%
copy from libs/rollback/testapp/res_v1/values/values.xml
copy to libs/install/testapp/res_v2/values-anydpi/values.xml
index 8e71917..9a1aa7f 100644
--- a/libs/rollback/testapp/res_v1/values/values.xml
+++ b/libs/install/testapp/res_v2/values-anydpi/values.xml
@@ -15,5 +15,5 @@
-->
<resources>
- <integer name="app_version">1</integer>
+ <integer name="split_version">2</integer>
</resources>
diff --git a/libs/rollback/testapp/res_v2/values/values.xml b/libs/install/testapp/res_v2/values/values.xml
similarity index 93%
rename from libs/rollback/testapp/res_v2/values/values.xml
rename to libs/install/testapp/res_v2/values/values.xml
index dac8dd6..fd988f5 100644
--- a/libs/rollback/testapp/res_v2/values/values.xml
+++ b/libs/install/testapp/res_v2/values/values.xml
@@ -16,4 +16,5 @@
<resources>
<integer name="app_version">2</integer>
+ <integer name="split_version">0</integer>
</resources>
diff --git a/libs/rollback/testapp/res_v1/values/values.xml b/libs/install/testapp/res_v3/values-anydpi/values.xml
similarity index 93%
copy from libs/rollback/testapp/res_v1/values/values.xml
copy to libs/install/testapp/res_v3/values-anydpi/values.xml
index 8e71917..f2d8992 100644
--- a/libs/rollback/testapp/res_v1/values/values.xml
+++ b/libs/install/testapp/res_v3/values-anydpi/values.xml
@@ -15,5 +15,5 @@
-->
<resources>
- <integer name="app_version">1</integer>
+ <integer name="split_version">3</integer>
</resources>
diff --git a/libs/rollback/testapp/res_v1/values/values.xml b/libs/install/testapp/res_v3/values/values.xml
similarity index 88%
copy from libs/rollback/testapp/res_v1/values/values.xml
copy to libs/install/testapp/res_v3/values/values.xml
index 8e71917..968168a 100644
--- a/libs/rollback/testapp/res_v1/values/values.xml
+++ b/libs/install/testapp/res_v3/values/values.xml
@@ -15,5 +15,6 @@
-->
<resources>
- <integer name="app_version">1</integer>
+ <integer name="app_version">3</integer>
+ <integer name="split_version">0</integer>
</resources>
diff --git a/libs/install/testapp/src/com/android/cts/install/lib/testapp/CrashingMainActivity.java b/libs/install/testapp/src/com/android/cts/install/lib/testapp/CrashingMainActivity.java
new file mode 100644
index 0000000..cbaccf7
--- /dev/null
+++ b/libs/install/testapp/src/com/android/cts/install/lib/testapp/CrashingMainActivity.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.install.lib.testapp;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+
+/**
+ * A crashing test app for testing apk rollback support.
+ */
+public class CrashingMainActivity extends Activity {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ incrementCountAndBroadcast();
+ throw new RuntimeException("Intended force crash");
+ }
+
+ private void incrementCountAndBroadcast() {
+ SharedPreferences preferences = getSharedPreferences("prefs", Context.MODE_PRIVATE);
+ SharedPreferences.Editor editor = preferences.edit();
+ int count = preferences.getInt("crash_count", 0);
+ editor.putInt("crash_count", ++count).commit();
+
+ Intent intent = new Intent("com.android.tests.rollback.CRASH");
+ intent.putExtra("count", count);
+ sendBroadcast(intent);
+ }
+}
diff --git a/tests/tests/packageinstaller/atomicinstall/testdata/apk/src/com/android/tests/atomicinstall/testapp/MainActivity.java b/libs/install/testapp/src/com/android/cts/install/lib/testapp/MainActivity.java
similarity index 67%
copy from tests/tests/packageinstaller/atomicinstall/testdata/apk/src/com/android/tests/atomicinstall/testapp/MainActivity.java
copy to libs/install/testapp/src/com/android/cts/install/lib/testapp/MainActivity.java
index 37276e2..ab2d90b 100644
--- a/tests/tests/packageinstaller/atomicinstall/testdata/apk/src/com/android/tests/atomicinstall/testapp/MainActivity.java
+++ b/libs/install/testapp/src/com/android/cts/install/lib/testapp/MainActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,15 +14,24 @@
* limitations under the License.
*/
-package com.android.tests.atomicinstall.testapp;
+package com.android.cts.install.lib.testapp;
import android.app.Activity;
import android.os.Bundle;
+/**
+ * A test app for testing apk rollback support.
+ */
public class MainActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+
+ try {
+ new ProcessUserData().processUserData(this);
+ } catch (ProcessUserData.UserDataException e) {
+ throw new AssertionError("Failed to process app user data", e);
+ }
}
}
diff --git a/libs/install/testapp/src/com/android/cts/install/lib/testapp/ProcessUserData.java b/libs/install/testapp/src/com/android/cts/install/lib/testapp/ProcessUserData.java
new file mode 100644
index 0000000..359e03f
--- /dev/null
+++ b/libs/install/testapp/src/com/android/cts/install/lib/testapp/ProcessUserData.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.install.lib.testapp;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.os.Process;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Scanner;
+
+/**
+ * A broadcast receiver to check for and update user app data version
+ * and user handle compatibility.
+ */
+public class ProcessUserData extends BroadcastReceiver {
+
+ /**
+ * Exception thrown in case of issue with user data.
+ */
+ public static class UserDataException extends Exception {
+ public UserDataException(String message) {
+ super(message);
+ }
+
+ public UserDataException(String message, Throwable cause) {
+ super(message, cause);
+ }
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ try {
+ processUserData(context);
+ setResultCode(1);
+ } catch (UserDataException e) {
+ setResultCode(0);
+ setResultData(e.getMessage());
+ }
+ }
+
+ /**
+ * Update the app's user data version to match the app version, and confirm
+ * the user data is for the correct user.
+ *
+ * @param context The application context.
+ * @throws UserDataException in case of problems with app user data.
+ */
+ public void processUserData(Context context) throws UserDataException {
+ Resources res = context.getResources();
+ String packageName = context.getPackageName();
+
+ String userHandle = Process.myUserHandle().toString();
+
+ int appVersionId = res.getIdentifier("app_version", "integer", packageName);
+ int appVersion = res.getInteger(appVersionId);
+
+ int splitVersionId = res.getIdentifier("split_version", "integer", packageName);
+ int splitVersion = res.getInteger(splitVersionId);
+
+ // Make sure the app version and split versions are compatible.
+ if (appVersion != splitVersion) {
+ throw new UserDataException("Split version " + splitVersion
+ + " does not match app version " + appVersion);
+ }
+
+ // Read the version of the app's user data and ensure it is compatible
+ // with our version of the application. Also ensure that the user data is
+ // for the correct user.
+ File versionFile = new File(context.getFilesDir(), "userdata.txt");
+ try {
+ Scanner s = new Scanner(versionFile);
+ int userDataVersion = s.nextInt();
+ s.nextLine();
+
+ if (userDataVersion > appVersion) {
+ throw new UserDataException("User data is from version " + userDataVersion
+ + ", which is not compatible with this version " + appVersion
+ + " of the RollbackTestApp");
+ }
+
+ String readUserHandle = s.nextLine();
+ s.close();
+
+ if (!readUserHandle.equals(userHandle)) {
+ throw new UserDataException("User handle expected to be: " + userHandle
+ + ", but was actually " + readUserHandle);
+ }
+ } catch (FileNotFoundException e) {
+ // No problem. This is a fresh install of the app or the user data
+ // has been wiped.
+ }
+
+ // Record the current version of the app in the user data.
+ try {
+ PrintWriter pw = new PrintWriter(versionFile);
+ pw.println(appVersion);
+ pw.println(userHandle);
+ pw.close();
+ } catch (IOException e) {
+ throw new UserDataException("Unable to write user data.", e);
+ }
+ }
+}
diff --git a/libs/rollback/Android.bp b/libs/rollback/Android.bp
index b4d31e2..3e4d0a3 100644
--- a/libs/rollback/Android.bp
+++ b/libs/rollback/Android.bp
@@ -12,29 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-android_test_helper_app {
- name: "RollbackManagerTestAppA1",
- manifest: "testapp/A1.xml",
- sdk_version: "current",
- srcs: ["testapp/src/**/*.java"],
- resource_dirs: ["testapp/res_v1"],
-}
-
-android_test_helper_app {
- name: "RollbackManagerTestAppA2",
- manifest: "testapp/A2.xml",
- sdk_version: "current",
- srcs: ["testapp/src/**/*.java"],
- resource_dirs: ["testapp/res_v2"],
-}
-
java_library {
name: "cts-rollback-lib",
srcs: ["src/**/*.java"],
- static_libs: ["androidx.test.rules", "truth-prebuilt"],
+ static_libs: ["androidx.test.rules", "truth-prebuilt", "cts-install-lib"],
sdk_version: "test_current",
- java_resources: [
- ":RollbackManagerTestAppA1",
- ":RollbackManagerTestAppA2",
- ],
}
diff --git a/libs/rollback/src/com/android/cts/rollback/lib/Install.java b/libs/rollback/src/com/android/cts/rollback/lib/Install.java
deleted file mode 100644
index caa39a0..0000000
--- a/libs/rollback/src/com/android/cts/rollback/lib/Install.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.rollback.lib;
-
-import android.content.Intent;
-import android.content.pm.PackageInstaller;
-
-import androidx.test.InstrumentationRegistry;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-/**
- * Helper class for installing test apps.
- */
-public class Install {
- private final boolean mIsMultiPackage;
- private final TestApp[] mTestApps;
- private boolean mIsStaged = false;
- private boolean mEnableRollback = false;
-
- private Install(boolean isMultiPackage, TestApp... testApps) {
- mIsMultiPackage = isMultiPackage;
- mTestApps = testApps;
- }
-
- /**
- * Creates an Install builder to install a single package.
- */
- public static Install single(TestApp testApp) {
- return new Install(false, testApp);
- }
-
- /**
- * Creates an Install builder to install using multiPackage.
- */
- public static Install multi(TestApp... testApps) {
- return new Install(true, testApps);
- }
-
- /**
- * Makes the install a staged install.
- */
- public Install setStaged() {
- mIsStaged = true;
- return this;
- }
-
- /**
- * Enables rollback for the install.
- */
- public Install setEnableRollback() {
- mEnableRollback = true;
- return this;
- }
-
- private static PackageInstaller getPackageInstaller() {
- return InstrumentationRegistry.getContext().getPackageManager().getPackageInstaller();
- }
-
- /**
- * Creates an empty install session with appropriate install params set.
- *
- * @return the session id of the newly created session
- */
- private int createEmptyInstallSession(boolean multiPackage, boolean isApex)
- throws IOException {
- PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
- PackageInstaller.SessionParams.MODE_FULL_INSTALL);
- if (multiPackage) {
- params.setMultiPackage();
- }
- if (isApex) {
- params.setInstallAsApex();
- }
- if (mIsStaged) {
- params.setStaged();
- }
- params.setEnableRollback(mEnableRollback);
- return getPackageInstaller().createSession(params);
- }
-
- /**
- * Creates an install session for the given test app.
- *
- * @return the session id of the newly created session.
- */
- private int createInstallSession(TestApp app) throws IOException {
- int sessionId = createEmptyInstallSession(/*multiPackage*/false, app.isApex());
- PackageInstaller.Session session = getPackageInstaller().openSession(sessionId);
-
- ClassLoader loader = TestApp.class.getClassLoader();
- for (String resourceName : app.getResourceNames()) {
- try (OutputStream os = session.openWrite(resourceName, 0, -1);
- InputStream is = loader.getResourceAsStream(resourceName);) {
- byte[] buffer = new byte[4096];
- int n;
- while ((n = is.read(buffer)) >= 0) {
- os.write(buffer, 0, n);
- }
- }
- }
- session.close();
- return sessionId;
- }
-
- /**
- * Commits the install.
- */
- public void commit() throws IOException, InterruptedException {
- final int sessionId;
- final PackageInstaller.Session session;
- if (mIsMultiPackage) {
- sessionId = createEmptyInstallSession(/*multiPackage*/ true, /*isApex*/false);
- session = getPackageInstaller().openSession(sessionId);
- for (TestApp app : mTestApps) {
- session.addChildSessionId(createInstallSession(app));
- }
- } else {
- assert mTestApps.length == 1;
- sessionId = createInstallSession(mTestApps[0]);
- session = getPackageInstaller().openSession(sessionId);
- }
-
- session.commit(LocalIntentSender.getIntentSender());
- Intent result = LocalIntentSender.getIntentSenderResult();
- int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
- PackageInstaller.STATUS_FAILURE);
- if (status == -1) {
- throw new AssertionError("PENDING USER ACTION");
- } else if (status > 0) {
- String message = result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
- throw new AssertionError(message == null ? "UNKNOWN FAILURE" : message);
- }
-
- if (mIsStaged) {
- Utils.waitForSessionReady(sessionId);
- }
- }
-}
diff --git a/libs/rollback/src/com/android/cts/rollback/lib/LocalIntentSender.java b/libs/rollback/src/com/android/cts/rollback/lib/LocalIntentSender.java
deleted file mode 100644
index bd8f0dd..0000000
--- a/libs/rollback/src/com/android/cts/rollback/lib/LocalIntentSender.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.rollback.lib;
-
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentSender;
-
-import androidx.test.InstrumentationRegistry;
-
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.LinkedBlockingQueue;
-
-/**
- * Helper for making IntentSenders whose results are sent back to the test
- * app.
- */
-public class LocalIntentSender extends BroadcastReceiver {
-
- private static final BlockingQueue<Intent> sIntentSenderResults = new LinkedBlockingQueue<>();
-
- @Override
- public void onReceive(Context context, Intent intent) {
- sIntentSenderResults.add(intent);
- }
-
- /**
- * Get a LocalIntentSender.
- */
- static IntentSender getIntentSender() {
- Context context = InstrumentationRegistry.getContext();
- Intent intent = new Intent(context, LocalIntentSender.class);
- PendingIntent pending = PendingIntent.getBroadcast(context, 0, intent, 0);
- return pending.getIntentSender();
- }
-
- /**
- * Returns the most recent Intent sent by a LocalIntentSender.
- */
- static Intent getIntentSenderResult() throws InterruptedException {
- return sIntentSenderResults.take();
- }
-}
diff --git a/libs/rollback/src/com/android/cts/rollback/lib/Rollback.java b/libs/rollback/src/com/android/cts/rollback/lib/Rollback.java
index 14e19bb..bb0bc60 100644
--- a/libs/rollback/src/com/android/cts/rollback/lib/Rollback.java
+++ b/libs/rollback/src/com/android/cts/rollback/lib/Rollback.java
@@ -19,6 +19,8 @@
import android.content.pm.VersionedPackage;
import android.content.rollback.PackageRollbackInfo;
+import com.android.cts.install.lib.TestApp;
+
/**
* Helper class for asserting PackageRollbackInfo contents.
*/
diff --git a/libs/rollback/src/com/android/cts/rollback/lib/RollbackBroadcastReceiver.java b/libs/rollback/src/com/android/cts/rollback/lib/RollbackBroadcastReceiver.java
new file mode 100644
index 0000000..15438e4
--- /dev/null
+++ b/libs/rollback/src/com/android/cts/rollback/lib/RollbackBroadcastReceiver.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.rollback.lib;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A broadcast receiver that can be used to get
+ * ACTION_ROLLBACK_COMMITTED broadcasts.
+ */
+public class RollbackBroadcastReceiver extends BroadcastReceiver {
+
+ private static final String TAG = "RollbackTest";
+
+ private final BlockingQueue<Intent> mRollbackBroadcasts = new LinkedBlockingQueue<>();
+
+ /**
+ * Creates a RollbackBroadcastReceiver and registers it with the given
+ * context.
+ */
+ public RollbackBroadcastReceiver() {
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_ROLLBACK_COMMITTED);
+ InstrumentationRegistry.getContext().registerReceiver(this, filter);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.i(TAG, "Received rollback broadcast intent");
+ mRollbackBroadcasts.add(intent);
+ }
+
+ /**
+ * Polls for at most the given amount of time for the next rollback
+ * broadcast.
+ */
+ public Intent poll(long timeout, TimeUnit unit) throws InterruptedException {
+ return mRollbackBroadcasts.poll(timeout, unit);
+ }
+
+ /**
+ * Waits forever for the next rollback broadcast.
+ */
+ public Intent take() throws InterruptedException {
+ return mRollbackBroadcasts.take();
+ }
+
+ /**
+ * Unregisters this broadcast receiver.
+ */
+ public void unregister() {
+ InstrumentationRegistry.getContext().unregisterReceiver(this);
+ }
+}
diff --git a/libs/rollback/src/com/android/cts/rollback/lib/RollbackInfoSubject.java b/libs/rollback/src/com/android/cts/rollback/lib/RollbackInfoSubject.java
index 8bc9247..e81a89e 100644
--- a/libs/rollback/src/com/android/cts/rollback/lib/RollbackInfoSubject.java
+++ b/libs/rollback/src/com/android/cts/rollback/lib/RollbackInfoSubject.java
@@ -20,6 +20,8 @@
import android.content.rollback.PackageRollbackInfo;
import android.content.rollback.RollbackInfo;
+import com.android.cts.install.lib.TestApp;
+
import com.google.common.truth.FailureMetadata;
import com.google.common.truth.Subject;
import com.google.common.truth.Truth;
diff --git a/libs/rollback/src/com/android/cts/rollback/lib/RollbackUtils.java b/libs/rollback/src/com/android/cts/rollback/lib/RollbackUtils.java
new file mode 100644
index 0000000..08ba87a
--- /dev/null
+++ b/libs/rollback/src/com/android/cts/rollback/lib/RollbackUtils.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.rollback.lib;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.ActivityManager;
+import android.app.AlarmManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.VersionedPackage;
+import android.content.rollback.PackageRollbackInfo;
+import android.content.rollback.RollbackInfo;
+import android.content.rollback.RollbackManager;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.cts.install.lib.LocalIntentSender;
+import com.android.cts.install.lib.TestApp;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+
+/**
+ * Utilities to facilitate testing rollbacks.
+ */
+public class RollbackUtils {
+
+ private static final String TAG = "RollbackTest";
+
+ /**
+ * Time between repeated checks in {@link #retry}.
+ */
+ private static final long RETRY_CHECK_INTERVAL_MILLIS = 500;
+
+ /**
+ * Maximum number of checks in {@link #retry} before a timeout occurs.
+ */
+ private static final long RETRY_MAX_INTERVALS = 20;
+
+
+ /**
+ * Gets the RollbackManager for the instrumentation context.
+ */
+ public static RollbackManager getRollbackManager() {
+ Context context = InstrumentationRegistry.getContext();
+ RollbackManager rm = (RollbackManager) context.getSystemService(Context.ROLLBACK_SERVICE);
+ if (rm == null) {
+ throw new AssertionError("Failed to get RollbackManager");
+ }
+ return rm;
+ }
+
+ /**
+ * Returns a rollback for the given rollback Id, if found. Otherwise, returns null.
+ */
+ private static RollbackInfo getRollbackById(List<RollbackInfo> rollbacks, int rollbackId) {
+ for (RollbackInfo rollback :rollbacks) {
+ if (rollback.getRollbackId() == rollbackId) {
+ return rollback;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns an available rollback for the given package name. Returns null
+ * if there are no available rollbacks, and throws an assertion if there
+ * is more than one.
+ */
+ public static RollbackInfo getAvailableRollback(String packageName) {
+ RollbackManager rm = getRollbackManager();
+ return getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), packageName);
+ }
+
+ /**
+ * Returns a recently committed rollback for the given package name. Returns null
+ * if there are no available rollbacks, and throws an assertion if there
+ * is more than one.
+ */
+ public static RollbackInfo getCommittedRollback(String packageName) {
+ RollbackManager rm = getRollbackManager();
+ return getUniqueRollbackInfoForPackage(rm.getRecentlyCommittedRollbacks(), packageName);
+ }
+
+ /**
+ * Returns a recently committed rollback for the given rollback Id.
+ * Returns null if no committed rollback with a matching Id was found.
+ */
+ public static RollbackInfo getCommittedRollbackById(int rollbackId) {
+ RollbackManager rm = getRollbackManager();
+ return getRollbackById(rm.getRecentlyCommittedRollbacks(), rollbackId);
+ }
+
+ /**
+ * Commit the given rollback.
+ * @throws AssertionError if the rollback fails.
+ */
+ public static void rollback(int rollbackId, TestApp... causePackages)
+ throws InterruptedException {
+ List<VersionedPackage> causes = new ArrayList<>();
+ for (TestApp cause : causePackages) {
+ causes.add(cause.getVersionedPackage());
+ }
+
+ RollbackManager rm = getRollbackManager();
+ rm.commitRollback(rollbackId, causes, LocalIntentSender.getIntentSender());
+ Intent result = LocalIntentSender.getIntentSenderResult();
+ int status = result.getIntExtra(RollbackManager.EXTRA_STATUS,
+ RollbackManager.STATUS_FAILURE);
+ if (status != RollbackManager.STATUS_SUCCESS) {
+ String message = result.getStringExtra(RollbackManager.EXTRA_STATUS_MESSAGE);
+ throw new AssertionError(message);
+ }
+ }
+
+ /**
+ * Forwards the device clock time by {@code offsetMillis}.
+ */
+ public static void forwardTimeBy(long offsetMillis) {
+ setTime(System.currentTimeMillis() + offsetMillis);
+ Log.i(TAG, "Forwarded time on device by " + offsetMillis + " millis");
+ }
+
+ /**
+ * Returns the RollbackInfo with a given package in the list of rollbacks.
+ * Throws an assertion failure if there is more than one such rollback
+ * info. Returns null if there are no such rollback infos.
+ */
+ public static RollbackInfo getUniqueRollbackInfoForPackage(List<RollbackInfo> rollbacks,
+ String packageName) {
+ RollbackInfo found = null;
+ for (RollbackInfo rollback : rollbacks) {
+ for (PackageRollbackInfo info : rollback.getPackages()) {
+ if (packageName.equals(info.getPackageName())) {
+ assertThat(found).isNull();
+ found = rollback;
+ break;
+ }
+ }
+ }
+ return found;
+ }
+
+ /**
+ * Returns an available rollback matching the specified package name. If no such rollback is
+ * available, getAvailableRollbacks is called repeatedly until one becomes available. An
+ * assertion is raised if this does not occur after a certain number of checks.
+ */
+ public static RollbackInfo waitForAvailableRollback(String packageName)
+ throws InterruptedException {
+ return retry(() -> getAvailableRollback(packageName),
+ Objects::nonNull, "Rollback did not become available.");
+ }
+
+ /**
+ * If there is no available rollback matching the specified package name, this returns
+ * immediately. If such a rollback is available, getAvailableRollbacks is called repeatedly
+ * until it is no longer available. An assertion is raised if this does not occur after a
+ * certain number of checks.
+ */
+ public static void waitForUnavailableRollback(String packageName) throws InterruptedException {
+ retry(() -> getAvailableRollback(packageName), Objects::isNull,
+ "Rollback did not become unavailable");
+ }
+
+ private static <T> T retry(Supplier<T> supplier, Predicate<T> predicate, String message)
+ throws InterruptedException {
+ for (int i = 0; i < RETRY_MAX_INTERVALS; i++) {
+ T result = supplier.get();
+ if (predicate.test(result)) {
+ return result;
+ }
+ Thread.sleep(RETRY_CHECK_INTERVAL_MILLIS);
+ }
+ throw new AssertionError(message);
+ }
+
+ /**
+ * Send broadcast to crash {@code packageName} {@code count} times. If {@code count} is at least
+ * {@link PackageWatchdog#TRIGGER_FAILURE_COUNT}, watchdog crash detection will be triggered.
+ */
+ public static void sendCrashBroadcast(String packageName,
+ int count) throws InterruptedException, IOException {
+ for (int i = 0; i < count; ++i) {
+ launchPackageForCrash(packageName);
+ }
+ }
+
+ private static void setTime(long millis) {
+ Context context = InstrumentationRegistry.getContext();
+ AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ am.setTime(millis);
+ }
+
+ /**
+ * Launches {@code packageName} with {@link Intent#ACTION_MAIN} and
+ * waits for a CRASH broadcast from the launched app.
+ */
+ private static void launchPackageForCrash(String packageName)
+ throws InterruptedException, IOException {
+ // Force stop the package before launching it to make sure it isn't
+ // stuck in a non-launchable state. And wait a second afterwards to
+ // avoid interfering with when we launch the app.
+ Log.i(TAG, "Force stopping " + packageName);
+ Context context = InstrumentationRegistry.getContext();
+ ActivityManager am = context.getSystemService(ActivityManager.class);
+ am.forceStopPackage(packageName);
+ Thread.sleep(1000);
+
+ // Register a receiver to listen for the CRASH broadcast.
+ CountDownLatch latch = new CountDownLatch(1);
+ IntentFilter crashFilter = new IntentFilter();
+ crashFilter.addAction("com.android.tests.rollback.CRASH");
+ crashFilter.addCategory(Intent.CATEGORY_DEFAULT);
+ BroadcastReceiver crashReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.i(TAG, "Received CRASH broadcast from " + packageName);
+ latch.countDown();
+ }
+ };
+ context.registerReceiver(crashReceiver, crashFilter);
+
+ // Launch the app.
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.setPackage(packageName);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.addCategory(Intent.CATEGORY_LAUNCHER);
+ Log.i(TAG, "Launching " + packageName + " with " + intent);
+ context.startActivity(intent);
+
+ Log.i(TAG, "Waiting for CRASH broadcast from " + packageName);
+ latch.await();
+
+ context.unregisterReceiver(crashReceiver);
+
+ // Sleep long enough for packagewatchdog to be notified of crash
+ Thread.sleep(1000);
+ }
+}
+
diff --git a/libs/rollback/src/com/android/cts/rollback/lib/TestApp.java b/libs/rollback/src/com/android/cts/rollback/lib/TestApp.java
deleted file mode 100644
index 530ead7..0000000
--- a/libs/rollback/src/com/android/cts/rollback/lib/TestApp.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.rollback.lib;
-
-import android.content.pm.VersionedPackage;
-
-/**
- * Collection of dummy apps used in tests.
- */
-public class TestApp {
- public static final String A = "com.android.cts.rollback.lib.testapp.A";
- public static final String Apex = "com.android.apex.cts.shim";
-
- public static final TestApp A1 = new TestApp("A1", A, 1, /*isApex*/false,
- "RollbackManagerTestAppA1.apk");
- public static final TestApp A2 = new TestApp("A2", A, 2, /*isApex*/false,
- "RollbackManagerTestAppA2.apk");
- public static final TestApp Apex2 = new TestApp("Apex2", Apex, 2, /*isApex*/true,
- "com.android.apex.cts.shim.v2.apex");
- public static final TestApp Apex3 = new TestApp("Apex3", Apex, 3, /*isApex*/true,
- "com.android.apex.cts.shim.v3.apex");
-
- private final String mName;
- private final String mPackageName;
- private final long mVersionCode;
- private final String[] mResourceNames;
- private final boolean mIsApex;
-
- public TestApp(String name, String packageName, long versionCode, boolean isApex,
- String... resourceNames) {
- mName = name;
- mPackageName = packageName;
- mVersionCode = versionCode;
- mResourceNames = resourceNames;
- mIsApex = isApex;
- }
-
- String getPackageName() {
- return mPackageName;
- }
-
- long getVersionCode() {
- return mVersionCode;
- }
-
- String[] getResourceNames() {
- return mResourceNames;
- }
-
- VersionedPackage getVersionedPackage() {
- return new VersionedPackage(mPackageName, mVersionCode);
- }
-
- boolean isApex() {
- return mIsApex;
- }
-
- @Override
- public String toString() {
- return mName;
- }
-}
diff --git a/libs/rollback/src/com/android/cts/rollback/lib/Utils.java b/libs/rollback/src/com/android/cts/rollback/lib/Utils.java
deleted file mode 100644
index 668f641..0000000
--- a/libs/rollback/src/com/android/cts/rollback/lib/Utils.java
+++ /dev/null
@@ -1,226 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.rollback.lib;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageInstaller;
-import android.content.pm.PackageManager;
-import android.content.pm.VersionedPackage;
-import android.content.rollback.PackageRollbackInfo;
-import android.content.rollback.RollbackInfo;
-import android.content.rollback.RollbackManager;
-
-import androidx.test.InstrumentationRegistry;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.LinkedBlockingQueue;
-
-/**
- * Utilities to facilitate testing rollbacks.
- */
-public class Utils {
- /**
- * Returns the version of the given package installed on device.
- * Returns -1 if the package is not currently installed.
- */
- public static long getInstalledVersion(String packageName) {
- Context context = InstrumentationRegistry.getContext();
- PackageManager pm = context.getPackageManager();
- try {
- PackageInfo info = pm.getPackageInfo(packageName, PackageManager.MATCH_APEX);
- return info.getLongVersionCode();
- } catch (PackageManager.NameNotFoundException e) {
- return -1;
- }
- }
-
- /**
- * Gets the RollbackManager for the instrumentation context.
- */
- public static RollbackManager getRollbackManager() {
- Context context = InstrumentationRegistry.getContext();
- RollbackManager rm = (RollbackManager) context.getSystemService(Context.ROLLBACK_SERVICE);
- if (rm == null) {
- throw new AssertionError("Failed to get RollbackManager");
- }
- return rm;
- }
-
- /**
- * Returns a rollback for the given package name in the list of
- * rollbacks. Returns null if there are no available rollbacks, and throws
- * an assertion if there is more than one.
- */
- private static RollbackInfo getRollback(List<RollbackInfo> rollbacks, String packageName) {
- RollbackInfo found = null;
- for (RollbackInfo rollback : rollbacks) {
- for (PackageRollbackInfo info : rollback.getPackages()) {
- if (packageName.equals(info.getPackageName())) {
- if (found != null) {
- throw new AssertionError("Multiple available matching rollbacks found");
- }
- found = rollback;
- break;
- }
- }
- }
- return found;
- }
-
- /**
- * Returns a rollback for the given rollback Id, if found. Otherwise, returns null.
- */
- private static RollbackInfo getRollbackById(List<RollbackInfo> rollbacks, int rollbackId) {
- for (RollbackInfo rollback :rollbacks) {
- if (rollback.getRollbackId() == rollbackId) {
- return rollback;
- }
- }
- return null;
- }
-
- /**
- * Returns an available rollback for the given package name. Returns null
- * if there are no available rollbacks, and throws an assertion if there
- * is more than one.
- */
- public static RollbackInfo getAvailableRollback(String packageName) {
- RollbackManager rm = getRollbackManager();
- return getRollback(rm.getAvailableRollbacks(), packageName);
- }
-
- /**
- * Returns a recently committed rollback for the given package name. Returns null
- * if there are no available rollbacks, and throws an assertion if there
- * is more than one.
- */
- public static RollbackInfo getCommittedRollback(String packageName) {
- RollbackManager rm = getRollbackManager();
- return getRollback(rm.getRecentlyCommittedRollbacks(), packageName);
- }
-
- /**
- * Returns a recently committed rollback for the given rollback Id.
- * Returns null if no committed rollback with a matching Id was found.
- */
- public static RollbackInfo getCommittedRollbackById(int rollbackId) {
- RollbackManager rm = getRollbackManager();
- return getRollbackById(rm.getRecentlyCommittedRollbacks(), rollbackId);
- }
-
- /**
- * Uninstalls the given package.
- * Does nothing if the package is not installed.
- * @throws AssertionError if package can't be uninstalled.
- */
- public static void uninstall(String packageName) throws InterruptedException, IOException {
- // No need to uninstall if the package isn't installed.
- if (getInstalledVersion(packageName) == -1) {
- return;
- }
-
- Context context = InstrumentationRegistry.getContext();
- PackageManager packageManager = context.getPackageManager();
- PackageInstaller packageInstaller = packageManager.getPackageInstaller();
- packageInstaller.uninstall(packageName, LocalIntentSender.getIntentSender());
- Intent result = LocalIntentSender.getIntentSenderResult();
- int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
- PackageInstaller.STATUS_FAILURE);
- if (status == -1) {
- throw new AssertionError("PENDING USER ACTION");
- } else if (status > 0) {
- String message = result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
- throw new AssertionError(message == null ? "UNKNOWN FAILURE" : message);
- }
- }
-
- /**
- * Commit the given rollback.
- * @throws AssertionError if the rollback fails.
- */
- public static void rollback(int rollbackId, TestApp... causePackages)
- throws InterruptedException {
- List<VersionedPackage> causes = new ArrayList<>();
- for (TestApp cause : causePackages) {
- causes.add(cause.getVersionedPackage());
- }
-
- RollbackManager rm = getRollbackManager();
- rm.commitRollback(rollbackId, causes, LocalIntentSender.getIntentSender());
- Intent result = LocalIntentSender.getIntentSenderResult();
- int status = result.getIntExtra(RollbackManager.EXTRA_STATUS,
- RollbackManager.STATUS_FAILURE);
- if (status != RollbackManager.STATUS_SUCCESS) {
- String message = result.getStringExtra(RollbackManager.EXTRA_STATUS_MESSAGE);
- throw new AssertionError(message);
- }
- }
-
- /**
- * Waits for the given session to be marked as ready.
- * Throws an assertion if the session fails.
- */
- public static void waitForSessionReady(int sessionId) {
- BlockingQueue<PackageInstaller.SessionInfo> sessionStatus = new LinkedBlockingQueue<>();
- BroadcastReceiver sessionUpdatedReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- PackageInstaller.SessionInfo info =
- intent.getParcelableExtra(PackageInstaller.EXTRA_SESSION);
- if (info != null && info.getSessionId() == sessionId) {
- if (info.isStagedSessionReady() || info.isStagedSessionFailed()) {
- try {
- sessionStatus.put(info);
- } catch (InterruptedException e) {
- throw new AssertionError(e);
- }
- }
- }
- }
- };
- IntentFilter sessionUpdatedFilter =
- new IntentFilter(PackageInstaller.ACTION_SESSION_UPDATED);
-
- Context context = InstrumentationRegistry.getContext();
- context.registerReceiver(sessionUpdatedReceiver, sessionUpdatedFilter);
-
- PackageInstaller installer = context.getPackageManager().getPackageInstaller();
- PackageInstaller.SessionInfo info = installer.getSessionInfo(sessionId);
-
- try {
- if (info.isStagedSessionReady() || info.isStagedSessionFailed()) {
- sessionStatus.put(info);
- }
-
- info = sessionStatus.take();
- context.unregisterReceiver(sessionUpdatedReceiver);
- if (info.isStagedSessionFailed()) {
- throw new AssertionError(info.getStagedSessionErrorMessage());
- }
- } catch (InterruptedException e) {
- throw new AssertionError(e);
- }
- }
-}
-
diff --git a/libs/rollback/testapp/src/com/android/cts/rollback/lib/testapp/MainActivity.java b/libs/rollback/testapp/src/com/android/cts/rollback/lib/testapp/MainActivity.java
deleted file mode 100644
index e426382..0000000
--- a/libs/rollback/testapp/src/com/android/cts/rollback/lib/testapp/MainActivity.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.rollback.lib.testapp;
-
-import android.app.Activity;
-
-/**
- * A test app for testing apk rollback support.
- */
-public class MainActivity extends Activity {
-}
diff --git a/libs/runner/Android.bp b/libs/runner/Android.bp
index 425c593..40977ff 100644
--- a/libs/runner/Android.bp
+++ b/libs/runner/Android.bp
@@ -12,16 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// The legacy library that brings in android-support-test transitively
-java_library {
- name: "ctstestrunner",
-
- static_libs: ["cts-test-runner"],
-
- sdk_version: "current",
-
-}
-
// The library variant that brings in androidx-test transitively
java_library {
name: "ctstestrunner-axt",
diff --git a/tests/DropBoxManager/OWNERS b/tests/DropBoxManager/OWNERS
new file mode 100644
index 0000000..1a5b740
--- /dev/null
+++ b/tests/DropBoxManager/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 1344
+mwachens@google.com
diff --git a/tests/JobScheduler/OWNERS b/tests/JobScheduler/OWNERS
new file mode 100644
index 0000000..ef7929f
--- /dev/null
+++ b/tests/JobScheduler/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 330738
+ctate@google.com
\ No newline at end of file
diff --git a/tests/JobSchedulerSharedUid/OWNERS b/tests/JobSchedulerSharedUid/OWNERS
new file mode 100644
index 0000000..ef7929f
--- /dev/null
+++ b/tests/JobSchedulerSharedUid/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 330738
+ctate@google.com
\ No newline at end of file
diff --git a/tests/acceleration/OWNERS b/tests/acceleration/OWNERS
new file mode 100644
index 0000000..3d2576a
--- /dev/null
+++ b/tests/acceleration/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 32850
+include platform/frameworks/base:/libs/hwui/OWNERS
diff --git a/tests/accessibility/AndroidManifest.xml b/tests/accessibility/AndroidManifest.xml
index f07494f..d4cea0f 100644
--- a/tests/accessibility/AndroidManifest.xml
+++ b/tests/accessibility/AndroidManifest.xml
@@ -21,9 +21,11 @@
android:targetSandboxVersion="2">
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
- <application android:theme="@android:style/Theme.Holo.NoActionBar" >
+ <application android:theme="@android:style/Theme.Holo.NoActionBar"
+ android:requestLegacyExternalStorage="true">
<uses-library android:name="android.test.runner"/>
<service android:name=".SpeakingAccessibilityService"
android:label="@string/title_speaking_accessibility_service"
diff --git a/tests/accessibility/AndroidTest.xml b/tests/accessibility/AndroidTest.xml
index 7783a25..3057ae8 100644
--- a/tests/accessibility/AndroidTest.xml
+++ b/tests/accessibility/AndroidTest.xml
@@ -30,4 +30,8 @@
<option name="package" value="android.view.accessibility.cts" />
<option name="runtime-hint" value="8m"/>
</test>
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="directory-keys" value="/sdcard/android.view.accessibility.cts" />
+ <option name="collect-on-run-ended-only" value="true" />
+ </metrics_collector>
</configuration>
diff --git a/tests/accessibility/OWNERS b/tests/accessibility/OWNERS
index d225f2c..e54f581 100644
--- a/tests/accessibility/OWNERS
+++ b/tests/accessibility/OWNERS
@@ -1,2 +1,3 @@
# Bug component: 44214
-pweaver@google.com
\ No newline at end of file
+pweaver@google.com
+rhedjao@google.com
diff --git a/tests/accessibility/common/src/android/accessibility/cts/common/AccessibilityDumpOnFailureRule.java b/tests/accessibility/common/src/android/accessibility/cts/common/AccessibilityDumpOnFailureRule.java
new file mode 100644
index 0000000..df7c550
--- /dev/null
+++ b/tests/accessibility/common/src/android/accessibility/cts/common/AccessibilityDumpOnFailureRule.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.accessibility.cts.common;
+
+import androidx.test.rule.ActivityTestRule;
+
+import org.junit.rules.ExternalResource;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+
+/**
+ * Custom {@code TestRule} that dump accessibility related data upon test failures.
+ *
+ * <p>Note: when using other {@code TestRule}s, make sure to use a {@link RuleChain} to ensure it
+ * is applied outside of other rules that can fail a test (otherwise this rule may not know that the
+ * test failed). If using with {@link ExternalResource}-like {@code TestRule}s, {@link
+ * ActivityTestRule} or {@link InstrumentedAccessibilityService}, this rule should chaining as a
+ * inner rule to resources-like rules so that it will dump data before resources are cleaned up.
+ *
+ * <p>To capture the output of this rule, add the following to AndroidTest.xml:
+ * <pre>
+ * <!-- Collect output of AccessibilityDumpOnFailureRule. -->
+ * <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ * <option name="directory-keys" value="/sdcard/<test.package.name>" />
+ * <option name="collect-on-run-ended-only" value="true" />
+ * </metrics_collector>
+ * </pre>
+ * <p>And disable external storage isolation:
+ * <pre>
+ * <application ... android:requestLegacyExternalStorage="true" ... >
+ * </pre>
+ */
+public class AccessibilityDumpOnFailureRule extends TestWatcher {
+
+ public void dump(int flag) {
+ AccessibilityDumper.getInstance().dump(flag);
+ }
+
+ @Override
+ protected void starting(Description description) {
+ AccessibilityDumper.getInstance().setName(getTestNameFrom(description));
+ }
+
+ @Override
+ protected void failed(Throwable t, Description description) {
+ AccessibilityDumper.getInstance().dump();
+ }
+
+ private String getTestNameFrom(Description description) {
+ return description.getTestClass().getSimpleName()
+ + "_" + description.getMethodName();
+ }
+}
diff --git a/tests/accessibility/common/src/android/accessibility/cts/common/AccessibilityDumper.java b/tests/accessibility/common/src/android/accessibility/cts/common/AccessibilityDumper.java
new file mode 100644
index 0000000..6fbd95a6
--- /dev/null
+++ b/tests/accessibility/common/src/android/accessibility/cts/common/AccessibilityDumper.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.accessibility.cts.common;
+
+import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
+import static android.app.UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES;
+
+import static androidx.test.InstrumentationRegistry.getContext;
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertFalse;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.app.UiAutomation;
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.os.Environment;
+import android.support.test.uiautomator.Configurator;
+import android.support.test.uiautomator.UiDevice;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityWindowInfo;
+
+import com.android.compatibility.common.util.BitmapUtils;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.time.LocalTime;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Helper class to dump data for accessibility test cases.
+ *
+ * It can dump {@code dumpsys accessibility}, accessibility node tree to logcat and/or
+ * screenshot for inspect later.
+ */
+public class AccessibilityDumper {
+ private static final String TAG = "AccessibilityDumper";
+
+ /** Dump flag to write the output of {@code dumpsys accessibility} to logcat. */
+ public static final int FLAG_DUMPSYS = 0x1;
+
+ /** Dump flag to write the output of {@code uiautomator dump} to logcat. */
+ public static final int FLAG_HIERARCHY = 0x2;
+
+ /** Dump flag to save the screenshot to external storage. */
+ public static final int FLAG_SCREENSHOT = 0x4;
+
+ /** Dump flag to write the tree of accessility node info to logcat. */
+ public static final int FLAG_NODETREE = 0x8;
+
+ /** Default dump flag */
+ public static final int FLAG_DUMP_ALL = FLAG_DUMPSYS | FLAG_HIERARCHY | FLAG_SCREENSHOT;
+
+ private static AccessibilityDumper sDumper;
+
+ private int mFlag;
+
+ /** Screenshot filename */
+ private String mName;
+
+ /** Root directory matching the directory-key of collector in AndroidTest.xml */
+ private File mRoot;
+
+ public static synchronized AccessibilityDumper getInstance() {
+ if (sDumper == null) {
+ sDumper = new AccessibilityDumper(FLAG_DUMP_ALL);
+ }
+ return sDumper;
+ }
+
+ /**
+ * Define the directory to dump/clean and initial dump options
+ *
+ * @param flag control what to dump
+ */
+ private AccessibilityDumper(int flag) {
+ mRoot = getDumpRoot(getContext().getPackageName());
+ mFlag = flag;
+ }
+
+ public void dump(int flag) {
+ final UiAutomation automation = getUiAutomation();
+
+ if ((flag & FLAG_DUMPSYS) != 0) {
+ dumpsysOnLogcat(automation);
+ }
+ if ((flag & FLAG_HIERARCHY) != 0) {
+ dumpHierarchyOnLogcat();
+ }
+ if ((flag & FLAG_SCREENSHOT) != 0) {
+ dumpScreen(automation);
+ }
+ if ((flag & FLAG_NODETREE) != 0) {
+ dumpAccessibilityNodeTreeOnLogcat(automation);
+ }
+ }
+
+ void dump() {
+ dump(mFlag);
+ }
+
+ void setName(String name) {
+ assertNotEmpty(name);
+ mName = name;
+ }
+
+ private File getDumpRoot(String directory) {
+ return new File(Environment.getExternalStorageDirectory(), directory);
+ }
+
+ private void dumpsysOnLogcat(UiAutomation automation) {
+ ShellCommandBuilder.create(automation)
+ .addCommandPrintOnLogCat("dumpsys accessibility")
+ .run();
+ }
+
+ private void dumpHierarchyOnLogcat() {
+ try(ByteArrayOutputStream os = new ByteArrayOutputStream()) {
+ UiDevice.getInstance(getInstrumentation()).dumpWindowHierarchy(os);
+ Log.w(TAG, "Window hierarchy:");
+ for (String line : os.toString("UTF-8").split("\\n")) {
+ Log.w(TAG, line);
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "ERROR: unable to dumping hierarchy on logcat", e);
+ }
+ }
+
+ private void dumpScreen(UiAutomation automation) {
+ assertNotEmpty(mName);
+ final Bitmap screenshot = automation.takeScreenshot();
+ final String filename = String.format("%s_%s__screenshot.png", mName, LocalTime.now());
+ BitmapUtils.saveBitmap(screenshot, mRoot.toString(), filename);
+ }
+
+ /** Dump hierarchy compactly and include nodes not visible to user */
+ private void dumpAccessibilityNodeTreeOnLogcat(UiAutomation automation) {
+ final Set<AccessibilityNodeInfo> roots = new HashSet<>();
+ for (AccessibilityWindowInfo window : automation.getWindows()) {
+ AccessibilityNodeInfo root = window.getRoot();
+ if (root == null) {
+ Log.w(TAG, String.format("Skipping null root node for window: %s",
+ window.toString()));
+ } else {
+ roots.add(root);
+ }
+ }
+ if (roots.isEmpty()) {
+ Log.w(TAG, "No node of windows to dump");
+ } else {
+ Log.w(TAG, "Accessibility nodes hierarchy:");
+ for (AccessibilityNodeInfo root : roots) {
+ dumpTreeWithPrefix(root, "");
+ }
+ }
+ }
+
+ private static void dumpTreeWithPrefix(AccessibilityNodeInfo node, String prefix) {
+ final StringBuilder nodeText = new StringBuilder(prefix);
+ appendNodeText(nodeText, node);
+ Log.v(TAG, nodeText.toString());
+ final int count = node.getChildCount();
+ for (int i = 0; i < count; i++) {
+ AccessibilityNodeInfo child = node.getChild(i);
+ if (child != null) {
+ dumpTreeWithPrefix(child, "-" + prefix);
+ } else {
+ Log.i(TAG, String.format("%sNull child %d/%d", prefix, i, count));
+ }
+ }
+ }
+
+ private static void appendNodeText(StringBuilder out, AccessibilityNodeInfo node) {
+ final CharSequence txt = node.getText();
+ final CharSequence description = node.getContentDescription();
+ final String viewId = node.getViewIdResourceName();
+
+ if (!TextUtils.isEmpty(description)) {
+ out.append(escape(description));
+ } else if (!TextUtils.isEmpty(txt)) {
+ out.append('"').append(escape(txt)).append('"');
+ }
+ if (!TextUtils.isEmpty(viewId)) {
+ out.append("(").append(viewId).append(")");
+ }
+ out.append("+").append(node.getClassName());
+ out.append("+ \t<");
+ out.append(node.isCheckable() ? "C" : ".");
+ out.append(node.isChecked() ? "c" : ".");
+ out.append(node.isClickable() ? "K" : ".");
+ out.append(node.isEnabled() ? "E" : ".");
+ out.append(node.isFocusable() ? "F" : ".");
+ out.append(node.isFocused() ? "f" : ".");
+ out.append(node.isLongClickable() ? "L" : ".");
+ out.append(node.isPassword() ? "P" : ".");
+ out.append(node.isScrollable() ? "S" : ".");
+ out.append(node.isSelected() ? "s" : ".");
+ out.append(node.isVisibleToUser() ? "V" : ".");
+ out.append("> ");
+ final Rect bounds = new Rect();
+ node.getBoundsInScreen(bounds);
+ out.append(bounds.toShortString());
+ }
+
+ /**
+ * Produce a displayable string from a CharSequence
+ */
+ private static String escape(CharSequence s) {
+ final StringBuilder out = new StringBuilder();
+ for (int i = 0; i < s.length(); i++) {
+ char c = s.charAt(i);
+ if ((c < 127) || (c == 0xa0) || ((c >= 0x2000) && (c < 0x2070))) {
+ out.append(c);
+ } else {
+ out.append("\\u").append(Integer.toHexString(c));
+ }
+ }
+ return out.toString();
+ }
+
+ private void assertNotEmpty(String name) {
+ assertFalse("Expected non empty name.", TextUtils.isEmpty(name));
+ }
+
+ private UiAutomation getUiAutomation() {
+ // Reuse UiAutomation from UiAutomator with the same flag
+ Configurator.getInstance().setUiAutomationFlags(
+ FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
+ final UiAutomation automation = getInstrumentation().getUiAutomation(
+ FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
+ // Dump window info & node tree
+ final AccessibilityServiceInfo info = automation.getServiceInfo();
+ if (info != null && ((info.flags & FLAG_RETRIEVE_INTERACTIVE_WINDOWS) == 0)) {
+ info.flags |= FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
+ automation.setServiceInfo(info);
+ }
+ return automation;
+ }
+}
diff --git a/tests/accessibility/common/src/android/accessibility/cts/common/InstrumentedAccessibilityService.java b/tests/accessibility/common/src/android/accessibility/cts/common/InstrumentedAccessibilityService.java
index 3f9a344..09af2ee 100644
--- a/tests/accessibility/common/src/android/accessibility/cts/common/InstrumentedAccessibilityService.java
+++ b/tests/accessibility/common/src/android/accessibility/cts/common/InstrumentedAccessibilityService.java
@@ -35,6 +35,7 @@
import android.view.accessibility.AccessibilityManager;
import androidx.annotation.CallSuper;
+import androidx.test.platform.app.InstrumentationRegistry;
import java.lang.ref.WeakReference;
import java.util.HashMap;
@@ -52,7 +53,7 @@
// Match com.android.server.accessibility.AccessibilityManagerService#COMPONENT_NAME_SEPARATOR
private static final String COMPONENT_NAME_SEPARATOR = ":";
- private static final int TIMEOUT_SERVICE_PERFORM_SYNC = DEBUG ? Integer.MAX_VALUE : 5000;
+ private static final int TIMEOUT_SERVICE_PERFORM_SYNC = DEBUG ? Integer.MAX_VALUE : 10000;
private static final HashMap<Class, WeakReference<InstrumentedAccessibilityService>>
sInstances = new HashMap<>();
@@ -119,13 +120,14 @@
public <T extends Object> T getOnService(Callable<T> callable) {
AtomicReference<T> returnValue = new AtomicReference<>(null);
AtomicReference<Throwable> throwable = new AtomicReference<>(null);
- runOnServiceSync(() -> {
- try {
- returnValue.set(callable.call());
- } catch (Throwable e) {
- throwable.set(e);
- }
- });
+ runOnServiceSync(
+ () -> {
+ try {
+ returnValue.set(callable.call());
+ } catch (Throwable e) {
+ throwable.set(e);
+ }
+ });
if (throwable.get() != null) {
throw new RuntimeException(throwable.get());
}
@@ -167,24 +169,27 @@
}
public static <T extends InstrumentedAccessibilityService> T enableService(
- Instrumentation instrumentation, Class<T> clazz) {
+ Class<T> clazz) {
+ final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
final String serviceName = clazz.getSimpleName();
final Context context = instrumentation.getContext();
- final String enabledServices = Settings.Secure.getString(
- context.getContentResolver(),
- Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
+ final String enabledServices =
+ Settings.Secure.getString(
+ context.getContentResolver(),
+ Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
if (enabledServices != null) {
assertFalse("Service is already enabled", enabledServices.contains(serviceName));
}
- final AccessibilityManager manager = (AccessibilityManager) context.getSystemService(
- Context.ACCESSIBILITY_SERVICE);
+ final AccessibilityManager manager =
+ (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
final List<AccessibilityServiceInfo> serviceInfos =
manager.getInstalledAccessibilityServiceList();
for (AccessibilityServiceInfo serviceInfo : serviceInfos) {
final String serviceId = serviceInfo.getId();
if (serviceId.endsWith(serviceName)) {
ShellCommandBuilder.create(instrumentation)
- .putSecureSetting(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+ .putSecureSetting(
+ Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
enabledServices + COMPONENT_NAME_SEPARATOR + serviceId)
.putSecureSetting(Settings.Secure.ACCESSIBILITY_ENABLED, "1")
.run();
@@ -192,11 +197,15 @@
final T instance = getInstanceForClass(clazz, TIMEOUT_SERVICE_ENABLE);
if (instance == null) {
ShellCommandBuilder.create(instrumentation)
- .putSecureSetting(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
- enabledServices)
+ .putSecureSetting(
+ Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, enabledServices)
.run();
- throw new RuntimeException("Starting accessibility service " + serviceName
- + " took longer than " + TIMEOUT_SERVICE_ENABLE + "ms");
+ throw new RuntimeException(
+ "Starting accessibility service "
+ + serviceName
+ + " took longer than "
+ + TIMEOUT_SERVICE_ENABLE
+ + "ms");
}
return instance;
}
@@ -204,19 +213,14 @@
throw new RuntimeException("Accessibility service " + serviceName + " not found");
}
- public static <T extends InstrumentedAccessibilityService> T getInstanceForClass(Class clazz,
- long timeoutMillis) {
+ public static <T extends InstrumentedAccessibilityService> T getInstanceForClass(
+ Class<T> clazz, long timeoutMillis) {
final long timeoutTimeMillis = SystemClock.uptimeMillis() + timeoutMillis;
while (SystemClock.uptimeMillis() < timeoutTimeMillis) {
synchronized (sInstances) {
- final WeakReference<InstrumentedAccessibilityService> ref = sInstances.get(clazz);
- if (ref != null) {
- final T instance = (T) ref.get();
- if (instance == null) {
- sInstances.remove(clazz);
- } else {
- return instance;
- }
+ final T instance = getInstanceForClass(clazz);
+ if (instance != null) {
+ return instance;
}
try {
sInstances.wait(timeoutTimeMillis - SystemClock.uptimeMillis());
@@ -228,19 +232,37 @@
return null;
}
- public static void disableAllServices(Instrumentation instrumentation) {
+ static <T extends InstrumentedAccessibilityService> T getInstanceForClass(
+ Class<T> clazz) {
+ synchronized (sInstances) {
+ final WeakReference<InstrumentedAccessibilityService> ref = sInstances.get(clazz);
+ if (ref != null) {
+ final T instance = (T) ref.get();
+ if (instance == null) {
+ sInstances.remove(clazz);
+ } else {
+ return instance;
+ }
+ }
+ }
+ return null;
+ }
+
+ public static void disableAllServices() {
+ final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
final Object waitLockForA11yOff = new Object();
final Context context = instrumentation.getContext();
final AccessibilityManager manager =
(AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
// Updates to manager.isEnabled() aren't synchronized
final AtomicBoolean accessibilityEnabled = new AtomicBoolean(manager.isEnabled());
- manager.addAccessibilityStateChangeListener(b -> {
- synchronized (waitLockForA11yOff) {
- waitLockForA11yOff.notifyAll();
- accessibilityEnabled.set(b);
- }
- });
+ manager.addAccessibilityStateChangeListener(
+ b -> {
+ synchronized (waitLockForA11yOff) {
+ waitLockForA11yOff.notifyAll();
+ accessibilityEnabled.set(b);
+ }
+ });
final UiAutomation uiAutomation = instrumentation.getUiAutomation(
UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
ShellCommandBuilder.create(uiAutomation)
diff --git a/tests/accessibility/common/src/android/accessibility/cts/common/InstrumentedAccessibilityServiceTestRule.java b/tests/accessibility/common/src/android/accessibility/cts/common/InstrumentedAccessibilityServiceTestRule.java
new file mode 100644
index 0000000..1684dbb
--- /dev/null
+++ b/tests/accessibility/common/src/android/accessibility/cts/common/InstrumentedAccessibilityServiceTestRule.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.accessibility.cts.common;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import android.util.Log;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * A JUnit rule that provides a simplified mechanism to enable and disable {@link
+ * InstrumentedAccessibilityService} before and after the duration of your test. It will
+ * automatically be disabled after the test completes and any methods annotated with
+ * <a href="http://junit.sourceforge.net/javadoc/org/junit/After.html"><code>After</code></a>
+ * are finished.
+ *
+ * <p>Usage:
+ *
+ * <pre>
+ * @Rule
+ * public final InstrumentedAccessibilityServiceTestRule<InstrumentedAccessibilityService>
+ * mServiceRule = new InstrumentedAccessibilityServiceTestRule<>(
+ * InstrumentedAccessibilityService.class, false);
+ *
+ * @Test
+ * public void testWithEnabledAccessibilityService() {
+ * MyService service = mServiceRule.enableService();
+ * //do something
+ * assertTrue("True wasn't returned", service.doSomethingToReturnTrue());
+ * }
+ * </pre>
+ *
+ * @param <T> The instrumented accessibility service class under test
+ */
+public class InstrumentedAccessibilityServiceTestRule<T extends InstrumentedAccessibilityService>
+ implements TestRule {
+
+ private static final String TAG = "InstrA11yServiceTestRule";
+
+ private final Class<T> mAccessibilityServiceClass;
+
+ private final boolean mEnableService;
+
+ /**
+ * Creates a {@link InstrumentedAccessibilityServiceTestRule} with the specified class of
+ * instrumented accessibility service and enable the service automatically.
+ *
+ * @param clazz The instrumented accessibility service under test. This must be a class in the
+ * instrumentation targetPackage specified in the AndroidManifest.xml
+ */
+ public InstrumentedAccessibilityServiceTestRule(@NonNull Class<T> clazz) {
+ this(clazz, true);
+ }
+
+ /**
+ * Creates a {@link InstrumentedAccessibilityServiceTestRule} with the specified class of
+ * instrumented accessibility service, and enable the service automatically or not according to
+ * given {@code enableService}.
+ *
+ * @param clazz The instrumented accessibility service under test. This must be a class in the
+ * instrumentation targetPackage specified in the AndroidManifest.xml
+ * @param enableService true if the service should be enabled once per <a
+ * href="http://junit.org/javadoc/latest/org/junit/Test.html"><code>Test</code></a> method.
+ * It will be enabled before the first <a
+ * href="http://junit.sourceforge.net/javadoc/org/junit/Before.html"><code>Before</code></a>
+ * method, and terminated after the last <a
+ * href="http://junit.sourceforge.net/javadoc/org/junit/After.html"><code>After</code></a>
+ * method.
+ */
+ public InstrumentedAccessibilityServiceTestRule(@NonNull Class<T> clazz,
+ boolean enableService) {
+ mAccessibilityServiceClass = clazz;
+ mEnableService = enableService;
+ }
+
+ /**
+ * Enable the instrumented accessibility service under test.
+ *
+ * <p>Don't call this method directly, unless you explicitly requested not to lazily enable the
+ * service manually using the enableService flag in {@link
+ * #InstrumentedAccessibilityServiceTestRule(Class, boolean)}.
+ *
+ * <p>Usage:
+ *
+ * <pre>
+ * @Test
+ * public void enableAccessibilityService() {
+ * service = mServiceRule.enableService();
+ * }
+ * </pre>
+ *
+ * @return the instrumented accessibility service enabled by this rule.
+ */
+ @NonNull
+ public T enableService() {
+ return InstrumentedAccessibilityService.enableService(mAccessibilityServiceClass);
+ }
+
+ /**
+ * Override this method to do your own service specific clean up or shutdown.
+ * The method is called after each test method is executed including any method annotated with
+ * <a href="http://junit.sourceforge.net/javadoc/org/junit/After.html"><code>After</code></a>
+ * and after necessary calls to stop (or unbind) the service under test were called.
+ */
+ protected void disableService() {
+ callFinishOnServiceSync();
+ }
+
+ /**
+ * Returns the reference to the instrumented accessibility service instance.
+ *
+ * <p>If the service wasn't enabled yet or already disabled, {@code null} will be returned.
+ */
+ @Nullable
+ public T getService() {
+ final T instance = InstrumentedAccessibilityService.getInstanceForClass(
+ mAccessibilityServiceClass,
+ InstrumentedAccessibilityService.TIMEOUT_SERVICE_ENABLE);
+ if (instance == null) {
+ Log.i(TAG, String.format(
+ "Accessibility service %s wasn't enabled yet or already disabled",
+ mAccessibilityServiceClass.getSimpleName()));
+ }
+ return instance;
+ }
+
+ private void callFinishOnServiceSync() {
+ final T service = InstrumentedAccessibilityService.getInstanceForClass(
+ mAccessibilityServiceClass);
+ if (service != null) {
+ service.runOnServiceSync(service::disableSelfAndRemove);
+ }
+ }
+
+ @Override
+ public Statement apply(Statement base, Description description) {
+ return new ServiceStatement(base);
+ }
+
+ /**
+ * {@link Statement} that executes the service lifecycle methods before and after the execution
+ * of the test.
+ */
+ private class ServiceStatement extends Statement {
+ private final Statement base;
+
+ public ServiceStatement(Statement base) {
+ this.base = base;
+ }
+
+ @Override
+ public void evaluate() throws Throwable {
+ try {
+ if (mEnableService) {
+ enableService();
+ }
+ base.evaluate();
+ } finally {
+ disableService();
+ }
+ }
+ }
+}
diff --git a/tests/accessibility/common/src/android/accessibility/cts/common/ShellCommandBuilder.java b/tests/accessibility/common/src/android/accessibility/cts/common/ShellCommandBuilder.java
index c305ceb..cfeb1fd 100644
--- a/tests/accessibility/common/src/android/accessibility/cts/common/ShellCommandBuilder.java
+++ b/tests/accessibility/common/src/android/accessibility/cts/common/ShellCommandBuilder.java
@@ -21,6 +21,8 @@
import android.os.ParcelFileDescriptor;
import android.util.Log;
+import com.android.compatibility.common.util.SystemUtil;
+
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
@@ -88,6 +90,25 @@
return this;
}
+ public ShellCommandBuilder addCommandPrintOnLogCat(String command) {
+ mCommands.add(() -> {
+ execShellCommandAndPrintOnLogcat(mUiAutomation, command);
+ });
+ return this;
+ }
+
+ public static void execShellCommandAndPrintOnLogcat(UiAutomation automation, String command) {
+ Log.i(LOG_TAG, "command [" + command + "]");
+ try {
+ final String output = SystemUtil.runShellCommand(automation, command);
+ for (String line : output.split("\\n", -1)) {
+ Log.i(LOG_TAG, line);
+ }
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to exec shell command [" + command + "]", e);
+ }
+ }
+
public static void execShellCommand(UiAutomation automation, String command) {
Log.i(LOG_TAG, "command [" + command + "]");
try (ParcelFileDescriptor fd = automation.executeShellCommand(command)) {
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityDelegateTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityDelegateTest.java
index fcc4f6e..d147b83 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityDelegateTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityDelegateTest.java
@@ -22,6 +22,7 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
@@ -36,6 +37,7 @@
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
/**
@@ -47,10 +49,18 @@
private LinearLayout mParentView;
private View mChildView;
- @Rule
- public ActivityTestRule<DummyActivity> mActivityRule =
+ private ActivityTestRule<DummyActivity> mActivityRule =
new ActivityTestRule<>(DummyActivity.class, false, false);
+ private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+ new AccessibilityDumpOnFailureRule();
+
+ @Rule
+ public final RuleChain mRuleChain = RuleChain
+ .outerRule(mActivityRule)
+ // Inner rule capture failure and dump data before finishing activity
+ .around(mDumpOnFailureRule);
+
@Before
public void setUp() throws Exception {
Activity activity = mActivityRule.launchActivity(null);
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityEventTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityEventTest.java
index 509092f..b140f37 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityEventTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityEventTest.java
@@ -16,27 +16,45 @@
package android.view.accessibility.cts;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
import android.os.Message;
import android.os.Parcel;
import android.platform.test.annotations.Presubmit;
-import android.test.suitebuilder.annotation.SmallTest;
import android.text.TextUtils;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityRecord;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
import junit.framework.TestCase;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
/**
* Class for testing {@link AccessibilityEvent}.
*/
@Presubmit
-public class AccessibilityEventTest extends TestCase {
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityEventTest {
+
+ @Rule
+ public final AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+ new AccessibilityDumpOnFailureRule();
+
/**
* Tests whether accessibility events are correctly written and
* read from a parcel (version 1).
*/
@SmallTest
+ @Test
public void testMarshaling() throws Exception {
// fully populate the event to marshal
AccessibilityEvent sentEvent = AccessibilityEvent.obtain();
@@ -58,6 +76,7 @@
* Tests if {@link AccessibilityEvent} are properly reused.
*/
@SmallTest
+ @Test
public void testReuse() {
AccessibilityEvent firstEvent = AccessibilityEvent.obtain();
firstEvent.recycle();
@@ -69,6 +88,7 @@
* Tests if {@link AccessibilityEvent} are properly recycled.
*/
@SmallTest
+ @Test
public void testRecycle() {
// obtain and populate an event
AccessibilityEvent populatedEvent = AccessibilityEvent.obtain();
@@ -86,6 +106,7 @@
* Tests whether the event types are correctly converted to strings.
*/
@SmallTest
+ @Test
public void testEventTypeToString() {
assertEquals("TYPE_NOTIFICATION_STATE_CHANGED", AccessibilityEvent.eventTypeToString(
AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED));
@@ -123,6 +144,7 @@
* Tests whether the event describes its contents consistently.
*/
@SmallTest
+ @Test
public void testDescribeContents() {
AccessibilityEvent event = AccessibilityEvent.obtain();
assertSame("Accessibility events always return 0 for this method.", 0,
@@ -137,6 +159,7 @@
* read from a parcel (version 2).
*/
@SmallTest
+ @Test
public void testMarshaling2() {
AccessibilityEvent marshaledEvent = AccessibilityEvent.obtain();
fullyPopulateAccessibilityEvent(marshaledEvent);
@@ -155,6 +178,7 @@
* can't change the object by changing the objects backing CharSequence
*/
@SmallTest
+ @Test
public void testChangeTextAfterSetting_shouldNotAffectEvent() {
final String originalText = "Cassowary";
final String newText = "Hornbill";
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityGestureEventTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityGestureEventTest.java
new file mode 100644
index 0000000..abf9be8
--- /dev/null
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityGestureEventTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.view.accessibility.cts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
+import android.accessibilityservice.AccessibilityGestureEvent;
+import android.accessibilityservice.AccessibilityService;
+import android.os.Parcel;
+import android.platform.test.annotations.Presubmit;
+import android.view.Display;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Class for testing {@link android.accessibilityservice.AccessibilityGestureEvent}.
+ */
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityGestureEventTest {
+
+ @Rule
+ public final AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+ new AccessibilityDumpOnFailureRule();
+
+ private static final int SENT_GESTURE = AccessibilityService.GESTURE_SWIPE_DOWN;
+ private static final int TARGET_DISPLAY = Display.DEFAULT_DISPLAY;
+
+ @SmallTest
+ @Test
+ public void testMarshaling() {
+
+ // Fully populate the gesture info to marshal.
+ AccessibilityGestureEvent sentGestureEvent = new AccessibilityGestureEvent(
+ SENT_GESTURE, TARGET_DISPLAY);
+
+ // Marshal and unmarshal the gesture info.
+ Parcel parcel = Parcel.obtain();
+ sentGestureEvent.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ AccessibilityGestureEvent receivedGestureEvent =
+ AccessibilityGestureEvent.CREATOR.createFromParcel(parcel);
+
+ // Make sure all fields properly marshaled.
+ assertEqualsGestureEvent(sentGestureEvent, receivedGestureEvent);
+
+ parcel.recycle();
+ }
+
+ /**
+ * Tests whether the value of Getter method is as same as the parameter of the constructor.
+ *
+ */
+ @SmallTest
+ @Test
+ public void testGetterMethods() {
+ AccessibilityGestureEvent actualGesture = new AccessibilityGestureEvent(SENT_GESTURE,
+ TARGET_DISPLAY);
+
+ assertEquals("getGestureId is different from parameter of constructor", SENT_GESTURE,
+ actualGesture.getGestureId());
+ assertEquals("getDisplayId is different from parameter of constructor", TARGET_DISPLAY,
+ actualGesture.getDisplayId());
+ }
+
+ /**
+ * Tests whether the gesture describes its contents consistently.
+ */
+ @SmallTest
+ @Test
+ public void testDescribeContents() {
+ AccessibilityGestureEvent event1 = new AccessibilityGestureEvent(SENT_GESTURE,TARGET_DISPLAY);
+ assertSame("accessibility gesture infos always return 0 for this method.", 0,
+ event1.describeContents());
+ AccessibilityGestureEvent event2 = new AccessibilityGestureEvent(
+ AccessibilityService.GESTURE_SWIPE_LEFT, TARGET_DISPLAY);
+ assertSame("accessibility gesture infos always return 0 for this method.", 0,
+ event2.describeContents());
+ }
+
+ private void assertEqualsGestureEvent(AccessibilityGestureEvent sentGestureEvent,
+ AccessibilityGestureEvent receivedGestureEvent) {
+ assertEquals("getDisplayId has incorrectValue", sentGestureEvent.getDisplayId(),
+ receivedGestureEvent.getDisplayId());
+ assertEquals("getGestureId has incorrectValue", sentGestureEvent.getGestureId(),
+ receivedGestureEvent.getGestureId());
+ }
+}
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityManagerTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityManagerTest.java
index a681baf..b0d217d 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityManagerTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityManagerTest.java
@@ -28,7 +28,9 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
import android.accessibility.cts.common.InstrumentedAccessibilityService;
+import android.accessibility.cts.common.InstrumentedAccessibilityServiceTestRule;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.app.Instrumentation;
import android.app.Service;
@@ -50,9 +52,10 @@
import com.android.compatibility.common.util.PollingCheck;
import com.android.compatibility.common.util.SystemUtil;
-import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
import java.io.IOException;
@@ -65,6 +68,30 @@
@RunWith(AndroidJUnit4.class)
public class AccessibilityManagerTest {
+ private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+ new AccessibilityDumpOnFailureRule();
+
+ private InstrumentedAccessibilityServiceTestRule<SpeakingAccessibilityService>
+ mSpeakingAccessibilityServiceRule = new InstrumentedAccessibilityServiceTestRule<>(
+ SpeakingAccessibilityService.class, false);
+
+ private InstrumentedAccessibilityServiceTestRule<VibratingAccessibilityService>
+ mVibratingAccessibilityServiceRule = new InstrumentedAccessibilityServiceTestRule<>(
+ VibratingAccessibilityService.class, false);
+
+ private InstrumentedAccessibilityServiceTestRule<SpeakingAndVibratingAccessibilityService>
+ mSpeakingAndVibratingAccessibilityServiceRule =
+ new InstrumentedAccessibilityServiceTestRule<>(
+ SpeakingAndVibratingAccessibilityService.class, false);
+
+ @Rule
+ public final RuleChain mRuleChain = RuleChain
+ .outerRule(mSpeakingAndVibratingAccessibilityServiceRule)
+ .around(mVibratingAccessibilityServiceRule)
+ .around(mSpeakingAccessibilityServiceRule)
+ // Inner rule capture failure and dump data before finishing activity and a11y service
+ .around(mDumpOnFailureRule);
+
private static final Instrumentation sInstrumentation =
InstrumentationRegistry.getInstrumentation();
@@ -97,12 +124,7 @@
mHandler = new Handler(mTargetContext.getMainLooper());
// In case the test runner started a UiAutomation, destroy it to start with a clean slate.
sInstrumentation.getUiAutomation().destroy();
- InstrumentedAccessibilityService.disableAllServices(sInstrumentation);
- }
-
- @After
- public void tearDown() throws Exception {
- InstrumentedAccessibilityService.disableAllServices(sInstrumentation);
+ InstrumentedAccessibilityService.disableAllServices();
}
@Test
@@ -127,8 +149,8 @@
@Test
public void testIsTouchExplorationEnabled() throws Exception {
- SpeakingAccessibilityService.enableSelf(sInstrumentation);
- VibratingAccessibilityService.enableSelf(sInstrumentation);
+ mSpeakingAccessibilityServiceRule.enableService();
+ mVibratingAccessibilityServiceRule.enableService();
new PollingCheck() {
@Override
protected boolean check() {
@@ -163,8 +185,8 @@
@Test
public void testGetEnabledAccessibilityServiceList() throws Exception {
- SpeakingAccessibilityService.enableSelf(sInstrumentation);
- VibratingAccessibilityService.enableSelf(sInstrumentation);
+ mSpeakingAccessibilityServiceRule.enableService();
+ mVibratingAccessibilityServiceRule.enableService();
List<AccessibilityServiceInfo> enabledServices =
mAccessibilityManager.getEnabledAccessibilityServiceList(
AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
@@ -189,8 +211,8 @@
@Test
public void testGetEnabledAccessibilityServiceListForType() throws Exception {
- SpeakingAccessibilityService.enableSelf(sInstrumentation);
- VibratingAccessibilityService.enableSelf(sInstrumentation);
+ mSpeakingAccessibilityServiceRule.enableService();
+ mVibratingAccessibilityServiceRule.enableService();
List<AccessibilityServiceInfo> enabledServices =
mAccessibilityManager.getEnabledAccessibilityServiceList(
AccessibilityServiceInfo.FEEDBACK_SPOKEN);
@@ -209,10 +231,10 @@
@Test
public void testGetEnabledAccessibilityServiceListForTypes() throws Exception {
- SpeakingAccessibilityService.enableSelf(sInstrumentation);
- VibratingAccessibilityService.enableSelf(sInstrumentation);
+ mSpeakingAccessibilityServiceRule.enableService();
+ mVibratingAccessibilityServiceRule.enableService();
// For this test, also enable a service with multiple feedback types
- SpeakingAndVibratingAccessibilityService.enableSelf(sInstrumentation);
+ mSpeakingAndVibratingAccessibilityServiceRule.enableService();
List<AccessibilityServiceInfo> enabledServices =
mAccessibilityManager.getEnabledAccessibilityServiceList(
@@ -272,8 +294,8 @@
public void testInterrupt() throws Exception {
// The APIs are heavily tested in the android.accessibilityservice package.
// This just makes sure the call does not throw an exception.
- SpeakingAccessibilityService.enableSelf(sInstrumentation);
- VibratingAccessibilityService.enableSelf(sInstrumentation);
+ mSpeakingAccessibilityServiceRule.enableService();
+ mVibratingAccessibilityServiceRule.enableService();
waitForAccessibilityEnabled();
mAccessibilityManager.interrupt();
}
@@ -282,8 +304,8 @@
public void testSendAccessibilityEvent() throws Exception {
// The APIs are heavily tested in the android.accessibilityservice package.
// This just makes sure the call does not throw an exception.
- SpeakingAccessibilityService.enableSelf(sInstrumentation);
- VibratingAccessibilityService.enableSelf(sInstrumentation);
+ mSpeakingAccessibilityServiceRule.enableService();
+ mVibratingAccessibilityServiceRule.enableService();
waitForAccessibilityEnabled();
mAccessibilityManager.sendAccessibilityEvent(AccessibilityEvent.obtain(
AccessibilityEvent.TYPE_VIEW_CLICKED));
@@ -301,13 +323,13 @@
}
};
mAccessibilityManager.addTouchExplorationStateChangeListener(listener);
- SpeakingAccessibilityService.enableSelf(sInstrumentation);
- VibratingAccessibilityService.enableSelf(sInstrumentation);
+ mSpeakingAccessibilityServiceRule.enableService();
+ mVibratingAccessibilityServiceRule.enableService();
assertAtomicBooleanBecomes(atomicBoolean, true, waitObject,
"Touch exploration state listener not called when services enabled");
assertTrue("Listener told that touch exploration is enabled, but manager says disabled",
mAccessibilityManager.isTouchExplorationEnabled());
- InstrumentedAccessibilityService.disableAllServices(sInstrumentation);
+ InstrumentedAccessibilityService.disableAllServices();
assertAtomicBooleanBecomes(atomicBoolean, false, waitObject,
"Touch exploration state listener not called when services disabled");
assertFalse("Listener told that touch exploration is disabled, but manager says it enabled",
@@ -327,13 +349,13 @@
}
};
mAccessibilityManager.addTouchExplorationStateChangeListener(listener, mHandler);
- SpeakingAccessibilityService.enableSelf(sInstrumentation);
- VibratingAccessibilityService.enableSelf(sInstrumentation);
+ mSpeakingAccessibilityServiceRule.enableService();
+ mVibratingAccessibilityServiceRule.enableService();
assertAtomicBooleanBecomes(atomicBoolean, true, waitObject,
"Touch exploration state listener not called when services enabled");
assertTrue("Listener told that touch exploration is enabled, but manager says disabled",
mAccessibilityManager.isTouchExplorationEnabled());
- InstrumentedAccessibilityService.disableAllServices(sInstrumentation);
+ InstrumentedAccessibilityService.disableAllServices();
assertAtomicBooleanBecomes(atomicBoolean, false, waitObject,
"Touch exploration state listener not called when services disabled");
assertFalse("Listener told that touch exploration is disabled, but manager says it enabled",
@@ -353,12 +375,12 @@
}
};
mAccessibilityManager.addAccessibilityStateChangeListener(listener);
- SpeakingAndVibratingAccessibilityService.enableSelf(sInstrumentation);
+ mSpeakingAndVibratingAccessibilityServiceRule.enableService();
assertAtomicBooleanBecomes(atomicBoolean, true, waitObject,
"Accessibility state listener not called when services enabled");
assertTrue("Listener told that accessibility is enabled, but manager says disabled",
mAccessibilityManager.isEnabled());
- InstrumentedAccessibilityService.disableAllServices(sInstrumentation);
+ InstrumentedAccessibilityService.disableAllServices();
assertAtomicBooleanBecomes(atomicBoolean, false, waitObject,
"Accessibility state listener not called when services disabled");
assertFalse("Listener told that accessibility is disabled, but manager says enabled",
@@ -378,12 +400,12 @@
}
};
mAccessibilityManager.addAccessibilityStateChangeListener(listener, mHandler);
- SpeakingAndVibratingAccessibilityService.enableSelf(sInstrumentation);
+ mSpeakingAndVibratingAccessibilityServiceRule.enableService();
assertAtomicBooleanBecomes(atomicBoolean, true, waitObject,
"Accessibility state listener not called when services enabled");
assertTrue("Listener told that accessibility is enabled, but manager says disabled",
mAccessibilityManager.isEnabled());
- InstrumentedAccessibilityService.disableAllServices(sInstrumentation);
+ InstrumentedAccessibilityService.disableAllServices();
assertAtomicBooleanBecomes(atomicBoolean, false, waitObject,
"Accessibility state listener not called when services disabled");
assertFalse("Listener told that accessibility is disabled, but manager says enabled",
@@ -393,8 +415,8 @@
@Test
public void testGetRecommendedTimeoutMillis() throws Exception {
- SpeakingAccessibilityService.enableSelf(sInstrumentation);
- VibratingAccessibilityService.enableSelf(sInstrumentation);
+ mSpeakingAccessibilityServiceRule.enableService();
+ mVibratingAccessibilityServiceRule.enableService();
waitForAccessibilityEnabled();
UiAutomation automan = sInstrumentation.getUiAutomation(
UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
index 0923a92..c48d402 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
@@ -16,15 +16,26 @@
package android.view.accessibility.cts;
+import static androidx.test.InstrumentationRegistry.getContext;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
+import android.graphics.Bitmap;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Bundle;
import android.os.Parcel;
import android.platform.test.annotations.Presubmit;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
import android.text.InputType;
+import android.text.Spannable;
+import android.text.SpannableString;
import android.text.TextUtils;
+import android.text.style.ImageSpan;
import android.util.ArrayMap;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
@@ -35,6 +46,13 @@
import android.view.accessibility.AccessibilityNodeInfo.RangeInfo;
import android.view.accessibility.AccessibilityNodeInfo.TouchDelegateInfo;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -43,9 +61,15 @@
* Class for testing {@link AccessibilityNodeInfo}.
*/
@Presubmit
-public class AccessibilityNodeInfoTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityNodeInfoTest {
+
+ @Rule
+ public final AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+ new AccessibilityDumpOnFailureRule();
@SmallTest
+ @Test
public void testMarshaling() throws Exception {
// fully populate the node info to marshal
AccessibilityNodeInfo sentInfo = AccessibilityNodeInfo.obtain(new View(getContext()));
@@ -67,6 +91,7 @@
* Tests if {@link AccessibilityNodeInfo}s are properly reused.
*/
@SmallTest
+ @Test
public void testReuse() {
AccessibilityEvent firstInfo = AccessibilityEvent.obtain();
firstInfo.recycle();
@@ -78,6 +103,7 @@
* Tests if {@link AccessibilityNodeInfo} are properly recycled.
*/
@SmallTest
+ @Test
public void testRecycle() {
// obtain and populate an node info
AccessibilityNodeInfo populatedInfo = AccessibilityNodeInfo.obtain();
@@ -95,6 +121,7 @@
* Tests whether the event describes its contents consistently.
*/
@SmallTest
+ @Test
public void testDescribeContents() {
AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
assertSame("Accessibility node infos always return 0 for this method.", 0,
@@ -108,6 +135,7 @@
* Tests whether accessibility actions are properly added.
*/
@SmallTest
+ @Test
public void testAddActions() {
List<AccessibilityAction> customActions = new ArrayList<AccessibilityAction>();
customActions.add(new AccessibilityAction(AccessibilityNodeInfo.ACTION_FOCUS, "Foo"));
@@ -133,6 +161,7 @@
* Tests whether we catch addition of an action with invalid id.
*/
@SmallTest
+ @Test
public void testCreateInvalidActionId() {
try {
new AccessibilityAction(3, null);
@@ -145,6 +174,7 @@
* Tests whether accessibility actions are properly removed.
*/
@SmallTest
+ @Test
public void testRemoveActions() {
AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
@@ -173,6 +203,7 @@
* can't change the object by changing the objects backing CharSequence
*/
@SmallTest
+ @Test
public void testChangeTextAfterSetting_shouldNotAffectInfo() {
final String originalText = "Cassowaries";
final String newText = "Hornbill";
@@ -181,6 +212,7 @@
info.setText(updatingString);
info.setError(updatingString);
info.setContentDescription(updatingString);
+ info.setStateDescription(updatingString);
updatingString.delete(0, updatingString.length());
updatingString.append(newText);
@@ -188,9 +220,31 @@
assertTrue(TextUtils.equals(originalText, info.getText()));
assertTrue(TextUtils.equals(originalText, info.getError()));
assertTrue(TextUtils.equals(originalText, info.getContentDescription()));
+ assertTrue(TextUtils.equals(originalText, info.getStateDescription()));
}
@SmallTest
+ @Test
+ public void testSetTextWithImageSpan_shouldTextSetToInfo() {
+ final Bitmap bitmap = Bitmap.createBitmap(/* width= */10, /* height= */10,
+ Bitmap.Config.ARGB_8888);
+ final ImageSpan imageSpan = new ImageSpan(getContext(), bitmap);
+ final String testString = "test string";
+ final String replacementString = " ";
+ final SpannableString textWithImageSpan = new SpannableString(testString);
+ final int indexIconStart = testString.indexOf(replacementString);
+ final int indexIconEnd = indexIconStart + replacementString.length();
+ textWithImageSpan.setSpan(imageSpan, /* start= */indexIconStart,/* end= */indexIconEnd,
+ /* flags= */Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+
+ AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
+ info.setText(textWithImageSpan);
+
+ assertEquals(testString, info.getText().toString());
+ }
+
+ @SmallTest
+ @Test
public void testIsHeadingTakesOldApiIntoAccount() {
final AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
assertFalse(info.isHeading());
@@ -213,6 +267,7 @@
info.setBoundsInScreen(new Rect(2,2,2,2));
info.setClassName("foo.bar.baz.Class");
info.setContentDescription("content description");
+ info.setStateDescription("state description");
info.setTooltipText("tooltip");
info.setPackageName("foo.bar.baz");
info.setText("text");
@@ -346,6 +401,8 @@
receivedInfo.getClassName());
assertEquals("contentDescription has incorrect value", expectedInfo.getContentDescription(),
receivedInfo.getContentDescription());
+ assertEquals("stateDescription has incorrect value", expectedInfo.getStateDescription(),
+ receivedInfo.getStateDescription());
assertEquals("tooltip text has incorrect value", expectedInfo.getTooltipText(),
receivedInfo.getTooltipText());
assertEquals("packageName has incorrect value", expectedInfo.getPackageName(),
@@ -399,11 +456,11 @@
(receivedRange != null));
if (expectedRange != null) {
assertEquals("RangeInfo#getCurrent has incorrect value", expectedRange.getCurrent(),
- receivedRange.getCurrent());
+ receivedRange.getCurrent(), 0.0);
assertEquals("RangeInfo#getMin has incorrect value", expectedRange.getMin(),
- receivedRange.getMin());
+ receivedRange.getMin(), 0.0);
assertEquals("RangeInfo#getMax has incorrect value", expectedRange.getMax(),
- receivedRange.getMax());
+ receivedRange.getMax(), 0.0);
assertEquals("RangeInfo#getType has incorrect value", expectedRange.getType(),
receivedRange.getType());
}
@@ -528,6 +585,7 @@
assertTrue("boundsInScreen not properly recycled", bounds.isEmpty());
assertNull("className not properly recycled", info.getClassName());
assertNull("contentDescription not properly recycled", info.getContentDescription());
+ assertNull("stateDescription not properly recycled", info.getStateDescription());
assertNull("tooltiptext not properly recycled", info.getTooltipText());
assertNull("packageName not properly recycled", info.getPackageName());
assertNull("text not properly recycled", info.getText());
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfo_CollectionInfoTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfo_CollectionInfoTest.java
index 98b87fc..cabfd98 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfo_CollectionInfoTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfo_CollectionInfoTest.java
@@ -16,18 +16,34 @@
package android.view.accessibility.cts;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
import android.platform.test.annotations.Presubmit;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
import android.view.accessibility.AccessibilityNodeInfo.CollectionInfo;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
/**
* Class for testing {@link CollectionInfo}.
*/
@Presubmit
-public class AccessibilityNodeInfo_CollectionInfoTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityNodeInfo_CollectionInfoTest {
+
+ @Rule
+ public final AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+ new AccessibilityDumpOnFailureRule();
@SmallTest
+ @Test
public void testObtain() {
CollectionInfo c;
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfo_RangeInfoTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfo_RangeInfoTest.java
index 4b01129..e7761f4 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfo_RangeInfoTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfo_RangeInfoTest.java
@@ -16,22 +16,36 @@
package android.view.accessibility.cts;
+import static org.junit.Assert.assertEquals;
+
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
import android.platform.test.annotations.Presubmit;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.RangeInfo;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
/**
* Class for testing {@link AccessibilityNodeInfo.RangeInfo}.
*/
@Presubmit
-public class AccessibilityNodeInfo_RangeInfoTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityNodeInfo_RangeInfoTest {
/** Allowed tolerance for floating point equality comparisons. */
public static final float FLOAT_TOLERANCE = 0.001f;
+ @Rule
+ public final AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+ new AccessibilityDumpOnFailureRule();
+
@SmallTest
+ @Test
public void testObtain() {
RangeInfo r;
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeProviderTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeProviderTest.java
index 0c2a2dd..60bc1a3 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeProviderTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeProviderTest.java
@@ -16,17 +16,33 @@
package android.view.accessibility.cts;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
import android.platform.test.annotations.Presubmit;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
import android.view.accessibility.AccessibilityNodeProvider;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
/**
* Class for testing {@link AccessibilityNodeProvider}.
*/
@Presubmit
-public class AccessibilityNodeProviderTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityNodeProviderTest {
+
+ @Rule
+ public final AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+ new AccessibilityDumpOnFailureRule();
+
@SmallTest
+ @Test
public void testDefaultBehavior() {
AccessibilityNodeProvider p = new AccessibilityNodeProvider() {
// Class is abstract, but has no abstract methods.
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityRecordTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityRecordTest.java
index 72bb4e0..119de9f 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityRecordTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityRecordTest.java
@@ -16,17 +16,23 @@
package android.view.accessibility.cts;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
import android.os.Message;
import android.platform.test.annotations.Presubmit;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityRecord;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
import junit.framework.TestCase;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import java.util.Iterator;
import java.util.List;
@@ -34,11 +40,18 @@
* Class for testing {@link AccessibilityRecord}.
*/
@Presubmit
-public class AccessibilityRecordTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityRecordTest {
+
+ @Rule
+ public final AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+ new AccessibilityDumpOnFailureRule();
+
/**
* Tests the cloning obtain method.
*/
@SmallTest
+ @Test
public void testObtain() {
AccessibilityRecord originalRecord = AccessibilityRecord.obtain();
fullyPopulateAccessibilityRecord(originalRecord);
@@ -50,6 +63,7 @@
* Tests if {@link AccessibilityRecord}s are properly recycled.
*/
@SmallTest
+ @Test
public void testRecycle() {
// obtain and populate an event
AccessibilityRecord populatedRecord = AccessibilityRecord.obtain();
@@ -84,10 +98,10 @@
TestCase.assertEquals("removedCount not properly recycled", -1, record.getRemovedCount());
TestCase.assertTrue("text not properly recycled", record.getText().isEmpty());
TestCase.assertFalse("scrollable not properly recycled", record.isScrollable());
- TestCase.assertSame("maxScrollX not properly recycled", -1, record.getMaxScrollX());
- TestCase.assertSame("maxScrollY not properly recycled", -1, record.getMaxScrollY());
- TestCase.assertSame("scrollX not properly recycled", -1, record.getScrollX());
- TestCase.assertSame("scrollY not properly recycled", -1, record.getScrollY());
+ TestCase.assertSame("maxScrollX not properly recycled", 0, record.getMaxScrollX());
+ TestCase.assertSame("maxScrollY not properly recycled", 0, record.getMaxScrollY());
+ TestCase.assertSame("scrollX not properly recycled", 0, record.getScrollX());
+ TestCase.assertSame("scrollY not properly recycled", 0, record.getScrollY());
TestCase.assertSame("toIndex not properly recycled", -1, record.getToIndex());
}
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityServiceInfoTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityServiceInfoTest.java
index 0029b53..f4abdf1 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityServiceInfoTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityServiceInfoTest.java
@@ -16,14 +16,28 @@
package android.view.accessibility.cts;
-import android.accessibility.cts.common.InstrumentedAccessibilityService;
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
+import android.accessibility.cts.common.InstrumentedAccessibilityServiceTestRule;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.app.Service;
-import android.test.InstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.runner.RunWith;
+
import java.util.List;
/**
@@ -33,18 +47,23 @@
* accessibility service and the fake service used for implementing the UI
* automation is not reported through the APIs.
*/
-public class AccessibilityServiceInfoTest extends InstrumentationTestCase {
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityServiceInfoTest {
- @Override
- public void setUp() throws Exception {
- SpeakingAccessibilityService.enableSelf(getInstrumentation());
- VibratingAccessibilityService.enableSelf(getInstrumentation());
- }
+ private InstrumentedAccessibilityServiceTestRule<SpeakingAccessibilityService>
+ mSpeakingAccessibilityServiceRule = new InstrumentedAccessibilityServiceTestRule<>(
+ SpeakingAccessibilityService.class);
- @Override
- public void tearDown() {
- InstrumentedAccessibilityService.disableAllServices(getInstrumentation());
- }
+ private InstrumentedAccessibilityServiceTestRule<VibratingAccessibilityService>
+ mVibratingAccessibilityServiceRule = new InstrumentedAccessibilityServiceTestRule<>(
+ VibratingAccessibilityService.class);
+
+ @Rule
+ public final RuleChain mRuleChain = RuleChain
+ .outerRule(mVibratingAccessibilityServiceRule)
+ .around(mSpeakingAccessibilityServiceRule)
+ // Inner rule capture failure and dump data before finishing a11y service
+ .around(new AccessibilityDumpOnFailureRule());
/**
* Tests whether a service can that requested it can retrieve
@@ -52,6 +71,7 @@
*/
@MediumTest
@SuppressWarnings("deprecation")
+ @Test
public void testAccessibilityServiceInfoForEnabledService() {
AccessibilityManager accessibilityManager = (AccessibilityManager)
getInstrumentation().getContext().getSystemService(Service.ACCESSIBILITY_SERVICE);
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityWindowInfoTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityWindowInfoTest.java
index e959544..9d4ecd7 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityWindowInfoTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityWindowInfoTest.java
@@ -16,21 +16,43 @@
package android.view.accessibility.cts;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
import android.graphics.Rect;
+import android.graphics.Region;
import android.os.Parcel;
import android.platform.test.annotations.Presubmit;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
import android.text.TextUtils;
+import android.view.Display;
import android.view.accessibility.AccessibilityWindowInfo;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
/**
* Class for testing {@link AccessibilityWindowInfo}.
*/
@Presubmit
-public class AccessibilityWindowInfoTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityWindowInfoTest {
+
+ @Rule
+ public final AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+ new AccessibilityDumpOnFailureRule();
@SmallTest
+ @Test
public void testObtain() {
AccessibilityWindowInfo w1 = AccessibilityWindowInfo.obtain();
assertNotNull(w1);
@@ -41,6 +63,7 @@
}
@SmallTest
+ @Test
public void testParceling() {
Parcel parcel = Parcel.obtain();
AccessibilityWindowInfo w1 = AccessibilityWindowInfo.obtain();
@@ -48,11 +71,12 @@
parcel.setDataPosition(0);
AccessibilityWindowInfo w2 = AccessibilityWindowInfo.CREATOR.createFromParcel(parcel);
assertNotSame(w1, w2);
- assertTrue(areWindowsEqual(w1, w2));
+ assertTrue(w2.toString(), areWindowsEqual(w1, w2));
parcel.recycle();
}
@SmallTest
+ @Test
public void testDefaultValues() {
AccessibilityWindowInfo w = AccessibilityWindowInfo.obtain();
assertEquals(0, w.getChildCount());
@@ -60,6 +84,7 @@
assertEquals(-1, w.getLayer());
assertEquals(-1, w.getId());
assertEquals(0, w.describeContents());
+ assertEquals(Display.INVALID_DISPLAY, w.getDisplayId());
assertNull(w.getParent());
assertNull(w.getRoot());
assertFalse(w.isAccessibilityFocused());
@@ -71,6 +96,10 @@
w.getBoundsInScreen(rect);
assertTrue(rect.isEmpty());
+ Region region = new Region();
+ w.getRegionInScreen(region);
+ assertTrue(region.isEmpty());
+
try {
w.getChild(0);
fail("Expected IndexOutOfBoundsException");
@@ -80,6 +109,7 @@
}
@SmallTest
+ @Test
public void testRecycle() {
AccessibilityWindowInfo w = AccessibilityWindowInfo.obtain();
w.recycle();
@@ -99,11 +129,17 @@
equality &= w1.isActive() == w2.isActive();
equality &= w1.getType() == w2.getType();
equality &= w1.getLayer() == w2.getLayer();
+ equality &= w1.getDisplayId() == w2.getDisplayId();
Rect bounds1 = new Rect();
Rect bounds2 = new Rect();
w1.getBoundsInScreen(bounds1);
w2.getBoundsInScreen(bounds2);
equality &= bounds1.equals(bounds2);
+ Region regions1 = new Region();
+ Region regions2 = new Region();
+ w1.getRegionInScreen(regions1);
+ w2.getRegionInScreen(regions2);
+ equality &= regions1.equals(regions2);
return equality;
}
}
diff --git a/tests/accessibility/src/android/view/accessibility/cts/CaptioningManagerTest.java b/tests/accessibility/src/android/view/accessibility/cts/CaptioningManagerTest.java
index d0db868..c3054ef 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/CaptioningManagerTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/CaptioningManagerTest.java
@@ -16,6 +16,12 @@
package android.view.accessibility.cts;
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.mockito.Matchers.anyFloat;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Mockito.mock;
@@ -23,13 +29,19 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
import android.app.UiAutomation;
import android.os.ParcelFileDescriptor;
-import android.test.InstrumentationTestCase;
import android.view.accessibility.CaptioningManager;
import android.view.accessibility.CaptioningManager.CaptionStyle;
import android.view.accessibility.CaptioningManager.CaptioningChangeListener;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.Mockito;
import java.io.FileInputStream;
@@ -40,15 +52,19 @@
/**
* Tests whether the CaptioningManager APIs are functional.
*/
-public class CaptioningManagerTest extends InstrumentationTestCase {
+@RunWith(AndroidJUnit4.class)
+public class CaptioningManagerTest {
+
+ @Rule
+ public final AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+ new AccessibilityDumpOnFailureRule();
+
private static final int LISTENER_TIMEOUT = 3000;
private CaptioningManager mManager;
private UiAutomation mUiAutomation;
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
+ @Before
+ public void setUp() throws Exception {
mManager = getInstrumentation().getTargetContext().getSystemService(
CaptioningManager.class);
@@ -60,6 +76,7 @@
/**
* Tests whether a client can observe changes in caption properties.
*/
+ @Test
public void testChangeListener() {
putSecureSetting("accessibility_captioning_enabled","0");
putSecureSetting("accessibility_captioning_preset", "1");
@@ -97,16 +114,18 @@
}
}
+ @Test
public void testProperties() {
putSecureSetting("accessibility_captioning_font_scale", "2.0");
putSecureSetting("accessibility_captioning_locale", "ja_JP");
putSecureSetting("accessibility_captioning_enabled", "1");
- assertEquals("Test runner set font scale to 2.0", 2.0f, mManager.getFontScale());
+ assertEquals("Test runner set font scale to 2.0", 2.0f, mManager.getFontScale(), 0f);
assertEquals("Test runner set locale to Japanese", Locale.JAPAN, mManager.getLocale());
assertEquals("Test runner set enabled to true", true, mManager.isEnabled());
}
+ @Test
public void testUserStyle() {
putSecureSetting("accessibility_captioning_preset", "-1");
putSecureSetting("accessibility_captioning_foreground_color", "511");
diff --git a/tests/accessibility/src/android/view/accessibility/cts/SpeakingAccessibilityService.java b/tests/accessibility/src/android/view/accessibility/cts/SpeakingAccessibilityService.java
index d05232a..8a18f58 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/SpeakingAccessibilityService.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/SpeakingAccessibilityService.java
@@ -17,7 +17,6 @@
package android.view.accessibility.cts;
import android.accessibility.cts.common.InstrumentedAccessibilityService;
-import android.app.Instrumentation;
import android.content.ComponentName;
/**
@@ -27,9 +26,4 @@
public static final ComponentName COMPONENT_NAME = new ComponentName(
"android.view.accessibility.cts",
"android.view.accessibility.cts.SpeakingAccessibilityService");
-
- public static SpeakingAccessibilityService enableSelf(Instrumentation instrumentation) {
- return InstrumentedAccessibilityService.enableService(
- instrumentation, SpeakingAccessibilityService.class);
- }
}
diff --git a/tests/accessibility/src/android/view/accessibility/cts/SpeakingAndVibratingAccessibilityService.java b/tests/accessibility/src/android/view/accessibility/cts/SpeakingAndVibratingAccessibilityService.java
index cb8126d..c7c9844 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/SpeakingAndVibratingAccessibilityService.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/SpeakingAndVibratingAccessibilityService.java
@@ -17,15 +17,9 @@
package android.view.accessibility.cts;
import android.accessibility.cts.common.InstrumentedAccessibilityService;
-import android.app.Instrumentation;
/**
* Stub accessibility service that reports itself as providing multiple feedback types.
*/
public class SpeakingAndVibratingAccessibilityService extends InstrumentedAccessibilityService {
- public static SpeakingAndVibratingAccessibilityService enableSelf(
- Instrumentation instrumentation) {
- return InstrumentedAccessibilityService.enableService(instrumentation,
- SpeakingAndVibratingAccessibilityService.class);
- }
}
diff --git a/tests/accessibility/src/android/view/accessibility/cts/VibratingAccessibilityService.java b/tests/accessibility/src/android/view/accessibility/cts/VibratingAccessibilityService.java
index 6b866aa..bec74e3 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/VibratingAccessibilityService.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/VibratingAccessibilityService.java
@@ -17,14 +17,9 @@
package android.view.accessibility.cts;
import android.accessibility.cts.common.InstrumentedAccessibilityService;
-import android.app.Instrumentation;
/**
* Stub accessibility service that reports itself as providing haptic feedback.
*/
public class VibratingAccessibilityService extends InstrumentedAccessibilityService {
- public static VibratingAccessibilityService enableSelf(Instrumentation instrumentation) {
- return InstrumentedAccessibilityService.enableService(instrumentation,
- VibratingAccessibilityService.class);
- }
}
diff --git a/tests/accessibilityservice/Android.bp b/tests/accessibilityservice/Android.bp
index b37f5cb..d96b545 100644
--- a/tests/accessibilityservice/Android.bp
+++ b/tests/accessibilityservice/Android.bp
@@ -19,6 +19,7 @@
"ctstestrunner-axt",
"hamcrest-library",
"mockito-target-minus-junit4",
+ "compatibility-device-util-axt",
"platform-test-annotations",
"CtsAccessibilityCommon",
],
diff --git a/tests/accessibilityservice/AndroidManifest.xml b/tests/accessibilityservice/AndroidManifest.xml
index fc0b2e6..be1799a 100644
--- a/tests/accessibilityservice/AndroidManifest.xml
+++ b/tests/accessibilityservice/AndroidManifest.xml
@@ -20,10 +20,12 @@
android:targetSandboxVersion="2">
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
- <application android:theme="@android:style/Theme.Holo.NoActionBar">
+ <application android:theme="@android:style/Theme.Holo.NoActionBar"
+ android:requestLegacyExternalStorage="true">
<uses-library android:name="android.test.runner" />
@@ -67,6 +69,17 @@
android:label="@string/accessibility_soft_keyboard_modes_activity"
android:name=".AccessibilitySoftKeyboardModesTest$SoftKeyboardModesActivity" />
+ <activity
+ android:label="@string/accessibility_embedded_display_test_parent_activity"
+ android:name=".AccessibilityEmbeddedDisplayTest$EmbeddedDisplayParentActivity"
+ android:theme="@android:style/Theme.Dialog"
+ android:screenOrientation="locked" />
+
+ <activity
+ android:label="@string/accessibility_embedded_display_test_activity"
+ android:name=".AccessibilityEmbeddedDisplayTest$EmbeddedDisplayActivity"
+ android:screenOrientation="locked" />
+
<service
android:name=".StubGestureAccessibilityService"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
@@ -93,6 +106,17 @@
</service>
<service
+ android:name=".TouchExplorationStubAccessibilityService"
+ android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
+ <intent-filter>
+ <action android:name="android.accessibilityservice.AccessibilityService" />
+ <category android:name="android.accessibilityservice.category.FEEDBACK_GENERIC" />
+ </intent-filter>
+ <meta-data
+ android:name="android.accessibilityservice"
+ android:resource="@xml/stub_touch_exploration_a11y_service" />
+ </service>
+ <service
android:name="android.accessibility.cts.common.InstrumentedAccessibilityService"
android:label="@string/title_soft_keyboard_modes_accessibility_service"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
diff --git a/tests/accessibilityservice/AndroidTest.xml b/tests/accessibilityservice/AndroidTest.xml
index eb75538..b0e873c 100644
--- a/tests/accessibilityservice/AndroidTest.xml
+++ b/tests/accessibilityservice/AndroidTest.xml
@@ -34,4 +34,8 @@
<option name="package" value="android.accessibilityservice.cts" />
<option name="runtime-hint" value="2m12s" />
</test>
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="directory-keys" value="/sdcard/android.accessibilityservice.cts" />
+ <option name="collect-on-run-ended-only" value="true" />
+ </metrics_collector>
</configuration>
diff --git a/tests/accessibilityservice/OWNERS b/tests/accessibilityservice/OWNERS
index d225f2c..e54f581 100644
--- a/tests/accessibilityservice/OWNERS
+++ b/tests/accessibilityservice/OWNERS
@@ -1,2 +1,3 @@
# Bug component: 44214
-pweaver@google.com
\ No newline at end of file
+pweaver@google.com
+rhedjao@google.com
diff --git a/tests/accessibilityservice/res/layout/accessibility_embedded_display_test.xml b/tests/accessibilityservice/res/layout/accessibility_embedded_display_test.xml
new file mode 100644
index 0000000..666bfbe
--- /dev/null
+++ b/tests/accessibilityservice/res/layout/accessibility_embedded_display_test.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:orientation="vertical">
+
+ <Button
+ android:id="@+id/button"
+ android:text="@string/button_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+</LinearLayout>
diff --git a/tests/accessibilityservice/res/layout/accessibility_view_tree_reporting_test.xml b/tests/accessibilityservice/res/layout/accessibility_view_tree_reporting_test.xml
index 0dbd62a..e05c259 100644
--- a/tests/accessibilityservice/res/layout/accessibility_view_tree_reporting_test.xml
+++ b/tests/accessibilityservice/res/layout/accessibility_view_tree_reporting_test.xml
@@ -88,6 +88,14 @@
android:text="@string/secondButton"
android:textSize="10dip" />
+ <Button
+ android:id="@+id/hiddenButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/secondButton"
+ android:textSize="10dip"
+ android:visibility="gone" />
+
</LinearLayout>
</FrameLayout>
diff --git a/tests/accessibilityservice/res/values/strings.xml b/tests/accessibilityservice/res/values/strings.xml
index ad5d3fd..4dd1981 100644
--- a/tests/accessibilityservice/res/values/strings.xml
+++ b/tests/accessibilityservice/res/values/strings.xml
@@ -139,6 +139,8 @@
<string name="a_b">A B</string>
+ <string name="testContentDescription">testContentDescription</string>
+
<string name="german_text_with_strong_s">ß</string>
<string name="android_wiki_search">Android is a Linux-based</string>
@@ -147,7 +149,9 @@
<string name="stub_gesture_dispatch_a11y_service_description">com.android.accessibilityservice.cts.StubGestureAccessibilityService</string>
- <string name="stub_gesture_detector_a11y_service_description">com.android.accessibilityservice.cts.StubService</string>
+ <string name="stub_gesture_detector_a11y_service_description">com.android.accessibilityservice.cts.GestureDetectionStubAccessibilityService</string>
+
+ <string name="stub_touch_exploration_a11y_service_description">com.android.accessibilityservice.cts.TouchExplorationStubAccessibilityService</string>
<string name="stub_fprint_a11y_service_description">com.android.accessibilityservice.cts.StubFingerprintGestureAccessibilityService</string>
@@ -170,4 +174,12 @@
<!-- Description of the accessibility service -->
<string name="soft_keyboard_modes_accessibility_service_description">This Accessibility Service was installed for testing purposes. It can be uninstalled by going to Settings > Apps > android.view.accessibilityservice.services and selecting \"Uninstall\".</string>
+ <!-- AccessibilityEmbeddedDisplayTest -->
+
+ <!-- String title of accessibility embedded display test parent window activity -->
+ <string name="accessibility_embedded_display_test_parent_activity">Embedded display test parent</string>
+
+ <!-- String title of accessibility embedded display test activity -->
+ <string name="accessibility_embedded_display_test_activity">Embedded display test</string>
+
</resources>
diff --git a/tests/accessibilityservice/res/xml/stub_touch_exploration_a11y_service.xml b/tests/accessibilityservice/res/xml/stub_touch_exploration_a11y_service.xml
new file mode 100644
index 0000000..33c5ec8
--- /dev/null
+++ b/tests/accessibilityservice/res/xml/stub_touch_exploration_a11y_service.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<accessibility-service
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:description="@string/stub_touch_exploration_a11y_service_description"
+ android:accessibilityEventTypes="typeAllMask"
+ android:accessibilityFeedbackType="feedbackGeneric"
+ android:accessibilityFlags="flagDefault|flagRequestTouchExplorationMode|flagReportViewIds"
+ android:canRequestTouchExplorationMode="true"
+ android:canRetrieveWindowContent="true"
+ android:canPerformGestures="true" />
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityButtonTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityButtonTest.java
index 84fb0ef..9ecebcf 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityButtonTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityButtonTest.java
@@ -14,18 +14,20 @@
package android.accessibilityservice.cts;
-import static android.accessibilityservice.cts.utils.CtsTestUtils.runIfNotNull;
+import static org.junit.Assert.assertNotNull;
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
+import android.accessibility.cts.common.InstrumentedAccessibilityServiceTestRule;
import android.accessibilityservice.AccessibilityButtonController;
-import android.app.Instrumentation;
import android.platform.test.annotations.AppModeFull;
+import android.view.Display;
-import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
-import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
/**
@@ -37,6 +39,17 @@
@RunWith(AndroidJUnit4.class)
public class AccessibilityButtonTest {
+ private InstrumentedAccessibilityServiceTestRule<StubAccessibilityButtonService> mServiceRule =
+ new InstrumentedAccessibilityServiceTestRule<>(StubAccessibilityButtonService.class);
+
+ private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+ new AccessibilityDumpOnFailureRule();
+
+ @Rule
+ public final RuleChain mRuleChain = RuleChain
+ .outerRule(mServiceRule)
+ .around(mDumpOnFailureRule);
+
private StubAccessibilityButtonService mService;
private AccessibilityButtonController mButtonController;
private AccessibilityButtonController.AccessibilityButtonCallback mStubCallback =
@@ -55,20 +68,23 @@
@Before
public void setUp() {
- Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
- mService = StubAccessibilityButtonService.enableSelf(instrumentation);
+ mService = mServiceRule.getService();
mButtonController = mService.getAccessibilityButtonController();
}
- @After
- public void tearDown() {
- runIfNotNull(mService, service -> service.runOnServiceSync(service::disableSelf));
- }
-
@Test
@AppModeFull
public void testCallbackRegistrationUnregistration_serviceDoesNotCrash() {
mButtonController.registerAccessibilityButtonCallback(mStubCallback);
mButtonController.unregisterAccessibilityButtonCallback(mStubCallback);
}
+
+ @Test
+ @AppModeFull
+ public void testGetAccessibilityButtonControllerByDisplayId_NotReturnNull() {
+ final AccessibilityButtonController buttonController =
+ mService.getAccessibilityButtonController(
+ Display.DEFAULT_DISPLAY);
+ assertNotNull(buttonController);
+ }
}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEmbeddedDisplayTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEmbeddedDisplayTest.java
new file mode 100644
index 0000000..9166356
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEmbeddedDisplayTest.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.accessibilityservice.cts;
+
+import static android.accessibilityservice.cts.utils.AccessibilityEventFilterUtils.filterWindowsChangeTypesAndWindowTitle;
+import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.findWindowByTitle;
+import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.launchActivityAndWaitForItToBeOnscreen;
+import static android.accessibilityservice.cts.utils.AsyncUtils.DEFAULT_TIMEOUT_MS;
+import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
+import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_BOUNDS;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
+import android.accessibilityservice.cts.activities.AccessibilityTestActivity;
+import android.app.Activity;
+import android.app.ActivityView;
+import android.app.Instrumentation;
+import android.app.UiAutomation;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.platform.test.annotations.Presubmit;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityWindowInfo;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests that AccessibilityWindowInfos and AccessibilityNodeInfos from a window on an embedded
+ * display that is re-parented to another window are properly populated.
+ */
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityEmbeddedDisplayTest {
+ private static Instrumentation sInstrumentation;
+ private static UiAutomation sUiAutomation;
+
+ private EmbeddedDisplayParentActivity mActivity;
+ private ActivityView mActivityView;
+ private Context mContext;
+
+ private String mParentActivityTitle;
+ private String mActivityTitle;
+
+ private final ActivityTestRule<EmbeddedDisplayParentActivity> mActivityRule =
+ new ActivityTestRule<>(EmbeddedDisplayParentActivity.class, false, false);
+
+ private final AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+ new AccessibilityDumpOnFailureRule();
+
+ @Rule
+ public final RuleChain mRuleChain = RuleChain
+ .outerRule(mActivityRule)
+ .around(mDumpOnFailureRule);
+
+ @BeforeClass
+ public static void oneTimeSetup() {
+ sInstrumentation = InstrumentationRegistry.getInstrumentation();
+ sUiAutomation = sInstrumentation.getUiAutomation();
+ }
+
+ @AfterClass
+ public static void postTestTearDown() {
+ sUiAutomation.destroy();
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = sInstrumentation.getContext();
+ assumeTrue(supportsMultiDisplay());
+
+ mParentActivityTitle = mContext.getString(
+ R.string.accessibility_embedded_display_test_parent_activity);
+ mActivityTitle = mContext.getString(R.string.accessibility_embedded_display_test_activity);
+
+ SystemUtil.runWithShellPermissionIdentity(() -> {
+ mActivity = launchActivityAndWaitForItToBeOnscreen(
+ sInstrumentation, sUiAutomation, mActivityRule);
+ mActivityView = mActivity.getActivityView();
+ });
+
+ launchActivityInActivityView();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (mActivityView != null) {
+ SystemUtil.runWithShellPermissionIdentity(() -> mActivityView.release());
+ }
+ }
+
+ @Presubmit
+ @Test
+ public void testA11yWindowInfoHasCorrectLayer() {
+ final AccessibilityWindowInfo parentActivityWindow =
+ findWindowByTitle(sUiAutomation, mParentActivityTitle);
+ final AccessibilityWindowInfo activityWindow =
+ findWindowByTitle(sUiAutomation, mActivityTitle);
+
+ assertNotNull(parentActivityWindow);
+ assertNotNull(activityWindow);
+ assertTrue(parentActivityWindow.getLayer() > activityWindow.getLayer());
+ }
+
+ @Presubmit
+ @Test
+ public void testA11yWindowInfoAndA11yNodeInfoHasCorrectBoundsInScreen() {
+ final AccessibilityWindowInfo parentActivityWindow =
+ findWindowByTitle(sUiAutomation, mParentActivityTitle);
+ final AccessibilityWindowInfo activityWindow =
+ findWindowByTitle(sUiAutomation, mActivityTitle);
+ final AccessibilityNodeInfo button = findWindowByTitle(sUiAutomation,
+ mActivityTitle).getRoot().findAccessibilityNodeInfosByViewId(
+ "android.accessibilityservice.cts:id/button").get(0);
+
+ assertNotNull(parentActivityWindow);
+ assertNotNull(activityWindow);
+ assertNotNull(button);
+
+ final Rect parentActivityBound = new Rect();
+ final Rect activityBound = new Rect();
+ final Rect buttonBound = new Rect();
+ parentActivityWindow.getBoundsInScreen(parentActivityBound);
+ activityWindow.getBoundsInScreen(activityBound);
+ button.getBoundsInScreen(buttonBound);
+
+ assertTrue("parentActivityBound" + parentActivityBound.toShortString()
+ + " doesn't contain activityBound" + activityBound.toShortString(),
+ parentActivityBound.contains(activityBound));
+ assertTrue("parentActivityBound" + parentActivityBound.toShortString()
+ + " doesn't contain buttonBound" + buttonBound.toShortString(),
+ parentActivityBound.contains(buttonBound));
+ }
+
+ @Test
+ public void testA11yWindowNotifyWhenResizeActivityView() throws Exception {
+ final AccessibilityWindowInfo oldActivityWindow =
+ findWindowByTitle(sUiAutomation, mActivityTitle);
+ final Rect activityBound = new Rect();
+ oldActivityWindow.getBoundsInScreen(activityBound);
+
+ final int width = activityBound.width() / 2;
+ final int height = activityBound.height() / 2;
+ sUiAutomation.executeAndWaitForEvent(() -> sInstrumentation.runOnMainSync(
+ () -> mActivityView.layout(0, 0, width, height)),
+ filterWindowsChangeTypesAndWindowTitle(sUiAutomation, WINDOWS_CHANGE_BOUNDS,
+ mActivityTitle),
+ DEFAULT_TIMEOUT_MS);
+
+ final AccessibilityWindowInfo newActivityWindow =
+ findWindowByTitle(sUiAutomation, mActivityTitle);
+ newActivityWindow.getBoundsInScreen(activityBound);
+
+ assertEquals(height, activityBound.height());
+ assertEquals(width, activityBound.width());
+ }
+
+ private void launchActivityInActivityView() throws Exception {
+ final Rect bounds = new Rect();
+ sUiAutomation.executeAndWaitForEvent(
+ () -> sInstrumentation.runOnMainSync(() -> {
+ Intent intent = new Intent(mContext, EmbeddedDisplayActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ SystemUtil.runWithShellPermissionIdentity(
+ () -> mActivityView.startActivity(intent));
+ }),
+ (event) -> {
+ // Ensure the target activity is shown
+ final AccessibilityWindowInfo window =
+ findWindowByTitle(sUiAutomation, mActivityTitle);
+ if (window == null) {
+ return false;
+ }
+ window.getBoundsInScreen(bounds);
+ return !bounds.isEmpty();
+ }, DEFAULT_TIMEOUT_MS);
+ }
+
+ private boolean supportsMultiDisplay() {
+ return mContext.getPackageManager().hasSystemFeature(
+ FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS);
+ }
+
+ public static class EmbeddedDisplayParentActivity extends Activity {
+ private ActivityView mActivityView;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mActivityView = new ActivityView(this);
+ setContentView(mActivityView);
+ }
+
+ ActivityView getActivityView() {
+ return mActivityView;
+ }
+ }
+
+ public static class EmbeddedDisplayActivity extends AccessibilityTestActivity {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.accessibility_embedded_display_test);
+ }
+ }
+}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
index 60ef850..e9a0fe7 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
@@ -16,6 +16,7 @@
package android.accessibilityservice.cts;
+import static android.accessibility.cts.common.InstrumentedAccessibilityService.enableService;
import static android.accessibilityservice.cts.utils.AccessibilityEventFilterUtils.filterForEventType;
import static android.accessibilityservice.cts.utils.AccessibilityEventFilterUtils.filterForEventTypeWithResource;
import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.findWindowByTitle;
@@ -45,6 +46,7 @@
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
import android.accessibility.cts.common.InstrumentedAccessibilityService;
import android.accessibility.cts.common.ShellCommandBuilder;
import android.accessibilityservice.AccessibilityServiceInfo;
@@ -101,6 +103,7 @@
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
import java.util.Iterator;
@@ -131,10 +134,17 @@
private AccessibilityEndToEndActivity mActivity;
- @Rule
- public ActivityTestRule<AccessibilityEndToEndActivity> mActivityRule =
+ private ActivityTestRule<AccessibilityEndToEndActivity> mActivityRule =
new ActivityTestRule<>(AccessibilityEndToEndActivity.class, false, false);
+ private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+ new AccessibilityDumpOnFailureRule();
+
+ @Rule
+ public final RuleChain mRuleChain = RuleChain
+ .outerRule(mActivityRule)
+ .around(mDumpOnFailureRule);
+
@BeforeClass
public static void oneTimeSetup() throws Exception {
sInstrumentation = InstrumentationRegistry.getInstrumentation();
@@ -502,8 +512,9 @@
public void testInterrupt_notifiesService() {
sInstrumentation
.getUiAutomation(UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
- InstrumentedAccessibilityService service = InstrumentedAccessibilityService.enableService(
- sInstrumentation, InstrumentedAccessibilityService.class);
+ InstrumentedAccessibilityService service =
+ enableService(InstrumentedAccessibilityService.class);
+
try {
assertFalse(service.wasOnInterruptCalled());
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityFingerprintGestureTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityFingerprintGestureTest.java
index 1db66a2..e36d1ee 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityFingerprintGestureTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityFingerprintGestureTest.java
@@ -14,7 +14,6 @@
package android.accessibilityservice.cts;
-import static android.accessibilityservice.cts.utils.CtsTestUtils.runIfNotNull;
import static android.content.pm.PackageManager.FEATURE_FINGERPRINT;
import static org.junit.Assert.assertFalse;
@@ -23,6 +22,8 @@
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
+import android.accessibility.cts.common.InstrumentedAccessibilityServiceTestRule;
import android.accessibilityservice.FingerprintGestureController;
import android.accessibilityservice.FingerprintGestureController.FingerprintGestureCallback;
import android.accessibilityservice.cts.activities.AccessibilityEndToEndActivity;
@@ -35,10 +36,10 @@
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
-import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -57,10 +58,20 @@
FingerprintGestureController mFingerprintGestureController;
CancellationSignal mCancellationSignal = new CancellationSignal();
- @Rule
- public ActivityTestRule<AccessibilityEndToEndActivity> mActivityRule =
+ private ActivityTestRule<AccessibilityEndToEndActivity> mActivityRule =
new ActivityTestRule<>(AccessibilityEndToEndActivity.class, false, false);
+ private InstrumentedAccessibilityServiceTestRule<StubFingerprintGestureService> mServiceRule =
+ new InstrumentedAccessibilityServiceTestRule<>(StubFingerprintGestureService.class);
+
+ private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+ new AccessibilityDumpOnFailureRule();
+
+ @Rule
+ public final RuleChain mRuleChain = RuleChain
+ .outerRule(mActivityRule)
+ .around(mServiceRule)
+ .around(mDumpOnFailureRule);
@Mock FingerprintManager.AuthenticationCallback mMockAuthenticationCallback;
@Mock FingerprintGestureCallback mMockFingerprintGestureCallback;
@@ -72,17 +83,11 @@
mFingerprintManager = instrumentation.getContext().getPackageManager()
.hasSystemFeature(FEATURE_FINGERPRINT)
? instrumentation.getContext().getSystemService(FingerprintManager.class) : null;
- mFingerprintGestureService = StubFingerprintGestureService.enableSelf(instrumentation);
+ mFingerprintGestureService = mServiceRule.getService();
mFingerprintGestureController =
mFingerprintGestureService.getFingerprintGestureController();
}
- @After
- public void tearDown() throws Exception {
- runIfNotNull(mFingerprintGestureService,
- service -> service.runOnServiceSync(service::disableSelf));
- }
-
@Test
public void testGestureDetectionListener_whenAuthenticationStartsAndStops_calledBack() {
if (!mFingerprintGestureController.isGestureDetectionAvailable()) {
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityFocusAndInputFocusSyncTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityFocusAndInputFocusSyncTest.java
index 43c12e9..b7ccc19 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityFocusAndInputFocusSyncTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityFocusAndInputFocusSyncTest.java
@@ -26,8 +26,8 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
import android.accessibilityservice.AccessibilityServiceInfo;
-import android.accessibilityservice.cts.R;
import android.accessibilityservice.cts.activities.AccessibilityFocusAndInputFocusSyncActivity;
import android.app.Instrumentation;
import android.app.UiAutomation;
@@ -46,6 +46,7 @@
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
import java.util.LinkedList;
@@ -65,10 +66,17 @@
private AccessibilityFocusAndInputFocusSyncActivity mActivity;
- @Rule
- public ActivityTestRule<AccessibilityFocusAndInputFocusSyncActivity> mActivityRule =
+ private ActivityTestRule<AccessibilityFocusAndInputFocusSyncActivity> mActivityRule =
new ActivityTestRule<>(AccessibilityFocusAndInputFocusSyncActivity.class, false, false);
+ private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+ new AccessibilityDumpOnFailureRule();
+
+ @Rule
+ public final RuleChain mRuleChain = RuleChain
+ .outerRule(mActivityRule)
+ .around(mDumpOnFailureRule);
+
@BeforeClass
public static void oneTimeSetup() throws Exception {
sInstrumentation = InstrumentationRegistry.getInstrumentation();
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDetectorTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDetectorTest.java
index 24c00ff..28e8f67 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDetectorTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDetectorTest.java
@@ -14,17 +14,20 @@
package android.accessibilityservice.cts;
-import static android.accessibilityservice.cts.utils.CtsTestUtils.runIfNotNull;
import static android.accessibilityservice.cts.utils.GestureUtils.click;
import static android.accessibilityservice.cts.utils.GestureUtils.endTimeOf;
import static android.accessibilityservice.cts.utils.GestureUtils.longClick;
import static android.accessibilityservice.cts.utils.GestureUtils.startingAt;
+import static android.accessibilityservice.cts.utils.DisplayUtils.VirtualDisplaySession;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
+import android.accessibility.cts.common.InstrumentedAccessibilityServiceTestRule;
+import android.accessibilityservice.AccessibilityGestureEvent;
import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.GestureDescription;
import android.accessibilityservice.GestureDescription.StrokeDescription;
@@ -36,15 +39,17 @@
import android.graphics.PointF;
import android.platform.test.annotations.AppModeFull;
import android.util.DisplayMetrics;
+import android.view.Display;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
-import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -59,6 +64,18 @@
private static final long GESTURE_DISPATCH_TIMEOUT_MS = 3000;
private static final long EVENT_DISPATCH_TIMEOUT_MS = 3000;
+ private InstrumentedAccessibilityServiceTestRule<GestureDetectionStubAccessibilityService>
+ mServiceRule = new InstrumentedAccessibilityServiceTestRule<>(
+ GestureDetectionStubAccessibilityService.class, false);
+
+ private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+ new AccessibilityDumpOnFailureRule();
+
+ @Rule
+ public final RuleChain mRuleChain = RuleChain
+ .outerRule(mServiceRule)
+ .around(mDumpOnFailureRule);
+
// Test AccessibilityService that collects gestures.
GestureDetectionStubAccessibilityService mService;
boolean mHasTouchScreen;
@@ -97,15 +114,7 @@
return;
}
// Start stub accessibility service.
- mService = GestureDetectionStubAccessibilityService.enableSelf(instrumentation);
- }
-
- @After
- public void tearDown() throws Exception {
- if (!mHasTouchScreen || !mScreenBigEnough) {
- return;
- }
- runIfNotNull(mService, service -> service.runOnServiceSync(service::disableSelf));
+ mService = mServiceRule.enableService();
}
@Test
@@ -142,27 +151,91 @@
testPath(p(+0, +dy), p(+0, +0), AccessibilityService.GESTURE_SWIPE_DOWN_AND_UP);
}
+ @Test
+ @AppModeFull
+ public void testRecognizeGesturePathOnVirtualDisplay() {
+ if (!mHasTouchScreen || !mScreenBigEnough) {
+ return;
+ }
+
+ try (final VirtualDisplaySession displaySession = new VirtualDisplaySession()) {
+ final int displayId = displaySession.createDisplayWithDefaultDisplayMetricsAndWait(
+ InstrumentationRegistry.getInstrumentation().getTargetContext(),
+ false).getDisplayId();
+ // Compute gesture stroke lengths, in pixels.
+ final int dx = mStrokeLenPxX;
+ final int dy = mStrokeLenPxY;
+
+ // Test recognizing various gestures.
+ testPath(p(-dx, +0), AccessibilityService.GESTURE_SWIPE_LEFT, displayId);
+ testPath(p(+dx, +0), AccessibilityService.GESTURE_SWIPE_RIGHT, displayId);
+ testPath(p(+0, -dy), AccessibilityService.GESTURE_SWIPE_UP, displayId);
+ testPath(p(+0, +dy), AccessibilityService.GESTURE_SWIPE_DOWN, displayId);
+
+ testPath(p(-dx, +0), p(+0, +0), AccessibilityService.GESTURE_SWIPE_LEFT_AND_RIGHT,
+ displayId);
+ testPath(p(-dx, +0), p(-dx, -dy), AccessibilityService.GESTURE_SWIPE_LEFT_AND_UP,
+ displayId);
+ testPath(p(-dx, +0), p(-dx, +dy), AccessibilityService.GESTURE_SWIPE_LEFT_AND_DOWN,
+ displayId);
+
+ testPath(p(+dx, +0), p(+0, +0), AccessibilityService.GESTURE_SWIPE_RIGHT_AND_LEFT,
+ displayId);
+ testPath(p(+dx, +0), p(+dx, -dy), AccessibilityService.GESTURE_SWIPE_RIGHT_AND_UP,
+ displayId);
+ testPath(p(+dx, +0), p(+dx, +dy), AccessibilityService.GESTURE_SWIPE_RIGHT_AND_DOWN,
+ displayId);
+
+ testPath(p(+0, -dy), p(-dx, -dy), AccessibilityService.GESTURE_SWIPE_UP_AND_LEFT,
+ displayId);
+ testPath(p(+0, -dy), p(+dx, -dy), AccessibilityService.GESTURE_SWIPE_UP_AND_RIGHT,
+ displayId);
+ testPath(p(+0, -dy), p(+0, +0), AccessibilityService.GESTURE_SWIPE_UP_AND_DOWN,
+ displayId);
+
+ testPath(p(+0, +dy), p(-dx, +dy), AccessibilityService.GESTURE_SWIPE_DOWN_AND_LEFT,
+ displayId);
+ testPath(p(+0, +dy), p(+dx, +dy), AccessibilityService.GESTURE_SWIPE_DOWN_AND_RIGHT,
+ displayId);
+ testPath(p(+0, +dy), p(+0, +0), AccessibilityService.GESTURE_SWIPE_DOWN_AND_UP,
+ displayId);
+ }
+ }
+
/** Convenient short alias to make a Point. */
private static Point p(int x, int y) {
return new Point(x, y);
}
- /** Test recognizing path from PATH_START to PATH_START+delta. */
+ /** Test recognizing path from PATH_START to PATH_START+delta on default display. */
private void testPath(Point delta, int gestureId) {
- testPath(delta, null, gestureId);
+ testPath(delta, null, gestureId, Display.DEFAULT_DISPLAY);
}
- /** Test recognizing path from PATH_START to PATH_START+delta1 to PATH_START+delta2. */
+ /** Test recognizing path from PATH_START to PATH_START+delta on specified display. */
+ private void testPath(Point delta, int gestureId, int displayId) {
+ testPath(delta, null, gestureId, displayId);
+ }
+ /** Test recognizing path from PATH_START to PATH_START+delta on default display. */
private void testPath(Point delta1, Point delta2, int gestureId) {
+ testPath(delta1, delta2, gestureId, Display.DEFAULT_DISPLAY);
+ }
+
+ /**
+ * Test recognizing path from PATH_START to PATH_START+delta1 to PATH_START+delta2. on specified
+ * display.
+ */
+ private void testPath(Point delta1, Point delta2, int gestureId, int displayId) {
// Create gesture motions.
int numPathSegments = (delta2 == null) ? 1 : 2;
long pathDurationMs = numPathSegments * STROKE_MS;
GestureDescription gesture = new GestureDescription.Builder()
.addStroke(new StrokeDescription(
linePath(mCenter, delta1, delta2), 0, pathDurationMs, false))
+ .setDisplayId(displayId)
.build();
- // Dispatch gesture motions.
+ // Dispatch gesture motions to specified display with GestureDescription..
// Use AccessibilityService.dispatchGesture() instead of Instrumentation.sendPointerSync()
// because accessibility services read gesture events upstream from the point where
// sendPointerSync() injects events.
@@ -173,9 +246,16 @@
.onCompleted(any());
// Wait for gesture recognizer, and check recognized gesture.
- mService.waitUntilGesture();
- assertEquals(1, mService.getGesturesSize());
- assertEquals(gestureId, mService.getGesture(0));
+ mService.waitUntilGestureInfo();
+ if(displayId == Display.DEFAULT_DISPLAY) {
+ assertEquals(1, mService.getGesturesSize());
+ assertEquals(gestureId, mService.getGesture(0));
+ }
+
+ AccessibilityGestureEvent expectedGestureEvent = new AccessibilityGestureEvent(gestureId,
+ displayId);
+ assertEquals(1, mService.getGestureInfoSize());
+ assertEquals(expectedGestureEvent.toString(), mService.getGestureInfo(0).toString());
}
/** Create a path from startPoint, moving by delta1, then delta2. (delta2 may be null.) */
@@ -196,25 +276,74 @@
return;
}
- assertEventAfterGesture(swipe(),
+ assertEventAfterGesture(swipe(Display.DEFAULT_DISPLAY),
AccessibilityEvent.TYPE_TOUCH_INTERACTION_START,
AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
- assertEventAfterGesture(tap(),
+ assertEventAfterGesture(tap(Display.DEFAULT_DISPLAY),
AccessibilityEvent.TYPE_TOUCH_INTERACTION_START,
AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START,
AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END,
AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
- assertEventAfterGesture(doubleTap(),
+ assertEventAfterGesture(doubleTap(Display.DEFAULT_DISPLAY),
AccessibilityEvent.TYPE_TOUCH_INTERACTION_START,
AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
- assertEventAfterGesture(doubleTapAndHold(),
+ assertEventAfterGesture(doubleTapAndHold(Display.DEFAULT_DISPLAY),
AccessibilityEvent.TYPE_TOUCH_INTERACTION_START,
AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
}
+ @Test
+ @AppModeFull
+ public void testVerifyGestureTouchEventOnVirtualDisplay() {
+ if (!mHasTouchScreen || !mScreenBigEnough) {
+ return;
+ }
+
+ try (final VirtualDisplaySession displaySession = new VirtualDisplaySession()) {
+ final int displayId = displaySession.createDisplayWithDefaultDisplayMetricsAndWait(
+ InstrumentationRegistry.getInstrumentation().getTargetContext(),
+ false).getDisplayId();
+
+ assertEventAfterGesture(swipe(displayId),
+ AccessibilityEvent.TYPE_TOUCH_INTERACTION_START,
+ AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
+
+ assertEventAfterGesture(tap(displayId),
+ AccessibilityEvent.TYPE_TOUCH_INTERACTION_START,
+ AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START,
+ AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END,
+ AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
+
+ assertEventAfterGesture(doubleTap(displayId),
+ AccessibilityEvent.TYPE_TOUCH_INTERACTION_START,
+ AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
+
+ assertEventAfterGesture(doubleTapAndHold(displayId),
+ AccessibilityEvent.TYPE_TOUCH_INTERACTION_START,
+ AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
+ }
+ }
+
+ @Test
+ @AppModeFull
+ public void testDispatchGesture_privateDisplay_gestureCancelled() {
+ try (final VirtualDisplaySession displaySession = new VirtualDisplaySession()) {
+ final int displayId = displaySession.createDisplayWithDefaultDisplayMetricsAndWait
+ (InstrumentationRegistry.getInstrumentation().getTargetContext(),
+ true).getDisplayId();
+ GestureDescription gesture = swipe(displayId);
+ mService.clearGestures();
+ mService.runOnServiceSync(() ->
+ mService.dispatchGesture(gesture, mGestureDispatchCallback, null));
+
+ verify(mGestureDispatchCallback, timeout(GESTURE_DISPATCH_TIMEOUT_MS).atLeastOnce())
+ .onCancelled(any());
+ }
+ }
+
/** Test touch for accessibility events */
private void assertEventAfterGesture(GestureDescription gesture, int... events) {
mService.clearEvents();
@@ -230,36 +359,40 @@
}
}
- private GestureDescription swipe() {
+ private GestureDescription swipe(int displayId) {
GestureDescription.Builder builder = new GestureDescription.Builder();
StrokeDescription swipe = new StrokeDescription(
linePath(mCenter, p(0, mStrokeLenPxY), null), 0, STROKE_MS, false);
builder.addStroke(swipe);
+ builder.setDisplayId(displayId);
return builder.build();
}
- private GestureDescription tap() {
+ private GestureDescription tap(int displayId) {
GestureDescription.Builder builder = new GestureDescription.Builder();
StrokeDescription tap = click(mTapLocation);
builder.addStroke(tap);
+ builder.setDisplayId(displayId);
return builder.build();
}
- private GestureDescription doubleTap() {
+ private GestureDescription doubleTap(int displayId) {
GestureDescription.Builder builder = new GestureDescription.Builder();
StrokeDescription tap1 = click(mTapLocation);
StrokeDescription tap2 = startingAt(endTimeOf(tap1) + 20, click(mTapLocation));
builder.addStroke(tap1);
builder.addStroke(tap2);
+ builder.setDisplayId(displayId);
return builder.build();
}
- private GestureDescription doubleTapAndHold() {
+ private GestureDescription doubleTapAndHold(int displayId) {
GestureDescription.Builder builder = new GestureDescription.Builder();
StrokeDescription tap1 = click(mTapLocation);
StrokeDescription tap2 = startingAt(endTimeOf(tap1) + 20, longClick(mTapLocation));
builder.addStroke(tap1);
builder.addStroke(tap2);
+ builder.setDisplayId(displayId);
return builder.build();
}
}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDispatchTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDispatchTest.java
index 2c5da19..a56cf07 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDispatchTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDispatchTest.java
@@ -14,31 +14,46 @@
package android.accessibilityservice.cts;
+import static android.accessibility.cts.common.InstrumentedAccessibilityService.enableService;
+import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.launchActivityAndWaitForItToBeOnscreen;
import static android.accessibilityservice.cts.utils.AsyncUtils.await;
import static android.accessibilityservice.cts.utils.AsyncUtils.awaitCancellation;
-import static android.accessibilityservice.cts.utils.CtsTestUtils.runIfNotNull;
+import static android.accessibilityservice.cts.utils.GestureUtils.IS_ACTION_CANCEL;
+import static android.accessibilityservice.cts.utils.GestureUtils.IS_ACTION_DOWN;
+import static android.accessibilityservice.cts.utils.GestureUtils.IS_ACTION_MOVE;
+import static android.accessibilityservice.cts.utils.GestureUtils.IS_ACTION_POINTER_DOWN;
+import static android.accessibilityservice.cts.utils.GestureUtils.IS_ACTION_POINTER_UP;
+import static android.accessibilityservice.cts.utils.GestureUtils.IS_ACTION_UP;
import static android.accessibilityservice.cts.utils.GestureUtils.add;
import static android.accessibilityservice.cts.utils.GestureUtils.ceil;
import static android.accessibilityservice.cts.utils.GestureUtils.click;
import static android.accessibilityservice.cts.utils.GestureUtils.diff;
import static android.accessibilityservice.cts.utils.GestureUtils.dispatchGesture;
+import static android.accessibilityservice.cts.utils.GestureUtils.isAtPoint;
import static android.accessibilityservice.cts.utils.GestureUtils.longClick;
import static android.accessibilityservice.cts.utils.GestureUtils.path;
import static android.accessibilityservice.cts.utils.GestureUtils.times;
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.any;
import static org.hamcrest.CoreMatchers.both;
import static org.hamcrest.CoreMatchers.everyItem;
import static org.hamcrest.CoreMatchers.hasItem;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
+import android.accessibility.cts.common.InstrumentedAccessibilityServiceTestRule;
import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.GestureDescription;
import android.accessibilityservice.GestureDescription.StrokeDescription;
import android.accessibilityservice.cts.activities.AccessibilityTestActivity;
+import android.app.Instrumentation;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Matrix;
@@ -47,7 +62,6 @@
import android.os.Bundle;
import android.os.SystemClock;
import android.platform.test.annotations.AppModeFull;
-import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
import android.view.Display;
import android.view.MotionEvent;
@@ -56,9 +70,15 @@
import android.view.WindowManager;
import android.widget.TextView;
-import org.hamcrest.Description;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
import org.hamcrest.Matcher;
-import org.hamcrest.TypeSafeMatcher;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.List;
@@ -68,26 +88,28 @@
* Verify that gestures dispatched from an accessibility service show up in the current UI
*/
@AppModeFull
-public class AccessibilityGestureDispatchTest extends
- ActivityInstrumentationTestCase2<AccessibilityGestureDispatchTest.GestureDispatchActivity> {
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityGestureDispatchTest {
private static final String TAG = AccessibilityGestureDispatchTest.class.getSimpleName();
private static final int GESTURE_COMPLETION_TIMEOUT = 5000; // millis
private static final int MOTION_EVENT_TIMEOUT = 1000; // millis
- private static final Matcher<MotionEvent> IS_ACTION_DOWN =
- new MotionEventActionMatcher(MotionEvent.ACTION_DOWN);
- private static final Matcher<MotionEvent> IS_ACTION_POINTER_DOWN =
- new MotionEventActionMatcher(MotionEvent.ACTION_POINTER_DOWN);
- private static final Matcher<MotionEvent> IS_ACTION_UP =
- new MotionEventActionMatcher(MotionEvent.ACTION_UP);
- private static final Matcher<MotionEvent> IS_ACTION_POINTER_UP =
- new MotionEventActionMatcher(MotionEvent.ACTION_POINTER_UP);
- private static final Matcher<MotionEvent> IS_ACTION_CANCEL =
- new MotionEventActionMatcher(MotionEvent.ACTION_CANCEL);
- private static final Matcher<MotionEvent> IS_ACTION_MOVE =
- new MotionEventActionMatcher(MotionEvent.ACTION_MOVE);
+ private ActivityTestRule<GestureDispatchActivity> mActivityRule =
+ new ActivityTestRule<>(GestureDispatchActivity.class, false, false);
+ private InstrumentedAccessibilityServiceTestRule<StubGestureAccessibilityService> mServiceRule =
+ new InstrumentedAccessibilityServiceTestRule<>(
+ StubGestureAccessibilityService.class, false);
+
+ private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+ new AccessibilityDumpOnFailureRule();
+
+ @Rule
+ public final RuleChain mRuleChain = RuleChain
+ .outerRule(mActivityRule)
+ .around(mServiceRule)
+ .around(mDumpOnFailureRule);
final List<MotionEvent> mMotionEvents = new ArrayList<>();
StubGestureAccessibilityService mService;
@@ -100,27 +122,25 @@
boolean mHasTouchScreen;
boolean mHasMultiTouch;
- public AccessibilityGestureDispatchTest() {
- super(GestureDispatchActivity.class);
- }
+ private GestureDispatchActivity mActivity;
- @Override
+ @Before
public void setUp() throws Exception {
- super.setUp();
- PackageManager pm = getInstrumentation().getContext().getPackageManager();
+ Instrumentation instrumentation = getInstrumentation();
+ PackageManager pm = instrumentation.getContext().getPackageManager();
mHasTouchScreen = pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN)
|| pm.hasSystemFeature(PackageManager.FEATURE_FAKETOUCH);
if (!mHasTouchScreen) {
return;
}
- getActivity().waitForEnterAnimationComplete();
+ mActivity = launchActivityAndWaitForItToBeOnscreen(instrumentation,
+ instrumentation.getUiAutomation(), mActivityRule);
mHasMultiTouch = pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH)
|| pm.hasSystemFeature(PackageManager.FEATURE_FAKETOUCH_MULTITOUCH_DISTINCT);
- mFullScreenTextView =
- (TextView) getActivity().findViewById(R.id.full_screen_text_view);
+ mFullScreenTextView = mActivity.findViewById(R.id.full_screen_text_view);
getInstrumentation().runOnMainSync(() -> {
final int midX = mFullScreenTextView.getWidth() / 2;
final int midY = mFullScreenTextView.getHeight() / 2;
@@ -129,22 +149,13 @@
mStartPoint.set(mViewLocation[0] + midX, mViewLocation[1] + midY);
});
- mService = StubGestureAccessibilityService.enableSelf(getInstrumentation());
+ mService = mServiceRule.enableService();
mMotionEvents.clear();
mGotUpEvent = false;
}
- @Override
- public void tearDown() throws Exception {
- if (!mHasTouchScreen) {
- return;
- }
-
- runIfNotNull(mService, service -> service.runOnServiceSync(service::disableSelf));
- super.tearDown();
- }
-
+ @Test
public void testClickAt_producesDownThenUp() throws InterruptedException {
if (!mHasTouchScreen) {
return;
@@ -164,10 +175,10 @@
assertEquals(0, clickDown.getActionIndex());
assertEquals(0, clickDown.getDeviceId());
assertEquals(0, clickDown.getEdgeFlags());
- assertEquals(1F, clickDown.getXPrecision());
- assertEquals(1F, clickDown.getYPrecision());
+ assertEquals(1F, clickDown.getXPrecision(), 0F);
+ assertEquals(1F, clickDown.getYPrecision(), 0F);
assertEquals(1, clickDown.getPointerCount());
- assertEquals(1F, clickDown.getPressure());
+ assertEquals(1F, clickDown.getPressure(), 0F);
// Verify timing matches click
assertEquals(clickDown.getDownTime(), clickDown.getEventTime());
@@ -178,6 +189,7 @@
> clickUp.getEventTime());
}
+ @Test
public void testLongClickAt_producesEventsWithLongClickTiming() throws InterruptedException {
if (!mHasTouchScreen) {
return;
@@ -198,6 +210,7 @@
assertEquals(clickDown.getDownTime(), clickUp.getDownTime());
}
+ @Test
public void testSwipe_shouldContainPointsInALine() throws InterruptedException {
if (!mHasTouchScreen) {
return;
@@ -236,6 +249,7 @@
await(dispatchGesture(mService, gesture), timeoutMs, MILLISECONDS);
}
+ @Test
public void testSlowSwipe_shouldNotContainMovesForTinyMovement() throws InterruptedException {
if (!mHasTouchScreen) {
return;
@@ -260,6 +274,7 @@
assertThat(mMotionEvents.get(4), both(IS_ACTION_UP).and(isAtPoint(endPoint)));
}
+ @Test
public void testAngledPinch_looksReasonable() throws InterruptedException {
if (!(mHasTouchScreen && mHasMultiTouch)) {
return;
@@ -309,6 +324,7 @@
// This test assumes device's screen contains its center (W/2, H/2) with some surroundings
// and should work for rectangular, round and round with chin screens.
+ @Test
public void testClickWhenMagnified_matchesActualTouch() throws InterruptedException {
final float POINT_TOL = 2.0f;
final float CLICK_OFFSET_X = 10;
@@ -318,7 +334,7 @@
return;
}
- int displayId = getActivity().getWindow().getDecorView().getDisplay().getDisplayId();
+ int displayId = mActivity.getWindow().getDecorView().getDisplay().getDisplayId();
if (displayId != Display.DEFAULT_DISPLAY) {
Log.i(TAG, "Magnification is not supported on virtual displays.");
return;
@@ -327,7 +343,7 @@
final WindowManager wm = (WindowManager) getInstrumentation().getContext().getSystemService(
Context.WINDOW_SERVICE);
final StubMagnificationAccessibilityService magnificationService =
- StubMagnificationAccessibilityService.enableSelf(getInstrumentation());
+ enableService(StubMagnificationAccessibilityService.class);
final AccessibilityService.MagnificationController
magnificationController = magnificationService.getMagnificationController();
@@ -392,6 +408,7 @@
both(IS_ACTION_UP).and(isAtPoint(magRegionOffsetPoint, POINT_TOL)));
}
+ @Test
public void testContinuedGestures_motionEventsContinue() throws Exception {
if (!mHasTouchScreen) {
return;
@@ -426,6 +443,7 @@
allOf(IS_ACTION_UP, isAtPoint(end)));
}
+ @Test
public void testContinuedGesture_withLineDisconnect_isCancelled() throws Exception {
if (!mHasTouchScreen) {
return;
@@ -454,6 +472,7 @@
assertEquals(1, mMotionEvents.size());
}
+ @Test
public void testContinuedGesture_nextGestureDoesntContinue_isCancelled() throws Exception {
if (!mHasTouchScreen) {
return;
@@ -486,6 +505,7 @@
both(IS_ACTION_UP).and(isAtPoint(endPoint)));
}
+ @Test
public void testContinuingGesture_withNothingToContinue_isCancelled() {
if (!mHasTouchScreen) {
return;
@@ -631,42 +651,4 @@
.addStroke(new StrokeDescription(path2, 0, duration))
.build();
}
-
- private static class MotionEventActionMatcher extends TypeSafeMatcher<MotionEvent> {
- int mAction;
-
- MotionEventActionMatcher(int action) {
- super();
- mAction = action;
- }
-
- @Override
- protected boolean matchesSafely(MotionEvent motionEvent) {
- return motionEvent.getActionMasked() == mAction;
- }
-
- @Override
- public void describeTo(Description description) {
- description.appendText("Matching to action " + MotionEvent.actionToString(mAction));
- }
- }
-
-
- Matcher<MotionEvent> isAtPoint(final PointF point) {
- return isAtPoint(point, 0.01f);
- }
-
- Matcher<MotionEvent> isAtPoint(final PointF point, final float tol) {
- return new TypeSafeMatcher<MotionEvent>() {
- @Override
- protected boolean matchesSafely(MotionEvent event) {
- return Math.hypot(event.getX() - point.x, event.getY() - point.y) < tol;
- }
-
- @Override
- public void describeTo(Description description) {
- description.appendText("Matching to point " + point);
- }
- };
- }
}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGlobalActionsTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGlobalActionsTest.java
index c809f26..b5b5766 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGlobalActionsTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGlobalActionsTest.java
@@ -16,14 +16,30 @@
package android.accessibilityservice.cts;
+import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.homeScreenOrBust;
+
+import static org.junit.Assert.assertTrue;
+
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
import android.accessibilityservice.AccessibilityService;
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.app.Instrumentation;
+import android.app.UiAutomation;
import android.os.SystemClock;
import android.platform.test.annotations.AppModeFull;
import android.platform.test.annotations.Presubmit;
-import android.test.InstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-import androidx.test.filters.FlakyTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
import java.util.concurrent.TimeoutException;
@@ -32,7 +48,8 @@
*/
@Presubmit
@AppModeFull
-public class AccessibilityGlobalActionsTest extends InstrumentationTestCase {
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityGlobalActionsTest {
/**
* Timeout required for pending Binder calls or event processing to
* complete.
@@ -44,109 +61,113 @@
*/
private static final long TIMEOUT_ACCESSIBILITY_STATE_IDLE = 500;
+ private static Instrumentation sInstrumentation;
+ private static UiAutomation sUiAutomation;
+
+ @Rule
+ public final AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+ new AccessibilityDumpOnFailureRule();
+
+ @BeforeClass
+ public static void oneTimeSetup() {
+ sInstrumentation = InstrumentationRegistry.getInstrumentation();
+ sUiAutomation = sInstrumentation.getUiAutomation();
+ AccessibilityServiceInfo info = sUiAutomation.getServiceInfo();
+ info.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
+ sUiAutomation.setServiceInfo(info);
+ }
+
+ @AfterClass
+ public static void postTestTearDown() {
+ sUiAutomation.destroy();
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ // Make sure we start test on home screen so we can know if clean up is successful
+ // or not in the end.
+ homeScreenOrBust(sInstrumentation.getContext(), sUiAutomation);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ // Make sure we clean up and back to home screen again, or let test fail...
+ homeScreenOrBust(sInstrumentation.getContext(), sUiAutomation);
+ }
+
@MediumTest
+ @Test
public void testPerformGlobalActionBack() throws Exception {
- assertTrue(getInstrumentation().getUiAutomation().performGlobalAction(
- AccessibilityService.GLOBAL_ACTION_BACK));
+ assertTrue(sUiAutomation.performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK));
// Sleep a bit so the UI is settled.
waitForIdle();
}
@MediumTest
+ @Test
public void testPerformGlobalActionHome() throws Exception {
- assertTrue(getInstrumentation().getUiAutomation().performGlobalAction(
- AccessibilityService.GLOBAL_ACTION_HOME));
+ assertTrue(sUiAutomation.performGlobalAction(AccessibilityService.GLOBAL_ACTION_HOME));
// Sleep a bit so the UI is settled.
waitForIdle();
}
@MediumTest
+ @Test
public void testPerformGlobalActionRecents() throws Exception {
// Perform the action.
- boolean actionWasPerformed =
- getInstrumentation().getUiAutomation().performGlobalAction(
- AccessibilityService.GLOBAL_ACTION_RECENTS);
+ boolean actionWasPerformed = sUiAutomation.performGlobalAction(
+ AccessibilityService.GLOBAL_ACTION_RECENTS);
// Sleep a bit so the UI is settled.
waitForIdle();
if (actionWasPerformed) {
- // This is a necessary since the back action does not
+ // This is a necessary since the global action does not
// dismiss recents until they stop animating. Sigh...
SystemClock.sleep(5000);
-
- // Clean up.
- getInstrumentation().getUiAutomation().performGlobalAction(
- AccessibilityService.GLOBAL_ACTION_BACK);
}
-
- // Sleep a bit so the UI is settled.
- waitForIdle();
}
@MediumTest
- @FlakyTest
+ @Test
public void testPerformGlobalActionNotifications() throws Exception {
// Perform the action under test
- assertTrue(getInstrumentation().getUiAutomation().performGlobalAction(
+ assertTrue(sUiAutomation.performGlobalAction(
AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS));
// Sleep a bit so the UI is settled.
waitForIdle();
-
- // Clean up.
- assertTrue(getInstrumentation().getUiAutomation().performGlobalAction(
- AccessibilityService.GLOBAL_ACTION_BACK));
-
- // Sleep a bit so the UI is settled.
- waitForIdle();
}
@MediumTest
- @FlakyTest
+ @Test
public void testPerformGlobalActionQuickSettings() throws Exception {
// Check whether the action succeeded.
- assertTrue(getInstrumentation().getUiAutomation().performGlobalAction(
+ assertTrue(sUiAutomation.performGlobalAction(
AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS));
// Sleep a bit so the UI is settled.
waitForIdle();
-
- // Clean up.
- // The GLOBAL_ACTION_HOME is needed in the case where additional
- // notifications showing up, we need to clear them as well for the upcoming
- // new tests to work properly.
- getInstrumentation().getUiAutomation().performGlobalAction(
- AccessibilityService.GLOBAL_ACTION_HOME);
-
- // Sleep a bit so the UI is settled.
- waitForIdle();
}
@MediumTest
- @FlakyTest
+ @Test
public void testPerformGlobalActionPowerDialog() throws Exception {
// Check whether the action succeeded.
- assertTrue(getInstrumentation().getUiAutomation().performGlobalAction(
+ assertTrue(sUiAutomation.performGlobalAction(
AccessibilityService.GLOBAL_ACTION_POWER_DIALOG));
// Sleep a bit so the UI is settled.
waitForIdle();
-
- // Clean up.
- getInstrumentation().getUiAutomation().performGlobalAction(
- AccessibilityService.GLOBAL_ACTION_BACK);
-
- // Sleep a bit so the UI is settled.
- waitForIdle();
}
@MediumTest
+ @Test
public void testPerformActionScreenshot() throws Exception {
// Action should succeed
- assertTrue(getInstrumentation().getUiAutomation().performGlobalAction(
+ assertTrue(sUiAutomation.performGlobalAction(
AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT));
// Ideally should verify that we actually have a screenshot, but it's also possible
// for the screenshot to fail
@@ -154,8 +175,6 @@
}
private void waitForIdle() throws TimeoutException {
- getInstrumentation().getUiAutomation().waitForIdle(
- TIMEOUT_ACCESSIBILITY_STATE_IDLE,
- TIMEOUT_ASYNC_PROCESSING);
+ sUiAutomation.waitForIdle(TIMEOUT_ACCESSIBILITY_STATE_IDLE, TIMEOUT_ASYNC_PROCESSING);
}
}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityLoggingTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityLoggingTest.java
index 9654352..66baa04 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityLoggingTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityLoggingTest.java
@@ -17,6 +17,7 @@
import static junit.framework.Assert.assertTrue;
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
import android.content.Context;
import android.platform.test.annotations.Presubmit;
@@ -25,6 +26,7 @@
import com.android.compatibility.common.util.AppOpsUtils;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -32,6 +34,10 @@
@Presubmit
public class AccessibilityLoggingTest {
+ @Rule
+ public final AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+ new AccessibilityDumpOnFailureRule();
+
/**
* Tests that new accessibility services are logged by the system.
*/
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityMagnificationTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityMagnificationTest.java
index 2774457..c832368 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityMagnificationTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityMagnificationTest.java
@@ -16,8 +16,16 @@
package android.accessibilityservice.cts;
-import static android.accessibilityservice.cts.utils.CtsTestUtils.runIfNotNull;
+import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.launchActivityAndWaitForItToBeOnscreen;
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.compatibility.common.util.TestUtils.waitOn;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyFloat;
import static org.mockito.Mockito.eq;
@@ -25,16 +33,35 @@
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
import android.accessibility.cts.common.InstrumentedAccessibilityService;
+import android.accessibility.cts.common.InstrumentedAccessibilityServiceTestRule;
import android.accessibility.cts.common.ShellCommandBuilder;
import android.accessibilityservice.AccessibilityService.MagnificationController;
import android.accessibilityservice.AccessibilityService.MagnificationController.OnMagnificationChangedListener;
import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.cts.activities.AccessibilityWindowQueryActivity;
+import android.app.Activity;
import android.app.Instrumentation;
+import android.app.UiAutomation;
+import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
import android.platform.test.annotations.AppModeFull;
-import android.test.InstrumentationTestCase;
+import android.util.DisplayMetrics;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.Button;
+
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.runner.RunWith;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -42,7 +69,8 @@
* Class for testing {@link AccessibilityServiceInfo}.
*/
@AppModeFull
-public class AccessibilityMagnificationTest extends InstrumentationTestCase {
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityMagnificationTest {
/** Maximum timeout when waiting for a magnification callback. */
public static final int LISTENER_TIMEOUT_MILLIS = 500;
@@ -51,25 +79,39 @@
private StubMagnificationAccessibilityService mService;
private Instrumentation mInstrumentation;
- @Override
+ private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+ new AccessibilityDumpOnFailureRule();
+
+ private final ActivityTestRule<AccessibilityWindowQueryActivity> mActivityRule =
+ new ActivityTestRule<>(AccessibilityWindowQueryActivity.class, false, false);
+
+ private InstrumentedAccessibilityServiceTestRule<InstrumentedAccessibilityService>
+ mInstrumentedAccessibilityServiceRule = new InstrumentedAccessibilityServiceTestRule<>(
+ InstrumentedAccessibilityService.class, false);
+
+ private InstrumentedAccessibilityServiceTestRule<StubMagnificationAccessibilityService>
+ mMagnificationAccessibilityServiceRule = new InstrumentedAccessibilityServiceTestRule<>(
+ StubMagnificationAccessibilityService.class, false);
+
+ @Rule
+ public final RuleChain mRuleChain = RuleChain
+ .outerRule(mActivityRule)
+ .around(mMagnificationAccessibilityServiceRule)
+ .around(mInstrumentedAccessibilityServiceRule)
+ .around(mDumpOnFailureRule);
+
+ @Before
public void setUp() throws Exception {
- super.setUp();
- ShellCommandBuilder.create(this.getInstrumentation())
+ ShellCommandBuilder.create(getInstrumentation())
.deleteSecureSetting(ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED)
.run();
mInstrumentation = getInstrumentation();
// Starting the service will force the accessibility subsystem to examine its settings, so
// it will update magnification in the process to disable it.
- mService = StubMagnificationAccessibilityService.enableSelf(mInstrumentation);
+ mService = mMagnificationAccessibilityServiceRule.enableService();
}
- @Override
- protected void tearDown() throws Exception {
- runIfNotNull(mService, service -> service.runOnServiceSync(service::disableSelfAndRemove));
-
- super.tearDown();
- }
-
+ @Test
public void testSetScale() {
final MagnificationController controller = mService.getMagnificationController();
final float scale = 2.0f;
@@ -78,14 +120,15 @@
mService.runOnServiceSync(() -> result.set(controller.setScale(scale, false)));
assertTrue("Failed to set scale", result.get());
- assertEquals("Failed to apply scale", scale, controller.getScale());
+ assertEquals("Failed to apply scale", scale, controller.getScale(), 0f);
mService.runOnServiceSync(() -> result.set(controller.reset(false)));
assertTrue("Failed to reset", result.get());
- assertEquals("Failed to apply reset", 1.0f, controller.getScale());
+ assertEquals("Failed to apply reset", 1.0f, controller.getScale(), 0f);
}
+ @Test
public void testSetScaleAndCenter() {
final MagnificationController controller = mService.getMagnificationController();
final Region region = controller.getMagnificationRegion();
@@ -103,7 +146,7 @@
});
assertTrue("Failed to set scale", setScale.get());
- assertEquals("Failed to apply scale", scale, controller.getScale());
+ assertEquals("Failed to apply scale", scale, controller.getScale(), 0f);
assertTrue("Failed to set center", setCenter.get());
assertEquals("Failed to apply center X", x, controller.getCenterX(), 5.0f);
@@ -112,9 +155,10 @@
mService.runOnServiceSync(() -> result.set(controller.reset(false)));
assertTrue("Failed to reset", result.get());
- assertEquals("Failed to apply reset", 1.0f, controller.getScale());
+ assertEquals("Failed to apply reset", 1.0f, controller.getScale(), 0f);
}
+ @Test
public void testListener() {
final MagnificationController controller = mService.getMagnificationController();
final OnMagnificationChangedListener listener = mock(OnMagnificationChangedListener.class);
@@ -140,23 +184,21 @@
}
}
+ @Test
public void testMagnificationServiceShutsDownWhileMagnifying_shouldReturnTo1x() {
final MagnificationController controller = mService.getMagnificationController();
mService.runOnServiceSync(() -> controller.setScale(2.0f, false));
mService.runOnServiceSync(() -> mService.disableSelf());
mService = null;
- InstrumentedAccessibilityService service = InstrumentedAccessibilityService.enableService(
- mInstrumentation, InstrumentedAccessibilityService.class);
+ InstrumentedAccessibilityService service =
+ mInstrumentedAccessibilityServiceRule.enableService();
final MagnificationController controller2 = service.getMagnificationController();
- try {
- assertEquals("Magnification must reset when a service dies",
- 1.0f, controller2.getScale());
- } finally {
- service.runOnServiceSync(() -> service.disableSelf());
- }
+ assertEquals("Magnification must reset when a service dies",
+ 1.0f, controller2.getScale(), 0f);
}
+ @Test
public void testGetMagnificationRegion_whenCanControlMagnification_shouldNotBeEmpty() {
final MagnificationController controller = mService.getMagnificationController();
Region magnificationRegion = controller.getMagnificationRegion();
@@ -164,42 +206,40 @@
+ "magnification is being actively controlled", magnificationRegion.isEmpty());
}
+ @Test
public void testGetMagnificationRegion_whenCantControlMagnification_shouldBeEmpty() {
mService.runOnServiceSync(() -> mService.disableSelf());
mService = null;
- InstrumentedAccessibilityService service = InstrumentedAccessibilityService.enableService(
- mInstrumentation, InstrumentedAccessibilityService.class);
- try {
- final MagnificationController controller = service.getMagnificationController();
- Region magnificationRegion = controller.getMagnificationRegion();
- assertTrue("Magnification region should be empty when magnification "
- + "is not being actively controlled", magnificationRegion.isEmpty());
- } finally {
- service.runOnServiceSync(() -> service.disableSelf());
- }
+ InstrumentedAccessibilityService service =
+ mInstrumentedAccessibilityServiceRule.enableService();
+ final MagnificationController controller = service.getMagnificationController();
+ Region magnificationRegion = controller.getMagnificationRegion();
+ assertTrue("Magnification region should be empty when magnification "
+ + "is not being actively controlled", magnificationRegion.isEmpty());
}
+ @Test
public void testGetMagnificationRegion_whenMagnificationGesturesEnabled_shouldNotBeEmpty() {
ShellCommandBuilder.create(mInstrumentation)
.putSecureSetting(ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, "1")
.run();
mService.runOnServiceSync(() -> mService.disableSelf());
mService = null;
- InstrumentedAccessibilityService service = InstrumentedAccessibilityService.enableService(
- mInstrumentation, InstrumentedAccessibilityService.class);
+ InstrumentedAccessibilityService service =
+ mInstrumentedAccessibilityServiceRule.enableService();
try {
final MagnificationController controller = service.getMagnificationController();
Region magnificationRegion = controller.getMagnificationRegion();
assertFalse("Magnification region should not be empty when magnification "
+ "gestures are active", magnificationRegion.isEmpty());
} finally {
- service.runOnServiceSync(() -> service.disableSelf());
ShellCommandBuilder.create(mInstrumentation)
.deleteSecureSetting(ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED)
.run();
}
}
+ @Test
public void testAnimatingMagnification() throws InterruptedException {
final MagnificationController controller = mService.getMagnificationController();
final int timeBetweenAnimationChanges = 100;
@@ -239,4 +279,121 @@
Thread.sleep(timeBetweenAnimationChanges);
}
}
+
+ @Test
+ public void testA11yNodeInfoVisibility_whenOutOfMagnifiedArea_shouldVisible()
+ throws Exception{
+ final UiAutomation uiAutomation = mInstrumentation.getUiAutomation(
+ UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
+ final Activity activity = launchActivityAndWaitForItToBeOnscreen(
+ mInstrumentation, uiAutomation, mActivityRule);
+ final MagnificationController controller = mService.getMagnificationController();
+ final Rect magnifyBounds = controller.getMagnificationRegion().getBounds();
+ final float scale = 8.0f;
+ final Button button = activity.findViewById(R.id.button1);
+ adjustViewBoundsIfNeeded(button, scale, magnifyBounds);
+
+ final AccessibilityNodeInfo buttonNode = uiAutomation.getRootInActiveWindow()
+ .findAccessibilityNodeInfosByViewId(
+ "android.accessibilityservice.cts:id/button1").get(0);
+ assertNotNull("Can't find button on the screen", buttonNode);
+ assertTrue("Button should be visible", buttonNode.isVisibleToUser());
+
+ // Get right-bottom center position
+ final float centerX = magnifyBounds.left + (((float) magnifyBounds.width() / (2.0f * scale))
+ * ((2.0f * scale) - 1.0f));
+ final float centerY = magnifyBounds.top + (((float) magnifyBounds.height() / (2.0f * scale))
+ * ((2.0f * scale) - 1.0f));
+ try {
+ waitOnMagnificationChanged(controller, scale, centerX, centerY);
+ // Waiting for UI refresh
+ mInstrumentation.waitForIdleSync();
+ buttonNode.refresh();
+
+ final Rect boundsInScreen = new Rect();
+ final DisplayMetrics displayMetrics = new DisplayMetrics();
+ buttonNode.getBoundsInScreen(boundsInScreen);
+ mInstrumentation.getContext().getDisplay().getMetrics(displayMetrics);
+ final Rect displayRect = new Rect(0, 0,
+ displayMetrics.widthPixels, displayMetrics.heightPixels);
+ // The boundsInScreen of button is adjusted to outside of screen by framework,
+ // for example, Rect(-xxx, -xxx, -xxx, -xxx). Intersection of button and screen
+ // should be empty.
+ assertFalse("Button shouldn't be on the screen, screen is " + displayRect
+ + ", button bounds is " + boundsInScreen,
+ Rect.intersects(displayRect, boundsInScreen));
+ assertTrue("Button should be visible", buttonNode.isVisibleToUser());
+ } finally {
+ mService.runOnServiceSync(() -> controller.reset(false));
+ }
+ }
+
+ private void waitOnMagnificationChanged(MagnificationController controller, float newScale,
+ float newCenterX, float newCenterY) {
+ final Object waitLock = new Object();
+ final AtomicBoolean notified = new AtomicBoolean();
+ final OnMagnificationChangedListener listener = (c, region, scale, centerX, centerY) -> {
+ final float delta = 5.0f;
+ synchronized (waitLock) {
+ if (newScale == scale
+ && (centerX > newCenterX - delta) && (centerY > newCenterY - delta)) {
+ notified.set(true);
+ waitLock.notifyAll();
+ }
+ }
+ };
+ controller.addListener(listener);
+ try {
+ final AtomicBoolean setScale = new AtomicBoolean();
+ final AtomicBoolean setCenter = new AtomicBoolean();
+ mService.runOnServiceSync(() -> {
+ setScale.set(controller.setScale(newScale, false));
+ setCenter.set(controller.setCenter(newCenterX, newCenterY, false));
+ });
+ assertTrue("Failed to set scale", setScale.get());
+ assertEquals("Failed to apply scale", newScale, controller.getScale(), 0f);
+ assertTrue("Failed to set center", setCenter.get());
+ waitOn(waitLock, () -> notified.get(), LISTENER_TIMEOUT_MILLIS,
+ "waitOnMagnificationChanged");
+ } finally {
+ controller.removeListener(listener);
+ }
+ }
+
+ /**
+ * Adjust top-left view bounds if it's still in the magnified viewport after sets magnification
+ * scale and move centers to bottom-right.
+ */
+ private void adjustViewBoundsIfNeeded(View topLeftview, float scale, Rect magnifyBounds) {
+ final Point magnifyViewportTopLeft = new Point();
+ magnifyViewportTopLeft.x = (int)((scale - 1.0f) * ((float) magnifyBounds.width() / scale));
+ magnifyViewportTopLeft.y = (int)((scale - 1.0f) * ((float) magnifyBounds.height() / scale));
+ magnifyViewportTopLeft.offset(magnifyBounds.left, magnifyBounds.top);
+
+ final int[] viewLocation = new int[2];
+ topLeftview.getLocationOnScreen(viewLocation);
+ final Rect viewBounds = new Rect(viewLocation[0], viewLocation[1],
+ viewLocation[0] + topLeftview.getWidth(),
+ viewLocation[1] + topLeftview.getHeight());
+ if (viewBounds.right < magnifyViewportTopLeft.x
+ && viewBounds.bottom < magnifyViewportTopLeft.y) {
+ // no need
+ return;
+ }
+
+ final ViewGroup.LayoutParams layoutParams = topLeftview.getLayoutParams();
+ if (viewBounds.right >= magnifyViewportTopLeft.x) {
+ layoutParams.width = topLeftview.getWidth() - 1
+ - (viewBounds.right - magnifyViewportTopLeft.x);
+ assertTrue("Needs to fix layout", layoutParams.width > 0);
+ }
+ if (viewBounds.bottom >= magnifyViewportTopLeft.y) {
+ layoutParams.height = topLeftview.getHeight() - 1
+ - (viewBounds.bottom - magnifyViewportTopLeft.y);
+ assertTrue("Needs to fix layout", layoutParams.height > 0);
+ }
+ mInstrumentation.runOnMainSync(() -> topLeftview.setLayoutParams(layoutParams));
+ // Waiting for UI refresh
+ mInstrumentation.waitForIdleSync();
+ }
}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityOverlayTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityOverlayTest.java
index e461bdf..6f88779 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityOverlayTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityOverlayTest.java
@@ -16,14 +16,13 @@
package android.accessibilityservice.cts;
-import static android.accessibilityservice.cts.utils.CtsTestUtils.runIfNotNull;
-
import static org.junit.Assert.assertTrue;
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
+import android.accessibility.cts.common.InstrumentedAccessibilityServiceTestRule;
import android.accessibility.cts.common.InstrumentedAccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.cts.utils.AsyncUtils;
-import android.app.Instrumentation;
import android.app.UiAutomation;
import android.text.TextUtils;
import android.view.WindowManager;
@@ -33,10 +32,11 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
-import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
import java.util.List;
@@ -45,14 +45,24 @@
@RunWith(AndroidJUnit4.class)
public class AccessibilityOverlayTest {
- private static Instrumentation sInstrumentation;
private static UiAutomation sUiAutomation;
InstrumentedAccessibilityService mService;
+ private InstrumentedAccessibilityServiceTestRule<StubAccessibilityButtonService>
+ mServiceRule = new InstrumentedAccessibilityServiceTestRule<>(
+ StubAccessibilityButtonService.class);
+
+ private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+ new AccessibilityDumpOnFailureRule();
+
+ @Rule
+ public final RuleChain mRuleChain = RuleChain
+ .outerRule(mServiceRule)
+ .around(mDumpOnFailureRule);
+
@BeforeClass
public static void oneTimeSetUp() {
- sInstrumentation = InstrumentationRegistry.getInstrumentation();
- sUiAutomation = sInstrumentation
+ sUiAutomation = InstrumentationRegistry.getInstrumentation()
.getUiAutomation(UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
AccessibilityServiceInfo info = sUiAutomation.getServiceInfo();
info.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
@@ -61,12 +71,7 @@
@Before
public void setUp() {
- mService = StubAccessibilityButtonService.enableSelf(sInstrumentation);
- }
-
- @After
- public void tearDown() {
- runIfNotNull(mService, service -> service.runOnServiceSync(service::disableSelf));
+ mService = mServiceRule.getService();
}
@Test
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityPaneTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityPaneTest.java
index 959f315..d8b613f 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityPaneTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityPaneTest.java
@@ -28,6 +28,7 @@
import static org.hamcrest.Matchers.both;
import static org.junit.Assert.assertEquals;
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
import android.accessibilityservice.cts.activities.AccessibilityEndToEndActivity;
import android.app.Activity;
import android.app.Instrumentation;
@@ -45,6 +46,7 @@
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
/**
@@ -58,10 +60,17 @@
private Activity mActivity;
private View mPaneView;
- @Rule
- public ActivityTestRule<AccessibilityEndToEndActivity> mActivityRule =
+ private ActivityTestRule<AccessibilityEndToEndActivity> mActivityRule =
new ActivityTestRule<>(AccessibilityEndToEndActivity.class, false, false);
+ private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+ new AccessibilityDumpOnFailureRule();
+
+ @Rule
+ public final RuleChain mRuleChain = RuleChain
+ .outerRule(mActivityRule)
+ .around(mDumpOnFailureRule);
+
@BeforeClass
public static void oneTimeSetup() throws Exception {
sInstrumentation = InstrumentationRegistry.getInstrumentation();
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityServiceInfoTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityServiceInfoTest.java
index 67626fe..01d1659 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityServiceInfoTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityServiceInfoTest.java
@@ -16,20 +16,35 @@
package android.accessibilityservice.cts;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.os.Parcel;
import android.platform.test.annotations.Presubmit;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
import android.view.accessibility.AccessibilityEvent;
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
/**
* Class for testing {@link AccessibilityServiceInfo}.
*/
@Presubmit
-public class AccessibilityServiceInfoTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityServiceInfoTest {
+
+ @Rule
+ public final AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+ new AccessibilityDumpOnFailureRule();
@MediumTest
+ @Test
public void testMarshalling() throws Exception {
// fully populate the service info to marshal
@@ -51,6 +66,7 @@
* Tests whether the service info describes its contents consistently.
*/
@MediumTest
+ @Test
public void testDescribeContents() {
AccessibilityServiceInfo info = new AccessibilityServiceInfo();
assertSame("Accessibility service info always return 0 for this method.", 0,
@@ -64,6 +80,7 @@
* Tests whether a feedback type is correctly transformed to a string.
*/
@MediumTest
+ @Test
public void testFeedbackTypeToString() {
assertEquals("[FEEDBACK_AUDIBLE]", AccessibilityServiceInfo.feedbackTypeToString(
AccessibilityServiceInfo.FEEDBACK_AUDIBLE));
@@ -87,6 +104,7 @@
* Tests whether a flag is correctly transformed to a string.
*/
@MediumTest
+ @Test
public void testFlagToString() {
assertEquals("DEFAULT", AccessibilityServiceInfo.flagToString(
AccessibilityServiceInfo.DEFAULT));
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySettingsTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySettingsTest.java
index f01251a..3199dc6 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySettingsTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySettingsTest.java
@@ -16,15 +16,25 @@
package android.accessibilityservice.cts;
+import static androidx.test.InstrumentationRegistry.getContext;
+
+import static org.junit.Assert.assertTrue;
+
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.platform.test.annotations.AppModeFull;
import android.platform.test.annotations.Presubmit;
import android.provider.Settings;
-import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import java.util.List;
/**
@@ -32,12 +42,18 @@
* accessibility settings has an activity that handles it.
*/
@Presubmit
-public class AccessibilitySettingsTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class AccessibilitySettingsTest {
+
+ @Rule
+ public final AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+ new AccessibilityDumpOnFailureRule();
@MediumTest
@AppModeFull
+ @Test
public void testAccessibilitySettingsIntentHandled() throws Throwable {
- PackageManager packageManager = mContext.getPackageManager();
+ PackageManager packageManager = getContext().getPackageManager();
Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
List<ResolveInfo> resolvedActivities = packageManager.queryIntentActivities(intent,
PackageManager.MATCH_DEFAULT_ONLY);
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySoftKeyboardModesTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySoftKeyboardModesTest.java
index 8bb1bfb..0afa1fd 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySoftKeyboardModesTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySoftKeyboardModesTest.java
@@ -13,30 +13,32 @@
*/
package android.accessibilityservice.cts;
+
+import static android.accessibility.cts.common.InstrumentedAccessibilityService.enableService;
import static android.accessibilityservice.AccessibilityService.SHOW_MODE_AUTO;
import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HIDDEN;
import static android.accessibilityservice.AccessibilityService.SHOW_MODE_IGNORE_HARD_KEYBOARD;
-import static android.accessibilityservice.cts.utils.CtsTestUtils.runIfNotNull;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
+import android.accessibility.cts.common.InstrumentedAccessibilityServiceTestRule;
import android.accessibility.cts.common.InstrumentedAccessibilityService;
import android.accessibilityservice.AccessibilityService.SoftKeyboardController;
import android.accessibilityservice.AccessibilityService.SoftKeyboardController.OnShowModeChangedListener;
import android.accessibilityservice.cts.activities.AccessibilityTestActivity;
import android.accessibilityservice.cts.utils.AsyncUtils;
-import android.app.Instrumentation;
import android.os.Bundle;
import android.os.SystemClock;
import android.platform.test.annotations.AppModeFull;
-import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
-import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
/**
@@ -47,7 +49,6 @@
public class AccessibilitySoftKeyboardModesTest {
private int mLastCallbackValue;
- private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
private InstrumentedAccessibilityService mService;
private final Object mLock = new Object();
private final OnShowModeChangedListener mListener = (c, showMode) -> {
@@ -57,17 +58,21 @@
}
};
+ private InstrumentedAccessibilityServiceTestRule<InstrumentedAccessibilityService>
+ mServiceRule = new InstrumentedAccessibilityServiceTestRule<>(
+ InstrumentedAccessibilityService.class);
+ private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+ new AccessibilityDumpOnFailureRule();
+
+ @Rule
+ public final RuleChain mRuleChain = RuleChain
+ .outerRule(mServiceRule)
+ .around(mDumpOnFailureRule);
@Before
- public void setUp() throws Exception {
- mService = InstrumentedAccessibilityService.enableService(mInstrumentation,
- InstrumentedAccessibilityService.class);
- }
-
- @After
- public void tearDown() throws Exception {
- runIfNotNull(mService, service -> service.runOnServiceSync(service::disableSelf));
+ public void setUp() {
+ mService = mServiceRule.getService();
}
@Test
@@ -95,7 +100,7 @@
assertEquals(SHOW_MODE_AUTO, controller.getShowMode());
final InstrumentedAccessibilityService secondService =
- StubAccessibilityButtonService.enableSelf(mInstrumentation);
+ enableService(StubAccessibilityButtonService.class);
try {
// Listen on the first service
controller.addOnShowModeChangedListener(mListener);
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextActionTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextActionTest.java
index e89b4dd..3d013cb 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextActionTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextActionTest.java
@@ -30,10 +30,11 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import android.accessibilityservice.cts.R;
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
import android.accessibilityservice.cts.activities.AccessibilityTextTraversalActivity;
import android.app.Instrumentation;
import android.app.UiAutomation;
+import android.graphics.Bitmap;
import android.graphics.RectF;
import android.os.Bundle;
import android.os.Message;
@@ -42,6 +43,8 @@
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.ClickableSpan;
+import android.text.style.ImageSpan;
+import android.text.style.ReplacementSpan;
import android.text.style.URLSpan;
import android.view.View;
import android.view.accessibility.AccessibilityManager;
@@ -60,6 +63,7 @@
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
import java.util.Arrays;
@@ -76,14 +80,20 @@
private static UiAutomation sUiAutomation;
final Object mClickableSpanCallbackLock = new Object();
final AtomicBoolean mClickableSpanCalled = new AtomicBoolean(false);
-
private AccessibilityTextTraversalActivity mActivity;
- @Rule
- public ActivityTestRule<AccessibilityTextTraversalActivity> mActivityRule =
+ private ActivityTestRule<AccessibilityTextTraversalActivity> mActivityRule =
new ActivityTestRule<>(AccessibilityTextTraversalActivity.class, false, false);
+ private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+ new AccessibilityDumpOnFailureRule();
+
+ @Rule
+ public final RuleChain mRuleChain = RuleChain
+ .outerRule(mActivityRule)
+ .around(mDumpOnFailureRule);
+
@BeforeClass
public static void oneTimeSetup() throws Exception {
sInstrumentation = InstrumentationRegistry.getInstrumentation();
@@ -228,6 +238,25 @@
}
@Test
+ public void testImageSpan_accessibilityServiceShouldSeeContentDescription() {
+ final TextView textView = (TextView) mActivity.findViewById(R.id.text);
+ final Bitmap bitmap = Bitmap.createBitmap(/* width= */10, /* height= */10,
+ Bitmap.Config.ARGB_8888);
+ final ImageSpan imageSpan = new ImageSpan(mActivity, bitmap);
+ final String contentDescription = mActivity.getString(R.string.contentDescription);
+ imageSpan.setContentDescription(contentDescription);
+ final SpannableString textWithImageSpan =
+ new SpannableString(mActivity.getString(R.string.a_b));
+ textWithImageSpan.setSpan(imageSpan, /* start= */0, /* end= */1, /* flags= */0);
+ makeTextViewVisibleAndSetText(textView, textWithImageSpan);
+
+ ReplacementSpan replacementSpanFromA11y = findSingleSpanInViewWithText(R.string.a_b,
+ ReplacementSpan.class);
+
+ assertEquals(contentDescription, replacementSpanFromA11y.getContentDescription());
+ }
+
+ @Test
public void testTextLocations_textViewShouldProvideWhenRequested() {
final TextView textView = (TextView) mActivity.findViewById(R.id.text);
// Use text with a strong s, since that gets replaced with a double s for all caps.
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextTraversalTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextTraversalTest.java
index 75eda1d..17acc3b 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextTraversalTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextTraversalTest.java
@@ -23,7 +23,7 @@
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
-import android.accessibilityservice.cts.R;
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
import android.accessibilityservice.cts.activities.AccessibilityTextTraversalActivity;
import android.app.Instrumentation;
import android.app.UiAutomation;
@@ -47,6 +47,7 @@
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
/**
@@ -64,10 +65,17 @@
private AccessibilityTextTraversalActivity mActivity;
- @Rule
- public ActivityTestRule<AccessibilityTextTraversalActivity> mActivityRule =
+ private ActivityTestRule<AccessibilityTextTraversalActivity> mActivityRule =
new ActivityTestRule<>(AccessibilityTextTraversalActivity.class, false, false);
+ private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+ new AccessibilityDumpOnFailureRule();
+
+ @Rule
+ public final RuleChain mRuleChain = RuleChain
+ .outerRule(mActivityRule)
+ .around(mDumpOnFailureRule);
+
@BeforeClass
public static void oneTimeSetup() throws Exception {
sInstrumentation = InstrumentationRegistry.getInstrumentation();
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityViewTreeReportingTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityViewTreeReportingTest.java
index 28a3acd..0ca307a 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityViewTreeReportingTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityViewTreeReportingTest.java
@@ -14,8 +14,7 @@
package android.accessibilityservice.cts;
-import static android.accessibilityservice.cts.utils.ActivityLaunchUtils
- .launchActivityAndWaitForItToBeOnscreen;
+import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.launchActivityAndWaitForItToBeOnscreen;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -23,15 +22,12 @@
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
import android.accessibilityservice.AccessibilityServiceInfo;
-import android.accessibilityservice.cts.R;
import android.accessibilityservice.cts.activities.AccessibilityViewTreeReportingActivity;
import android.app.Instrumentation;
import android.app.UiAutomation;
import android.content.Context;
-import androidx.test.InstrumentationRegistry;
-import androidx.test.rule.ActivityTestRule;
-import androidx.test.runner.AndroidJUnit4;
import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
@@ -40,11 +36,16 @@
import android.widget.Button;
import android.widget.LinearLayout;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
/**
@@ -61,10 +62,17 @@
private AccessibilityViewTreeReportingActivity mActivity;
- @Rule
- public ActivityTestRule<AccessibilityViewTreeReportingActivity> mActivityRule =
+ private ActivityTestRule<AccessibilityViewTreeReportingActivity> mActivityRule =
new ActivityTestRule<>(AccessibilityViewTreeReportingActivity.class, false, false);
+ private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+ new AccessibilityDumpOnFailureRule();
+
+ @Rule
+ public final RuleChain mRuleChain = RuleChain
+ .outerRule(mActivityRule)
+ .around(mDumpOnFailureRule);
+
@BeforeClass
public static void oneTimeSetup() throws Exception {
sInstrumentation = InstrumentationRegistry.getInstrumentation();
@@ -309,6 +317,44 @@
assertTrue(awaitedEvent.getSource().isImportantForAccessibility());
}
+
+ @Test
+ public void testHideView_receiveSubtreeEvent() throws Throwable {
+ final View view = mActivity.findViewById(R.id.secondButton);
+ AccessibilityEvent awaitedEvent =
+ sUiAutomation.executeAndWaitForEvent(
+ () -> mActivity.runOnUiThread(() -> view.setVisibility(View.GONE)),
+ (event) -> {
+ boolean isContentChanged = event.getEventType()
+ == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
+ int isSubTree = (event.getContentChangeTypes()
+ & AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE);
+ boolean isFromThisPackage = TextUtils.equals(event.getPackageName(),
+ mActivity.getPackageName());
+ return isContentChanged && (isSubTree != 0) && isFromThisPackage;
+ }, TIMEOUT_ASYNC_PROCESSING);
+ awaitedEvent.recycle();
+ }
+
+ @Test
+ public void testUnhideView_receiveSubtreeEvent() throws Throwable {
+ final View view = mActivity.findViewById(R.id.hiddenButton);
+ AccessibilityEvent awaitedEvent =
+ sUiAutomation.executeAndWaitForEvent(
+ () -> mActivity.runOnUiThread(() -> view.setVisibility(View.VISIBLE)),
+ (event) -> {
+ boolean isContentChanged = event.getEventType()
+ == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
+ int isSubTree = (event.getContentChangeTypes()
+ & AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE);
+ boolean isFromThisPackage = TextUtils.equals(event.getPackageName(),
+ mActivity.getPackageName());
+ return isContentChanged && (isSubTree != 0) && isFromThisPackage;
+ }, TIMEOUT_ASYNC_PROCESSING);
+ awaitedEvent.recycle();
+ }
+
+
private void setGetNonImportantViews(boolean getNonImportantViews) {
AccessibilityServiceInfo serviceInfo = sUiAutomation.getServiceInfo();
serviceInfo.flags &= ~AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityVolumeTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityVolumeTest.java
index 16e0b68..3cf9c49 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityVolumeTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityVolumeTest.java
@@ -17,7 +17,9 @@
import static org.junit.Assert.assertEquals;
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
import android.accessibility.cts.common.InstrumentedAccessibilityService;
+import android.accessibility.cts.common.InstrumentedAccessibilityServiceTestRule;
import android.app.Instrumentation;
import android.content.pm.PackageManager;
import android.media.AudioManager;
@@ -28,7 +30,9 @@
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
/**
@@ -43,6 +47,18 @@
// If a11y volume is stuck at a single value, don't run the tests
boolean mFixedA11yVolume;
+ private InstrumentedAccessibilityServiceTestRule<InstrumentedAccessibilityService>
+ mServiceRule = new InstrumentedAccessibilityServiceTestRule<>(
+ InstrumentedAccessibilityService.class, false);
+
+ private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+ new AccessibilityDumpOnFailureRule();
+
+ @Rule
+ public final RuleChain mRuleChain = RuleChain
+ .outerRule(mServiceRule)
+ .around(mDumpOnFailureRule);
+
@Before
public void setUp() {
mInstrumentation = InstrumentationRegistry.getInstrumentation();
@@ -83,20 +99,13 @@
final int MIN = mAudioManager.getStreamMinVolume(AudioManager.STREAM_ACCESSIBILITY);
final int MAX = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_ACCESSIBILITY);
final int otherVolume = (startingVolume == MIN) ? MAX : MIN;
- final InstrumentedAccessibilityService service = InstrumentedAccessibilityService
- .enableService(mInstrumentation, InstrumentedAccessibilityService.class);
- try {
- service.runOnServiceSync(() ->
- mAudioManager.setStreamVolume(AudioManager.STREAM_ACCESSIBILITY, otherVolume,
- 0));
- assertEquals("Accessibility service should be able to change accessibility volume",
- otherVolume, mAudioManager.getStreamVolume(AudioManager.STREAM_ACCESSIBILITY));
- service.runOnServiceSync(() -> mAudioManager.setStreamVolume(
- AudioManager.STREAM_ACCESSIBILITY, startingVolume, 0));
- } finally {
- if (service != null) {
- service.runOnServiceSync(() -> service.disableSelf());
- }
- }
+ final InstrumentedAccessibilityService service = mServiceRule.enableService();
+
+ service.runOnServiceSync(() -> mAudioManager.setStreamVolume(
+ AudioManager.STREAM_ACCESSIBILITY, otherVolume, 0));
+ assertEquals("Accessibility service should be able to change accessibility volume",
+ otherVolume, mAudioManager.getStreamVolume(AudioManager.STREAM_ACCESSIBILITY));
+ service.runOnServiceSync(() -> mAudioManager.setStreamVolume(
+ AudioManager.STREAM_ACCESSIBILITY, startingVolume, 0));
}
}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java
old mode 100755
new mode 100644
index f1fca9f..f32871b
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java
@@ -19,8 +19,10 @@
import static android.accessibilityservice.cts.utils.AccessibilityEventFilterUtils.filterForEventType;
import static android.accessibilityservice.cts.utils.AccessibilityEventFilterUtils.filterWindowsChangedWithChangeTypes;
import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.launchActivityAndWaitForItToBeOnscreen;
+import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.launchActivityOnSpecifiedDisplayAndWaitForItToBeOnscreen;
import static android.accessibilityservice.cts.utils.AsyncUtils.DEFAULT_TIMEOUT_MS;
import static android.accessibilityservice.cts.utils.DisplayUtils.getStatusBarHeight;
+import static android.accessibilityservice.cts.utils.DisplayUtils.VirtualDisplaySession;
import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED;
import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_CLICKED;
@@ -48,10 +50,11 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
-import android.accessibilityservice.cts.activities.AccessibilityEndToEndActivity;
import android.accessibilityservice.cts.activities.AccessibilityWindowQueryActivity;
+import android.app.Activity;
import android.app.ActivityTaskManager;
import android.app.Instrumentation;
import android.app.UiAutomation;
@@ -59,8 +62,11 @@
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Rect;
+import android.os.Bundle;
import android.platform.test.annotations.AppModeFull;
import android.test.suitebuilder.annotation.MediumTest;
+import android.util.SparseArray;
+import android.view.Display;
import android.view.Gravity;
import android.view.View;
import android.view.WindowManager;
@@ -81,6 +87,7 @@
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
import java.util.ArrayList;
@@ -108,14 +115,18 @@
private static UiAutomation sUiAutomation;
private AccessibilityWindowQueryActivity mActivity;
+ private Activity mActivityOnVirtualDisplay;
- @Rule
- public ActivityTestRule<AccessibilityWindowQueryActivity> mActivityRule =
+ private ActivityTestRule<AccessibilityWindowQueryActivity> mActivityRule =
new ActivityTestRule<>(AccessibilityWindowQueryActivity.class, false, false);
+ private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+ new AccessibilityDumpOnFailureRule();
+
@Rule
- public ActivityTestRule<AccessibilityEndToEndActivity> mEndToEndActivityRule =
- new ActivityTestRule<>(AccessibilityEndToEndActivity.class, false, false);
+ public final RuleChain mRuleChain = RuleChain
+ .outerRule(mActivityRule)
+ .around(mDumpOnFailureRule);
@BeforeClass
public static void oneTimeSetup() throws Exception {
@@ -313,7 +324,7 @@
try {
// Add two more windows.
final View views[];
- views = addTwoAppPanelWindows();
+ views = addTwoAppPanelWindows(mActivity);
try {
// Put accessibility focus in the first app window.
@@ -611,7 +622,7 @@
@Test
public void testGetWindows_resultIsSortedByLayerDescending() throws TimeoutException {
- addTwoAppPanelWindows();
+ addTwoAppPanelWindows(mActivity);
List<AccessibilityWindowInfo> windows = sUiAutomation.getWindows();
AccessibilityWindowInfo windowAddedFirst = findWindow(windows, R.string.button1);
@@ -621,11 +632,67 @@
assertThat(windows, new IsSortedBy<>(w -> w.getLayer(), /* ascending */ false));
}
+ @Test
+ public void testGetWindowsOnAllDisplays_resultIsSortedByLayerDescending() throws Exception {
+ addTwoAppPanelWindows(mActivity);
+ // Creates a virtual display.
+ try (final VirtualDisplaySession displaySession = new VirtualDisplaySession()) {
+ final int virtualDisplayId =
+ displaySession.createDisplayWithDefaultDisplayMetricsAndWait(
+ sInstrumentation.getContext(), false).getDisplayId();
+ // Launches an activity on virtual display.
+ mActivityOnVirtualDisplay = launchActivityOnSpecifiedDisplayAndWaitForItToBeOnscreen(
+ sInstrumentation, sUiAutomation,
+ AccessibilityEmbeddedDisplayTest.EmbeddedDisplayActivity.class,
+ virtualDisplayId);
+ // Adds two app panel windows on activity of virtual display.
+ addTwoAppPanelWindows(mActivityOnVirtualDisplay);
+
+ // Gets all windows.
+ SparseArray<List<AccessibilityWindowInfo>> allWindows =
+ sUiAutomation.getWindowsOnAllDisplays();
+ assertNotNull(allWindows);
+ assertTrue(allWindows.size() == 2);
+
+ // Gets windows on default display.
+ List<AccessibilityWindowInfo> windowsOnDefaultDisplay =
+ allWindows.get(Display.DEFAULT_DISPLAY);
+ assertNotNull(windowsOnDefaultDisplay);
+ assertTrue(windowsOnDefaultDisplay.size() > 0);
+
+ AccessibilityWindowInfo windowAddedFirst =
+ findWindow(windowsOnDefaultDisplay, R.string.button1);
+ AccessibilityWindowInfo windowAddedSecond =
+ findWindow(windowsOnDefaultDisplay, R.string.button2);
+ assertThat(windowAddedFirst.getLayer(), lessThan(windowAddedSecond.getLayer()));
+ assertThat(windowsOnDefaultDisplay,
+ new IsSortedBy<>(w -> w.getLayer(), /* ascending */ false));
+
+ // Gets windows on virtual display.
+ List<AccessibilityWindowInfo> windowsOnVirtualDisplay =
+ allWindows.get(virtualDisplayId);
+ assertNotNull(windowsOnVirtualDisplay);
+ assertTrue(windowsOnVirtualDisplay.size() > 0);
+
+ AccessibilityWindowInfo windowAddedFirstOnVirtualDisplay =
+ findWindow(windowsOnVirtualDisplay, R.string.button1);
+ AccessibilityWindowInfo windowAddedSecondOnVirtualDisplay =
+ findWindow(windowsOnVirtualDisplay, R.string.button2);
+ assertThat(windowAddedFirstOnVirtualDisplay.getLayer(),
+ lessThan(windowAddedSecondOnVirtualDisplay.getLayer()));
+ assertThat(windowsOnVirtualDisplay,
+ new IsSortedBy<>(w -> w.getLayer(), /* ascending */ false));
+
+ mActivityOnVirtualDisplay.finish();
+ }
+ }
+
private AccessibilityWindowInfo findWindow(List<AccessibilityWindowInfo> windows,
int btnTextRes) {
return windows.stream()
.filter(w -> w.getRoot()
- .findAccessibilityNodeInfosByText(mActivity.getString(btnTextRes))
+ .findAccessibilityNodeInfosByText(
+ sInstrumentation.getTargetContext().getString(btnTextRes))
.size() == 1)
.findFirst()
.get();
@@ -714,7 +781,7 @@
}
}
- private View[] addTwoAppPanelWindows() throws TimeoutException {
+ private View[] addTwoAppPanelWindows(Activity activity) throws TimeoutException {
setAccessInteractiveWindowsFlag();
sUiAutomation
.waitForIdle(TIMEOUT_WINDOW_STATE_IDLE, DEFAULT_TIMEOUT_MS);
@@ -722,16 +789,16 @@
return new View[] {
addWindow(R.string.button1, params -> {
params.gravity = Gravity.TOP;
- params.y = getStatusBarHeight(mActivity);
- }),
+ params.y = getStatusBarHeight(activity);
+ }, activity),
addWindow(R.string.button2, params -> {
params.gravity = Gravity.BOTTOM;
- })
+ }, activity)
};
}
- private Button addWindow(int btnTextRes, Consumer<WindowManager.LayoutParams> configure)
- throws TimeoutException {
+ private Button addWindow(int btnTextRes, Consumer<WindowManager.LayoutParams> configure,
+ Activity activity) throws TimeoutException {
AtomicReference<Button> result = new AtomicReference<>();
sUiAutomation.executeAndWaitForEvent(() -> {
sInstrumentation.runOnMainSync(() -> {
@@ -743,13 +810,13 @@
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
params.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
- params.token = mActivity.getWindow().getDecorView().getWindowToken();
+ params.token = activity.getWindow().getDecorView().getWindowToken();
configure.accept(params);
- final Button button = new Button(mActivity);
+ final Button button = new Button(activity);
button.setText(btnTextRes);
result.set(button);
- mActivity.getWindowManager().addView(button, params);
+ activity.getWindowManager().addView(button, params);
});
}, filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_ADDED), DEFAULT_TIMEOUT_MS);
return result.get();
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowReportingTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowReportingTest.java
index 71c34c2..25e6d1f 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowReportingTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowReportingTest.java
@@ -39,6 +39,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.cts.activities.AccessibilityWindowReportingActivity;
import android.app.Activity;
@@ -62,6 +63,7 @@
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
import java.util.List;
@@ -82,10 +84,17 @@
private Activity mActivity;
private CharSequence mActivityTitle;
- @Rule
- public ActivityTestRule<AccessibilityWindowReportingActivity> mActivityRule =
+ private ActivityTestRule<AccessibilityWindowReportingActivity> mActivityRule =
new ActivityTestRule<>(AccessibilityWindowReportingActivity.class, false, false);
+ private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+ new AccessibilityDumpOnFailureRule();
+
+ @Rule
+ public final RuleChain mRuleChain = RuleChain
+ .outerRule(mActivityRule)
+ .around(mDumpOnFailureRule);
+
@BeforeClass
public static void oneTimeSetup() throws Exception {
sInstrumentation = InstrumentationRegistry.getInstrumentation();
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/GestureDescriptionTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/GestureDescriptionTest.java
index 92821b8..346c62e 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/GestureDescriptionTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/GestureDescriptionTest.java
@@ -14,30 +14,48 @@
package android.accessibilityservice.cts;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
import android.accessibilityservice.GestureDescription;
import android.accessibilityservice.GestureDescription.StrokeDescription;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.platform.test.annotations.AppModeFull;
import android.platform.test.annotations.Presubmit;
-import android.test.InstrumentationTestCase;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
/**
* Tests for creating gesture descriptions.
*/
@Presubmit
@AppModeFull
-public class GestureDescriptionTest extends InstrumentationTestCase {
+@RunWith(AndroidJUnit4.class)
+public class GestureDescriptionTest {
static final int NOMINAL_PATH_DURATION = 100;
private Path mNominalPath;
- @Override
+ @Rule
+ public final AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+ new AccessibilityDumpOnFailureRule();
+
+ @Before
public void setUp() {
mNominalPath = new Path();
mNominalPath.moveTo(0, 0);
mNominalPath.lineTo(10, 10);
}
+ @Test
public void testCreateStroke_noDuration_shouldThrow() {
try {
new StrokeDescription(mNominalPath, 0, 0);
@@ -46,6 +64,7 @@
}
}
+ @Test
public void testCreateStroke_negativeStartTime_shouldThrow() {
try {
new StrokeDescription(mNominalPath, -1, NOMINAL_PATH_DURATION);
@@ -54,6 +73,7 @@
}
}
+ @Test
public void testCreateStroke_negativeStartX_shouldThrow() {
Path negativeStartXPath = new Path();
negativeStartXPath.moveTo(-1, 0);
@@ -65,6 +85,7 @@
}
}
+ @Test
public void testCreateStroke_negativeStartY_shouldThrow() {
Path negativeStartYPath = new Path();
negativeStartYPath.moveTo(0, -1);
@@ -76,6 +97,7 @@
}
}
+ @Test
public void testCreateStroke_negativeEndX_shouldThrow() {
Path negativeEndXPath = new Path();
negativeEndXPath.moveTo(0, 0);
@@ -87,6 +109,7 @@
}
}
+ @Test
public void testCreateStroke_negativeEndY_shouldThrow() {
Path negativeEndYPath = new Path();
negativeEndYPath.moveTo(0, 0);
@@ -98,6 +121,7 @@
}
}
+ @Test
public void testCreateStroke_withEmptyPath_shouldThrow() {
Path emptyPath = new Path();
try {
@@ -107,6 +131,7 @@
}
}
+ @Test
public void testCreateStroke_pathWithMultipleContours_shouldThrow() {
Path multiContourPath = new Path();
multiContourPath.moveTo(0, 0);
@@ -120,6 +145,7 @@
}
}
+ @Test
public void testStrokeDescriptionWillContinue() {
StrokeDescription strokeDescription = new StrokeDescription(mNominalPath, 0, 100);
assertFalse(strokeDescription.willContinue());
@@ -138,6 +164,7 @@
assertTrue(continuation.willContinue());
}
+ @Test
public void testAddStroke_allowUpToMaxPaths() {
GestureDescription.Builder gestureBuilder = new GestureDescription.Builder();
for (int i = 0; i < GestureDescription.getMaxStrokeCount(); i++) {
@@ -156,6 +183,7 @@
}
}
+ @Test
public void testAddStroke_withDurationTooLong_shouldThrow() {
Path path = new Path();
path.moveTo(10, 10);
@@ -169,6 +197,7 @@
}
}
+ @Test
public void testEmptyDescription_shouldThrow() {
GestureDescription.Builder gestureBuilder = new GestureDescription.Builder();
try {
@@ -178,6 +207,7 @@
}
}
+ @Test
public void testStrokeDescriptionGetters_workAsExpected() {
int x = 100;
int startY = 100;
@@ -201,4 +231,18 @@
PathMeasure measure = new PathMeasure(returnedPath, false);
assertEquals(50, (int) measure.getLength());
}
+
+ @Test
+ public void testSetDisplayId_getCorrectDisplayId() {
+ Path path = new Path();
+ path.moveTo(100, 100);
+ StrokeDescription stroke = new StrokeDescription(path, 150, 100);
+ GestureDescription.Builder builder = new GestureDescription.Builder();
+ builder.addStroke(stroke);
+ builder.setDisplayId(2);
+
+ GestureDescription gesture = builder.build();
+
+ assertEquals(2, gesture.getDisplayId());
+ }
}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/GestureDetectionStubAccessibilityService.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/GestureDetectionStubAccessibilityService.java
index 042904b..049bae1 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/GestureDetectionStubAccessibilityService.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/GestureDetectionStubAccessibilityService.java
@@ -14,32 +14,39 @@
package android.accessibilityservice.cts;
-import android.accessibility.cts.common.InstrumentedAccessibilityService;
+import static org.junit.Assert.fail;
-import android.app.Instrumentation;
+import android.accessibility.cts.common.InstrumentedAccessibilityService;
+import android.accessibilityservice.AccessibilityGestureEvent;
import android.view.accessibility.AccessibilityEvent;
+
import java.util.ArrayList;
+import java.util.List;
/** Accessibility service stub, which will collect recognized gestures. */
public class GestureDetectionStubAccessibilityService extends InstrumentedAccessibilityService {
private static final long GESTURE_RECOGNIZE_TIMEOUT_MS = 3000;
- private static final long EVENT_RECOGNIZE_TIMEOUT_MS = 3000;
+ private static final long EVENT_RECOGNIZE_TIMEOUT_MS = 5000;
// Member variables
- private final Object mLock = new Object();
+ protected final Object mLock = new Object();
private ArrayList<Integer> mCollectedGestures = new ArrayList();
- private ArrayList<Integer> mCollectedEvents = new ArrayList();
-
- public static GestureDetectionStubAccessibilityService enableSelf(
- Instrumentation instrumentation) {
- return InstrumentedAccessibilityService.enableService(
- instrumentation, GestureDetectionStubAccessibilityService.class);
- }
+ private ArrayList<AccessibilityGestureEvent> mCollectedGestureEvents = new ArrayList();
+ protected ArrayList<Integer> mCollectedEvents = new ArrayList();
@Override
protected boolean onGesture(int gestureId) {
synchronized (mCollectedGestures) {
mCollectedGestures.add(gestureId);
- mCollectedGestures.notifyAll(); // Stop waiting for gesture.
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onGesture(AccessibilityGestureEvent gestureEvent) {
+ super.onGesture(gestureEvent);
+ synchronized (mCollectedGestureEvents) {
+ mCollectedGestureEvents.add(gestureEvent);
+ mCollectedGestureEvents.notifyAll(); // Stop waiting for gesture.
}
return true;
}
@@ -48,6 +55,9 @@
synchronized (mCollectedGestures) {
mCollectedGestures.clear();
}
+ synchronized (mCollectedGestureEvents) {
+ mCollectedGestureEvents.clear();
+ }
}
public int getGesturesSize() {
@@ -62,20 +72,33 @@
}
}
- /** Wait for onGesture() to collect next gesture. */
- public void waitUntilGesture() {
- synchronized (mCollectedGestures) {
- if (mCollectedGestures.size() > 0) {
+ /** Waits for {@link #onGesture(AccessibilityGestureEvent)} to collect next gesture. */
+ public void waitUntilGestureInfo() {
+ synchronized (mCollectedGestureEvents) {
+ //Assume the size of mCollectedGestures is changed before mCollectedGestureEvents.
+ if (mCollectedGestureEvents.size() > 0) {
return;
}
try {
- mCollectedGestures.wait(GESTURE_RECOGNIZE_TIMEOUT_MS);
+ mCollectedGestureEvents.wait(GESTURE_RECOGNIZE_TIMEOUT_MS);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
+ public int getGestureInfoSize() {
+ synchronized (mCollectedGestureEvents) {
+ return mCollectedGestureEvents.size();
+ }
+ }
+
+ public AccessibilityGestureEvent getGestureInfo(int index) {
+ synchronized (mCollectedGestureEvents) {
+ return mCollectedGestureEvents.get(index);
+ }
+ }
+
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
synchronized (mLock) {
@@ -124,4 +147,37 @@
}
}
}
+
+ /** Insure that the specified accessibility events have been received. */
+ public void assertPropagated(int... events) {
+ waitUntilEvent(events.length);
+ // Set up readable error reporting.
+ List<String> received = new ArrayList<>();
+ List<String> expected = new ArrayList<>();
+ for (int event : events) {
+ expected.add(AccessibilityEvent.eventTypeToString(event));
+ }
+ for (int i = 0; i < getEventsSize(); ++i) {
+ received.add(AccessibilityEvent.eventTypeToString(getEvent(i)));
+ }
+
+ if (events.length != getEventsSize()) {
+ String message =
+ String.format(
+ "Received %d events when expecting %d. Received %s, expected %s",
+ received.size(),
+ expected.size(),
+ received.toString(),
+ expected.toString());
+ fail(message);
+ }
+ else if (!expected.equals(received)) {
+ String message =
+ String.format(
+ "Received %s, expected %s",
+ received.toString(),
+ expected.toString());
+ fail(message);
+ }
+ }
}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/MagnificationGestureHandlerTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/MagnificationGestureHandlerTest.java
index 72bbd97..8393012 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/MagnificationGestureHandlerTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/MagnificationGestureHandlerTest.java
@@ -18,11 +18,11 @@
import static android.accessibilityservice.cts.utils.AsyncUtils.await;
import static android.accessibilityservice.cts.utils.AsyncUtils.waitOn;
-import static android.accessibilityservice.cts.utils.CtsTestUtils.runIfNotNull;
import static android.accessibilityservice.cts.utils.GestureUtils.add;
import static android.accessibilityservice.cts.utils.GestureUtils.click;
import static android.accessibilityservice.cts.utils.GestureUtils.dispatchGesture;
import static android.accessibilityservice.cts.utils.GestureUtils.distance;
+import static android.accessibilityservice.cts.utils.GestureUtils.doubleTap;
import static android.accessibilityservice.cts.utils.GestureUtils.drag;
import static android.accessibilityservice.cts.utils.GestureUtils.endTimeOf;
import static android.accessibilityservice.cts.utils.GestureUtils.lastPointOf;
@@ -31,21 +31,17 @@
import static android.accessibilityservice.cts.utils.GestureUtils.pointerUp;
import static android.accessibilityservice.cts.utils.GestureUtils.startingAt;
import static android.accessibilityservice.cts.utils.GestureUtils.swipe;
+import static android.accessibilityservice.cts.utils.GestureUtils.tripleTap;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_MOVE;
import static android.view.MotionEvent.ACTION_UP;
-import static org.hamcrest.Matchers.empty;
-import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static java.util.concurrent.TimeUnit.SECONDS;
-
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
import android.accessibility.cts.common.InstrumentedAccessibilityService;
+import android.accessibility.cts.common.InstrumentedAccessibilityServiceTestRule;
import android.accessibilityservice.GestureDescription;
import android.accessibilityservice.GestureDescription.StrokeDescription;
import android.accessibilityservice.cts.AccessibilityGestureDispatchTest.GestureDispatchActivity;
@@ -53,10 +49,8 @@
import android.app.Instrumentation;
import android.content.pm.PackageManager;
import android.graphics.PointF;
-import android.os.SystemClock;
import android.platform.test.annotations.AppModeFull;
import android.provider.Settings;
-import android.view.MotionEvent;
import android.widget.TextView;
import androidx.test.InstrumentationRegistry;
@@ -67,12 +61,9 @@
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-
/**
* Class for testing magnification.
*/
@@ -95,10 +86,22 @@
private final Object mZoomLock = new Object();
- @Rule
- public ActivityTestRule<GestureDispatchActivity> mActivityRule =
+ private ActivityTestRule<GestureDispatchActivity> mActivityRule =
new ActivityTestRule<>(GestureDispatchActivity.class);
+ private InstrumentedAccessibilityServiceTestRule<StubMagnificationAccessibilityService>
+ mServiceRule = new InstrumentedAccessibilityServiceTestRule<>(
+ StubMagnificationAccessibilityService.class, false);
+
+ private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+ new AccessibilityDumpOnFailureRule();
+
+ @Rule
+ public final RuleChain mRuleChain = RuleChain
+ .outerRule(mActivityRule)
+ .around(mServiceRule)
+ .around(mDumpOnFailureRule);
+
@Before
public void setUp() throws Exception {
mInstrumentation = InstrumentationRegistry.getInstrumentation();
@@ -113,7 +116,7 @@
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, 0) == 1;
setMagnificationEnabled(true);
- mService = StubMagnificationAccessibilityService.enableSelf(mInstrumentation);
+ mService = mServiceRule.enableService();
mService.getMagnificationController().addListener(
(controller, region, scale, centerX, centerY) -> {
mCurrentScale = scale;
@@ -140,8 +143,6 @@
if (!mHasTouchscreen) return;
setMagnificationEnabled(mOriginalIsMagnificationEnabled);
-
- runIfNotNull(mService, service -> service.runOnServiceSync(service::disableSelfAndRemove));
}
@Test
@@ -196,9 +197,9 @@
private void setZoomByTripleTapping(boolean desiredZoomState) {
if (isZoomed() == desiredZoomState) return;
- dispatch(tripleTap());
+ dispatch(tripleTap(mTapLocation));
waitOn(mZoomLock, () -> isZoomed() == desiredZoomState);
- assertNoTouchInputPropagated();
+ mTouchListener.assertNonePropagated();
}
private void tripleTapAndDragViewport() {
@@ -210,10 +211,10 @@
dispatch(drag);
waitOn(mZoomLock, () -> distance(mCurrentZoomCenter, oldCenter) >= mPan / 5);
assertTrue(isZoomed());
- assertNoTouchInputPropagated();
+ mTouchListener.assertNonePropagated();
dispatch(pointerUp(drag));
- assertNoTouchInputPropagated();
+ mTouchListener.assertNonePropagated();
}
private StrokeDescription tripleTapAndHold() {
@@ -227,22 +228,18 @@
private void assertGesturesPropagateToView() {
dispatch(click(mTapLocation));
- assertPropagated(ACTION_DOWN, ACTION_UP);
+ mTouchListener.assertPropagated(ACTION_DOWN, ACTION_UP);
dispatch(longClick(mTapLocation));
- assertPropagated(ACTION_DOWN, ACTION_UP);
+ mTouchListener.assertPropagated(ACTION_DOWN, ACTION_UP);
- dispatch(doubleTap());
- assertPropagated(ACTION_DOWN, ACTION_UP, ACTION_DOWN, ACTION_UP);
+ dispatch(doubleTap(mTapLocation));
+ mTouchListener.assertPropagated(ACTION_DOWN, ACTION_UP, ACTION_DOWN, ACTION_UP);
dispatch(swipe(
mTapLocation,
add(mTapLocation, 0, 29)));
- assertPropagated(ACTION_DOWN, ACTION_MOVE, ACTION_UP);
- }
-
- private void assertNoTouchInputPropagated() {
- assertThat(prettyPrintable(mTouchListener.events), is(empty()));
+ mTouchListener.assertPropagated(ACTION_DOWN, ACTION_MOVE, ACTION_UP);
}
private void setMagnificationEnabled(boolean enabled) {
@@ -254,50 +251,6 @@
return mCurrentScale >= MIN_SCALE;
}
- private void assertPropagated(int... eventTypes) {
- MotionEvent ev;
- try {
- while (true) {
- if (eventTypes.length == 0) return;
- int expectedEventType = eventTypes[0];
- long startedPollingAt = SystemClock.uptimeMillis();
- ev = mTouchListener.events.poll(5, SECONDS);
- assertNotNull("Expected "
- + MotionEvent.actionToString(expectedEventType)
- + " but none present after "
- + (SystemClock.uptimeMillis() - startedPollingAt) + "ms",
- ev);
- int action = ev.getActionMasked();
- if (action == expectedEventType) {
- eventTypes = Arrays.copyOfRange(eventTypes, 1, eventTypes.length);
- } else {
- if (action != ACTION_MOVE) fail("Unexpected event: " + ev);
- }
- }
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
-
- private GestureDescription doubleTap() {
- return multiTap(2);
- }
-
- private GestureDescription tripleTap() {
- return multiTap(3);
- }
-
- private GestureDescription multiTap(int taps) {
- GestureDescription.Builder builder = new GestureDescription.Builder();
- long time = 0;
- for (int i = 0; i < taps; i++) {
- StrokeDescription stroke = click(mTapLocation);
- builder.addStroke(startingAt(time, stroke));
- time += stroke.getDuration() + 20;
- }
- return builder.build();
- }
-
public void dispatch(StrokeDescription firstStroke, StrokeDescription... rest) {
GestureDescription.Builder builder =
new GestureDescription.Builder().addStroke(firstStroke);
@@ -310,17 +263,4 @@
public void dispatch(GestureDescription gesture) {
await(dispatchGesture(mService, gesture));
}
-
- private static <T> Collection<T> prettyPrintable(Collection<T> c) {
- return new ArrayList<T>(c) {
-
- @Override
- public String toString() {
- return stream()
- .map(t -> "\n" + t)
- .reduce(String::concat)
- .orElse("");
- }
- };
- }
}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/StubAccessibilityButtonService.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubAccessibilityButtonService.java
index c0ce5b6..5223f22 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/StubAccessibilityButtonService.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubAccessibilityButtonService.java
@@ -15,15 +15,9 @@
package android.accessibilityservice.cts;
import android.accessibility.cts.common.InstrumentedAccessibilityService;
-import android.app.Instrumentation;
-import android.app.UiAutomation;
/**
* A stub accessibility service to install for testing accessibility button APIs
*/
public class StubAccessibilityButtonService extends InstrumentedAccessibilityService {
- public static StubAccessibilityButtonService enableSelf(Instrumentation instrumentation) {
- return InstrumentedAccessibilityService.enableService(
- instrumentation, StubAccessibilityButtonService.class);
- }
}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/StubFingerprintGestureService.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubFingerprintGestureService.java
index 87539f8..bf78b48 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/StubFingerprintGestureService.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubFingerprintGestureService.java
@@ -15,14 +15,9 @@
package android.accessibilityservice.cts;
import android.accessibility.cts.common.InstrumentedAccessibilityService;
-import android.app.Instrumentation;
/**
* A stub accessibility service to install for testing gesture dispatch
*/
public class StubFingerprintGestureService extends InstrumentedAccessibilityService {
- public static StubFingerprintGestureService enableSelf(Instrumentation instrumentation) {
- return InstrumentedAccessibilityService.enableService(
- instrumentation, StubFingerprintGestureService.class);
- }
}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/StubGestureAccessibilityService.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubGestureAccessibilityService.java
index 68c4e7b..eb90389 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/StubGestureAccessibilityService.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubGestureAccessibilityService.java
@@ -15,22 +15,9 @@
package android.accessibilityservice.cts;
import android.accessibility.cts.common.InstrumentedAccessibilityService;
-import android.accessibilityservice.GestureDescription;
-import android.app.Instrumentation;
-import android.os.Handler;
/**
* A stub accessibility service to install for testing gesture dispatch
*/
public class StubGestureAccessibilityService extends InstrumentedAccessibilityService {
-
- public boolean doDispatchGesture(GestureDescription description, GestureResultCallback callback,
- Handler handler) {
- return dispatchGesture(description, callback, handler);
- }
-
- public static StubGestureAccessibilityService enableSelf(Instrumentation instrumentation) {
- return InstrumentedAccessibilityService.enableService(
- instrumentation, StubGestureAccessibilityService.class);
- }
}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/StubMagnificationAccessibilityService.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubMagnificationAccessibilityService.java
index 195ce89..2ce0e37 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/StubMagnificationAccessibilityService.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubMagnificationAccessibilityService.java
@@ -15,16 +15,9 @@
package android.accessibilityservice.cts;
import android.accessibility.cts.common.InstrumentedAccessibilityService;
-import android.app.Instrumentation;
/**
* A stub accessibility service to install for testing gesture dispatch
*/
public class StubMagnificationAccessibilityService extends InstrumentedAccessibilityService {
-
- public static StubMagnificationAccessibilityService enableSelf(
- Instrumentation instrumentation) {
- return InstrumentedAccessibilityService.enableService(
- instrumentation, StubMagnificationAccessibilityService.class);
- }
}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/TouchExplorationStubAccessibilityService.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/TouchExplorationStubAccessibilityService.java
new file mode 100644
index 0000000..61a1cea
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/TouchExplorationStubAccessibilityService.java
@@ -0,0 +1,44 @@
+/**
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * <p>Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * <p>http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * <p>Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.accessibilityservice.cts;
+
+import static android.view.accessibility.AccessibilityEvent.TYPE_GESTURE_DETECTION_END;
+import static android.view.accessibility.AccessibilityEvent.TYPE_GESTURE_DETECTION_START;
+import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED;
+import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_CLICKED;
+import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_LONG_CLICKED;
+
+import android.view.accessibility.AccessibilityEvent;
+
+/**
+ * This accessibility service stub collects all events relating to touch exploration rather than
+ * just the few collected by GestureDetectionStubAccessibilityService
+ */
+public class TouchExplorationStubAccessibilityService
+ extends GestureDetectionStubAccessibilityService {
+ @Override
+ public void onAccessibilityEvent(AccessibilityEvent event) {
+ synchronized (mLock) {
+ switch (event.getEventType()) {
+ case TYPE_GESTURE_DETECTION_START:
+ case TYPE_GESTURE_DETECTION_END:
+ case TYPE_VIEW_ACCESSIBILITY_FOCUSED:
+ case TYPE_VIEW_CLICKED:
+ case TYPE_VIEW_LONG_CLICKED:
+ mCollectedEvents.add(event.getEventType());
+ }
+ }
+ super.onAccessibilityEvent(event);
+ }
+}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/TouchExplorerTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/TouchExplorerTest.java
new file mode 100644
index 0000000..4beae0b
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/TouchExplorerTest.java
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accessibilityservice.cts;
+
+import static android.accessibilityservice.cts.utils.AsyncUtils.await;
+import static android.accessibilityservice.cts.utils.GestureUtils.IS_ACTION_DOWN;
+import static android.accessibilityservice.cts.utils.GestureUtils.IS_ACTION_MOVE;
+import static android.accessibilityservice.cts.utils.GestureUtils.IS_ACTION_UP;
+import static android.accessibilityservice.cts.utils.GestureUtils.add;
+import static android.accessibilityservice.cts.utils.GestureUtils.ceil;
+import static android.accessibilityservice.cts.utils.GestureUtils.click;
+import static android.accessibilityservice.cts.utils.GestureUtils.diff;
+import static android.accessibilityservice.cts.utils.GestureUtils.dispatchGesture;
+import static android.accessibilityservice.cts.utils.GestureUtils.doubleTap;
+import static android.accessibilityservice.cts.utils.GestureUtils.doubleTapAndHold;
+import static android.accessibilityservice.cts.utils.GestureUtils.isRawAtPoint;
+import static android.accessibilityservice.cts.utils.GestureUtils.swipe;
+import static android.accessibilityservice.cts.utils.GestureUtils.times;
+import static android.view.MotionEvent.ACTION_HOVER_ENTER;
+import static android.view.MotionEvent.ACTION_HOVER_EXIT;
+import static android.view.MotionEvent.ACTION_HOVER_MOVE;
+import static android.view.accessibility.AccessibilityEvent.TYPE_GESTURE_DETECTION_END;
+import static android.view.accessibility.AccessibilityEvent.TYPE_GESTURE_DETECTION_START;
+import static android.view.accessibility.AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END;
+import static android.view.accessibility.AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START;
+import static android.view.accessibility.AccessibilityEvent.TYPE_TOUCH_INTERACTION_END;
+import static android.view.accessibility.AccessibilityEvent.TYPE_TOUCH_INTERACTION_START;
+import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED;
+import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_CLICKED;
+import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_LONG_CLICKED;
+
+import static org.hamcrest.CoreMatchers.both;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
+import android.accessibility.cts.common.InstrumentedAccessibilityServiceTestRule;
+import android.accessibilityservice.GestureDescription;
+import android.accessibilityservice.GestureDescription.StrokeDescription;
+import android.accessibilityservice.cts.AccessibilityGestureDispatchTest.GestureDispatchActivity;
+import android.accessibilityservice.cts.utils.EventCapturingClickListener;
+import android.accessibilityservice.cts.utils.EventCapturingHoverListener;
+import android.accessibilityservice.cts.utils.EventCapturingLongClickListener;
+import android.accessibilityservice.cts.utils.EventCapturingTouchListener;
+import android.app.Instrumentation;
+import android.app.UiAutomation;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.Point;
+import android.graphics.PointF;
+import android.platform.test.annotations.AppModeFull;
+import android.util.DisplayMetrics;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+/**
+ * A set of tests for testing touch exploration. Each test dispatches a gesture and checks for the
+ * appropriate hover and/or touch events followed by the appropriate accessibility events. Some
+ * tests will then check for events from the view.
+ */
+@RunWith(AndroidJUnit4.class)
+@AppModeFull
+public class TouchExplorerTest {
+ // Constants
+ private static final float GESTURE_LENGTH_INCHES = 1.0f;
+ private static final int SWIPE_TIME_MILLIS = 400;
+ private TouchExplorationStubAccessibilityService mService;
+ private Instrumentation mInstrumentation;
+ private UiAutomation mUiAutomation;
+ private boolean mHasTouchscreen;
+ private boolean mScreenBigEnough;
+ private EventCapturingHoverListener mHoverListener = new EventCapturingHoverListener(false);
+ private EventCapturingTouchListener mTouchListener = new EventCapturingTouchListener(false);
+ private EventCapturingClickListener mClickListener = new EventCapturingClickListener();
+ private EventCapturingLongClickListener mLongClickListener =
+ new EventCapturingLongClickListener();
+
+ private ActivityTestRule<GestureDispatchActivity> mActivityRule =
+ new ActivityTestRule<>(GestureDispatchActivity.class, false);
+
+ private InstrumentedAccessibilityServiceTestRule<TouchExplorationStubAccessibilityService>
+ mServiceRule = new InstrumentedAccessibilityServiceTestRule<>(
+ TouchExplorationStubAccessibilityService.class, false);
+
+ private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+ new AccessibilityDumpOnFailureRule();
+
+ @Rule
+ public final RuleChain mRuleChain = RuleChain
+ .outerRule(mActivityRule)
+ .around(mServiceRule)
+ .around(mDumpOnFailureRule);
+
+ Point mCenter; // Center of screen. Gestures all start from this point.
+ PointF mTapLocation;
+ float mSwipeDistance;
+ View mView;
+
+ @Before
+ public void setUp() throws Exception {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mUiAutomation = mInstrumentation.getUiAutomation(
+ UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
+ PackageManager pm = mInstrumentation.getContext().getPackageManager();
+ mHasTouchscreen = pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN)
+ || pm.hasSystemFeature(PackageManager.FEATURE_FAKETOUCH);
+ // Find screen size, check that it is big enough for gestures.
+ // Gestures will start in the center of the screen, so we need enough horiz/vert space.
+ WindowManager windowManager =
+ (WindowManager)
+ mInstrumentation.getContext().getSystemService(Context.WINDOW_SERVICE);
+ final DisplayMetrics metrics = new DisplayMetrics();
+ windowManager.getDefaultDisplay().getRealMetrics(metrics);
+ mCenter = new Point((int) metrics.widthPixels / 2, (int) metrics.heightPixels / 2);
+ mTapLocation = new PointF(mCenter);
+ mScreenBigEnough = (metrics.widthPixels / (2 * metrics.xdpi) > GESTURE_LENGTH_INCHES);
+ if (!mHasTouchscreen || !mScreenBigEnough) return;
+ mService = mServiceRule.enableService();
+ mView = mActivityRule.getActivity().findViewById(R.id.full_screen_text_view);
+ mView.setOnHoverListener(mHoverListener);
+ mView.setOnTouchListener(mTouchListener);
+ mInstrumentation.runOnMainSync(
+ () -> {
+ mSwipeDistance = mView.getWidth() / 4;
+ mView.setOnClickListener(mClickListener);
+ mView.setOnLongClickListener(mLongClickListener);
+ });
+ }
+
+ /** Test a slow swipe which should initiate touch exploration. */
+ @Test
+ @AppModeFull
+ public void testSlowSwipe_initiatesTouchExploration() {
+ if (!mHasTouchscreen || !mScreenBigEnough) return;
+ dispatch(swipe(mTapLocation, add(mTapLocation, mSwipeDistance, 0), SWIPE_TIME_MILLIS));
+ mHoverListener.assertPropagated(ACTION_HOVER_ENTER, ACTION_HOVER_MOVE, ACTION_HOVER_EXIT);
+ mTouchListener.assertNonePropagated();
+ mService.assertPropagated(
+ TYPE_TOUCH_INTERACTION_START,
+ TYPE_TOUCH_EXPLORATION_GESTURE_START,
+ TYPE_TOUCH_EXPLORATION_GESTURE_END,
+ TYPE_TOUCH_INTERACTION_END);
+ }
+
+ /** Test a fast swipe which should not initiate touch exploration. */
+ @Test
+ @AppModeFull
+ public void testFastSwipe_doesNotInitiateTouchExploration() {
+ if (!mHasTouchscreen || !mScreenBigEnough) return;
+ dispatch(swipe(mTapLocation, add(mTapLocation, mSwipeDistance, 0)));
+ mHoverListener.assertNonePropagated();
+ mTouchListener.assertNonePropagated();
+ mService.assertPropagated(
+ TYPE_TOUCH_INTERACTION_START,
+ TYPE_GESTURE_DETECTION_START,
+ TYPE_GESTURE_DETECTION_END,
+ TYPE_TOUCH_INTERACTION_END);
+ }
+
+ /**
+ * Test a two finger drag. TouchExplorer would perform a drag gesture when two fingers moving
+ * in the same direction.
+ */
+ @Test
+ @AppModeFull
+ public void testTwoFingerDrag_dispatchesEventsBetweenFingers() {
+ if (!mHasTouchscreen || !mScreenBigEnough) return;
+ // A two point moving that are in the same direction can perform a drag gesture by
+ // TouchExplorer while one point moving can not perform a drag gesture. We use two swipes
+ // to emulate a two finger drag gesture.
+ final int twoFingerOffset = (int) mSwipeDistance;
+ final PointF dragStart = mTapLocation;
+ final PointF dragEnd = add(dragStart, 0, mSwipeDistance);
+ final PointF finger1Start = add(dragStart, twoFingerOffset, 0);
+ final PointF finger1End = add(finger1Start, 0, mSwipeDistance);
+ final PointF finger2Start = add(dragStart, -twoFingerOffset, 0);
+ final PointF finger2End = add(finger2Start, 0, mSwipeDistance);
+ dispatch(swipe(finger1Start, finger1End, SWIPE_TIME_MILLIS),
+ swipe(finger2Start, finger2End, SWIPE_TIME_MILLIS));
+ List<MotionEvent> twoFingerPoints = mTouchListener.getRawEvents();
+
+ // Check the drag events performed by a two finger drag. The moving locations would be
+ // adjusted to the middle of two fingers.
+ final int numEvents = twoFingerPoints.size();
+ final int upEventIndex = numEvents - 1;
+ final float intervalFraction = ((float) (twoFingerPoints.get(1).getEventTime()
+ - twoFingerPoints.get(0).getEventTime())) / SWIPE_TIME_MILLIS;
+ for (int i = 0; i < numEvents; i++) {
+ MotionEvent moveEvent = twoFingerPoints.get(i);
+ float fractionOfDrag = intervalFraction * (i + 1);
+ if (i == 0) {
+ PointF downPoint = add(finger2Start,
+ ceil(times(fractionOfDrag, diff(dragEnd, dragStart))));
+ assertThat(moveEvent,
+ both(IS_ACTION_DOWN).and(isRawAtPoint(downPoint)));
+ } else if (i == upEventIndex) {
+ assertThat(moveEvent,
+ both(IS_ACTION_UP).and(isRawAtPoint(finger2End)));
+ } else {
+ PointF intermediatePoint = add(dragStart,
+ ceil(times(fractionOfDrag, diff(dragEnd, dragStart))));
+ assertThat(moveEvent,
+ both(IS_ACTION_MOVE).and(isRawAtPoint(intermediatePoint)));
+ }
+ }
+ }
+
+ /** Test a basic single tap which should initiate touch exploration. */
+ @Test
+ @AppModeFull
+ public void testSingleTap_initiatesTouchExploration() {
+ if (!mHasTouchscreen || !mScreenBigEnough) return;
+ dispatch(click(mTapLocation));
+ mHoverListener.assertPropagated(ACTION_HOVER_ENTER, ACTION_HOVER_EXIT);
+ mTouchListener.assertNonePropagated();
+ mService.assertPropagated(
+ TYPE_TOUCH_INTERACTION_START,
+ TYPE_TOUCH_EXPLORATION_GESTURE_START,
+ TYPE_TOUCH_EXPLORATION_GESTURE_END,
+ TYPE_TOUCH_INTERACTION_END);
+ }
+
+ /**
+ * Test the case where we want to click on the item that has accessibility focus by using
+ * AccessibilityNodeInfo.performAction.
+ */
+ @Test
+ @AppModeFull
+ public void testDoubleTapAccessibilityFocus_performsClick() {
+ if (!mHasTouchscreen || !mScreenBigEnough) return;
+ syncAccessibilityFocusToInputFocus();
+ dispatch(doubleTap(mTapLocation));
+ mHoverListener.assertNonePropagated();
+ // The click should not be delivered via touch events in this case.
+ mTouchListener.assertNonePropagated();
+ mService.assertPropagated(
+ TYPE_VIEW_ACCESSIBILITY_FOCUSED,
+ TYPE_TOUCH_INTERACTION_START,
+ TYPE_TOUCH_INTERACTION_END,
+ TYPE_VIEW_CLICKED);
+ mClickListener.assertClicked(mView);
+ }
+
+ /**
+ * Test the case where we double tap but there is no accessibility focus. Nothing should
+ * happen.
+ */
+ @Test
+ @AppModeFull
+ public void testDoubleTapNoAccessibilityFocus_doesNotPerformClick() {
+ if (!mHasTouchscreen || !mScreenBigEnough) return;
+ dispatch(doubleTap(mTapLocation));
+ mHoverListener.assertNonePropagated();
+ mTouchListener.assertNonePropagated();
+ mService.assertPropagated(
+ TYPE_TOUCH_INTERACTION_START, TYPE_TOUCH_INTERACTION_END);
+ mService.clearEvents();
+ mClickListener.assertNoneClicked();
+ }
+
+ /** Test the case where we want to long click on the item that has accessibility focus. */
+ @Test
+ @AppModeFull
+ public void testDoubleTapAndHoldAccessibilityFocus_performsLongClick() {
+ if (!mHasTouchscreen || !mScreenBigEnough) return;
+ syncAccessibilityFocusToInputFocus();
+ dispatch(doubleTapAndHold(mTapLocation));
+ mHoverListener.assertNonePropagated();
+ // The click should not be delivered via touch events in this case.
+ mTouchListener.assertNonePropagated();
+ mService.assertPropagated(
+ TYPE_VIEW_ACCESSIBILITY_FOCUSED,
+ TYPE_TOUCH_INTERACTION_START,
+ TYPE_VIEW_LONG_CLICKED,
+ TYPE_TOUCH_INTERACTION_END);
+ mLongClickListener.assertLongClicked(mView);
+ }
+
+ /**
+ * Test the case where we double tap and hold but there is no accessibility focus.
+ * Nothing should happen.
+ */
+ @Test
+ @AppModeFull
+ public void testDoubleTapAndHoldNoAccessibilityFocus_doesNotPerformLongClick() {
+ if (!mHasTouchscreen || !mScreenBigEnough) return;
+ dispatch(doubleTap(mTapLocation));
+ mHoverListener.assertNonePropagated();
+ mTouchListener.assertNonePropagated();
+ mService.assertPropagated(
+ TYPE_TOUCH_INTERACTION_START, TYPE_TOUCH_INTERACTION_END);
+ mService.clearEvents();
+ mLongClickListener.assertNoneLongClicked();
+ }
+
+ public void dispatch(StrokeDescription firstStroke, StrokeDescription... rest) {
+ GestureDescription.Builder builder =
+ new GestureDescription.Builder().addStroke(firstStroke);
+ for (StrokeDescription stroke : rest) {
+ builder.addStroke(stroke);
+ }
+ dispatch(builder.build());
+ }
+
+ public void dispatch(GestureDescription gesture) {
+ await(dispatchGesture(mService, gesture));
+ }
+
+ /** Set the accessibility focus to the element that has input focus. */
+ private void syncAccessibilityFocusToInputFocus() {
+ mService.runOnServiceSync(
+ () -> {
+ mUiAutomation
+ .getRootInActiveWindow()
+ .findFocus(AccessibilityNodeInfo.FOCUS_INPUT)
+ .performAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
+ });
+ }
+}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/ActivityLaunchUtils.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/ActivityLaunchUtils.java
index 6902c8f..c3220ad 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/ActivityLaunchUtils.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/ActivityLaunchUtils.java
@@ -17,11 +17,13 @@
import static android.accessibilityservice.cts.utils.AsyncUtils.DEFAULT_TIMEOUT_MS;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.app.Activity;
+import android.app.ActivityOptions;
import android.app.Instrumentation;
import android.app.UiAutomation;
import android.content.Context;
@@ -29,10 +31,13 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.Rect;
+import android.os.Bundle;
import android.os.PowerManager;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.Log;
+import android.util.SparseArray;
+import android.view.Display;
import android.view.InputDevice;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
@@ -62,40 +67,37 @@
public static <T extends Activity> T launchActivityAndWaitForItToBeOnscreen(
Instrumentation instrumentation, UiAutomation uiAutomation,
ActivityTestRule<T> rule) throws Exception {
- final int[] location = new int[2];
- final StringBuilder activityPackage = new StringBuilder();
- final Rect bounds = new Rect();
- final StringBuilder activityTitle = new StringBuilder();
- // Make sure we get window events, so we'll know when the window appears
- AccessibilityServiceInfo info = uiAutomation.getServiceInfo();
- info.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
- uiAutomation.setServiceInfo(info);
- homeScreenOrBust(instrumentation.getContext(), uiAutomation);
- final AccessibilityEvent awaitedEvent = uiAutomation.executeAndWaitForEvent(
- () -> {
- mTempActivity = rule.launchActivity(null);
- final StringBuilder builder = new StringBuilder();
- instrumentation.runOnMainSync(() -> {
- mTempActivity.getWindow().getDecorView().getLocationOnScreen(location);
- activityPackage.append(mTempActivity.getPackageName());
- });
- instrumentation.waitForIdleSync();
- activityTitle.append(getActivityTitle(instrumentation, mTempActivity));
- },
- (event) -> {
- final AccessibilityWindowInfo window =
- findWindowByTitle(uiAutomation, activityTitle);
- if (window == null) return false;
- window.getBoundsInScreen(bounds);
- mTempActivity.getWindow().getDecorView().getLocationOnScreen(location);
- if (bounds.isEmpty()) {
- return false;
- }
- return (!bounds.isEmpty())
- && (bounds.left == location[0]) && (bounds.top == location[1]);
- }, DEFAULT_TIMEOUT_MS);
- assertNotNull(awaitedEvent);
- return (T) mTempActivity;
+ ActivityLauncher activityLauncher = new ActivityLauncher() {
+ @Override
+ Activity launchActivity() {
+ return rule.launchActivity(null);
+ }
+ };
+ return launchActivityOnSpecifiedDisplayAndWaitForItToBeOnscreen(instrumentation,
+ uiAutomation, activityLauncher, Display.DEFAULT_DISPLAY);
+ }
+
+ /**
+ * If this activity would be launched at virtual display, please finishes this activity before
+ * this test ended. Otherwise it will be displayed on default display and impacts the next test.
+ */
+ public static <T extends Activity> T launchActivityOnSpecifiedDisplayAndWaitForItToBeOnscreen(
+ Instrumentation instrumentation, UiAutomation uiAutomation, Class<T> clazz,
+ int displayId) throws Exception {
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchDisplayId(displayId);
+ final Intent intent = new Intent(instrumentation.getTargetContext(), clazz);
+ // Add clear task because this activity may on other display.
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK|Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ ActivityLauncher activityLauncher = new ActivityLauncher() {
+ @Override
+ Activity launchActivity() {
+ return instrumentation.startActivitySync(intent, options.toBundle());
+ }
+ };
+ return launchActivityOnSpecifiedDisplayAndWaitForItToBeOnscreen(instrumentation,
+ uiAutomation, activityLauncher, displayId);
}
public static CharSequence getActivityTitle(
@@ -108,16 +110,15 @@
public static AccessibilityWindowInfo findWindowByTitle(
UiAutomation uiAutomation, CharSequence title) {
final List<AccessibilityWindowInfo> windows = uiAutomation.getWindows();
- AccessibilityWindowInfo returnValue = null;
- for (int i = 0; i < windows.size(); i++) {
- final AccessibilityWindowInfo window = windows.get(i);
- if (TextUtils.equals(title, window.getTitle())) {
- returnValue = window;
- } else {
- window.recycle();
- }
- }
- return returnValue;
+ return findWindowByTitleWithList(title, windows);
+ }
+
+ public static AccessibilityWindowInfo findWindowByTitleAndDisplay(
+ UiAutomation uiAutomation, CharSequence title, int displayId) {
+ final SparseArray<List<AccessibilityWindowInfo>> allWindows =
+ uiAutomation.getWindowsOnAllDisplays();
+ final List<AccessibilityWindowInfo> windowsOfDisplay = allWindows.get(displayId);
+ return findWindowByTitleWithList(title, windowsOfDisplay);
}
public static void homeScreenOrBust(Context context, UiAutomation uiAutomation) {
@@ -230,4 +231,74 @@
uiAutomation.setOnAccessibilityEventListener(null);
}
}
+
+ private static <T extends Activity> T launchActivityOnSpecifiedDisplayAndWaitForItToBeOnscreen(
+ Instrumentation instrumentation, UiAutomation uiAutomation,
+ ActivityLauncher activityLauncher, int displayId) throws Exception {
+ final int[] location = new int[2];
+ final StringBuilder activityPackage = new StringBuilder();
+ final Rect bounds = new Rect();
+ final StringBuilder activityTitle = new StringBuilder();
+ // Make sure we get window events, so we'll know when the window appears
+ AccessibilityServiceInfo info = uiAutomation.getServiceInfo();
+ info.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
+ uiAutomation.setServiceInfo(info);
+ // There is no any window on virtual display even doing GLOBAL_ACTION_HOME, so only
+ // checking the home screen for default display.
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ homeScreenOrBust(instrumentation.getContext(), uiAutomation);
+ }
+
+ final AccessibilityEvent awaitedEvent = uiAutomation.executeAndWaitForEvent(
+ () -> {
+ mTempActivity = activityLauncher.launchActivity();
+ instrumentation.runOnMainSync(() -> {
+ mTempActivity.getWindow().getDecorView().getLocationOnScreen(location);
+ activityPackage.append(mTempActivity.getPackageName());
+ });
+ instrumentation.waitForIdleSync();
+ activityTitle.append(getActivityTitle(instrumentation, mTempActivity));
+ },
+ (event) -> {
+ AccessibilityNodeInfo node = event.getSource();
+ if (node != null) {
+ final AccessibilityWindowInfo window = node.getWindow();
+ if(TextUtils.equals(activityTitle, window.getTitle())) {
+ return true;
+ }
+ }
+ final AccessibilityWindowInfo window =
+ findWindowByTitleAndDisplay(uiAutomation, activityTitle, displayId);
+ if (window == null) return false;
+ window.getBoundsInScreen(bounds);
+ mTempActivity.getWindow().getDecorView().getLocationOnScreen(location);
+ if (bounds.isEmpty()) {
+ return false;
+ }
+ return (!bounds.isEmpty())
+ && (bounds.left == location[0]) && (bounds.top == location[1]);
+ }, DEFAULT_TIMEOUT_MS);
+ assertNotNull(awaitedEvent);
+ return (T) mTempActivity;
+ }
+
+ private static AccessibilityWindowInfo findWindowByTitleWithList(CharSequence title,
+ List<AccessibilityWindowInfo> windows) {
+ AccessibilityWindowInfo returnValue = null;
+ if (windows != null && windows.size() > 0) {
+ for (int i = 0; i < windows.size(); i++) {
+ final AccessibilityWindowInfo window = windows.get(i);
+ if (TextUtils.equals(title, window.getTitle())) {
+ returnValue = window;
+ } else {
+ window.recycle();
+ }
+ }
+ }
+ return returnValue;
+ }
+
+ private static abstract class ActivityLauncher {
+ abstract Activity launchActivity();
+ }
}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/DisplayUtils.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/DisplayUtils.java
index a65d859..e11c60e 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/DisplayUtils.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/DisplayUtils.java
@@ -14,18 +14,118 @@
package android.accessibilityservice.cts.utils;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
+import static android.view.Display.FLAG_PRIVATE;
+
import android.app.Activity;
+import android.content.Context;
+import android.graphics.PixelFormat;
import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.VirtualDisplay;
+import android.media.ImageReader;
+import android.os.Looper;
+import android.util.DisplayMetrics;
+import android.view.Display;
import android.view.Window;
+import android.view.WindowManager;
+
+import com.android.compatibility.common.util.TestUtils;
/**
* Utilities needed when interacting with the display
*/
public class DisplayUtils {
+ private static final int DISPLAY_ADDED_TIMOUT_MS = 5000;
+
public static int getStatusBarHeight(Activity activity) {
final Rect rect = new Rect();
Window window = activity.getWindow();
window.getDecorView().getWindowVisibleDisplayFrame(rect);
return rect.top;
}
+
+ public static class VirtualDisplaySession implements AutoCloseable {
+ private VirtualDisplay mVirtualDisplay;
+ private ImageReader mReader;
+
+ public Display createDisplay(Context context, int width, int height, int density,
+ boolean isPrivate) {
+ if (mReader != null) {
+ throw new IllegalStateException(
+ "Only one display can be created during this session.");
+ }
+ mReader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888,
+ 1 /* maxImages */);
+ int flags = isPrivate ? 0
+ :(VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY | VIRTUAL_DISPLAY_FLAG_PUBLIC);
+ mVirtualDisplay = context.getSystemService(DisplayManager.class).createVirtualDisplay(
+ "A11yDisplay", width, height, density, mReader.getSurface(), flags);
+ return mVirtualDisplay.getDisplay();
+ }
+
+ @Override
+ public void close() {
+ if (mVirtualDisplay != null) {
+ mVirtualDisplay.release();
+ }
+ if (mReader != null) {
+ mReader.close();
+ }
+ }
+
+ /**
+ * Creates a virtual display and waits until it's in display list.
+ * @param context
+ * @param isPrivate if this display is a private display.
+ * @return virtual display.
+ *
+ * @throws IllegalStateException if called from main thread.
+ */
+ public Display createDisplayWithDefaultDisplayMetricsAndWait(Context context,
+ boolean isPrivate) {
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ throw new IllegalStateException("Should not call from main thread");
+ }
+
+ final Object waitObject = new Object();
+ final DisplayManager.DisplayListener listener = new DisplayManager.DisplayListener() {
+ @Override
+ public void onDisplayAdded(int i) {
+ synchronized (waitObject) {
+ waitObject.notifyAll();
+ }
+ }
+
+ @Override
+ public void onDisplayRemoved(int i) {
+ }
+
+ @Override
+ public void onDisplayChanged(int i) {
+ }
+ };
+ final DisplayManager displayManager = (DisplayManager) context.getSystemService(
+ Context.DISPLAY_SERVICE);
+ displayManager.registerDisplayListener(listener, null);
+
+ final WindowManager windowManager = (WindowManager) context.getSystemService(
+ Context.WINDOW_SERVICE);
+ final DisplayMetrics metrics = new DisplayMetrics();
+ windowManager.getDefaultDisplay().getRealMetrics(metrics);
+ final Display display = createDisplay(context, metrics.widthPixels,
+ metrics.heightPixels, metrics.densityDpi, isPrivate);
+
+ try {
+ TestUtils.waitOn(waitObject,
+ () -> displayManager.getDisplay(display.getDisplayId()) != null,
+ DISPLAY_ADDED_TIMOUT_MS,
+ String.format("wait for virtual display %d adding", display.getDisplayId()));
+ } finally {
+ displayManager.unregisterDisplayListener(listener);
+ }
+ return display;
+ }
+ }
}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/EventCapturingClickListener.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/EventCapturingClickListener.java
new file mode 100644
index 0000000..116bccf
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/EventCapturingClickListener.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accessibilityservice.cts.utils;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import android.view.View;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+/** A click event listener that keeps an ordered record of events. */
+public class EventCapturingClickListener implements View.OnClickListener {
+
+ private final BlockingQueue<View> mViews = new LinkedBlockingQueue<>();
+
+ @Override
+ public void onClick(View view) {
+ mViews.offer(view);
+ }
+
+ /** Insure that the specified views have received click events. */
+ public void assertClicked(View... views) {
+ View view;
+ try {
+ for (View v : views) {
+ long waitTime = 5; // seconds
+ view = mViews.poll(waitTime, SECONDS);
+ assertNotNull(
+ "Expected click event for "
+ + v.toString()
+ + " but none present after "
+ + waitTime
+ + " seconds",
+ view);
+ if (v != view) {
+ fail("Unexpected click event for view" + view.toString());
+ }
+ }
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /** Insure that no click events have been received. */
+ public void assertNoneClicked() {
+ try {
+ long waitTime = 1; // seconds
+ View view = mViews.poll(waitTime, SECONDS);
+ if (view != null) {
+ fail("Unexpected click event for view" + view.toString());
+ }
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/EventCapturingHoverListener.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/EventCapturingHoverListener.java
new file mode 100644
index 0000000..87d46ba
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/EventCapturingHoverListener.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accessibilityservice.cts.utils;
+
+import static android.view.MotionEvent.ACTION_HOVER_MOVE;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.view.MotionEvent;
+import android.view.View;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+/** This Listener listens for and logs hover events so they can be checked later by tests. */
+public class EventCapturingHoverListener implements View.OnHoverListener {
+
+ private boolean shouldConsumeEvents; // whether or not to keep events from propagating to other
+ // listeners
+ private final BlockingQueue<MotionEvent> mEvents = new LinkedBlockingQueue<>();
+
+ public EventCapturingHoverListener(boolean shouldConsumeEvents) {
+ this.shouldConsumeEvents = shouldConsumeEvents;
+ }
+
+ public EventCapturingHoverListener() {
+ this.shouldConsumeEvents = true;
+ }
+
+ @Override
+ public boolean onHover(View view, MotionEvent MotionEvent) {
+ assertTrue(mEvents.offer(MotionEvent.obtain(MotionEvent)));
+ return shouldConsumeEvents;
+ }
+
+ /** Insure that no hover events have been detected. */
+ public void assertNonePropagated() {
+ try {
+ long waitTime = 1; // seconds
+ MotionEvent event = mEvents.poll(waitTime, SECONDS);
+ if (event != null) {
+ fail("Unexpected touch event " + event.toString());
+ }
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Check for the specified hover events. Note that specifying ACTION_HOVER_MOVE will match one
+ * or more consecutive ACTION_HOVER_MOVE events.
+ */
+ public void assertPropagated(int... eventTypes) {
+ MotionEvent ev;
+ long waitTime = 5; // seconds
+ try {
+ List<String> expected = new ArrayList<>();
+ List<String> received = new ArrayList<>();
+ for (int e : eventTypes) {
+ expected.add(MotionEvent.actionToString(e));
+ }
+ ev = mEvents.poll(waitTime, SECONDS);
+ assertNotNull(
+ "Expected " + expected + " but none present after " + waitTime + " seconds",
+ ev);
+ // By this point there is at least one received event.
+ received.add(MotionEvent.actionToString(ev.getActionMasked()));
+ ev = mEvents.poll(waitTime, SECONDS);
+ while (ev != null) {
+ int action = ev.getActionMasked();
+ if (action != ACTION_HOVER_MOVE) {
+ received.add(MotionEvent.actionToString(action));
+ } else {
+ // Add the current event if the previous received event was not ACTION_MOVE
+ String prev = received.get(received.size() - 1);
+ if (!prev.equals(MotionEvent.actionToString(ACTION_HOVER_MOVE))) {
+ received.add(MotionEvent.actionToString(action));
+ }
+ }
+ ev = mEvents.poll(waitTime, SECONDS);
+ }
+ assertEquals(expected, received);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/EventCapturingLongClickListener.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/EventCapturingLongClickListener.java
new file mode 100644
index 0000000..5daf89e
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/EventCapturingLongClickListener.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accessibilityservice.cts.utils;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import android.view.View;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+/** A click event listener that keeps an ordered record of events. */
+public class EventCapturingLongClickListener implements View.OnLongClickListener {
+
+ // whether or not to keep events from propagating to other listeners.
+ // Note that setting this to false means that the accessibility service will see both a long
+ // click and a click event when you perform a double tap and hold gesture
+ private boolean shouldConsumeEvents;
+ private final BlockingQueue<View> mViews = new LinkedBlockingQueue<>();
+
+ public EventCapturingLongClickListener(boolean shouldConsumeEvents) {
+ this.shouldConsumeEvents = shouldConsumeEvents;
+ }
+
+ public EventCapturingLongClickListener() {
+ this.shouldConsumeEvents = true;
+ }
+
+ @Override
+ public boolean onLongClick(View view) {
+ mViews.offer(view);
+ return true;
+ }
+
+ /** Insure that the specified views have received long click events. */
+ public void assertLongClicked(View... views) {
+ View view;
+ try {
+ for (View v : views) {
+ long waitTime = 5; // seconds
+ view = mViews.poll(waitTime, SECONDS);
+ assertNotNull(
+ "Expected long click event for "
+ + v.toString()
+ + " but none present after "
+ + waitTime
+ + " seconds",
+ view);
+ if (v != view) {
+ fail("Unexpected long click event for view" + view.toString());
+ }
+ }
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /** Insure that no click events have been received. */
+ public void assertNoneLongClicked() {
+ try {
+ long waitTime = 1; // seconds
+ View view = mViews.poll(waitTime, SECONDS);
+ if (view != null) {
+ fail("Unexpected long click event for view" + view.toString());
+ }
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/EventCapturingTouchListener.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/EventCapturingTouchListener.java
index d7ae4592..3997ebe 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/EventCapturingTouchListener.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/EventCapturingTouchListener.java
@@ -16,22 +16,109 @@
package android.accessibilityservice.cts.utils;
+import static android.view.MotionEvent.ACTION_MOVE;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import android.view.MotionEvent;
import android.view.View;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class EventCapturingTouchListener implements View.OnTouchListener {
+ private static final long WAIT_TIME_SECONDS = 5;
+ private static final long MIN_WAIT_TIME_SECONDS = 2;
+ // whether or not to keep events from propagating to other listeners
+ private boolean shouldConsumeEvents;
+ private final BlockingQueue<MotionEvent> events = new LinkedBlockingQueue<>();
- public final BlockingQueue<MotionEvent> events = new LinkedBlockingQueue<>();
+ public EventCapturingTouchListener(boolean shouldConsumeEvents) {
+ this.shouldConsumeEvents = shouldConsumeEvents;
+ }
+
+ public EventCapturingTouchListener() {
+ this.shouldConsumeEvents = true;
+ }
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
assertTrue(events.offer(MotionEvent.obtain(motionEvent)));
- return true;
+ return shouldConsumeEvents;
+ }
+
+ /** Insure that no touch events have been detected. */
+ public void assertNonePropagated() {
+ try {
+ MotionEvent event = events.poll(MIN_WAIT_TIME_SECONDS, SECONDS);
+ if (event != null) {
+ fail("Unexpected touch event " + event.toString());
+ }
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Check for the specified touch events. Note that specifying ACTION_MOVE will match one or more
+ * consecutive ACTION_MOVE events.
+ */
+ public void assertPropagated(int... eventTypes) {
+ MotionEvent ev;
+ try {
+ List<String> expected = new ArrayList<>();
+ List<String> received = new ArrayList<>();
+ for (int e : eventTypes) {
+ expected.add(MotionEvent.actionToString(e));
+ }
+ ev = events.poll(WAIT_TIME_SECONDS, SECONDS);
+ assertNotNull(
+ "Expected " + expected + " but none present after " + WAIT_TIME_SECONDS
+ + " seconds",
+ ev);
+ // By this point there is at least one received event.
+ received.add(MotionEvent.actionToString(ev.getActionMasked()));
+ ev = events.poll(WAIT_TIME_SECONDS, SECONDS);
+ while (ev != null) {
+ int action = ev.getActionMasked();
+ if (action != ACTION_MOVE) {
+ received.add(MotionEvent.actionToString(action));
+ } else {
+ // Add the current event if the previous received event was not ACTION_MOVE
+ String prev = received.get(received.size() - 1);
+ if (!prev.equals(MotionEvent.actionToString(ACTION_MOVE))) {
+ received.add(MotionEvent.actionToString(action));
+ }
+ }
+ ev = events.poll(WAIT_TIME_SECONDS, SECONDS);
+ }
+ assertEquals(expected, received);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public List<MotionEvent> getRawEvents() {
+ List<MotionEvent> motionEvents = new ArrayList<>();
+ MotionEvent ev;
+ try {
+ ev = events.poll(WAIT_TIME_SECONDS, SECONDS);
+ while (ev != null) {
+ motionEvents.add(ev);
+ ev = events.poll(WAIT_TIME_SECONDS, SECONDS);
+ }
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ assertFalse(motionEvents.isEmpty());
+ return motionEvents;
}
}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/GestureUtils.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/GestureUtils.java
index 99fa727..58716ee 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/GestureUtils.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/GestureUtils.java
@@ -22,12 +22,30 @@
import android.accessibilityservice.GestureDescription.StrokeDescription;
import android.graphics.Path;
import android.graphics.PointF;
+import android.view.MotionEvent;
import android.view.ViewConfiguration;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+
import java.util.concurrent.CompletableFuture;
public class GestureUtils {
+ public static final Matcher<MotionEvent> IS_ACTION_DOWN =
+ new MotionEventActionMatcher(MotionEvent.ACTION_DOWN);
+ public static final Matcher<MotionEvent> IS_ACTION_POINTER_DOWN =
+ new MotionEventActionMatcher(MotionEvent.ACTION_POINTER_DOWN);
+ public static final Matcher<MotionEvent> IS_ACTION_UP =
+ new MotionEventActionMatcher(MotionEvent.ACTION_UP);
+ public static final Matcher<MotionEvent> IS_ACTION_POINTER_UP =
+ new MotionEventActionMatcher(MotionEvent.ACTION_POINTER_UP);
+ public static final Matcher<MotionEvent> IS_ACTION_CANCEL =
+ new MotionEventActionMatcher(MotionEvent.ACTION_CANCEL);
+ public static final Matcher<MotionEvent> IS_ACTION_MOVE =
+ new MotionEventActionMatcher(MotionEvent.ACTION_MOVE);
+
private GestureUtils() {}
public static CompletableFuture<Void> dispatchGesture(
@@ -73,7 +91,7 @@
public static StrokeDescription longClick(PointF point) {
return new StrokeDescription(path(point), 0,
- ViewConfiguration.getLongPressTimeout() * 3 / 2);
+ ViewConfiguration.getLongPressTimeout() * 3);
}
public static StrokeDescription swipe(PointF from, PointF to) {
@@ -141,4 +159,87 @@
public static PointF ceil(PointF p) {
return new PointF((float) Math.ceil(p.x), (float) Math.ceil(p.y));
}
-}
+
+ public static GestureDescription doubleTap(PointF point) {
+ return multiTap(point, 2);
+ }
+
+ public static GestureDescription tripleTap(PointF point) {
+ return multiTap(point, 3);
+ }
+
+ public static GestureDescription multiTap(PointF point, int taps) {
+ GestureDescription.Builder builder = new GestureDescription.Builder();
+ long time = 0;
+ for (int i = 0; i < taps; i++) {
+ StrokeDescription stroke = click(point);
+ builder.addStroke(startingAt(time, stroke));
+ time += stroke.getDuration() + 40;
+ }
+ return builder.build();
+ }
+
+ public static GestureDescription doubleTapAndHold(PointF point) {
+ GestureDescription.Builder builder = new GestureDescription.Builder();
+ StrokeDescription tap1 = click(point);
+ StrokeDescription tap2 = startingAt(endTimeOf(tap1) + 40, longClick(point));
+ builder.addStroke(tap1);
+ builder.addStroke(tap2);
+ return builder.build();
+ }
+
+ private static class MotionEventActionMatcher extends TypeSafeMatcher<MotionEvent> {
+ int mAction;
+
+ MotionEventActionMatcher(int action) {
+ super();
+ mAction = action;
+ }
+
+ @Override
+ protected boolean matchesSafely(MotionEvent motionEvent) {
+ return motionEvent.getActionMasked() == mAction;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("Matching to action " + MotionEvent.actionToString(mAction));
+ }
+ }
+
+ public static Matcher<MotionEvent> isAtPoint(final PointF point) {
+ return isAtPoint(point, 0.01f);
+ }
+
+ public static Matcher<MotionEvent> isAtPoint(final PointF point, final float tol) {
+ return new TypeSafeMatcher<MotionEvent>() {
+ @Override
+ protected boolean matchesSafely(MotionEvent event) {
+ return Math.hypot(event.getX() - point.x, event.getY() - point.y) < tol;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("Matching to point " + point);
+ }
+ };
+ }
+
+ public static Matcher<MotionEvent> isRawAtPoint(final PointF point) {
+ return isRawAtPoint(point, 0.01f);
+ }
+
+ public static Matcher<MotionEvent> isRawAtPoint(final PointF point, final float tol) {
+ return new TypeSafeMatcher<MotionEvent>() {
+ @Override
+ protected boolean matchesSafely(MotionEvent event) {
+ return Math.hypot(event.getRawX() - point.x, event.getRawY() - point.y) < tol;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("Matching to point " + point);
+ }
+ };
+ }
+}
\ No newline at end of file
diff --git a/tests/admin/OWNERS b/tests/admin/OWNERS
new file mode 100644
index 0000000..58d02a3
--- /dev/null
+++ b/tests/admin/OWNERS
@@ -0,0 +1,9 @@
+# Bug component: 100560
+sandness@google.com
+rubinxu@google.com
+eranm@google.com
+kholoudm@google.com
+pgrafov@google.com
+alexkershaw@google.com
+arangelov@google.com
+scottjonathan@google.com
diff --git a/tests/app/AndroidTest.xml b/tests/app/AndroidTest.xml
index 461a929..5c2e4cf 100644
--- a/tests/app/AndroidTest.xml
+++ b/tests/app/AndroidTest.xml
@@ -18,6 +18,7 @@
<option name="config-descriptor:metadata" key="component" value="framework" />
<option name="config-descriptor:metadata" key="parameter" value="instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.LocationCheck" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="force-install-mode" value="FULL"/>
@@ -28,6 +29,7 @@
<option name="test-file-name" value="CtsAppTestStubsApp3.apk" />
<option name="test-file-name" value="CtsAppTestStubsApp2.apk" />
<option name="test-file-name" value="CtsAppTestCases.apk" />
+ <option name="test-file-name" value="CtsBadProviderStubs.apk" />
<option name="test-file-name" value="CtsCantSaveState1.apk" />
<option name="test-file-name" value="CtsCantSaveState2.apk" />
<option name="test-file-name" value="NotificationDelegator.apk" />
diff --git a/tests/app/BadProviderStubs/Android.bp b/tests/app/BadProviderStubs/Android.bp
new file mode 100644
index 0000000..3180ef5
--- /dev/null
+++ b/tests/app/BadProviderStubs/Android.bp
@@ -0,0 +1,34 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+android_test_helper_app {
+ name: "CtsBadProviderStubs",
+ defaults: ["cts_support_defaults"],
+ static_libs: ["android-support-annotations"],
+ srcs: ["src/**/*.java"],
+ sdk_version: "current",
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+ optimize: {
+ enabled: false,
+ },
+ dex_preopt: {
+ enabled: false,
+ },
+}
diff --git a/libs/rollback/testapp/res_v1/values/values.xml b/tests/app/BadProviderStubs/AndroidManifest.xml
similarity index 66%
copy from libs/rollback/testapp/res_v1/values/values.xml
copy to tests/app/BadProviderStubs/AndroidManifest.xml
index 8e71917..55f2228 100644
--- a/libs/rollback/testapp/res_v1/values/values.xml
+++ b/tests/app/BadProviderStubs/AndroidManifest.xml
@@ -14,6 +14,14 @@
limitations under the License.
-->
-<resources>
- <integer name="app_version">1</integer>
-</resources>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.cts.stubbad">
+
+ <application>
+ <provider
+ android:name=".BadProviderStub"
+ android:authorities="com.android.cts.stubbad.badprovider"
+ android:exported="true" />
+ </application>
+
+</manifest>
diff --git a/tests/app/BadProviderStubs/src/com/android/cts/stubbad/BadProviderStub.java b/tests/app/BadProviderStubs/src/com/android/cts/stubbad/BadProviderStub.java
new file mode 100644
index 0000000..0103391
--- /dev/null
+++ b/tests/app/BadProviderStubs/src/com/android/cts/stubbad/BadProviderStub.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.stubbad;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+public class BadProviderStub extends ContentProvider {
+ @Override
+ public boolean onCreate() {
+ System.exit(0);
+ return false;
+ }
+
+ @Override
+ public Cursor query(@NonNull Uri uri, @Nullable String[] projection,
+ @Nullable String selection, @Nullable String[] selectionArgs,
+ @Nullable String sortOrder) {
+ return null;
+ }
+
+ @Override
+ public String getType(@NonNull Uri uri) {
+ return null;
+ }
+
+ @Override
+ public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
+ return null;
+ }
+
+ @Override
+ public int delete(@NonNull Uri uri, @Nullable String selection,
+ @Nullable String[] selectionArgs) {
+ return 0;
+ }
+
+ @Override
+ public int update(@NonNull Uri uri, @Nullable ContentValues values,
+ @Nullable String selection, @Nullable String[] selectionArgs) {
+ return 0;
+ }
+}
diff --git a/tests/app/NotificationDelegator/AndroidManifest.xml b/tests/app/NotificationDelegator/AndroidManifest.xml
index 54d1f05..fbdf219 100644
--- a/tests/app/NotificationDelegator/AndroidManifest.xml
+++ b/tests/app/NotificationDelegator/AndroidManifest.xml
@@ -30,5 +30,13 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
+
+ <activity android:name="com.android.test.notificationdelegator.NotificationDelegateAndPost">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
</application>
</manifest>
diff --git a/tests/app/NotificationDelegator/src/com/android/test/notificationdelegator/NotificationDelegateAndPost.java b/tests/app/NotificationDelegator/src/com/android/test/notificationdelegator/NotificationDelegateAndPost.java
new file mode 100644
index 0000000..521adc5
--- /dev/null
+++ b/tests/app/NotificationDelegator/src/com/android/test/notificationdelegator/NotificationDelegateAndPost.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.test.notificationdelegator;
+
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+
+import android.app.Activity;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.os.Bundle;
+import android.util.Log;
+
+public class NotificationDelegateAndPost extends Activity {
+ private static final String TAG = "DelegateAndPost";
+ private static final String DELEGATE = "android.app.stubs";
+ private static final String CHANNEL = "channel";
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity);
+
+ NotificationManager nm = getSystemService(NotificationManager.class);
+
+ nm.createNotificationChannel(new NotificationChannel(CHANNEL, CHANNEL, IMPORTANCE_LOW));
+ nm.setNotificationDelegate(DELEGATE);
+ Log.d(TAG, "Set delegate: " + nm.getNotificationDelegate());
+
+ Log.d(TAG, "Posting notification with id 9");
+
+ Notification n = new Notification.Builder(this, CHANNEL)
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setContentTitle("posted by delegator")
+ .build();
+
+ nm.notify(9, n);
+
+ finish();
+ }
+}
diff --git a/tests/app/OWNERS b/tests/app/OWNERS
new file mode 100644
index 0000000..3b15c95
--- /dev/null
+++ b/tests/app/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 316234
+include platform/frameworks/base:/services/core/java/com/android/server/am/OWNERS
diff --git a/tests/app/TEST_MAPPING b/tests/app/TEST_MAPPING
index 60d71d3..ca2dd6c 100644
--- a/tests/app/TEST_MAPPING
+++ b/tests/app/TEST_MAPPING
@@ -11,5 +11,10 @@
}
]
}
+ ],
+ "postsubmit": [
+ {
+ "name": "CtsAppTestCases"
+ }
]
}
diff --git a/tests/app/app/AndroidManifest.xml b/tests/app/app/AndroidManifest.xml
index 7d34da0..ddd8910 100644
--- a/tests/app/app/AndroidManifest.xml
+++ b/tests/app/app/AndroidManifest.xml
@@ -45,6 +45,7 @@
<uses-permission android:name="android.permission.SET_WALLPAPER" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
+ <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<application android:label="Android TestCase"
android:icon="@drawable/size_48x48"
@@ -419,6 +420,8 @@
android:label="BubblesTestsService"
android:exported="true">
</service>
+
+ <service android:name="android.app.stubs.LocalAlertService" />
</application>
</manifest>
diff --git a/tests/app/app/src/android/app/stubs/CommandReceiver.java b/tests/app/app/src/android/app/stubs/CommandReceiver.java
index bc2b4d4..674fd41 100644
--- a/tests/app/app/src/android/app/stubs/CommandReceiver.java
+++ b/tests/app/app/src/android/app/stubs/CommandReceiver.java
@@ -40,6 +40,8 @@
public static final int COMMAND_STOP_FOREGROUND_SERVICE = 4;
public static final int COMMAND_START_FOREGROUND_SERVICE_LOCATION = 5;
public static final int COMMAND_STOP_FOREGROUND_SERVICE_LOCATION = 6;
+ public static final int COMMAND_START_ALERT_SERVICE = 7;
+ public static final int COMMAND_STOP_ALERT_SERVICE = 8;
public static final String EXTRA_COMMAND = "android.app.stubs.extra.COMMAND";
public static final String EXTRA_TARGET_PACKAGE = "android.app.stubs.extra.TARGET_PACKAGE";
@@ -84,6 +86,12 @@
case COMMAND_STOP_FOREGROUND_SERVICE_LOCATION:
doStopForegroundService(context, LocalForegroundServiceLocation.class);
break;
+ case COMMAND_START_ALERT_SERVICE:
+ doStartAlertService(context);
+ break;
+ case COMMAND_STOP_ALERT_SERVICE:
+ doStopAlertService(context);
+ break;
}
}
@@ -95,13 +103,13 @@
bindIntent.setComponent(new ComponentName(targetPackage, SERVICE_NAME));
ServiceConnection connection = addServiceConnection(targetPackage);
+
context.bindService(bindIntent, connection, flags | Context.BIND_AUTO_CREATE);
}
private void doUnbindService(Context context, Intent commandIntent) {
String targetPackage = getTargetPackage(commandIntent);
- ServiceConnection connection = sServiceMap.remove(targetPackage);
- context.unbindService(connection);
+ context.unbindService(sServiceMap.remove(targetPackage));
}
private void doStartForegroundService(Context context, Class cls) {
@@ -124,6 +132,18 @@
context.stopService(fgsIntent);
}
+ private void doStartAlertService(Context context) {
+ Intent intent = new Intent(context, LocalAlertService.class);
+ intent.setAction(LocalAlertService.COMMAND_SHOW_ALERT);
+ context.startService(intent);
+ }
+
+ private void doStopAlertService(Context context) {
+ Intent intent = new Intent(context, LocalAlertService.class);
+ intent.setAction(LocalAlertService.COMMAND_HIDE_ALERT);
+ context.startService(intent);
+ }
+
private String getTargetPackage(Intent intent) {
return intent.getStringExtra(EXTRA_TARGET_PACKAGE);
}
diff --git a/tests/app/app/src/android/app/stubs/LocalAlertService.java b/tests/app/app/src/android/app/stubs/LocalAlertService.java
new file mode 100644
index 0000000..52dbc58
--- /dev/null
+++ b/tests/app/app/src/android/app/stubs/LocalAlertService.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package android.app.stubs;
+
+import android.app.Service;
+import android.content.Intent;
+import android.graphics.Color;
+import android.graphics.Point;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.util.Log;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.TextView;
+
+import static android.view.Gravity.LEFT;
+import static android.view.Gravity.TOP;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
+
+public class LocalAlertService extends Service {
+ public static final String COMMAND_SHOW_ALERT = "show";
+ public static final String COMMAND_HIDE_ALERT = "hide";
+
+ private static View mAlertWindow = null;
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ String action = intent.getAction();
+
+ if (COMMAND_SHOW_ALERT.equals(action)) {
+ mAlertWindow = showAlertWindow(getPackageName());
+ } else if (COMMAND_HIDE_ALERT.equals(action)) {
+ hideAlertWindow(mAlertWindow);
+ mAlertWindow = null;
+ }
+ return START_NOT_STICKY;
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ private View showAlertWindow(String windowName) {
+ final Point size = new Point();
+ final WindowManager wm = getSystemService(WindowManager.class);
+ wm.getDefaultDisplay().getSize(size);
+
+ WindowManager.LayoutParams params = new WindowManager.LayoutParams(
+ TYPE_APPLICATION_OVERLAY, FLAG_NOT_FOCUSABLE | FLAG_WATCH_OUTSIDE_TOUCH |
+ FLAG_NOT_TOUCHABLE);
+ params.width = size.x / 3;
+ params.height = size.y / 3;
+ params.gravity = TOP | LEFT;
+ params.setTitle(windowName);
+
+ final TextView view = new TextView(this);
+ view.setText(windowName);
+ view.setBackgroundColor(Color.RED);
+ wm.addView(view, params);
+ return view;
+ }
+
+ private void hideAlertWindow(View window) {
+ final WindowManager wm = getSystemService(WindowManager.class);
+ wm.removeViewImmediate(window);
+ }
+}
diff --git a/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java b/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
index 2a0e096..410ae34 100644
--- a/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
+++ b/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
@@ -82,10 +82,18 @@
static final String SIMPLE_PACKAGE_NAME = "com.android.cts.launcherapps.simpleapp";
static final String SIMPLE_SERVICE = ".SimpleService";
static final String SIMPLE_SERVICE2 = ".SimpleService2";
+ static final String SIMPLE_SERVICE3 = ".SimpleService3";
static final String SIMPLE_RECEIVER_START_SERVICE = ".SimpleReceiverStartService";
static final String SIMPLE_ACTIVITY_START_SERVICE = ".SimpleActivityStartService";
+ static final String SIMPLE_ACTIVITY_START_FG_SERVICE = ".SimpleActivityStartFgService";
public static String ACTION_SIMPLE_ACTIVITY_START_SERVICE_RESULT =
"com.android.cts.launcherapps.simpleapp.SimpleActivityStartService.RESULT";
+ static final String ACTION_SIMPLE_ACTIVITY_START_FG =
+ "com.android.cts.launcherapps.simpleapp.SimpleActivityStartFgService.START_THEN_FG";
+ public static String ACTION_SIMPLE_ACTIVITY_START_FG_SERVICE_RESULT =
+ "com.android.cts.launcherapps.simpleapp.SimpleActivityStartFgService.NOW_FOREGROUND";
+ public static String ACTION_FINISH_EVERYTHING =
+ "com.android.cts.launcherapps.simpleapp.SimpleActivityStartFgService.FINISH_ALL";
// APKs for testing heavy weight app interactions.
static final String CANT_SAVE_STATE_1_PACKAGE_NAME = "com.android.test.cantsavestate1";
@@ -94,6 +102,8 @@
// Actions
static final String ACTION_START_FOREGROUND = "com.android.test.action.START_FOREGROUND";
static final String ACTION_STOP_FOREGROUND = "com.android.test.action.STOP_FOREGROUND";
+ static final String ACTION_START_THEN_FG = "com.android.test.action.START_THEN_FG";
+ static final String ACTION_STOP_SERVICE = "com.android.test.action.STOP";
private static final int TEMP_WHITELIST_DURATION_MS = 2000;
@@ -103,6 +113,8 @@
private Intent mServiceStartForegroundIntent;
private Intent mServiceStopForegroundIntent;
private Intent mService2Intent;
+ private Intent mService3Intent;
+ private Intent mServiceStartForeground3Intent;
private Intent mMainProcess[];
private Intent mAllProcesses[];
@@ -122,8 +134,10 @@
mServiceStartForegroundIntent.setAction(ACTION_START_FOREGROUND);
mServiceStopForegroundIntent = new Intent(mServiceIntent);
mServiceStopForegroundIntent.setAction(ACTION_STOP_FOREGROUND);
- mService2Intent = new Intent();
- mService2Intent.setClassName(SIMPLE_PACKAGE_NAME, SIMPLE_PACKAGE_NAME + SIMPLE_SERVICE2);
+ mService2Intent = new Intent()
+ .setClassName(SIMPLE_PACKAGE_NAME, SIMPLE_PACKAGE_NAME + SIMPLE_SERVICE2);
+ mService3Intent = new Intent()
+ .setClassName(SIMPLE_PACKAGE_NAME, SIMPLE_PACKAGE_NAME + SIMPLE_SERVICE3);
mMainProcess = new Intent[1];
mMainProcess[0] = mServiceIntent;
mAllProcesses = new Intent[2];
@@ -131,6 +145,7 @@
mAllProcesses[1] = mService2Intent;
mContext.stopService(mServiceIntent);
mContext.stopService(mService2Intent);
+ mContext.stopService(mService3Intent);
turnScreenOn();
removeTestAppFromWhitelists();
mAppCount = 0;
@@ -160,17 +175,16 @@
executeShellCmd("input keyevent KEYCODE_WAKEUP");
executeShellCmd("wm dismiss-keyguard");
/*
- Wait until the screen becomes interactive to start the test cases.
- Otherwise the procstat may start in TOP_SLEEPING state, and this
- causes test case testBackgroundCheckActivityService to fail.
- Note: There could still a small chance the procstat is TOP_SLEEPING
- when the predicate returns true.
- */
- CommonTestUtils.waitUntil("Device does not wake up after 5 seconds",
- 5,
- () -> {
- return isScreenInteractive() && !isKeyguardLocked();
- });
+ Wait until the screen becomes interactive to start the test cases.
+ Otherwise the procstat may start in TOP_SLEEPING state, and this
+ causes test case testBackgroundCheckActivityService to fail.
+ Note: There could still a small chance the procstat is TOP_SLEEPING
+ when the predicate returns true.
+ */
+ CommonTestUtils.waitUntil("Device does not wake up after 5 seconds", 5,
+ () -> {
+ return isScreenInteractive() && !isKeyguardLocked();
+ });
}
private void removeTestAppFromWhitelists() throws Exception {
@@ -912,7 +926,7 @@
waiter.prepare(ACTION_SIMPLE_ACTIVITY_START_SERVICE_RESULT);
activityIntent.putExtra("service", mServiceIntent);
mContext.startActivity(activityIntent);
- Intent resultIntent = waiter.doWait(WAIT_TIME);
+ Intent resultIntent = waiter.doWait(WAIT_TIME * 2);
int brCode = resultIntent.getIntExtra("result", Activity.RESULT_CANCELED);
if (brCode != Activity.RESULT_FIRST_USER) {
fail("Failed starting service, result=" + brCode);
@@ -1122,6 +1136,78 @@
}
}
+ /**
+ * Verify that an app under background restrictions has its foreground services
+ * demoted to ordinary service state when it is no longer the top app.
+ */
+ public void testBgRestrictedForegroundService() throws Exception {
+ final Intent activityIntent = new Intent()
+ .setClassName(SIMPLE_PACKAGE_NAME,
+ SIMPLE_PACKAGE_NAME + SIMPLE_ACTIVITY_START_FG_SERVICE)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ final ServiceProcessController controller = new ServiceProcessController(mContext,
+ getInstrumentation(), STUB_PACKAGE_NAME, mAllProcesses, WAIT_TIME);
+ final WatchUidRunner uidWatcher = controller.getUidWatcher();
+
+ final Intent homeIntent = new Intent()
+ .setAction(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_HOME)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+
+ final Intent serviceStartIntent = new Intent(mService3Intent)
+ .setAction(ACTION_START_THEN_FG);
+ activityIntent.putExtra("service", serviceStartIntent);
+ boolean activityStarted = false;
+
+ try {
+ // First kill the process to start out in a stable state.
+ controller.ensureProcessGone();
+
+ // Do initial setup.
+ controller.denyAnyInBackgroundOp();
+ controller.makeUidIdle();
+ controller.removeFromWhitelist();
+ controller.setAppOpMode(AppOpsManager.OPSTR_START_FOREGROUND, "allow");
+
+ // Start the activity, which will start the fg service as well, and wait
+ // for the report that it's all up and running.
+ WaitForBroadcast waiter = new WaitForBroadcast(mInstrumentation.getTargetContext());
+ waiter.prepare(ACTION_SIMPLE_ACTIVITY_START_FG_SERVICE_RESULT);
+
+ activityIntent.setAction(ACTION_SIMPLE_ACTIVITY_START_FG);
+ mContext.startActivity(activityIntent);
+ activityStarted = true;
+
+ Intent resultIntent = waiter.doWait(WAIT_TIME);
+ int brCode = resultIntent.getIntExtra("result", Activity.RESULT_CANCELED);
+ if (brCode != Activity.RESULT_FIRST_USER) {
+ fail("Failed starting service, result=" + brCode);
+ }
+
+ // activity is in front, fg service is running. make sure that we see
+ // the expected state at this point.
+ uidWatcher.waitFor(WatchUidRunner.CMD_ACTIVE, null);
+ uidWatcher.waitFor(WatchUidRunner.CMD_UNCACHED, null);
+ uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_TOP);
+
+ // Switch to the home app; make sure the test app drops all the way
+ // down to SERVICE, not FG_SERVICE
+ mContext.startActivity(homeIntent);
+ uidWatcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_SERVICE);
+ } finally {
+ // tear down everything and we're done
+ if (activityStarted) {
+ activityIntent.setAction(ACTION_FINISH_EVERYTHING);
+ mContext.startActivity(activityIntent);
+ }
+
+ controller.cleanup();
+ }
+
+ }
+
private boolean supportsCantSaveState() {
if (mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_CANT_SAVE_STATE)) {
@@ -1182,6 +1268,10 @@
appInfo.uid, ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE-1,
WAIT_TIME);
uidBackgroundListener.register();
+ UidImportanceListener uidCachedListener = new UidImportanceListener(mContext,
+ appInfo.uid, ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE + 1,
+ WAIT_TIME);
+ uidCachedListener.register();
WatchUidRunner uidWatcher = new WatchUidRunner(getInstrumentation(), appInfo.uid,
WAIT_TIME);
@@ -1250,14 +1340,14 @@
AccessibilityService.GLOBAL_ACTION_BACK);
// Wait for process to become cached
- uidBackgroundListener.waitForValue(
+ uidCachedListener.waitForValue(
IMPORTANCE_CACHED,
IMPORTANCE_CACHED);
assertEquals(IMPORTANCE_CACHED,
am.getPackageImportance(CANT_SAVE_STATE_1_PACKAGE_NAME));
uidWatcher.expect(WatchUidRunner.CMD_CACHED, null);
- uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_RECENT);
+ uidWatcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_RECENT);
// While in background, should go in to normal idle state.
// Force app to go idle now
@@ -1269,6 +1359,7 @@
uidWatcher.finish();
uidForegroundListener.unregister();
uidBackgroundListener.unregister();
+ uidCachedListener.unregister();
}
}
@@ -1413,7 +1504,7 @@
getInstrumentation().getUiAutomation().performGlobalAction(
AccessibilityService.GLOBAL_ACTION_BACK);
uid1Watcher.expect(WatchUidRunner.CMD_CACHED, null);
- uid1Watcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_RECENT);
+ uid1Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_RECENT);
// Make both apps idle for cleanliness.
cmd = "am make-uid-idle " + CANT_SAVE_STATE_1_PACKAGE_NAME;
@@ -1477,9 +1568,9 @@
// Clean up: unbind services to avoid from interferences with other tests
CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
- PACKAGE_NAME_APP3, PACKAGE_NAME_APP1, 0, null);
+ PACKAGE_NAME_APP3, PACKAGE_NAME_APP1, 0, null);
CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
- PACKAGE_NAME_APP1, PACKAGE_NAME_APP3, 0, null);
+ PACKAGE_NAME_APP1, PACKAGE_NAME_APP3, 0, null);
} finally {
uid1Watcher.finish();
uid3Watcher.finish();
@@ -1558,12 +1649,12 @@
// Clean up: unbind services to avoid from interferences with other tests
CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
- PACKAGE_NAME_APP2, PACKAGE_NAME_APP3, 0, null);
+ PACKAGE_NAME_APP2, PACKAGE_NAME_APP3, 0, null);
CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
- PACKAGE_NAME_APP3, PACKAGE_NAME_APP1, 0, null);
+ PACKAGE_NAME_APP3, PACKAGE_NAME_APP1, 0, null);
// Stop the foreground service
CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_STOP_FOREGROUND_SERVICE,
- PACKAGE_NAME_APP1, PACKAGE_NAME_APP1, 0, null);
+ PACKAGE_NAME_APP1, PACKAGE_NAME_APP1, 0, null);
} finally {
uid1Watcher.finish();
uid2Watcher.finish();
@@ -1634,15 +1725,15 @@
// Clean up: unbind services to avoid from interferences with other tests
CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
- PACKAGE_NAME_APP1, PACKAGE_NAME_APP2, 0, null);
+ PACKAGE_NAME_APP1, PACKAGE_NAME_APP2, 0, null);
CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
- PACKAGE_NAME_APP1, PACKAGE_NAME_APP3, 0, null);
+ PACKAGE_NAME_APP1, PACKAGE_NAME_APP3, 0, null);
CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
- PACKAGE_NAME_APP2, PACKAGE_NAME_APP3, 0, null);
+ PACKAGE_NAME_APP2, PACKAGE_NAME_APP3, 0, null);
CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
- PACKAGE_NAME_APP3, PACKAGE_NAME_APP2, 0, null);
+ PACKAGE_NAME_APP3, PACKAGE_NAME_APP2, 0, null);
CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
- PACKAGE_NAME_APP3, PACKAGE_NAME_APP1, 0, null);
+ PACKAGE_NAME_APP3, PACKAGE_NAME_APP1, 0, null);
} finally {
uid1Watcher.finish();
uid2Watcher.finish();
@@ -1757,9 +1848,9 @@
// Clean up: unbind services to avoid from interferences with other tests
CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
- mAppInfo[0].packageName, mAppInfo[1].packageName, 0, null);
+ mAppInfo[0].packageName, mAppInfo[1].packageName, 0, null);
CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
- mAppInfo[0].packageName, mAppInfo[2].packageName, 0, bundle);
+ mAppInfo[0].packageName, mAppInfo[2].packageName, 0, bundle);
} finally {
shutdownWatchers();
}
@@ -1794,9 +1885,9 @@
// Clean up: unbind services to avoid from interferences with other tests
CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
- STUB_PACKAGE_NAME, mAppInfo[0].packageName, 0, null);
+ STUB_PACKAGE_NAME, mAppInfo[0].packageName, 0, null);
CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
- STUB_PACKAGE_NAME, mAppInfo[1].packageName, 0, bundle);
+ STUB_PACKAGE_NAME, mAppInfo[1].packageName, 0, bundle);
} finally {
shutdownWatchers();
if (activity != null) {
@@ -1921,11 +2012,11 @@
// Clean up: unbind services to avoid from interferences with other tests
CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
- PACKAGE_NAME_APP1, PACKAGE_NAME_APP2, 0, null);
+ PACKAGE_NAME_APP1, PACKAGE_NAME_APP2, 0, null);
CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
- PACKAGE_NAME_APP2, PACKAGE_NAME_APP3, 0, null);
+ PACKAGE_NAME_APP2, PACKAGE_NAME_APP3, 0, null);
CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
- PACKAGE_NAME_APP3, PACKAGE_NAME_APP1, 0, null);
+ PACKAGE_NAME_APP3, PACKAGE_NAME_APP1, 0, null);
} finally {
uid1Listener.unregister();
uid1ServiceListener.unregister();
@@ -1937,4 +2028,114 @@
}
}
}
+
+ public void testCycleFgAppAndAlert() throws Exception {
+ ApplicationInfo stubInfo = mContext.getPackageManager().getApplicationInfo(
+ STUB_PACKAGE_NAME, 0);
+ ApplicationInfo app1Info = mContext.getPackageManager().getApplicationInfo(
+ PACKAGE_NAME_APP1, 0);
+ ApplicationInfo app2Info = mContext.getPackageManager().getApplicationInfo(
+ PACKAGE_NAME_APP2, 0);
+ ApplicationInfo app3Info = mContext.getPackageManager().getApplicationInfo(
+ PACKAGE_NAME_APP3, 0);
+
+ UidImportanceListener stubListener = new UidImportanceListener(mContext,
+ stubInfo.uid, ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE,
+ WAITFOR_MSEC);
+ stubListener.register();
+
+ UidImportanceListener uid1Listener = new UidImportanceListener(mContext,
+ app1Info.uid, IMPORTANCE_VISIBLE,
+ WAITFOR_MSEC);
+ uid1Listener.register();
+
+ UidImportanceListener uid2Listener = new UidImportanceListener(mContext,
+ app2Info.uid, IMPORTANCE_VISIBLE,
+ WAITFOR_MSEC);
+ uid2Listener.register();
+
+ UidImportanceListener uid3Listener = new UidImportanceListener(mContext,
+ app3Info.uid, IMPORTANCE_VISIBLE,
+ WAITFOR_MSEC);
+ uid3Listener.register();
+
+ WatchUidRunner uid1Watcher = new WatchUidRunner(mInstrumentation, app1Info.uid,
+ WAITFOR_MSEC);
+ WatchUidRunner uid2Watcher = new WatchUidRunner(mInstrumentation, app2Info.uid,
+ WAITFOR_MSEC);
+ WatchUidRunner uid3Watcher = new WatchUidRunner(mInstrumentation, app3Info.uid,
+ WAITFOR_MSEC);
+
+ try {
+ // Stub app should have been in foreground since it's being instrumented.
+
+ // Show an alert on app0
+ CommandReceiver.sendCommand(mContext,
+ CommandReceiver.COMMAND_START_ALERT_SERVICE, STUB_PACKAGE_NAME,
+ STUB_PACKAGE_NAME, 0, null);
+
+ // Start a FGS in app2
+ CommandReceiver.sendCommand(mContext,
+ CommandReceiver.COMMAND_START_FOREGROUND_SERVICE, PACKAGE_NAME_APP2,
+ PACKAGE_NAME_APP2, 0, null);
+
+ uid2Listener.waitForValue(IMPORTANCE_FOREGROUND_SERVICE,
+ IMPORTANCE_FOREGROUND_SERVICE);
+
+ // Bind from app0 to a service in app1
+ CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_BIND_SERVICE,
+ STUB_PACKAGE_NAME, PACKAGE_NAME_APP1, 0, null);
+
+ // Bind from app2 to a service in app1
+ CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_BIND_SERVICE,
+ PACKAGE_NAME_APP2, PACKAGE_NAME_APP1, 0, null);
+
+ // Bind from app3 to a service in app1
+ CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_BIND_SERVICE,
+ PACKAGE_NAME_APP3, PACKAGE_NAME_APP1, 0, null);
+
+ // Create a cycle
+ CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_BIND_SERVICE,
+ PACKAGE_NAME_APP1, PACKAGE_NAME_APP3, 0, null);
+
+ uid1Listener.waitForValue(IMPORTANCE_FOREGROUND_SERVICE,
+ IMPORTANCE_FOREGROUND_SERVICE);
+ uid3Listener.waitForValue(IMPORTANCE_FOREGROUND_SERVICE,
+ IMPORTANCE_FOREGROUND_SERVICE);
+
+ CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
+ STUB_PACKAGE_NAME, PACKAGE_NAME_APP1, 0, null);
+
+ CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
+ PACKAGE_NAME_APP2, PACKAGE_NAME_APP1, 0, null);
+
+ CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
+ PACKAGE_NAME_APP3, PACKAGE_NAME_APP1, 0, null);
+
+ CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
+ PACKAGE_NAME_APP1, PACKAGE_NAME_APP3, 0, null);
+
+ // Stop the foreground service
+ CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_STOP_FOREGROUND_SERVICE,
+ PACKAGE_NAME_APP2, PACKAGE_NAME_APP2, 0, null);
+
+ // hide the alert
+ CommandReceiver.sendCommand(mContext,
+ CommandReceiver.COMMAND_STOP_ALERT_SERVICE, STUB_PACKAGE_NAME,
+ STUB_PACKAGE_NAME, 0, null);
+
+ // Check that the apps' proc state has fallen
+ uid1Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY);
+ uid2Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY);
+ uid3Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY);
+ } finally {
+ stubListener.unregister();
+ uid1Listener.unregister();
+ uid2Listener.unregister();
+ uid3Listener.unregister();
+ uid1Watcher.finish();
+ uid2Watcher.finish();
+ uid3Watcher.finish();
+ }
+ }
}
diff --git a/tests/app/src/android/app/cts/BadProviderTest.java b/tests/app/src/android/app/cts/BadProviderTest.java
new file mode 100644
index 0000000..3c16fd3
--- /dev/null
+++ b/tests/app/src/android/app/cts/BadProviderTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.cts;
+
+import android.app.ActivityManager;
+import android.app.cts.android.app.cts.tools.WatchUidRunner;
+import android.content.ContentResolver;
+import android.content.pm.ApplicationInfo;
+import android.net.Uri;
+import android.os.HandlerThread;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.test.AndroidTestCase;
+
+import androidx.test.InstrumentationRegistry;
+
+/**
+ * Test system behavior of a bad provider.
+ */
+public class BadProviderTest extends AndroidTestCase {
+ private static final String AUTHORITY = "com.android.cts.stubbad.badprovider";
+ private static final String TEST_PACKAGE_NAME = "com.android.cts.stubbad";
+ private static final int WAIT_TIME = 2000;
+
+ public void testExitOnCreate() {
+ WatchUidRunner uidWatcher = null;
+ ContentResolver res = mContext.getContentResolver();
+ HandlerThread worker = new HandlerThread("work");
+ worker.start();
+ Handler handler = new Handler(worker.getLooper());
+ try {
+ ApplicationInfo appInfo = mContext.getPackageManager().getApplicationInfo(
+ TEST_PACKAGE_NAME, 0);
+ uidWatcher = new WatchUidRunner(InstrumentationRegistry.getInstrumentation(),
+ appInfo.uid, WAIT_TIME);
+ long startTs = SystemClock.uptimeMillis();
+ handler.post(()->
+ res.query(Uri.parse("content://" + AUTHORITY), null, null, null, null)
+ );
+ // Ensure the system will try at least 3 times for a bad content provider.
+ uidWatcher.waitFor(WatchUidRunner.CMD_GONE, null);
+ uidWatcher.waitFor(WatchUidRunner.CMD_GONE, null);
+ uidWatcher.waitFor(WatchUidRunner.CMD_GONE, null);
+ // Finish the watcher
+ uidWatcher.finish();
+ // Sleep for 10 seconds and initialize the watcher again
+ // (content provider publish timeout is 10 seconds)
+ Thread.sleep(Math.max(0, 10000 - (SystemClock.uptimeMillis() - startTs)));
+ uidWatcher = new WatchUidRunner(InstrumentationRegistry.getInstrumentation(),
+ appInfo.uid, WAIT_TIME);
+ // By now we shouldn't see it's retrying again.
+ try {
+ uidWatcher.waitFor(WatchUidRunner.CMD_GONE, null);
+ fail("Excessive attempts to bring up a provider");
+ } catch (IllegalStateException e) {
+ }
+ } catch (Exception e) {
+ fail("Unexpected exception while query provider: " + e.getMessage());
+ } finally {
+ if (uidWatcher != null) {
+ uidWatcher.finish();
+ }
+ worker.quitSafely();
+ }
+ }
+}
diff --git a/tests/app/src/android/app/cts/NotificationChannelTest.java b/tests/app/src/android/app/cts/NotificationChannelTest.java
index cff8dcb..6f3e1f1 100644
--- a/tests/app/src/android/app/cts/NotificationChannelTest.java
+++ b/tests/app/src/android/app/cts/NotificationChannelTest.java
@@ -18,6 +18,7 @@
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
import android.app.Notification;
import android.app.NotificationChannel;
@@ -60,11 +61,25 @@
assertTrue(channel.getLightColor() == 0);
assertTrue(channel.canBubble());
assertFalse(channel.isImportanceLockedByOEM());
+ assertEquals(IMPORTANCE_UNSPECIFIED, channel.getOriginalImportance());
}
public void testWriteToParcel() {
NotificationChannel channel =
new NotificationChannel("1", "one", IMPORTANCE_DEFAULT);
+ channel.setBypassDnd(true);
+ channel.setOriginalImportance(IMPORTANCE_HIGH);
+ channel.setShowBadge(false);
+ channel.setAllowBubbles(false);
+ channel.setGroup("a thing");
+ channel.setSound(Uri.fromParts("a", "b", "c"),
+ new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION_EVENT)
+ .build());
+ channel.setLightColor(Color.RED);
+ channel.setDeleted(true);
+ channel.setFgServiceShown(true);
+ channel.setVibrationPattern(new long[] {299, 4562});
+ channel.setBlockableSystem(true);
Parcel parcel = Parcel.obtain();
channel.writeToParcel(parcel, 0);
parcel.setDataPosition(0);
@@ -172,4 +187,17 @@
channel.setImportanceLockedByOEM(true);
assertTrue(channel.isImportanceLockedByOEM());
}
+
+ public void testSystemBlockable() {
+ NotificationChannel channel = new NotificationChannel("a", "ab", IMPORTANCE_DEFAULT);
+ assertEquals(false, channel.isBlockableSystem());
+ channel.setBlockableSystem(true);
+ assertEquals(true, channel.isBlockableSystem());
+ }
+
+ public void testOriginalImportance() {
+ NotificationChannel channel = new NotificationChannel("a", "ab", IMPORTANCE_DEFAULT);
+ channel.setOriginalImportance(IMPORTANCE_HIGH);
+ assertEquals(IMPORTANCE_HIGH, channel.getOriginalImportance());
+ }
}
diff --git a/tests/app/src/android/app/cts/NotificationManagerTest.java b/tests/app/src/android/app/cts/NotificationManagerTest.java
index 6e77fea..45725bc 100644
--- a/tests/app/src/android/app/cts/NotificationManagerTest.java
+++ b/tests/app/src/android/app/cts/NotificationManagerTest.java
@@ -18,7 +18,12 @@
import static android.app.Notification.CATEGORY_CALL;
import static android.app.Notification.FLAG_BUBBLE;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL;
+import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
+import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS;
+import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
@@ -68,6 +73,7 @@
import android.graphics.Bitmap;
import android.graphics.drawable.Icon;
import android.media.AudioAttributes;
+import android.media.AudioManager;
import android.media.session.MediaSession;
import android.net.Uri;
import android.os.Build;
@@ -91,9 +97,6 @@
import android.util.Log;
import android.widget.RemoteViews;
-import androidx.test.filters.FlakyTest;
-import androidx.test.InstrumentationRegistry;
-
import com.android.compatibility.common.util.SystemUtil;
import junit.framework.Assert;
@@ -111,6 +114,8 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import androidx.test.InstrumentationRegistry;
+
/* This tests NotificationListenerService together with NotificationManager, as you need to have
* notifications to manipulate in order to test the listener service. */
public class NotificationManagerTest extends AndroidTestCase {
@@ -119,10 +124,12 @@
final String NOTIFICATION_CHANNEL_ID = "NotificationManagerTest";
private static final String DELEGATOR = "com.android.test.notificationdelegator";
+ private static final String DELEGATE_POST_CLASS = DELEGATOR + ".NotificationDelegateAndPost";
private static final String REVOKE_CLASS = DELEGATOR + ".NotificationRevoker";
private static final int WAIT_TIME = 2000;
private PackageManager mPackageManager;
+ private AudioManager mAudioManager;
private NotificationManager mNotificationManager;
private ActivityManager mActivityManager;
private String mId;
@@ -138,12 +145,22 @@
Context.NOTIFICATION_SERVICE);
// clear the deck so that our getActiveNotifications results are predictable
mNotificationManager.cancelAll();
+
+ assertEquals("Previous test left system in a bad state",
+ 0, mNotificationManager.getActiveNotifications().length);
+
mNotificationManager.createNotificationChannel(new NotificationChannel(
NOTIFICATION_CHANNEL_ID, "name", NotificationManager.IMPORTANCE_DEFAULT));
mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
mPackageManager = mContext.getPackageManager();
+ mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
mRuleIds = new ArrayList<>();
+ toggleNotificationPolicyAccess(mContext.getPackageName(),
+ InstrumentationRegistry.getInstrumentation(), true);
+ mNotificationManager.setInterruptionFilter(INTERRUPTION_FILTER_ALL);
+ toggleNotificationPolicyAccess(mContext.getPackageName(),
+ InstrumentationRegistry.getInstrumentation(), false);
// delay between tests so notifications aren't dropped by the rate limiter
try {
Thread.sleep(500);
@@ -184,8 +201,8 @@
private void toggleBubbleSetting(boolean enabled) throws InterruptedException {
SystemUtil.runWithShellPermissionIdentity(() ->
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.NOTIFICATION_BUBBLES, enabled ? 1 : 0));
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.NOTIFICATION_BUBBLES, enabled ? 1 : 0));
Thread.sleep(500); // wait for ranking update
}
@@ -257,14 +274,13 @@
return null;
}
-
- private StatusBarNotification findPostedNotification(int id) {
+ private StatusBarNotification findPostedNotification(int id, boolean all) {
// notification is a bit asynchronous so it may take a few ms to appear in
// getActiveNotifications()
// we will check for it for up to 300ms before giving up
StatusBarNotification n = null;
for (int tries = 3; tries-- > 0; ) {
- final StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications();
+ final StatusBarNotification[] sbns = getActiveNotifications(all);
for (StatusBarNotification sbn : sbns) {
Log.d(TAG, "Found " + sbn.getKey());
if (sbn.getId() == id) {
@@ -282,6 +298,14 @@
return n;
}
+ private StatusBarNotification[] getActiveNotifications(boolean all) {
+ if (all) {
+ return mListener.getActiveNotifications();
+ } else {
+ return mNotificationManager.getActiveNotifications();
+ }
+ }
+
private PendingIntent getPendingIntent() {
return PendingIntent.getActivity(
getContext(), 0, new Intent(getContext(), this.getClass()), 0);
@@ -553,13 +577,17 @@
&& Objects.equals(a.getConfigurationActivity(), b.getConfigurationActivity());
}
- private AutomaticZenRule createRule(String name) {
+ private AutomaticZenRule createRule(String name, int filter) {
return new AutomaticZenRule(name, null,
new ComponentName(mContext, AutomaticZenRuleActivity.class),
new Uri.Builder().scheme("scheme")
.appendPath("path")
.appendQueryParameter("fake_rule", "fake_value")
- .build(), null, NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
+ .build(), null, filter, true);
+ }
+
+ private AutomaticZenRule createRule(String name) {
+ return createRule(name, NotificationManager.INTERRUPTION_FILTER_PRIORITY);
}
private void assertExpectedDndState(int expectedState) {
@@ -1089,8 +1117,8 @@
// turn on bubbles globally
toggleBubbleSetting(true);
- assertEquals(1, Settings.Secure.getInt(
- mContext.getContentResolver(), Settings.Secure.NOTIFICATION_BUBBLES));
+ assertEquals(1, Settings.Global.getInt(
+ mContext.getContentResolver(), Settings.Global.NOTIFICATION_BUBBLES));
toggleListenerAccess(TestNotificationListener.getId(),
InstrumentationRegistry.getInstrumentation(), true);
@@ -1292,7 +1320,7 @@
NotificationListenerService.Ranking outRanking =
new NotificationListenerService.Ranking();
- StatusBarNotification sbn = findPostedNotification(notificationId);
+ StatusBarNotification sbn = findPostedNotification(notificationId, false);
// check that the key and channel ids are the same in the ranking as the posted notification
for (String key : rankingMap.getOrderedKeys()) {
@@ -1467,12 +1495,12 @@
}
}
- public void testMediaStyle_empty() throws Exception {
+ public void testMediaStyle_empty() {
Notification.MediaStyle style = new Notification.MediaStyle();
assertNotNull(style);
}
- public void testMediaStyle() throws Exception {
+ public void testMediaStyle() {
mNotificationManager.cancelAll();
final int id = 99;
MediaSession session = new MediaSession(getContext(), "media");
@@ -1499,7 +1527,7 @@
}
}
- public void testInboxStyle() throws Exception {
+ public void testInboxStyle() {
final int id = 100;
final Notification notification =
@@ -1523,7 +1551,7 @@
}
}
- public void testBigTextStyle() throws Exception {
+ public void testBigTextStyle() {
final int id = 101;
final Notification notification =
@@ -1549,7 +1577,7 @@
}
}
- public void testBigPictureStyle() throws Exception {
+ public void testBigPictureStyle() {
final int id = 102;
final Notification notification =
@@ -1564,10 +1592,10 @@
Icon.createWithResource(getContext(), R.drawable.icon_blue),
"a2", getPendingIntent()).build())
.setStyle(new Notification.BigPictureStyle()
- .setBigContentTitle("title")
- .bigPicture(Bitmap.createBitmap(100, 100, Bitmap.Config.RGB_565))
- .bigLargeIcon(Icon.createWithResource(getContext(), R.drawable.icon_blue))
- .setSummaryText("summary"))
+ .setBigContentTitle("title")
+ .bigPicture(Bitmap.createBitmap(100, 100, Bitmap.Config.RGB_565))
+ .bigLargeIcon(Icon.createWithResource(getContext(), R.drawable.icon_blue))
+ .setSummaryText("summary"))
.build();
mNotificationManager.notify(id, notification);
@@ -1577,77 +1605,77 @@
}
public void testAutogrouping() throws Exception {
- sendNotification(1, R.drawable.black);
- sendNotification(2, R.drawable.blue);
- sendNotification(3, R.drawable.yellow);
- sendNotification(4, R.drawable.yellow);
+ sendNotification(801, R.drawable.black);
+ sendNotification(802, R.drawable.blue);
+ sendNotification(803, R.drawable.yellow);
+ sendNotification(804, R.drawable.yellow);
assertNotificationCount(5);
assertAllPostedNotificationsAutogrouped();
}
public void testAutogrouping_autogroupStaysUntilAllNotificationsCanceled() throws Exception {
- sendNotification(1, R.drawable.black);
- sendNotification(2, R.drawable.blue);
- sendNotification(3, R.drawable.yellow);
- sendNotification(4, R.drawable.yellow);
+ sendNotification(701, R.drawable.black);
+ sendNotification(702, R.drawable.blue);
+ sendNotification(703, R.drawable.yellow);
+ sendNotification(704, R.drawable.yellow);
assertNotificationCount(5);
assertAllPostedNotificationsAutogrouped();
// Assert all notis stay in the same autogroup until all children are canceled
- for (int i = 4; i > 1; i--) {
+ for (int i = 704; i > 701; i--) {
cancelAndPoll(i);
- assertNotificationCount(i);
+ assertNotificationCount(i - 700);
assertAllPostedNotificationsAutogrouped();
}
- cancelAndPoll(1);
+ cancelAndPoll(701);
assertNotificationCount(0);
}
public void testAutogrouping_autogroupStaysUntilAllNotificationsAddedToGroup()
throws Exception {
String newGroup = "new!";
- sendNotification(1, R.drawable.black);
- sendNotification(2, R.drawable.blue);
- sendNotification(3, R.drawable.yellow);
- sendNotification(4, R.drawable.yellow);
+ sendNotification(901, R.drawable.black);
+ sendNotification(902, R.drawable.blue);
+ sendNotification(903, R.drawable.yellow);
+ sendNotification(904, R.drawable.yellow);
List<Integer> postedIds = new ArrayList<>();
- postedIds.add(1);
- postedIds.add(2);
- postedIds.add(3);
- postedIds.add(4);
+ postedIds.add(901);
+ postedIds.add(902);
+ postedIds.add(903);
+ postedIds.add(904);
assertNotificationCount(5);
assertAllPostedNotificationsAutogrouped();
// Assert all notis stay in the same autogroup until all children are canceled
- for (int i = 4; i > 1; i--) {
+ for (int i = 904; i > 901; i--) {
sendNotification(i, newGroup, R.drawable.blue);
postedIds.remove(postedIds.size() - 1);
assertNotificationCount(5);
assertOnlySomeNotificationsAutogrouped(postedIds);
}
- sendNotification(1, newGroup, R.drawable.blue);
+ sendNotification(901, newGroup, R.drawable.blue);
assertNotificationCount(4); // no more autogroup summary
postedIds.remove(0);
assertOnlySomeNotificationsAutogrouped(postedIds);
}
public void testNewNotificationsAddedToAutogroup_ifOriginalNotificationsCanceled()
- throws Exception {
+ throws Exception {
String newGroup = "new!";
- sendNotification(10, R.drawable.black);
- sendNotification(20, R.drawable.blue);
- sendNotification(30, R.drawable.yellow);
- sendNotification(40, R.drawable.yellow);
+ sendNotification(910, R.drawable.black);
+ sendNotification(920, R.drawable.blue);
+ sendNotification(930, R.drawable.yellow);
+ sendNotification(940, R.drawable.yellow);
List<Integer> postedIds = new ArrayList<>();
- postedIds.add(10);
- postedIds.add(20);
- postedIds.add(30);
- postedIds.add(40);
+ postedIds.add(910);
+ postedIds.add(920);
+ postedIds.add(930);
+ postedIds.add(940);
assertNotificationCount(5);
assertAllPostedNotificationsAutogrouped();
@@ -1667,8 +1695,8 @@
// send a new non-grouped notification. since the autogroup summary still exists,
// the notification should be added to it
- sendNotification(50, R.drawable.blue);
- postedIds.add(50);
+ sendNotification(950, R.drawable.blue);
+ postedIds.add(950);
try {
Thread.sleep(200);
} catch (InterruptedException ex) {
@@ -1677,6 +1705,94 @@
assertOnlySomeNotificationsAutogrouped(postedIds);
}
+ public void testTotalSilenceOnlyMuteStreams() throws Exception {
+ if (mActivityManager.isLowRamDevice()) {
+ return;
+ }
+
+ final int originalFilter = mNotificationManager.getCurrentInterruptionFilter();
+ try {
+ toggleNotificationPolicyAccess(mContext.getPackageName(),
+ InstrumentationRegistry.getInstrumentation(), true);
+
+ // ensure volume is not muted/0 to start test
+ mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 1, 0);
+ mAudioManager.setStreamVolume(AudioManager.STREAM_ALARM, 1, 0);
+ mAudioManager.setStreamVolume(AudioManager.STREAM_SYSTEM, 1, 0);
+ mAudioManager.setStreamVolume(AudioManager.STREAM_RING, 1, 0);
+
+ mNotificationManager.setNotificationPolicy(new NotificationManager.Policy(
+ PRIORITY_CATEGORY_ALARMS | PRIORITY_CATEGORY_MEDIA, 0, 0));
+ AutomaticZenRule rule = createRule("test_total_silence",
+ NotificationManager.INTERRUPTION_FILTER_NONE);
+ String id = mNotificationManager.addAutomaticZenRule(rule);
+ mRuleIds.add(id);
+ Condition condition =
+ new Condition(rule.getConditionId(), "summary", Condition.STATE_TRUE);
+ mNotificationManager.setAutomaticZenRuleState(id, condition);
+ mNotificationManager.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY);
+
+ // delay for streams to get into correct mute states
+ Thread.sleep(50);
+ assertTrue("Music (media) stream should be muted",
+ mAudioManager.isStreamMute(AudioManager.STREAM_MUSIC));
+ assertTrue("System stream should be muted",
+ mAudioManager.isStreamMute(AudioManager.STREAM_SYSTEM));
+ assertTrue("Alarm stream should be muted",
+ mAudioManager.isStreamMute(AudioManager.STREAM_ALARM));
+
+ // Test requires that the phone's default state has no channels that can bypass dnd
+ assertTrue("Ringer stream should be muted",
+ mAudioManager.isStreamMute(AudioManager.STREAM_RING));
+ } finally {
+ mNotificationManager.setInterruptionFilter(originalFilter);
+ }
+ }
+
+ public void testAlarmsOnlyMuteStreams() throws Exception {
+ if (mActivityManager.isLowRamDevice()) {
+ return;
+ }
+
+ final int originalFilter = mNotificationManager.getCurrentInterruptionFilter();
+ try {
+ toggleNotificationPolicyAccess(mContext.getPackageName(),
+ InstrumentationRegistry.getInstrumentation(), true);
+
+ // ensure volume is not muted/0 to start test
+ mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 1, 0);
+ mAudioManager.setStreamVolume(AudioManager.STREAM_ALARM, 1, 0);
+ mAudioManager.setStreamVolume(AudioManager.STREAM_SYSTEM, 1, 0);
+ mAudioManager.setStreamVolume(AudioManager.STREAM_RING, 1, 0);
+
+ mNotificationManager.setNotificationPolicy(new NotificationManager.Policy(
+ PRIORITY_CATEGORY_ALARMS | PRIORITY_CATEGORY_MEDIA, 0, 0));
+ AutomaticZenRule rule = createRule("test_alarms",
+ NotificationManager.INTERRUPTION_FILTER_ALARMS);
+ String id = mNotificationManager.addAutomaticZenRule(rule);
+ mRuleIds.add(id);
+ Condition condition =
+ new Condition(rule.getConditionId(), "summary", Condition.STATE_TRUE);
+ mNotificationManager.setAutomaticZenRuleState(id, condition);
+ mNotificationManager.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY);
+
+ // delay for streams to get into correct mute states
+ Thread.sleep(50);
+ assertFalse("Music (media) stream should not be muted",
+ mAudioManager.isStreamMute(AudioManager.STREAM_MUSIC));
+ assertTrue("System stream should be muted",
+ mAudioManager.isStreamMute(AudioManager.STREAM_SYSTEM));
+ assertFalse("Alarm stream should not be muted",
+ mAudioManager.isStreamMute(AudioManager.STREAM_ALARM));
+
+ // Test requires that the phone's default state has no channels that can bypass dnd
+ assertTrue("Ringer stream should be muted",
+ mAudioManager.isStreamMute(AudioManager.STREAM_RING));
+ } finally {
+ mNotificationManager.setInterruptionFilter(originalFilter);
+ }
+ }
+
public void testAddAutomaticZenRule_configActivity() throws Exception {
if (mActivityManager.isLowRamDevice()) {
return;
@@ -1813,7 +1929,6 @@
assertExpectedDndState(INTERRUPTION_FILTER_ALL);
}
- @FlakyTest
public void testSetAutomaticZenRuleState_multipleRules() throws Exception {
if (mActivityManager.isLowRamDevice()) {
return;
@@ -1932,7 +2047,7 @@
.build();
mNotificationManager.notify(id, notification);
- StatusBarNotification n = findPostedNotification(id);
+ StatusBarNotification n = findPostedNotification(id, false);
assertNotNull(n);
assertEquals(notification.fullScreenIntent, n.getNotification().fullScreenIntent);
}
@@ -1986,7 +2101,81 @@
.build();
mNotificationManager.notifyAsPackage(DELEGATOR, "tag", 0, n);
- findPostedNotification(0);
+ assertNotNull(findPostedNotification(0, false));
+
+ final Intent revokeIntent = new Intent();
+ revokeIntent.setClassName(DELEGATOR, REVOKE_CLASS);
+ revokeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(revokeIntent);
+ Thread.sleep(1000);
+ }
+
+ public void testNotificationDelegate_grantAndPostAndCancel() throws Exception {
+ // grant this test permission to post
+ final Intent activityIntent = new Intent();
+ activityIntent.setPackage(DELEGATOR);
+ activityIntent.setAction(Intent.ACTION_MAIN);
+ activityIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+ activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ // wait for the activity to launch and finish
+ mContext.startActivity(activityIntent);
+ Thread.sleep(1000);
+
+ // send notification
+ Notification n = new Notification.Builder(mContext, "channel")
+ .setSmallIcon(android.R.id.icon)
+ .build();
+ mNotificationManager.notifyAsPackage(DELEGATOR, "toBeCanceled", 10000, n);
+
+ assertNotNull(findPostedNotification(10000, false));
+
+ mNotificationManager.cancelAsPackage(DELEGATOR, "toBeCanceled", 10000);
+
+ Thread.sleep(100);
+
+ assertNull(findPostedNotification(10000, false));
+
+ final Intent revokeIntent = new Intent();
+ revokeIntent.setClassName(DELEGATOR, REVOKE_CLASS);
+ revokeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(revokeIntent);
+ Thread.sleep(1000);
+ }
+
+ public void testNotificationDelegate_cannotCancelNotificationsPostedByDelegator()
+ throws Exception {
+ if (mActivityManager.isLowRamDevice() && !mPackageManager.hasSystemFeature(FEATURE_WATCH)) {
+ return;
+ }
+
+ toggleListenerAccess(TestNotificationListener.getId(),
+ InstrumentationRegistry.getInstrumentation(), true);
+ Thread.sleep(500); // wait for listener to be allowed
+
+ mListener = TestNotificationListener.getInstance();
+ assertNotNull(mListener);
+
+ // grant this test permission to post
+ final Intent activityIntent = new Intent();
+ activityIntent.setClassName(DELEGATOR, DELEGATE_POST_CLASS);
+ activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ mContext.startActivity(activityIntent);
+
+ Thread.sleep(1000);
+
+ assertNotNull(findPostedNotification(9, true));
+
+ try {
+ mNotificationManager.cancelAsPackage(DELEGATOR, null, 9);
+ fail ("Delegate should not be able to cancel notification they did not post");
+ } catch (SecurityException e) {
+ // yay
+ }
+
+ // double check that the notification does still exist
+ assertNotNull(findPostedNotification(9, true));
final Intent revokeIntent = new Intent();
revokeIntent.setClassName(DELEGATOR, REVOKE_CLASS);
@@ -2105,7 +2294,7 @@
.build();
mNotificationManager.notify(id, notification);
- StatusBarNotification n = findPostedNotification(id);
+ StatusBarNotification n = findPostedNotification(id, false);
assertNotNull(n);
}
@@ -2192,15 +2381,15 @@
mListener = TestNotificationListener.getInstance();
assertNotNull(mListener);
- final int notificationId1 = 1;
- final int notificationId2 = 2;
+ final int notificationId1 = 1003;
+ final int notificationId2 = 1004;
sendNotification(notificationId1, R.drawable.black);
sendNotification(notificationId2, R.drawable.black);
Thread.sleep(500); // wait for notification listener to receive notification
- StatusBarNotification sbn1 = findPostedNotification(notificationId1);
- StatusBarNotification sbn2 = findPostedNotification(notificationId2);
+ StatusBarNotification sbn1 = findPostedNotification(notificationId1, false);
+ StatusBarNotification sbn2 = findPostedNotification(notificationId2, false);
mListener.setNotificationsShown(new String[]{ sbn1.getKey() });
toggleListenerAccess(TestNotificationListener.getId(),
@@ -2288,15 +2477,15 @@
mListener = TestNotificationListener.getInstance();
assertNotNull(mListener);
- final int notificationId1 = 1;
- final int notificationId2 = 2;
+ final int notificationId1 = 1001;
+ final int notificationId2 = 1002;
sendNotification(notificationId1, R.drawable.black);
sendNotification(notificationId2, R.drawable.black);
Thread.sleep(500); // wait for notification listener to receive notification
- StatusBarNotification sbn1 = findPostedNotification(notificationId1);
- StatusBarNotification sbn2 = findPostedNotification(notificationId2);
+ StatusBarNotification sbn1 = findPostedNotification(notificationId1, false);
+ StatusBarNotification sbn2 = findPostedNotification(notificationId2, false);
StatusBarNotification[] notifs =
mListener.getActiveNotifications(new String[]{ sbn2.getKey(), sbn1.getKey() });
assertEquals(sbn2.getKey(), notifs[0].getKey());
@@ -2338,12 +2527,12 @@
mListener = TestNotificationListener.getInstance();
assertNotNull(mListener);
- final int notificationId = 1;
+ final int notificationId = 1006;
sendNotification(notificationId, R.drawable.black);
Thread.sleep(500); // wait for notification listener to receive notification
- StatusBarNotification sbn = findPostedNotification(notificationId);
+ StatusBarNotification sbn = findPostedNotification(notificationId, false);
mListener.cancelNotification(sbn.getPackageName(), sbn.getTag(), sbn.getId());
if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
@@ -2355,7 +2544,7 @@
// Tested in LegacyNotificationManager20Test
if (checkNotificationExistence(notificationId, /*shouldExist=*/ true)) {
fail("Notification should have been cancelled for targetSdk below L. targetSdk="
- + mContext.getApplicationInfo().targetSdkVersion);
+ + mContext.getApplicationInfo().targetSdkVersion);
}
}
@@ -2376,7 +2565,8 @@
lengthGreaterThanZero);
String badNumberString = NotificationManager.Policy.priorityCategoriesToString(1234567);
- assertNotNull("priorityCategories with a non-relevant int returns a string", oneString);
+ assertNotNull("priorityCategories with a non-relevant int returns a string",
+ badNumberString);
}
public void testNotificationManagerPolicy_prioritySendersToString() {
@@ -2629,7 +2819,7 @@
}
// Should be foreground now
- a.sendBubble(1);
+ a.sendBubble(4000);
boolean shouldBeBubble = !mActivityManager.isLowRamDevice();
@@ -2643,7 +2833,7 @@
homeHelper.goHome();
// The notif should be allowed to update as a bubble
- a.sendBubble(2);
+ a.sendBubble(4001);
if (!checkNotificationExistence(BUBBLE_NOTIF_ID,
true /* shouldExist */, shouldBeBubble)) {
@@ -2654,7 +2844,7 @@
cancelAndPoll(BUBBLE_NOTIF_ID);
// Send it again when not foreground, this should not be a bubble & just be a notif
- a.sendBubble(3);
+ a.sendBubble(4002);
if (!checkNotificationExistence(BUBBLE_NOTIF_ID,
true /* shouldExist */, false /* shouldBeBubble */)) {
fail("couldn't find posted notification with id=" + BUBBLE_NOTIF_ID
@@ -2669,7 +2859,7 @@
}
}
- public void testNotificationManagerBubblePolicy_noFlag_notEmbeddable() throws Exception {
+ public void testNotificationManagerBubblePolicy_noFlag_notEmbeddable() {
Person person = new Person.Builder()
.setName("bubblebot")
.build();
@@ -2705,7 +2895,7 @@
sendAndVerifyBubble(1, nb, metadataBuilder.build(), false);
}
- public void testNotificationManagerBubblePolicy_noFlag_notDocumentLaunchModeAlways() throws Exception {
+ public void testNotificationManagerBubblePolicy_noFlag_notDocumentLaunchModeAlways() {
Person person = new Person.Builder()
.setName("bubblebot")
.build();
@@ -2740,4 +2930,24 @@
sendAndVerifyBubble(1, nb, metadataBuilder.build(), false);
}
+
+ public void testOriginalChannelImportance() {
+ NotificationChannel channel = new NotificationChannel(
+ "my channel", "my channel", IMPORTANCE_HIGH);
+
+ mNotificationManager.createNotificationChannel(channel);
+
+ NotificationChannel actual = mNotificationManager.getNotificationChannel(channel.getId());
+ assertEquals(IMPORTANCE_HIGH, actual.getImportance());
+ assertEquals(IMPORTANCE_HIGH, actual.getOriginalImportance());
+
+ // Apps are allowed to downgrade channel importance if the user has not changed any
+ // fields on this channel yet.
+ channel.setImportance(IMPORTANCE_DEFAULT);
+ mNotificationManager.createNotificationChannel(channel);
+
+ actual = mNotificationManager.getNotificationChannel(channel.getId());
+ assertEquals(IMPORTANCE_DEFAULT, actual.getImportance());
+ assertEquals(IMPORTANCE_HIGH, actual.getOriginalImportance());
+ }
}
diff --git a/tests/app/src/android/app/cts/UserHandleTest.java b/tests/app/src/android/app/cts/UserHandleTest.java
new file mode 100644
index 0000000..342ee36
--- /dev/null
+++ b/tests/app/src/android/app/cts/UserHandleTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.cts;
+
+import android.os.Parcel;
+import android.os.UserHandle;
+import android.test.AndroidTestCase;
+
+public class UserHandleTest extends AndroidTestCase {
+ private static void assertSameUserHandle(int userId) {
+ assertSame(UserHandle.of(userId), UserHandle.of(userId));
+ }
+
+ public void testOf() {
+ for (int i = -1000; i < 100; i++) {
+ assertEquals(i, UserHandle.of(i).getIdentifier());
+ }
+
+ // Ensure common objects are cached.
+ assertSameUserHandle(UserHandle.USER_SYSTEM);
+ assertSameUserHandle(UserHandle.USER_ALL);
+ assertSameUserHandle(UserHandle.USER_NULL);
+ assertSameUserHandle(UserHandle.MIN_SECONDARY_USER_ID);
+ assertSameUserHandle(UserHandle.MIN_SECONDARY_USER_ID + 1);
+ }
+
+ private static void assertParcel(int userId) {
+ Parcel p = Parcel.obtain();
+ p.writeParcelable(UserHandle.of(userId), 0);
+ p.setDataPosition(0);
+
+ UserHandle read = p.readParcelable(UserHandleTest.class.getClassLoader());
+
+ assertEquals(userId, read.getIdentifier());
+
+ p.recycle();
+ }
+
+ public void testParcel() {
+ for (int i = -1000; i < 100; i++) {
+ assertParcel(i);
+ }
+ }
+}
diff --git a/tests/app/src/android/app/cts/WallpaperManagerTest.java b/tests/app/src/android/app/cts/WallpaperManagerTest.java
index fe5f183..7c284b0 100644
--- a/tests/app/src/android/app/cts/WallpaperManagerTest.java
+++ b/tests/app/src/android/app/cts/WallpaperManagerTest.java
@@ -16,11 +16,11 @@
package android.app.cts;
+import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -46,6 +46,9 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import com.android.compatibility.common.util.SystemUtil;
+
+import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -66,15 +69,36 @@
private WallpaperManager mWallpaperManager;
private Context mContext;
private Handler mHandler;
+ private BroadcastReceiver mBroadcastReceiver;
+ private CountDownLatch mCountDownLatch;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mContext = InstrumentationRegistry.getTargetContext();
mWallpaperManager = WallpaperManager.getInstance(mContext);
+ assumeTrue("Device does not support wallpapers", mWallpaperManager.isWallpaperSupported());
final HandlerThread handlerThread = new HandlerThread("TestCallbacks");
handlerThread.start();
mHandler = new Handler(handlerThread.getLooper());
+ mCountDownLatch = new CountDownLatch(1);
+ mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mCountDownLatch.countDown();
+ if (DEBUG) {
+ Log.d(TAG, "broadcast state count down: " + mCountDownLatch.getCount());
+ }
+ }
+ };
+ mContext.registerReceiver(mBroadcastReceiver,
+ new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED));
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mWallpaperManager.clear();
+ mContext.unregisterReceiver(mBroadcastReceiver);
}
@Test
@@ -117,22 +141,12 @@
Canvas canvas = new Canvas(tmpWallpaper);
canvas.drawColor(Color.BLACK);
- CountDownLatch latch = new CountDownLatch(1);
- mContext.registerReceiver(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- latch.countDown();
- }
- }, new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED));
-
try {
mWallpaperManager.setBitmap(tmpWallpaper);
// Wait for up to 5 sec since this is an async call.
// Should fail if Intent.ACTION_WALLPAPER_CHANGED isn't delivered.
- if (!latch.await(5, TimeUnit.SECONDS)) {
- throw new AssertionError("Intent.ACTION_WALLPAPER_CHANGED not received.");
- }
+ Assert.assertTrue(mCountDownLatch.await(5, TimeUnit.SECONDS));
} catch (InterruptedException | IOException e) {
throw new AssertionError("Intent.ACTION_WALLPAPER_CHANGED not received.");
} finally {
@@ -142,22 +156,12 @@
@Test
public void wallpaperClearBroadcastTest() {
- CountDownLatch latch = new CountDownLatch(1);
- mContext.registerReceiver(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- latch.countDown();
- }
- }, new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED));
-
try {
mWallpaperManager.clear(WallpaperManager.FLAG_LOCK | WallpaperManager.FLAG_SYSTEM);
// Wait for 5 sec since this is an async call.
// Should fail if Intent.ACTION_WALLPAPER_CHANGED isn't delivered.
- if (!latch.await(5, TimeUnit.SECONDS)) {
- throw new AssertionError("Intent.ACTION_WALLPAPER_CHANGED not received.");
- }
+ Assert.assertTrue(mCountDownLatch.await(5, TimeUnit.SECONDS));
} catch (InterruptedException | IOException e) {
throw new AssertionError(e);
}
@@ -299,6 +303,63 @@
}
}
+ @Test
+ public void highRatioWallpaper_largeWidth() throws Exception {
+ final String sysuiPid = getSysuiPid();
+ Bitmap highRatioWallpaper = Bitmap.createBitmap(800, 8000, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(highRatioWallpaper);
+ canvas.drawColor(Color.RED);
+
+ try {
+ mWallpaperManager.setBitmap(highRatioWallpaper);
+
+ Assert.assertTrue(mCountDownLatch.await(5, TimeUnit.SECONDS));
+ Assert.assertTrue(sysuiPid.contentEquals(getSysuiPid()));
+ } finally {
+ highRatioWallpaper.recycle();
+ }
+ }
+
+ @Test
+ public void highRatioWallpaper_largeHeight() throws Exception {
+ final String sysuiPid = getSysuiPid();
+ Bitmap highRatioWallpaper = Bitmap.createBitmap(8000, 800, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(highRatioWallpaper);
+ canvas.drawColor(Color.RED);
+
+ try {
+ mWallpaperManager.setBitmap(highRatioWallpaper);
+
+ Assert.assertTrue(mCountDownLatch.await(5, TimeUnit.SECONDS));
+ Assert.assertTrue(sysuiPid.contentEquals(getSysuiPid()));
+ } finally {
+ highRatioWallpaper.recycle();
+ }
+ }
+
+ @Test
+ public void highResolutionWallpaper() throws Exception {
+ final String sysuiPid = getSysuiPid();
+ Bitmap highResolutionWallpaper = Bitmap.createBitmap(10000, 10000, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(highResolutionWallpaper);
+ canvas.drawColor(Color.BLUE);
+
+ try {
+ mWallpaperManager.setBitmap(highResolutionWallpaper);
+
+ Assert.assertTrue(mCountDownLatch.await(5, TimeUnit.SECONDS));
+ Assert.assertTrue(sysuiPid.contentEquals(getSysuiPid()));
+ } finally {
+ highResolutionWallpaper.recycle();
+ }
+ }
+
+ private static String getSysuiPid() {
+ final String sysuiPkgName = "com.android.systemui";
+ final String sysuiPid = "pidof " + sysuiPkgName;
+ return SystemUtil.runShellCommand(sysuiPid);
+ }
+
private void assertDesiredDimension(Point suggestedSize, Point expectedSize) {
mWallpaperManager.suggestDesiredDimensions(suggestedSize.x, suggestedSize.y);
Point actualSize = new Point(mWallpaperManager.getDesiredMinimumWidth(),
@@ -422,31 +483,22 @@
// • System colors are known
// • Lock colors are known
final int expectedEvents = 5;
- CountDownLatch latch = new CountDownLatch(expectedEvents);
+ mCountDownLatch = new CountDownLatch(expectedEvents);
if (DEBUG) {
- Log.d("WP", "Started latch expecting: " + latch.getCount());
+ Log.d(TAG, "Started latch expecting: " + mCountDownLatch.getCount());
}
- BroadcastReceiver receiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- latch.countDown();
- if (DEBUG) {
- Log.d("WP", "broadcast state count down: " + latch.getCount());
- }
- }
- };
+
WallpaperManager.OnColorsChangedListener callback = (colors, which) -> {
if ((which & WallpaperManager.FLAG_LOCK) != 0) {
- latch.countDown();
+ mCountDownLatch.countDown();
}
if ((which & WallpaperManager.FLAG_SYSTEM) != 0) {
- latch.countDown();
+ mCountDownLatch.countDown();
}
if (DEBUG) {
- Log.d("WP", "color state count down: " + which + " - " + colors);
+ Log.d(TAG, "color state count down: " + which + " - " + colors);
}
};
- mContext.registerReceiver(receiver, new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED));
mWallpaperManager.addOnColorsChangedListener(callback, mHandler);
try {
@@ -454,14 +506,11 @@
// Wait for up to 10 sec since this is an async call.
// Will pass as soon as the expected callbacks are executed.
- latch.await(10, TimeUnit.SECONDS);
- if (latch.getCount() != 0) {
- Log.w(TAG, "Did not receive all events! This is probably a bug.");
- }
+ Assert.assertTrue(mCountDownLatch.await(10, TimeUnit.SECONDS));
+ Assert.assertEquals(0, mCountDownLatch.getCount());
} catch (InterruptedException | IOException e) {
throw new RuntimeException("Can't ensure a clean state.");
} finally {
- mContext.unregisterReceiver(receiver);
mWallpaperManager.removeOnColorsChangedListener(callback);
bmp.recycle();
}
@@ -477,4 +526,4 @@
public void onColorsChanged(WallpaperColors colors, int which) {
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/app/src/android/app/cts/android/app/cts/tools/ServiceProcessController.java b/tests/app/src/android/app/cts/android/app/cts/tools/ServiceProcessController.java
index c372a38..01235ae 100644
--- a/tests/app/src/android/app/cts/android/app/cts/tools/ServiceProcessController.java
+++ b/tests/app/src/android/app/cts/android/app/cts/tools/ServiceProcessController.java
@@ -118,6 +118,17 @@
String result = SystemUtil.runShellCommand(mInstrumentation, cmd);
}
+ /** The "battery restriction" forced app standby app-op */
+ public void denyAnyInBackgroundOp() throws IOException {
+ String cmd = "appops set " + mServicePackage + " RUN_ANY_IN_BACKGROUND deny";
+ String result = SystemUtil.runShellCommand(mInstrumentation, cmd);
+ }
+
+ public void allowAnyInBackgroundOp() throws IOException {
+ String cmd = "appops set " + mServicePackage + " RUN_ANY_IN_BACKGROUND allow";
+ String result = SystemUtil.runShellCommand(mInstrumentation, cmd);
+ }
+
public void makeUidIdle() throws IOException {
String cmd = "am make-uid-idle " + mServicePackage;
String result = SystemUtil.runShellCommand(mInstrumentation, cmd);
@@ -151,6 +162,7 @@
public void cleanup() throws IOException {
removeFromWhitelist();
allowBackgroundOp();
+ allowAnyInBackgroundOp();
mUidWatcher.finish();
mUidGoneListener.unregister();
mUidForegroundListener.unregister();
diff --git a/tests/app/src/android/app/cts/android/app/cts/tools/WatchUidRunner.java b/tests/app/src/android/app/cts/android/app/cts/tools/WatchUidRunner.java
index f3e91a1..e6e844f 100644
--- a/tests/app/src/android/app/cts/android/app/cts/tools/WatchUidRunner.java
+++ b/tests/app/src/android/app/cts/android/app/cts/tools/WatchUidRunner.java
@@ -211,6 +211,7 @@
if (res[0].startsWith("#")) {
Log.d(TAG, "Note: " + res[0]);
} else {
+ Log.v(TAG, "LINE: " + Arrays.toString(res));
return res;
}
}
diff --git a/tests/aslr/OWNERS b/tests/aslr/OWNERS
new file mode 100644
index 0000000..94522e3
--- /dev/null
+++ b/tests/aslr/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 36824
+include /tests/tests/security/OWNERS
diff --git a/tests/attentionservice/OWNERS b/tests/attentionservice/OWNERS
new file mode 100644
index 0000000..dcfd7dd
--- /dev/null
+++ b/tests/attentionservice/OWNERS
@@ -0,0 +1,5 @@
+# Bug component: 557553
+aarli@google.com
+asalo@google.com
+eejiang@google.com
+payamp@google.com
diff --git a/tests/autofillservice/OWNERS b/tests/autofillservice/OWNERS
index eac063b..18f17f3 100644
--- a/tests/autofillservice/OWNERS
+++ b/tests/autofillservice/OWNERS
@@ -1,4 +1,4 @@
# Bug component: 351486
-felipeal@google.com
+adamhe@google.com
svetoslavganov@google.com
-adamhe@google.com
\ No newline at end of file
+felipeal@google.com
diff --git a/tests/autofillservice/res/layout/login_with_strings_activity.xml b/tests/autofillservice/res/layout/login_with_strings_activity.xml
index 972f53f..0837129 100644
--- a/tests/autofillservice/res/layout/login_with_strings_activity.xml
+++ b/tests/autofillservice/res/layout/login_with_strings_activity.xml
@@ -38,7 +38,8 @@
<EditText
android:id="@+id/username"
android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ android:layout_height="wrap_content"
+ android:hint="@string/username_hint" />
</LinearLayout>
<LinearLayout
@@ -56,7 +57,8 @@
android:id="@+id/password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:inputType="textPassword"/>
+ android:inputType="textPassword"
+ android:hint="@string/password_hint" />
</LinearLayout>
<LinearLayout
diff --git a/tests/autofillservice/res/values/strings.xml b/tests/autofillservice/res/values/strings.xml
index 785c7a5..54ce11f 100644
--- a/tests/autofillservice/res/values/strings.xml
+++ b/tests/autofillservice/res/values/strings.xml
@@ -29,4 +29,7 @@
<string name="username_string">Username</string>
<string name="password_string">Password</string>
+ <string name="username_hint">Hint for username</string>
+ <string name="password_hint">Hint for password</string>
+
</resources>
\ No newline at end of file
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AutofillValueTest.java b/tests/autofillservice/src/android/autofillservice/cts/AutofillValueTest.java
index 4aa0858..5446b30 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AutofillValueTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutofillValueTest.java
@@ -303,10 +303,7 @@
startAutoFill(mSpinner);
// Autofill it.
- mUiBot.selectDataset("dataset");
-
- // TODO(b/137856201): Fix race condition in getSelectedItemPosition().
- Thread.sleep(1000);
+ mUiBot.selectDatasetSync("dataset");
if (expectAutoFill) {
// Check the results.
diff --git a/tests/autofillservice/src/android/autofillservice/cts/DatasetFilteringTest.java b/tests/autofillservice/src/android/autofillservice/cts/DatasetFilteringTest.java
index 86a8c7d..10df1cd 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/DatasetFilteringTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/DatasetFilteringTest.java
@@ -25,6 +25,8 @@
import static com.android.cts.mockime.ImeEventStreamTestUtils.expectCommand;
import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent;
+import static org.junit.Assume.assumeTrue;
+
import android.autofillservice.cts.CannedFillResponse.CannedDataset;
import android.content.IntentSender;
import android.os.Process;
@@ -172,12 +174,13 @@
@Test
public void testFilter_usingKeyboard() throws Exception {
+ final MockImeSession mockImeSession = sMockImeSessionRule.getMockImeSession();
+ assumeTrue("MockIME not available", mockImeSession != null);
+
final String aa = "Two A's";
final String ab = "A and B";
final String b = "Only B";
- final MockImeSession mockImeSession = sMockImeSessionRule.getMockImeSession();
-
enableService();
// Set expectations.
diff --git a/tests/autofillservice/src/android/autofillservice/cts/FieldsClassificationTest.java b/tests/autofillservice/src/android/autofillservice/cts/FieldsClassificationTest.java
index ff25596..99debab 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/FieldsClassificationTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/FieldsClassificationTest.java
@@ -28,6 +28,7 @@
import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE;
import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MAX_VALUE_LENGTH;
import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MIN_VALUE_LENGTH;
+import static android.service.autofill.AutofillFieldClassificationService.REQUIRED_ALGORITHM_CREDIT_CARD;
import static android.service.autofill.AutofillFieldClassificationService.REQUIRED_ALGORITHM_EDIT_DISTANCE;
import static android.service.autofill.AutofillFieldClassificationService.REQUIRED_ALGORITHM_EXACT_MATCH;
@@ -80,12 +81,17 @@
new SettingsStateChangerRule(sContext, AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT, "42");
private AutofillManager mAfm;
- private Bundle mLast4Bundle = new Bundle();
+ private final Bundle mLast4Bundle = new Bundle();
+ private final Bundle mCreditCardBundle = new Bundle();
@Override
protected void postActivityLaunched() {
mAfm = mActivity.getAutofillManager();
- mLast4Bundle.putInt("suffix", 4);
+ mLast4Bundle.putInt("MATCH_SUFFIX", 4);
+
+ mCreditCardBundle.putInt("REQUIRED_ARG_MIN_CC_LENGTH", 13);
+ mCreditCardBundle.putInt("REQUIRED_ARG_MAX_CC_LENGTH", 19);
+ mCreditCardBundle.putInt("OPTIONAL_ARG_SUFFIX_LENGTH", 4);
}
@Test
@@ -140,6 +146,7 @@
assertThat(availableAlgorithms).isNotNull();
assertThat(availableAlgorithms.contains(REQUIRED_ALGORITHM_EDIT_DISTANCE)).isTrue();
assertThat(availableAlgorithms.contains(REQUIRED_ALGORITHM_EXACT_MATCH)).isTrue();
+ assertThat(availableAlgorithms.contains(REQUIRED_ALGORITHM_CREDIT_CARD)).isTrue();
}
@Test
@@ -243,6 +250,43 @@
}
@Test
+ public void testHit_CreditCardAlgorithm() throws Exception {
+ enableService();
+
+ // Set expectations.
+ mAfm.setUserData(new UserData
+ .Builder("id", "1122334455667788", "card")
+ .setFieldClassificationAlgorithmForCategory("card",
+ REQUIRED_ALGORITHM_CREDIT_CARD, mCreditCardBundle)
+ .build());
+ final MyAutofillCallback callback = mActivity.registerCallback();
+ final EditText field = mActivity.getCell(1, 1);
+ final AtomicReference<AutofillId> fieldId = new AtomicReference<>();
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .setFieldClassificationIds(ID_L1C1)
+ .setVisitor((contexts, builder) -> fieldId
+ .set(findAutofillIdByResourceId(contexts.get(0), ID_L1C1)))
+ .build());
+
+ // Trigger autofill
+ mActivity.focusCell(1, 1);
+ sReplier.getNextFillRequest();
+
+ mUiBot.assertNoDatasetsEver();
+ callback.assertUiUnavailableEvent(field);
+
+ // Simulate user input
+ mActivity.setText(1, 1, "7788");
+
+ // Finish context.
+ mAfm.commit();
+
+ // Assert results
+ final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
+ assertFillEventForFieldsClassification(events.get(0), fieldId.get(), "card", 1);
+ }
+
+ @Test
public void testHit_useDefaultAlgorithm() throws Exception {
enableService();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/FillEventHistoryTest.java b/tests/autofillservice/src/android/autofillservice/cts/FillEventHistoryTest.java
index 6e0914d..238ab45 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/FillEventHistoryTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/FillEventHistoryTest.java
@@ -26,6 +26,7 @@
import static android.autofillservice.cts.Helper.assertFillEventForAuthenticationSelected;
import static android.autofillservice.cts.Helper.assertFillEventForDatasetAuthenticationSelected;
import static android.autofillservice.cts.Helper.assertFillEventForDatasetSelected;
+import static android.autofillservice.cts.Helper.assertFillEventForDatasetShown;
import static android.autofillservice.cts.Helper.assertFillEventForSaveShown;
import static android.autofillservice.cts.Helper.assertNoDeprecatedClientState;
import static android.autofillservice.cts.Helper.findAutofillIdByResourceId;
@@ -104,8 +105,9 @@
mActivity.assertAutoFilled();
// Verify fill selection
- final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
- assertFillEventForDatasetAuthenticationSelected(events.get(0), "name",
+ final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
+ assertFillEventForDatasetShown(events.get(0), "clientStateKey", "clientStateValue");
+ assertFillEventForDatasetAuthenticationSelected(events.get(1), "name",
"clientStateKey", "clientStateValue");
}
@@ -141,11 +143,13 @@
mUiBot.assertDatasets("dataset");
// Verify fill selection
- final FillEventHistory selection = InstrumentedAutoFillService.getFillEventHistory(1);
+ final FillEventHistory selection = InstrumentedAutoFillService.getFillEventHistory(3);
assertDeprecatedClientState(selection, "clientStateKey", "clientStateValue");
List<Event> events = selection.getEvents();
- assertFillEventForAuthenticationSelected(events.get(0), NULL_DATASET_ID,
+ assertFillEventForDatasetShown(events.get(0), "clientStateKey", "clientStateValue");
+ assertFillEventForAuthenticationSelected(events.get(1), NULL_DATASET_ID,
"clientStateKey", "clientStateValue");
+ assertFillEventForDatasetShown(events.get(2), "clientStateKey", "clientStateValue");
}
@Test
@@ -174,10 +178,11 @@
{
// Verify fill selection
- final FillEventHistory selection = InstrumentedAutoFillService.getFillEventHistory(1);
+ final FillEventHistory selection = InstrumentedAutoFillService.getFillEventHistory(2);
assertDeprecatedClientState(selection, "clientStateKey", "Value1");
final List<Event> events = selection.getEvents();
- assertFillEventForDatasetSelected(events.get(0), NULL_DATASET_ID,
+ assertFillEventForDatasetShown(events.get(0), "clientStateKey", "Value1");
+ assertFillEventForDatasetSelected(events.get(1), NULL_DATASET_ID,
"clientStateKey", "Value1");
}
@@ -210,10 +215,11 @@
{
// Verify fill selection
- final FillEventHistory selection = InstrumentedAutoFillService.getFillEventHistory(1);
+ final FillEventHistory selection = InstrumentedAutoFillService.getFillEventHistory(2);
assertDeprecatedClientState(selection, "clientStateKey", "Value2");
final List<Event> events = selection.getEvents();
- assertFillEventForDatasetSelected(events.get(0), "name3",
+ assertFillEventForDatasetShown(events.get(0), "clientStateKey", "Value2");
+ assertFillEventForDatasetSelected(events.get(1), "name3",
"clientStateKey", "Value2");
}
@@ -223,13 +229,15 @@
{
// Verify fill selection
- final FillEventHistory selection = InstrumentedAutoFillService.getFillEventHistory(2);
+ final FillEventHistory selection = InstrumentedAutoFillService.getFillEventHistory(4);
assertDeprecatedClientState(selection, "clientStateKey", "Value2");
final List<Event> events = selection.getEvents();
- assertFillEventForDatasetSelected(events.get(0), "name3",
+ assertFillEventForDatasetShown(events.get(0), "clientStateKey", "Value2");
+ assertFillEventForDatasetSelected(events.get(1), "name3",
"clientStateKey", "Value2");
- assertFillEventForSaveShown(events.get(1), NULL_DATASET_ID,
+ assertFillEventForDatasetShown(events.get(2), "clientStateKey", "Value2");
+ assertFillEventForSaveShown(events.get(3), NULL_DATASET_ID,
"clientStateKey", "Value2");
}
}
@@ -255,10 +263,11 @@
{
// Verify fill selection
- final FillEventHistory selection = InstrumentedAutoFillService.getFillEventHistory(1);
+ final FillEventHistory selection = InstrumentedAutoFillService.getFillEventHistory(2);
assertNoDeprecatedClientState(selection);
final List<Event> events = selection.getEvents();
- assertFillEventForDatasetSelected(events.get(0), NULL_DATASET_ID);
+ assertFillEventForDatasetShown(events.get(0));
+ assertFillEventForDatasetSelected(events.get(1), NULL_DATASET_ID);
}
// Second request
@@ -292,10 +301,11 @@
{
// Verify fill selection
- final FillEventHistory selection = InstrumentedAutoFillService.getFillEventHistory(1);
+ final FillEventHistory selection = InstrumentedAutoFillService.getFillEventHistory(2);
assertNoDeprecatedClientState(selection);
final List<Event> events = selection.getEvents();
- assertFillEventForDatasetSelected(events.get(0), NULL_DATASET_ID);
+ assertFillEventForDatasetShown(events.get(0));
+ assertFillEventForDatasetSelected(events.get(1), NULL_DATASET_ID);
}
// Second request
@@ -329,10 +339,11 @@
{
// Verify fill selection
- final FillEventHistory selection = InstrumentedAutoFillService.getFillEventHistory(1);
+ final FillEventHistory selection = InstrumentedAutoFillService.getFillEventHistory(2);
assertNoDeprecatedClientState(selection);
final List<Event> events = selection.getEvents();
- assertFillEventForDatasetSelected(events.get(0), NULL_DATASET_ID);
+ assertFillEventForDatasetShown(events.get(0));
+ assertFillEventForDatasetSelected(events.get(1), NULL_DATASET_ID);
}
// Second request
@@ -398,8 +409,9 @@
sReplier.getNextFillRequest();
// Verify fill selection for Activity B
- final FillEventHistory selectionB = InstrumentedAutoFillService.getFillEventHistory(0);
+ final FillEventHistory selectionB = InstrumentedAutoFillService.getFillEventHistory(1);
assertDeprecatedClientState(selectionB, "activity", "B");
+ assertFillEventForDatasetShown(selectionB.getEvents().get(0), "activity", "B");
// Now switch back to A...
mUiBot.pressBack(); // dismiss autofill
@@ -424,8 +436,9 @@
sReplier.getNextSaveRequest();
// Finally, make sure history is right
- final FillEventHistory finalSelection = InstrumentedAutoFillService.getFillEventHistory(0);
+ final FillEventHistory finalSelection = InstrumentedAutoFillService.getFillEventHistory(1);
assertDeprecatedClientState(finalSelection, "activity", "B");
+ assertFillEventForDatasetShown(finalSelection.getEvents().get(0), "activity", "B");
}
@Test
@@ -498,11 +511,12 @@
mActivity.assertAutoFilled();
// Verify fill history
{
- final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
- assertFillEventForDatasetSelected(events.get(0), "id1");
+ final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
+ assertFillEventForDatasetShown(events.get(0));
+ assertFillEventForDatasetSelected(events.get(1), "id1");
}
- // Trigger 2st autofill request (which will clear the fill event history)
+ // Trigger 2nd autofill request (which will clear the fill event history)
sReplier.addResponse(new CannedFillResponse.Builder().addDataset(
new CannedDataset.Builder()
.setId("id2")
@@ -518,8 +532,9 @@
mActivity.assertAutoFilled();
// Verify fill history
{
- final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
- assertFillEventForDatasetSelected(events.get(0), "id2");
+ final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
+ assertFillEventForDatasetShown(events.get(0));
+ assertFillEventForDatasetSelected(events.get(1), "id2");
}
// Finish the context by login in
@@ -530,9 +545,9 @@
{
// Verify fill history
- final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
-
- assertFillEventForDatasetSelected(events.get(0), "id2");
+ final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
+ assertFillEventForDatasetShown(events.get(0));
+ assertFillEventForDatasetSelected(events.get(1), "id2");
}
}
@@ -565,8 +580,9 @@
// Verify dataset selection
{
- final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
- assertFillEventForDatasetSelected(events.get(0), NULL_DATASET_ID);
+ final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
+ assertFillEventForDatasetShown(events.get(0));
+ assertFillEventForDatasetSelected(events.get(1), NULL_DATASET_ID);
}
// Finish the context by login in
@@ -580,8 +596,10 @@
// ...and check again
{
- final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
- assertFillEventForDatasetSelected(events.get(0), NULL_DATASET_ID);
+ final List<Event> events = InstrumentedAutoFillService.getFillEvents(3);
+ assertFillEventForDatasetShown(events.get(0));
+ assertFillEventForDatasetSelected(events.get(1), NULL_DATASET_ID);
+ assertFillEventForDatasetShown(events.get(2));
}
}
@@ -616,8 +634,9 @@
// Verify dataset selection
{
- final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
- assertFillEventForDatasetSelected(events.get(0), NULL_DATASET_ID);
+ final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
+ assertFillEventForDatasetShown(events.get(0));
+ assertFillEventForDatasetSelected(events.get(1), NULL_DATASET_ID);
}
// Finish the context by login in
@@ -630,10 +649,11 @@
// ...and check again
{
- final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
- assertFillEventForDatasetSelected(events.get(0), NULL_DATASET_ID);
+ final List<Event> events = InstrumentedAutoFillService.getFillEvents(3);
+ assertFillEventForDatasetShown(events.get(0));
+ assertFillEventForDatasetSelected(events.get(1), NULL_DATASET_ID);
- FillEventHistory.Event event2 = events.get(1);
+ FillEventHistory.Event event2 = events.get(2);
assertThat(event2.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED);
assertThat(event2.getDatasetId()).isNull();
assertThat(event2.getClientState()).isNull();
@@ -678,8 +698,9 @@
// Verify dataset selection
{
- final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
- assertFillEventForDatasetSelected(events.get(0), "id2");
+ final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
+ assertFillEventForDatasetShown(events.get(0));
+ assertFillEventForDatasetSelected(events.get(1), "id2");
}
// Finish the context by login in
@@ -692,10 +713,11 @@
// ...and check again
{
- final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
- assertFillEventForDatasetSelected(events.get(0), "id2");
+ final List<Event> events = InstrumentedAutoFillService.getFillEvents(3);
+ assertFillEventForDatasetShown(events.get(0));
+ assertFillEventForDatasetSelected(events.get(1), "id2");
- final FillEventHistory.Event event2 = events.get(1);
+ final FillEventHistory.Event event2 = events.get(2);
assertThat(event2.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED);
assertThat(event2.getDatasetId()).isNull();
assertThat(event2.getClientState()).isNull();
@@ -738,8 +760,10 @@
mUiBot.assertDatasets("dataset1", "dataset2");
// Verify history
- InstrumentedAutoFillService.getFillEventHistory(0);
-
+ {
+ final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
+ assertFillEventForDatasetShown(events.get(0));
+ }
// Enter values not present at the datasets
mActivity.onUsername((v) -> v.setText("USERNAME"));
mActivity.onPassword((v) -> v.setText("USERNAME"));
@@ -752,8 +776,9 @@
// Verify history again
{
- final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
- final Event event = events.get(0);
+ final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
+ assertFillEventForDatasetShown(events.get(0));
+ final Event event = events.get(1);
assertThat(event.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED);
assertThat(event.getDatasetId()).isNull();
assertThat(event.getClientState()).isNull();
@@ -798,8 +823,9 @@
// Verify dataset selection
{
- final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
- assertFillEventForDatasetSelected(events.get(0), "id1");
+ final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
+ assertFillEventForDatasetShown(events.get(0));
+ assertFillEventForDatasetSelected(events.get(1), "id1");
}
// Finish the context by login in
@@ -813,10 +839,12 @@
// ...and check again
{
- final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
- assertFillEventForDatasetSelected(events.get(0), "id1");
+ final List<Event> events = InstrumentedAutoFillService.getFillEvents(4);
+ assertFillEventForDatasetShown(events.get(0));
+ assertFillEventForDatasetSelected(events.get(1), "id1");
- final FillEventHistory.Event event2 = events.get(1);
+ assertFillEventForDatasetShown(events.get(2));
+ final FillEventHistory.Event event2 = events.get(3);
assertThat(event2.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED);
assertThat(event2.getDatasetId()).isNull();
assertThat(event2.getClientState()).isNull();
@@ -865,8 +893,9 @@
mActivity.assertAutoFilled();
{
// Verify fill history
- final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
- assertFillEventForDatasetSelected(events.get(0), "id1");
+ final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
+ assertFillEventForDatasetShown(events.get(0));
+ assertFillEventForDatasetSelected(events.get(1), "id1");
}
// Autofill password
@@ -878,10 +907,12 @@
{
// Verify fill history
- final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
+ final List<Event> events = InstrumentedAutoFillService.getFillEvents(4);
- assertFillEventForDatasetSelected(events.get(0), "id1");
- assertFillEventForDatasetSelected(events.get(1), "id2");
+ assertFillEventForDatasetShown(events.get(0));
+ assertFillEventForDatasetSelected(events.get(1), "id1");
+ assertFillEventForDatasetShown(events.get(2));
+ assertFillEventForDatasetSelected(events.get(3), "id2");
}
// Finish the context by login in
@@ -894,12 +925,15 @@
{
// Verify fill history
- final List<Event> events = InstrumentedAutoFillService.getFillEvents(3);
+ final List<Event> events = InstrumentedAutoFillService.getFillEvents(6);
- assertFillEventForDatasetSelected(events.get(0), "id1");
- assertFillEventForDatasetSelected(events.get(1), "id2");
+ assertFillEventForDatasetShown(events.get(0));
+ assertFillEventForDatasetSelected(events.get(1), "id1");
+ assertFillEventForDatasetShown(events.get(2));
+ assertFillEventForDatasetSelected(events.get(3), "id2");
- final FillEventHistory.Event event3 = events.get(2);
+ assertFillEventForDatasetShown(events.get(4));
+ final FillEventHistory.Event event3 = events.get(5);
assertThat(event3.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED);
assertThat(event3.getDatasetId()).isNull();
assertThat(event3.getClientState()).isNull();
@@ -945,8 +979,9 @@
mActivity.assertAutoFilled();
{
// Verify fill history
- final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
- assertFillEventForDatasetSelected(events.get(0), "id1");
+ final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
+ assertFillEventForDatasetShown(events.get(0));
+ assertFillEventForDatasetSelected(events.get(1), "id1");
}
// Autofill password
@@ -958,10 +993,12 @@
{
// Verify fill history
- final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
+ final List<Event> events = InstrumentedAutoFillService.getFillEvents(4);
- assertFillEventForDatasetSelected(events.get(0), "id1");
- assertFillEventForDatasetSelected(events.get(1), "id2");
+ assertFillEventForDatasetShown(events.get(0));
+ assertFillEventForDatasetSelected(events.get(1), "id1");
+ assertFillEventForDatasetShown(events.get(2));
+ assertFillEventForDatasetSelected(events.get(3), "id2");
}
// Finish the context by login in
@@ -972,12 +1009,14 @@
{
// Verify fill history
- final List<Event> events = InstrumentedAutoFillService.getFillEvents(3);
+ final List<Event> events = InstrumentedAutoFillService.getFillEvents(5);
- assertFillEventForDatasetSelected(events.get(0), "id1");
- assertFillEventForDatasetSelected(events.get(1), "id2");
+ assertFillEventForDatasetShown(events.get(0));
+ assertFillEventForDatasetSelected(events.get(1), "id1");
+ assertFillEventForDatasetShown(events.get(2));
+ assertFillEventForDatasetSelected(events.get(3), "id2");
- final FillEventHistory.Event event3 = events.get(2);
+ final FillEventHistory.Event event3 = events.get(4);
assertThat(event3.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED);
assertThat(event3.getDatasetId()).isNull();
assertThat(event3.getClientState()).isNull();
@@ -1018,11 +1057,12 @@
// Verify dataset selection
{
- final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
- assertFillEventForDatasetSelected(events.get(0), "id1");
+ final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
+ assertFillEventForDatasetShown(events.get(0));
+ assertFillEventForDatasetSelected(events.get(1), "id1");
}
- // Change the fields to different values from datasets
+ // Change the fields to different values from0 datasets
mActivity.onUsername((v) -> v.setText("USERNAME"));
mActivity.onPassword((v) -> v.setText("USERNAME"));
@@ -1037,17 +1077,19 @@
// ...and check again
{
- final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
- assertFillEventForDatasetSelected(events.get(0), "id1");
+ final List<Event> events = InstrumentedAutoFillService.getFillEvents(4);
+ assertFillEventForDatasetShown(events.get(0));
+ assertFillEventForDatasetSelected(events.get(1), "id1");
+ assertFillEventForDatasetShown(events.get(2));
- FillEventHistory.Event event2 = events.get(1);
- assertThat(event2.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED);
- assertThat(event2.getDatasetId()).isNull();
- assertThat(event2.getClientState()).isNull();
- assertThat(event2.getSelectedDatasetIds()).containsExactly("id1");
- assertThat(event2.getIgnoredDatasetIds()).isEmpty();
- assertThat(event2.getChangedFields()).isEmpty();
- assertThat(event2.getManuallyEnteredField()).isEmpty();
+ FillEventHistory.Event event4 = events.get(3);
+ assertThat(event4.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED);
+ assertThat(event4.getDatasetId()).isNull();
+ assertThat(event4.getClientState()).isNull();
+ assertThat(event4.getSelectedDatasetIds()).containsExactly("id1");
+ assertThat(event4.getIgnoredDatasetIds()).isEmpty();
+ assertThat(event4.getChangedFields()).isEmpty();
+ assertThat(event4.getManuallyEnteredField()).isEmpty();
}
}
@@ -1081,7 +1123,10 @@
mUiBot.assertDatasets("dataset1", "dataset2");
// Verify history
- InstrumentedAutoFillService.getFillEventHistory(0);
+ {
+ final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
+ assertFillEventForDatasetShown(events.get(0));
+ }
// Enter values present at the datasets
mActivity.onUsername((v) -> v.setText(BACKDOOR_USERNAME));
@@ -1095,8 +1140,9 @@
// Verify history
{
- final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
- FillEventHistory.Event event = events.get(0);
+ final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
+ assertFillEventForDatasetShown(events.get(0));
+ FillEventHistory.Event event = events.get(1);
assertThat(event.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED);
assertThat(event.getDatasetId()).isNull();
assertThat(event.getClientState()).isNull();
@@ -1153,7 +1199,10 @@
mUiBot.assertDatasets("dataset1", "dataset2", "dataset3");
// Verify history
- InstrumentedAutoFillService.getFillEventHistory(0);
+ {
+ final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
+ assertFillEventForDatasetShown(events.get(0));
+ }
// Enter values present at the datasets
mActivity.onUsername((v) -> v.setText(BACKDOOR_USERNAME));
@@ -1167,9 +1216,10 @@
// Verify history
{
- final List<Event> events = InstrumentedAutoFillService.getFillEvents(1);
+ final List<Event> events = InstrumentedAutoFillService.getFillEvents(2);
+ assertFillEventForDatasetShown(events.get(0));
- final FillEventHistory.Event event = events.get(0);
+ final FillEventHistory.Event event = events.get(1);
assertThat(event.getType()).isEqualTo(TYPE_CONTEXT_COMMITTED);
assertThat(event.getDatasetId()).isNull();
assertThat(event.getClientState()).isNull();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/Helper.java b/tests/autofillservice/src/android/autofillservice/cts/Helper.java
index 06c0e00..f0bfd39 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/Helper.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/Helper.java
@@ -21,6 +21,7 @@
import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
import static android.service.autofill.FillEventHistory.Event.TYPE_AUTHENTICATION_SELECTED;
import static android.service.autofill.FillEventHistory.Event.TYPE_CONTEXT_COMMITTED;
+import static android.service.autofill.FillEventHistory.Event.TYPE_DATASETS_SHOWN;
import static android.service.autofill.FillEventHistory.Event.TYPE_DATASET_AUTHENTICATION_SELECTED;
import static android.service.autofill.FillEventHistory.Event.TYPE_DATASET_SELECTED;
import static android.service.autofill.FillEventHistory.Event.TYPE_SAVE_SHOWN;
@@ -548,7 +549,7 @@
/**
* Asserts the values of a text-based node whose string come from resoruces.
*/
- public static ViewNode assertTextFromResouces(AssistStructure structure, String resourceId,
+ public static ViewNode assertTextFromResources(AssistStructure structure, String resourceId,
String expectedValue, boolean isAutofillable, String expectedTextIdEntry) {
final ViewNode node = findNodeByResourceId(structure, resourceId);
assertText(node, expectedValue, isAutofillable);
@@ -556,6 +557,14 @@
return node;
}
+ public static ViewNode assertHintFromResources(AssistStructure structure, String resourceId,
+ String expectedValue, String expectedHintIdEntry) {
+ final ViewNode node = findNodeByResourceId(structure, resourceId);
+ assertThat(node.getHint()).isEqualTo(expectedValue);
+ assertThat(node.getHintIdEntry()).isEqualTo(expectedHintIdEntry);
+ return node;
+ }
+
private static void assertText(ViewNode node, String expectedValue, boolean isAutofillable) {
assertWithMessage("wrong text on %s", node.getAutofillId()).that(node.getText().toString())
.isEqualTo(expectedValue);
@@ -1150,7 +1159,7 @@
* @param value the only value expected in the client state bundle
*/
public static void assertFillEventForSaveShown(@NonNull FillEventHistory.Event event,
- @NonNull String datasetId, @NonNull String key, @NonNull String value) {
+ @Nullable String datasetId, @NonNull String key, @NonNull String value) {
assertFillEvent(event, TYPE_SAVE_SHOWN, datasetId, key, value, null);
}
@@ -1162,12 +1171,35 @@
* @param datasetId dataset set id expected in the event
*/
public static void assertFillEventForSaveShown(@NonNull FillEventHistory.Event event,
- @NonNull String datasetId) {
+ @Nullable String datasetId) {
assertFillEvent(event, TYPE_SAVE_SHOWN, datasetId, null, null, null);
}
/**
* Asserts the content of a
+ * {@link android.service.autofill.FillEventHistory.Event#TYPE_DATASETS_SHOWN} event.
+ *
+ * @param event event to be asserted
+ * @param key the only key expected in the client state bundle
+ * @param value the only value expected in the client state bundle
+ */
+ public static void assertFillEventForDatasetShown(@NonNull FillEventHistory.Event event,
+ @NonNull String key, @NonNull String value) {
+ assertFillEvent(event, TYPE_DATASETS_SHOWN, NULL_DATASET_ID, key, value, null);
+ }
+
+ /**
+ * Asserts the content of a
+ * {@link android.service.autofill.FillEventHistory.Event#TYPE_DATASETS_SHOWN} event.
+ *
+ * @param event event to be asserted
+ */
+ public static void assertFillEventForDatasetShown(@NonNull FillEventHistory.Event event) {
+ assertFillEvent(event, TYPE_DATASETS_SHOWN, NULL_DATASET_ID, null, null, null);
+ }
+
+ /**
+ * Asserts the content of a
* {@link android.service.autofill.FillEventHistory.Event#TYPE_DATASET_AUTHENTICATION_SELECTED}
* event.
*
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginActivity.java b/tests/autofillservice/src/android/autofillservice/cts/LoginActivity.java
index efd001f..ace3d6f 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/LoginActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginActivity.java
@@ -140,6 +140,7 @@
final Intent intent = new Intent(this, WelcomeActivity.class);
final String message = getWelcomeMessage(username);
intent.putExtra(WelcomeActivity.EXTRA_MESSAGE, message);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
setLoginMessage(message);
startActivity(intent);
finish();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
index 64dee4d..9636026 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
@@ -519,10 +519,6 @@
@Test
public void testDatasetPickerPosition() throws Exception {
- // TODO(b/75281985): currently disabled because the screenshot contains elements external to
- // the activity that can change (for example, clock), which causes flakiness to the test.
- final boolean compareBitmaps = false;
-
final boolean pickerAndViewBoundsMatches = !isAutofillWindowFullScreen(mContext);
// Set service.
@@ -544,7 +540,7 @@
sReplier.getNextFillRequest();
callback.assertUiShownEvent(username);
final Rect usernamePickerBoundaries1 = mUiBot.assertDatasets("DUDE").getVisibleBounds();
- final Bitmap usernameScreenshot1 = compareBitmaps ? mUiBot.takeScreenshot() : null;
+ final Bitmap usernameScreenshot1 = mUiBot.takeScreenshot(mActivity);
Log.v(TAG,
"Username1 at " + usernameBoundaries1 + "; picker at " + usernamePickerBoundaries1);
// TODO(b/37566627): assertions below might be too aggressive - use range instead?
@@ -558,7 +554,7 @@
callback.assertUiHiddenEvent(username);
callback.assertUiShownEvent(password);
final Rect passwordPickerBoundaries1 = mUiBot.assertDatasets("SWEET").getVisibleBounds();
- final Bitmap passwordScreenshot1 = compareBitmaps ? mUiBot.takeScreenshot() : null;
+ final Bitmap passwordScreenshot1 = mUiBot.takeScreenshot(mActivity);
Log.v(TAG,
"Password1 at " + passwordBoundaries1 + "; picker at " + passwordPickerBoundaries1);
// TODO(b/37566627): assertions below might be too aggressive - use range instead?
@@ -572,7 +568,7 @@
callback.assertUiHiddenEvent(password);
callback.assertUiShownEvent(username);
final Rect usernamePickerBoundaries2 = mUiBot.assertDatasets("DUDE").getVisibleBounds();
- final Bitmap usernameScreenshot2 = compareBitmaps ? mUiBot.takeScreenshot() : null;
+ final Bitmap usernameScreenshot2 = mUiBot.takeScreenshot(mActivity);
Log.v(TAG,
"Username2 at " + usernameBoundaries2 + "; picker at " + usernamePickerBoundaries2);
@@ -581,7 +577,7 @@
callback.assertUiHiddenEvent(username);
callback.assertUiShownEvent(password);
final Rect passwordPickerBoundaries2 = mUiBot.assertDatasets("SWEET").getVisibleBounds();
- final Bitmap passwordScreenshot2 = compareBitmaps ? mUiBot.takeScreenshot() : null;
+ final Bitmap passwordScreenshot2 = mUiBot.takeScreenshot(mActivity);
Log.v(TAG,
"Password2 at " + passwordBoundaries2 + "; picker at " + passwordPickerBoundaries2);
@@ -589,15 +585,12 @@
// ... for username
assertThat(usernameBoundaries2).isEqualTo(usernameBoundaries1);
assertThat(usernamePickerBoundaries2).isEqualTo(usernamePickerBoundaries1);
- if (compareBitmaps) {
- Helper.assertBitmapsAreSame("username", usernameScreenshot1, usernameScreenshot2);
- }
+ Helper.assertBitmapsAreSame("username", usernameScreenshot1, usernameScreenshot2);
+
// ... for password
assertThat(passwordBoundaries2).isEqualTo(passwordBoundaries1);
assertThat(passwordPickerBoundaries2).isEqualTo(passwordPickerBoundaries1);
- if (compareBitmaps) {
- Helper.assertBitmapsAreSame("password", passwordScreenshot1, passwordScreenshot2);
- }
+ Helper.assertBitmapsAreSame("password", passwordScreenshot1, passwordScreenshot2);
// Final sanity check
callback.assertNumberUnhandledEvents(0);
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginWithStringsActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/LoginWithStringsActivityTest.java
index e612161..d702052 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/LoginWithStringsActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginWithStringsActivityTest.java
@@ -20,8 +20,9 @@
import static android.autofillservice.cts.Helper.ID_PASSWORD_LABEL;
import static android.autofillservice.cts.Helper.ID_USERNAME;
import static android.autofillservice.cts.Helper.ID_USERNAME_LABEL;
+import static android.autofillservice.cts.Helper.assertHintFromResources;
import static android.autofillservice.cts.Helper.assertTextAndValue;
-import static android.autofillservice.cts.Helper.assertTextFromResouces;
+import static android.autofillservice.cts.Helper.assertTextFromResources;
import static android.autofillservice.cts.Helper.assertTextIsSanitized;
import static android.autofillservice.cts.Helper.findNodeByResourceId;
import static android.autofillservice.cts.InstrumentedAutoFillService.waitUntilConnected;
@@ -92,11 +93,17 @@
assertTextIsSanitized(fillRequest.structure, ID_PASSWORD);
// Make sure labels were not sanitized
- assertTextFromResouces(fillRequest.structure, ID_USERNAME_LABEL, "Username", false,
+ assertTextFromResources(fillRequest.structure, ID_USERNAME_LABEL, "Username", false,
"username_string");
- assertTextFromResouces(fillRequest.structure, ID_PASSWORD_LABEL, "Password", false,
+ assertTextFromResources(fillRequest.structure, ID_PASSWORD_LABEL, "Password", false,
"password_string");
+ // Check text hints
+ assertHintFromResources(fillRequest.structure, ID_USERNAME, "Hint for username",
+ "username_hint");
+ assertHintFromResources(fillRequest.structure, ID_PASSWORD, "Hint for password",
+ "password_hint");
+
// Auto-fill it.
mUiBot.selectDataset("The Dude");
@@ -130,11 +137,17 @@
assertTextAndValue(password, "dude");
// Make sure labels were not sanitized
- assertTextFromResouces(saveRequest.structure, ID_USERNAME_LABEL, "Username", false,
+ assertTextFromResources(saveRequest.structure, ID_USERNAME_LABEL, "Username", false,
"username_string");
- assertTextFromResouces(saveRequest.structure, ID_PASSWORD_LABEL, "Password", false,
+ assertTextFromResources(saveRequest.structure, ID_PASSWORD_LABEL, "Password", false,
"password_string");
+ // Check text hints
+ assertHintFromResources(fillRequest.structure, ID_USERNAME, "Hint for username",
+ "username_hint");
+ assertHintFromResources(fillRequest.structure, ID_PASSWORD, "Hint for password",
+ "password_hint");
+
// Make sure extras were passed back on onSave()
assertThat(saveRequest.data).isNotNull();
final String extraValue = saveRequest.data.getString("numbers");
diff --git a/tests/autofillservice/src/android/autofillservice/cts/MultiWindowLoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/MultiWindowLoginActivityTest.java
index 681fd1a..e7f26e2 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/MultiWindowLoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/MultiWindowLoginActivityTest.java
@@ -141,12 +141,12 @@
// Make LoginActivity to regain window focus and fill ui is expected to show
tapViewAndExpectWindowEvent(loginActivity.getUsername());
- mUiBot.assertDatasets("The Dude");
+ mUiBot.assertNoDatasetsEver();
assertThat(emptyActivity.hasWindowFocus()).isFalse();
// Tap on EmptyActivity and fill ui is gone.
tapViewAndExpectWindowEvent(emptyActivity.getEmptyView());
- mUiBot.assertNoDatasets();
+ mUiBot.assertNoDatasetsEver();
assertThat(emptyActivity.hasWindowFocus()).isTrue();
// LoginActivity username field is still focused but window has no focus
assertThat(loginActivity.getUsername().hasFocus()).isTrue();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/PreFilledLoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/PreFilledLoginActivityTest.java
index fd392d5..06191a5 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/PreFilledLoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/PreFilledLoginActivityTest.java
@@ -20,7 +20,7 @@
import static android.autofillservice.cts.Helper.ID_USERNAME;
import static android.autofillservice.cts.Helper.ID_USERNAME_LABEL;
import static android.autofillservice.cts.Helper.assertTextAndValue;
-import static android.autofillservice.cts.Helper.assertTextFromResouces;
+import static android.autofillservice.cts.Helper.assertTextFromResources;
import static android.autofillservice.cts.Helper.assertTextIsSanitized;
import static android.autofillservice.cts.Helper.assertTextOnly;
import static android.autofillservice.cts.Helper.findNodeByResourceId;
@@ -75,7 +75,7 @@
assertTextIsSanitized(fillRequest.structure, ID_USERNAME_LABEL);
// ...password label should be ok because it was set from other resource id
- assertTextFromResouces(fillRequest.structure, ID_PASSWORD_LABEL, "DA PASSWORD", false,
+ assertTextFromResources(fillRequest.structure, ID_PASSWORD_LABEL, "DA PASSWORD", false,
"new_password_label");
// ...username and password should be ok because they were set in the SML
@@ -94,7 +94,7 @@
// Assert sanitization on save: everything should be available!
assertTextOnly(findNodeByResourceId(saveRequest.structure, ID_USERNAME_LABEL), "DA USER");
- assertTextFromResouces(saveRequest.structure, ID_PASSWORD_LABEL, "DA PASSWORD", false,
+ assertTextFromResources(saveRequest.structure, ID_PASSWORD_LABEL, "DA PASSWORD", false,
"new_password_label");
assertTextAndValue(findNodeByResourceId(saveRequest.structure, ID_USERNAME), "malkovich");
assertTextAndValue(findNodeByResourceId(saveRequest.structure, ID_PASSWORD), "malkovich");
diff --git a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
index ff884ce..3c24967 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
@@ -36,11 +36,13 @@
import static org.junit.Assume.assumeTrue;
+import android.app.Activity;
import android.app.Instrumentation;
import android.app.UiAutomation;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
+import android.graphics.Rect;
import android.os.SystemClock;
import android.service.autofill.SaveInfo;
import android.support.test.uiautomator.By;
@@ -51,6 +53,8 @@
import android.support.test.uiautomator.Until;
import android.text.Html;
import android.util.Log;
+import android.view.View;
+import android.view.WindowInsets;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityWindowInfo;
@@ -333,7 +337,8 @@
}
/**
- * Selects a dataset that should be visible in the floating UI.
+ * Selects a dataset that should be visible in the floating UI and does not need to wait for
+ * application become idle.
*/
public void selectDataset(String name) throws Exception {
final UiObject2 picker = findDatasetPicker(UI_DATASET_PICKER_TIMEOUT);
@@ -341,6 +346,16 @@
}
/**
+ * Selects a dataset that should be visible in the floating UI and waits for application become
+ * idle if needed.
+ */
+ public void selectDatasetSync(String name) throws Exception {
+ final UiObject2 picker = findDatasetPicker(UI_DATASET_PICKER_TIMEOUT);
+ selectDataset(picker, name);
+ mDevice.waitForIdle();
+ }
+
+ /**
* Selects a dataset that should be visible in the floating UI.
*/
public void selectDataset(UiObject2 picker, String name) {
@@ -817,7 +832,12 @@
private String getString(String id) {
final Resources resources = mContext.getResources();
final int stringId = resources.getIdentifier(id, "string", "android");
- return resources.getString(stringId);
+ try {
+ return resources.getString(stringId);
+ } catch (Resources.NotFoundException e) {
+ throw new IllegalStateException("no internal string for '" + id + "' / res=" + stringId
+ + ": ", e);
+ }
}
/**
@@ -826,7 +846,12 @@
private String getString(String id, Object... formatArgs) {
final Resources resources = mContext.getResources();
final int stringId = resources.getIdentifier(id, "string", "android");
- return resources.getString(stringId, formatArgs);
+ try {
+ return resources.getString(stringId, formatArgs);
+ } catch (Resources.NotFoundException e) {
+ throw new IllegalStateException("no internal string for '" + id + "' / res=" + stringId
+ + ": ", e);
+ }
}
/**
@@ -1014,15 +1039,49 @@
}
}
+ private Rect cropScreenshotWithoutScreenDecoration(Activity activity) {
+ final WindowInsets[] inset = new WindowInsets[1];
+ final View[] rootView = new View[1];
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ rootView[0] = activity.getWindow().getDecorView();
+ inset[0] = rootView[0].getRootWindowInsets();
+ });
+ final int navBarHeight = inset[0].getStableInsetBottom();
+ final int statusBarHeight = inset[0].getStableInsetTop();
+
+ return new Rect(0, statusBarHeight, rootView[0].getWidth(),
+ rootView[0].getHeight() - navBarHeight - statusBarHeight);
+ }
+
// TODO(b/74358143): ideally we should take a screenshot limited by the boundaries of the
// activity window, so external elements (such as the clock) are filtered out and don't cause
// test flakiness when the contents are compared.
public Bitmap takeScreenshot() {
+ return takeScreenshotWithRect(null);
+ }
+
+ public Bitmap takeScreenshot(@NonNull Activity activity) {
+ // crop the screenshot without screen decoration to prevent test flakiness.
+ final Rect rect = cropScreenshotWithoutScreenDecoration(activity);
+ return takeScreenshotWithRect(rect);
+ }
+
+ private Bitmap takeScreenshotWithRect(@Nullable Rect r) {
final long before = SystemClock.elapsedRealtime();
final Bitmap bitmap = mAutoman.takeScreenshot();
final long delta = SystemClock.elapsedRealtime() - before;
Log.v(TAG, "Screenshot taken in " + delta + "ms");
- return bitmap;
+ if (r == null) {
+ return bitmap;
+ }
+ try {
+ return Bitmap.createBitmap(bitmap, r.left, r.top, r.right, r.bottom);
+ } finally {
+ if (bitmap != null) {
+ bitmap.recycle();
+ }
+ }
}
/**
diff --git a/tests/backup/Android.bp b/tests/backup/Android.bp
new file mode 100644
index 0000000..019c864
--- /dev/null
+++ b/tests/backup/Android.bp
@@ -0,0 +1,39 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test {
+ name: "CtsBackupTestCases",
+ defaults: ["cts_defaults"],
+ compile_multilib: "both",
+ libs: [
+ "android.test.runner.stubs",
+ "org.apache.http.legacy",
+ "android.test.base.stubs",
+ ],
+ static_libs: [
+ "compatibility-device-util-axt",
+ "ctstestrunner-axt",
+ "ctstestserver",
+ "mockito-target-minus-junit4",
+ "testng",
+ ],
+ host_required: ["CtsBackupHostTestCases"],
+ srcs: ["src/**/*.java"],
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+ sdk_version: "test_current",
+}
diff --git a/tests/backup/Android.mk b/tests/backup/Android.mk
deleted file mode 100644
index 44fb751..0000000
--- a/tests/backup/Android.mk
+++ /dev/null
@@ -1,48 +0,0 @@
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-# don't include this package in any target
-LOCAL_MODULE_TAGS := optional
-# and when built explicitly put it in the data partition
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-
-LOCAL_JAVA_LIBRARIES := \
- android.test.runner.stubs \
- org.apache.http.legacy \
- android.test.base.stubs \
-
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- compatibility-device-util-axt \
- ctstestrunner-axt \
- ctstestserver \
- mockito-target-minus-junit4 \
- testng
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-
-LOCAL_PACKAGE_NAME := CtsBackupTestCases
-
-LOCAL_SDK_VERSION := test_current
-
-include $(BUILD_CTS_PACKAGE)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/backup/OWNERS b/tests/backup/OWNERS
index 3637e32..e0e5e22 100644
--- a/tests/backup/OWNERS
+++ b/tests/backup/OWNERS
@@ -1,6 +1,8 @@
+# Bug component: 41666
# Use this reviewer by default.
br-framework-team+reviews@google.com
+alsutton@google.com
anniemeng@google.com
brufino@google.com
nathch@google.com
diff --git a/tests/backup/TEST_MAPPING b/tests/backup/TEST_MAPPING
new file mode 100644
index 0000000..4e5beb0
--- /dev/null
+++ b/tests/backup/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsBackupTestCases"
+ }
+ ]
+}
diff --git a/tests/backup/app/Android.bp b/tests/backup/app/Android.bp
new file mode 100644
index 0000000..10e66ed
--- /dev/null
+++ b/tests/backup/app/Android.bp
@@ -0,0 +1,92 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test_helper_app {
+ name: "CtsFullBackupApp",
+ defaults: ["cts_support_defaults"],
+ srcs: [
+ "src/**/*.java",
+ ],
+ static_libs: [
+ "compatibility-device-util-axt",
+ "ctstestrunner-axt",
+ ],
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+ platform_apis: true,
+ manifest: "fullbackup/AndroidManifest.xml"
+}
+
+android_test_helper_app {
+ name: "CtsKeyValueBackupApp",
+ defaults: ["cts_support_defaults"],
+ // Tag this module as a cts test artifact
+ srcs: [
+ "src/**/*.java",
+ ],
+ static_libs: [
+ "compatibility-device-util-axt",
+ "ctstestrunner-axt",
+ ],
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+ platform_apis: true,
+ manifest: "keyvalue/AndroidManifest.xml"
+}
+
+android_test_helper_app {
+ name: "CtsPermissionBackupApp",
+ defaults: ["cts_support_defaults"],
+ // Tag this module as a cts test artifact
+ srcs: [
+ "src/**/*.java",
+ ],
+ static_libs: [
+ "compatibility-device-util-axt",
+ "ctstestrunner-axt",
+ ],
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+ platform_apis: true,
+ manifest: "permission/AndroidManifest.xml"
+}
+
+android_test_helper_app {
+ name: "CtsPermissionBackupApp22",
+ defaults: ["cts_support_defaults"],
+ // Tag this module as a cts test artifact
+ srcs: [
+ "src/**/*.java",
+ ],
+ static_libs: [
+ "compatibility-device-util-axt",
+ "ctstestrunner-axt",
+ ],
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+ platform_apis: true,
+ manifest: "permission22/AndroidManifest.xml"
+}
diff --git a/tests/backup/app/AndroidManifest.xml b/tests/backup/app/AndroidManifest.xml
new file mode 100644
index 0000000..d2a8a75
--- /dev/null
+++ b/tests/backup/app/AndroidManifest.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.backup.app">
+
+</manifest>
+
diff --git a/tests/backup/app/fullbackup/Android.mk b/tests/backup/app/fullbackup/Android.mk
deleted file mode 100644
index b53e38a..0000000
--- a/tests/backup/app/fullbackup/Android.mk
+++ /dev/null
@@ -1,35 +0,0 @@
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-
-LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
-
-LOCAL_PACKAGE_NAME := CtsFullBackupApp
-LOCAL_SDK_VERSION := current
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- compatibility-device-util-axt \
- ctstestrunner-axt
-
-include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/backup/app/keyvalue/Android.mk b/tests/backup/app/keyvalue/Android.mk
deleted file mode 100644
index dc5f2e8..0000000
--- a/tests/backup/app/keyvalue/Android.mk
+++ /dev/null
@@ -1,35 +0,0 @@
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-
-LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
-
-LOCAL_PACKAGE_NAME := CtsKeyValueBackupApp
-LOCAL_SDK_VERSION := current
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- compatibility-device-util-axt \
- ctstestrunner-axt
-
-include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/backup/app/permission/Android.mk b/tests/backup/app/permission/Android.mk
deleted file mode 100644
index f51a44d..0000000
--- a/tests/backup/app/permission/Android.mk
+++ /dev/null
@@ -1,35 +0,0 @@
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-
-LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
-
-LOCAL_PACKAGE_NAME := CtsPermissionBackupApp
-LOCAL_SDK_VERSION := current
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- compatibility-device-util-axt \
- ctstestrunner-axt
-
-include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/backup/app/src/android/backup/app/FullBackupBackupAgent.java b/tests/backup/app/src/android/backup/app/FullBackupBackupAgent.java
index 8535344..0f4a123 100644
--- a/tests/backup/app/src/android/backup/app/FullBackupBackupAgent.java
+++ b/tests/backup/app/src/android/backup/app/FullBackupBackupAgent.java
@@ -53,23 +53,25 @@
@Override
public void onRestoreFile(ParcelFileDescriptor data, long size,
File destination, int type, long mode, long mtime) throws IOException {
- Log.d(MainActivity.TAG, "onRestoreFile " + destination);
super.onRestoreFile(data, size, destination, type, mode, mtime);
+ Log.d(MainActivity.TAG, "onRestoreFile " + destination);
}
@Override
public void onFullBackup(FullBackupDataOutput data) throws IOException {
- Log.d(MainActivity.TAG, "Full backup requested, quota is " + data.getQuota());
super.onFullBackup(data);
+ Log.d(MainActivity.TAG, "Full backup requested, quota is " + data.getQuota());
}
@Override
public void onQuotaExceeded(long backupDataBytes, long quotaBytes) {
+ super.onQuotaExceeded(backupDataBytes, quotaBytes);
Log.d(MainActivity.TAG, "Quota exceeded!");
}
@Override
public void onRestoreFinished() {
+ super.onRestoreFinished();
Log.d(MainActivity.TAG, "onRestoreFinished");
}
diff --git a/tests/backup/app/src/android/backup/app/KeyValueBackupAgent.java b/tests/backup/app/src/android/backup/app/KeyValueBackupAgent.java
index 155d8f9..c73731e 100644
--- a/tests/backup/app/src/android/backup/app/KeyValueBackupAgent.java
+++ b/tests/backup/app/src/android/backup/app/KeyValueBackupAgent.java
@@ -77,11 +77,13 @@
@Override
public void onQuotaExceeded(long backupDataBytes, long quotaBytes) {
+ super.onQuotaExceeded(backupDataBytes, quotaBytes);
Log.d(MainActivity.TAG, "Quota exceeded!");
}
@Override
public void onRestoreFinished() {
+ super.onRestoreFinished();
Log.d(MainActivity.TAG, "onRestoreFinished");
}
diff --git a/tests/camera/Android.mk b/tests/camera/Android.mk
index b605b16..083359a 100644
--- a/tests/camera/Android.mk
+++ b/tests/camera/Android.mk
@@ -28,7 +28,7 @@
LOCAL_MODULE := CtsCameraUtils
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := test_current
-include cts/error_prone_rules_tests.mk
include $(BUILD_STATIC_JAVA_LIBRARY)
@@ -50,7 +50,7 @@
androidx.test.rules
LOCAL_SRC_FILES := \
- src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java \
+ src/android/hardware/camera2/cts/testcases/Camera2AndroidBasicTestCase.java \
src/android/hardware/camera2/cts/PerformanceTest.java \
src/android/hardware/cts/CameraTestCase.java \
src/android/hardware/cts/LegacyCameraPerformanceTest.java
diff --git a/tests/camera/libctscamera2jni/native-camera-jni.cpp b/tests/camera/libctscamera2jni/native-camera-jni.cpp
index b9fc256..fb78a90 100644
--- a/tests/camera/libctscamera2jni/native-camera-jni.cpp
+++ b/tests/camera/libctscamera2jni/native-camera-jni.cpp
@@ -2721,10 +2721,12 @@
goto cleanup;
}
+ StaticInfo staticInfo(chars);
ACameraMetadata_const_entry sessionParamKeys{};
ret = ACameraMetadata_getConstEntry(chars, ACAMERA_REQUEST_AVAILABLE_SESSION_KEYS,
&sessionParamKeys);
- if ((ret != ACAMERA_OK) || (sessionParamKeys.count == 0)) {
+ if ((ret != ACAMERA_OK) || (sessionParamKeys.count == 0) ||
+ !staticInfo.isColorOutputSupported()) {
ACameraMetadata_free(chars);
chars = nullptr;
continue;
@@ -3420,6 +3422,8 @@
goto exit;
}
+ usleep(100000); // sleep to give some time for callbacks to happen
+
if (testCase.isCameraAvailable(cameraId)) {
LOG_ERROR(errorString, "Camera %s should be unavailable now", cameraId);
goto exit;
diff --git a/tests/camera/src/android/hardware/camera2/cts/AllocationTest.java b/tests/camera/src/android/hardware/camera2/cts/AllocationTest.java
index 92b171a..776879d 100644
--- a/tests/camera/src/android/hardware/camera2/cts/AllocationTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/AllocationTest.java
@@ -21,10 +21,13 @@
import static android.hardware.camera2.cts.helpers.AssertHelpers.*;
import static android.hardware.camera2.cts.CameraTestUtils.*;
import static com.android.ex.camera2.blocking.BlockingStateCallback.*;
+import static junit.framework.Assert.*;
import android.content.Context;
import android.graphics.ImageFormat;
import android.graphics.RectF;
+
+import android.hardware.camera2.cts.Camera2ParameterizedTestCase;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
@@ -50,11 +53,12 @@
import android.os.HandlerThread;
import android.renderscript.Allocation;
import android.renderscript.Script.LaunchOptions;
-import android.test.AndroidTestCase;
import android.util.Log;
import android.util.Rational;
import android.view.Surface;
+import androidx.test.InstrumentationRegistry;
+
import com.android.ex.camera2.blocking.BlockingCameraManager.BlockingOpenException;
import com.android.ex.camera2.blocking.BlockingStateCallback;
import com.android.ex.camera2.blocking.BlockingSessionCallback;
@@ -63,6 +67,10 @@
import java.util.Arrays;
import java.util.List;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.Test;
+
/**
* Suite of tests for camera2 -> RenderScript APIs.
*
@@ -71,17 +79,17 @@
*
* <p>YUV_420_888: flexible YUV420, it is a mandatory format for camera.</p>
*/
-public class AllocationTest extends AndroidTestCase {
+
+@RunWith(Parameterized.class)
+public class AllocationTest extends Camera2ParameterizedTestCase {
private static final String TAG = "AllocationTest";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
- private CameraManager mCameraManager;
private CameraDevice mCamera;
private CameraCaptureSession mSession;
private BlockingStateCallback mCameraListener;
private BlockingSessionCallback mSessionListener;
- private String[] mCameraIds;
private Handler mHandler;
private HandlerThread mHandlerThread;
@@ -91,16 +99,8 @@
private ResultIterable mResultIterable;
@Override
- public synchronized void setContext(Context context) {
- super.setContext(context);
- mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
- assertNotNull("Can't connect to camera manager!", mCameraManager);
- }
-
- @Override
- protected void setUp() throws Exception {
+ public void setUp() throws Exception {
super.setUp();
- mCameraIds = mCameraManager.getCameraIdList();
mHandlerThread = new HandlerThread("AllocationTest");
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
@@ -110,11 +110,11 @@
mSizeIterable = new SizeIterable();
mResultIterable = new ResultIterable();
- RenderScriptSingleton.setContext(getContext());
+ RenderScriptSingleton.setContext(mContext);
}
@Override
- protected void tearDown() throws Exception {
+ public void tearDown() throws Exception {
MaybeNull.close(mCamera);
RenderScriptSingleton.clearContext();
mHandlerThread.quitSafely();
@@ -475,6 +475,7 @@
if (VERBOSE) Log.v(TAG, "validating Buffer , size = " + actualSize);
}
+ @Test
public void testAllocationFromCameraFlexibleYuv() throws Exception {
/** number of frame (for streaming requests) to be verified. */
@@ -516,7 +517,7 @@
if (VERBOSE) Log.v(TAG, "Cleanup Renderscript cache");
scriptGraph.close();
RenderScriptSingleton.clearContext();
- RenderScriptSingleton.setContext(getContext());
+ RenderScriptSingleton.setContext(mContext);
}
}
});
@@ -533,6 +534,7 @@
*
* @throws Exception
*/
+ @Test
public void testBlackWhite() throws CameraAccessException {
/** low iso + low exposure (first shot) */
@@ -610,6 +612,7 @@
/**
* Test that the android.sensitivity.parameter is applied.
*/
+ @Test
public void testParamSensitivity() throws CameraAccessException {
final float THRESHOLD_MAX_MIN_DIFF = 0.3f;
final float THRESHOLD_MAX_MIN_RATIO = 2.0f;
@@ -761,33 +764,33 @@
public void forEachCamera(boolean fullHwLevel, CameraBlock runnable)
throws CameraAccessException {
assertNotNull("No camera manager", mCameraManager);
- assertNotNull("No camera IDs", mCameraIds);
+ assertNotNull("No camera IDs", mCameraIdsUnderTest);
- for (int i = 0; i < mCameraIds.length; i++) {
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
// Don't execute the runnable against non-FULL cameras if FULL is required
CameraCharacteristics properties =
- mCameraManager.getCameraCharacteristics(mCameraIds[i]);
+ mCameraManager.getCameraCharacteristics(mCameraIdsUnderTest[i]);
StaticMetadata staticInfo = new StaticMetadata(properties);
if (fullHwLevel && !staticInfo.isHardwareLevelAtLeastFull()) {
Log.i(TAG, String.format(
"Skipping this test for camera %s, needs FULL hw level",
- mCameraIds[i]));
+ mCameraIdsUnderTest[i]));
continue;
}
if (!staticInfo.isColorOutputSupported()) {
Log.i(TAG, String.format(
"Skipping this test for camera %s, does not support regular outputs",
- mCameraIds[i]));
+ mCameraIdsUnderTest[i]));
continue;
}
// Open camera and execute test
- Log.i(TAG, "Testing Camera " + mCameraIds[i]);
+ Log.i(TAG, "Testing Camera " + mCameraIdsUnderTest[i]);
try {
- openDevice(mCameraIds[i]);
+ openDevice(mCameraIdsUnderTest[i]);
runnable.run(mCamera);
} finally {
- closeDevice(mCameraIds[i]);
+ closeDevice(mCameraIdsUnderTest[i]);
}
}
}
diff --git a/tests/camera/src/android/hardware/camera2/cts/BurstCaptureRawTest.java b/tests/camera/src/android/hardware/camera2/cts/BurstCaptureRawTest.java
index 7b23abf..afff41b 100644
--- a/tests/camera/src/android/hardware/camera2/cts/BurstCaptureRawTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/BurstCaptureRawTest.java
@@ -37,11 +37,14 @@
import java.util.ArrayList;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
import org.junit.Test;
/**
* Basic tests for burst capture in RAW formats.
*/
+@RunWith(Parameterized.class)
public class BurstCaptureRawTest extends Camera2SurfaceViewTestCase {
private static final String TAG = "BurstCaptureRawTest";
private static final int RAW_FORMATS[] = {
@@ -71,7 +74,7 @@
@Test
public void testRawSensorSize() throws Exception {
Log.i(TAG, "Begin testRawSensorSize");
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
ArrayList<Integer> supportedRawList = new ArrayList<Integer>(RAW_FORMATS.length);
if (!checkCapability(id, supportedRawList, RAW_FORMATS)) {
@@ -675,7 +678,7 @@
private void performTestRoutine(TestRoutine routine, int[] testedFormats) throws Exception
{
final int PREPARE_TIMEOUT_MS = 10000;
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
ArrayList<Integer> supportedRawList = new ArrayList<Integer>(RAW_FORMATS.length);
if (!checkCapability(id, supportedRawList, testedFormats)) {
diff --git a/tests/camera/src/android/hardware/camera2/cts/BurstCaptureTest.java b/tests/camera/src/android/hardware/camera2/cts/BurstCaptureTest.java
index 03dbdeb..b86c511 100644
--- a/tests/camera/src/android/hardware/camera2/cts/BurstCaptureTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/BurstCaptureTest.java
@@ -34,8 +34,11 @@
import java.util.List;
import java.util.ArrayList;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
import org.junit.Test;
+@RunWith(Parameterized.class)
public class BurstCaptureTest extends Camera2SurfaceViewTestCase {
private static final String TAG = "BurstCaptureTest";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -65,9 +68,9 @@
}
private void testBurst(int fmt, int burstSize, boolean checkFrameRate) throws Exception {
- for (int i = 0; i < mCameraIds.length; i++) {
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
try {
- String id = mCameraIds[i];
+ String id = mCameraIdsUnderTest[i];
StaticMetadata staticInfo = mAllStaticInfo.get(id);
if (!staticInfo.isColorOutputSupported()) {
diff --git a/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java b/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java
index a64cd50..73e3828 100644
--- a/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java
@@ -67,6 +67,9 @@
import java.util.Set;
import android.util.Size;
+import org.junit.runners.Parameterized;
+import org.junit.runner.RunWith;
+import org.junit.Test;
import org.mockito.ArgumentMatcher;
import java.util.concurrent.Executor;
@@ -76,6 +79,8 @@
/**
* <p>Basic test for CameraDevice APIs.</p>
*/
+
+@RunWith(Parameterized.class)
public class CameraDeviceTest extends Camera2AndroidTestCase {
private static final String TAG = "CameraDeviceTest";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -114,16 +119,15 @@
}
@Override
- public void setContext(Context context) {
- super.setContext(context);
-
+ public void setUp() throws Exception {
+ super.setUp();
/**
* Workaround for mockito and JB-MR2 incompatibility
*
* Avoid java.lang.IllegalArgumentException: dexcache == null
* https://code.google.com/p/dexmaker/issues/detail?id=2
*/
- System.setProperty("dexmaker.dexcache", getContext().getCacheDir().toString());
+ System.setProperty("dexmaker.dexcache", mContext.getCacheDir().toString());
/**
* Create error listener in context scope, to catch asynchronous device error.
@@ -131,11 +135,6 @@
* implementation (spy doesn't stub the functions unless we ask it to do so).
*/
mCameraMockListener = spy(new BlockingStateCallback());
- }
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
/**
* Due to the asynchronous nature of camera device error callback, we
* have to make sure device doesn't run into error state before. If so,
@@ -154,7 +153,7 @@
}
@Override
- protected void tearDown() throws Exception {
+ public void tearDown() throws Exception {
super.tearDown();
}
@@ -176,9 +175,10 @@
* settings.</li>
* </ul>
*/
+ @Test
public void testCameraDevicePreviewTemplate() throws Exception {
- for (int i = 0; i < mCameraIds.length; i++) {
- captureTemplateTestByCamera(mCameraIds[i], CameraDevice.TEMPLATE_PREVIEW);
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
+ captureTemplateTestByCamera(mCameraIdsUnderTest[i], CameraDevice.TEMPLATE_PREVIEW);
}
// TODO: test the frame rate sustainability in preview use case test.
@@ -202,9 +202,10 @@
* frame rate for the given settings.</li>
* </ul>
*/
+ @Test
public void testCameraDeviceStillTemplate() throws Exception {
- for (int i = 0; i < mCameraIds.length; i++) {
- captureTemplateTestByCamera(mCameraIds[i], CameraDevice.TEMPLATE_STILL_CAPTURE);
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
+ captureTemplateTestByCamera(mCameraIdsUnderTest[i], CameraDevice.TEMPLATE_STILL_CAPTURE);
}
}
@@ -222,9 +223,10 @@
* <li>Frame rate should be stable, for example, wide fps range like [7, 30]
* is a bad setting.</li>
*/
+ @Test
public void testCameraDeviceRecordingTemplate() throws Exception {
- for (int i = 0; i < mCameraIds.length; i++) {
- captureTemplateTestByCamera(mCameraIds[i], CameraDevice.TEMPLATE_RECORD);
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
+ captureTemplateTestByCamera(mCameraIdsUnderTest[i], CameraDevice.TEMPLATE_RECORD);
}
// TODO: test the frame rate sustainability in recording use case test.
@@ -238,9 +240,10 @@
* as recording, with an additional requirement: the settings should maximize image quality
* without compromising stable frame rate.</p>
*/
+ @Test
public void testCameraDeviceVideoSnapShotTemplate() throws Exception {
- for (int i = 0; i < mCameraIds.length; i++) {
- captureTemplateTestByCamera(mCameraIds[i], CameraDevice.TEMPLATE_VIDEO_SNAPSHOT);
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
+ captureTemplateTestByCamera(mCameraIdsUnderTest[i], CameraDevice.TEMPLATE_VIDEO_SNAPSHOT);
}
// TODO: test the frame rate sustainability in video snapshot use case test.
@@ -253,9 +256,10 @@
* metadata keys, and their values must be set correctly. It has the similar requirement
* as preview, with an additional requirement: </p>
*/
+ @Test
public void testCameraDeviceZSLTemplate() throws Exception {
- for (int i = 0; i < mCameraIds.length; i++) {
- captureTemplateTestByCamera(mCameraIds[i], CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG);
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
+ captureTemplateTestByCamera(mCameraIdsUnderTest[i], CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG);
}
}
@@ -276,16 +280,18 @@
* set to reasonable defaults.</li>
* </ul>
*/
+ @Test
public void testCameraDeviceManualTemplate() throws Exception {
- for (int i = 0; i < mCameraIds.length; i++) {
- captureTemplateTestByCamera(mCameraIds[i], CameraDevice.TEMPLATE_MANUAL);
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
+ captureTemplateTestByCamera(mCameraIdsUnderTest[i], CameraDevice.TEMPLATE_MANUAL);
}
}
+ @Test
public void testCameraDeviceCreateCaptureBuilder() throws Exception {
- for (int i = 0; i < mCameraIds.length; i++) {
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
try {
- openDevice(mCameraIds[i], mCameraMockListener);
+ openDevice(mCameraIdsUnderTest[i], mCameraMockListener);
/**
* Test: that each template type is supported, and that its required fields are
* present.
@@ -331,16 +337,17 @@
try {
closeSession();
} finally {
- closeDevice(mCameraIds[i], mCameraMockListener);
+ closeDevice(mCameraIdsUnderTest[i], mCameraMockListener);
}
}
}
}
+ @Test
public void testCameraDeviceSetErrorListener() throws Exception {
- for (int i = 0; i < mCameraIds.length; i++) {
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
try {
- openDevice(mCameraIds[i], mCameraMockListener);
+ openDevice(mCameraIdsUnderTest[i], mCameraMockListener);
/**
* Test: that the error listener can be set without problems.
* Also, wait some time to check if device doesn't run into error.
@@ -355,27 +362,31 @@
try {
closeSession();
} finally {
- closeDevice(mCameraIds[i], mCameraMockListener);
+ closeDevice(mCameraIdsUnderTest[i], mCameraMockListener);
}
}
}
}
+ @Test
public void testCameraDeviceCapture() throws Exception {
runCaptureTest(/*burst*/false, /*repeating*/false, /*abort*/false, /*useExecutor*/false);
runCaptureTest(/*burst*/false, /*repeating*/false, /*abort*/false, /*useExecutor*/true);
}
+ @Test
public void testCameraDeviceCaptureBurst() throws Exception {
runCaptureTest(/*burst*/true, /*repeating*/false, /*abort*/false, /*useExecutor*/false);
runCaptureTest(/*burst*/true, /*repeating*/false, /*abort*/false, /*useExecutor*/true);
}
+ @Test
public void testCameraDeviceRepeatingRequest() throws Exception {
runCaptureTest(/*burst*/false, /*repeating*/true, /*abort*/false, /*useExecutor*/false);
runCaptureTest(/*burst*/false, /*repeating*/true, /*abort*/false, /*useExecutor*/true);
}
+ @Test
public void testCameraDeviceRepeatingBurst() throws Exception {
runCaptureTest(/*burst*/true, /*repeating*/true, /*abort*/false, /*useExecutor*/ false);
runCaptureTest(/*burst*/true, /*repeating*/true, /*abort*/false, /*useExecutor*/ true);
@@ -389,6 +400,7 @@
* discarding in-progress work. Once the abort is complete, the idle callback will be called.
* </p>
*/
+ @Test
public void testCameraDeviceAbort() throws Exception {
runCaptureTest(/*burst*/false, /*repeating*/true, /*abort*/true, /*useExecutor*/false);
runCaptureTest(/*burst*/false, /*repeating*/true, /*abort*/true, /*useExecutor*/true);
@@ -414,10 +426,11 @@
/**
* Test invalid capture (e.g. null or empty capture request).
*/
+ @Test
public void testInvalidCapture() throws Exception {
- for (int i = 0; i < mCameraIds.length; i++) {
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
try {
- openDevice(mCameraIds[i], mCameraMockListener);
+ openDevice(mCameraIdsUnderTest[i], mCameraMockListener);
waitForDeviceState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
prepareCapture();
@@ -427,7 +440,7 @@
closeSession();
}
finally {
- closeDevice(mCameraIds[i], mCameraMockListener);
+ closeDevice(mCameraIdsUnderTest[i], mCameraMockListener);
}
}
}
@@ -442,6 +455,7 @@
* onCaptureCompleted -> createCaptureRequest, getDevice, abortCaptures,
* capture, setRepeatingRequest, stopRepeating, session+device.close
*/
+ @Test
public void testChainedOperation() throws Throwable {
final ArrayList<Surface> outputs = new ArrayList<>();
@@ -585,13 +599,13 @@
// Actual test code
- for (int i = 0; i < mCameraIds.length; i++) {
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
try {
Throwable result;
- if (!(new StaticMetadata(mCameraManager.getCameraCharacteristics(mCameraIds[i]))).
+ if (!(new StaticMetadata(mCameraManager.getCameraCharacteristics(mCameraIdsUnderTest[i]))).
isColorOutputSupported()) {
- Log.i(TAG, "Camera " + mCameraIds[i] +
+ Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
" does not support color outputs, skipping");
continue;
}
@@ -602,7 +616,7 @@
// Start chained cascade
ChainedCameraListener cameraListener = new ChainedCameraListener();
- mCameraManager.openCamera(mCameraIds[i], cameraListener, mHandler);
+ mCameraManager.openCamera(mCameraIdsUnderTest[i], cameraListener, mHandler);
// Check if open succeeded
result = results.poll(CAMERA_OPEN_TIMEOUT_MS, TimeUnit.MILLISECONDS);
@@ -648,21 +662,22 @@
* Verify basic semantics and error conditions of the prepare call.
*
*/
+ @Test
public void testPrepare() throws Exception {
- for (int i = 0; i < mCameraIds.length; i++) {
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
try {
- if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
- Log.i(TAG, "Camera " + mCameraIds[i] +
+ if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+ Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
" does not support color outputs, skipping");
continue;
}
- openDevice(mCameraIds[i], mCameraMockListener);
+ openDevice(mCameraIdsUnderTest[i], mCameraMockListener);
waitForDeviceState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
prepareTestByCamera();
}
finally {
- closeDevice(mCameraIds[i], mCameraMockListener);
+ closeDevice(mCameraIdsUnderTest[i], mCameraMockListener);
}
}
}
@@ -671,26 +686,27 @@
* Verify prepare call behaves properly when sharing surfaces.
*
*/
+ @Test
public void testPrepareForSharedSurfaces() throws Exception {
- for (int i = 0; i < mCameraIds.length; i++) {
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
try {
- StaticMetadata staticInfo = mAllStaticInfo.get(mCameraIds[i]);
+ StaticMetadata staticInfo = mAllStaticInfo.get(mCameraIdsUnderTest[i]);
if (staticInfo.isHardwareLevelLegacy()) {
- Log.i(TAG, "Camera " + mCameraIds[i] + " is legacy, skipping");
+ Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] + " is legacy, skipping");
continue;
}
if (!staticInfo.isColorOutputSupported()) {
- Log.i(TAG, "Camera " + mCameraIds[i] +
+ Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
" does not support color outputs, skipping");
continue;
}
- openDevice(mCameraIds[i], mCameraMockListener);
+ openDevice(mCameraIdsUnderTest[i], mCameraMockListener);
waitForDeviceState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
prepareTestForSharedSurfacesByCamera();
}
finally {
- closeDevice(mCameraIds[i], mCameraMockListener);
+ closeDevice(mCameraIdsUnderTest[i], mCameraMockListener);
}
}
}
@@ -698,21 +714,22 @@
/**
* Verify creating sessions back to back.
*/
+ @Test
public void testCreateSessions() throws Exception {
- for (int i = 0; i < mCameraIds.length; i++) {
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
try {
- if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
- Log.i(TAG, "Camera " + mCameraIds[i] +
+ if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+ Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
" does not support color outputs, skipping");
continue;
}
- openDevice(mCameraIds[i], mCameraMockListener);
+ openDevice(mCameraIdsUnderTest[i], mCameraMockListener);
waitForDeviceState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
- testCreateSessionsByCamera(mCameraIds[i]);
+ testCreateSessionsByCamera(mCameraIdsUnderTest[i]);
}
finally {
- closeDevice(mCameraIds[i], mCameraMockListener);
+ closeDevice(mCameraIdsUnderTest[i], mCameraMockListener);
}
}
}
@@ -720,21 +737,22 @@
/**
* Verify creating a custom session
*/
+ @Test
public void testCreateCustomSession() throws Exception {
- for (int i = 0; i < mCameraIds.length; i++) {
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
try {
- if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
- Log.i(TAG, "Camera " + mCameraIds[i] +
+ if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+ Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
" does not support color outputs, skipping");
continue;
}
- openDevice(mCameraIds[i], mCameraMockListener);
+ openDevice(mCameraIdsUnderTest[i], mCameraMockListener);
waitForDeviceState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
- testCreateCustomSessionByCamera(mCameraIds[i]);
+ testCreateCustomSessionByCamera(mCameraIdsUnderTest[i]);
}
finally {
- closeDevice(mCameraIds[i], mCameraMockListener);
+ closeDevice(mCameraIdsUnderTest[i], mCameraMockListener);
}
}
}
@@ -808,6 +826,7 @@
/**
* Test session configuration.
*/
+ @Test
public void testSessionConfiguration() throws Exception {
ArrayList<OutputConfiguration> outConfigs = new ArrayList<OutputConfiguration> ();
outConfigs.add(new OutputConfiguration(new Size(1, 1), SurfaceTexture.class));
@@ -856,14 +875,14 @@
assertEquals("Session configuration input doesn't match",
highspeedSessionConfig.getInputConfiguration(), null);
- for (int i = 0; i < mCameraIds.length; i++) {
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
try {
- if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
- Log.i(TAG, "Camera " + mCameraIds[i] +
+ if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+ Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
" does not support color outputs, skipping");
continue;
}
- openDevice(mCameraIds[i], mCameraMockListener);
+ openDevice(mCameraIdsUnderTest[i], mCameraMockListener);
waitForDeviceState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
CaptureRequest.Builder builder =
@@ -881,7 +900,7 @@
highspeedSessionConfig.getSessionParameters());
}
finally {
- closeDevice(mCameraIds[i], mCameraMockListener);
+ closeDevice(mCameraIdsUnderTest[i], mCameraMockListener);
}
}
}
@@ -889,21 +908,22 @@
/**
* Check for any state leakage in case of internal re-configure
*/
+ @Test
public void testSessionParametersStateLeak() throws Exception {
- for (int i = 0; i < mCameraIds.length; i++) {
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
try {
- if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
- Log.i(TAG, "Camera " + mCameraIds[i] +
+ if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+ Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
" does not support color outputs, skipping");
continue;
}
- openDevice(mCameraIds[i], mCameraMockListener);
+ openDevice(mCameraIdsUnderTest[i], mCameraMockListener);
waitForDeviceState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
- testSessionParametersStateLeakByCamera(mCameraIds[i]);
+ testSessionParametersStateLeakByCamera(mCameraIdsUnderTest[i]);
}
finally {
- closeDevice(mCameraIds[i], mCameraMockListener);
+ closeDevice(mCameraIdsUnderTest[i], mCameraMockListener);
}
}
}
@@ -1059,22 +1079,23 @@
/**
* Verify creating a session with additional parameters.
*/
+ @Test
public void testCreateSessionWithParameters() throws Exception {
- for (int i = 0; i < mCameraIds.length; i++) {
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
try {
- if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
- Log.i(TAG, "Camera " + mCameraIds[i] +
+ if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+ Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
" does not support color outputs, skipping");
continue;
}
- openDevice(mCameraIds[i], mCameraMockListener);
+ openDevice(mCameraIdsUnderTest[i], mCameraMockListener);
waitForDeviceState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
- testCreateSessionWithParametersByCamera(mCameraIds[i], /*reprocessable*/false);
- testCreateSessionWithParametersByCamera(mCameraIds[i], /*reprocessable*/true);
+ testCreateSessionWithParametersByCamera(mCameraIdsUnderTest[i], /*reprocessable*/false);
+ testCreateSessionWithParametersByCamera(mCameraIdsUnderTest[i], /*reprocessable*/true);
}
finally {
- closeDevice(mCameraIds[i], mCameraMockListener);
+ closeDevice(mCameraIdsUnderTest[i], mCameraMockListener);
}
}
}
@@ -1596,9 +1617,9 @@
*/
private void runCaptureTest(boolean burst, boolean repeating, boolean abort,
boolean useExecutor) throws Exception {
- for (int i = 0; i < mCameraIds.length; i++) {
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
try {
- openDevice(mCameraIds[i], mCameraMockListener);
+ openDevice(mCameraIdsUnderTest[i], mCameraMockListener);
waitForDeviceState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
prepareCapture();
@@ -1617,13 +1638,13 @@
continue;
}
- captureSingleShot(mCameraIds[i], sTemplates[j], repeating, abort,
+ captureSingleShot(mCameraIdsUnderTest[i], sTemplates[j], repeating, abort,
useExecutor);
}
}
else {
// Test: burst of one shot
- captureBurstShot(mCameraIds[i], sTemplates, 1, repeating, abort, useExecutor);
+ captureBurstShot(mCameraIdsUnderTest[i], sTemplates, 1, repeating, abort, useExecutor);
int template = mStaticInfo.isColorOutputSupported() ?
CameraDevice.TEMPLATE_STILL_CAPTURE :
@@ -1637,12 +1658,12 @@
};
// Test: burst of 5 shots of the same template type
- captureBurstShot(mCameraIds[i], templates, templates.length, repeating, abort,
+ captureBurstShot(mCameraIdsUnderTest[i], templates, templates.length, repeating, abort,
useExecutor);
if (mStaticInfo.isColorOutputSupported()) {
// Test: burst of 6 shots of different template types
- captureBurstShot(mCameraIds[i], sTemplates, sTemplates.length, repeating,
+ captureBurstShot(mCameraIdsUnderTest[i], sTemplates, sTemplates.length, repeating,
abort, useExecutor);
}
}
@@ -1658,7 +1679,7 @@
} catch (Exception e) {
mCollector.addError(e);
}finally {
- closeDevice(mCameraIds[i], mCameraMockListener);
+ closeDevice(mCameraIdsUnderTest[i], mCameraMockListener);
}
}
}
@@ -2698,4 +2719,178 @@
}
}
}
+
+ /**
+ * Verify audio restrictions are set properly for single CameraDevice usage
+ */
+ @Test
+ public void testAudioRestrictionSingleDevice() throws Exception {
+ int[] testModes = {
+ CameraDevice.AUDIO_RESTRICTION_VIBRATION_SOUND,
+ CameraDevice.AUDIO_RESTRICTION_NONE,
+ CameraDevice.AUDIO_RESTRICTION_VIBRATION,
+ };
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
+ try {
+ openDevice(mCameraIdsUnderTest[i], mCameraMockListener);
+ waitForDeviceState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
+
+ for (int mode : testModes) {
+ mCamera.setCameraAudioRestriction(mode);
+ int retMode = mCamera.getCameraAudioRestriction();
+ assertTrue("Audio restriction mode mismatch: input: " + mode +
+ ", output:" + retMode, mode == retMode);
+ }
+
+ try {
+ // Test invalid mode
+ mCamera.setCameraAudioRestriction(42);
+ fail("Should get IllegalArgumentException for invalid mode");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+ }
+ finally {
+ closeDevice(mCameraIdsUnderTest[i], mCameraMockListener);
+ }
+ }
+ }
+
+ private void testTwoCameraDevicesAudioRestriction(String id0, String id1) throws Exception {
+ BlockingStateCallback cam0Cb = new BlockingStateCallback();
+ BlockingStateCallback cam1Cb = new BlockingStateCallback();
+ CameraDevice cam0 = null;
+ CameraDevice cam1 = null;
+ try {
+ cam0 = CameraTestUtils.openCamera(mCameraManager, id0, cam0Cb, mHandler);
+ cam0Cb.waitForState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
+
+ int mode0 = CameraDevice.AUDIO_RESTRICTION_VIBRATION_SOUND;
+ cam0.setCameraAudioRestriction(mode0);
+ int retMode = cam0.getCameraAudioRestriction();
+ assertTrue("Audio restriction mode mismatch: input: " + mode0 + ", output:" + retMode,
+ retMode == mode0);
+
+ cam1 = CameraTestUtils.openCamera(mCameraManager, id1, cam1Cb, mHandler);
+ cam1Cb.waitForState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
+
+ // See if cam0 is evicted.
+ boolean cam0Evicted = true;
+ try {
+ final int cameraEvictedTimeoutMs = 1000;
+ cam0Cb.waitForState(STATE_DISCONNECTED, cameraEvictedTimeoutMs);
+ } catch (TimeoutRuntimeException e) {
+ // camera 0 is not evicted
+ cam0Evicted = false;
+ }
+
+ if (cam0Evicted) {
+ Log.i(TAG, "Camera " + id0 + " is evicted. Testing camera " + id1 + " alone.");
+ // cam0 is evicted
+ try {
+ cam0.setCameraAudioRestriction(mode0);
+ fail("Should get CameraAccessException for disconnected camera.");
+ } catch (CameraAccessException e) {
+ // expected
+ }
+ // Test the behavior for single remaining client
+ int mode1 = CameraDevice.AUDIO_RESTRICTION_VIBRATION;
+ cam1.setCameraAudioRestriction(mode1);
+ retMode = cam1.getCameraAudioRestriction();
+ assertTrue("Audio restriction mode mismatch: input: " + mode1 +
+ ", output:" + retMode, retMode == mode1);
+ return;
+ }
+
+ // The output mode should be union of all CameraDevices
+ int mode1 = CameraDevice.AUDIO_RESTRICTION_VIBRATION;
+ int expectMode = mode0 | mode1;
+ cam1.setCameraAudioRestriction(mode1);
+ retMode = cam1.getCameraAudioRestriction();
+ assertTrue("Audio restriction mode mismatch: expect: " + expectMode +
+ ", output:" + retMode, retMode == expectMode);
+
+ // test turning off mute settings also
+ mode0 = CameraDevice.AUDIO_RESTRICTION_NONE;
+ expectMode = mode0 | mode1;
+ cam0.setCameraAudioRestriction(mode0);
+ retMode = cam0.getCameraAudioRestriction();
+ assertTrue("Audio restriction mode mismatch: expect: " + expectMode +
+ ", output:" + retMode, retMode == expectMode);
+
+ // mode should be NONE when both device set to NONE
+ mode1 = CameraDevice.AUDIO_RESTRICTION_NONE;
+ expectMode = mode0 | mode1;
+ cam1.setCameraAudioRestriction(mode1);
+ retMode = cam1.getCameraAudioRestriction();
+ assertTrue("Audio restriction mode mismatch: expect: " + expectMode +
+ ", output:" + retMode, retMode == expectMode);
+
+ // test removal of VIBRATE won't affect existing VIBRATE_SOUND state
+ mode0 = CameraDevice.AUDIO_RESTRICTION_VIBRATION_SOUND;
+ expectMode = mode0 | mode1;
+ cam0.setCameraAudioRestriction(mode0);
+ retMode = cam0.getCameraAudioRestriction();
+ assertTrue("Audio restriction mode mismatch: expect: " + expectMode +
+ ", output:" + retMode, retMode == expectMode);
+
+ mode1 = CameraDevice.AUDIO_RESTRICTION_VIBRATION;
+ expectMode = mode0 | mode1;
+ cam1.setCameraAudioRestriction(mode1);
+ retMode = cam1.getCameraAudioRestriction();
+ assertTrue("Audio restriction mode mismatch: expect: " + expectMode +
+ ", output:" + retMode, retMode == expectMode);
+
+ mode1 = CameraDevice.AUDIO_RESTRICTION_NONE;
+ expectMode = mode0 | mode1;
+ cam1.setCameraAudioRestriction(mode1);
+ retMode = cam1.getCameraAudioRestriction();
+ assertTrue("Audio restriction mode mismatch: expect: " + expectMode +
+ ", output:" + retMode, retMode == expectMode);
+
+ // Now test CameraDevice.close will remove setting and exception is thrown for closed
+ // camera.
+ cam0.close();
+ cam0Cb.waitForState(STATE_CLOSED, CAMERA_CLOSE_TIMEOUT_MS);
+ try {
+ cam0.setCameraAudioRestriction(mode0);
+ fail("Should get IllegalStateException for closed camera.");
+ } catch (IllegalStateException e) {
+ // expected;
+ }
+
+ cam0 = null;
+ cam0Cb = null;
+ expectMode = mode1;
+ cam1.setCameraAudioRestriction(mode1);
+ retMode = cam1.getCameraAudioRestriction();
+ assertTrue("Audio restriction mode mismatch: expect: " + expectMode +
+ ", output:" + retMode, retMode == expectMode);
+ } finally {
+ if (cam0 != null) {
+ cam0.close();
+ cam0Cb.waitForState(STATE_CLOSED, CAMERA_CLOSE_TIMEOUT_MS);
+ cam0Cb = null;
+ }
+ if (cam1 != null) {
+ cam1.close();
+ cam1Cb.waitForState(STATE_CLOSED, CAMERA_CLOSE_TIMEOUT_MS);
+ cam1Cb = null;
+ }
+ }
+ }
+
+ @Test
+ public void testAudioRestrictionMultipleDevices() throws Exception {
+ if (mCameraIdsUnderTest.length < 2) {
+ Log.i(TAG, "device doesn't have multiple cameras, skipping");
+ return;
+ }
+
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
+ for (int j = i+1; j < mCameraIdsUnderTest.length; j++) {
+ testTwoCameraDevicesAudioRestriction(mCameraIdsUnderTest[i], mCameraIdsUnderTest[j]);
+ }
+ }
+ }
}
diff --git a/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java b/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java
index 4457f95..922dc3c 100644
--- a/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java
@@ -19,6 +19,7 @@
import static org.mockito.Mockito.*;
import static org.mockito.AdditionalMatchers.not;
import static org.mockito.AdditionalMatchers.and;
+import static junit.framework.Assert.*;
import android.app.ActivityManager;
import android.app.Instrumentation;
@@ -31,6 +32,7 @@
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraDevice.StateCallback;
import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.cts.Camera2ParameterizedTestCase;
import android.hardware.camera2.cts.CameraTestUtils.HandlerExecutor;
import android.hardware.camera2.cts.CameraTestUtils.MockStateCallback;
import android.hardware.camera2.cts.helpers.CameraErrorCollector;
@@ -45,6 +47,9 @@
import com.android.compatibility.common.util.PropertyUtil;
import com.android.ex.camera2.blocking.BlockingStateCallback;
+import org.junit.runners.Parameterized;
+import org.junit.runner.RunWith;
+import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
@@ -61,13 +66,14 @@
/**
* <p>Basic test for CameraManager class.</p>
*/
-public class CameraManagerTest extends AndroidTestCase {
+
+@RunWith(Parameterized.class)
+public class CameraManagerTest extends Camera2ParameterizedTestCase {
private static final String TAG = "CameraManagerTest";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
private static final int NUM_CAMERA_REOPENS = 10;
private PackageManager mPackageManager;
- private CameraManager mCameraManager;
private NoopCameraListener mListener;
private HandlerThread mHandlerThread;
private Handler mHandler;
@@ -75,18 +81,11 @@
private CameraErrorCollector mCollector;
@Override
- public void setContext(Context context) {
- super.setContext(context);
- mCameraManager = (CameraManager)context.getSystemService(Context.CAMERA_SERVICE);
- assertNotNull("Can't connect to camera manager", mCameraManager);
- mPackageManager = context.getPackageManager();
+ public void setUp() throws Exception {
+ super.setUp();
+ mPackageManager = mContext.getPackageManager();
assertNotNull("Can't get package manager", mPackageManager);
mListener = new NoopCameraListener();
- }
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
/**
* Workaround for mockito and JB-MR2 incompatibility
@@ -94,7 +93,7 @@
* Avoid java.lang.IllegalArgumentException: dexcache == null
* https://code.google.com/p/dexmaker/issues/detail?id=2
*/
- System.setProperty("dexmaker.dexcache", getContext().getCacheDir().toString());
+ System.setProperty("dexmaker.dexcache", mContext.getCacheDir().toString());
mCameraListener = spy(new BlockingStateCallback());
@@ -105,7 +104,7 @@
}
@Override
- protected void tearDown() throws Exception {
+ public void tearDown() throws Exception {
mHandlerThread.quitSafely();
mHandler = null;
@@ -137,10 +136,10 @@
return -1; // unreachable
}
+ @Test
public void testCameraManagerGetDeviceIdList() throws Exception {
- // Test: that the getCameraIdList method runs without exceptions.
- String[] ids = mCameraManager.getCameraIdList();
+ String[] ids = mCameraIdsUnderTest;
if (VERBOSE) Log.v(TAG, "CameraManager ids: " + Arrays.toString(ids));
/**
@@ -197,8 +196,9 @@
}
// Test: that properties can be queried from each device, without exceptions.
+ @Test
public void testCameraManagerGetCameraCharacteristics() throws Exception {
- String[] ids = mCameraManager.getCameraIdList();
+ String[] ids = mCameraIdsUnderTest;
for (int i = 0; i < ids.length; i++) {
CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
assertNotNull(
@@ -207,8 +207,9 @@
}
// Test: that an exception is thrown if an invalid device id is passed down.
+ @Test
public void testCameraManagerInvalidDevice() throws Exception {
- String[] ids = mCameraManager.getCameraIdList();
+ String[] ids = mCameraIdsUnderTest;
// Create an invalid id by concatenating all the valid ids together.
StringBuilder invalidId = new StringBuilder();
invalidId.append("INVALID");
@@ -226,6 +227,7 @@
}
// Test: that each camera device can be opened one at a time, several times.
+ @Test
public void testCameraManagerOpenCamerasSerially() throws Exception {
testCameraManagerOpenCamerasSerially(/*useExecutor*/ false);
testCameraManagerOpenCamerasSerially(/*useExecutor*/ true);
@@ -233,7 +235,7 @@
private void testCameraManagerOpenCamerasSerially(boolean useExecutor) throws Exception {
final Executor executor = useExecutor ? new HandlerExecutor(mHandler) : null;
- String[] ids = mCameraManager.getCameraIdList();
+ String[] ids = mCameraIdsUnderTest;
for (int i = 0; i < ids.length; i++) {
for (int j = 0; j < NUM_CAMERA_REOPENS; j++) {
CameraDevice camera = null;
@@ -268,13 +270,14 @@
* Test: one or more camera devices can be open at the same time, or the right error state
* is set if this can't be done.
*/
+ @Test
public void testCameraManagerOpenAllCameras() throws Exception {
testCameraManagerOpenAllCameras(/*useExecutor*/ false);
testCameraManagerOpenAllCameras(/*useExecutor*/ true);
}
private void testCameraManagerOpenAllCameras(boolean useExecutor) throws Exception {
- String[] ids = mCameraManager.getCameraIdList();
+ String[] ids = mCameraIdsUnderTest;
assertNotNull("Camera ids shouldn't be null", ids);
// Skip test if the device doesn't have multiple cameras.
@@ -451,13 +454,14 @@
* Test: that opening the same device multiple times and make sure the right
* error state is set.
*/
+ @Test
public void testCameraManagerOpenCameraTwice() throws Exception {
testCameraManagerOpenCameraTwice(/*useExecutor*/ false);
testCameraManagerOpenCameraTwice(/*useExecutor*/ true);
}
private void testCameraManagerOpenCameraTwice(boolean useExecutor) throws Exception {
- String[] ids = mCameraManager.getCameraIdList();
+ String[] ids = mCameraIdsUnderTest;
final Executor executor = useExecutor ? new HandlerExecutor(mHandler) : null;
// Test across every camera device.
@@ -523,6 +527,7 @@
* Registering a listener multiple times should have no effect, and unregistering
* a listener that isn't registered should have no effect.
*/
+ @Test
public void testCameraManagerListener() throws Exception {
mCameraManager.unregisterAvailabilityCallback(mListener);
// Test Handler API
@@ -541,6 +546,7 @@
/**
* Test that the availability callbacks fire when expected
*/
+ @Test
public void testCameraManagerListenerCallbacks() throws Exception {
testCameraManagerListenerCallbacks(/*useExecutor*/ false);
testCameraManagerListenerCallbacks(/*useExecutor*/ true);
@@ -556,6 +562,17 @@
CameraManager.AvailabilityCallback ac = new CameraManager.AvailabilityCallback() {
@Override
public void onCameraAvailable(String cameraId) {
+ try {
+ // When we're testing system cameras, we don't list non system cameras in the
+ // camera id list as mentioned in Camera2ParameterizedTest.java
+ if (mAdoptShellPerm &&
+ !CameraTestUtils.isSystemCamera(mCameraManager, cameraId)) {
+ return;
+ }
+ } catch (CameraAccessException e) {
+ fail("CameraAccessException thrown when attempting to access camera" +
+ "characteristics" + cameraId);
+ }
availableEventQueue.offer(cameraId);
}
@@ -570,7 +587,7 @@
} else {
mCameraManager.registerAvailabilityCallback(ac, mHandler);
}
- String[] cameras = mCameraManager.getCameraIdList();
+ String[] cameras = mCameraIdsUnderTest;
if (cameras.length == 0) {
Log.i(TAG, "No cameras present, skipping test");
@@ -683,12 +700,13 @@
} // testCameraManagerListenerCallbacks
// Verify no LEGACY-level devices appear on devices first launched in the Q release or newer
+ @Test
public void testNoLegacyOnQ() throws Exception {
if(PropertyUtil.getFirstApiLevel() < Build.VERSION_CODES.Q){
// LEGACY still allowed for devices upgrading to Q
return;
}
- String[] ids = mCameraManager.getCameraIdList();
+ String[] ids = mCameraIdsUnderTest;
for (int i = 0; i < ids.length; i++) {
CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
assertNotNull(
@@ -703,14 +721,15 @@
}
}
+ @Test
public void testCameraManagerWithDnD() throws Exception {
- String[] cameras = mCameraManager.getCameraIdList();
+ String[] cameras = mCameraIdsUnderTest;
if (cameras.length == 0) {
Log.i(TAG, "No cameras present, skipping test");
return;
}
- ActivityManager am = getContext().getSystemService(ActivityManager.class);
+ ActivityManager am = mContext.getSystemService(ActivityManager.class);
// Go devices do not support all interrupt filtering functionality
if (am.isLowRamDevice()) {
@@ -718,12 +737,12 @@
}
// Allow the test package to adjust notification policy
- toggleNotificationPolicyAccess(getContext().getPackageName(),
+ toggleNotificationPolicyAccess(mContext.getPackageName(),
InstrumentationRegistry.getInstrumentation(), true);
// Enable DnD filtering
- NotificationManager nm = getContext().getSystemService(NotificationManager.class);
+ NotificationManager nm = mContext.getSystemService(NotificationManager.class);
try {
nm.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_NONE);
@@ -755,7 +774,7 @@
runCommand(command, instrumentation);
- NotificationManager nm = getContext().getSystemService(NotificationManager.class);
+ NotificationManager nm = mContext.getSystemService(NotificationManager.class);
assertEquals("Notification Policy Access Grant is " +
nm.isNotificationPolicyAccessGranted() + " not " + on, on,
nm.isNotificationPolicyAccessGranted());
@@ -764,17 +783,14 @@
private void runCommand(String command, Instrumentation instrumentation) throws IOException {
UiAutomation uiAutomation = instrumentation.getUiAutomation();
// Execute command
- try (ParcelFileDescriptor fd = uiAutomation.executeShellCommand(command)) {
- assertNotNull("Failed to execute shell command: " + command, fd);
- // Wait for the command to finish by reading until EOF
- try (InputStream in = new FileInputStream(fd.getFileDescriptor())) {
- byte[] buffer = new byte[4096];
- while (in.read(buffer) > 0) {}
- } catch (IOException e) {
- throw new IOException("Could not read stdout of command: " + command, e);
- }
- } finally {
- uiAutomation.destroy();
+ ParcelFileDescriptor fd = mUiAutomation.executeShellCommand(command);
+ assertNotNull("Failed to execute shell command: " + command, fd);
+ // Wait for the command to finish by reading until EOF
+ try (InputStream in = new FileInputStream(fd.getFileDescriptor())) {
+ byte[] buffer = new byte[4096];
+ while (in.read(buffer) > 0) {}
+ } catch (IOException e) {
+ throw new IOException("Could not read stdout of command: " + command, e);
}
}
diff --git a/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java b/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java
index 2a46cf7..b02f13d 100644
--- a/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java
@@ -52,6 +52,8 @@
import java.util.Arrays;
import java.util.List;
+import org.junit.runners.Parameterized;
+import org.junit.runner.RunWith;
import org.junit.Test;
/**
@@ -63,6 +65,8 @@
* manual ISP control and other per-frame control and synchronization.
* </p>
*/
+
+@RunWith(Parameterized.class)
public class CaptureRequestTest extends Camera2SurfaceViewTestCase {
private static final String TAG = "CaptureRequestTest";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -147,9 +151,9 @@
SurfaceTexture outputTexture = new SurfaceTexture(/* random texture ID */ 5);
Surface surface = new Surface(outputTexture);
- for (int i = 0; i < mCameraIds.length; i++) {
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
try {
- openDevice(mCameraIds[i]);
+ openDevice(mCameraIdsUnderTest[i]);
CaptureRequest.Builder requestBuilder =
mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
requestBuilder.addTarget(surface);
@@ -236,14 +240,14 @@
*/
@Test
public void testBlackLevelLock() throws Exception {
- for (int i = 0; i < mCameraIds.length; i++) {
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
try {
- if (!mAllStaticInfo.get(mCameraIds[i]).isCapabilitySupported(
+ if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isCapabilitySupported(
CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
continue;
}
- openDevice(mCameraIds[i]);
+ openDevice(mCameraIdsUnderTest[i]);
SimpleCaptureCallback listener = new SimpleCaptureCallback();
CaptureRequest.Builder requestBuilder =
mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
@@ -290,7 +294,7 @@
*/
@Test
public void testDynamicBlackWhiteLevel() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
if (!mAllStaticInfo.get(id).isDynamicBlackLevelSupported()) {
continue;
@@ -318,11 +322,11 @@
*/
@Test
public void testLensShadingMap() throws Exception {
- for (int i = 0; i < mCameraIds.length; i++) {
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
try {
- StaticMetadata staticInfo = mAllStaticInfo.get(mCameraIds[i]);
+ StaticMetadata staticInfo = mAllStaticInfo.get(mCameraIdsUnderTest[i]);
if (!staticInfo.isManualLensShadingMapSupported()) {
- Log.i(TAG, "Camera " + mCameraIds[i] +
+ Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
" doesn't support lens shading controls, skipping test");
continue;
}
@@ -334,7 +338,7 @@
continue;
}
- openDevice(mCameraIds[i]);
+ openDevice(mCameraIdsUnderTest[i]);
SimpleCaptureCallback listener = new SimpleCaptureCallback();
CaptureRequest.Builder requestBuilder =
mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
@@ -396,15 +400,15 @@
*/
@Test
public void testAntiBandingModes() throws Exception {
- for (int i = 0; i < mCameraIds.length; i++) {
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
try {
// Without manual sensor control, exposure time cannot be verified
- if (!mAllStaticInfo.get(mCameraIds[i]).isCapabilitySupported(
+ if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isCapabilitySupported(
CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
continue;
}
- openDevice(mCameraIds[i]);
+ openDevice(mCameraIdsUnderTest[i]);
int[] modes = mStaticInfo.getAeAvailableAntiBandingModesChecked();
Size previewSz =
@@ -432,15 +436,15 @@
*/
@Test(timeout=60*60*1000) // timeout = 60 mins for long running tests
public void testAeModeAndLock() throws Exception {
- for (int i = 0; i < mCameraIds.length; i++) {
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
try {
- if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
- Log.i(TAG, "Camera " + mCameraIds[i] +
+ if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+ Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
" does not support color outputs, skipping");
continue;
}
- openDevice(mCameraIds[i]);
+ openDevice(mCameraIdsUnderTest[i]);
Size maxPreviewSz = mOrderedPreviewSizes.get(0); // Max preview size.
// Update preview surface with given size for all sub-tests.
@@ -465,15 +469,15 @@
*/
@Test
public void testFlashControl() throws Exception {
- for (int i = 0; i < mCameraIds.length; i++) {
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
try {
- if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
- Log.i(TAG, "Camera " + mCameraIds[i] +
+ if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+ Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
" does not support color outputs, skipping");
continue;
}
- openDevice(mCameraIds[i]);
+ openDevice(mCameraIdsUnderTest[i]);
SimpleCaptureCallback listener = new SimpleCaptureCallback();
CaptureRequest.Builder requestBuilder =
mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
@@ -509,15 +513,15 @@
*/
@Test
public void testFlashTurnOff() throws Exception {
- for (int i = 0; i < mCameraIds.length; i++) {
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
try {
- if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
- Log.i(TAG, "Camera " + mCameraIds[i] +
+ if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+ Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
" does not support color outputs, skipping");
continue;
}
- openDevice(mCameraIds[i]);
+ openDevice(mCameraIdsUnderTest[i]);
SimpleCaptureCallback listener = new SimpleCaptureCallback();
CaptureRequest.Builder requestBuilder =
mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
@@ -550,14 +554,14 @@
*/
@Test
public void testFaceDetection() throws Exception {
- for (int i = 0; i < mCameraIds.length; i++) {
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
try {
- if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
- Log.i(TAG, "Camera " + mCameraIds[i] +
+ if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+ Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
" does not support color outputs, skipping");
continue;
}
- openDevice(mCameraIds[i]);
+ openDevice(mCameraIdsUnderTest[i]);
faceDetectionTestByCamera();
} finally {
closeDevice();
@@ -570,7 +574,7 @@
*/
@Test
public void testToneMapControl() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
if (!mAllStaticInfo.get(id).isManualToneMapSupported()) {
Log.i(TAG, "Camera " + id +
@@ -590,7 +594,7 @@
*/
@Test
public void testColorCorrectionControl() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
if (!mAllStaticInfo.get(id).isColorCorrectionSupported()) {
Log.i(TAG, "Camera " + id +
@@ -610,7 +614,7 @@
*/
@Test
public void testEdgeModeControl() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
if (!mAllStaticInfo.get(id).isEdgeModeControlSupported()) {
Log.i(TAG, "Camera " + id +
@@ -632,7 +636,7 @@
*/
@Test
public void testEdgeModeControlFastFps() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
if (!mAllStaticInfo.get(id).isEdgeModeControlSupported()) {
Log.i(TAG, "Camera " + id +
@@ -655,7 +659,7 @@
*/
@Test
public void testFocusDistanceControl() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
StaticMetadata staticInfo = mAllStaticInfo.get(id);
if (!staticInfo.hasFocuser()) {
@@ -683,7 +687,7 @@
*/
@Test
public void testNoiseReductionModeControl() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
if (!mAllStaticInfo.get(id).isNoiseReductionModeControlSupported()) {
Log.i(TAG, "Camera " + id +
@@ -705,7 +709,7 @@
*/
@Test
public void testNoiseReductionModeControlFastFps() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
if (!mAllStaticInfo.get(id).isNoiseReductionModeControlSupported()) {
Log.i(TAG, "Camera " + id +
@@ -729,7 +733,7 @@
*/
@Test
public void testAwbModeAndLock() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
@@ -748,7 +752,7 @@
*/
@Test
public void testAfModes() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
@@ -767,7 +771,7 @@
*/
@Test
public void testCameraStabilizations() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
StaticMetadata staticInfo = mAllStaticInfo.get(id);
List<Key<?>> keys = staticInfo.getCharacteristics().getKeys();
@@ -796,7 +800,7 @@
*/
@Test
public void testDigitalZoom() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
@@ -817,7 +821,7 @@
*/
@Test
public void testDigitalZoomPreviewCombinations() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
@@ -836,7 +840,7 @@
*/
@Test
public void testSceneModes() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
if (mAllStaticInfo.get(id).isSceneModeSupported()) {
openDevice(id);
@@ -853,7 +857,7 @@
*/
@Test
public void testEffectModes() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
diff --git a/tests/camera/src/android/hardware/camera2/cts/CaptureResultTest.java b/tests/camera/src/android/hardware/camera2/cts/CaptureResultTest.java
index ab65015..0e68bd4 100644
--- a/tests/camera/src/android/hardware/camera2/cts/CaptureResultTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CaptureResultTest.java
@@ -21,6 +21,7 @@
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CaptureFailure;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.params.BlackLevelPattern;
@@ -37,6 +38,7 @@
import static android.hardware.camera2.cts.CameraTestUtils.*;
import static android.hardware.camera2.cts.helpers.CameraSessionUtils.*;
+import static junit.framework.Assert.*;
import android.util.Log;
import android.view.Surface;
@@ -51,6 +53,11 @@
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
+import org.junit.runners.Parameterized;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+@RunWith(Parameterized.class)
public class CaptureResultTest extends Camera2AndroidTestCase {
private static final String TAG = "CaptureResultTest";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -58,29 +65,15 @@
private static final int NUM_FRAMES_VERIFIED = 30;
private static final long WAIT_FOR_RESULT_TIMEOUT_MS = 3000;
-
// List tracking the failed test keys.
@Override
- public void setContext(Context context) {
- super.setContext(context);
-
- /**
- * Workaround for mockito and JB-MR2 incompatibility
- *
- * Avoid java.lang.IllegalArgumentException: dexcache == null
- * https://code.google.com/p/dexmaker/issues/detail?id=2
- */
- System.setProperty("dexmaker.dexcache", getContext().getCacheDir().toString());
- }
-
- @Override
- protected void setUp() throws Exception {
+ public void setUp() throws Exception {
super.setUp();
}
@Override
- protected void tearDown() throws Exception {
+ public void tearDown() throws Exception {
super.tearDown();
}
@@ -95,8 +88,9 @@
* a capture result.
* </p>
*/
+ @Test
public void testCameraCaptureResultAllKeys() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
openDevice(id);
if (mStaticInfo.isColorOutputSupported()) {
@@ -149,10 +143,11 @@
* onCaptureProgressed callbacks.
* </ul></p>
*/
+ @Test
public void testPartialResult() throws Exception {
final int NUM_FRAMES_TESTED = 30;
final int WAIT_FOR_RESULT_TIMOUT_MS = 2000;
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
// Skip the test if partial result is not supported
int partialResultCount = mAllStaticInfo.get(id).getPartialResultCount();
@@ -264,8 +259,9 @@
* Check that the timestamps passed in the results, buffers, and capture callbacks match for
* a single request, and increase monotonically
*/
+ @Test
public void testResultTimestamps() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
ImageReader previewReader = null;
ImageReader jpegReader = null;
@@ -401,16 +397,22 @@
}
TotalCaptureResult result = null;
+ // List of (frameNumber, physical camera Id) pairs
+ ArrayList<Pair<Long, String>> droppedPhysicalResults = new ArrayList<>();
for (int i = 0; i < numFramesVerified; i++) {
result = captureListener.getTotalCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+
Map<String, CaptureResult> physicalCaptureResults = result.getPhysicalCameraResults();
- errorCollector.expectEquals("Number of physical result metadata doesn't match " +
- physicalCaptureResults.size() + " vs " + requestedPhysicalIds.size(),
- physicalCaptureResults.size(), requestedPhysicalIds.size());
+ ArrayList<String> droppedIds = new ArrayList<String>(requestedPhysicalIds);
+ droppedIds.removeAll(physicalCaptureResults.keySet());
+ for (String droppedId : droppedIds) {
+ droppedPhysicalResults.add(
+ new Pair<Long, String>(result.getFrameNumber(), droppedId));
+ }
validateOneCaptureResult(errorCollector, staticInfo, waiverKeys, allKeys,
requestBuilder, result, null/*cameraId*/, i);
- for (String physicalId : requestedPhysicalIds) {
+ for (String physicalId : physicalCaptureResults.keySet()) {
StaticMetadata physicalStaticInfo = allStaticInfo.get(physicalId);
validateOneCaptureResult(errorCollector, physicalStaticInfo,
physicalWaiverKeys.get(physicalId),
@@ -418,6 +420,23 @@
physicalId, i);
}
}
+
+ // Verify that all dropped physical camera results are notified via capture failure.
+ while (captureListener.hasMoreFailures()) {
+ ArrayList<CaptureFailure> failures =
+ captureListener.getCaptureFailures(/*maxNumFailures*/ 1);
+ for (CaptureFailure failure : failures) {
+ String failedPhysicalId = failure.getPhysicalCameraId();
+ Long failedFrameNumber = failure.getFrameNumber();
+ if (failedPhysicalId != null) {
+ droppedPhysicalResults.removeIf(
+ n -> n.equals(
+ new Pair<Long, String>(failedFrameNumber, failedPhysicalId)));
+ }
+ }
+ }
+ errorCollector.expectTrue("Not all dropped results for physical cameras are notified",
+ droppedPhysicalResults.isEmpty());
}
private static void validateOneCaptureResult(CameraErrorCollector errorCollector,
diff --git a/tests/camera/src/android/hardware/camera2/cts/DngCreatorTest.java b/tests/camera/src/android/hardware/camera2/cts/DngCreatorTest.java
index 4af437c..b0c806f 100644
--- a/tests/camera/src/android/hardware/camera2/cts/DngCreatorTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/DngCreatorTest.java
@@ -60,11 +60,18 @@
import java.util.TimeZone;
import java.text.SimpleDateFormat;
+import org.junit.runners.Parameterized;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
import static android.hardware.camera2.cts.helpers.AssertHelpers.*;
+import static junit.framework.Assert.*;
/**
* Tests for the DngCreator API.
*/
+
+@RunWith(Parameterized.class)
public class DngCreatorTest extends Camera2AndroidTestCase {
private static final String TAG = "DngCreatorTest";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -99,24 +106,17 @@
}
@Override
- protected void setUp() throws Exception {
- RenderScriptSingleton.setContext(getContext());
-
+ public void setUp() throws Exception {
super.setUp();
+ RenderScriptSingleton.setContext(mContext);
}
@Override
- protected void tearDown() throws Exception {
+ public void tearDown() throws Exception {
RenderScriptSingleton.clearContext();
-
super.tearDown();
}
- @Override
- public synchronized void setContext(Context context) {
- super.setContext(context);
- }
-
/**
* Test basic raw capture and DNG saving functionality for each of the available cameras.
*
@@ -131,16 +131,17 @@
* raw image captured for the first reported camera device to be saved to an output file.
* </p>
*/
+ @Test
public void testSingleImageBasic() throws Exception {
- for (int i = 0; i < mCameraIds.length; i++) {
- String deviceId = mCameraIds[i];
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
+ String deviceId = mCameraIdsUnderTest[i];
ImageReader captureReader = null;
FileOutputStream fileStream = null;
ByteArrayOutputStream outputStream = null;
try {
if (!mAllStaticInfo.get(deviceId).isCapabilitySupported(
CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
- Log.i(TAG, "RAW capability is not supported in camera " + mCameraIds[i] +
+ Log.i(TAG, "RAW capability is not supported in camera " + mCameraIdsUnderTest[i] +
". Skip the test.");
continue;
}
@@ -205,9 +206,10 @@
* raw image captured for the first reported camera device to be saved to an output file.
* </p>
*/
+ @Test
public void testSingleImageThumbnail() throws Exception {
- for (int i = 0; i < mCameraIds.length; i++) {
- String deviceId = mCameraIds[i];
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
+ String deviceId = mCameraIdsUnderTest[i];
List<ImageReader> captureReaders = new ArrayList<ImageReader>();
List<CameraTestUtils.SimpleImageReaderListener> captureListeners =
new ArrayList<CameraTestUtils.SimpleImageReaderListener>();
@@ -216,7 +218,7 @@
try {
if (!mAllStaticInfo.get(deviceId).isCapabilitySupported(
CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
- Log.i(TAG, "RAW capability is not supported in camera " + mCameraIds[i] +
+ Log.i(TAG, "RAW capability is not supported in camera " + mCameraIdsUnderTest[i] +
". Skip the test.");
continue;
}
@@ -369,8 +371,9 @@
* adb shell setprop log.tag.DngCreatorTest VERBOSE
* </p>
*/
+ @Test
public void testRaw16JpegConsistency() throws Exception {
- for (String deviceId : mCameraIds) {
+ for (String deviceId : mCameraIdsUnderTest) {
List<ImageReader> captureReaders = new ArrayList<>();
FileOutputStream fileStream = null;
ByteArrayOutputStream outputStream = null;
@@ -461,8 +464,9 @@
/**
* Test basic DNG creation, ensure that the DNG image can be rendered by BitmapFactory.
*/
+ @Test
public void testDngRenderingByBitmapFactor() throws Exception {
- for (String deviceId : mCameraIds) {
+ for (String deviceId : mCameraIdsUnderTest) {
List<ImageReader> captureReaders = new ArrayList<>();
CapturedData data = captureRawJpegImagePair(deviceId, captureReaders);
diff --git a/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java b/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
index a5ea222..d761c6f 100644
--- a/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
@@ -59,14 +59,21 @@
import java.util.regex.Pattern;
import java.util.Set;
+import org.junit.runners.Parameterized;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
import static android.hardware.camera2.cts.helpers.AssertHelpers.*;
import static android.hardware.camera2.cts.CameraTestUtils.SimpleCaptureCallback;
+import static junit.framework.Assert.*;
+
import static org.mockito.Mockito.*;
/**
* Extended tests for static camera characteristics.
*/
+@RunWith(Parameterized.class)
public class ExtendedCameraCharacteristicsTest extends Camera2AndroidTestCase {
private static final String TAG = "ExChrsTest"; // must be short so next line doesn't throw
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -128,7 +135,7 @@
private static final int HIGH_SPEED_FPS_UPPER_MIN = 120;
@Override
- protected void setUp() throws Exception {
+ public void setUp() throws Exception {
super.setUp();
mCharacteristics = new ArrayList<>();
for (int i = 0; i < mAllCameraIds.length; i++) {
@@ -137,7 +144,7 @@
}
@Override
- protected void tearDown() throws Exception {
+ public void tearDown() throws Exception {
super.tearDown();
mCharacteristics = null;
}
@@ -146,6 +153,7 @@
* Test that the available stream configurations contain a few required formats and sizes.
*/
@CddTest(requirement="7.5.1/C-1-2")
+ @Test
public void testAvailableStreamConfigs() throws Exception {
boolean firstBackFacingCamera = true;
for (int i = 0; i < mAllCameraIds.length; i++) {
@@ -169,7 +177,7 @@
boolean isMonochromeWithY8 = arrayContains(actualCapabilities, MONOCHROME)
&& arrayContains(outputFormats, ImageFormat.Y8);
- boolean isHiddenPhysicalCamera = !arrayContains(mCameraIds, mAllCameraIds[i]);
+ boolean isHiddenPhysicalCamera = !arrayContains(mCameraIdsUnderTest, mAllCameraIds[i]);
boolean supportHeic = arrayContains(outputFormats, ImageFormat.HEIC);
assertArrayContains(
@@ -769,6 +777,7 @@
}
+ @Test
public void testRecommendedStreamConfigurations() throws Exception {
for (int i = 0; i < mAllCameraIds.length; i++) {
CameraCharacteristics c = mCharacteristics.get(i);
@@ -868,6 +877,7 @@
/**
* Test {@link CameraCharacteristics#getKeys}
*/
+ @Test
public void testKeys() {
for (int i = 0; i < mAllCameraIds.length; i++) {
CameraCharacteristics c = mCharacteristics.get(i);
@@ -1036,6 +1046,7 @@
/**
* Test values for static metadata used by the RAW capability.
*/
+ @Test
public void testStaticRawCharacteristics() {
for (int i = 0; i < mAllCameraIds.length; i++) {
CameraCharacteristics c = mCharacteristics.get(i);
@@ -1142,6 +1153,7 @@
/**
* Test values for the available session keys.
*/
+ @Test
public void testStaticSessionKeys() throws Exception {
for (CameraCharacteristics c : mCharacteristics) {
List<CaptureRequest.Key<?>> availableSessionKeys = c.getAvailableSessionKeys();
@@ -1161,6 +1173,7 @@
/**
* Test values for static metadata used by the BURST capability.
*/
+ @Test
public void testStaticBurstCharacteristics() throws Exception {
for (int i = 0; i < mAllCameraIds.length; i++) {
CameraCharacteristics c = mCharacteristics.get(i);
@@ -1325,6 +1338,7 @@
/**
* Check reprocessing capabilities.
*/
+ @Test
public void testReprocessingCharacteristics() {
for (int i = 0; i < mAllCameraIds.length; i++) {
Log.i(TAG, "testReprocessingCharacteristics: Testing camera ID " + mAllCameraIds[i]);
@@ -1453,6 +1467,7 @@
/**
* Check depth output capability
*/
+ @Test
public void testDepthOutputCharacteristics() {
for (int i = 0; i < mAllCameraIds.length; i++) {
Log.i(TAG, "testDepthOutputCharacteristics: Testing camera ID " + mAllCameraIds[i]);
@@ -1728,6 +1743,7 @@
/**
* Cross-check StreamConfigurationMap output
*/
+ @Test
public void testStreamConfigurationMap() throws Exception {
for (int i = 0; i < mAllCameraIds.length; i++) {
Log.i(TAG, "testStreamConfigurationMap: Testing camera ID " + mAllCameraIds[i]);
@@ -1933,6 +1949,7 @@
* Test high speed capability and cross-check the high speed sizes and fps ranges from
* the StreamConfigurationMap.
*/
+ @Test
public void testConstrainedHighSpeedCapability() throws Exception {
for (int i = 0; i < mAllCameraIds.length; i++) {
CameraCharacteristics c = mCharacteristics.get(i);
@@ -2013,6 +2030,7 @@
/**
* Sanity check of optical black regions.
*/
+ @Test
public void testOpticalBlackRegions() {
for (int i = 0; i < mAllCameraIds.length; i++) {
CameraCharacteristics c = mCharacteristics.get(i);
@@ -2089,6 +2107,7 @@
/**
* Check Logical camera capability
*/
+ @Test
public void testLogicalCameraCharacteristics() throws Exception {
for (int i = 0; i < mAllCameraIds.length; i++) {
CameraCharacteristics c = mCharacteristics.get(i);
@@ -2163,6 +2182,7 @@
/**
* Check monochrome camera capability
*/
+ @Test
public void testMonochromeCharacteristics() {
for (int i = 0; i < mAllCameraIds.length; i++) {
Log.i(TAG, "testMonochromeCharacteristics: Testing camera ID " + mAllCameraIds[i]);
@@ -2297,7 +2317,13 @@
* accessible via Camera2.
*/
@CddTest(requirement="7.5.4/C-0-11")
+ @Test
public void testLegacyCameraDeviceParity() {
+ if (mAdoptShellPerm) {
+ // There is no current way to determine in camera1 api if a device is a system camera
+ // Skip test, http://b/141496896
+ return;
+ }
int legacyDeviceCount = Camera.getNumberOfCameras();
assertTrue("More legacy devices: " + legacyDeviceCount + " compared to Camera2 devices: " +
mCharacteristics.size(), legacyDeviceCount <= mCharacteristics.size());
@@ -2339,6 +2365,7 @@
* Check camera orientation against device orientation
*/
@CddTest(requirement="7.5.5/C-1-1")
+ @Test
public void testCameraOrientationAlignedWithDevice() {
WindowManager windowManager =
(WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
diff --git a/tests/camera/src/android/hardware/camera2/cts/FastBasicsTest.java b/tests/camera/src/android/hardware/camera2/cts/FastBasicsTest.java
index 0716c24..fc09e51 100644
--- a/tests/camera/src/android/hardware/camera2/cts/FastBasicsTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/FastBasicsTest.java
@@ -41,6 +41,8 @@
import android.hardware.camera2.cts.CameraTestUtils.SimpleImageReaderListener;
import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
+import org.junit.runners.Parameterized;
+import org.junit.runner.RunWith;
import org.junit.Test;
/**
@@ -50,6 +52,8 @@
* May not take more than a few seconds to run, to be suitable for quick
* testing.
*/
+
+@RunWith(Parameterized.class)
public class FastBasicsTest extends Camera2SurfaceViewTestCase {
private static final String TAG = "FastBasicsTest";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -62,17 +66,17 @@
@Presubmit
@Test
public void testCamera2() throws Exception {
- for (int i = 0; i < mCameraIds.length; i++) {
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
try {
- Log.i(TAG, "Testing camera2 API for camera device " + mCameraIds[i]);
+ Log.i(TAG, "Testing camera2 API for camera device " + mCameraIdsUnderTest[i]);
- if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
- Log.i(TAG, "Camera " + mCameraIds[i] +
+ if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+ Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
" does not support color outputs, skipping");
continue;
}
- openDevice(mCameraIds[i]);
+ openDevice(mCameraIdsUnderTest[i]);
camera2TestByCamera();
} finally {
closeDevice();
diff --git a/tests/camera/src/android/hardware/camera2/cts/FlashlightTest.java b/tests/camera/src/android/hardware/camera2/cts/FlashlightTest.java
index 1aa6a17..fb1bd6c 100644
--- a/tests/camera/src/android/hardware/camera2/cts/FlashlightTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/FlashlightTest.java
@@ -31,10 +31,17 @@
import java.util.concurrent.TimeUnit;
import static org.mockito.Mockito.*;
+import org.junit.runners.Parameterized;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+import static junit.framework.Assert.*;
/**
* <p>Tests for flashlight API.</p>
*/
+
+@RunWith(Parameterized.class)
public class FlashlightTest extends Camera2AndroidTestCase {
private static final String TAG = "FlashlightTest";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -45,13 +52,13 @@
private ArrayList<String> mFlashCameraIdList;
@Override
- protected void setUp() throws Exception {
+ public void setUp() throws Exception {
super.setUp();
// initialize the list of cameras that have a flash unit so it won't interfere with
// flash tests.
mFlashCameraIdList = new ArrayList<String>();
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
StaticMetadata info =
new StaticMetadata(mCameraManager.getCameraCharacteristics(id),
CheckLevel.ASSERT, /*collector*/ null);
@@ -61,6 +68,7 @@
}
}
+ @Test
public void testSetTorchModeOnOff() throws Exception {
if (mFlashCameraIdList.size() == 0)
return;
@@ -124,6 +132,7 @@
}
}
+ @Test
public void testTorchCallback() throws Exception {
testTorchCallback(/*useExecutor*/ false);
testTorchCallback(/*useExecutor*/ true);
@@ -169,12 +178,13 @@
}
}
+ @Test
public void testCameraDeviceOpenAfterTorchOn() throws Exception {
if (mFlashCameraIdList.size() == 0)
return;
for (String id : mFlashCameraIdList) {
- for (String idToOpen : mCameraIds) {
+ for (String idToOpen : mCameraIdsUnderTest) {
resetTorchModeStatus(id);
CameraManager.TorchCallback torchListener =
@@ -225,15 +235,16 @@
}
}
+ @Test
public void testTorchModeExceptions() throws Exception {
// cameraIdsToTestTorch = all available camera ID + non-existing camera id +
// non-existing numeric camera id + null
- String[] cameraIdsToTestTorch = new String[mCameraIds.length + 3];
- System.arraycopy(mCameraIds, 0, cameraIdsToTestTorch, 0, mCameraIds.length);
- cameraIdsToTestTorch[mCameraIds.length] = generateNonexistingCameraId();
- cameraIdsToTestTorch[mCameraIds.length + 1] = generateNonexistingNumericCameraId();
+ String[] cameraIdsToTestTorch = new String[mCameraIdsUnderTest.length + 3];
+ System.arraycopy(mCameraIdsUnderTest, 0, cameraIdsToTestTorch, 0, mCameraIdsUnderTest.length);
+ cameraIdsToTestTorch[mCameraIdsUnderTest.length] = generateNonexistingCameraId();
+ cameraIdsToTestTorch[mCameraIdsUnderTest.length + 1] = generateNonexistingNumericCameraId();
- for (String idToOpen : mCameraIds) {
+ for (String idToOpen : mCameraIdsUnderTest) {
openDevice(idToOpen);
try {
for (String id : cameraIdsToTestTorch) {
@@ -288,8 +299,8 @@
private String generateNonexistingCameraId() {
String nonExisting = "none_existing_camera";
- for (String id : mCameraIds) {
- if (Arrays.asList(mCameraIds).contains(nonExisting)) {
+ for (String id : mCameraIdsUnderTest) {
+ if (Arrays.asList(mCameraIdsUnderTest).contains(nonExisting)) {
nonExisting += id;
} else {
break;
@@ -299,11 +310,15 @@
}
// return a non-existing and non-negative numeric camera id.
- private String generateNonexistingNumericCameraId() {
- int[] numericCameraIds = new int[mCameraIds.length];
+ private String generateNonexistingNumericCameraId() throws Exception {
+ // We don't rely on mCameraIdsUnderTest to generate a non existing camera id since
+ // mCameraIdsUnderTest doesn't give us an accurate reflection of which camera ids actually
+ // exist. It just tells us the ones we're testing right now.
+ String[] allCameraIds = mCameraManager.getCameraIdListNoLazy();
+ int[] numericCameraIds = new int[allCameraIds.length];
int size = 0;
- for (String cameraId : mCameraIds) {
+ for (String cameraId : allCameraIds) {
try {
int value = Integer.parseInt(cameraId);
if (value >= 0) {
diff --git a/tests/camera/src/android/hardware/camera2/cts/HeifWriterTest.java b/tests/camera/src/android/hardware/camera2/cts/HeifWriterTest.java
index 0cb5c1b..f729cc7 100644
--- a/tests/camera/src/android/hardware/camera2/cts/HeifWriterTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/HeifWriterTest.java
@@ -55,6 +55,11 @@
import java.util.Arrays;
import java.util.List;
+import org.junit.runners.Parameterized;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+@RunWith(Parameterized.class)
public class HeifWriterTest extends Camera2AndroidTestCase {
private static final String TAG = HeifWriterTest.class.getSimpleName();
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -79,6 +84,7 @@
super.tearDown();
}
+ @Test
public void testHeif() throws Exception {
final int NUM_SINGLE_CAPTURE_TESTED = 3;
final int NUM_HEIC_CAPTURE_TESTED = 2;
@@ -93,7 +99,7 @@
boolean sessionFailure = false;
Integer[] sessionStates = {BlockingSessionCallback.SESSION_READY,
BlockingSessionCallback.SESSION_CONFIGURE_FAILED};
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.v(TAG, "Testing HEIF capture for Camera " + id);
openDevice(id);
diff --git a/tests/camera/src/android/hardware/camera2/cts/IdleUidTest.java b/tests/camera/src/android/hardware/camera2/cts/IdleUidTest.java
index 34d0947..9caf365 100644
--- a/tests/camera/src/android/hardware/camera2/cts/IdleUidTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/IdleUidTest.java
@@ -26,9 +26,12 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
+import android.hardware.camera2.cts.Camera2ParameterizedTestCase;
+import android.hardware.camera2.cts.CameraTestUtils;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
+
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Process;
@@ -40,6 +43,8 @@
import org.junit.AfterClass;
import org.junit.BeforeClass;
+import org.junit.runners.Parameterized;
+import org.junit.runner.RunWith;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -52,8 +57,9 @@
* get an error callback losing the camera handle. Similarly if the UID is
* already idle it cannot obtain a camera handle.
*/
-@RunWith(AndroidJUnit4.class)
-public final class IdleUidTest {
+
+@RunWith(Parameterized.class)
+public final class IdleUidTest extends Camera2ParameterizedTestCase {
private static final long CAMERA_OPERATION_TIMEOUT_MILLIS = 5000; // 5 sec
private static final HandlerThread sCallbackThread = new HandlerThread("Callback thread");
@@ -73,10 +79,8 @@
*/
@Test
public void testCameraAccessForIdleUid() throws Exception {
- final CameraManager cameraManager = InstrumentationRegistry.getTargetContext()
- .getSystemService(CameraManager.class);
- for (String cameraId : cameraManager.getCameraIdList()) {
- testCameraAccessForIdleUidByCamera(cameraManager, cameraId,
+ for (String cameraId : mCameraIdsUnderTest) {
+ testCameraAccessForIdleUidByCamera(cameraId,
new Handler(sCallbackThread.getLooper()));
}
}
@@ -86,32 +90,29 @@
*/
@Test
public void testCameraAccessBecomingInactiveUid() throws Exception {
- final CameraManager cameraManager = InstrumentationRegistry.getTargetContext()
- .getSystemService(CameraManager.class);
- for (String cameraId : cameraManager.getCameraIdList()) {
- testCameraAccessBecomingInactiveUidByCamera(cameraManager, cameraId,
+ for (String cameraId : mCameraIdsUnderTest) {
+ testCameraAccessBecomingInactiveUidByCamera(cameraId,
new Handler(sCallbackThread.getLooper()));
}
-
}
- private void testCameraAccessForIdleUidByCamera(CameraManager cameraManager,
- String cameraId, Handler handler) throws Exception {
+ private void testCameraAccessForIdleUidByCamera(String cameraId, Handler handler)
+ throws Exception {
// Can access camera from an active UID.
- assertCameraAccess(cameraManager, cameraId, true, handler);
+ assertCameraAccess(mCameraManager, cameraId, true, handler);
// Make our UID idle
makeMyPackageIdle();
try {
// Can not access camera from an idle UID.
- assertCameraAccess(cameraManager, cameraId, false, handler);
+ assertCameraAccess(mCameraManager, cameraId, false, handler);
} finally {
// Restore our UID as active
makeMyPackageActive();
}
// Can access camera from an active UID.
- assertCameraAccess(cameraManager, cameraId, true, handler);
+ assertCameraAccess(mCameraManager, cameraId, true, handler);
}
private static void assertCameraAccess(CameraManager cameraManager,
@@ -152,14 +153,14 @@
}
}
- private void testCameraAccessBecomingInactiveUidByCamera(CameraManager cameraManager,
- String cameraId, Handler handler) throws Exception {
+ private void testCameraAccessBecomingInactiveUidByCamera(String cameraId, Handler handler)
+ throws Exception {
// Mock the callback used to observe camera state.
final CameraDevice.StateCallback callback = mock(CameraDevice.StateCallback.class);
// Open the camera
try {
- cameraManager.openCamera(cameraId, callback, handler);
+ mCameraManager.openCamera(cameraId, callback, handler);
} catch (CameraAccessException e) {
fail("Unexpected exception" + e);
}
diff --git a/tests/camera/src/android/hardware/camera2/cts/ImageReaderTest.java b/tests/camera/src/android/hardware/camera2/cts/ImageReaderTest.java
index abd6bb6..971b51c 100644
--- a/tests/camera/src/android/hardware/camera2/cts/ImageReaderTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/ImageReaderTest.java
@@ -52,11 +52,17 @@
import java.util.Arrays;
import java.util.List;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.Test;
+
import static android.hardware.camera2.cts.CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS;
+import static android.hardware.camera2.cts.CameraTestUtils.SESSION_READY_TIMEOUT_MS;
import static android.hardware.camera2.cts.CameraTestUtils.SimpleCaptureCallback;
import static android.hardware.camera2.cts.CameraTestUtils.SimpleImageReaderListener;
import static android.hardware.camera2.cts.CameraTestUtils.dumpFile;
import static android.hardware.camera2.cts.CameraTestUtils.getValueNotNull;
+import static junit.framework.Assert.*;
/**
* <p>Basic test for ImageReader APIs. It uses CameraDevice as producer, camera
@@ -68,6 +74,7 @@
* <p>Some invalid access test. </p>
* <p>TODO: Add more format tests? </p>
*/
+@RunWith(Parameterized.class)
public class ImageReaderTest extends Camera2AndroidTestCase {
private static final String TAG = "ImageReaderTest";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -92,22 +99,18 @@
private SimpleImageListener mListener;
@Override
- public void setContext(Context context) {
- super.setContext(context);
- }
-
- @Override
- protected void setUp() throws Exception {
+ public void setUp() throws Exception {
super.setUp();
}
@Override
- protected void tearDown() throws Exception {
+ public void tearDown() throws Exception {
super.tearDown();
}
+ @Test
public void testFlexibleYuv() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.i(TAG, "Testing Camera " + id);
openDevice(id);
@@ -118,8 +121,9 @@
}
}
+ @Test
public void testDepth16() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.i(TAG, "Testing Camera " + id);
openDevice(id);
@@ -130,8 +134,9 @@
}
}
+ @Test
public void testDepthPointCloud() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.i(TAG, "Testing Camera " + id);
openDevice(id);
@@ -142,8 +147,9 @@
}
}
+ @Test
public void testDynamicDepth() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
openDevice(id);
bufferFormatTestByCamera(ImageFormat.DEPTH_JPEG, /*repeating*/true,
@@ -154,8 +160,9 @@
}
}
+ @Test
public void testY8() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.i(TAG, "Testing Camera " + id);
openDevice(id);
@@ -166,8 +173,9 @@
}
}
+ @Test
public void testJpeg() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.v(TAG, "Testing jpeg capture for Camera " + id);
openDevice(id);
@@ -178,8 +186,9 @@
}
}
+ @Test
public void testRaw() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.v(TAG, "Testing raw capture for camera " + id);
openDevice(id);
@@ -191,8 +200,9 @@
}
}
+ @Test
public void testRawPrivate() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.v(TAG, "Testing raw capture for camera " + id);
openDevice(id);
@@ -204,8 +214,9 @@
}
}
+ @Test
public void testHeic() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.v(TAG, "Testing heic capture for Camera " + id);
openDevice(id);
@@ -216,8 +227,9 @@
}
}
+ @Test
public void testRepeatingJpeg() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.v(TAG, "Testing repeating jpeg capture for Camera " + id);
openDevice(id);
@@ -228,8 +240,9 @@
}
}
+ @Test
public void testRepeatingRaw() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.v(TAG, "Testing repeating raw capture for camera " + id);
openDevice(id);
@@ -241,8 +254,9 @@
}
}
+ @Test
public void testRepeatingRawPrivate() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.v(TAG, "Testing repeating raw capture for camera " + id);
openDevice(id);
@@ -254,8 +268,9 @@
}
}
+ @Test
public void testRepeatingHeic() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.v(TAG, "Testing repeating heic capture for Camera " + id);
openDevice(id);
@@ -266,8 +281,9 @@
}
}
+ @Test
public void testLongProcessingRepeatingRaw() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.v(TAG, "Testing long processing on repeating raw for camera " + id);
@@ -284,8 +300,9 @@
}
}
+ @Test
public void testLongProcessingRepeatingFlexibleYuv() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.v(TAG, "Testing long processing on repeating YUV for camera " + id);
@@ -309,9 +326,10 @@
* for camera case. For if the produced image byte buffer is not direct byte buffer, there
* is no guarantee to get an ISE for this invalid access case.
*/
+ @Test
public void testInvalidAccessTest() throws Exception {
// Test byte buffer access after an image is released, it should throw ISE.
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.v(TAG, "Testing invalid image access for Camera " + id);
openDevice(id);
@@ -328,8 +346,9 @@
*
* <p>Both stream formats are mandatory for Camera2 API</p>
*/
+ @Test
public void testYuvAndJpeg() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.v(TAG, "YUV and JPEG testing for camera " + id);
if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
@@ -351,8 +370,9 @@
*
* <p>Both stream formats are mandatory for Camera2 API</p>
*/
+ @Test
public void testYuvAndJpegWithUsageFlag() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.v(TAG, "YUV and JPEG testing for camera " + id);
if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
@@ -372,8 +392,9 @@
* Test two image stream (YUV420_888 and RAW_SENSOR) capture by using ImageReader.
*
*/
+ @Test
public void testImageReaderYuvAndRaw() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.v(TAG, "YUV and RAW testing for camera " + id);
if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
@@ -394,8 +415,9 @@
* ImageFormat.PRIVATE + PROTECTED usage capture by using ImageReader with the
* ImageReader factory method that has usage flag argument, and uses a custom usage flag.
*/
+ @Test
public void testImageReaderPrivateWithProtectedUsageFlag() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.v(TAG, "Private format and protected usage testing for camera " + id);
if (!mAllStaticInfo.get(id).isCapabilitySupported(
@@ -420,8 +442,9 @@
* ImageReader factory method that has usage flag argument.
*
*/
+ @Test
public void testImageReaderYuvAndRawWithUsageFlag() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.v(TAG, "YUV and RAW testing for camera " + id);
if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
@@ -441,10 +464,11 @@
* Check that the center patches for YUV and JPEG outputs for the same frame match for each YUV
* resolution and format supported.
*/
+ @Test
public void testAllOutputYUVResolutions() throws Exception {
Integer[] sessionStates = {BlockingSessionCallback.SESSION_READY,
BlockingSessionCallback.SESSION_CONFIGURE_FAILED};
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.v(TAG, "Testing all YUV image resolutions for camera " + id);
@@ -685,10 +709,11 @@
/**
* Test that images captured after discarding free buffers are valid.
*/
+ @Test
public void testDiscardFreeBuffers() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
- Log.v(TAG, "Testing jpeg capture for Camera " + id);
+ Log.v(TAG, "Testing discardFreeBuffers for Camera " + id);
openDevice(id);
discardFreeBuffersTestByCamera();
} finally {
@@ -698,6 +723,7 @@
}
/** Tests that usage bits are preserved */
+ @Test
public void testUsageRespected() throws Exception {
ImageReader reader = ImageReader.newInstance(1, 1, PixelFormat.RGBA_8888, 1,
HardwareBuffer.USAGE_GPU_COLOR_OUTPUT | HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
@@ -964,13 +990,13 @@
final Size SIZE = mStaticInfo.getAvailableSizesForFormatChecked(FORMAT,
StaticMetadata.StreamDirection.Output)[0];
- Image img = null;
// Create ImageReader.
mListener = new SimpleImageListener();
createDefaultImageReader(SIZE, FORMAT, MAX_NUM_IMAGES, mListener);
// Start capture.
final boolean REPEATING = true;
+ final boolean SINGLE = false;
CaptureRequest request = prepareCaptureRequest();
SimpleCaptureCallback listener = new SimpleCaptureCallback();
startCapture(request, REPEATING, listener, mHandler);
@@ -985,6 +1011,23 @@
// Validate images and capture resulst again.
validateImage(SIZE, FORMAT, NUM_FRAME_VERIFIED, REPEATING);
validateCaptureResult(FORMAT, SIZE, listener, NUM_FRAME_VERIFIED);
+
+ // Stop repeating request in preparation for discardFreeBuffers
+ mCameraSession.stopRepeating();
+ mCameraSessionListener.getStateWaiter().waitForState(
+ BlockingSessionCallback.SESSION_READY, SESSION_READY_TIMEOUT_MS);
+
+ // Drain the reader queue and discard free buffers from the reader.
+ Image img = mReader.acquireLatestImage();
+ if (img != null) {
+ img.close();
+ }
+ mReader.discardFreeBuffers();
+
+ // Do a single capture for camera device to reallocate buffers
+ mListener.reset();
+ startCapture(request, SINGLE, listener, mHandler);
+ validateImage(SIZE, FORMAT, /*captureCount*/1, SINGLE);
}
private void bufferFormatTestByCamera(int format, boolean repeating) throws Exception {
@@ -1212,6 +1255,10 @@
image.close();
}
}
+
+ public void reset() {
+ imageAvailable.close();
+ }
}
private void validateImage(Size sz, int format, int captureCount, boolean repeating)
diff --git a/tests/camera/src/android/hardware/camera2/cts/ImageWriterTest.java b/tests/camera/src/android/hardware/camera2/cts/ImageWriterTest.java
index 38f8816..e5cbf5e 100644
--- a/tests/camera/src/android/hardware/camera2/cts/ImageWriterTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/ImageWriterTest.java
@@ -17,8 +17,10 @@
package android.hardware.camera2.cts;
import static android.hardware.camera2.cts.CameraTestUtils.*;
+import static junit.framework.Assert.*;
import android.graphics.ImageFormat;
+import android.graphics.SurfaceTexture;
import android.hardware.camera2.cts.CameraTestUtils.SimpleCaptureCallback;
import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
import android.hardware.camera2.CameraDevice;
@@ -38,6 +40,10 @@
import java.util.Arrays;
import java.util.List;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.Test;
+
/**
* <p>
* Basic test for ImageWriter APIs. ImageWriter takes the images produced by
@@ -45,6 +51,7 @@
* interface or ImageReader.
* </p>
*/
+@RunWith(Parameterized.class)
public class ImageWriterTest extends Camera2AndroidTestCase {
private static final String TAG = "ImageWriterTest";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -56,7 +63,7 @@
private ImageWriter mWriter;
@Override
- protected void tearDown() throws Exception {
+ public void tearDown() throws Exception {
try {
closeImageReader(mReaderForWriter);
} finally {
@@ -88,8 +95,9 @@
* interface.</li>
* </p>
*/
+ @Test
public void testYuvImageWriterReaderOperation() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.i(TAG, "Testing Camera " + id);
if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
@@ -110,8 +118,9 @@
* factory method of ImageReader and ImageWriter.
* </p>
*/
+ @Test
public void testYuvImageWriterReaderOperationAlt() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.i(TAG, "Testing Camera " + id);
if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
@@ -126,6 +135,7 @@
}
}
+ @Test
public void testAbandonedSurfaceExceptions() throws Exception {
final int READER_WIDTH = 1920;
final int READER_HEIGHT = 1080;
@@ -170,6 +180,24 @@
}
}
+ @Test
+ public void testWriterFormatOverride() throws Exception {
+ int[] TEXTURE_TEST_FORMATS = {ImageFormat.YV12, ImageFormat.YUV_420_888};
+ SurfaceTexture texture = new SurfaceTexture(/*random int*/1);
+ texture.setDefaultBufferSize(640, 480);
+ Surface surface = new Surface(texture);
+
+ for (int format : TEXTURE_TEST_FORMATS) {
+ // Override default buffer format of Surface texture to test format
+ ImageWriter writer = ImageWriter.newInstance(surface, MAX_NUM_IMAGES, format);
+ Image image = writer.dequeueInputImage();
+ Log.i(TAG, "testing format " + format + ", got input image format " +
+ image.getFormat());
+ assertTrue(image.getFormat() == format);
+ writer.close();
+ }
+ }
+
private void readerWriterFormatTestByCamera(int format, boolean altFactoryMethod)
throws Exception {
List<Size> sizes = getSortedSizesForFormat(mCamera.getId(), mCameraManager, format, null);
diff --git a/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java b/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java
index 37dfbdd..27e6492 100644
--- a/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java
@@ -66,6 +66,8 @@
import java.util.Map;
import java.util.Set;
+import org.junit.runners.Parameterized;
+import org.junit.runner.RunWith;
import org.junit.Test;
import static org.mockito.Mockito.*;
@@ -73,6 +75,7 @@
/**
* Tests exercising logical camera setup, configuration, and usage.
*/
+@RunWith(Parameterized.class)
public final class LogicalCameraDeviceTest extends Camera2SurfaceViewTestCase {
private static final String TAG = "LogicalCameraDeviceTest";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -107,7 +110,7 @@
*/
@Test
public void testInvalidPhysicalCameraIdInOutputConfiguration() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.i(TAG, "Testing Camera " + id);
if (mAllStaticInfo.get(id).isHardwareLevelLegacy()) {
@@ -167,7 +170,7 @@
@Test
public void testBasicPhysicalStreaming() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.i(TAG, "Testing Camera " + id);
@@ -209,7 +212,7 @@
@Test
public void testBasicLogicalPhysicalStreamCombination() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.i(TAG, "Testing Camera " + id);
@@ -331,7 +334,7 @@
@Test
public void testBasicPhysicalRequests() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.i(TAG, "Testing Camera " + id);
@@ -462,7 +465,7 @@
@Test
public void testInvalidPhysicalCameraRequests() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.i(TAG, "Testing Camera " + id);
@@ -569,7 +572,7 @@
@Test
public void testLogicalCameraZoomSwitch() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.i(TAG, "Testing Camera " + id);
@@ -708,7 +711,7 @@
*/
@Test
public void testActivePhysicalId() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.i(TAG, "Testing Camera " + id);
@@ -792,7 +795,7 @@
if (!isHandheldDevice()) {
return;
}
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.i(TAG, "Testing Camera " + id);
@@ -1109,8 +1112,12 @@
Map<String, CaptureResult> physicalResultsDual =
totalCaptureResultDual.getPhysicalCameraResults();
for (String physicalId : physicalCameraIds) {
- physicalTimestamps[index][i] = physicalResultsDual.get(physicalId).get(
- CaptureResult.SENSOR_TIMESTAMP);
+ if (physicalResultsDual.containsKey(physicalId)) {
+ physicalTimestamps[index][i] = physicalResultsDual.get(physicalId).get(
+ CaptureResult.SENSOR_TIMESTAMP);
+ } else {
+ physicalTimestamps[index][i] = -1;
+ }
index++;
}
}
@@ -1129,15 +1136,19 @@
}
for (int i = 0; i < physicalCameraIds.size(); i++) {
for (int j = 0 ; j < NUM_FRAMES_CHECKED-1; j++) {
- assertTrue("Physical camera timestamp must monolithically increase",
- physicalTimestamps[i][j] < physicalTimestamps[i][j+1]);
- if (j > 0) {
+ if (physicalTimestamps[i][j] != -1 && physicalTimestamps[i][j+1] != -1) {
+ assertTrue("Physical camera timestamp must monolithically increase",
+ physicalTimestamps[i][j] < physicalTimestamps[i][j+1]);
+ }
+ if (j > 0 && physicalTimestamps[i][j] != -1) {
assertTrue("Physical camera's timestamp N must be greater than logical " +
"camera's timestamp N-1",
physicalTimestamps[i][j] > logicalTimestamps[j-1]);
}
- assertTrue("Physical camera's timestamp N must be less than logical camera's " +
- "timestamp N+1", physicalTimestamps[i][j] > logicalTimestamps[j+1]);
+ if (physicalTimestamps[i][j] != -1) {
+ assertTrue("Physical camera's timestamp N must be less than logical camera's " +
+ "timestamp N+1", physicalTimestamps[i][j] > logicalTimestamps[j+1]);
+ }
}
}
double logicalAvgDurationMs2 = (logicalTimestamps2[NUM_FRAMES_CHECKED-1] -
diff --git a/tests/camera/src/android/hardware/camera2/cts/MultiViewTest.java b/tests/camera/src/android/hardware/camera2/cts/MultiViewTest.java
index 5c1f11e..fd081b3 100644
--- a/tests/camera/src/android/hardware/camera2/cts/MultiViewTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/MultiViewTest.java
@@ -50,11 +50,15 @@
import java.util.Arrays;
import java.util.List;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
import org.junit.Test;
/**
* CameraDevice test by using combination of SurfaceView, TextureView and ImageReader
*/
+
+@RunWith(Parameterized.class)
public class MultiViewTest extends Camera2MultiViewTestCase {
private static final String TAG = "MultiViewTest";
private final static long WAIT_FOR_COMMAND_TO_COMPLETE = 5000; //ms
@@ -67,7 +71,7 @@
@Test
public void testTextureViewPreview() throws Exception {
- for (String cameraId : mCameraIds) {
+ for (String cameraId : mCameraIdsUnderTest) {
Exception prior = null;
try {
@@ -96,7 +100,7 @@
@Test
public void testTextureViewPreviewWithImageReader() throws Exception {
- for (String cameraId : mCameraIds) {
+ for (String cameraId : mCameraIdsUnderTest) {
Exception prior = null;
ImageVerifierListener yuvListener;
@@ -144,7 +148,7 @@
@Test
public void testDualTextureViewPreview() throws Exception {
- for (String cameraId : mCameraIds) {
+ for (String cameraId : mCameraIdsUnderTest) {
Exception prior = null;
try {
openCamera(cameraId);
@@ -177,7 +181,7 @@
@Test
public void testDualTextureViewAndImageReaderPreview() throws Exception {
- for (String cameraId : mCameraIds) {
+ for (String cameraId : mCameraIdsUnderTest) {
Exception prior = null;
ImageVerifierListener yuvListener;
@@ -223,31 +227,31 @@
@Test
public void testDualCameraPreview() throws Exception {
final int NUM_CAMERAS_TESTED = 2;
- if (mCameraIds.length < NUM_CAMERAS_TESTED) {
+ if (mCameraIdsUnderTest.length < NUM_CAMERAS_TESTED) {
return;
}
try {
for (int i = 0; i < NUM_CAMERAS_TESTED; i++) {
- openCamera(mCameraIds[i]);
- if (!getStaticInfo(mCameraIds[i]).isColorOutputSupported()) {
- Log.i(TAG, "Camera " + mCameraIds[i] +
+ openCamera(mCameraIdsUnderTest[i]);
+ if (!getStaticInfo(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+ Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
" does not support color outputs, skipping");
continue;
}
List<TextureView> views = Arrays.asList(mTextureView[i]);
- startTextureViewPreview(mCameraIds[i], views, /*ImageReader*/null);
+ startTextureViewPreview(mCameraIdsUnderTest[i], views, /*ImageReader*/null);
}
// TODO: check the framerate is correct
SystemClock.sleep(PREVIEW_TIME_MS);
for (int i = 0; i < NUM_CAMERAS_TESTED; i++) {
- if (!getStaticInfo(mCameraIds[i]).isColorOutputSupported()) {
- Log.i(TAG, "Camera " + mCameraIds[i] +
+ if (!getStaticInfo(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+ Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
" does not support color outputs, skipping");
continue;
}
- stopPreview(mCameraIds[i]);
+ stopPreview(mCameraIdsUnderTest[i]);
}
} catch (BlockingOpenException e) {
// The only error accepted is ERROR_MAX_CAMERAS_IN_USE, which means HAL doesn't support
@@ -257,7 +261,7 @@
Log.i(TAG, "Camera HAL does not support dual camera preview. Skip the test");
} finally {
for (int i = 0; i < NUM_CAMERAS_TESTED; i++) {
- closeCamera(mCameraIds[i]);
+ closeCamera(mCameraIdsUnderTest[i]);
}
}
}
@@ -267,7 +271,7 @@
*/
@Test
public void testSharedSurfaceBasic() throws Exception {
- for (String cameraId : mCameraIds) {
+ for (String cameraId : mCameraIdsUnderTest) {
try {
openCamera(cameraId);
if (getStaticInfo(cameraId).isHardwareLevelLegacy()) {
@@ -409,7 +413,7 @@
*/
@Test
public void testSharedSurfaceImageReaderSwitch() throws Exception {
- for (String cameraId : mCameraIds) {
+ for (String cameraId : mCameraIdsUnderTest) {
try {
openCamera(cameraId);
if (getStaticInfo(cameraId).isHardwareLevelLegacy()) {
@@ -501,7 +505,7 @@
int YUVFormats[] = {ImageFormat.YUV_420_888, ImageFormat.YUV_422_888,
ImageFormat.YUV_444_888, ImageFormat.YUY2, ImageFormat.YV12,
ImageFormat.NV16, ImageFormat.NV21};
- for (String cameraId : mCameraIds) {
+ for (String cameraId : mCameraIdsUnderTest) {
try {
openCamera(cameraId);
if (getStaticInfo(cameraId).isHardwareLevelLegacy()) {
@@ -629,7 +633,7 @@
*/
@Test
public void testSharedSurfaceLimit() throws Exception {
- for (String cameraId : mCameraIds) {
+ for (String cameraId : mCameraIdsUnderTest) {
try {
openCamera(cameraId);
if (getStaticInfo(cameraId).isHardwareLevelLegacy()) {
@@ -747,7 +751,7 @@
*/
@Test(timeout=60*60*1000) // timeout = 60 mins for long running tests
public void testSharedSurfaceSwitch() throws Exception {
- for (String cameraId : mCameraIds) {
+ for (String cameraId : mCameraIdsUnderTest) {
try {
openCamera(cameraId);
if (getStaticInfo(cameraId).isHardwareLevelLegacy()) {
@@ -848,7 +852,7 @@
*/
@Test
public void testTextureImageWriterReaderOperation() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
ImageReader reader = null;
ImageWriter writer = null;
Surface writerOutput = null;
@@ -1037,7 +1041,7 @@
*/
@Test
public void testSharedSurfaces() throws Exception {
- for (String cameraId : mCameraIds) {
+ for (String cameraId : mCameraIdsUnderTest) {
try {
openCamera(cameraId);
if (getStaticInfo(cameraId).isHardwareLevelLegacy()) {
diff --git a/tests/camera/src/android/hardware/camera2/cts/NativeCameraDeviceTest.java b/tests/camera/src/android/hardware/camera2/cts/NativeCameraDeviceTest.java
index 291d998..207c3e3 100644
--- a/tests/camera/src/android/hardware/camera2/cts/NativeCameraDeviceTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/NativeCameraDeviceTest.java
@@ -22,14 +22,17 @@
import android.util.Size;
import android.view.Surface;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
import org.junit.Test;
import static org.junit.Assert.assertTrue;
-
/**
* <p>Basic test for CameraManager class.</p>
*/
+
+@RunWith(Parameterized.class)
public class NativeCameraDeviceTest extends Camera2SurfaceViewTestCase {
private static final String TAG = "NativeCameraDeviceTest";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
diff --git a/tests/camera/src/android/hardware/camera2/cts/NativeCameraManagerTest.java b/tests/camera/src/android/hardware/camera2/cts/NativeCameraManagerTest.java
index 08e0363..0ed5f0e 100644
--- a/tests/camera/src/android/hardware/camera2/cts/NativeCameraManagerTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/NativeCameraManagerTest.java
@@ -16,13 +16,21 @@
package android.hardware.camera2.cts;
-import android.test.AndroidTestCase;
+import android.hardware.cts.helpers.CameraParameterizedTestCase;
import android.util.Log;
+import org.junit.runners.Parameterized;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+import static junit.framework.Assert.*;
+
/**
* <p>Basic test for CameraManager class.</p>
*/
-public class NativeCameraManagerTest extends AndroidTestCase {
+
+@RunWith(Parameterized.class)
+public class NativeCameraManagerTest extends CameraParameterizedTestCase {
private static final String TAG = "NativeCameraManagerTest";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -33,21 +41,25 @@
Log.i("NativeCameraManagerTest", "after loadlibrary");
}
+ @Test
public void testCameraManagerGetAndClose() {
assertTrue("testCameraManagerGetAndClose fail, see log for details",
testCameraManagerGetAndCloseNative());
}
+ @Test
public void testCameraManagerGetCameraIds() {
assertTrue("testCameraManagerGetCameraIds fail, see log for details",
testCameraManagerGetCameraIdsNative());
}
+ @Test
public void testCameraManagerAvailabilityCallback() {
assertTrue("testCameraManagerAvailabilityCallback fail, see log for details",
testCameraManagerAvailabilityCallbackNative());
}
+ @Test
public void testCameraManagerCameraCharacteristics() {
assertTrue("testCameraManagerCameraCharacteristics fail, see log for details",
testCameraManagerCharacteristicsNative());
diff --git a/tests/camera/src/android/hardware/camera2/cts/NativeImageReaderTest.java b/tests/camera/src/android/hardware/camera2/cts/NativeImageReaderTest.java
index 0933363..493e670 100644
--- a/tests/camera/src/android/hardware/camera2/cts/NativeImageReaderTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/NativeImageReaderTest.java
@@ -19,9 +19,16 @@
import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
import android.util.Log;
+import org.junit.Test;
+
+import static junit.framework.Assert.*;
+
/**
* <p>Basic test for CameraManager class.</p>
*/
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+@RunWith(Parameterized.class)
public class NativeImageReaderTest extends Camera2AndroidTestCase {
private static final String TAG = "NativeImageReaderTest";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -33,26 +40,31 @@
Log.i("NativeImageReaderTest", "after loadlibrary");
}
+ @Test
public void testJpeg() {
assertTrue("testJpeg fail, see log for details",
testJpegNative(mDebugFileNameBase));
}
+ @Test
public void testY8() {
assertTrue("testY8 fail, see log for details",
testY8Native(mDebugFileNameBase));
}
+ @Test
public void testHeic() {
assertTrue("testHeic fail, see log for details",
testHeicNative(mDebugFileNameBase));
}
+ @Test
public void testDepthJpeg() {
assertTrue("testDepthJpeg fail, see log for details",
testDepthJpegNative(mDebugFileNameBase));
}
+ @Test
public void testImageReaderCloseAcquiredImages() {
assertTrue("testImageReaderClose fail, see log for details",
testImageReaderCloseAcquiredImagesNative());
diff --git a/tests/camera/src/android/hardware/camera2/cts/NativeStillCaptureTest.java b/tests/camera/src/android/hardware/camera2/cts/NativeStillCaptureTest.java
index a892970..a778f7d 100644
--- a/tests/camera/src/android/hardware/camera2/cts/NativeStillCaptureTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/NativeStillCaptureTest.java
@@ -21,6 +21,8 @@
import android.util.Size;
import android.view.Surface;
+import org.junit.runners.Parameterized;
+import org.junit.runner.RunWith;
import org.junit.Test;
import static org.junit.Assert.assertTrue;
@@ -28,6 +30,8 @@
/**
* <p>Basic test for CameraManager class.</p>
*/
+
+@RunWith(Parameterized.class)
public class NativeStillCaptureTest extends Camera2SurfaceViewTestCase {
private static final String TAG = "NativeStillCaptureTest";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
diff --git a/tests/camera/src/android/hardware/camera2/cts/PerformanceTest.java b/tests/camera/src/android/hardware/camera2/cts/PerformanceTest.java
index 2d6cb0a..0616433 100644
--- a/tests/camera/src/android/hardware/camera2/cts/PerformanceTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/PerformanceTest.java
@@ -36,7 +36,7 @@
import android.hardware.camera2.cts.CameraTestUtils.SimpleImageReaderListener;
import android.hardware.camera2.cts.helpers.StaticMetadata;
import android.hardware.camera2.cts.helpers.StaticMetadata.CheckLevel;
-import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
+import android.hardware.camera2.cts.testcases.Camera2AndroidBasicTestCase;
import android.hardware.camera2.params.InputConfiguration;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
@@ -71,7 +71,7 @@
* Test camera2 API use case performance KPIs, such as camera open time, session creation time,
* shutter lag etc. The KPI data will be reported in cts results.
*/
-public class PerformanceTest extends Camera2AndroidTestCase {
+public class PerformanceTest extends Camera2AndroidBasicTestCase {
private static final String TAG = "PerformanceTest";
private static final String REPORT_LOG_NAME = "CtsCameraTestCases";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -133,10 +133,10 @@
* </p>
*/
public void testCameraLaunch() throws Exception {
- double[] avgCameraLaunchTimes = new double[mCameraIds.length];
+ double[] avgCameraLaunchTimes = new double[mCameraIdsUnderTest.length];
int counter = 0;
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
// Do NOT move these variables to outer scope
// They will be passed to DeviceReportLog and their references will be stored
String streamName = "test_camera_launch";
@@ -259,7 +259,7 @@
+ ". Max(ms): " + Stat.getMax(cameraLaunchTimes));
}
}
- if (mCameraIds.length != 0) {
+ if (mCameraIdsUnderTest.length != 0) {
String streamName = "test_camera_launch_average";
mReportLog = new DeviceReportLog(REPORT_LOG_NAME, streamName);
mReportLog.setSummary("camera_launch_average_time_for_all_cameras",
@@ -309,10 +309,10 @@
private void testSingleCaptureForFormat(int[] formats, String formatDescription,
boolean addPreviewDelay) throws Exception {
- double[] avgResultTimes = new double[mCameraIds.length];
+ double[] avgResultTimes = new double[mCameraIdsUnderTest.length];
int counter = 0;
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
// Do NOT move these variables to outer scope
// They will be passed to DeviceReportLog and their references will be stored
String streamName = appendFormatDescription("test_single_capture", formatDescription);
@@ -448,7 +448,7 @@
}
// Result will not be reported in CTS report if no summary is printed.
- if (mCameraIds.length != 0) {
+ if (mCameraIdsUnderTest.length != 0) {
String streamName = appendFormatDescription("test_single_capture_average",
formatDescription);
mReportLog = new DeviceReportLog(REPORT_LOG_NAME, streamName);
@@ -470,8 +470,8 @@
* </p>
*/
public void testMultipleCapture() throws Exception {
- double[] avgResultTimes = new double[mCameraIds.length];
- double[] avgDurationMs = new double[mCameraIds.length];
+ double[] avgResultTimes = new double[mCameraIdsUnderTest.length];
+ double[] avgDurationMs = new double[mCameraIdsUnderTest.length];
// A simple CaptureSession StateCallback to handle onCaptureQueueEmpty
class MultipleCaptureStateCallback extends CameraCaptureSession.StateCallback {
@@ -520,7 +520,7 @@
final MultipleCaptureStateCallback sessionListener = new MultipleCaptureStateCallback();
int counter = 0;
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
// Do NOT move these variables to outer scope
// They will be passed to DeviceReportLog and their references will be stored
String streamName = "test_multiple_capture";
@@ -657,7 +657,7 @@
}
// Result will not be reported in CTS report if no summary is printed.
- if (mCameraIds.length != 0) {
+ if (mCameraIdsUnderTest.length != 0) {
String streamName = "test_multiple_capture_average";
mReportLog = new DeviceReportLog(REPORT_LOG_NAME, streamName);
mReportLog.setSummary("camera_multiple_capture_result_average_latency_for_all_cameras",
@@ -675,7 +675,7 @@
* a reprocess request is issued to the time the reprocess image is returned.
*/
public void testReprocessingLatency() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
for (int format : REPROCESS_FORMATS) {
if (!isReprocessSupported(id, format)) {
continue;
@@ -705,7 +705,7 @@
*
*/
public void testReprocessingThroughput() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
for (int format : REPROCESS_FORMATS) {
if (!isReprocessSupported(id, format)) {
continue;
@@ -734,7 +734,7 @@
* time a reprocess request is issued to the time the reprocess image is returned.
*/
public void testHighQualityReprocessingLatency() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
for (int format : REPROCESS_FORMATS) {
if (!isReprocessSupported(id, format)) {
continue;
@@ -764,7 +764,7 @@
*
*/
public void testHighQualityReprocessingThroughput() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
for (int format : REPROCESS_FORMATS) {
if (!isReprocessSupported(id, format)) {
continue;
@@ -792,7 +792,7 @@
* Testing reprocessing caused preview stall (frame drops)
*/
public void testReprocessingCaptureStall() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
for (int format : REPROCESS_FORMATS) {
if (!isReprocessSupported(id, format)) {
continue;
diff --git a/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java b/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java
index c61071e..30d1943 100644
--- a/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java
@@ -55,6 +55,8 @@
import junit.framework.AssertionFailedError;
+import org.junit.runners.Parameterized;
+import org.junit.runner.RunWith;
import org.junit.Test;
import java.io.File;
@@ -69,6 +71,7 @@
* MediaCodec.
*/
@LargeTest
+@RunWith(Parameterized.class)
public class RecordingTest extends Camera2SurfaceViewTestCase {
private static final String TAG = "RecordingTest";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -131,25 +134,25 @@
private void doBasicRecording(boolean useVideoStab, boolean useIntermediateSurface)
throws Exception {
- for (int i = 0; i < mCameraIds.length; i++) {
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
try {
- Log.i(TAG, "Testing basic recording for camera " + mCameraIds[i]);
- StaticMetadata staticInfo = mAllStaticInfo.get(mCameraIds[i]);
+ Log.i(TAG, "Testing basic recording for camera " + mCameraIdsUnderTest[i]);
+ StaticMetadata staticInfo = mAllStaticInfo.get(mCameraIdsUnderTest[i]);
if (!staticInfo.isColorOutputSupported()) {
- Log.i(TAG, "Camera " + mCameraIds[i] +
+ Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
" does not support color outputs, skipping");
continue;
}
// External camera doesn't support CamcorderProfile recording
if (staticInfo.isExternalCamera()) {
- Log.i(TAG, "Camera " + mCameraIds[i] +
+ Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
" does not support CamcorderProfile, skipping");
continue;
}
if (!staticInfo.isVideoStabilizationSupported() && useVideoStab) {
- Log.i(TAG, "Camera " + mCameraIds[i] +
+ Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
" does not support video stabilization, skipping the stabilization"
+ " test");
continue;
@@ -157,8 +160,8 @@
// Re-use the MediaRecorder object for the same camera device.
mMediaRecorder = new MediaRecorder();
- openDevice(mCameraIds[i]);
- initSupportedVideoSize(mCameraIds[i]);
+ openDevice(mCameraIdsUnderTest[i]);
+ initSupportedVideoSize(mCameraIdsUnderTest[i]);
basicRecordingTestByCamera(mCamcorderProfileList, useVideoStab,
useIntermediateSurface);
@@ -256,19 +259,19 @@
*/
@Test(timeout=60*60*1000) // timeout = 60 mins for long running tests
public void testSupportedVideoSizes() throws Exception {
- for (int i = 0; i < mCameraIds.length; i++) {
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
try {
- Log.i(TAG, "Testing supported video size recording for camera " + mCameraIds[i]);
- if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
- Log.i(TAG, "Camera " + mCameraIds[i] +
+ Log.i(TAG, "Testing supported video size recording for camera " + mCameraIdsUnderTest[i]);
+ if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+ Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
" does not support color outputs, skipping");
continue;
}
// Re-use the MediaRecorder object for the same camera device.
mMediaRecorder = new MediaRecorder();
- openDevice(mCameraIds[i]);
+ openDevice(mCameraIdsUnderTest[i]);
- initSupportedVideoSize(mCameraIds[i]);
+ initSupportedVideoSize(mCameraIdsUnderTest[i]);
recordingSizeTestByCamera();
} finally {
@@ -357,7 +360,7 @@
@Test
public void testAbandonedHighSpeedRequest() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.i(TAG, "Testing bad suface for createHighSpeedRequestList for camera " + id);
StaticMetadata staticInfo = mAllStaticInfo.get(id);
@@ -479,25 +482,25 @@
*/
@Test
public void testRecordingFramerateLowToHigh() throws Exception {
- for (int i = 0; i < mCameraIds.length; i++) {
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
try {
- Log.i(TAG, "Testing basic recording for camera " + mCameraIds[i]);
- StaticMetadata staticInfo = mAllStaticInfo.get(mCameraIds[i]);
+ Log.i(TAG, "Testing recording framerate low to high for camera " + mCameraIdsUnderTest[i]);
+ StaticMetadata staticInfo = mAllStaticInfo.get(mCameraIdsUnderTest[i]);
if (!staticInfo.isColorOutputSupported()) {
- Log.i(TAG, "Camera " + mCameraIds[i] +
+ Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
" does not support color outputs, skipping");
continue;
}
if (staticInfo.isExternalCamera()) {
- Log.i(TAG, "Camera " + mCameraIds[i] +
+ Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
" does not support CamcorderProfile, skipping");
continue;
}
// Re-use the MediaRecorder object for the same camera device.
mMediaRecorder = new MediaRecorder();
- openDevice(mCameraIds[i]);
+ openDevice(mCameraIdsUnderTest[i]);
- initSupportedVideoSize(mCameraIds[i]);
+ initSupportedVideoSize(mCameraIdsUnderTest[i]);
int minFpsProfileId = -1, minFps = 1000;
int maxFpsProfileId = -1, maxFps = 0;
@@ -534,23 +537,23 @@
*/
@Test
public void testVideoPreviewSurfaceSharing() throws Exception {
- for (int i = 0; i < mCameraIds.length; i++) {
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
try {
- StaticMetadata staticInfo = mAllStaticInfo.get(mCameraIds[i]);
+ StaticMetadata staticInfo = mAllStaticInfo.get(mCameraIdsUnderTest[i]);
if (staticInfo.isHardwareLevelLegacy()) {
- Log.i(TAG, "Camera " + mCameraIds[i] + " is legacy, skipping");
+ Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] + " is legacy, skipping");
continue;
}
if (!staticInfo.isColorOutputSupported()) {
- Log.i(TAG, "Camera " + mCameraIds[i] +
+ Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
" does not support color outputs, skipping");
continue;
}
// Re-use the MediaRecorder object for the same camera device.
mMediaRecorder = new MediaRecorder();
- openDevice(mCameraIds[i]);
+ openDevice(mCameraIdsUnderTest[i]);
- initSupportedVideoSize(mCameraIds[i]);
+ initSupportedVideoSize(mCameraIdsUnderTest[i]);
videoPreviewSurfaceSharingTestByCamera();
} finally {
@@ -561,6 +564,128 @@
}
/**
+ * <p>
+ * Test recording with same recording surface and different preview surfaces.
+ * </p>
+ * <p>
+ * This test maintains persistent video surface while changing preview surface.
+ * This exercises format/dataspace override behavior of the camera device.
+ * </p>
+ */
+ @Test
+ public void testRecordingWithDifferentPreviewSizes() throws Exception {
+ if (!MediaUtils.checkCodecForDomain(true /* encoder */, "video")) {
+ return; // skipped
+ }
+ mPersistentSurface = MediaCodec.createPersistentInputSurface();
+ assertNotNull("Failed to create persistent input surface!", mPersistentSurface);
+
+ try {
+ doRecordingWithDifferentPreviewSizes();
+ } finally {
+ mPersistentSurface.release();
+ mPersistentSurface = null;
+ }
+ }
+
+ public void doRecordingWithDifferentPreviewSizes() throws Exception {
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
+ try {
+ Log.i(TAG, "Testing recording with different preview sizes for camera " +
+ mCameraIdsUnderTest[i]);
+ StaticMetadata staticInfo = mAllStaticInfo.get(mCameraIdsUnderTest[i]);
+ if (!staticInfo.isColorOutputSupported()) {
+ Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
+ " does not support color outputs, skipping");
+ continue;
+ }
+ if (staticInfo.isExternalCamera()) {
+ Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
+ " does not support CamcorderProfile, skipping");
+ continue;
+ }
+ // Re-use the MediaRecorder object for the same camera device.
+ mMediaRecorder = new MediaRecorder();
+ openDevice(mCameraIdsUnderTest[i]);
+
+ initSupportedVideoSize(mCameraIdsUnderTest[i]);
+
+ Size maxPreviewSize = mOrderedPreviewSizes.get(0);
+ List<Range<Integer> > fpsRanges = Arrays.asList(
+ mStaticInfo.getAeAvailableTargetFpsRangesChecked());
+ int cameraId = Integer.valueOf(mCamera.getId());
+ int maxVideoFrameRate = -1;
+ for (int profileId : mCamcorderProfileList) {
+ if (!CamcorderProfile.hasProfile(cameraId, profileId)) {
+ continue;
+ }
+ CamcorderProfile profile = CamcorderProfile.get(cameraId, profileId);
+
+ Size videoSz = new Size(profile.videoFrameWidth, profile.videoFrameHeight);
+ Range<Integer> fpsRange = new Range(
+ profile.videoFrameRate, profile.videoFrameRate);
+ if (maxVideoFrameRate < profile.videoFrameRate) {
+ maxVideoFrameRate = profile.videoFrameRate;
+ }
+
+ if (allowedUnsupported(cameraId, profileId)) {
+ continue;
+ }
+
+ if (mStaticInfo.isHardwareLevelLegacy() &&
+ (videoSz.getWidth() > maxPreviewSize.getWidth() ||
+ videoSz.getHeight() > maxPreviewSize.getHeight())) {
+ // Skip. Legacy mode can only do recording up to max preview size
+ continue;
+ }
+ assertTrue("Video size " + videoSz.toString() + " for profile ID " + profileId +
+ " must be one of the camera device supported video size!",
+ mSupportedVideoSizes.contains(videoSz));
+ assertTrue("Frame rate range " + fpsRange + " (for profile ID " + profileId +
+ ") must be one of the camera device available FPS range!",
+ fpsRanges.contains(fpsRange));
+
+ // Configure preview and recording surfaces.
+ mOutMediaFileName = mDebugFileNameBase + "/test_video_surface_reconfig.mp4";
+
+ // prepare preview surface by using video size.
+ List<Size> previewSizes = getPreviewSizesForVideo(videoSz,
+ profile.videoFrameRate);
+ if (previewSizes.size() <= 1) {
+ continue;
+ }
+
+ // 1. Do video recording using largest compatbile preview sizes
+ prepareRecordingWithProfile(profile);
+ updatePreviewSurface(previewSizes.get(0));
+ SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
+ startRecording(
+ /* useMediaRecorder */true, resultListener,
+ /*useVideoStab*/false, fpsRange, false);
+ SystemClock.sleep(RECORDING_DURATION_MS);
+ stopRecording(/* useMediaRecorder */true, /* useIntermediateSurface */false,
+ /* stopStreaming */false);
+
+ // 2. Reconfigure with the same recording surface, but switch to a smaller
+ // preview size.
+ prepareRecordingWithProfile(profile);
+ updatePreviewSurface(previewSizes.get(1));
+ SimpleCaptureCallback resultListener2 = new SimpleCaptureCallback();
+ startRecording(
+ /* useMediaRecorder */true, resultListener2,
+ /*useVideoStab*/false, fpsRange, false);
+ SystemClock.sleep(RECORDING_DURATION_MS);
+ stopRecording(/* useMediaRecorder */true);
+ break;
+ }
+ } finally {
+ closeDevice();
+ releaseRecorder();
+ }
+ }
+ }
+
+ /**
* Test camera preview and video surface sharing for maximum supported size.
*/
private void videoPreviewSurfaceSharingTestByCamera() throws Exception {
@@ -652,7 +777,7 @@
* </p>
*/
private void slowMotionRecording() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.i(TAG, "Testing slow motion recording for camera " + id);
StaticMetadata staticInfo = mAllStaticInfo.get(id);
@@ -727,7 +852,7 @@
}
private void constrainedHighSpeedRecording() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.i(TAG, "Testing constrained high speed recording for camera " + id);
@@ -1027,7 +1152,8 @@
SystemClock.sleep(RECORDING_DURATION_MS);
// Stop recording and preview
- stopRecording(/* useMediaRecorder */true, useIntermediateSurface);
+ stopRecording(/* useMediaRecorder */true, useIntermediateSurface,
+ /* stopCameraStreaming */false);
// Convert number of frames camera produced into the duration in unit of ms.
float frameDurationMs = 1000.0f / profile.videoFrameRate;
float durationMs = 0.f;
@@ -1142,7 +1268,7 @@
* Simple wrapper to wrap normal/burst video snapshot tests
*/
private void videoSnapshotHelper(boolean burstTest) throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.i(TAG, "Testing video snapshot for camera " + id);
@@ -1478,15 +1604,14 @@
}
/**
- * Update preview size with video size.
+ * Find compatible preview sizes for video size and framerate.
*
* <p>Preview size will be capped with max preview size.</p>
*
* @param videoSize The video size used for preview.
* @param videoFrameRate The video frame rate
- *
*/
- private void updatePreviewSurfaceWithVideo(Size videoSize, int videoFrameRate) {
+ private List<Size> getPreviewSizesForVideo(Size videoSize, int videoFrameRate) {
if (mOrderedPreviewSizes == null) {
throw new IllegalStateException("supported preview size list is not initialized yet");
}
@@ -1496,7 +1621,7 @@
HashMap<Size, Long> minFrameDurationMap = mStaticInfo.
getAvailableMinFrameDurationsForFormatChecked(ImageFormat.PRIVATE);
Size maxPreviewSize = mOrderedPreviewSizes.get(0);
- Size previewSize = null;
+ ArrayList<Size> previewSizes = new ArrayList<>();
if (videoSize.getWidth() > maxPreviewSize.getWidth() ||
videoSize.getHeight() > maxPreviewSize.getHeight()) {
for (Size s : mOrderedPreviewSizes) {
@@ -1510,18 +1635,32 @@
if (frameDuration <= videoFrameDuration &&
s.getWidth() <= videoSize.getWidth() &&
s.getHeight() <= videoSize.getHeight()) {
- Log.w(TAG, "Overwrite preview size from " + videoSize.toString() +
- " to " + s.toString());
- previewSize = s;
- break;
- // If all preview size doesn't work then we fallback to video size
+ Log.v(TAG, "Add preview size " + s.toString() + " for video size " +
+ videoSize.toString());
+ previewSizes.add(s);
}
}
}
- if (previewSize == null) {
- previewSize = videoSize;
+
+ if (previewSizes.isEmpty()) {
+ previewSizes.add(videoSize);
}
- updatePreviewSurface(previewSize);
+
+ return previewSizes;
+ }
+
+ /**
+ * Update preview size with video size.
+ *
+ * <p>Preview size will be capped with max preview size.</p>
+ *
+ * @param videoSize The video size used for preview.
+ * @param videoFrameRate The video frame rate
+ *
+ */
+ private void updatePreviewSurfaceWithVideo(Size videoSize, int videoFrameRate) {
+ List<Size> previewSizes = getPreviewSizesForVideo(videoSize, videoFrameRate);
+ updatePreviewSurface(previewSizes.get(0));
}
private void prepareRecordingWithProfile(CamcorderProfile profile) throws Exception {
@@ -1734,15 +1873,18 @@
}
private int stopRecording(boolean useMediaRecorder) throws Exception {
- return stopRecording(useMediaRecorder, false);
+ return stopRecording(useMediaRecorder, /*useIntermediateSurface*/false,
+ /*stopStreaming*/true);
}
// Stop recording and return the estimated video duration in milliseconds.
- private int stopRecording(boolean useMediaRecorder, boolean useIntermediateSurface)
- throws Exception {
+ private int stopRecording(boolean useMediaRecorder, boolean useIntermediateSurface,
+ boolean stopStreaming) throws Exception {
long stopRecordingTime = SystemClock.elapsedRealtime();
if (useMediaRecorder) {
- stopCameraStreaming();
+ if (stopStreaming) {
+ stopCameraStreaming();
+ }
if (useIntermediateSurface) {
mIntermediateReader.setOnImageAvailableListener(null, null);
mQueuer.expectInvalidSurface();
diff --git a/tests/camera/src/android/hardware/camera2/cts/ReprocessCaptureTest.java b/tests/camera/src/android/hardware/camera2/cts/ReprocessCaptureTest.java
index bdf98f5..e59b161 100644
--- a/tests/camera/src/android/hardware/camera2/cts/ReprocessCaptureTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/ReprocessCaptureTest.java
@@ -43,11 +43,15 @@
import java.util.ArrayList;
import java.util.List;
+import org.junit.runners.Parameterized;
+import org.junit.runner.RunWith;
import org.junit.Test;
/**
* <p>Tests for Reprocess API.</p>
*/
+
+@RunWith(Parameterized.class)
public class ReprocessCaptureTest extends Camera2SurfaceViewTestCase {
private static final String TAG = "ReprocessCaptureTest";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -90,7 +94,7 @@
*/
@Test
public void testBasicYuvToYuvReprocessing() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
if (!isYuvReprocessSupported(id)) {
continue;
}
@@ -105,7 +109,7 @@
*/
@Test
public void testBasicYuvToJpegReprocessing() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
if (!isYuvReprocessSupported(id)) {
continue;
}
@@ -120,7 +124,7 @@
*/
@Test
public void testBasicYuvToHeicReprocessing() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
if (!isYuvReprocessSupported(id)) {
continue;
}
@@ -138,7 +142,7 @@
*/
@Test
public void testBasicOpaqueToYuvReprocessing() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
if (!isOpaqueReprocessSupported(id)) {
continue;
}
@@ -153,7 +157,7 @@
*/
@Test
public void testBasicOpaqueToJpegReprocessing() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
if (!isOpaqueReprocessSupported(id)) {
continue;
}
@@ -168,7 +172,7 @@
*/
@Test
public void testBasicOpaqueToHeicReprocessing() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
if (!isOpaqueReprocessSupported(id)) {
continue;
}
@@ -186,7 +190,7 @@
*/
@Test(timeout=400*60*1000) // timeout = 400 mins for long running reprocessing tests
public void testReprocessingSizeFormat() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
if (!isYuvReprocessSupported(id) && !isOpaqueReprocessSupported(id)) {
continue;
}
@@ -208,7 +212,7 @@
*/
@Test(timeout=400*60*1000) // timeout = 400 mins for long running reprocessing tests
public void testReprocessingSizeFormatWithPreview() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
if (!isYuvReprocessSupported(id) && !isOpaqueReprocessSupported(id)) {
continue;
}
@@ -229,7 +233,7 @@
*/
@Test
public void testRecreateReprocessingSessions() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
if (!isYuvReprocessSupported(id) && !isOpaqueReprocessSupported(id)) {
continue;
}
@@ -267,7 +271,7 @@
*/
@Test
public void testCrossSessionCaptureException() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
// Test one supported input format -> JPEG
int inputFormat;
int reprocessOutputFormat = ImageFormat.JPEG;
@@ -333,11 +337,78 @@
}
/**
+ * Verify queued input images are cleared in new reprocessable capture session.
+ *
+ * This tests the case where an application receives onCaptureBufferLost() for an
+ * output stream, resulting in pending input buffers not having corresponding request.
+ *
+ * For subsequent new reprocessable capture session, ImageWriter.queueInputBuffer may become
+ * stuck due to stale buffers from previous session.
+ */
+ @Test
+ public void testQueueImageWithoutRequest() throws Exception {
+ final int MAX_IMAGES = 1;
+ final int ITERATIONS = MAX_IMAGES + 3;
+ for (String id : mCameraIdsUnderTest) {
+ // Test one supported input format -> JPEG
+ int inputFormat;
+ int reprocessOutputFormat = ImageFormat.JPEG;
+
+ if (isOpaqueReprocessSupported(id)) {
+ inputFormat = ImageFormat.PRIVATE;
+ } else if (isYuvReprocessSupported(id)) {
+ inputFormat = ImageFormat.YUV_420_888;
+ } else {
+ continue;
+ }
+
+ openDevice(id);
+
+ // Test the largest sizes
+ Size inputSize =
+ getMaxSize(inputFormat, StaticMetadata.StreamDirection.Input);
+ Size reprocessOutputSize =
+ getMaxSize(reprocessOutputFormat, StaticMetadata.StreamDirection.Output);
+
+ try {
+ if (VERBOSE) {
+ Log.v(TAG, "testQueueImageWithoutRequest: cameraId: " + id +
+ " inputSize: " + inputSize + " inputFormat: " + inputFormat +
+ " reprocessOutputSize: " + reprocessOutputSize +
+ " reprocessOutputFormat: " + reprocessOutputFormat);
+ }
+
+ setupImageReaders(inputSize, inputFormat, reprocessOutputSize,
+ reprocessOutputFormat, MAX_IMAGES);
+
+ for (int i = 0; i < ITERATIONS; i++) {
+ setupReprocessableSession(/*previewSurface*/null, /*numImageWriterImages*/1);
+
+ TotalCaptureResult result = submitCaptureRequest(mFirstImageReader.getSurface(),
+ /*inputResult*/null);
+ Image image = mFirstImageReaderListener.getImage(CAPTURE_TIMEOUT_MS);
+
+ // queue the image to image writer
+ mImageWriter.queueInputImage(image);
+
+ mInputSurface = null;
+ mImageWriter.close();
+ mImageWriter = null;
+ }
+ } finally {
+ closeReprossibleSession();
+ closeImageReaders();
+ closeDevice();
+ }
+ }
+ }
+
+ /**
* Test burst reprocessing captures with and without preview.
*/
@Test(timeout=400*60*1000) // timeout = 400 mins for long running reprocessing tests
public void testBurstReprocessing() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
if (!isYuvReprocessSupported(id) && !isOpaqueReprocessSupported(id)) {
continue;
}
@@ -361,7 +432,7 @@
*/
@Test(timeout=400*60*1000) // timeout = 400 mins for long running reprocessing tests
public void testMixedBurstReprocessing() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
if (!isYuvReprocessSupported(id) && !isOpaqueReprocessSupported(id)) {
continue;
}
@@ -387,7 +458,7 @@
*/
@Test
public void testReprocessAbort() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
if (!isYuvReprocessSupported(id) && !isOpaqueReprocessSupported(id)) {
continue;
}
@@ -417,7 +488,7 @@
*/
@Test
public void testReprocessTimestamps() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
if (!isYuvReprocessSupported(id) && !isOpaqueReprocessSupported(id)) {
continue;
}
@@ -448,7 +519,7 @@
*/
@Test
public void testReprocessJpegExif() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
if (!isYuvReprocessSupported(id) && !isOpaqueReprocessSupported(id)) {
continue;
}
@@ -479,7 +550,7 @@
@Test
public void testReprocessRequestKeys() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
if (!isYuvReprocessSupported(id) && !isOpaqueReprocessSupported(id)) {
continue;
}
diff --git a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
index d108600e..e2bbb9c 100644
--- a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
@@ -54,12 +54,18 @@
import java.util.Map;
import java.util.Set;
+import org.junit.runners.Parameterized;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
import static junit.framework.Assert.assertTrue;
import static org.mockito.Mockito.*;
/**
* Tests exercising edge cases in camera setup, configuration, and usage.
*/
+
+@RunWith(Parameterized.class)
public class RobustnessTest extends Camera2AndroidTestCase {
private static final String TAG = "RobustnessTest";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -79,8 +85,9 @@
* this surface are expected have the dimensions of the closest possible buffer size in the
* available stream configurations for a surface with this format.
*/
+ @Test
public void testBadSurfaceDimensions() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.i(TAG, "Testing Camera " + id);
openDevice(id);
@@ -160,8 +167,9 @@
/**
* Test for making sure the mandatory stream combinations work as expected.
*/
+ @Test
public void testMandatoryOutputCombinations() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
openDevice(id);
MandatoryStreamCombination[] combinations =
mStaticInfo.getCharacteristics().get(
@@ -186,7 +194,7 @@
Set<String> physicalCameraIds =
mStaticInfo.getCharacteristics().getPhysicalCameraIds();
for (String physicalId : physicalCameraIds) {
- if (Arrays.asList(mCameraIds).contains(physicalId)) {
+ if (Arrays.asList(mCameraIdsUnderTest).contains(physicalId)) {
// If physicalId is advertised in camera ID list, do not need to test
// its stream combination through logical camera.
continue;
@@ -480,8 +488,9 @@
* Test for making sure the required reprocess input/output combinations for each hardware
* level and capability work as expected.
*/
+ @Test
public void testMandatoryReprocessConfigurations() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
openDevice(id);
MandatoryStreamCombination[] combinations =
mStaticInfo.getCharacteristics().get(
@@ -716,9 +725,10 @@
}
}
+ @Test
public void testBasicTriggerSequence() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
Log.i(TAG, String.format("Testing Camera %s", id));
try {
@@ -856,8 +866,9 @@
}
+ @Test
public void testSimultaneousTriggers() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
Log.i(TAG, String.format("Testing Camera %s", id));
try {
@@ -958,8 +969,9 @@
}
}
+ @Test
public void testAfThenAeTrigger() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
Log.i(TAG, String.format("Testing Camera %s", id));
try {
@@ -1074,8 +1086,9 @@
}
}
+ @Test
public void testAeThenAfTrigger() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
Log.i(TAG, String.format("Testing Camera %s", id));
try {
@@ -1190,9 +1203,10 @@
}
}
+ @Test
public void testAeAndAfCausality() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
Log.i(TAG, String.format("Testing Camera %s", id));
try {
@@ -1372,8 +1386,9 @@
}
+ @Test
public void testAbandonRepeatingRequestSurface() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
Log.i(TAG, String.format(
"Testing Camera %s for abandoning surface of a repeating request", id));
@@ -1441,8 +1456,9 @@
}
}
+ @Test
public void testConfigureAbandonedSurface() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
Log.i(TAG, String.format(
"Testing Camera %s for configuring abandoned surface", id));
@@ -1498,10 +1514,11 @@
}
}
+ @Test
public void testAfSceneChange() throws Exception {
final int NUM_FRAMES_VERIFIED = 3;
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
Log.i(TAG, String.format("Testing Camera %s for AF scene change", id));
StaticMetadata staticInfo =
@@ -1548,10 +1565,11 @@
}
}
+ @Test
public void testOisDataMode() throws Exception {
final int NUM_FRAMES_VERIFIED = 3;
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
Log.i(TAG, String.format("Testing Camera %s for OIS mode", id));
StaticMetadata staticInfo =
diff --git a/tests/camera/src/android/hardware/camera2/cts/StaticMetadataTest.java b/tests/camera/src/android/hardware/camera2/cts/StaticMetadataTest.java
index b56fcbf..1ffa501 100644
--- a/tests/camera/src/android/hardware/camera2/cts/StaticMetadataTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/StaticMetadataTest.java
@@ -41,6 +41,13 @@
import java.util.List;
import java.util.Set;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
/**
* <p>
* This class covers the {@link CameraCharacteristics} tests that are not
@@ -50,6 +57,8 @@
* Note that most of the tests in this class don't require camera open.
* </p>
*/
+
+@RunWith(Parameterized.class)
public class StaticMetadataTest extends Camera2AndroidTestCase {
private static final String TAG = "StaticMetadataTest";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -59,6 +68,7 @@
/**
* Test the available capability for different hardware support level devices.
*/
+ @Test
public void testHwSupportedLevel() throws Exception {
Key<StreamConfigurationMap> key =
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP;
@@ -135,6 +145,7 @@
/**
* Test max number of output stream reported by device
*/
+ @Test
public void testMaxNumOutputStreams() throws Exception {
for (String id : mAllCameraIds) {
initStaticMetadata(id);
@@ -163,6 +174,7 @@
/**
* Test advertised capability does match available keys and vice versa
*/
+ @Test
public void testCapabilities() throws Exception {
for (String id : mAllCameraIds) {
initStaticMetadata(id);
@@ -533,6 +545,7 @@
/**
* Test lens facing.
*/
+ @Test
public void testLensFacing() throws Exception {
for (String id : mAllCameraIds) {
initStaticMetadata(id);
diff --git a/tests/camera/src/android/hardware/camera2/cts/StillCaptureTest.java b/tests/camera/src/android/hardware/camera2/cts/StillCaptureTest.java
index e8d2812..25fde0e 100644
--- a/tests/camera/src/android/hardware/camera2/cts/StillCaptureTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/StillCaptureTest.java
@@ -57,8 +57,11 @@
import junit.framework.Assert;
+import org.junit.runners.Parameterized;
+import org.junit.runner.RunWith;
import org.junit.Test;
+@RunWith(Parameterized.class)
public class StillCaptureTest extends Camera2SurfaceViewTestCase {
private static final String TAG = "StillCaptureTest";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -91,15 +94,15 @@
*/
@Test
public void testJpegExif() throws Exception {
- for (int i = 0; i < mCameraIds.length; i++) {
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
try {
- Log.i(TAG, "Testing JPEG exif for Camera " + mCameraIds[i]);
- if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
- Log.i(TAG, "Camera " + mCameraIds[i] +
+ Log.i(TAG, "Testing JPEG exif for Camera " + mCameraIdsUnderTest[i]);
+ if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+ Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
" does not support color outputs, skipping");
continue;
}
- openDevice(mCameraIds[i]);
+ openDevice(mCameraIdsUnderTest[i]);
Size maxJpegSize = mOrderedStillSizes.get(0);
stillExifTestByCamera(ImageFormat.JPEG, maxJpegSize);
} finally {
@@ -114,25 +117,25 @@
*/
@Test
public void testHeicExif() throws Exception {
- for (int i = 0; i < mCameraIds.length; i++) {
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
try {
- Log.i(TAG, "Testing HEIC exif for Camera " + mCameraIds[i]);
- if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
- Log.i(TAG, "Camera " + mCameraIds[i] +
+ Log.i(TAG, "Testing HEIC exif for Camera " + mCameraIdsUnderTest[i]);
+ if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+ Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
" does not support color outputs, skipping");
continue;
}
- if (!mAllStaticInfo.get(mCameraIds[i]).isHeicSupported()) {
- Log.i(TAG, "Camera " + mCameraIds[i] +
+ if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isHeicSupported()) {
+ Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
" does not support HEIC, skipping");
continue;
}
- openDevice(mCameraIds[i]);
+ openDevice(mCameraIdsUnderTest[i]);
// Test maximum Heic size capture
List<Size> orderedHeicSizes = CameraTestUtils.getSupportedHeicSizes(
- mCameraIds[i], mCameraManager, null/*bound*/);
+ mCameraIdsUnderTest[i], mCameraManager, null/*bound*/);
Size maxHeicSize = orderedHeicSizes.get(0);
stillExifTestByCamera(ImageFormat.HEIC, maxHeicSize);
@@ -152,25 +155,25 @@
*/
@Test
public void testDynamicDepthCapture() throws Exception {
- for (int i = 0; i < mCameraIds.length; i++) {
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
try {
- Log.i(TAG, "Testing dynamic depth for Camera " + mCameraIds[i]);
- if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
- Log.i(TAG, "Camera " + mCameraIds[i] +
+ Log.i(TAG, "Testing dynamic depth for Camera " + mCameraIdsUnderTest[i]);
+ if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+ Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
" does not support color outputs, skipping");
continue;
}
- if (!mAllStaticInfo.get(mCameraIds[i]).isDepthJpegSupported()) {
- Log.i(TAG, "Camera " + mCameraIds[i] +
+ if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isDepthJpegSupported()) {
+ Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
" does not support dynamic depth, skipping");
continue;
}
- openDevice(mCameraIds[i]);
+ openDevice(mCameraIdsUnderTest[i]);
// Check the maximum supported size.
List<Size> orderedDepthJpegSizes = CameraTestUtils.getSortedSizesForFormat(
- mCameraIds[i], mCameraManager, ImageFormat.DEPTH_JPEG, null/*bound*/);
+ mCameraIdsUnderTest[i], mCameraManager, ImageFormat.DEPTH_JPEG, null/*bound*/);
Size maxDepthJpegSize = orderedDepthJpegSizes.get(0);
stillDynamicDepthTestByCamera(ImageFormat.DEPTH_JPEG, maxDepthJpegSize);
} finally {
@@ -192,7 +195,7 @@
*/
@Test
public void testTakePicture() throws Exception{
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.i(TAG, "Testing basic take picture for Camera " + id);
if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
@@ -220,7 +223,7 @@
*/
@Test
public void testTakePictureZsl() throws Exception{
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.i(TAG, "Testing basic ZSL take picture for Camera " + id);
if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
@@ -246,18 +249,18 @@
*/
@Test
public void testBasicRawCapture() throws Exception {
- for (int i = 0; i < mCameraIds.length; i++) {
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
try {
- Log.i(TAG, "Testing raw capture for Camera " + mCameraIds[i]);
+ Log.i(TAG, "Testing raw capture for Camera " + mCameraIdsUnderTest[i]);
- if (!mAllStaticInfo.get(mCameraIds[i]).isCapabilitySupported(
+ if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isCapabilitySupported(
CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
- Log.i(TAG, "RAW capability is not supported in camera " + mCameraIds[i] +
+ Log.i(TAG, "RAW capability is not supported in camera " + mCameraIdsUnderTest[i] +
". Skip the test.");
continue;
}
- openDevice(mCameraIds[i]);
+ openDevice(mCameraIdsUnderTest[i]);
rawCaptureTestByCamera(/*stillRequest*/null);
} finally {
closeDevice();
@@ -271,17 +274,17 @@
*/
@Test
public void testBasicRawZslCapture() throws Exception {
- for (int i = 0; i < mCameraIds.length; i++) {
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
try {
- Log.i(TAG, "Testing raw ZSL capture for Camera " + mCameraIds[i]);
+ Log.i(TAG, "Testing raw ZSL capture for Camera " + mCameraIdsUnderTest[i]);
- if (!mAllStaticInfo.get(mCameraIds[i]).isCapabilitySupported(
+ if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isCapabilitySupported(
CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
- Log.i(TAG, "RAW capability is not supported in camera " + mCameraIds[i] +
+ Log.i(TAG, "RAW capability is not supported in camera " + mCameraIdsUnderTest[i] +
". Skip the test.");
continue;
}
- openDevice(mCameraIds[i]);
+ openDevice(mCameraIdsUnderTest[i]);
CaptureRequest.Builder stillRequest =
mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
stillRequest.set(CaptureRequest.CONTROL_ENABLE_ZSL, true);
@@ -304,17 +307,17 @@
*/
@Test
public void testFullRawCapture() throws Exception {
- for (int i = 0; i < mCameraIds.length; i++) {
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
try {
- Log.i(TAG, "Testing raw+JPEG capture for Camera " + mCameraIds[i]);
- if (!mAllStaticInfo.get(mCameraIds[i]).isCapabilitySupported(
+ Log.i(TAG, "Testing raw+JPEG capture for Camera " + mCameraIdsUnderTest[i]);
+ if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isCapabilitySupported(
CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
- Log.i(TAG, "RAW capability is not supported in camera " + mCameraIds[i] +
+ Log.i(TAG, "RAW capability is not supported in camera " + mCameraIdsUnderTest[i] +
". Skip the test.");
continue;
}
- openDevice(mCameraIds[i]);
+ openDevice(mCameraIdsUnderTest[i]);
fullRawCaptureTestByCamera(/*stillRequest*/null);
} finally {
closeDevice();
@@ -333,16 +336,16 @@
*/
@Test
public void testFullRawZSLCapture() throws Exception {
- for (int i = 0; i < mCameraIds.length; i++) {
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
try {
- Log.i(TAG, "Testing raw+JPEG ZSL capture for Camera " + mCameraIds[i]);
- if (!mAllStaticInfo.get(mCameraIds[i]).isCapabilitySupported(
+ Log.i(TAG, "Testing raw+JPEG ZSL capture for Camera " + mCameraIdsUnderTest[i]);
+ if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isCapabilitySupported(
CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
- Log.i(TAG, "RAW capability is not supported in camera " + mCameraIds[i] +
+ Log.i(TAG, "RAW capability is not supported in camera " + mCameraIdsUnderTest[i] +
". Skip the test.");
continue;
}
- openDevice(mCameraIds[i]);
+ openDevice(mCameraIdsUnderTest[i]);
CaptureRequest.Builder stillRequest =
mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
stillRequest.set(CaptureRequest.CONTROL_ENABLE_ZSL, true);
@@ -364,7 +367,7 @@
*/
@Test
public void testTouchForFocus() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.i(TAG, "Testing touch for focus for Camera " + id);
StaticMetadata staticInfo = mAllStaticInfo.get(id);
@@ -395,7 +398,7 @@
*/
@Test(timeout=60*60*1000) // timeout = 60 mins for long running tests
public void testStillPreviewCombination() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.i(TAG, "Testing Still preview capture combination for Camera " + id);
if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
@@ -423,7 +426,7 @@
*/
@Test
public void testAeCompensation() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.i(TAG, "Testing AE compensation for Camera " + id);
@@ -450,7 +453,7 @@
*/
@Test
public void testAeRegions() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.i(TAG, "Testing AE regions for Camera " + id);
openDevice(id);
@@ -476,7 +479,7 @@
*/
@Test
public void testAwbRegions() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.i(TAG, "Testing AE regions for Camera " + id);
openDevice(id);
@@ -502,7 +505,7 @@
*/
@Test
public void testAfRegions() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.i(TAG, "Testing AF regions for Camera " + id);
openDevice(id);
@@ -528,7 +531,7 @@
*/
@Test
public void testPreviewPersistence() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.i(TAG, "Testing preview persistence for Camera " + id);
if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
@@ -546,7 +549,7 @@
@Test
public void testAePrecaptureTriggerCancelJpegCapture() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.i(TAG, "Testing AE precapture cancel for jpeg capture for Camera " + id);
@@ -581,7 +584,7 @@
*/
@Test
public void testAllocateBitmap() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
Log.i(TAG, "Testing bitmap allocations for Camera " + id);
if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
@@ -605,7 +608,7 @@
*/
@Test
public void testFocalLengths() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
StaticMetadata staticInfo = mAllStaticInfo.get(id);
if (staticInfo.isHardwareLevelLegacy()) {
diff --git a/tests/camera/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java b/tests/camera/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
index 3d84d38..1f2e93e 100644
--- a/tests/camera/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
@@ -52,11 +52,15 @@
import java.util.Arrays;
import java.util.List;
+import org.junit.runners.Parameterized;
+import org.junit.runner.RunWith;
import org.junit.Test;
/**
* CameraDevice preview test by using SurfaceView.
*/
+
+@RunWith(Parameterized.class)
public class SurfaceViewPreviewTest extends Camera2SurfaceViewTestCase {
private static final String TAG = "SurfaceViewPreviewTest";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -86,15 +90,15 @@
*/
@Test
public void testCameraPreview() throws Exception {
- for (int i = 0; i < mCameraIds.length; i++) {
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
try {
- Log.i(TAG, "Testing preview for Camera " + mCameraIds[i]);
- if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
- Log.i(TAG, "Camera " + mCameraIds[i] +
+ Log.i(TAG, "Testing preview for Camera " + mCameraIdsUnderTest[i]);
+ if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+ Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
" does not support color outputs, skipping");
continue;
}
- openDevice(mCameraIds[i]);
+ openDevice(mCameraIdsUnderTest[i]);
previewTestByCamera();
} finally {
closeDevice();
@@ -111,15 +115,15 @@
*/
@Test
public void testBasicTestPatternPreview() throws Exception{
- for (int i = 0; i < mCameraIds.length; i++) {
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
try {
- Log.i(TAG, "Testing preview for Camera " + mCameraIds[i]);
- if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
- Log.i(TAG, "Camera " + mCameraIds[i] +
+ Log.i(TAG, "Testing preview for Camera " + mCameraIdsUnderTest[i]);
+ if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+ Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
" does not support color outputs, skipping");
continue;
}
- openDevice(mCameraIds[i]);
+ openDevice(mCameraIdsUnderTest[i]);
previewTestPatternTestByCamera();
} finally {
closeDevice();
@@ -133,7 +137,7 @@
*/
@Test
public void testPreviewFpsRange() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
@@ -159,7 +163,7 @@
*/
@Test
public void testSurfaceSet() throws Exception {
- for (String id : mCameraIds) {
+ for (String id : mCameraIdsUnderTest) {
try {
if (!mAllStaticInfo.get(id).isColorOutputSupported()) {
Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
@@ -184,15 +188,15 @@
*/
@Test
public void testPreparePerformance() throws Throwable {
- for (int i = 0; i < mCameraIds.length; i++) {
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
try {
- if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
- Log.i(TAG, "Camera " + mCameraIds[i] +
+ if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+ Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
" does not support color outputs, skipping");
continue;
}
- openDevice(mCameraIds[i]);
- preparePerformanceTestByCamera(mCameraIds[i]);
+ openDevice(mCameraIdsUnderTest[i]);
+ preparePerformanceTestByCamera(mCameraIdsUnderTest[i]);
}
finally {
closeDevice();
@@ -344,15 +348,15 @@
*/
@Test
public void testSurfaceEquality() throws Exception {
- for (int i = 0; i < mCameraIds.length; i++) {
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
try {
- if (!mAllStaticInfo.get(mCameraIds[i]).isColorOutputSupported()) {
- Log.i(TAG, "Camera " + mCameraIds[i] +
+ if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+ Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
" does not support color outputs, skipping");
continue;
}
- openDevice(mCameraIds[i]);
- surfaceEqualityTestByCamera(mCameraIds[i]);
+ openDevice(mCameraIdsUnderTest[i]);
+ surfaceEqualityTestByCamera(mCameraIdsUnderTest[i]);
}
finally {
closeDevice();
@@ -435,21 +439,21 @@
*/
@Test
public void testDeferredSurfaces() throws Exception {
- for (int i = 0; i < mCameraIds.length; i++) {
+ for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
try {
- StaticMetadata staticInfo = mAllStaticInfo.get(mCameraIds[i]);
+ StaticMetadata staticInfo = mAllStaticInfo.get(mCameraIdsUnderTest[i]);
if (staticInfo.isHardwareLevelLegacy()) {
- Log.i(TAG, "Camera " + mCameraIds[i] + " is legacy, skipping");
+ Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] + " is legacy, skipping");
continue;
}
if (!staticInfo.isColorOutputSupported()) {
- Log.i(TAG, "Camera " + mCameraIds[i] +
+ Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
" does not support color outputs, skipping");
continue;
}
- openDevice(mCameraIds[i]);
- testDeferredSurfacesByCamera(mCameraIds[i]);
+ openDevice(mCameraIdsUnderTest[i]);
+ testDeferredSurfacesByCamera(mCameraIdsUnderTest[i]);
}
finally {
closeDevice();
diff --git a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidBasicTestCase.java b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidBasicTestCase.java
new file mode 100644
index 0000000..c240065
--- /dev/null
+++ b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidBasicTestCase.java
@@ -0,0 +1,631 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2.cts.testcases;
+
+import static android.hardware.camera2.cts.CameraTestUtils.*;
+import static com.android.ex.camera2.blocking.BlockingStateCallback.*;
+
+import android.content.Context;
+import android.graphics.ImageFormat;
+import android.graphics.Rect;
+
+import android.hardware.camera2.cts.CameraTestUtils;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraCaptureSession.CaptureCallback;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.params.OutputConfiguration;
+import android.hardware.camera2.params.SessionConfiguration;
+import android.util.Size;
+import android.hardware.camera2.cts.helpers.CameraErrorCollector;
+import android.hardware.camera2.cts.helpers.StaticMetadata;
+import android.hardware.camera2.cts.helpers.StaticMetadata.CheckLevel;
+import android.media.Image;
+import android.media.Image.Plane;
+import android.media.ImageReader;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.test.AndroidTestCase;
+import android.util.Log;
+import android.view.Surface;
+import android.view.WindowManager;
+
+import com.android.ex.camera2.blocking.BlockingSessionCallback;
+import com.android.ex.camera2.blocking.BlockingStateCallback;
+
+import java.io.File;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+
+public class Camera2AndroidBasicTestCase extends AndroidTestCase {
+ private static final String TAG = "Camera2AndroidBasicTestCase";
+ private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+ // Default capture size: VGA size is required by CDD.
+ protected static final Size DEFAULT_CAPTURE_SIZE = new Size(640, 480);
+ protected static final int CAPTURE_WAIT_TIMEOUT_MS = 5000;
+
+ protected CameraManager mCameraManager;
+ protected CameraDevice mCamera;
+ protected CameraCaptureSession mCameraSession;
+ protected BlockingSessionCallback mCameraSessionListener;
+ protected BlockingStateCallback mCameraListener;
+ protected String[] mCameraIdsUnderTest;
+ // include both standalone camera IDs and "hidden" physical camera IDs
+ protected String[] mAllCameraIds;
+ protected HashMap<String, StaticMetadata> mAllStaticInfo;
+ protected ImageReader mReader;
+ protected Surface mReaderSurface;
+ protected Handler mHandler;
+ protected HandlerThread mHandlerThread;
+ protected StaticMetadata mStaticInfo;
+ protected CameraErrorCollector mCollector;
+ protected List<Size> mOrderedPreviewSizes; // In descending order.
+ protected List<Size> mOrderedVideoSizes; // In descending order.
+ protected List<Size> mOrderedStillSizes; // In descending order.
+ protected String mDebugFileNameBase;
+
+ protected WindowManager mWindowManager;
+
+ @Override
+ public void setContext(Context context) {
+ super.setContext(context);
+ mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
+ assertNotNull("Can't connect to camera manager!", mCameraManager);
+ mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+ }
+
+ /**
+ * Set up the camera2 test case required environments, including CameraManager,
+ * HandlerThread, Camera IDs, and CameraStateCallback etc.
+ */
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ /**
+ * Workaround for mockito and JB-MR2 incompatibility
+ *
+ * Avoid java.lang.IllegalArgumentException: dexcache == null
+ * https://code.google.com/p/dexmaker/issues/detail?id=2
+ */
+ System.setProperty("dexmaker.dexcache", getContext().getCacheDir().toString());
+
+ mCameraIdsUnderTest = mCameraManager.getCameraIdListNoLazy();
+ assertNotNull("Camera ids shouldn't be null", mCameraIdsUnderTest);
+ mHandlerThread = new HandlerThread(TAG);
+ mHandlerThread.start();
+ mHandler = new Handler(mHandlerThread.getLooper());
+ mCameraListener = new BlockingStateCallback();
+ mCollector = new CameraErrorCollector();
+
+ File filesDir = mContext.getPackageManager().isInstantApp()
+ ? mContext.getFilesDir()
+ : mContext.getExternalFilesDir(null);
+
+ mDebugFileNameBase = filesDir.getPath();
+
+ mAllStaticInfo = new HashMap<String, StaticMetadata>();
+ List<String> hiddenPhysicalIds = new ArrayList<>();
+ for (String cameraId : mCameraIdsUnderTest) {
+ CameraCharacteristics props = mCameraManager.getCameraCharacteristics(cameraId);
+ StaticMetadata staticMetadata = new StaticMetadata(props,
+ CheckLevel.ASSERT, /*collector*/null);
+ mAllStaticInfo.put(cameraId, staticMetadata);
+
+ for (String physicalId : props.getPhysicalCameraIds()) {
+ if (!Arrays.asList(mCameraIdsUnderTest).contains(physicalId) &&
+ !hiddenPhysicalIds.contains(physicalId)) {
+ hiddenPhysicalIds.add(physicalId);
+ props = mCameraManager.getCameraCharacteristics(physicalId);
+ staticMetadata = new StaticMetadata(
+ mCameraManager.getCameraCharacteristics(physicalId),
+ CheckLevel.ASSERT, /*collector*/null);
+ mAllStaticInfo.put(physicalId, staticMetadata);
+ }
+ }
+ }
+ mAllCameraIds = new String[mCameraIdsUnderTest.length + hiddenPhysicalIds.size()];
+ System.arraycopy(mCameraIdsUnderTest, 0, mAllCameraIds, 0, mCameraIdsUnderTest.length);
+ for (int i = 0; i < hiddenPhysicalIds.size(); i++) {
+ mAllCameraIds[mCameraIdsUnderTest.length + i] = hiddenPhysicalIds.get(i);
+ }
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ String[] cameraIdsPostTest =
+ mCameraManager.getCameraIdListNoLazy();
+ assertNotNull("Camera ids shouldn't be null", cameraIdsPostTest);
+ Log.i(TAG, "Camera ids in setup:" + Arrays.toString(mCameraIdsUnderTest));
+ Log.i(TAG, "Camera ids in tearDown:" + Arrays.toString(cameraIdsPostTest));
+ assertTrue(
+ "Number of cameras changed from " + mCameraIdsUnderTest.length + " to " +
+ cameraIdsPostTest.length,
+ mCameraIdsUnderTest.length == cameraIdsPostTest.length);
+ mHandlerThread.quitSafely();
+ mHandler = null;
+ closeDefaultImageReader();
+
+ try {
+ mCollector.verify();
+ } catch (Throwable e) {
+ // When new Exception(e) is used, exception info will be printed twice.
+ throw new Exception(e.getMessage());
+ } finally {
+ super.tearDown();
+ }
+ }
+
+ /**
+ * Start capture with given {@link #CaptureRequest}.
+ *
+ * @param request The {@link #CaptureRequest} to be captured.
+ * @param repeating If the capture is single capture or repeating.
+ * @param listener The {@link #CaptureCallback} camera device used to notify callbacks.
+ * @param handler The handler camera device used to post callbacks.
+ */
+ protected void startCapture(CaptureRequest request, boolean repeating,
+ CaptureCallback listener, Handler handler) throws Exception {
+ if (VERBOSE) Log.v(TAG, "Starting capture from device");
+
+ if (repeating) {
+ mCameraSession.setRepeatingRequest(request, listener, handler);
+ } else {
+ mCameraSession.capture(request, listener, handler);
+ }
+ }
+
+ /**
+ * Stop the current active capture.
+ *
+ * @param fast When it is true, {@link CameraDevice#flush} is called, the stop capture
+ * could be faster.
+ */
+ protected void stopCapture(boolean fast) throws Exception {
+ if (VERBOSE) Log.v(TAG, "Stopping capture");
+
+ if (fast) {
+ /**
+ * Flush is useful for canceling long exposure single capture, it also could help
+ * to make the streaming capture stop sooner.
+ */
+ mCameraSession.abortCaptures();
+ mCameraSessionListener.getStateWaiter().
+ waitForState(BlockingSessionCallback.SESSION_READY, CAMERA_IDLE_TIMEOUT_MS);
+ } else {
+ mCameraSession.close();
+ mCameraSessionListener.getStateWaiter().
+ waitForState(BlockingSessionCallback.SESSION_CLOSED, CAMERA_IDLE_TIMEOUT_MS);
+ }
+ }
+
+ /**
+ * Open a {@link #CameraDevice camera device} and get the StaticMetadata for a given camera id.
+ * The default mCameraListener is used to wait for states.
+ *
+ * @param cameraId The id of the camera device to be opened.
+ */
+ protected void openDevice(String cameraId) throws Exception {
+ openDevice(cameraId, mCameraListener);
+ }
+
+ /**
+ * Open a {@link #CameraDevice} and get the StaticMetadata for a given camera id and listener.
+ *
+ * @param cameraId The id of the camera device to be opened.
+ * @param listener The {@link #BlockingStateCallback} used to wait for states.
+ */
+ protected void openDevice(String cameraId, BlockingStateCallback listener) throws Exception {
+ mCamera = CameraTestUtils.openCamera(
+ mCameraManager, cameraId, listener, mHandler);
+ mCollector.setCameraId(cameraId);
+ mStaticInfo = mAllStaticInfo.get(cameraId);
+ if (mStaticInfo.isColorOutputSupported()) {
+ mOrderedPreviewSizes = getSupportedPreviewSizes(
+ cameraId, mCameraManager,
+ getPreviewSizeBound(mWindowManager, PREVIEW_SIZE_BOUND));
+ mOrderedVideoSizes = getSupportedVideoSizes(cameraId, mCameraManager, PREVIEW_SIZE_BOUND);
+ mOrderedStillSizes = getSupportedStillSizes(cameraId, mCameraManager, null);
+ }
+
+ if (VERBOSE) {
+ Log.v(TAG, "Camera " + cameraId + " is opened");
+ }
+ }
+
+ /**
+ * Create a {@link #CameraCaptureSession} using the currently open camera.
+ *
+ * @param outputSurfaces The set of output surfaces to configure for this session
+ */
+ protected void createSession(List<Surface> outputSurfaces) throws Exception {
+ mCameraSessionListener = new BlockingSessionCallback();
+ mCameraSession = CameraTestUtils.configureCameraSession(mCamera, outputSurfaces,
+ mCameraSessionListener, mHandler);
+ }
+
+ /**
+ * Create a {@link #CameraCaptureSession} using the currently open camera with
+ * OutputConfigurations.
+ *
+ * @param outputSurfaces The set of output surfaces to configure for this session
+ */
+ protected void createSessionByConfigs(List<OutputConfiguration> outputConfigs) throws Exception {
+ mCameraSessionListener = new BlockingSessionCallback();
+ mCameraSession = CameraTestUtils.configureCameraSessionWithConfig(mCamera, outputConfigs,
+ mCameraSessionListener, mHandler);
+ }
+
+ /**
+ * Close a {@link #CameraDevice camera device} and clear the associated StaticInfo field for a
+ * given camera id. The default mCameraListener is used to wait for states.
+ * <p>
+ * This function must be used along with the {@link #openDevice} for the
+ * same camera id.
+ * </p>
+ *
+ * @param cameraId The id of the {@link #CameraDevice camera device} to be closed.
+ */
+ protected void closeDevice(String cameraId) {
+ closeDevice(cameraId, mCameraListener);
+ }
+
+ /**
+ * Close a {@link #CameraDevice camera device} and clear the associated StaticInfo field for a
+ * given camera id and listener.
+ * <p>
+ * This function must be used along with the {@link #openDevice} for the
+ * same camera id.
+ * </p>
+ *
+ * @param cameraId The id of the camera device to be closed.
+ * @param listener The BlockingStateCallback used to wait for states.
+ */
+ protected void closeDevice(String cameraId, BlockingStateCallback listener) {
+ if (mCamera != null) {
+ if (!cameraId.equals(mCamera.getId())) {
+ throw new IllegalStateException("Try to close a device that is not opened yet");
+ }
+ mCamera.close();
+ listener.waitForState(STATE_CLOSED, CAMERA_CLOSE_TIMEOUT_MS);
+ mCamera = null;
+ mCameraSession = null;
+ mCameraSessionListener = null;
+ mStaticInfo = null;
+ mOrderedPreviewSizes = null;
+ mOrderedVideoSizes = null;
+ mOrderedStillSizes = null;
+
+ if (VERBOSE) {
+ Log.v(TAG, "Camera " + cameraId + " is closed");
+ }
+ }
+ }
+
+ /**
+ * Create an {@link ImageReader} object and get the surface.
+ * <p>
+ * This function creates {@link ImageReader} object and surface, then assign
+ * to the default {@link mReader} and {@link mReaderSurface}. It closes the
+ * current default active {@link ImageReader} if it exists.
+ * </p>
+ *
+ * @param size The size of this ImageReader to be created.
+ * @param format The format of this ImageReader to be created
+ * @param maxNumImages The max number of images that can be acquired
+ * simultaneously.
+ * @param listener The listener used by this ImageReader to notify
+ * callbacks.
+ */
+ protected void createDefaultImageReader(Size size, int format, int maxNumImages,
+ ImageReader.OnImageAvailableListener listener) throws Exception {
+ closeDefaultImageReader();
+
+ mReader = createImageReader(size, format, maxNumImages, listener);
+ mReaderSurface = mReader.getSurface();
+ if (VERBOSE) Log.v(TAG, "Created ImageReader size " + size.toString());
+ }
+
+ /**
+ * Create an {@link ImageReader} object and get the surface.
+ * <p>
+ * This function creates {@link ImageReader} object and surface, then assign
+ * to the default {@link mReader} and {@link mReaderSurface}. It closes the
+ * current default active {@link ImageReader} if it exists.
+ * </p>
+ *
+ * @param size The size of this ImageReader to be created.
+ * @param format The format of this ImageReader to be created
+ * @param maxNumImages The max number of images that can be acquired
+ * simultaneously.
+ * @param usage The usage flag of the ImageReader
+ * @param listener The listener used by this ImageReader to notify
+ * callbacks.
+ */
+ protected void createDefaultImageReader(Size size, int format, int maxNumImages, long usage,
+ ImageReader.OnImageAvailableListener listener) throws Exception {
+ closeDefaultImageReader();
+
+ mReader = createImageReader(size, format, maxNumImages, usage, listener);
+ mReaderSurface = mReader.getSurface();
+ if (VERBOSE) Log.v(TAG, "Created ImageReader size " + size.toString());
+ }
+
+ /**
+ * Create an {@link ImageReader} object.
+ *
+ * <p>This function creates image reader object for given format, maxImages, and size.</p>
+ *
+ * @param size The size of this ImageReader to be created.
+ * @param format The format of this ImageReader to be created
+ * @param maxNumImages The max number of images that can be acquired simultaneously.
+ * @param listener The listener used by this ImageReader to notify callbacks.
+ */
+
+ protected ImageReader createImageReader(Size size, int format, int maxNumImages,
+ ImageReader.OnImageAvailableListener listener) throws Exception {
+
+ ImageReader reader = null;
+ reader = ImageReader.newInstance(size.getWidth(), size.getHeight(),
+ format, maxNumImages);
+
+ reader.setOnImageAvailableListener(listener, mHandler);
+ if (VERBOSE) Log.v(TAG, "Created ImageReader size " + size.toString());
+ return reader;
+ }
+
+ /**
+ * Create an {@link ImageReader} object.
+ *
+ * <p>This function creates image reader object for given format, maxImages, usage and size.</p>
+ *
+ * @param size The size of this ImageReader to be created.
+ * @param format The format of this ImageReader to be created
+ * @param maxNumImages The max number of images that can be acquired simultaneously.
+ * @param usage The usage flag of the ImageReader
+ * @param listener The listener used by this ImageReader to notify callbacks.
+ */
+
+ protected ImageReader createImageReader(Size size, int format, int maxNumImages, long usage,
+ ImageReader.OnImageAvailableListener listener) throws Exception {
+ ImageReader reader = null;
+ reader = ImageReader.newInstance(size.getWidth(), size.getHeight(),
+ format, maxNumImages, usage);
+
+ reader.setOnImageAvailableListener(listener, mHandler);
+ if (VERBOSE) Log.v(TAG, "Created ImageReader size " + size.toString());
+ return reader;
+ }
+
+ /**
+ * Close the pending images then close current default {@link ImageReader} object.
+ */
+ protected void closeDefaultImageReader() {
+ closeImageReader(mReader);
+ mReader = null;
+ mReaderSurface = null;
+ }
+
+ /**
+ * Close an image reader instance.
+ *
+ * @param reader
+ */
+ protected void closeImageReader(ImageReader reader) {
+ if (reader != null) {
+ try {
+ // Close all possible pending images first.
+ Image image = reader.acquireLatestImage();
+ if (image != null) {
+ image.close();
+ }
+ } finally {
+ reader.close();
+ reader = null;
+ }
+ }
+ }
+
+ protected void checkImageReaderSessionConfiguration(String msg) throws Exception {
+ List<OutputConfiguration> outputConfigs = new ArrayList<OutputConfiguration>();
+ outputConfigs.add(new OutputConfiguration(mReaderSurface));
+
+ checkSessionConfigurationSupported(mCamera, mHandler, outputConfigs, /*inputConfig*/ null,
+ SessionConfiguration.SESSION_REGULAR, /*expectedResult*/ true, msg);
+ }
+
+ protected CaptureRequest prepareCaptureRequest() throws Exception {
+ return prepareCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+ }
+
+ protected CaptureRequest prepareCaptureRequest(int template) throws Exception {
+ List<Surface> outputSurfaces = new ArrayList<Surface>();
+ Surface surface = mReader.getSurface();
+ assertNotNull("Fail to get surface from ImageReader", surface);
+ outputSurfaces.add(surface);
+ return prepareCaptureRequestForSurfaces(outputSurfaces, template)
+ .build();
+ }
+
+ protected CaptureRequest.Builder prepareCaptureRequestForSurfaces(List<Surface> surfaces,
+ int template)
+ throws Exception {
+ createSession(surfaces);
+
+ CaptureRequest.Builder captureBuilder =
+ mCamera.createCaptureRequest(template);
+ assertNotNull("Fail to get captureRequest", captureBuilder);
+ for (Surface surface : surfaces) {
+ captureBuilder.addTarget(surface);
+ }
+
+ return captureBuilder;
+ }
+
+ protected CaptureRequest.Builder prepareCaptureRequestForConfigs(
+ List<OutputConfiguration> outputConfigs, int template) throws Exception {
+ createSessionByConfigs(outputConfigs);
+
+ CaptureRequest.Builder captureBuilder =
+ mCamera.createCaptureRequest(template);
+ assertNotNull("Fail to get captureRequest", captureBuilder);
+ for (OutputConfiguration config : outputConfigs) {
+ for (Surface s : config.getSurfaces()) {
+ captureBuilder.addTarget(s);
+ }
+ }
+
+ return captureBuilder;
+ }
+
+ /**
+ * Test the invalid Image access: accessing a closed image must result in
+ * {@link IllegalStateException}.
+ *
+ * @param closedImage The closed image.
+ * @param closedBuffer The ByteBuffer from a closed Image. buffer invalid
+ * access will be skipped if it is null.
+ */
+ protected void imageInvalidAccessTestAfterClose(Image closedImage,
+ Plane closedPlane, ByteBuffer closedBuffer) {
+ if (closedImage == null) {
+ throw new IllegalArgumentException(" closedImage must be non-null");
+ }
+ if (closedBuffer != null && !closedBuffer.isDirect()) {
+ throw new IllegalArgumentException("The input ByteBuffer should be direct ByteBuffer");
+ }
+
+ if (closedPlane != null) {
+ // Plane#getBuffer test
+ try {
+ closedPlane.getBuffer(); // An ISE should be thrown here.
+ fail("Image should throw IllegalStateException when calling getBuffer"
+ + " after the image is closed");
+ } catch (IllegalStateException e) {
+ // Expected.
+ }
+
+ // Plane#getPixelStride test
+ try {
+ closedPlane.getPixelStride(); // An ISE should be thrown here.
+ fail("Image should throw IllegalStateException when calling getPixelStride"
+ + " after the image is closed");
+ } catch (IllegalStateException e) {
+ // Expected.
+ }
+
+ // Plane#getRowStride test
+ try {
+ closedPlane.getRowStride(); // An ISE should be thrown here.
+ fail("Image should throw IllegalStateException when calling getRowStride"
+ + " after the image is closed");
+ } catch (IllegalStateException e) {
+ // Expected.
+ }
+ }
+
+ // ByteBuffer access test
+ if (closedBuffer != null) {
+ try {
+ closedBuffer.get(); // An ISE should be thrown here.
+ fail("Image should throw IllegalStateException when accessing a byte buffer"
+ + " after the image is closed");
+ } catch (IllegalStateException e) {
+ // Expected.
+ }
+ }
+
+ // Image#getFormat test
+ try {
+ closedImage.getFormat();
+ fail("Image should throw IllegalStateException when calling getFormat"
+ + " after the image is closed");
+ } catch (IllegalStateException e) {
+ // Expected.
+ }
+
+ // Image#getWidth test
+ try {
+ closedImage.getWidth();
+ fail("Image should throw IllegalStateException when calling getWidth"
+ + " after the image is closed");
+ } catch (IllegalStateException e) {
+ // Expected.
+ }
+
+ // Image#getHeight test
+ try {
+ closedImage.getHeight();
+ fail("Image should throw IllegalStateException when calling getHeight"
+ + " after the image is closed");
+ } catch (IllegalStateException e) {
+ // Expected.
+ }
+
+ // Image#getTimestamp test
+ try {
+ closedImage.getTimestamp();
+ fail("Image should throw IllegalStateException when calling getTimestamp"
+ + " after the image is closed");
+ } catch (IllegalStateException e) {
+ // Expected.
+ }
+
+ // Image#getTimestamp test
+ try {
+ closedImage.getTimestamp();
+ fail("Image should throw IllegalStateException when calling getTimestamp"
+ + " after the image is closed");
+ } catch (IllegalStateException e) {
+ // Expected.
+ }
+
+ // Image#getCropRect test
+ try {
+ closedImage.getCropRect();
+ fail("Image should throw IllegalStateException when calling getCropRect"
+ + " after the image is closed");
+ } catch (IllegalStateException e) {
+ // Expected.
+ }
+
+ // Image#setCropRect test
+ try {
+ Rect rect = new Rect();
+ closedImage.setCropRect(rect);
+ fail("Image should throw IllegalStateException when calling setCropRect"
+ + " after the image is closed");
+ } catch (IllegalStateException e) {
+ // Expected.
+ }
+
+ // Image#getPlanes test
+ try {
+ closedImage.getPlanes();
+ fail("Image should throw IllegalStateException when calling getPlanes"
+ + " after the image is closed");
+ } catch (IllegalStateException e) {
+ // Expected.
+ }
+ }
+}
diff --git a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java
index 4cd0046..e4695e9 100644
--- a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java
+++ b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java
@@ -22,6 +22,7 @@
import android.content.Context;
import android.graphics.ImageFormat;
import android.graphics.Rect;
+import android.hardware.cts.helpers.CameraParameterizedTestCase;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCaptureSession.CaptureCallback;
import android.hardware.camera2.CameraCharacteristics;
@@ -31,6 +32,7 @@
import android.hardware.camera2.params.OutputConfiguration;
import android.hardware.camera2.params.SessionConfiguration;
import android.util.Size;
+import android.hardware.camera2.cts.Camera2ParameterizedTestCase;
import android.hardware.camera2.cts.CameraTestUtils;
import android.hardware.camera2.cts.helpers.CameraErrorCollector;
import android.hardware.camera2.cts.helpers.StaticMetadata;
@@ -44,6 +46,7 @@
import android.util.Log;
import android.view.Surface;
import android.view.WindowManager;
+import androidx.test.InstrumentationRegistry;
import com.android.ex.camera2.blocking.BlockingSessionCallback;
import com.android.ex.camera2.blocking.BlockingStateCallback;
@@ -55,7 +58,11 @@
import java.util.HashMap;
import java.util.List;
-public class Camera2AndroidTestCase extends AndroidTestCase {
+import org.junit.Ignore;
+import org.junit.Test;
+
+// TODO: Can we de-duplicate this with Camera2AndroidBasicTestCase keeping in mind CtsVerifier ?
+public class Camera2AndroidTestCase extends Camera2ParameterizedTestCase {
private static final String TAG = "Camera2AndroidTestCase";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -63,12 +70,10 @@
protected static final Size DEFAULT_CAPTURE_SIZE = new Size(640, 480);
protected static final int CAPTURE_WAIT_TIMEOUT_MS = 5000;
- protected CameraManager mCameraManager;
protected CameraDevice mCamera;
protected CameraCaptureSession mCameraSession;
protected BlockingSessionCallback mCameraSessionListener;
protected BlockingStateCallback mCameraListener;
- protected String[] mCameraIds;
// include both standalone camera IDs and "hidden" physical camera IDs
protected String[] mAllCameraIds;
protected HashMap<String, StaticMetadata> mAllStaticInfo;
@@ -85,32 +90,15 @@
protected WindowManager mWindowManager;
- @Override
- public void setContext(Context context) {
- super.setContext(context);
- mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
- assertNotNull("Can't connect to camera manager!", mCameraManager);
- mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
- }
-
/**
* Set up the camera2 test case required environments, including CameraManager,
* HandlerThread, Camera IDs, and CameraStateCallback etc.
*/
@Override
- protected void setUp() throws Exception {
+ public void setUp() throws Exception {
super.setUp();
+ mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
- /**
- * Workaround for mockito and JB-MR2 incompatibility
- *
- * Avoid java.lang.IllegalArgumentException: dexcache == null
- * https://code.google.com/p/dexmaker/issues/detail?id=2
- */
- System.setProperty("dexmaker.dexcache", getContext().getCacheDir().toString());
-
- mCameraIds = mCameraManager.getCameraIdList();
- assertNotNull("Camera ids shouldn't be null", mCameraIds);
mHandlerThread = new HandlerThread(TAG);
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
@@ -125,14 +113,14 @@
mAllStaticInfo = new HashMap<String, StaticMetadata>();
List<String> hiddenPhysicalIds = new ArrayList<>();
- for (String cameraId : mCameraIds) {
+ for (String cameraId : mCameraIdsUnderTest) {
CameraCharacteristics props = mCameraManager.getCameraCharacteristics(cameraId);
StaticMetadata staticMetadata = new StaticMetadata(props,
CheckLevel.ASSERT, /*collector*/null);
mAllStaticInfo.put(cameraId, staticMetadata);
for (String physicalId : props.getPhysicalCameraIds()) {
- if (!Arrays.asList(mCameraIds).contains(physicalId) &&
+ if (!Arrays.asList(mCameraIdsUnderTest).contains(physicalId) &&
!hiddenPhysicalIds.contains(physicalId)) {
hiddenPhysicalIds.add(physicalId);
props = mCameraManager.getCameraCharacteristics(physicalId);
@@ -143,23 +131,15 @@
}
}
}
- mAllCameraIds = new String[mCameraIds.length + hiddenPhysicalIds.size()];
- System.arraycopy(mCameraIds, 0, mAllCameraIds, 0, mCameraIds.length);
+ mAllCameraIds = new String[mCameraIdsUnderTest.length + hiddenPhysicalIds.size()];
+ System.arraycopy(mCameraIdsUnderTest, 0, mAllCameraIds, 0, mCameraIdsUnderTest.length);
for (int i = 0; i < hiddenPhysicalIds.size(); i++) {
- mAllCameraIds[mCameraIds.length + i] = hiddenPhysicalIds.get(i);
+ mAllCameraIds[mCameraIdsUnderTest.length + i] = hiddenPhysicalIds.get(i);
}
}
@Override
- protected void tearDown() throws Exception {
- String[] cameraIdsPostTest = mCameraManager.getCameraIdList();
- assertNotNull("Camera ids shouldn't be null", cameraIdsPostTest);
- Log.i(TAG, "Camera ids in setup:" + Arrays.toString(mCameraIds));
- Log.i(TAG, "Camera ids in tearDown:" + Arrays.toString(cameraIdsPostTest));
- assertTrue(
- "Number of cameras changed from " + mCameraIds.length + " to " +
- cameraIdsPostTest.length,
- mCameraIds.length == cameraIdsPostTest.length);
+ public void tearDown() throws Exception {
mHandlerThread.quitSafely();
mHandler = null;
closeDefaultImageReader();
diff --git a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java
index c345b41..090aa6c 100644
--- a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java
+++ b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java
@@ -28,6 +28,8 @@
import android.graphics.RectF;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.cts.Camera2ParameterizedTestCase;
+import android.hardware.camera2.cts.CameraTestUtils;
import android.hardware.camera2.CameraCaptureSession.CaptureCallback;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
@@ -58,6 +60,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Rule;
import java.util.Arrays;
@@ -67,7 +70,8 @@
/**
* Camera2 test case base class by using mixed SurfaceView and TextureView as rendering target.
*/
-public class Camera2MultiViewTestCase {
+
+public class Camera2MultiViewTestCase extends Camera2ParameterizedTestCase {
private static final String TAG = "MultiViewTestCase";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
@@ -75,13 +79,10 @@
protected TextureView[] mTextureView =
new TextureView[Camera2MultiViewCtsActivity.MAX_TEXTURE_VIEWS];
- protected String[] mCameraIds;
protected Handler mHandler;
- private CameraManager mCameraManager;
private HandlerThread mHandlerThread;
private Activity mActivity;
- private Context mContext;
private CameraHolder[] mCameraHolders;
private HashMap<String, Integer> mCameraIdMap;
@@ -92,15 +93,10 @@
public ActivityTestRule<Camera2MultiViewCtsActivity> mActivityRule =
new ActivityTestRule<>(Camera2MultiViewCtsActivity.class);
- @Before
+ @Override
public void setUp() throws Exception {
+ super.setUp();
mActivity = mActivityRule.getActivity();
- mContext = mActivity.getApplicationContext();
- assertNotNull("Unable to get activity", mContext);
- mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
- assertNotNull("Unable to get CameraManager", mCameraManager);
- mCameraIds = mCameraManager.getCameraIdList();
- assertNotNull("Unable to get camera ids", mCameraIds);
mHandlerThread = new HandlerThread(TAG);
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
@@ -110,25 +106,17 @@
}
assertNotNull("Unable to get texture view", mTextureView);
mCameraIdMap = new HashMap<String, Integer>();
- int numCameras = mCameraIds.length;
+ int numCameras = mCameraIdsUnderTest.length;
mCameraHolders = new CameraHolder[numCameras];
for (int i = 0; i < numCameras; i++) {
- mCameraHolders[i] = new CameraHolder(mCameraIds[i]);
- mCameraIdMap.put(mCameraIds[i], i);
+ mCameraHolders[i] = new CameraHolder(mCameraIdsUnderTest[i]);
+ mCameraIdMap.put(mCameraIdsUnderTest[i], i);
}
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
}
- @After
+ @Override
public void tearDown() throws Exception {
- String[] cameraIdsPostTest = mCameraManager.getCameraIdList();
- assertNotNull("Camera ids shouldn't be null", cameraIdsPostTest);
- Log.i(TAG, "Camera ids in setup:" + Arrays.toString(mCameraIds));
- Log.i(TAG, "Camera ids in tearDown:" + Arrays.toString(cameraIdsPostTest));
- assertTrue(
- "Number of cameras changed from " + mCameraIds.length + " to " +
- cameraIdsPostTest.length,
- mCameraIds.length == cameraIdsPostTest.length);
mHandlerThread.quitSafely();
mHandler = null;
for (CameraHolder camera : mCameraHolders) {
@@ -137,6 +125,7 @@
camera = null;
}
}
+ super.tearDown();
}
/**
diff --git a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
index 5a95d62..ecc87d6 100644
--- a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
+++ b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
@@ -19,6 +19,8 @@
import static android.hardware.camera2.cts.CameraTestUtils.*;
import static com.android.ex.camera2.blocking.BlockingStateCallback.STATE_CLOSED;
+import androidx.test.InstrumentationRegistry;
+import android.app.UiAutomation;
import android.content.Context;
import android.graphics.ImageFormat;
@@ -32,6 +34,7 @@
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.cts.Camera2SurfaceViewCtsActivity;
+import android.hardware.camera2.cts.Camera2ParameterizedTestCase;
import android.hardware.camera2.cts.CameraTestUtils;
import android.hardware.camera2.cts.CameraTestUtils.SimpleCaptureCallback;
import android.hardware.camera2.cts.helpers.CameraErrorCollector;
@@ -57,6 +60,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Rule;
import java.io.File;
@@ -64,6 +68,12 @@
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
/**
* Camera2 Preview test case base class by using SurfaceView as rendering target.
@@ -75,7 +85,7 @@
* </p>
*/
-public class Camera2SurfaceViewTestCase {
+public class Camera2SurfaceViewTestCase extends Camera2ParameterizedTestCase {
private static final String TAG = "SurfaceViewTestCase";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
private static final int WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS = 1000;
@@ -86,9 +96,6 @@
protected static final int NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY = 8;
protected static final int MIN_FRAME_DURATION_ERROR_MARGIN = 100; // ns
- protected Context mContext;
- protected CameraManager mCameraManager;
- protected String[] mCameraIds;
protected HandlerThread mHandlerThread;
protected Handler mHandler;
protected BlockingStateCallback mCameraListener;
@@ -119,18 +126,7 @@
@Before
public void setUp() throws Exception {
- mContext = mActivityRule.getActivity().getApplicationContext();
- /**
- * Workaround for mockito and JB-MR2 incompatibility
- *
- * Avoid java.lang.IllegalArgumentException: dexcache == null
- * https://code.google.com/p/dexmaker/issues/detail?id=2
- */
- System.setProperty("dexmaker.dexcache", mContext.getCacheDir().toString());
- mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
- assertNotNull("Unable to get CameraManager", mCameraManager);
- mCameraIds = mCameraManager.getCameraIdList();
- assertNotNull("Unable to get camera ids", mCameraIds);
+ super.setUp();
mHandlerThread = new HandlerThread(TAG);
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
@@ -145,14 +141,14 @@
mAllStaticInfo = new HashMap<String, StaticMetadata>();
List<String> hiddenPhysicalIds = new ArrayList<>();
- for (String cameraId : mCameraIds) {
+ for (String cameraId : mCameraIdsUnderTest) {
CameraCharacteristics props = mCameraManager.getCameraCharacteristics(cameraId);
StaticMetadata staticMetadata = new StaticMetadata(props,
CheckLevel.ASSERT, /*collector*/null);
mAllStaticInfo.put(cameraId, staticMetadata);
for (String physicalId : props.getPhysicalCameraIds()) {
- if (!Arrays.asList(mCameraIds).contains(physicalId) &&
+ if (!Arrays.asList(mCameraIdsUnderTest).contains(physicalId) &&
!hiddenPhysicalIds.contains(physicalId)) {
hiddenPhysicalIds.add(physicalId);
props = mCameraManager.getCameraCharacteristics(physicalId);
@@ -169,15 +165,6 @@
@After
public void tearDown() throws Exception {
- String[] cameraIdsPostTest = mCameraManager.getCameraIdList();
- assertNotNull("Camera ids shouldn't be null", cameraIdsPostTest);
- Log.i(TAG, "Camera ids in setup:" + Arrays.toString(mCameraIds));
- Log.i(TAG, "Camera ids in tearDown:" + Arrays.toString(cameraIdsPostTest));
- assertTrue(
- "Number of cameras changed from " + mCameraIds.length + " to " +
- cameraIdsPostTest.length,
- mCameraIds.length == cameraIdsPostTest.length);
- // Teardown the camera preview required environments.
mHandlerThread.quitSafely();
mHandler = null;
mCameraListener = null;
@@ -188,6 +175,7 @@
// When new Exception(e) is used, exception info will be printed twice.
throw new Exception(e.getMessage());
}
+ super.tearDown();
}
/**
@@ -541,7 +529,7 @@
List<Integer> expectedAeStates = new ArrayList<Integer>();
expectedAeStates.add(new Integer(CaptureResult.CONTROL_AE_STATE_LOCKED));
CameraTestUtils.waitForAnyResultValue(resultListener, CaptureResult.CONTROL_AE_STATE,
- expectedAeStates, WAIT_FOR_RESULT_TIMEOUT_MS, NUM_RESULTS_WAIT_TIMEOUT);
+ expectedAeStates, NUM_RESULTS_WAIT_TIMEOUT, WAIT_FOR_RESULT_TIMEOUT_MS);
}
/**
diff --git a/tests/camera/src/android/hardware/multiprocess/camera/cts/Camera2Activity.java b/tests/camera/src/android/hardware/multiprocess/camera/cts/Camera2Activity.java
index 418eb7c..398ffb8 100644
--- a/tests/camera/src/android/hardware/multiprocess/camera/cts/Camera2Activity.java
+++ b/tests/camera/src/android/hardware/multiprocess/camera/cts/Camera2Activity.java
@@ -63,7 +63,7 @@
" could not connect camera service");
return;
}
- String[] cameraIds = manager.getCameraIdList();
+ String[] cameraIds = manager.getCameraIdListNoLazy();
if (cameraIds == null || cameraIds.length == 0) {
mErrorServiceConnection.logAsync(TestConstants.EVENT_CAMERA_ERROR, TAG +
diff --git a/tests/camera/src/android/hardware/multiprocess/camera/cts/CameraEvictionTest.java b/tests/camera/src/android/hardware/multiprocess/camera/cts/CameraEvictionTest.java
index 2a69aed..14dbd92 100644
--- a/tests/camera/src/android/hardware/multiprocess/camera/cts/CameraEvictionTest.java
+++ b/tests/camera/src/android/hardware/multiprocess/camera/cts/CameraEvictionTest.java
@@ -165,7 +165,7 @@
public void testBasicCamera2ActivityEviction() throws Throwable {
CameraManager manager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
assertNotNull(manager);
- String[] cameraIds = manager.getCameraIdList();
+ String[] cameraIds = manager.getCameraIdListNoLazy();
if (cameraIds.length == 0) {
Log.i(TAG, "Skipping testBasicCamera2ActivityEviction, device has no cameras.");
@@ -269,7 +269,7 @@
int PERMISSION_CALLBACK_TIMEOUT_MS = 2000;
CameraManager manager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
assertNotNull(manager);
- String[] cameraIds = manager.getCameraIdList();
+ String[] cameraIds = manager.getCameraIdListNoLazy();
if (cameraIds.length == 0) {
Log.i(TAG, "Skipping testCamera2AccessCallback, device has no cameras.");
@@ -305,7 +305,7 @@
int PERMISSION_CALLBACK_TIMEOUT_MS = 2000;
CameraManager manager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
assertNotNull(manager);
- String[] cameraIds = manager.getCameraIdList();
+ String[] cameraIds = manager.getCameraIdListNoLazy();
if (cameraIds.length == 0) {
Log.i(TAG, "Skipping testBasicCamera2AccessCallback, device has no cameras.");
diff --git a/tests/camera/utils/src/android/hardware/camera2/cts/Camera2ParameterizedTestCase.java b/tests/camera/utils/src/android/hardware/camera2/cts/Camera2ParameterizedTestCase.java
new file mode 100644
index 0000000..c7deb5a
--- /dev/null
+++ b/tests/camera/utils/src/android/hardware/camera2/cts/Camera2ParameterizedTestCase.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2.cts;
+
+import android.content.Context;
+import android.hardware.cts.helpers.CameraParameterizedTestCase;
+import android.hardware.camera2.cts.CameraTestUtils;
+import android.hardware.camera2.CameraManager;
+import android.util.Log;
+
+import java.util.Arrays;
+
+import org.junit.Ignore;
+
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertTrue;
+
+public class Camera2ParameterizedTestCase extends CameraParameterizedTestCase {
+ private static final String TAG = "Camera2ParameterizedTestCase";
+ protected CameraManager mCameraManager;
+ // The list of camera ids we're testing. If we're testing system cameras
+ // (mAdoptShellPerm == true), we have only system camera ids in the array and not normal camera
+ // ids.
+ protected String[] mCameraIdsUnderTest;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ /**
+ * Workaround for mockito and JB-MR2 incompatibility
+ *
+ * Avoid java.lang.IllegalArgumentException: dexcache == null
+ * https://code.google.com/p/dexmaker/issues/detail?id=2
+ */
+ System.setProperty("dexmaker.dexcache", mContext.getCacheDir().toString());
+ mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
+ assertNotNull("Unable to get CameraManager", mCameraManager);
+ mCameraIdsUnderTest =
+ CameraTestUtils.getCameraIdListForTesting(mCameraManager, mAdoptShellPerm);
+ assertNotNull("Unable to get camera ids", mCameraIdsUnderTest);
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ String[] cameraIdsPostTest =
+ CameraTestUtils.getCameraIdListForTesting(mCameraManager, mAdoptShellPerm);
+ assertNotNull("Camera ids shouldn't be null", cameraIdsPostTest);
+ Log.i(TAG, "Camera ids in setup:" + Arrays.toString(mCameraIdsUnderTest));
+ Log.i(TAG, "Camera ids in tearDown:" + Arrays.toString(cameraIdsPostTest));
+ assertTrue(
+ "Number of cameras changed from " + mCameraIdsUnderTest.length + " to " +
+ cameraIdsPostTest.length,
+ mCameraIdsUnderTest.length == cameraIdsPostTest.length);
+ super.tearDown();
+ }
+}
diff --git a/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java b/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
index e408cb5..b49a0a6 100644
--- a/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
+++ b/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
@@ -704,6 +704,38 @@
}
}
+ public static boolean hasCapability(CameraCharacteristics characteristics, int capability) {
+ int [] capabilities =
+ characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
+ for (int c : capabilities) {
+ if (c == capability) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static boolean isSystemCamera(CameraManager manager, String cameraId)
+ throws CameraAccessException {
+ CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
+ return hasCapability(characteristics,
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA);
+ }
+
+ public static String[] getCameraIdListForTesting(CameraManager manager,
+ boolean getSystemCameras)
+ throws CameraAccessException {
+ String [] ids = manager.getCameraIdListNoLazy();
+ List<String> idsForTesting = new ArrayList<String>();
+ for (String id : ids) {
+ boolean isSystemCamera = isSystemCamera(manager, id);
+ if (getSystemCameras == isSystemCamera) {
+ idsForTesting.add(id);
+ }
+ }
+ return idsForTesting.toArray(new String[idsForTesting.size()]);
+ }
+
/**
* Block until the camera is opened.
*
diff --git a/tests/camera/utils/src/android/hardware/camera2/cts/helpers/CameraMetadataGetter.java b/tests/camera/utils/src/android/hardware/camera2/cts/helpers/CameraMetadataGetter.java
index db75cdd..e0a956e 100644
--- a/tests/camera/utils/src/android/hardware/camera2/cts/helpers/CameraMetadataGetter.java
+++ b/tests/camera/utils/src/android/hardware/camera2/cts/helpers/CameraMetadataGetter.java
@@ -108,7 +108,7 @@
CameraCharacteristics staticMetadata;
String[] cameraIds;
try {
- cameraIds = mCameraManager.getCameraIdList();
+ cameraIds = mCameraManager.getCameraIdListNoLazy();
} catch (CameraAccessException e) {
Log.e(TAG, "Unable to get camera ids, skip this info, error: " + e.getMessage());
return "";
@@ -180,7 +180,7 @@
StringBuffer templates = new StringBuffer("{\"CameraRequestTemplates\":{");
String[] cameraIds;
try {
- cameraIds = mCameraManager.getCameraIdList();
+ cameraIds = mCameraManager.getCameraIdListNoLazy();
} catch (CameraAccessException e) {
Log.e(TAG, "Unable to get camera ids, skip this info, error: " + e.getMessage());
return "";
diff --git a/tests/camera/utils/src/android/hardware/cts/helpers/CameraParameterizedTestCase.java b/tests/camera/utils/src/android/hardware/cts/helpers/CameraParameterizedTestCase.java
new file mode 100644
index 0000000..b2f4c04
--- /dev/null
+++ b/tests/camera/utils/src/android/hardware/cts/helpers/CameraParameterizedTestCase.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers;
+
+import android.app.UiAutomation;
+import android.content.Context;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+import android.app.Activity;
+import androidx.test.rule.ActivityTestRule;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class CameraParameterizedTestCase {
+ protected UiAutomation mUiAutomation;
+ protected Context mContext;
+ @Parameter(0)
+ public boolean mAdoptShellPerm;
+
+ @Parameters
+ public static Iterable<? extends Object> data() {
+ List<Boolean> adoptShellPerm = new ArrayList<Boolean>();
+ adoptShellPerm.add(true);
+ adoptShellPerm.add(false);
+ return adoptShellPerm;
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+ mContext = InstrumentationRegistry.getTargetContext();
+ if (mAdoptShellPerm) {
+ mUiAutomation.adoptShellPermissionIdentity();
+ }
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (mAdoptShellPerm) {
+ mUiAutomation.dropShellPermissionIdentity();
+ }
+ }
+}
diff --git a/tests/camera/utils/src/android/hardware/cts/helpers/CameraUtils.java b/tests/camera/utils/src/android/hardware/cts/helpers/CameraUtils.java
index fe119a9..0c143a5 100644
--- a/tests/camera/utils/src/android/hardware/cts/helpers/CameraUtils.java
+++ b/tests/camera/utils/src/android/hardware/cts/helpers/CameraUtils.java
@@ -38,7 +38,7 @@
*/
public static boolean isLegacyHAL(Context context, int cameraId) throws Exception {
CameraManager manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
- String cameraIdStr = manager.getCameraIdList()[cameraId];
+ String cameraIdStr = manager.getCameraIdListNoLazy()[cameraId];
CameraCharacteristics characteristics =
manager.getCameraCharacteristics(cameraIdStr);
@@ -56,7 +56,7 @@
*/
public static boolean isExternal(Context context, int cameraId) throws Exception {
CameraManager manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
- String cameraIdStr = manager.getCameraIdList()[cameraId];
+ String cameraIdStr = manager.getCameraIdListNoLazy()[cameraId];
CameraCharacteristics characteristics =
manager.getCameraCharacteristics(cameraIdStr);
diff --git a/tests/contentcaptureservice/OWNERS b/tests/contentcaptureservice/OWNERS
index 4df1ffa..2abf830 100644
--- a/tests/contentcaptureservice/OWNERS
+++ b/tests/contentcaptureservice/OWNERS
@@ -1,2 +1,4 @@
# Bug component: 544200
-felipeal@google.com
\ No newline at end of file
+adamhe@google.com
+svetoslavganov@google.com
+felipeal@google.com
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/AbstractContentCaptureIntegrationTest.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/AbstractContentCaptureIntegrationTest.java
index 8f77b2b..2534412 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/AbstractContentCaptureIntegrationTest.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/AbstractContentCaptureIntegrationTest.java
@@ -44,6 +44,7 @@
import com.android.compatibility.common.util.SettingsStateChangerRule;
import com.android.compatibility.common.util.SettingsUtils;
+import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
@@ -201,6 +202,12 @@
CtsContentCaptureService.resetStaticState();
}
+ @After
+ public void clearServiceWatcher() {
+ Log.v(mTag, "@After: clearServiceWatcher()");
+ CtsContentCaptureService.clearServiceWatcher();
+ }
+
@Nullable
public static void setFeatureEnabledBySettings(@Nullable boolean enabled) {
SettingsUtils.syncSet(sContext, CONTENT_CAPTURE_ENABLED, enabled ? "1" : "0");
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Assertions.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Assertions.java
index 0f7e298..866b947 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Assertions.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Assertions.java
@@ -311,12 +311,17 @@
@NonNull List<ContentCaptureEvent> events, int minimumSize,
@NonNull AutofillId... expectedIds) {
final int actualSize = events.size();
+ final int disappearedEventIndex;
if (actualSize == minimumSize) {
// Activity stopped before TYPE_VIEW_DISAPPEARED were sent.
return false;
+ } else if (actualSize == minimumSize + 1) {
+ // Activity did not receive TYPE_VIEW_TREE_APPEARING and TYPE_VIEW_TREE_APPEARED.
+ disappearedEventIndex = minimumSize;
+ } else {
+ disappearedEventIndex = minimumSize + 1;
}
- assertThat(events).hasSize(minimumSize + 1);
- final ContentCaptureEvent batchDisappearEvent = events.get(minimumSize);
+ final ContentCaptureEvent batchDisappearEvent = events.get(disappearedEventIndex);
if (expectedIds.length == 1) {
assertWithMessage("Should have just one deleted id on %s", batchDisappearEvent)
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/ChildlessActivityTest.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/ChildlessActivityTest.java
index 9f453b8..5beb910 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/ChildlessActivityTest.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/ChildlessActivityTest.java
@@ -1140,6 +1140,7 @@
watcher1.waitFor(DESTROYED);
// Re-enable feature
+ CtsContentCaptureService.clearServiceWatcher();
final ServiceWatcher reconnectionWatcher = CtsContentCaptureService.setServiceWatcher();
reconnectionWatcher.whitelistSelf();
setFeatureEnabled(service1, reason, /* enabled= */ true);
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CtsContentCaptureService.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CtsContentCaptureService.java
index 349ff36..840bd03 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CtsContentCaptureService.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CtsContentCaptureService.java
@@ -149,6 +149,16 @@
}
}
+ public static void clearServiceWatcher() {
+ if (sServiceWatcher != null) {
+ if (sServiceWatcher.mReadyToClear) {
+ sServiceWatcher.mService = null;
+ sServiceWatcher = null;
+ } else {
+ sServiceWatcher.mReadyToClear = true;
+ }
+ }
+ }
/**
* When set, doesn't throw exceptions when it receives an event from a session that doesn't
@@ -170,13 +180,14 @@
return;
}
- if (sServiceWatcher.mService != null) {
+ if (!sServiceWatcher.mReadyToClear && sServiceWatcher.mService != null) {
addException("onConnected(): already created: %s", sServiceWatcher);
return;
}
sServiceWatcher.mService = this;
sServiceWatcher.mCreated.countDown();
+ sServiceWatcher.mReadyToClear = false;
if (mConnectedLatch.getCount() == 0) {
addException("already connected: %s", mConnectedLatch);
@@ -208,8 +219,7 @@
latch.countDown();
}
sServiceWatcher.mDestroyed.countDown();
- sServiceWatcher.mService = null;
- sServiceWatcher = null;
+ clearServiceWatcher();
}
/**
@@ -479,6 +489,7 @@
private final CountDownLatch mCreated = new CountDownLatch(1);
private final CountDownLatch mDestroyed = new CountDownLatch(1);
+ private boolean mReadyToClear = true;
private Pair<Set<String>, Set<ComponentName>> mWhitelist;
private CtsContentCaptureService mService;
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomViewActivityTest.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomViewActivityTest.java
index e01ed72..74429de 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomViewActivityTest.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomViewActivityTest.java
@@ -111,6 +111,65 @@
}
/**
+ * Test for session lifecycle events.
+ */
+ @Test
+ public void testSessionLifecycleEvents() throws Exception {
+ final CtsContentCaptureService service = enableService();
+ final ActivityWatcher watcher = startWatcher();
+ final AtomicReference<CustomView> customViewRef = new AtomicReference<>();
+
+ CustomViewActivity.setCustomViewDelegate((customView, structure) -> {
+ customViewRef.set(customView);
+ final ContentCaptureSession session = customView.getContentCaptureSession();
+ session.notifySessionResumed();
+ session.notifySessionPaused();
+ });
+
+ final CustomViewActivity activity = launchActivity();
+ watcher.waitFor(RESUMED);
+
+ activity.finish();
+ watcher.waitFor(DESTROYED);
+
+ final Session session = service.getOnlyFinishedSession();
+ Log.v(TAG, "session id: " + session.id);
+
+ assertRightActivity(session, session.id, activity);
+
+ final View grandpa1 = (View) activity.mCustomView.getParent();
+ final View grandpa2 = (View) grandpa1.getParent();
+ final View decorView = activity.getDecorView();
+ final AutofillId customViewId = activity.mCustomView.getAutofillId();
+ Log.v(TAG, "assertJustInitialViewsAppeared(): grandpa1=" + grandpa1.getAutofillId()
+ + ", grandpa2=" + grandpa2.getAutofillId() + ", decor="
+ + decorView.getAutofillId() + "customView=" + customViewId);
+
+ final List<ContentCaptureEvent> events = session.getEvents();
+ Log.v(TAG, "events(" + events.size() + "): " + events);
+ final int additionalEvents = 2;
+
+ assertThat(events.size()).isAtLeast(CustomViewActivity.MIN_EVENTS + additionalEvents);
+
+ // Assert just the relevant events
+ assertSessionResumed(events, 0);
+ assertViewTreeStarted(events, 1);
+ assertDecorViewAppeared(events, 2, decorView);
+ assertViewAppeared(events, 3, grandpa2, decorView.getAutofillId());
+ assertViewAppeared(events, 4, grandpa1, grandpa2.getAutofillId());
+
+ // Assert for session lifecycle events.
+ assertSessionResumed(events, 5);
+ assertSessionPaused(events, 6);
+
+ assertViewWithUnknownParentAppeared(events, 7, session.id, customViewRef.get());
+ assertViewTreeFinished(events, 8);
+ assertSessionPaused(events, 9);
+
+ activity.assertInitialViewsDisappeared(events, additionalEvents);
+ }
+
+ /**
* Tests when the view has virtual children but it doesn't return right away and calls
* the session notification methods instead - this is wrong because the main view will be
* notified last, but we cannot prevent the apps from doing so...
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/LoginActivityTest.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/LoginActivityTest.java
index ea22140..aa4fa9e 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/LoginActivityTest.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/LoginActivityTest.java
@@ -122,6 +122,46 @@
}
@Test
+ public void testContentCaptureSessionCache() throws Exception {
+ final CtsContentCaptureService service = enableService();
+ final ActivityWatcher watcher = startWatcher();
+
+ final ContentCaptureContext clientContext = newContentCaptureContext();
+
+ final AtomicReference<ContentCaptureSession> mainSessionRef = new AtomicReference<>();
+ final AtomicReference<ContentCaptureSession> childSessionRef = new AtomicReference<>();
+
+ LoginActivity.onRootView((activity, rootView) -> {
+ final ContentCaptureSession mainSession = rootView.getContentCaptureSession();
+ mainSessionRef.set(mainSession);
+ final ContentCaptureSession childSession = mainSession
+ .createContentCaptureSession(clientContext);
+ childSessionRef.set(childSession);
+
+ rootView.setContentCaptureSession(childSession);
+ // Already called getContentCaptureSession() earlier, use cached session (main).
+ assertThat(rootView.getContentCaptureSession()).isEqualTo(childSession);
+
+ rootView.setContentCaptureSession(mainSession);
+ assertThat(rootView.getContentCaptureSession()).isEqualTo(mainSession);
+
+ rootView.setContentCaptureSession(childSession);
+ assertThat(rootView.getContentCaptureSession()).isEqualTo(childSession);
+ });
+
+ final LoginActivity activity = launchActivity();
+ watcher.waitFor(RESUMED);
+
+ activity.finish();
+ watcher.waitFor(DESTROYED);
+
+ final ContentCaptureSessionId childSessionId = childSessionRef.get()
+ .getContentCaptureSessionId();
+
+ assertSessionId(childSessionId, activity.getRootView());
+ }
+
+ @Test
public void testSimpleLifecycle_rootViewSession() throws Exception {
final CtsContentCaptureService service = enableService();
final ActivityWatcher watcher = startWatcher();
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/unit/ViewNodeTest.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/unit/ViewNodeTest.java
index 5ece59f..67d6670 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/unit/ViewNodeTest.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/unit/ViewNodeTest.java
@@ -143,6 +143,9 @@
assertThrows(NullPointerException.class, () -> structure.setTextIdEntry(null));
assertThat(node.getTextIdEntry()).isNull();
+
+ assertThrows(NullPointerException.class, () -> structure.setHintIdEntry(null));
+ assertThat(node.getHintIdEntry()).isNull();
}
@Test
@@ -363,6 +366,7 @@
structure.setText("IGNORE ME!");
structure.setText("Now we're talking!", 4, 8);
structure.setHint("Soylent Green is SPOILER ALERT");
+ structure.setHintIdEntry("HINT ID ENTRY");
structure.setTextStyle(15.0f, 16, 23, 42);
structure.setTextLines(new int[] {4, 8, 15} , new int[] {16, 23, 42});
return structure;
@@ -377,6 +381,7 @@
assertThat(node.getTextSelectionStart()).isEqualTo(4);
assertThat(node.getTextSelectionEnd()).isEqualTo(8);
assertThat(node.getHint()).isEqualTo("Soylent Green is SPOILER ALERT");
+ assertThat(node.getHintIdEntry()).isEqualTo("HINT ID ENTRY");
assertThat(node.getTextSize()).isWithin(1.0e-10f).of(15.0f);
assertThat(node.getTextColor()).isEqualTo(16);
assertThat(node.getTextBackgroundColor()).isEqualTo(23);
diff --git a/tests/core/runner/Android.bp b/tests/core/runner/Android.bp
deleted file mode 100644
index de3d724..0000000
--- a/tests/core/runner/Android.bp
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//==========================================================
-// Build the core runner.
-//==========================================================
-
-// Build library
-java_library {
- name: "cts-core-test-runner",
-
- srcs: ["src/**/*.java"],
- static_libs: [
- "compatibility-device-util",
- "android-support-test",
- "vogarexpect",
- "testng",
- ],
-
- libs: ["android.test.runner.stubs"],
- sdk_version: "test_current",
-
-}
-
-//==========================================================
-// Build the run listener
-//==========================================================
-
-// Build library
-java_library {
- name: "cts-test-runner",
-
- srcs: ["src/com/android/cts/runner/**/*.java"],
- static_libs: ["android-support-test"],
- sdk_version: "current",
-
-}
diff --git a/tests/core/runner/AndroidManifest.xml b/tests/core/runner/AndroidManifest.xml
deleted file mode 100644
index 001e6f2..0000000
--- a/tests/core/runner/AndroidManifest.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.core.tests.runner">
- <uses-permission android:name="android.permission.INTERNET" />
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-
- <application>
- <uses-library android:name="android.test.runner" />
- </application>
-
- <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
- android:targetPackage="android.core.tests.runner"
- android:label="cts framework tests"/>
-
-</manifest>
diff --git a/tests/core/runner/src/com/android/cts/core/runner/ExpectationBasedFilter.java b/tests/core/runner/src/com/android/cts/core/runner/ExpectationBasedFilter.java
deleted file mode 100644
index 4419b4b..0000000
--- a/tests/core/runner/src/com/android/cts/core/runner/ExpectationBasedFilter.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.cts.core.runner;
-
-import android.os.Bundle;
-import android.util.Log;
-import com.google.common.base.Splitter;
-import java.io.IOException;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Set;
-import javax.annotation.Nullable;
-import org.junit.runner.Description;
-import org.junit.runner.manipulation.Filter;
-import org.junit.runners.ParentRunner;
-import org.junit.runners.Suite;
-import vogar.expect.Expectation;
-import vogar.expect.ExpectationStore;
-import vogar.expect.ModeId;
-import vogar.expect.Result;
-
-/**
- * Filter out tests/classes that are not requested or which are expected to fail.
- *
- * <p>This filter has to handle both a hierarchy of {@code Description descriptions} that looks
- * something like this:
- * <pre>
- * Suite
- * Suite
- * Suite
- * ParentRunner
- * Test
- * ...
- * ...
- * ParentRunner
- * Test
- * ...
- * ...
- * Suite
- * ParentRunner
- * Test
- * ...
- * ...
- * ...
- * </pre>
- *
- * <p>It cannot filter out the non-leaf nodes in the hierarchy, i.e. {@link Suite} and
- * {@link ParentRunner}, as that would prevent it from traversing the hierarchy and finding
- * the leaf nodes.
- */
-class ExpectationBasedFilter extends Filter {
-
- static final String TAG = "ExpectationBasedFilter";
-
- private static final String ARGUMENT_EXPECTATIONS = "core-expectations";
-
- private static final Splitter CLASS_LIST_SPLITTER = Splitter.on(',').trimResults();
-
- private final ExpectationStore expectationStore;
-
- private static List<String> getExpectationResourcePaths(Bundle args) {
- return CLASS_LIST_SPLITTER.splitToList(args.getString(ARGUMENT_EXPECTATIONS));
- }
-
- public ExpectationBasedFilter(Bundle args) {
- ExpectationStore expectationStore = null;
- try {
- // Get the set of resource names containing the expectations.
- Set<String> expectationResources = new LinkedHashSet<>(
- getExpectationResourcePaths(args));
- Log.i(TAG, "Loading expectations from: " + expectationResources);
- expectationStore = ExpectationStore.parseResources(
- getClass(), expectationResources, ModeId.DEVICE);
- } catch (IOException e) {
- Log.e(TAG, "Could not initialize ExpectationStore: ", e);
- }
-
- this.expectationStore = expectationStore;
- }
-
- @Override
- public boolean shouldRun(Description description) {
- // Only filter leaf nodes. The description is for a test if and only if it is a leaf node.
- // Non-leaf nodes must not be filtered out as that would prevent leaf nodes from being
- // visited in the case when we are traversing the hierarchy of classes.
- Description testDescription = getTestDescription(description);
- if (testDescription != null) {
- String className = testDescription.getClassName();
- String methodName = testDescription.getMethodName();
- String testName = className + "#" + methodName;
-
- if (expectationStore != null) {
- Expectation expectation = expectationStore.get(testName);
- if (expectation.getResult() != Result.SUCCESS) {
- Log.d(TAG, "Excluding test " + testDescription
- + " as it matches expectation: " + expectation);
- return false;
- }
- }
- }
-
- return true;
- }
-
- private Description getTestDescription(Description description) {
- List<Description> children = description.getChildren();
- // An empty description is by definition a test.
- if (children.isEmpty()) {
- return description;
- }
-
- // Handle initialization errors that were wrapped in an ErrorReportingRunner as a special
- // case. This is needed because ErrorReportingRunner is treated as a suite of Throwables,
- // (where each Throwable corresponds to a test called initializationError) and so its
- // description contains children, one for each Throwable, and so is not treated as a test
- // to filter. Unfortunately, it does not support Filterable so this filter is never applied
- // to its children.
- // See https://github.com/junit-team/junit/issues/1253
- Description child = children.get(0);
- String methodName = child.getMethodName();
- if ("initializationError".equals(methodName)) {
- return child;
- }
-
- return null;
- }
-
- @Override
- public String describe() {
- return "TestFilter";
- }
-}
diff --git a/tests/core/runner/src/com/android/cts/core/runner/support/SingleTestNGTestRunListener.java b/tests/core/runner/src/com/android/cts/core/runner/support/SingleTestNGTestRunListener.java
deleted file mode 100644
index 7a68a8b..0000000
--- a/tests/core/runner/src/com/android/cts/core/runner/support/SingleTestNGTestRunListener.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.core.runner.support;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-/**
- * Listener for TestNG runs that provides gtest-like console output.
- *
- * Prints a message like [RUN], [OK], [ERROR], [SKIP] to stdout
- * as tests are being executed with their status.
- *
- * This output is also saved as the device logs (logcat) when the test is run through
- * cts-tradefed.
- */
-public class SingleTestNGTestRunListener implements org.testng.ITestListener {
- private int mTestStarted = 0;
-
- private Map<String, Throwable> failures = new LinkedHashMap<>();
-
- private static class Prefixes {
- @SuppressWarnings("unused")
- private static final String INFORMATIONAL_MARKER = "[----------]";
- private static final String START_TEST_MARKER = "[ RUN ]";
- private static final String OK_TEST_MARKER = "[ OK ]";
- private static final String ERROR_TEST_RUN_MARKER = "[ ERROR ]";
- private static final String SKIPPED_TEST_MARKER = "[ SKIP ]";
- private static final String TEST_RUN_MARKER = "[==========]";
- }
-
- // How many tests did TestNG *actually* try to run?
- public int getNumTestStarted() {
- return mTestStarted;
- }
-
- public Map<String, Throwable> getFailures() {
- return Collections.unmodifiableMap(failures);
- }
-
- @Override
- public void onFinish(org.testng.ITestContext context) {
- System.out.println(String.format("%s", Prefixes.TEST_RUN_MARKER));
- }
-
- @Override
- public void onStart(org.testng.ITestContext context) {
- System.out.println(String.format("%s", Prefixes.INFORMATIONAL_MARKER));
- }
-
- @Override
- public void onTestFailedButWithinSuccessPercentage(org.testng.ITestResult result) {
- onTestFailure(result);
- }
-
- @Override
- public void onTestFailure(org.testng.ITestResult result) {
- // All failures are coalesced into one '[ FAILED ]' message at the end
- // This is because a single test method can run multiple times with different parameters.
- // Since we only test a single method, it's safe to combine all failures into one
- // failure at the end.
- //
- // The big pass/fail is printed from SingleTestNGTestRunner, not from the listener.
- String id = getId(result);
- Throwable throwable = result.getThrowable();
- System.out.println(String.format("%s %s ::: %s", Prefixes.ERROR_TEST_RUN_MARKER,
- id, stringify(throwable)));
- failures.put(id, throwable);
- }
-
- @Override
- public void onTestSkipped(org.testng.ITestResult result) {
- System.out.println(String.format("%s %s", Prefixes.SKIPPED_TEST_MARKER,
- getId(result)));
- }
-
- @Override
- public void onTestStart(org.testng.ITestResult result) {
- mTestStarted++;
- System.out.println(String.format("%s %s", Prefixes.START_TEST_MARKER,
- getId(result)));
- }
-
- @Override
- public void onTestSuccess(org.testng.ITestResult result) {
- System.out.println(String.format("%s", Prefixes.OK_TEST_MARKER));
- }
-
- private String getId(org.testng.ITestResult test) {
- // TestNG is quite complicated since tests can have arbitrary parameters.
- // Use its code to stringify a result name instead of doing it ourselves.
-
- org.testng.remote.strprotocol.TestResultMessage msg =
- new org.testng.remote.strprotocol.TestResultMessage(
- null, /*suite name*/
- null, /*test name -- display the test method name instead */
- test);
-
- String className = test.getTestClass().getName();
- //String name = test.getMethod().getMethodName();
- return String.format("%s#%s", className, msg.toDisplayString());
-
- }
-
- private String stringify(Throwable error) {
- return Arrays.toString(error.getStackTrace()).replaceAll("\n", " ");
- }
-}
diff --git a/tests/core/runner/src/com/android/cts/core/runner/support/SingleTestNgTestExecutor.java b/tests/core/runner/src/com/android/cts/core/runner/support/SingleTestNgTestExecutor.java
deleted file mode 100644
index deb18df..0000000
--- a/tests/core/runner/src/com/android/cts/core/runner/support/SingleTestNgTestExecutor.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.core.runner.support;
-
-import android.util.Log;
-
-import org.testng.TestNG;
-import org.testng.xml.XmlClass;
-import org.testng.xml.XmlInclude;
-import org.testng.xml.XmlSuite;
-import org.testng.xml.XmlTest;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Test executor to run a single TestNG test method.
- */
-public class SingleTestNgTestExecutor {
- // Execute any method which is in the class klass.
- // The klass is passed in separately to handle inherited methods only.
- // Returns true if all tests pass, false otherwise.
- public static Result execute(Class<?> klass, String methodName) {
- if (klass == null) {
- throw new NullPointerException("klass must not be null");
- }
-
- if (methodName == null) {
- throw new NullPointerException("methodName must not be null");
- }
-
- //if (!method.getDeclaringClass().isAssignableFrom(klass)) {
- // throw new IllegalArgumentException("klass must match method's declaring class");
- //}
-
- SingleTestNGTestRunListener listener = new SingleTestNGTestRunListener();
-
- // Although creating a new testng "core" every time might seem heavyweight, in practice
- // it seems to take a mere few milliseconds at most.
- // Since we're running all the parameteric combinations of a test,
- // this ends up being neglible relative to that.
- TestNG testng = createTestNG(klass.getName(), methodName, listener);
- testng.run();
-
- if (listener.getNumTestStarted() <= 0) {
- // It's possible to be invoked here with an arbitrary method name
- // so print out a warning incase TestNG actually had a no-op.
- Log.w("TestNgExec", "execute class " + klass.getName() + ", method " + methodName +
- " had 0 tests executed. Not a test method?");
- }
-
- return new Result(testng.hasFailure(), listener.getFailures());
- }
-
- private static org.testng.TestNG createTestNG(String klass, String method,
- SingleTestNGTestRunListener listener) {
- org.testng.TestNG testng = new org.testng.TestNG();
- testng.setUseDefaultListeners(false); // Don't create the testng-specific HTML/XML reports.
- // It still prints the X/Y tests succeeded/failed summary to stdout.
-
- // We don't strictly need this listener for CTS, but having it print SUCCESS/FAIL
- // makes it easier to diagnose which particular combination of a test method had failed
- // from looking at device logcat.
- testng.addListener(listener);
-
- /* Construct the following equivalent XML configuration:
- *
- * <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
- * <suite>
- * <test>
- * <classes>
- * <class name="$klass">
- * <include name="$method" />
- * </class>
- * </classes>
- * </test>
- * </suite>
- *
- * This will ensure that only a single klass/method is being run by testng.
- * (It can still be run multiple times due to @DataProvider, with different parameters
- * each time)
- */
- List<XmlSuite> suites = new ArrayList<>();
- XmlSuite the_suite = new XmlSuite();
- XmlTest the_test = new XmlTest(the_suite);
- XmlClass the_class = new XmlClass(klass);
- XmlInclude the_include = new XmlInclude(method);
-
- the_class.getIncludedMethods().add(the_include);
- the_test.getXmlClasses().add(the_class);
- suites.add(the_suite);
- testng.setXmlSuites(suites);
-
- return testng;
- }
-
- public static class Result {
- private final boolean hasFailure;
- private final Map<String,Throwable> failures;
-
-
- Result(boolean hasFailure, Map<String, Throwable> failures) {
- this.hasFailure = hasFailure;
- this.failures = Collections.unmodifiableMap(new LinkedHashMap<>(failures));
- }
-
- public boolean hasFailure() {
- return hasFailure;
- }
-
- public Map<String, Throwable> getFailures() {
- return failures;
- }
- }
-}
diff --git a/tests/core/runner/src/com/android/cts/core/runner/support/TestNgRunner.java b/tests/core/runner/src/com/android/cts/core/runner/support/TestNgRunner.java
deleted file mode 100644
index d9bf037..0000000
--- a/tests/core/runner/src/com/android/cts/core/runner/support/TestNgRunner.java
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.core.runner.support;
-
-import android.util.Log;
-
-import org.junit.runner.Description;
-import org.junit.runner.Runner;
-import org.junit.runner.manipulation.Filter;
-import org.junit.runner.manipulation.Filterable;
-import org.junit.runner.manipulation.NoTestsRemainException;
-import org.junit.runner.notification.Failure;
-import org.junit.runner.notification.RunNotifier;
-
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.util.HashSet;
-import java.util.Map;
-
-/**
- * A {@link Runner} that can TestNG tests.
- *
- * <p>Implementation note: Avoid extending ParentRunner since that also has
- * logic to handle BeforeClass/AfterClass and other junit-specific functionality
- * that would be invalid for TestNG.</p>
- */
-class TestNgRunner extends Runner implements Filterable {
-
- private static final boolean DEBUG = false;
-
- private Description mDescription;
- /** Class name for debugging. */
- private String mClassName;
- /** Don't include the same method names twice. */
- private HashSet<String> mMethodSet = new HashSet<>();
-
- /**
- * @param testClass the test class to run
- */
- TestNgRunner(Class<?> testClass) {
- mDescription = generateTestNgDescription(testClass);
- mClassName = testClass.getName();
- }
-
- // Runner implementation
- @Override
- public Description getDescription() {
- return mDescription;
- }
-
- // Runner implementation
- @Override
- public int testCount() {
- if (!descriptionHasChildren(getDescription())) { // Avoid NPE when description is null.
- return 0;
- }
-
- // We always follow a flat Parent->Leaf hierarchy, so no recursion necessary.
- return getDescription().testCount();
- }
-
- // Filterable implementation
- @Override
- public void filter(Filter filter) throws NoTestsRemainException {
- mDescription = filterDescription(mDescription, filter);
-
- if (!descriptionHasChildren(getDescription())) { // Avoid NPE when description is null.
- if (DEBUG) {
- Log.d("TestNgRunner",
- "Filtering has removed all tests :( for class " + mClassName);
- }
- throw new NoTestsRemainException();
- }
-
- if (DEBUG) {
- Log.d("TestNgRunner",
- "Filtering has retained " + testCount() + " tests for class " + mClassName);
- }
- }
-
- // Filterable implementation
- @Override
- public void run(RunNotifier notifier) {
- if (!descriptionHasChildren(getDescription())) { // Avoid NPE when description is null.
- // Nothing to do.
- return;
- }
-
- for (Description child : getDescription().getChildren()) {
- String className = child.getClassName();
- String methodName = child.getMethodName();
-
- Class<?> klass;
- try {
- klass = Class.forName(className, false, Thread.currentThread().getContextClassLoader());
- } catch (ClassNotFoundException e) {
- throw new AssertionError(e);
- }
-
- notifier.fireTestStarted(child);
-
- // Avoid looking at all the methods by just using the string method name.
- SingleTestNgTestExecutor.Result result = SingleTestNgTestExecutor.execute(klass, methodName);
- if (result.hasFailure()) {
- // TODO: get the error messages from testng somehow.
- notifier.fireTestFailure(new Failure(child, extractException(result.getFailures())));
- }
-
- notifier.fireTestFinished(child);
- // TODO: Check @Test(enabled=false) and invoke #fireTestIgnored instead.
- }
- }
-
- private Throwable extractException(Map<String, Throwable> failures) {
- if (failures.isEmpty()) {
- return new AssertionError();
- }
- if (failures.size() == 1) {
- return failures.values().iterator().next();
- }
-
- StringBuilder errorMessage = new StringBuilder("========== Multiple Failures ==========");
- for (Map.Entry<String, Throwable> failureEntry : failures.entrySet()) {
- errorMessage.append("\n\n=== "). append(failureEntry.getKey()).append(" ===\n");
- Throwable throwable = failureEntry.getValue();
- errorMessage
- .append(throwable.getClass()).append(": ")
- .append(throwable.getMessage());
- for (StackTraceElement e : throwable.getStackTrace()) {
- if (e.getClassName().equals(getClass().getName())) {
- break;
- }
- errorMessage.append("\n at ").append(e);
- }
- }
- errorMessage.append("\n=======================================\n\n");
- return new AssertionError(errorMessage.toString());
- }
-
-
- /**
- * Recursively (preorder traversal) apply the filter to all the descriptions.
- *
- * @return null if the filter rejects the whole tree.
- */
- private static Description filterDescription(Description desc, Filter filter) {
- if (!filter.shouldRun(desc)) { // XX: Does the filter itself do the recursion?
- return null;
- }
-
- Description newDesc = desc.childlessCopy();
-
- // Return leafs.
- if (!descriptionHasChildren(desc)) {
- return newDesc;
- }
-
- // Filter all subtrees, only copying them if the filter accepts them.
- for (Description child : desc.getChildren()) {
- Description filteredChild = filterDescription(child, filter);
-
- if (filteredChild != null) {
- newDesc.addChild(filteredChild);
- }
- }
-
- return newDesc;
- }
-
- private Description generateTestNgDescription(Class<?> cls) {
- // Add the overall class description as the parent.
- Description parent = Description.createSuiteDescription(cls);
-
- if (DEBUG) {
- Log.d("TestNgRunner", "Generating TestNg Description for class " + cls.getName());
- }
-
- // Add each test method as a child.
- for (Method m : cls.getDeclaredMethods()) {
-
- // Filter to only 'public void' signatures.
- if ((m.getModifiers() & Modifier.PUBLIC) == 0) {
- continue;
- }
-
- if (!m.getReturnType().equals(Void.TYPE)) {
- continue;
- }
-
- // Note that TestNG methods may actually have parameters
- // (e.g. with @DataProvider) which TestNG will populate itself.
-
- // Add [Class, MethodName] as a Description leaf node.
- String name = m.getName();
-
- if (!mMethodSet.add(name)) {
- // Overloaded methods have the same name, don't add them twice.
- if (DEBUG) {
- Log.d("TestNgRunner", "Already added child " + cls.getName() + "#" + name);
- }
- continue;
- }
-
- Description child = Description.createTestDescription(cls, name);
-
- parent.addChild(child);
-
- if (DEBUG) {
- Log.d("TestNgRunner", "Add child " + cls.getName() + "#" + name);
- }
- }
-
- return parent;
- }
-
- private static boolean descriptionHasChildren(Description desc) {
- // Note: Although "desc.isTest()" is equivalent to "!desc.getChildren().isEmpty()"
- // we add the pre-requisite 2 extra null checks to avoid throwing NPEs.
- return desc != null && desc.getChildren() != null && !desc.getChildren().isEmpty();
- }
-}
diff --git a/tests/core/runner/src/com/android/cts/core/runner/support/TestNgRunnerBuilder.java b/tests/core/runner/src/com/android/cts/core/runner/support/TestNgRunnerBuilder.java
deleted file mode 100644
index 2f084b3..0000000
--- a/tests/core/runner/src/com/android/cts/core/runner/support/TestNgRunnerBuilder.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.core.runner.support;
-
-import java.lang.reflect.Method;
-
-import org.junit.runner.Runner;
-import org.junit.runners.model.RunnerBuilder;
-
-import org.testng.annotations.Test;
-
-/**
- * A {@link RunnerBuilder} that can handle TestNG tests.
- */
-public class TestNgRunnerBuilder extends RunnerBuilder {
- // Returns a TestNG runner for this class, only if it is a class
- // annotated with testng's @Test or has any methods with @Test in it.
- @Override
- public Runner runnerForClass(Class<?> testClass) {
- if (isTestNgTestClass(testClass)) {
- return new TestNgRunner(testClass);
- }
-
- return null;
- }
-
- private static boolean isTestNgTestClass(Class<?> cls) {
- // TestNG test is either marked @Test at the class
- if (cls.getAnnotation(Test.class) != null) {
- return true;
- }
-
- // Or It's marked @Test at the method level
- for (Method m : cls.getDeclaredMethods()) {
- if (m.getAnnotation(Test.class) != null) {
- return true;
- }
- }
-
- return false;
- }
-}
diff --git a/tests/core/runner/src/com/android/cts/runner/CrashParserRunListener.java b/tests/core/runner/src/com/android/cts/runner/CrashParserRunListener.java
deleted file mode 100644
index fbbb684..0000000
--- a/tests/core/runner/src/com/android/cts/runner/CrashParserRunListener.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.runner;
-
-import android.support.test.internal.runner.listener.InstrumentationRunListener;
-import android.util.Log;
-import org.junit.runner.Description;
-
-/**
- * A {@link RunListener} for CrashParser. Dumps the test name to logs when
- * tests start.
- */
-public class CrashParserRunListener extends InstrumentationRunListener {
-
- private static final String TAG = "CrashParserRunListener";
-
- // Constant must be kept in sync with CrashUtils.java
- public static final String NEW_TEST_ALERT = "New test starting with name: ";
-
- @Override
- public void testStarted(Description description) throws Exception {
- Log.i(TAG, NEW_TEST_ALERT + description.toString());
- }
-
-}
diff --git a/tests/core/runner/src/com/android/cts/runner/CtsTestRunListener.java b/tests/core/runner/src/com/android/cts/runner/CtsTestRunListener.java
deleted file mode 100644
index 1f9a939..0000000
--- a/tests/core/runner/src/com/android/cts/runner/CtsTestRunListener.java
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.runner;
-
-import android.app.ActivityManager;
-import android.app.Instrumentation;
-import android.app.KeyguardManager;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.support.test.internal.runner.listener.InstrumentationRunListener;
-import android.text.TextUtils;
-import android.util.Log;
-
-import junit.framework.TestCase;
-
-import org.junit.runner.Description;
-import org.junit.runner.notification.RunListener;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.lang.Class;
-import java.lang.ReflectiveOperationException;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.net.Authenticator;
-import java.net.CookieHandler;
-import java.net.ResponseCache;
-import java.text.DateFormat;
-import java.util.Locale;
-import java.util.Properties;
-import java.util.TimeZone;
-
-import javax.net.ssl.HostnameVerifier;
-import javax.net.ssl.HttpsURLConnection;
-import javax.net.ssl.SSLSocketFactory;
-
-/**
- * A {@link RunListener} for CTS. Sets the system properties necessary for many
- * core tests to run. This is needed because there are some core tests that need
- * writing access to the file system.
- * Finally, we add a means to free memory allocated by a TestCase after its
- * execution.
- */
-public class CtsTestRunListener extends InstrumentationRunListener {
-
- private static final String TAG = "CtsTestRunListener";
-
- private TestEnvironment mEnvironment;
- private Class<?> lastClass;
-
- @Override
- public void testRunStarted(Description description) throws Exception {
- mEnvironment = new TestEnvironment(getInstrumentation().getTargetContext());
-
- // We might want to move this to /sdcard, if is is mounted/writable.
- File cacheDir = getInstrumentation().getTargetContext().getCacheDir();
- System.setProperty("java.io.tmpdir", cacheDir.getAbsolutePath());
-
- // attempt to disable keyguard, if current test has permission to do so
- // TODO: move this to a better place, such as InstrumentationTestRunner
- // ?
- if (getInstrumentation().getContext().checkCallingOrSelfPermission(
- android.Manifest.permission.DISABLE_KEYGUARD)
- == PackageManager.PERMISSION_GRANTED) {
- Log.i(TAG, "Disabling keyguard");
- KeyguardManager keyguardManager =
- (KeyguardManager) getInstrumentation().getContext().getSystemService(
- Context.KEYGUARD_SERVICE);
- keyguardManager.newKeyguardLock("cts").disableKeyguard();
- } else {
- Log.i(TAG, "Test lacks permission to disable keyguard. " +
- "UI based tests may fail if keyguard is up");
- }
- }
-
- @Override
- public void testStarted(Description description) throws Exception {
- if (description.getTestClass() != lastClass) {
- lastClass = description.getTestClass();
- printMemory(description.getTestClass());
- }
-
- mEnvironment.reset();
- }
-
- @Override
- public void testFinished(Description description) {
- // no way to implement this in JUnit4...
- // offending test cases that need this logic should probably be cleaned
- // up individually
- // if (test instanceof TestCase) {
- // cleanup((TestCase) test);
- // }
- }
-
- /**
- * Dumps some memory info.
- */
- private void printMemory(Class<?> testClass) {
- Runtime runtime = Runtime.getRuntime();
-
- long total = runtime.totalMemory();
- long free = runtime.freeMemory();
- long used = total - free;
-
- Log.d(TAG, "Total memory : " + total);
- Log.d(TAG, "Used memory : " + used);
- Log.d(TAG, "Free memory : " + free);
-
- String tempdir = System.getProperty("java.io.tmpdir", "");
- // TODO: Remove these extra Logs added to debug a specific timeout problem.
- Log.d(TAG, "java.io.tmpdir is:" + tempdir);
-
- if (!TextUtils.isEmpty(tempdir)) {
- String[] commands = {"df", tempdir};
- BufferedReader in = null;
- try {
- Log.d(TAG, "About to .exec df");
- Process proc = runtime.exec(commands);
- Log.d(TAG, ".exec returned");
- in = new BufferedReader(new InputStreamReader(proc.getInputStream()));
- Log.d(TAG, "Stream reader created");
- String line;
- while ((line = in.readLine()) != null) {
- Log.d(TAG, line);
- }
- } catch (IOException e) {
- Log.d(TAG, "Exception: " + e.toString());
- // Well, we tried
- } finally {
- Log.d(TAG, "In finally");
- if (in != null) {
- try {
- in.close();
- } catch (IOException e) {
- // Meh
- }
- }
- }
- }
-
- Log.d(TAG, "Now executing : " + testClass.getName());
- }
-
- /**
- * Nulls all non-static reference fields in the given test class. This
- * method helps us with those test classes that don't have an explicit
- * tearDown() method. Normally the garbage collector should take care of
- * everything, but since JUnit keeps references to all test cases, a little
- * help might be a good idea.
- */
- private void cleanup(TestCase test) {
- Class<?> clazz = test.getClass();
-
- while (clazz != TestCase.class) {
- Field[] fields = clazz.getDeclaredFields();
- for (int i = 0; i < fields.length; i++) {
- Field f = fields[i];
- if (!f.getType().isPrimitive() &&
- !Modifier.isStatic(f.getModifiers())) {
- try {
- f.setAccessible(true);
- f.set(test, null);
- } catch (Exception ignored) {
- // Nothing we can do about it.
- }
- }
- }
-
- clazz = clazz.getSuperclass();
- }
- }
-
- // http://code.google.com/p/vogar/source/browse/trunk/src/vogar/target/TestEnvironment.java
- static class TestEnvironment {
- private static final Field sDateFormatIs24HourField;
- static {
- try {
- Class<?> dateFormatClass = Class.forName("java.text.DateFormat");
- sDateFormatIs24HourField = dateFormatClass.getDeclaredField("is24Hour");
- } catch (ReflectiveOperationException e) {
- throw new AssertionError("Missing DateFormat.is24Hour", e);
- }
- }
-
- private final Locale mDefaultLocale;
- private final TimeZone mDefaultTimeZone;
- private final HostnameVerifier mHostnameVerifier;
- private final SSLSocketFactory mSslSocketFactory;
- private final Properties mProperties = new Properties();
- private final Boolean mDefaultIs24Hour;
-
- TestEnvironment(Context context) {
- mDefaultLocale = Locale.getDefault();
- mDefaultTimeZone = TimeZone.getDefault();
- mHostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier();
- mSslSocketFactory = HttpsURLConnection.getDefaultSSLSocketFactory();
-
- mProperties.setProperty("user.home", "");
- mProperties.setProperty("java.io.tmpdir", context.getCacheDir().getAbsolutePath());
- // The CDD mandates that devices that support WiFi are the only ones that will have
- // multicast.
- PackageManager pm = context.getPackageManager();
- mProperties.setProperty("android.cts.device.multicast",
- Boolean.toString(pm.hasSystemFeature(PackageManager.FEATURE_WIFI)));
- mDefaultIs24Hour = getDateFormatIs24Hour();
-
- // There are tests in libcore that should be disabled for low ram devices. They can't
- // access ActivityManager to call isLowRamDevice, but can read system properties.
- ActivityManager activityManager =
- (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
- mProperties.setProperty("android.cts.device.lowram",
- Boolean.toString(activityManager.isLowRamDevice()));
- }
-
- void reset() {
- System.setProperties(null);
- System.setProperties(mProperties);
- Locale.setDefault(mDefaultLocale);
- TimeZone.setDefault(mDefaultTimeZone);
- Authenticator.setDefault(null);
- CookieHandler.setDefault(null);
- ResponseCache.setDefault(null);
- HttpsURLConnection.setDefaultHostnameVerifier(mHostnameVerifier);
- HttpsURLConnection.setDefaultSSLSocketFactory(mSslSocketFactory);
- setDateFormatIs24Hour(mDefaultIs24Hour);
- }
-
- private static Boolean getDateFormatIs24Hour() {
- try {
- return (Boolean) sDateFormatIs24HourField.get(null);
- } catch (ReflectiveOperationException e) {
- throw new AssertionError("Unable to get java.text.DateFormat.is24Hour", e);
- }
- }
-
- private static void setDateFormatIs24Hour(Boolean value) {
- try {
- sDateFormatIs24HourField.set(null, value);
- } catch (ReflectiveOperationException e) {
- throw new AssertionError("Unable to set java.text.DateFormat.is24Hour", e);
- }
- }
- }
-
-}
diff --git a/tests/framework/base/windowmanager/AndroidManifest.xml b/tests/framework/base/windowmanager/AndroidManifest.xml
index a674c86..7ae8d88 100644
--- a/tests/framework/base/windowmanager/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/AndroidManifest.xml
@@ -114,6 +114,8 @@
<activity android:name="android.server.wm.lifecycle.ActivityLifecycleClientTestBase$CallbackTrackingActivity"/>
+ <activity android:name="android.server.wm.lifecycle.ActivityLifecycleClientTestBase$SecondCallbackTrackingActivity"/>
+
<activity android:name="android.server.wm.lifecycle.ActivityLifecycleClientTestBase$TranslucentCallbackTrackingActivity"
android:theme="@android:style/Theme.Translucent.NoTitleBar" />
@@ -150,6 +152,18 @@
<activity android:name="android.server.wm.lifecycle.ActivityLifecycleClientTestBase$SlowActivity"/>
+ <activity android:name="android.server.wm.lifecycle.ActivityLifecycleClientTestBase$NoDisplayActivity"
+ android:theme="@android:style/Theme.NoDisplay" />
+
+ <activity android:name="android.server.wm.lifecycle.ActivityLifecycleClientTestBase$DifferentAffinityActivity"
+ android:taskAffinity="nobody.but.DifferentAffinityActivity" />
+
+ <activity android:name="android.server.wm.lifecycle.ActivityLifecycleClientTestBase$TransitionSourceActivity"
+ android:theme="@style/window_activity_transitions" />
+
+ <activity android:name="android.server.wm.lifecycle.ActivityLifecycleClientTestBase$TransitionDestinationActivity"
+ android:theme="@style/window_activity_transitions" />
+
<activity android:name="android.server.wm.StartActivityTests$TestActivity2" />
<activity android:name="android.server.wm.MultiDisplaySystemDecorationTests$ImeTestActivity" />
@@ -233,6 +247,10 @@
android:launchMode="standard"
android:taskAffinity=".t1"/>
<activity
+ android:name="android.server.wm.intent.Activities$TaskAffinity1RelinquishTaskIdentityActivity"
+ android:relinquishTaskIdentity="true"
+ android:taskAffinity=".t1"/>
+ <activity
android:name="android.server.wm.intent.Activities$TaskAffinity2Activity"
android:allowTaskReparenting="true"
android:launchMode="standard"
@@ -264,6 +282,9 @@
android:name="android.server.wm.intent.Activities$LauncherActivity"
android:documentLaunchMode="always"
android:launchMode="singleInstance"/>
+ <activity
+ android:name="android.server.wm.intent.Activities$RelinquishTaskIdentityActivity"
+ android:relinquishTaskIdentity="true"/>
<service
android:name="android.server.wm.TestLogService"
@@ -295,7 +316,7 @@
<activity android:name="android.server.wm.WindowFocusTests$LosingFocusActivity" />
<activity android:name="android.app.Activity"/>
- <activity android:name="android.server.wm.DragDropActivity"
+ <activity android:name="android.server.wm.DragDropTest$DragDropActivity"
android:screenOrientation="locked"
android:turnScreenOn="true"
android:showWhenLocked="true"
diff --git a/tests/framework/base/windowmanager/app/src/android/server/wm/app/Components.java b/tests/framework/base/windowmanager/app/src/android/server/wm/app/Components.java
index 94b397f..879fbb7 100644
--- a/tests/framework/base/windowmanager/app/src/android/server/wm/app/Components.java
+++ b/tests/framework/base/windowmanager/app/src/android/server/wm/app/Components.java
@@ -220,6 +220,8 @@
public static final String EXTRA_FIXED_ORIENTATION = "fixed_orientation";
public static final String EXTRA_CONFIGURATION = "configuration";
public static final String EXTRA_CONFIG_ASSETS_SEQ = "config_assets_seq";
+ public static final String EXTRA_INTENTS = "intents";
+ public static final String COMMAND_START_ACTIVITIES = "start_activities";
}
/**
@@ -273,6 +275,12 @@
public static final String EXTRA_FONT_ACTIVITY_DPI = "fontActivityDpi";
}
+ /** Extra key constants for {@link android.server.wm.app.TurnScreenOnActivity}. */
+ public static class TurnScreenOnActivity {
+ // Turn on screen by window flags or APIs.
+ public static final String EXTRA_USE_WINDOW_FLAGS = "useWindowFlags";
+ }
+
/**
* Logging constants for {@link android.server.wm.app.KeyguardDismissLoggerCallback}.
*
diff --git a/tests/framework/base/windowmanager/app/src/android/server/wm/app/TurnScreenOnActivity.java b/tests/framework/base/windowmanager/app/src/android/server/wm/app/TurnScreenOnActivity.java
index af827cd..3c82695 100644
--- a/tests/framework/base/windowmanager/app/src/android/server/wm/app/TurnScreenOnActivity.java
+++ b/tests/framework/base/windowmanager/app/src/android/server/wm/app/TurnScreenOnActivity.java
@@ -16,17 +16,22 @@
package android.server.wm.app;
-import android.app.Activity;
import android.os.Bundle;
import android.view.WindowManager;
-public class TurnScreenOnActivity extends Activity {
+public class TurnScreenOnActivity extends AbstractLifecycleLogActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
- getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
- | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
- | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
- | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
super.onCreate(savedInstanceState);
+
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ if (getIntent().getBooleanExtra(Components.TurnScreenOnActivity.EXTRA_USE_WINDOW_FLAGS,
+ false /* defaultValue */)) {
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+ | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
+ } else {
+ setShowWhenLocked(true);
+ setTurnScreenOn(true);
+ }
}
}
diff --git a/tests/framework/base/windowmanager/app/src/android/server/wm/app/VirtualDisplayActivity.java b/tests/framework/base/windowmanager/app/src/android/server/wm/app/VirtualDisplayActivity.java
index 269a151..e8019aa 100644
--- a/tests/framework/base/windowmanager/app/src/android/server/wm/app/VirtualDisplayActivity.java
+++ b/tests/framework/base/windowmanager/app/src/android/server/wm/app/VirtualDisplayActivity.java
@@ -21,6 +21,7 @@
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
import static android.server.wm.ActivityLauncher.KEY_DISPLAY_ID;
import static android.server.wm.ActivityLauncher.KEY_LAUNCH_ACTIVITY;
+import static android.server.wm.ActivityLauncher.KEY_NEW_TASK;
import static android.server.wm.ActivityLauncher.KEY_TARGET_COMPONENT;
import static android.server.wm.ComponentNameUtils.getActivityName;
import static android.server.wm.app.Components.VirtualDisplayActivity.COMMAND_CREATE_DISPLAY;
@@ -255,6 +256,7 @@
extras.putBoolean(KEY_LAUNCH_ACTIVITY, true);
extras.putString(KEY_TARGET_COMPONENT, getActivityName(activityName));
extras.putInt(KEY_DISPLAY_ID, displayId);
+ extras.putBoolean(KEY_NEW_TASK, true);
ActivityLauncher.launchActivityFromExtras(this, extras);
}
}
diff --git a/tests/framework/base/windowmanager/app_base/src/android/server/wm/app/TestActivity.java b/tests/framework/base/windowmanager/app_base/src/android/server/wm/app/TestActivity.java
index 575e878..2d7f83d 100644
--- a/tests/framework/base/windowmanager/app_base/src/android/server/wm/app/TestActivity.java
+++ b/tests/framework/base/windowmanager/app_base/src/android/server/wm/app/TestActivity.java
@@ -18,7 +18,9 @@
import static android.server.wm.app.Components.TestActivity.EXTRA_CONFIG_ASSETS_SEQ;
import static android.server.wm.app.Components.TestActivity.EXTRA_FIXED_ORIENTATION;
+import static android.server.wm.app.Components.TestActivity.EXTRA_INTENTS;
import static android.server.wm.app.Components.TestActivity.TEST_ACTIVITY_ACTION_FINISH_SELF;
+import static android.server.wm.app.Components.TestActivity.COMMAND_START_ACTIVITIES;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -26,6 +28,9 @@
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.os.Bundle;
+import android.os.Parcelable;
+
+import java.util.Arrays;
public class TestActivity extends AbstractLifecycleLogActivity {
@@ -71,6 +76,18 @@
}
@Override
+ public void handleCommand(String command, Bundle data) {
+ switch (command) {
+ case COMMAND_START_ACTIVITIES:
+ final Parcelable[] intents = data.getParcelableArray(EXTRA_INTENTS);
+ startActivities(Arrays.copyOf(intents, intents.length, Intent[].class));
+ break;
+ default:
+ super.handleCommand(command, data);
+ }
+ }
+
+ @Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
dumpConfiguration(newConfig);
diff --git a/tests/framework/base/windowmanager/backgroundactivity/Android.mk b/tests/framework/base/windowmanager/backgroundactivity/Android.mk
index 3ddcdf0..18e466c 100644
--- a/tests/framework/base/windowmanager/backgroundactivity/Android.mk
+++ b/tests/framework/base/windowmanager/backgroundactivity/Android.mk
@@ -29,7 +29,8 @@
androidx.test.rules \
cts-wm-util \
cts-wm-app-base \
- cts-core-test-runner-axt
+ cts-core-test-runner-axt \
+ cts-background-activity-common
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/tests/framework/base/windowmanager/backgroundactivity/AppA/Android.mk b/tests/framework/base/windowmanager/backgroundactivity/AppA/Android.mk
index 08d4f36..c3080f9 100644
--- a/tests/framework/base/windowmanager/backgroundactivity/AppA/Android.mk
+++ b/tests/framework/base/windowmanager/backgroundactivity/AppA/Android.mk
@@ -22,7 +22,8 @@
LOCAL_USE_AAPT2 := true
LOCAL_STATIC_JAVA_LIBRARIES := \
- cts-wm-app-base
+ cts-wm-app-base \
+ cts-background-activity-common
LOCAL_STATIC_ANDROID_LIBRARIES := \
androidx.legacy_legacy-support-v4
diff --git a/tests/framework/base/windowmanager/backgroundactivity/AppA/src/android/server/wm/backgroundactivity/appa/SendPendingIntentReceiver.java b/tests/framework/base/windowmanager/backgroundactivity/AppA/src/android/server/wm/backgroundactivity/appa/SendPendingIntentReceiver.java
index 3647be4..7ec1cc9 100644
--- a/tests/framework/base/windowmanager/backgroundactivity/AppA/src/android/server/wm/backgroundactivity/appa/SendPendingIntentReceiver.java
+++ b/tests/framework/base/windowmanager/backgroundactivity/AppA/src/android/server/wm/backgroundactivity/appa/SendPendingIntentReceiver.java
@@ -23,11 +23,14 @@
import static android.server.wm.backgroundactivity.appa.Components.StartBackgroundActivityReceiver.START_ACTIVITY_DELAY_MS_EXTRA;
import static android.server.wm.backgroundactivity.appb.Components.APP_B_START_PENDING_INTENT_RECEIVER;
import static android.server.wm.backgroundactivity.appb.Components.StartPendingIntentReceiver.PENDING_INTENT_EXTRA;
+import static android.server.wm.backgroundactivity.common.CommonComponents.EVENT_NOTIFIER_EXTRA;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.os.ResultReceiver;
+import android.server.wm.backgroundactivity.common.CommonComponents.Event;
/**
* Receive broadcast command to create a pendingIntent and send it to AppB.
@@ -38,6 +41,10 @@
public void onReceive(Context context, Intent receivedIntent) {
boolean isBroadcast = receivedIntent.getBooleanExtra(IS_BROADCAST_EXTRA, false);
int startActivityDelayMs = receivedIntent.getIntExtra(START_ACTIVITY_DELAY_MS_EXTRA, 0);
+ ResultReceiver eventNotifier = receivedIntent.getParcelableExtra(EVENT_NOTIFIER_EXTRA);
+ if (eventNotifier != null) {
+ eventNotifier.send(Event.APP_A_SEND_PENDING_INTENT_BROADCAST_RECEIVED, null);
+ }
final PendingIntent pendingIntent;
if (isBroadcast) {
@@ -46,6 +53,7 @@
Intent newIntent = new Intent();
newIntent.setComponent(APP_A_START_ACTIVITY_RECEIVER);
newIntent.putExtra(START_ACTIVITY_DELAY_MS_EXTRA, startActivityDelayMs);
+ newIntent.putExtra(EVENT_NOTIFIER_EXTRA, eventNotifier);
pendingIntent = PendingIntent.getBroadcast(context, 0,
newIntent, PendingIntent.FLAG_UPDATE_CURRENT);
} else {
@@ -53,7 +61,6 @@
Intent newIntent = new Intent();
newIntent.setComponent(APP_A_BACKGROUND_ACTIVITY);
newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
pendingIntent = PendingIntent.getActivity(context, 0,
newIntent, PendingIntent.FLAG_UPDATE_CURRENT);
}
@@ -62,6 +69,7 @@
Intent intent = new Intent();
intent.setComponent(APP_B_START_PENDING_INTENT_RECEIVER);
intent.putExtra(PENDING_INTENT_EXTRA, pendingIntent);
+ intent.putExtra(EVENT_NOTIFIER_EXTRA, eventNotifier);
context.sendBroadcast(intent);
}
}
diff --git a/tests/framework/base/windowmanager/backgroundactivity/AppA/src/android/server/wm/backgroundactivity/appa/StartBackgroundActivityReceiver.java b/tests/framework/base/windowmanager/backgroundactivity/AppA/src/android/server/wm/backgroundactivity/appa/StartBackgroundActivityReceiver.java
index 844dd4f..0622a45 100644
--- a/tests/framework/base/windowmanager/backgroundactivity/AppA/src/android/server/wm/backgroundactivity/appa/StartBackgroundActivityReceiver.java
+++ b/tests/framework/base/windowmanager/backgroundactivity/AppA/src/android/server/wm/backgroundactivity/appa/StartBackgroundActivityReceiver.java
@@ -17,11 +17,14 @@
package android.server.wm.backgroundactivity.appa;
import static android.server.wm.backgroundactivity.appa.Components.StartBackgroundActivityReceiver.START_ACTIVITY_DELAY_MS_EXTRA;
+import static android.server.wm.backgroundactivity.common.CommonComponents.EVENT_NOTIFIER_EXTRA;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.os.ResultReceiver;
import android.os.SystemClock;
+import android.server.wm.backgroundactivity.common.CommonComponents.Event;
/**
* A class to help test case to start background activity.
@@ -30,6 +33,11 @@
@Override
public void onReceive(Context context, Intent intent) {
+ ResultReceiver eventNotifier = intent.getParcelableExtra(EVENT_NOTIFIER_EXTRA);
+ if (eventNotifier != null) {
+ eventNotifier.send(Event.APP_A_START_BACKGROUND_ACTIVITY_BROADCAST_RECEIVED, null);
+ }
+
if (!intent.hasExtra(START_ACTIVITY_DELAY_MS_EXTRA)) {
startActivityNow(context);
return;
diff --git a/tests/framework/base/windowmanager/backgroundactivity/AppB/Android.mk b/tests/framework/base/windowmanager/backgroundactivity/AppB/Android.mk
index 0398b74..d632370 100644
--- a/tests/framework/base/windowmanager/backgroundactivity/AppB/Android.mk
+++ b/tests/framework/base/windowmanager/backgroundactivity/AppB/Android.mk
@@ -22,7 +22,8 @@
LOCAL_USE_AAPT2 := true
LOCAL_STATIC_JAVA_LIBRARIES := \
- cts-wm-app-base
+ cts-wm-app-base \
+ cts-background-activity-common
LOCAL_STATIC_ANDROID_LIBRARIES := \
androidx.legacy_legacy-support-v4
diff --git a/tests/framework/base/windowmanager/backgroundactivity/AppB/src/android/server/wm/backgroundactivity/appb/StartPendingIntentReceiver.java b/tests/framework/base/windowmanager/backgroundactivity/AppB/src/android/server/wm/backgroundactivity/appb/StartPendingIntentReceiver.java
index 830a611..e8f6991 100644
--- a/tests/framework/base/windowmanager/backgroundactivity/AppB/src/android/server/wm/backgroundactivity/appb/StartPendingIntentReceiver.java
+++ b/tests/framework/base/windowmanager/backgroundactivity/AppB/src/android/server/wm/backgroundactivity/appb/StartPendingIntentReceiver.java
@@ -17,11 +17,14 @@
package android.server.wm.backgroundactivity.appb;
import static android.server.wm.backgroundactivity.appb.Components.StartPendingIntentReceiver.PENDING_INTENT_EXTRA;
+import static android.server.wm.backgroundactivity.common.CommonComponents.EVENT_NOTIFIER_EXTRA;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.os.ResultReceiver;
+import android.server.wm.backgroundactivity.common.CommonComponents.Event;
/**
* Receive pending intent from AppA and launch it
@@ -31,6 +34,11 @@
@Override
public void onReceive(Context context, Intent intent) {
PendingIntent pendingIntent = intent.getParcelableExtra(PENDING_INTENT_EXTRA);
+ ResultReceiver eventNotifier = intent.getParcelableExtra(EVENT_NOTIFIER_EXTRA);
+ if (eventNotifier != null) {
+ eventNotifier.send(Event.APP_B_START_PENDING_INTENT_BROADCAST_RECEIVED, null);
+ }
+
try {
pendingIntent.send();
} catch (PendingIntent.CanceledException e) {
diff --git a/tests/backup/app/permission22/Android.mk b/tests/framework/base/windowmanager/backgroundactivity/common/Android.mk
similarity index 66%
copy from tests/backup/app/permission22/Android.mk
copy to tests/framework/base/windowmanager/backgroundactivity/common/Android.mk
index 8790e19..a7eaad6 100644
--- a/tests/backup/app/permission22/Android.mk
+++ b/tests/framework/base/windowmanager/backgroundactivity/common/Android.mk
@@ -16,16 +16,17 @@
include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := optional
+# Don't include this package in any target.
+LOCAL_MODULE_TAGS := tests
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ androidx.annotation_annotation \
+ guava
-LOCAL_PACKAGE_NAME := CtsPermissionBackupApp22
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := test_current
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+LOCAL_MODULE := cts-background-activity-common
-include $(BUILD_CTS_SUPPORT_PACKAGE)
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tests/framework/base/windowmanager/backgroundactivity/common/src/android/server/wm/backgroundactivity/common/CommonComponents.java b/tests/framework/base/windowmanager/backgroundactivity/common/src/android/server/wm/backgroundactivity/common/CommonComponents.java
new file mode 100644
index 0000000..1981ffb
--- /dev/null
+++ b/tests/framework/base/windowmanager/backgroundactivity/common/src/android/server/wm/backgroundactivity/common/CommonComponents.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.wm.backgroundactivity.common;
+
+import androidx.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Constant-holding class common to AppA, AppB and tests.
+ */
+public class CommonComponents {
+
+ public static final String EVENT_NOTIFIER_EXTRA = "EVENT_NOTIFIER_EXTRA";
+
+ @IntDef({
+ Event.APP_A_SEND_PENDING_INTENT_BROADCAST_RECEIVED,
+ Event.APP_B_START_PENDING_INTENT_BROADCAST_RECEIVED,
+ Event.APP_A_START_BACKGROUND_ACTIVITY_BROADCAST_RECEIVED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Event {
+ int APP_A_SEND_PENDING_INTENT_BROADCAST_RECEIVED = 0;
+ int APP_B_START_PENDING_INTENT_BROADCAST_RECEIVED = 1;
+ int APP_A_START_BACKGROUND_ACTIVITY_BROADCAST_RECEIVED = 2;
+ }
+}
diff --git a/tests/framework/base/windowmanager/backgroundactivity/common/src/android/server/wm/backgroundactivity/common/EventReceiver.java b/tests/framework/base/windowmanager/backgroundactivity/common/src/android/server/wm/backgroundactivity/common/EventReceiver.java
new file mode 100644
index 0000000..4812fa6
--- /dev/null
+++ b/tests/framework/base/windowmanager/backgroundactivity/common/src/android/server/wm/backgroundactivity/common/EventReceiver.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.wm.backgroundactivity.common;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import android.os.Bundle;
+import android.os.ConditionVariable;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.ResultReceiver;
+import android.server.wm.backgroundactivity.common.CommonComponents.Event;
+
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Class used to register for events sent via IPC using {@link #getNotifier()} parcelable.
+ *
+ * Create an instance for the event of interest, pass {@link #getNotifier()} to the target, either
+ * via some AIDL or intent extra, have the caller call {@link ResultReceiver#send(int, Bundle)},
+ * then finally wait for the event with {@link #waitForEventOrThrow(long)}.
+ */
+public class EventReceiver {
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
+ private final ConditionVariable mEventReceivedVariable = new ConditionVariable(false);
+
+ @Event
+ private final int mEvent;
+
+ public EventReceiver(@Event int event) {
+ mEvent = event;
+ }
+
+ public ResultReceiver getNotifier() {
+ return new ResultReceiver(mHandler) {
+ @Override
+ protected void onReceiveResult(@Event int event, Bundle data) {
+ if (event == mEvent) {
+ mEventReceivedVariable.open();
+ }
+ }
+ };
+ }
+
+ /**
+ * Waits for registered event or throws {@link TimeoutException}.
+ */
+ public void waitForEventOrThrow(long timeoutMs) throws TimeoutException {
+ // Avoid deadlocks
+ checkState(Thread.currentThread() != mHandler.getLooper().getThread());
+
+ if (!mEventReceivedVariable.block(timeoutMs)) {
+ throw new TimeoutException("Timed out waiting for event " + mEvent);
+ }
+ }
+}
diff --git a/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/BackgroundActivityLaunchTest.java b/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/BackgroundActivityLaunchTest.java
index b4d3ee9..94dd7fa 100644
--- a/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/BackgroundActivityLaunchTest.java
+++ b/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/BackgroundActivityLaunchTest.java
@@ -40,6 +40,7 @@
import static android.server.wm.backgroundactivity.appa.Components.SendPendingIntentReceiver.IS_BROADCAST_EXTRA;
import static android.server.wm.backgroundactivity.appa.Components.StartBackgroundActivityReceiver.START_ACTIVITY_DELAY_MS_EXTRA;
import static android.server.wm.backgroundactivity.appb.Components.APP_B_FOREGROUND_ACTIVITY;
+import static android.server.wm.backgroundactivity.common.CommonComponents.EVENT_NOTIFIER_EXTRA;
import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
@@ -56,9 +57,13 @@
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.os.ResultReceiver;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
+import android.server.wm.backgroundactivity.common.CommonComponents.Event;
+import android.server.wm.backgroundactivity.common.EventReceiver;
+import androidx.annotation.Nullable;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.FlakyTest;
@@ -69,6 +74,7 @@
import org.junit.Test;
import java.util.List;
+import java.util.concurrent.TimeoutException;
/**
* This class covers all test cases for starting/blocking background activities.
@@ -87,6 +93,12 @@
private static final String TEST_PACKAGE_APP_A = "android.server.wm.backgroundactivity.appa";
private static final String TEST_PACKAGE_APP_B = "android.server.wm.backgroundactivity.appb";
+ /**
+ * Tests can be executed as soon as the device has booted. When that happens the broadcast queue
+ * is long and it takes some time to process the broadcast we just sent.
+ */
+ private static final int BROADCAST_DELIVERY_TIMEOUT_MS = 25000;
+
@Before
public void setUp() throws Exception {
mContext = InstrumentationRegistry.getContext();
@@ -115,15 +127,12 @@
public void tearDown() throws Exception {
stopTestPackage(TEST_PACKAGE_APP_A);
stopTestPackage(TEST_PACKAGE_APP_B);
- pressHomeButton();
+ launchHomeActivity();
AppOpsUtils.reset(APP_A_PACKAGE_NAME);
- mAmWmState.waitForHomeActivityVisible();
runWithShellPermissionIdentity(() -> {
runShellCommand("dpm remove-active-admin --user current "
+ APP_A_SIMPLE_ADMIN_RECEIVER.flattenToString());
});
- // TODO(b/130169434): Remove it when app switch protection bug is fixed
- SystemClock.sleep(5000);
}
@Test
@@ -136,16 +145,15 @@
assertFalse("Should not able to launch background activity", result);
assertTaskStack(null, APP_A_BACKGROUND_ACTIVITY);
- // TODO(b/137134312): Bring this back once the stacks leakage issue is fixed
// Make sure aborting activity starts won't have any empty task/stack leaks.
- // List<ActivityManagerState.ActivityStack> stacks = mAmWmState.getAmState().getStacks();
- // for (ActivityManagerState.ActivityStack stack : stacks) {
- // assertThat(stack.getTopTask()).isNotNull();
- // List<ActivityManagerState.ActivityTask> tasks = stack.getTasks();
- // for (ActivityManagerState.ActivityTask task : tasks) {
- // assertThat(task.getActivities().size()).isGreaterThan(0);
- // }
- // }
+ List<ActivityManagerState.ActivityStack> stacks = mAmWmState.getAmState().getStacks();
+ for (ActivityManagerState.ActivityStack stack : stacks) {
+ assertThat(stack.getTopTask()).isNotNull();
+ List<ActivityManagerState.ActivityTask> tasks = stack.getTasks();
+ for (ActivityManagerState.ActivityTask task : tasks) {
+ assertThat(task.getActivities().size()).isGreaterThan(0);
+ }
+ }
}
@Test
@@ -207,7 +215,7 @@
intent.setComponent(APP_A_FOREGROUND_ACTIVITY);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(LAUNCH_BACKGROUND_ACTIVITY_EXTRA, true);
- intent.putExtra(START_ACTIVITY_FROM_FG_ACTIVITY_DELAY_MS_EXTRA, 7000);
+ intent.putExtra(START_ACTIVITY_FROM_FG_ACTIVITY_DELAY_MS_EXTRA, 2000);
mContext.startActivity(intent);
boolean result = waitForActivityFocused(ACTIVITY_FOCUS_TIMEOUT_MS,
APP_A_FOREGROUND_ACTIVITY);
@@ -215,10 +223,8 @@
assertTaskStack(new ComponentName[]{APP_A_FOREGROUND_ACTIVITY}, APP_A_FOREGROUND_ACTIVITY);
// The foreground activity will be paused but will attempt to restart itself in onPause()
- pressHomeButton();
- mAmWmState.waitForHomeActivityVisible();
+ pressHomeAndResumeAppSwitch();
- waitToPreventAppSwitchProtection();
result = waitForActivityFocused(APP_A_FOREGROUND_ACTIVITY);
assertFalse("Previously foreground Activity should not be able to relaunch itself", result);
result = waitForActivityFocused(APP_A_BACKGROUND_ACTIVITY);
@@ -236,7 +242,7 @@
intent.setComponent(APP_A_FOREGROUND_ACTIVITY);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(LAUNCH_BACKGROUND_ACTIVITY_EXTRA, true);
- intent.putExtra(START_ACTIVITY_FROM_FG_ACTIVITY_DELAY_MS_EXTRA, 7000);
+ intent.putExtra(START_ACTIVITY_FROM_FG_ACTIVITY_DELAY_MS_EXTRA, 2000);
intent.putExtra(START_ACTIVITY_FROM_FG_ACTIVITY_NEW_TASK_EXTRA, true);
mContext.startActivity(intent);
boolean result = waitForActivityFocused(ACTIVITY_FOCUS_TIMEOUT_MS,
@@ -245,10 +251,8 @@
assertTaskStack(new ComponentName[]{APP_A_FOREGROUND_ACTIVITY}, APP_A_FOREGROUND_ACTIVITY);
// The foreground activity will be paused but will attempt to restart itself in onPause()
- pressHomeButton();
- mAmWmState.waitForHomeActivityVisible();
+ pressHomeAndResumeAppSwitch();
- waitToPreventAppSwitchProtection();
result = waitForActivityFocused(APP_A_FOREGROUND_ACTIVITY);
assertFalse("Previously foreground Activity should not be able to relaunch itself", result);
result = waitForActivityFocused(APP_A_BACKGROUND_ACTIVITY);
@@ -271,10 +275,8 @@
assertTaskStack(new ComponentName[]{APP_A_FOREGROUND_ACTIVITY}, APP_A_FOREGROUND_ACTIVITY);
// The foreground activity will be paused but will attempt to restart itself in onPause()
- pressHomeButton();
- mAmWmState.waitForHomeActivityVisible();
+ pressHomeAndResumeAppSwitch();
- waitToPreventAppSwitchProtection();
result = waitForActivityFocused(APP_A_FOREGROUND_ACTIVITY);
assertFalse("Previously foreground Activity should not be able to relaunch itself", result);
assertTaskStack(new ComponentName[]{APP_A_FOREGROUND_ACTIVITY}, APP_A_FOREGROUND_ACTIVITY);
@@ -310,9 +312,7 @@
boolean result = waitForActivityFocused(APP_A_FOREGROUND_ACTIVITY);
assertTrue("Not able to start foreground activity", result);
assertTaskStack(new ComponentName[]{APP_A_FOREGROUND_ACTIVITY}, APP_A_FOREGROUND_ACTIVITY);
- pressHomeButton();
- mAmWmState.waitForHomeActivityVisible();
- waitToPreventAppSwitchProtection();
+ pressHomeAndResumeAppSwitch();
// The activity, now in the background, will attempt to start 2 activities in quick
// succession
@@ -390,6 +390,7 @@
}
@Test
+ @FlakyTest(bugId = 141344170)
public void testPendingIntentBroadcastTimeout_delay1s() throws Exception {
assertPendingIntentBroadcastTimeoutTest(1000, true);
}
@@ -401,9 +402,16 @@
@Test
public void testPendingIntentBroadcast_appBIsBackground() throws Exception {
+ EventReceiver receiver = new EventReceiver(
+ Event.APP_A_START_BACKGROUND_ACTIVITY_BROADCAST_RECEIVED);
+
// Send pendingIntent from AppA to AppB, and the AppB launch the pending intent to start
// activity in App A
- sendPendingIntentBroadcast(0);
+ sendPendingIntentBroadcast(0, receiver.getNotifier());
+
+ // Waits for final hoop in AppA to start looking for activity, otherwise it could succeed
+ // if the broadcast took long time to get executed (which may happen after boot).
+ receiver.waitForEventOrThrow(BROADCAST_DELIVERY_TIMEOUT_MS);
boolean result = waitForActivityFocused(APP_A_BACKGROUND_ACTIVITY);
assertFalse("Should not able to launch background activity", result);
assertTaskStack(null, APP_A_BACKGROUND_ACTIVITY);
@@ -420,17 +428,29 @@
String cmdResult = runShellCommand("dpm set-device-owner --user cur "
+ APP_A_SIMPLE_ADMIN_RECEIVER.flattenToString());
assertThat(cmdResult).contains("Success");
+ EventReceiver receiver = new EventReceiver(
+ Event.APP_A_START_BACKGROUND_ACTIVITY_BROADCAST_RECEIVED);
Intent intent = new Intent();
intent.setComponent(APP_A_START_ACTIVITY_RECEIVER);
+ intent.putExtra(EVENT_NOTIFIER_EXTRA, receiver.getNotifier());
+
mContext.sendBroadcast(intent);
+
+ // Waits for final hoop in AppA to start looking for activity
+ receiver.waitForEventOrThrow(BROADCAST_DELIVERY_TIMEOUT_MS);
boolean result = waitForActivityFocused(APP_A_BACKGROUND_ACTIVITY);
assertTrue("Not able to launch background activity", result);
assertTaskStack(new ComponentName[]{APP_A_BACKGROUND_ACTIVITY}, APP_A_BACKGROUND_ACTIVITY);
}
- private void waitToPreventAppSwitchProtection() {
- // Any activity launch will be blocked for 5s because of app switching protection.
- SystemClock.sleep(7000);
+ private void pressHomeAndResumeAppSwitch() {
+ // Press home key to ensure stopAppSwitches is called because the last-stop-app-switch-time
+ // is a criteria of allowing background start.
+ pressHomeButton();
+ // Resume the stopped state (it won't affect last-stop-app-switch-time) so we don't need to
+ // wait extra time to prevent the next launch from being delayed.
+ resumeAppSwitches();
+ mAmWmState.waitForHomeActivityVisible();
}
private void assertTaskStack(ComponentName[] expectedComponents,
@@ -448,7 +468,8 @@
}
}
- private void assertPendingIntentBroadcastTimeoutTest(int delayMs, boolean expectedResult) {
+ private void assertPendingIntentBroadcastTimeoutTest(int delayMs, boolean expectedResult)
+ throws TimeoutException {
// Start AppB foreground activity
Intent intent = new Intent();
intent.setComponent(APP_B_FOREGROUND_ACTIVITY);
@@ -457,10 +478,15 @@
boolean result = waitForActivityFocused(APP_B_FOREGROUND_ACTIVITY);
assertTrue("Not able to start foreground Activity", result);
assertTaskStack(new ComponentName[]{APP_B_FOREGROUND_ACTIVITY}, APP_B_FOREGROUND_ACTIVITY);
+ EventReceiver receiver = new EventReceiver(
+ Event.APP_A_START_BACKGROUND_ACTIVITY_BROADCAST_RECEIVED);
// Send pendingIntent from AppA to AppB, and the AppB launch the pending intent to start
// activity in App A
- sendPendingIntentBroadcast(delayMs);
+ sendPendingIntentBroadcast(delayMs, receiver.getNotifier());
+
+ // Waits for final hoop in AppA to start looking for activity
+ receiver.waitForEventOrThrow(BROADCAST_DELIVERY_TIMEOUT_MS);
result = waitForActivityFocused(ACTIVITY_FOCUS_TIMEOUT_MS + delayMs,
APP_A_BACKGROUND_ACTIVITY);
assertEquals(expectedResult, result);
@@ -502,13 +528,14 @@
mContext.sendBroadcast(intent);
}
- private void sendPendingIntentBroadcast(int delayMs) {
+ private void sendPendingIntentBroadcast(int delayMs, @Nullable ResultReceiver eventNotifier) {
Intent intent = new Intent();
intent.setComponent(APP_A_SEND_PENDING_INTENT_RECEIVER);
intent.putExtra(IS_BROADCAST_EXTRA, true);
if (delayMs > 0) {
intent.putExtra(START_ACTIVITY_DELAY_MS_EXTRA, delayMs);
}
+ intent.putExtra(EVENT_NOTIFIER_EXTRA, eventNotifier);
mContext.sendBroadcast(intent);
}
}
diff --git a/tests/framework/base/windowmanager/intent_tests/clearCases/clear-task_with_new-task.json b/tests/framework/base/windowmanager/intent_tests/clearCases/clear-task_with_new-task.json
new file mode 100644
index 0000000..09753b8
--- /dev/null
+++ b/tests/framework/base/windowmanager/intent_tests/clearCases/clear-task_with_new-task.json
@@ -0,0 +1,54 @@
+{
+ "setup": {
+ "initialIntents": [
+ {
+ "flags": "FLAG_ACTIVITY_NEW_TASK",
+ "class": "android.server.wm.intent.Activities$RegularActivity",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ }
+ ],
+ "act": [
+ {
+ "flags": "FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK",
+ "class": "android.server.wm.intent.Activities$RegularActivity",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ }
+ ]
+ },
+ "initialState": {
+ "stacks": [
+ {
+ "tasks": [
+ {
+ "activities": [
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity",
+ "state": "RESUMED"
+ }
+ ]
+ }
+ ],
+ "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity"
+ }
+ ]
+ },
+ "endState": {
+ "stacks": [
+ {
+ "tasks": [
+ {
+ "activities": [
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity",
+ "state": "RESUMED"
+ }
+ ]
+ }
+ ],
+ "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity"
+ }
+ ]
+ }
+}
diff --git a/tests/framework/base/windowmanager/intent_tests/clearCases/clear-task_without_new-task.json b/tests/framework/base/windowmanager/intent_tests/clearCases/clear-task_without_new-task.json
new file mode 100644
index 0000000..da82d52
--- /dev/null
+++ b/tests/framework/base/windowmanager/intent_tests/clearCases/clear-task_without_new-task.json
@@ -0,0 +1,58 @@
+{
+ "setup": {
+ "initialIntents": [
+ {
+ "flags": "FLAG_ACTIVITY_NEW_TASK",
+ "class": "android.server.wm.intent.Activities$RegularActivity",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ }
+ ],
+ "act": [
+ {
+ "flags": "FLAG_ACTIVITY_CLEAR_TASK",
+ "class": "android.server.wm.intent.Activities$RegularActivity",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ }
+ ]
+ },
+ "initialState": {
+ "stacks": [
+ {
+ "tasks": [
+ {
+ "activities": [
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity",
+ "state": "RESUMED"
+ }
+ ]
+ }
+ ],
+ "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity"
+ }
+ ]
+ },
+ "endState": {
+ "stacks": [
+ {
+ "tasks": [
+ {
+ "activities": [
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity",
+ "state": "RESUMED"
+ },
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity",
+ "state": "STOPPED"
+ }
+ ]
+ }
+ ],
+ "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity"
+ }
+ ]
+ }
+}
diff --git a/tests/framework/base/windowmanager/intent_tests/clearCases/test-3.json b/tests/framework/base/windowmanager/intent_tests/clearCases/test-3.json
index 28e6a48..42f1ffa 100644
--- a/tests/framework/base/windowmanager/intent_tests/clearCases/test-3.json
+++ b/tests/framework/base/windowmanager/intent_tests/clearCases/test-3.json
@@ -53,6 +53,10 @@
{
"name": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity",
"state": "RESUMED"
+ },
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity",
+ "state": "STOPPED"
}
]
}
diff --git a/tests/framework/base/windowmanager/intent_tests/newTask/request_new_task_different_affinity-new_task.json b/tests/framework/base/windowmanager/intent_tests/newTask/request_new_task_different_affinity-new_task.json
new file mode 100644
index 0000000..9d878d8
--- /dev/null
+++ b/tests/framework/base/windowmanager/intent_tests/newTask/request_new_task_different_affinity-new_task.json
@@ -0,0 +1,66 @@
+{
+ "setup": {
+ "initialIntents": [
+ {
+ "flags": "FLAG_ACTIVITY_NEW_TASK",
+ "class": "android.server.wm.intent.Activities$TaskAffinity1Activity",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ }
+ ],
+ "act": [
+ {
+ "flags": "FLAG_ACTIVITY_NEW_TASK",
+ "class": "android.server.wm.intent.Activities$RegularActivity",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ }
+ ]
+ },
+ "initialState": {
+ "stacks": [
+ {
+ "tasks": [
+ {
+ "activities": [
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+ "state": "RESUMED"
+ }
+ ]
+ }
+ ],
+ "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity"
+ }
+ ]
+ },
+ "endState": {
+ "stacks": [
+ {
+ "tasks": [
+ {
+ "activities": [
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity",
+ "state": "RESUMED"
+ }
+ ]
+ }
+ ],
+ "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity"
+ },
+ {
+ "tasks": [
+ {
+ "activities": [
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+ "state": "STOPPED"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/tests/framework/base/windowmanager/intent_tests/newTask/request_new_task_same_affinity-same_task.json b/tests/framework/base/windowmanager/intent_tests/newTask/request_new_task_same_affinity-same_task.json
new file mode 100644
index 0000000..6944973
--- /dev/null
+++ b/tests/framework/base/windowmanager/intent_tests/newTask/request_new_task_same_affinity-same_task.json
@@ -0,0 +1,58 @@
+{
+ "setup": {
+ "initialIntents": [
+ {
+ "flags": "FLAG_ACTIVITY_NEW_TASK",
+ "class": "android.server.wm.intent.Activities$TaskAffinity1Activity",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ }
+ ],
+ "act": [
+ {
+ "flags": "FLAG_ACTIVITY_NEW_TASK",
+ "class": "android.server.wm.intent.Activities$TaskAffinity1Activity2",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ }
+ ]
+ },
+ "initialState": {
+ "stacks": [
+ {
+ "tasks": [
+ {
+ "activities": [
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+ "state": "RESUMED"
+ }
+ ]
+ }
+ ],
+ "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity"
+ }
+ ]
+ },
+ "endState": {
+ "stacks": [
+ {
+ "tasks": [
+ {
+ "activities": [
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity2",
+ "state": "RESUMED"
+ },
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+ "state": "STOPPED"
+ }
+ ]
+ }
+ ],
+ "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity2"
+ }
+ ]
+ }
+}
diff --git a/tests/framework/base/windowmanager/intent_tests/newTask/request_same_task_different_affinity-same_task.json b/tests/framework/base/windowmanager/intent_tests/newTask/request_same_task_different_affinity-same_task.json
new file mode 100644
index 0000000..a444299
--- /dev/null
+++ b/tests/framework/base/windowmanager/intent_tests/newTask/request_same_task_different_affinity-same_task.json
@@ -0,0 +1,58 @@
+{
+ "setup": {
+ "initialIntents": [
+ {
+ "flags": "FLAG_ACTIVITY_NEW_TASK",
+ "class": "android.server.wm.intent.Activities$TaskAffinity1Activity",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ }
+ ],
+ "act": [
+ {
+ "flags": "",
+ "class": "android.server.wm.intent.Activities$RegularActivity",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ }
+ ]
+ },
+ "initialState": {
+ "stacks": [
+ {
+ "tasks": [
+ {
+ "activities": [
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+ "state": "RESUMED"
+ }
+ ]
+ }
+ ],
+ "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity"
+ }
+ ]
+ },
+ "endState": {
+ "stacks": [
+ {
+ "tasks": [
+ {
+ "activities": [
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity",
+ "state": "RESUMED"
+ },
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+ "state": "STOPPED"
+ }
+ ]
+ }
+ ],
+ "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity"
+ }
+ ]
+ }
+}
diff --git a/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/keep_affinity-request_new_task_with_same_affinity-same_task.json b/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/keep_affinity-request_new_task_with_same_affinity-same_task.json
new file mode 100644
index 0000000..3dd2c99
--- /dev/null
+++ b/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/keep_affinity-request_new_task_with_same_affinity-same_task.json
@@ -0,0 +1,72 @@
+{
+ "setup": {
+ "initialIntents": [
+ {
+ "flags": "FLAG_ACTIVITY_NEW_TASK",
+ "class": "android.server.wm.intent.Activities$TaskAffinity1RelinquishTaskIdentityActivity",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ },
+ {
+ "flags": "",
+ "class": "android.server.wm.intent.Activities$TaskAffinity1Activity",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ }
+ ],
+ "act": [
+ {
+ "flags": "FLAG_ACTIVITY_NEW_TASK",
+ "class": "android.server.wm.intent.Activities$TaskAffinity1Activity2",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ }
+ ]
+ },
+ "initialState": {
+ "stacks": [
+ {
+ "tasks": [
+ {
+ "activities": [
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+ "state": "RESUMED"
+ },
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1RelinquishTaskIdentityActivity",
+ "state": "STOPPED"
+ }
+ ]
+ }
+ ],
+ "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity"
+ }
+ ]
+ },
+ "endState": {
+ "stacks": [
+ {
+ "tasks": [
+ {
+ "activities": [
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity2",
+ "state": "RESUMED"
+ },
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+ "state": "STOPPED"
+ },
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1RelinquishTaskIdentityActivity",
+ "state": "STOPPED"
+ }
+ ]
+ }
+ ],
+ "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity2"
+ }
+ ]
+ }
+}
diff --git a/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/request_change_affinity-request_new_task_with_changed_affinity-new_task.json b/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/request_change_affinity-request_new_task_with_changed_affinity-new_task.json
new file mode 100644
index 0000000..53f01b5
--- /dev/null
+++ b/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/request_change_affinity-request_new_task_with_changed_affinity-new_task.json
@@ -0,0 +1,81 @@
+{
+ "comment": "relinquishTaskIdentity does not allow changing the root affinity of the task - ag/548224",
+ "setup": {
+ "initialIntents": [
+ {
+ "flags": "FLAG_ACTIVITY_NEW_TASK",
+ "class": "android.server.wm.intent.Activities$RelinquishTaskIdentityActivity",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ },
+ {
+ "flags": "",
+ "class": "android.server.wm.intent.Activities$TaskAffinity1Activity",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ }
+ ],
+ "act": [
+ {
+ "flags": "FLAG_ACTIVITY_NEW_TASK",
+ "class": "android.server.wm.intent.Activities$TaskAffinity1Activity2",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ }
+ ]
+ },
+ "initialState": {
+ "stacks": [
+ {
+ "tasks": [
+ {
+ "activities": [
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+ "state": "RESUMED"
+ },
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RelinquishTaskIdentityActivity",
+ "state": "STOPPED"
+ }
+ ]
+ }
+ ],
+ "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity"
+ }
+ ]
+ },
+ "endState": {
+ "stacks": [
+ {
+ "tasks": [
+ {
+ "activities": [
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity2",
+ "state": "RESUMED"
+ }
+ ]
+ }
+ ],
+ "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity2"
+ },
+ {
+ "tasks": [
+ {
+ "activities": [
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+ "state": "STOPPED"
+ },
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RelinquishTaskIdentityActivity",
+ "state": "STOPPED"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/request_change_affinity-request_new_task_with_old_affinity-same_task.json b/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/request_change_affinity-request_new_task_with_old_affinity-same_task.json
new file mode 100644
index 0000000..2ec6f78
--- /dev/null
+++ b/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/request_change_affinity-request_new_task_with_old_affinity-same_task.json
@@ -0,0 +1,73 @@
+{
+ "comment": "relinquishTaskIdentity does not allow changing the root affinity of the task - ag/548224",
+ "setup": {
+ "initialIntents": [
+ {
+ "flags": "FLAG_ACTIVITY_NEW_TASK",
+ "class": "android.server.wm.intent.Activities$RelinquishTaskIdentityActivity",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ },
+ {
+ "flags": "",
+ "class": "android.server.wm.intent.Activities$TaskAffinity1Activity",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ }
+ ],
+ "act": [
+ {
+ "flags": "FLAG_ACTIVITY_NEW_TASK",
+ "class": "android.server.wm.intent.Activities$RegularActivity",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ }
+ ]
+ },
+ "initialState": {
+ "stacks": [
+ {
+ "tasks": [
+ {
+ "activities": [
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+ "state": "RESUMED"
+ },
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RelinquishTaskIdentityActivity",
+ "state": "STOPPED"
+ }
+ ]
+ }
+ ],
+ "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity"
+ }
+ ]
+ },
+ "endState": {
+ "stacks": [
+ {
+ "tasks": [
+ {
+ "activities": [
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity",
+ "state": "RESUMED"
+ },
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+ "state": "STOPPED"
+ },
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RelinquishTaskIdentityActivity",
+ "state": "STOPPED"
+ }
+ ]
+ }
+ ],
+ "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity"
+ }
+ ]
+ }
+}
diff --git a/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/request_change_affinity-request_same_task_with_changed_affinity-same_task.json b/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/request_change_affinity-request_same_task_with_changed_affinity-same_task.json
new file mode 100644
index 0000000..7e31db4
--- /dev/null
+++ b/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/request_change_affinity-request_same_task_with_changed_affinity-same_task.json
@@ -0,0 +1,73 @@
+{
+ "comment": "relinquishTaskIdentity does not allow changing the root affinity of the task - ag/548224",
+ "setup": {
+ "initialIntents": [
+ {
+ "flags": "FLAG_ACTIVITY_NEW_TASK",
+ "class": "android.server.wm.intent.Activities$RelinquishTaskIdentityActivity",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ },
+ {
+ "flags": "",
+ "class": "android.server.wm.intent.Activities$TaskAffinity1Activity",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ }
+ ],
+ "act": [
+ {
+ "flags": "",
+ "class": "android.server.wm.intent.Activities$TaskAffinity1Activity2",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ }
+ ]
+ },
+ "initialState": {
+ "stacks": [
+ {
+ "tasks": [
+ {
+ "activities": [
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+ "state": "RESUMED"
+ },
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RelinquishTaskIdentityActivity",
+ "state": "STOPPED"
+ }
+ ]
+ }
+ ],
+ "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity"
+ }
+ ]
+ },
+ "endState": {
+ "stacks": [
+ {
+ "tasks": [
+ {
+ "activities": [
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity2",
+ "state": "RESUMED"
+ },
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+ "state": "STOPPED"
+ },
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RelinquishTaskIdentityActivity",
+ "state": "STOPPED"
+ }
+ ]
+ }
+ ],
+ "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity2"
+ }
+ ]
+ }
+}
diff --git a/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/request_change_affinity-request_same_task_with_old_affinity-same_task.json b/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/request_change_affinity-request_same_task_with_old_affinity-same_task.json
new file mode 100644
index 0000000..afd155a
--- /dev/null
+++ b/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/request_change_affinity-request_same_task_with_old_affinity-same_task.json
@@ -0,0 +1,73 @@
+{
+ "comment": "relinquishTaskIdentity does not allow changing the root affinity of the task - ag/548224",
+ "setup": {
+ "initialIntents": [
+ {
+ "flags": "FLAG_ACTIVITY_NEW_TASK",
+ "class": "android.server.wm.intent.Activities$RelinquishTaskIdentityActivity",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ },
+ {
+ "flags": "",
+ "class": "android.server.wm.intent.Activities$TaskAffinity1Activity",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ }
+ ],
+ "act": [
+ {
+ "flags": "",
+ "class": "android.server.wm.intent.Activities$RegularActivity",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ }
+ ]
+ },
+ "initialState": {
+ "stacks": [
+ {
+ "tasks": [
+ {
+ "activities": [
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+ "state": "RESUMED"
+ },
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RelinquishTaskIdentityActivity",
+ "state": "STOPPED"
+ }
+ ]
+ }
+ ],
+ "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity"
+ }
+ ]
+ },
+ "endState": {
+ "stacks": [
+ {
+ "tasks": [
+ {
+ "activities": [
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity",
+ "state": "RESUMED"
+ },
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+ "state": "STOPPED"
+ },
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RelinquishTaskIdentityActivity",
+ "state": "STOPPED"
+ }
+ ]
+ }
+ ],
+ "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity"
+ }
+ ]
+ }
+}
diff --git a/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/request_new_task_with_different_affinity-new_task.json b/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/request_new_task_with_different_affinity-new_task.json
new file mode 100644
index 0000000..cd6a3a4
--- /dev/null
+++ b/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/request_new_task_with_different_affinity-new_task.json
@@ -0,0 +1,66 @@
+{
+ "setup": {
+ "initialIntents": [
+ {
+ "flags": "FLAG_ACTIVITY_NEW_TASK",
+ "class": "android.server.wm.intent.Activities$TaskAffinity1RelinquishTaskIdentityActivity",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ }
+ ],
+ "act": [
+ {
+ "flags": "FLAG_ACTIVITY_NEW_TASK",
+ "class": "android.server.wm.intent.Activities$RegularActivity",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ }
+ ]
+ },
+ "initialState": {
+ "stacks": [
+ {
+ "tasks": [
+ {
+ "activities": [
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1RelinquishTaskIdentityActivity",
+ "state": "RESUMED"
+ }
+ ]
+ }
+ ],
+ "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1RelinquishTaskIdentityActivity"
+ }
+ ]
+ },
+ "endState": {
+ "stacks": [
+ {
+ "tasks": [
+ {
+ "activities": [
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity",
+ "state": "RESUMED"
+ }
+ ]
+ }
+ ],
+ "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity"
+ },
+ {
+ "tasks": [
+ {
+ "activities": [
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1RelinquishTaskIdentityActivity",
+ "state": "STOPPED"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/request_new_task_with_same_affinity-same_task.json b/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/request_new_task_with_same_affinity-same_task.json
new file mode 100644
index 0000000..999ee82
--- /dev/null
+++ b/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/request_new_task_with_same_affinity-same_task.json
@@ -0,0 +1,58 @@
+{
+ "setup": {
+ "initialIntents": [
+ {
+ "flags": "FLAG_ACTIVITY_NEW_TASK",
+ "class": "android.server.wm.intent.Activities$TaskAffinity1RelinquishTaskIdentityActivity",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ }
+ ],
+ "act": [
+ {
+ "flags": "FLAG_ACTIVITY_NEW_TASK",
+ "class": "android.server.wm.intent.Activities$TaskAffinity1Activity",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ }
+ ]
+ },
+ "initialState": {
+ "stacks": [
+ {
+ "tasks": [
+ {
+ "activities": [
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1RelinquishTaskIdentityActivity",
+ "state": "RESUMED"
+ }
+ ]
+ }
+ ],
+ "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1RelinquishTaskIdentityActivity"
+ }
+ ]
+ },
+ "endState": {
+ "stacks": [
+ {
+ "tasks": [
+ {
+ "activities": [
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+ "state": "RESUMED"
+ },
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1RelinquishTaskIdentityActivity",
+ "state": "STOPPED"
+ }
+ ]
+ }
+ ],
+ "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity"
+ }
+ ]
+ }
+}
diff --git a/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/request_same_task_with_different_affinity-same_task.json b/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/request_same_task_with_different_affinity-same_task.json
new file mode 100644
index 0000000..d0ec60c
--- /dev/null
+++ b/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/request_same_task_with_different_affinity-same_task.json
@@ -0,0 +1,58 @@
+{
+ "setup": {
+ "initialIntents": [
+ {
+ "flags": "FLAG_ACTIVITY_NEW_TASK",
+ "class": "android.server.wm.intent.Activities$TaskAffinity1RelinquishTaskIdentityActivity",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ }
+ ],
+ "act": [
+ {
+ "flags": "",
+ "class": "android.server.wm.intent.Activities$RegularActivity",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ }
+ ]
+ },
+ "initialState": {
+ "stacks": [
+ {
+ "tasks": [
+ {
+ "activities": [
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1RelinquishTaskIdentityActivity",
+ "state": "RESUMED"
+ }
+ ]
+ }
+ ],
+ "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1RelinquishTaskIdentityActivity"
+ }
+ ]
+ },
+ "endState": {
+ "stacks": [
+ {
+ "tasks": [
+ {
+ "activities": [
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity",
+ "state": "RESUMED"
+ },
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1RelinquishTaskIdentityActivity",
+ "state": "STOPPED"
+ }
+ ]
+ }
+ ],
+ "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity"
+ }
+ ]
+ }
+}
diff --git a/tests/framework/base/windowmanager/intent_tests/reorderToFront/reorder-to-front.json b/tests/framework/base/windowmanager/intent_tests/reorderToFront/reorder-to-front.json
new file mode 100644
index 0000000..9daaa0f
--- /dev/null
+++ b/tests/framework/base/windowmanager/intent_tests/reorderToFront/reorder-to-front.json
@@ -0,0 +1,96 @@
+{
+ "setup": {
+ "initialIntents": [
+ {
+ "flags": "FLAG_ACTIVITY_NEW_TASK",
+ "class": "android.server.wm.intent.Activities$RegularActivity",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ },
+ {
+ "flags": "",
+ "class": "android.server.wm.intent.Activities$TaskAffinity1Activity",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ },
+ {
+ "flags": "",
+ "class": "android.server.wm.intent.Activities$TaskAffinity2Activity",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ },
+ {
+ "flags": "",
+ "class": "android.server.wm.intent.Activities$RegularActivity",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ }
+ ],
+ "act": [
+ {
+ "flags": "FLAG_ACTIVITY_REORDER_TO_FRONT",
+ "class": "android.server.wm.intent.Activities$TaskAffinity1Activity",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ }
+ ]
+ },
+ "initialState": {
+ "stacks": [
+ {
+ "tasks": [
+ {
+ "activities": [
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity",
+ "state": "RESUMED"
+ },
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity2Activity",
+ "state": "STOPPED"
+ },
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+ "state": "STOPPED"
+ },
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity",
+ "state": "STOPPED"
+ }
+ ]
+ }
+ ],
+ "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity"
+ }
+ ]
+ },
+ "endState": {
+ "stacks": [
+ {
+ "tasks": [
+ {
+ "activities": [
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+ "state": "RESUMED"
+ },
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity",
+ "state": "STOPPED"
+ },
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity2Activity",
+ "state": "STOPPED"
+ },
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity",
+ "state": "STOPPED"
+ }
+ ]
+ }
+ ],
+ "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity"
+ }
+ ]
+ }
+}
diff --git a/tests/framework/base/windowmanager/intent_tests/reorderToFront/reorder-to-front_with_new-task_on_different_affinity.json b/tests/framework/base/windowmanager/intent_tests/reorderToFront/reorder-to-front_with_new-task_on_different_affinity.json
new file mode 100644
index 0000000..ab9ae9b
--- /dev/null
+++ b/tests/framework/base/windowmanager/intent_tests/reorderToFront/reorder-to-front_with_new-task_on_different_affinity.json
@@ -0,0 +1,95 @@
+{
+ "comment": "A new activity should be created on a new task, without interfering the activity order of caller's task.",
+ "setup": {
+ "initialIntents": [
+ {
+ "flags": "FLAG_ACTIVITY_NEW_TASK",
+ "class": "android.server.wm.intent.Activities$RegularActivity",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ },
+ {
+ "flags": "",
+ "class": "android.server.wm.intent.Activities$TaskAffinity1Activity",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ },
+ {
+ "flags": "",
+ "class": "android.server.wm.intent.Activities$TaskAffinity2Activity",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ }
+ ],
+ "act": [
+ {
+ "flags": "FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_REORDER_TO_FRONT",
+ "class": "android.server.wm.intent.Activities$TaskAffinity1Activity",
+ "package": "android.server.wm.cts",
+ "startForResult": false
+ }
+ ]
+ },
+ "initialState": {
+ "stacks": [
+ {
+ "tasks": [
+ {
+ "activities": [
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity2Activity",
+ "state": "RESUMED"
+ },
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+ "state": "STOPPED"
+ },
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity",
+ "state": "STOPPED"
+ }
+ ]
+ }
+ ],
+ "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity2Activity"
+ }
+ ]
+ },
+ "endState": {
+ "stacks": [
+ {
+ "tasks": [
+ {
+ "activities": [
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+ "state": "RESUMED"
+ }
+ ]
+ }
+ ],
+ "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity"
+ },
+ {
+ "tasks": [
+ {
+ "activities": [
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity2Activity",
+ "state": "STOPPED"
+ },
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$TaskAffinity1Activity",
+ "state": "STOPPED"
+ },
+ {
+ "name": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity",
+ "state": "STOPPED"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/tests/framework/base/windowmanager/intent_tests/resetTaskIfNeeded/test-1.json b/tests/framework/base/windowmanager/intent_tests/resetTaskIfNeeded/test-1.json
index 601134c..2cf1652 100644
--- a/tests/framework/base/windowmanager/intent_tests/resetTaskIfNeeded/test-1.json
+++ b/tests/framework/base/windowmanager/intent_tests/resetTaskIfNeeded/test-1.json
@@ -55,7 +55,12 @@
"state": "RESUMED"
}
]
- },
+ }
+ ],
+ "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity"
+ },
+ {
+ "tasks": [
{
"activities": [
{
@@ -64,8 +69,7 @@
}
]
}
- ],
- "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity"
+ ]
}
]
}
diff --git a/tests/framework/base/windowmanager/intent_tests/resetTaskIfNeeded/test-3.json b/tests/framework/base/windowmanager/intent_tests/resetTaskIfNeeded/test-3.json
index 7364d63..c89cdd3 100644
--- a/tests/framework/base/windowmanager/intent_tests/resetTaskIfNeeded/test-3.json
+++ b/tests/framework/base/windowmanager/intent_tests/resetTaskIfNeeded/test-3.json
@@ -73,7 +73,12 @@
"state": "RESUMED"
}
]
- },
+ }
+ ],
+ "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity"
+ },
+ {
+ "tasks": [
{
"activities": [
{
@@ -82,8 +87,7 @@
}
]
}
- ],
- "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity"
+ ]
},
{
"tasks": [
diff --git a/tests/framework/base/windowmanager/intent_tests/resetTaskIfNeeded/test-4.json b/tests/framework/base/windowmanager/intent_tests/resetTaskIfNeeded/test-4.json
index 62c1441..d8f3f7f 100644
--- a/tests/framework/base/windowmanager/intent_tests/resetTaskIfNeeded/test-4.json
+++ b/tests/framework/base/windowmanager/intent_tests/resetTaskIfNeeded/test-4.json
@@ -65,7 +65,12 @@
"state": "RESUMED"
}
]
- },
+ }
+ ],
+ "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity"
+ },
+ {
+ "tasks": [
{
"activities": [
{
@@ -78,8 +83,7 @@
}
]
}
- ],
- "resumedActivity": "android.server.wm.cts\/android.server.wm.intent.Activities$RegularActivity"
+ ]
}
]
}
diff --git a/tests/framework/base/windowmanager/res/layout/transition_destination_layout.xml b/tests/framework/base/windowmanager/res/layout/transition_destination_layout.xml
new file mode 100644
index 0000000..b7b4e3d
--- /dev/null
+++ b/tests/framework/base/windowmanager/res/layout/transition_destination_layout.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <ImageView
+ android:id="@+id/transitionView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:transitionName="sharedTransition"
+ android:src="@android:drawable/ic_media_play"/>
+
+</LinearLayout>
+
diff --git a/tests/framework/base/windowmanager/res/layout/transition_source_layout.xml b/tests/framework/base/windowmanager/res/layout/transition_source_layout.xml
new file mode 100644
index 0000000..7612430
--- /dev/null
+++ b/tests/framework/base/windowmanager/res/layout/transition_source_layout.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <ImageView
+ android:id="@+id/transitionView"
+ android:layout_width="200dp"
+ android:layout_height="200dp"
+ android:transitionName="sharedTransition"
+ android:src="@android:drawable/ic_media_play" />
+
+</LinearLayout>
+
diff --git a/tests/framework/base/windowmanager/res/values/styles.xml b/tests/framework/base/windowmanager/res/values/styles.xml
index a3de74e..c315745 100644
--- a/tests/framework/base/windowmanager/res/values/styles.xml
+++ b/tests/framework/base/windowmanager/res/values/styles.xml
@@ -31,4 +31,7 @@
<item name="android:windowContentTransitions">false</item>
<item name="android:windowAnimationStyle">@null</item>
</style>
+ <style name="window_activity_transitions" parent="@android:style/Theme.DeviceDefault">
+ <item name="android:windowActivityTransitions">true</item>
+ </style>
</resources>
\ No newline at end of file
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/ActivityMetricsLoggerTests.java b/tests/framework/base/windowmanager/src/android/server/wm/ActivityMetricsLoggerTests.java
index b827353..d11ae93 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/ActivityMetricsLoggerTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/ActivityMetricsLoggerTests.java
@@ -61,8 +61,6 @@
import android.support.test.metricshelper.MetricsAsserts;
import android.util.EventLog.Event;
-import androidx.test.filters.FlakyTest;
-
import com.android.compatibility.common.util.SystemUtil;
import org.hamcrest.collection.IsIn;
@@ -215,7 +213,6 @@
* metrics logs. Verify we output the correct launch state.
*/
@Test
- @FlakyTest(bugId = 130764822)
public void testAppWarmLaunchSetsWaitResultDelayData() {
SystemUtil.runShellCommand("am start -S -W " + TEST_ACTIVITY.flattenToShortString());
@@ -252,12 +249,11 @@
* metrics logs. Verify we output the correct launch state.
*/
@Test
- @FlakyTest(bugId = 130764822)
public void testAppHotLaunchSetsWaitResultDelayData() {
SystemUtil.runShellCommand("am start -S -W " + TEST_ACTIVITY.flattenToShortString());
// Test hot launch
- pressHomeButton();
+ launchHomeActivityNoWait();
waitForDeviceIdle(1000);
mMetricsReader.checkpoint(); // clear out old logs
@@ -289,7 +285,6 @@
* metrics logs. Verify we output the correct launch state.
*/
@Test
- @FlakyTest(bugId = 130764822)
public void testAppColdLaunchSetsWaitResultDelayData() {
final String amStartOutput = SystemUtil.runShellCommand(
"am start -S -W " + TEST_ACTIVITY.flattenToShortString());
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/ActivityViewTest.java b/tests/framework/base/windowmanager/src/android/server/wm/ActivityViewTest.java
index c2deb24..0664b52 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/ActivityViewTest.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/ActivityViewTest.java
@@ -128,18 +128,16 @@
boundsMatched);
}
+ /** @return {@code true} if the display size for the activity matches the given size. */
private boolean checkDisplaySize(ComponentName activity, int requestedWidth,
int requestedHeight) {
- final int maxTries = 5;
- final int retryIntervalMs = 1000;
-
- boolean boundsMatched = false;
-
// Display size for the activity may not get updated right away. Retry in case.
- for (int i = 0; i < maxTries; i++) {
- mAmWmState.getWmState().computeState();
- int displayId = mAmWmState.getAmState().getDisplayByActivity(activity);
- WindowManagerState.Display display = mAmWmState.getWmState().getDisplay(displayId);
+ return Condition.waitFor("display size=" + requestedWidth + "x" + requestedHeight, () -> {
+ final WindowManagerState wmState = mAmWmState.getWmState();
+ wmState.computeState();
+
+ final int displayId = mAmWmState.getAmState().getDisplayByActivity(activity);
+ final WindowManagerState.Display display = wmState.getDisplay(displayId);
int avDisplayWidth = 0;
int avDisplayHeight = 0;
if (display != null) {
@@ -149,16 +147,8 @@
avDisplayHeight = bounds.height();
}
}
- boundsMatched = avDisplayWidth == requestedWidth && avDisplayHeight == requestedHeight;
- if (boundsMatched) {
- return true;
- }
-
- // wait and try again
- SystemClock.sleep(retryIntervalMs);
- }
-
- return boundsMatched;
+ return avDisplayWidth == requestedWidth && avDisplayHeight == requestedHeight;
+ });
}
@Test
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/ActivityVisibilityTests.java b/tests/framework/base/windowmanager/src/android/server/wm/ActivityVisibilityTests.java
index 523e660..63947ba 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/ActivityVisibilityTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/ActivityVisibilityTests.java
@@ -59,8 +59,9 @@
import static org.junit.Assume.assumeTrue;
import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.FlakyTest;
+import android.server.wm.CommandSession.ActivitySession;
+import android.server.wm.CommandSession.ActivitySessionClient;
+import android.server.wm.app.Components;
import org.junit.Rule;
import org.junit.Test;
@@ -76,7 +77,6 @@
public final DisableScreenDozeRule mDisableScreenDozeRule = new DisableScreenDozeRule();
@Test
- @FlakyTest(bugId = 110276714)
public void testTranslucentActivityOnTopOfPinnedStack() throws Exception {
if (!supportsPip()) {
return;
@@ -177,19 +177,34 @@
}
@Test
- @FlakyTest(bugId = 110276714)
- public void testTurnScreenOnActivity() throws Exception {
- try (final LockScreenSession lockScreenSession = new LockScreenSession()) {
- lockScreenSession.sleepDevice();
- launchActivity(TURN_SCREEN_ON_ACTIVITY);
-
- mAmWmState.assertVisibility(TURN_SCREEN_ON_ACTIVITY, true);
- assertTrue("Display turns on", isDisplayOn(DEFAULT_DISPLAY));
+ public void testTurnScreenOnActivity() {
+ try (final LockScreenSession lockScreenSession = new LockScreenSession();
+ final ActivitySessionClient activityClient = new ActivitySessionClient(mContext)) {
+ testTurnScreenOnActivity(lockScreenSession, activityClient, true /* useWindowFlags */);
+ testTurnScreenOnActivity(lockScreenSession, activityClient, false /* useWindowFlags */);
}
}
+ private void testTurnScreenOnActivity(LockScreenSession lockScreenSession,
+ ActivitySessionClient activitySessionClient, boolean useWindowFlags) {
+ lockScreenSession.sleepDevice();
+
+ final ActivitySession activity = activitySessionClient.startActivity(
+ getLaunchActivityBuilder()
+ .setUseInstrumentation()
+ .setIntentExtra(extra -> extra.putBoolean(
+ Components.TurnScreenOnActivity.EXTRA_USE_WINDOW_FLAGS,
+ useWindowFlags))
+ .setTargetActivity(TURN_SCREEN_ON_ACTIVITY));
+
+ mAmWmState.assertVisibility(TURN_SCREEN_ON_ACTIVITY, true);
+ assertTrue("Display turns on by " + (useWindowFlags ? "flags" : "APIs"),
+ isDisplayOn(DEFAULT_DISPLAY));
+
+ activity.finish();
+ }
+
@Test
- @FlakyTest(bugId = 110276714)
public void testFinishActivityInNonFocusedStack() throws Exception {
if (!supportsSplitScreenMultiWindow()) {
// Skipping test: no multi-window support
@@ -235,13 +250,11 @@
}
@Test
- @FlakyTest
public void testFinishActivityWithMoveTaskToBackAfterPause() throws Exception {
performFinishActivityWithMoveTaskToBack(FINISH_POINT_ON_PAUSE);
}
@Test
- @FlakyTest
public void testFinishActivityWithMoveTaskToBackAfterStop() throws Exception {
performFinishActivityWithMoveTaskToBack(FINISH_POINT_ON_STOP);
}
@@ -428,7 +441,6 @@
}
@Test
- @FlakyTest
public void testTurnScreenOnAttrRemove() throws Exception {
try (final LockScreenSession lockScreenSession = new LockScreenSession()) {
lockScreenSession.sleepDevice();
@@ -499,7 +511,7 @@
waitAndAssertTopResumedActivity(TEST_ACTIVITY, DEFAULT_DISPLAY,
"Activity launched on default display must be focused");
- // Press home button
+ // Start home activity directly
launchHomeActivity();
mAmWmState.assertHomeActivityVisible(true);
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/AlertWindowsAppOpsTests.java b/tests/framework/base/windowmanager/src/android/server/wm/AlertWindowsAppOpsTests.java
index 10c6fc8..324398b 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/AlertWindowsAppOpsTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/AlertWindowsAppOpsTests.java
@@ -91,10 +91,11 @@
final AlertWindowsAppOpsTestsActivity activity = mActivityRule.getActivity();
// Start watching for app op
- appOpsManager.startWatchingActive(new int[] {OP_SYSTEM_ALERT_WINDOW}, listener);
+ appOpsManager.startWatchingActive(new String[] { OPSTR_SYSTEM_ALERT_WINDOW },
+ getInstrumentation().getContext().getMainExecutor(), listener);
// Assert the app op is not started
- assertFalse(appOpsManager.isOperationActive(OP_SYSTEM_ALERT_WINDOW, uid, packageName));
+ assertFalse(appOpsManager.isOpActive(OPSTR_SYSTEM_ALERT_WINDOW, uid, packageName));
// Show a system alert window.
@@ -102,11 +103,11 @@
// The app op should start
verify(listener, timeout(APP_OP_CHANGE_TIMEOUT_MILLIS)
- .only()).onOpActiveChanged(eq(OP_SYSTEM_ALERT_WINDOW),
+ .only()).onOpActiveChanged(eq(OPSTR_SYSTEM_ALERT_WINDOW),
eq(uid), eq(packageName), eq(true));
// The app op should be reported as started
- assertTrue(appOpsManager.isOperationActive(OP_SYSTEM_ALERT_WINDOW,
+ assertTrue(appOpsManager.isOpActive(OPSTR_SYSTEM_ALERT_WINDOW,
uid, packageName));
@@ -118,11 +119,11 @@
// The app op should finish
verify(listener, timeout(APP_OP_CHANGE_TIMEOUT_MILLIS)
- .only()).onOpActiveChanged(eq(OP_SYSTEM_ALERT_WINDOW),
+ .only()).onOpActiveChanged(eq(OPSTR_SYSTEM_ALERT_WINDOW),
eq(uid), eq(packageName), eq(false));
// The app op should be reported as finished
- assertFalse(appOpsManager.isOperationActive(OP_SYSTEM_ALERT_WINDOW, uid, packageName));
+ assertFalse(appOpsManager.isOpActive(OPSTR_SYSTEM_ALERT_WINDOW, uid, packageName));
// Start with a clean slate
@@ -136,10 +137,10 @@
// No other callbacks expected
verify(listener, timeout(APP_OP_CHANGE_TIMEOUT_MILLIS).times(0))
- .onOpActiveChanged(eq(OP_SYSTEM_ALERT_WINDOW),
+ .onOpActiveChanged(eq(OPSTR_SYSTEM_ALERT_WINDOW),
anyInt(), anyString(), anyBoolean());
// The app op should be reported as started
- assertTrue(appOpsManager.isOperationActive(OP_SYSTEM_ALERT_WINDOW, uid, packageName));
+ assertTrue(appOpsManager.isOpActive(OPSTR_SYSTEM_ALERT_WINDOW, uid, packageName));
}
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/AmProfileTests.java b/tests/framework/base/windowmanager/src/android/server/wm/AmProfileTests.java
index d08c9d2..24d2daa 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/AmProfileTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/AmProfileTests.java
@@ -107,7 +107,7 @@
executeShellCommand(
getStartCmd(PROFILEABLE_APP_ACTIVITY, startActivityFirst, sampling, streaming));
// Go to home screen and then warm start the activity to generate some interesting trace.
- pressHomeButton();
+ launchHomeActivity();
launchActivity(PROFILEABLE_APP_ACTIVITY);
executeShellCommand(getStopProfileCmd(PROFILEABLE_APP_ACTIVITY));
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/AmStartOptionsTests.java b/tests/framework/base/windowmanager/src/android/server/wm/AmStartOptionsTests.java
index 839371f..fb39c49 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/AmStartOptionsTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/AmStartOptionsTests.java
@@ -20,7 +20,6 @@
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
import static android.server.wm.ComponentNameUtils.getActivityName;
-import static android.server.wm.UiDeviceUtils.pressHomeButton;
import static android.server.wm.app.Components.ENTRY_POINT_ALIAS_ACTIVITY;
import static android.server.wm.app.Components.LAUNCHING_ACTIVITY;
import static android.server.wm.app.Components.SINGLE_TASK_ACTIVITY;
@@ -34,8 +33,6 @@
import android.content.ComponentName;
import android.platform.test.annotations.Presubmit;
-import androidx.test.filters.FlakyTest;
-
import org.junit.Test;
/**
@@ -70,7 +67,6 @@
}
@Test
- @FlakyTest
public void testDashW_Indirect() throws Exception {
testDashW(ENTRY_POINT_ALIAS_ACTIVITY, SINGLE_TASK_ACTIVITY);
}
@@ -82,7 +78,7 @@
.setTargetActivity(TEST_ACTIVITY).execute();
// Return to home
- pressHomeButton();
+ launchHomeActivity();
// Start LaunchingActivity again and finish TestActivity
final int flags =
@@ -99,7 +95,7 @@
startActivityAndVerifyResult(entryActivity, actualActivity, true);
// Test warm start
- pressHomeButton();
+ launchHomeActivity();
startActivityAndVerifyResult(entryActivity, actualActivity, false);
// Test "hot" start (app already in front)
@@ -120,4 +116,4 @@
waitAndAssertTopResumedActivity(actualActivity, DEFAULT_DISPLAY,
"Activity must be launched");
}
-}
\ No newline at end of file
+}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/AnimationBackgroundTests.java b/tests/framework/base/windowmanager/src/android/server/wm/AnimationBackgroundTests.java
deleted file mode 100644
index 2126f83..0000000
--- a/tests/framework/base/windowmanager/src/android/server/wm/AnimationBackgroundTests.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.server.wm;
-
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.server.wm.app.Components.ANIMATION_TEST_ACTIVITY;
-import static android.server.wm.app.Components.LAUNCHING_ACTIVITY;
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeFalse;
-
-import android.content.ComponentName;
-import android.platform.test.annotations.Presubmit;
-import android.server.wm.WindowManagerState.Display;
-
-import org.junit.Test;
-
-/**
- * Build/Install/Run:
- * atest CtsWindowManagerDeviceTestCases:AnimationBackgroundTests
- */
-@Presubmit
-public class AnimationBackgroundTests extends ActivityManagerTestBase {
-
- @Test
- public void testAnimationBackground_duringAnimation() throws Exception {
- launchActivityOnDisplay(LAUNCHING_ACTIVITY, DEFAULT_DISPLAY);
- getLaunchActivityBuilder()
- .setTargetActivity(ANIMATION_TEST_ACTIVITY)
- .setWaitForLaunched(false)
- .execute();
-
- // Make sure we're testing an activity that runs on fullscreen display. This animation API
- // doesn't make much sense in freeform displays.
- assumeActivityNotInFreeformDisplay(ANIMATION_TEST_ACTIVITY);
-
- // Make sure we are in the middle of the animation.
- mAmWmState.waitForWithWmState(state -> state
- .getStandardStackByWindowingMode(WINDOWING_MODE_FULLSCREEN)
- .isWindowAnimationBackgroundSurfaceShowing(),
- "***Waiting for animation background showing");
-
- assertTrue("window animation background needs to be showing", mAmWmState.getWmState()
- .getStandardStackByWindowingMode(WINDOWING_MODE_FULLSCREEN)
- .isWindowAnimationBackgroundSurfaceShowing());
- }
-
- @Test
- public void testAnimationBackground_gone() throws Exception {
- launchActivityOnDisplay(LAUNCHING_ACTIVITY, DEFAULT_DISPLAY);
- getLaunchActivityBuilder().setTargetActivity(ANIMATION_TEST_ACTIVITY).execute();
- mAmWmState.computeState(ANIMATION_TEST_ACTIVITY);
- mAmWmState.waitForAppTransitionIdleOnDisplay(DEFAULT_DISPLAY);
-
- // Make sure we're testing an activity that runs on fullscreen display. This animation API
- // doesn't make much sense in freeform displays.
- assumeActivityNotInFreeformDisplay(ANIMATION_TEST_ACTIVITY);
-
- assertFalse("window animation background needs to be gone", mAmWmState.getWmState()
- .getStandardStackByWindowingMode(WINDOWING_MODE_FULLSCREEN)
- .isWindowAnimationBackgroundSurfaceShowing());
- }
-
- private void assumeActivityNotInFreeformDisplay(ComponentName activity) throws Exception {
- mAmWmState.waitForValidState(activity);
- final int displayId = mAmWmState.getAmState().getDisplayByActivity(activity);
- final Display display = mAmWmState.getWmState().getDisplay(displayId);
- assumeFalse("Animation test activity is in freeform display. It may not run "
- + "cross-task animations.", display.getWindowingMode() == WINDOWING_MODE_FREEFORM);
- }
-}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java b/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java
index d6489a8..9fffd6b 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java
@@ -58,8 +58,6 @@
import android.platform.test.annotations.Presubmit;
import android.server.wm.CommandSession.SizeInfo;
-import androidx.test.filters.FlakyTest;
-
import org.junit.Ignore;
import org.junit.Test;
@@ -104,7 +102,6 @@
* from docked state to fullscreen (reverse).
*/
@Test
- @FlakyTest(bugId = 71792393)
public void testConfigurationUpdatesWhenResizedFromDockedStack() {
assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
@@ -259,7 +256,7 @@
// Resize docked stack to fullscreen size. This will trigger activity relaunch with
// non-empty override configuration corresponding to fullscreen size.
separateTestJournal();
- resizeStack(stack.mStackId, displayRect.left, displayRect.top, displayRect.width(),
+ resizeDockedStack(displayRect.left, displayRect.top, displayRect.width(),
displayRect.height());
// Move activity back to fullscreen stack.
@@ -276,7 +273,6 @@
* relaunched twice and it should have same config as initial one.
*/
@Test
- @FlakyTest
public void testSameConfigurationSplitFullSplitRelaunch() {
moveActivitySplitFullSplit(TEST_ACTIVITY);
}
@@ -285,7 +281,6 @@
* Same as {@link #testSameConfigurationSplitFullSplitRelaunch} but without relaunch.
*/
@Test
- @FlakyTest
public void testSameConfigurationSplitFullSplitNoRelaunch() {
moveActivitySplitFullSplit(RESIZEABLE_ACTIVITY);
}
@@ -295,7 +290,6 @@
* screen.
*/
@Test
- @FlakyTest(bugId = 110276714)
public void testDialogWhenLargeSplitSmall() {
assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
@@ -308,7 +302,7 @@
final int smallWidthPx = dpToPx(SMALL_WIDTH_DP, density);
final int smallHeightPx = dpToPx(SMALL_HEIGHT_DP, density);
- resizeStack(stack.mStackId, 0, 0, smallWidthPx, smallHeightPx);
+ resizeDockedStack(0, 0, smallWidthPx, smallHeightPx);
mAmWmState.waitForValidState(
new WaitForValidActivityState.Builder(DIALOG_WHEN_LARGE_ACTIVITY)
.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
@@ -320,7 +314,6 @@
* Test that device handles consequent requested orientations and displays the activities.
*/
@Test
- @FlakyTest(bugId = 71875755)
public void testFullscreenAppOrientationRequests() {
assumeTrue("Skipping test: no rotation support", supportsRotation());
@@ -374,7 +367,6 @@
* change to an invisible activity.
*/
@Test
- @FlakyTest
public void testAppOrientationRequestConfigChanges() {
assumeTrue("Skipping test: no rotation support", supportsRotation());
@@ -571,7 +563,6 @@
* Also verify that occluded activity will not get config changes.
*/
@Test
- @FlakyTest
public void testAppOrientationWhenRotating() throws Exception {
assumeTrue("Skipping test: no rotation support", supportsRotation());
@@ -660,7 +651,6 @@
* Test that device handles moving between two tasks with different orientations.
*/
@Test
- @FlakyTest(bugId = 71792393)
public void testTaskMoveToBackOrientation() {
assumeTrue("Skipping test: no rotation support", supportsRotation());
@@ -690,7 +680,6 @@
/**
* Test that device doesn't change device orientation by app request while in multi-window.
*/
- @FlakyTest(bugId = 71918731)
@Test
public void testSplitscreenPortraitAppOrientationRequests() throws Exception {
assumeTrue("Skipping test: no rotation support", supportsRotation());
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/AssistantStackTests.java b/tests/framework/base/windowmanager/src/android/server/wm/AssistantStackTests.java
index 9662980..d850ed7 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/AssistantStackTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/AssistantStackTests.java
@@ -98,7 +98,6 @@
}
}
- @FlakyTest(bugId = 69573940)
@Test
public void testAssistantStackZOrder() throws Exception {
assumeTrue(assistantRunsOnPrimaryDisplay());
@@ -189,7 +188,6 @@
}
@Test
- @FlakyTest(bugId = 71875631)
public void testAssistantStackFinishToPreviousApp() throws Exception {
// Launch an assistant activity on top of an existing fullscreen activity, and ensure that
// the fullscreen activity is still visible and on top after the assistant activity finishes
@@ -200,7 +198,7 @@
launchActivityNoWait(LAUNCH_ASSISTANT_ACTIVITY_INTO_STACK,
EXTRA_ASSISTANT_FINISH_SELF, "true");
mAmWmState.waitFor((amState, wmState) -> !amState.containsActivity(ASSISTANT_ACTIVITY),
- "Waiting for " + getActivityName(ASSISTANT_ACTIVITY) + " finished");
+ getActivityName(ASSISTANT_ACTIVITY) + " finished");
}
waitForValidStateWithActivityTypeAndWindowingMode(
TEST_ACTIVITY, ACTIVITY_TYPE_STANDARD, WINDOWING_MODE_FULLSCREEN);
@@ -214,7 +212,6 @@
}
@Test
- @FlakyTest(bugId = 71875631)
public void testDisallowEnterPiPFromAssistantStack() throws Exception {
try (final AssistantSession assistantSession = new AssistantSession()) {
assistantSession.setVoiceInteractionService(ASSISTANT_VOICE_INTERACTION_SERVICE);
@@ -300,7 +297,6 @@
}
}
- @FlakyTest(bugId = 69229402)
@Test
public void testLaunchIntoSameTask() throws Exception {
try (final AssistantSession assistantSession = new AssistantSession()) {
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/ConfigChangeTests.java b/tests/framework/base/windowmanager/src/android/server/wm/ConfigChangeTests.java
index bc84c83..8fc36e8 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/ConfigChangeTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/ConfigChangeTests.java
@@ -46,7 +46,6 @@
import android.server.wm.settings.SettingsSession;
import androidx.annotation.IntDef;
-import androidx.test.filters.FlakyTest;
import com.android.compatibility.common.util.SystemUtil;
@@ -119,7 +118,6 @@
}
@Test
- @FlakyTest(bugId = 110533226, detail = "Promote to presubmit once confirm it's not flaky")
public void testRotation180RelaunchWithCutout() throws Exception {
assumeTrue("Skipping test: no rotation support", supportsRotation());
assumeTrue("Skipping test: no display cutout", hasDisplayCutout());
@@ -128,7 +126,6 @@
}
@Test
- @FlakyTest(bugId = 110533226, detail = "Promote to presubmit once confirm it's not flaky")
public void testRotation180NoRelaunchWithCutout() throws Exception {
assumeTrue("Skipping test: no rotation support", supportsRotation());
assumeTrue("Skipping test: no display cutout", hasDisplayCutout());
@@ -141,7 +138,6 @@
* reverse-landscape rotations should result in same screen space available for apps.
*/
@Test
- @FlakyTest(bugId = 110533226, detail = "Promote to presubmit once confirm it's not flaky")
public void testConfigChangeWhenRotatingWithCutout() throws Exception {
assumeTrue("Skipping test: no rotation support", supportsRotation());
assumeTrue("Skipping test: no display cutout", hasDisplayCutout());
@@ -320,7 +316,7 @@
logE("Error waiting for valid state: " + e.getMessage());
return false;
}
- }, "Waiting asset sequence number to be updated and for activity to be resumed.");
+ }, "asset sequence number to be updated and for activity to be resumed.");
// Check if activity is relaunched and asset seq is updated.
assertRelaunchOrConfigChanged(TEST_ACTIVITY, 1 /* numRelaunch */,
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/DragDropActivity.java b/tests/framework/base/windowmanager/src/android/server/wm/DragDropActivity.java
deleted file mode 100644
index be741f3..0000000
--- a/tests/framework/base/windowmanager/src/android/server/wm/DragDropActivity.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.server.wm;
-
-import android.app.Activity;
-import android.os.Bundle;
-
-import android.server.wm.cts.R;
-
-public class DragDropActivity extends Activity {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.drag_drop_layout);
- }
-}
\ No newline at end of file
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/DragDropTest.java b/tests/framework/base/windowmanager/src/android/server/wm/DragDropTest.java
index 2983eb8..bd054b9 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/DragDropTest.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/DragDropTest.java
@@ -21,12 +21,14 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeFalse;
import android.app.Instrumentation;
import android.app.UiAutomation;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.pm.PackageManager;
+import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
@@ -38,12 +40,10 @@
import android.view.ViewGroup;
import androidx.test.InstrumentationRegistry;
-import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -54,16 +54,12 @@
import java.util.stream.IntStream;
@RunWith(AndroidJUnit4.class)
-public class DragDropTest {
+public class DragDropTest extends WindowManagerTestBase {
static final String TAG = "DragDropTest";
final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
final UiAutomation mAutomation = mInstrumentation.getUiAutomation();
- @Rule
- public ActivityTestRule<DragDropActivity> mActivityRule =
- new ActivityTestRule<>(DragDropActivity.class);
-
private DragDropActivity mActivity;
private CountDownLatch mStartReceived;
@@ -307,22 +303,19 @@
});
}
- private boolean init() {
- // Only run for non-watch devices
- if (mActivity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
- return false;
- }
- return true;
+ /** Checks if device type is watch. */
+ private boolean isWatchDevice() {
+ return mInstrumentation.getTargetContext().getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_WATCH);
}
@Before
- public void setUp() {
- mActivity = mActivityRule.getActivity();
+ public void setUp() throws InterruptedException {
+ assumeFalse(isWatchDevice());
+ mActivity = startActivity(DragDropActivity.class);
+
mStartReceived = new CountDownLatch(1);
mEndReceived = new CountDownLatch(1);
-
- // Wait for idle
- mInstrumentation.waitForIdleSync();
}
@After
@@ -387,10 +380,6 @@
*/
@Test
public void testNoExtraEvents() throws Exception {
- if (!init()) {
- return;
- }
-
runOnMain(() -> {
// Tell all views in layout to return false to all events, and log them.
setRejectingHandlersOnTree(mActivity.findViewById(R.id.drag_drop_activity_main));
@@ -435,10 +424,6 @@
*/
@Test
public void testBlackHole() throws Exception {
- if (!init()) {
- return;
- }
-
runOnMain(() -> {
// Accepting child.
mActivity.findViewById(R.id.inner).setOnDragListener((v, ev) -> {
@@ -483,10 +468,6 @@
*/
@Test
public void testEnterExit() throws Exception {
- if (!init()) {
- return;
- }
-
runOnMain(() -> {
// The setup is same as for testBlackHole.
@@ -546,10 +527,6 @@
*/
@Test
public void testOverNowhere() throws Exception {
- if (!init()) {
- return;
- }
-
runOnMain(() -> {
// Accepting child.
mActivity.findViewById(R.id.inner).setOnDragListener((v, ev) -> {
@@ -589,10 +566,6 @@
*/
@Test
public void testAcceptingGroupInTheMiddle() throws Exception {
- if (!init()) {
- return;
- }
-
runOnMain(() -> {
// Set accepting handlers to the inner view and its 2 ancestors.
mActivity.findViewById(R.id.inner).setOnDragListener((v, ev) -> {
@@ -645,10 +618,6 @@
*/
@Test
public void testDrawableState() throws Exception {
- if (!init()) {
- return;
- }
-
runOnMain(() -> {
// Set accepting handler for the inner view.
mActivity.findViewById(R.id.inner).setOnDragListener((v, ev) -> {
@@ -689,4 +658,46 @@
assertFalse(drawableStateContains(R.id.inner, android.R.attr.state_drag_hovered));
});
}
-}
\ No newline at end of file
+
+ /**
+ * Tests if window is removing, it should not perform drag.
+ */
+ @Test
+ public void testNoDragIfWindowCantReceiveInput() throws InterruptedException {
+ injectMouse5(R.id.draggable, MotionEvent.ACTION_DOWN);
+
+ runOnMain(() -> {
+ // finish activity and start drag drop.
+ View v = mActivity.findViewById(R.id.draggable);
+ mActivity.finish();
+ assertFalse("Shouldn't start drag",
+ v.startDragAndDrop(sClipData, new View.DragShadowBuilder(v), sLocalState, 0));
+ });
+
+ injectMouse5(R.id.draggable, MotionEvent.ACTION_UP);
+ }
+
+ /**
+ * Tests if there is no touch down, it should not perform drag.
+ */
+ @Test
+ public void testNoDragIfNoTouchDown() throws InterruptedException {
+ // perform a click.
+ injectMouse5(R.id.draggable, MotionEvent.ACTION_DOWN);
+ injectMouse5(R.id.draggable, MotionEvent.ACTION_UP);
+
+ runOnMain(() -> {
+ View v = mActivity.findViewById(R.id.draggable);
+ assertFalse("Shouldn't start drag",
+ v.startDragAndDrop(sClipData, new View.DragShadowBuilder(v), sLocalState, 0));
+ });
+ }
+
+ public static class DragDropActivity extends FocusableActivity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.drag_drop_layout);
+ }
+ }
+}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/KeyguardTestBase.java b/tests/framework/base/windowmanager/src/android/server/wm/KeyguardTestBase.java
index d5f7709..5ad4c01 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/KeyguardTestBase.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/KeyguardTestBase.java
@@ -16,16 +16,12 @@
package android.server.wm;
-import static android.server.wm.StateLogger.logAlways;
import static android.server.wm.app.Components.KeyguardDismissLoggerCallback.ENTRY_ON_DISMISS_CANCELLED;
import static android.server.wm.app.Components.KeyguardDismissLoggerCallback.ENTRY_ON_DISMISS_ERROR;
import static android.server.wm.app.Components.KeyguardDismissLoggerCallback.ENTRY_ON_DISMISS_SUCCEEDED;
-import static org.junit.Assert.fail;
-
import android.app.KeyguardManager;
import android.content.ComponentName;
-import android.os.SystemClock;
import android.server.wm.TestJournalProvider.TestJournalContainer;
class KeyguardTestBase extends ActivityManagerTestBase {
@@ -51,14 +47,7 @@
}
private static void assertDismissCallback(ComponentName testingComponentName, String entry) {
- for (int retry = 1; retry <= 5; retry++) {
- if (TestJournalContainer.get(testingComponentName).extras
- .getBoolean(entry)) {
- return;
- }
- logAlways("Waiting for " + entry + "... retry=" + retry);
- SystemClock.sleep(500);
- }
- fail("Waiting for " + entry + " failed");
+ waitForOrFail(entry + " of " + testingComponentName,
+ () -> TestJournalContainer.get(testingComponentName).extras.getBoolean(entry));
}
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/KeyguardTests.java b/tests/framework/base/windowmanager/src/android/server/wm/KeyguardTests.java
index c608991..c1f97a3 100755
--- a/tests/framework/base/windowmanager/src/android/server/wm/KeyguardTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/KeyguardTests.java
@@ -23,7 +23,6 @@
import static android.server.wm.ComponentNameUtils.getActivityName;
import static android.server.wm.ComponentNameUtils.getWindowName;
import static android.server.wm.UiDeviceUtils.pressBackButton;
-import static android.server.wm.UiDeviceUtils.pressHomeButton;
import static android.server.wm.app.Components.BROADCAST_RECEIVER_ACTIVITY;
import static android.server.wm.app.Components.DISMISS_KEYGUARD_ACTIVITY;
import static android.server.wm.app.Components.DISMISS_KEYGUARD_METHOD_ACTIVITY;
@@ -533,7 +532,7 @@
public void testUnoccludeRotationChange() throws Exception {
// Go home now to make sure Home is behind Keyguard.
- pressHomeButton();
+ launchHomeActivity();
try (final LockScreenSession lockScreenSession = new LockScreenSession();
final RotationSession rotationSession = new RotationSession()) {
lockScreenSession.gotoKeyguard();
@@ -553,7 +552,7 @@
// The activity may not be destroyed immediately.
mAmWmState.waitForWithWmState(
wmState -> !wmState.containsWindow(getWindowName(SHOW_WHEN_LOCKED_ACTIVITY)),
- "Waiting for " + getActivityName(SHOW_WHEN_LOCKED_ACTIVITY) + " to be removed");
+ getActivityName(SHOW_WHEN_LOCKED_ACTIVITY) + " to be removed");
// The {@link SHOW_WHEN_LOCKED_ACTIVITY} has gone because of {@link pressBackButton()}.
mAmWmState.assertNotExist(SHOW_WHEN_LOCKED_ACTIVITY);
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/LayoutTests.java b/tests/framework/base/windowmanager/src/android/server/wm/LayoutTests.java
index 5513de7..b9d84d7 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/LayoutTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/LayoutTests.java
@@ -54,7 +54,6 @@
* Build/Install/Run:
* atest CtsWindowManagerDeviceTestCases:LayoutTests
*/
-@FlakyTest(detail = "Can be promoted to pre-submit once confirmed stable.")
@AppModeFull(reason = "Cannot write global settings as an instant app.")
@Presubmit
public class LayoutTests extends WindowManagerTestBase {
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/LocationOnScreenTests.java b/tests/framework/base/windowmanager/src/android/server/wm/LocationOnScreenTests.java
index ad3995c..9c9491c 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/LocationOnScreenTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/LocationOnScreenTests.java
@@ -64,7 +64,6 @@
import java.util.function.Supplier;
-@FlakyTest(detail = "until proven non-flaky")
@SmallTest
@Presubmit
public class LocationOnScreenTests {
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/ManifestLayoutTests.java b/tests/framework/base/windowmanager/src/android/server/wm/ManifestLayoutTests.java
index 4086978..0632c4a 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/ManifestLayoutTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/ManifestLayoutTests.java
@@ -141,22 +141,22 @@
getDisplayAndWindowState(activityName, true);
final Rect containingRect = mWindowState.getContainingFrame();
- final Rect appRect = mDisplay.getAppRect();
+ final Rect stableBounds = mDisplay.getStableBounds();
final int expectedWidthPx, expectedHeightPx;
// Evaluate the expected window size in px. If we're using fraction dimensions,
// calculate the size based on the app rect size. Otherwise, convert the expected
// size in dp to px.
if (fraction) {
- expectedWidthPx = (int) (appRect.width() * DEFAULT_WIDTH_FRACTION);
- expectedHeightPx = (int) (appRect.height() * DEFAULT_HEIGHT_FRACTION);
+ expectedWidthPx = (int) (stableBounds.width() * DEFAULT_WIDTH_FRACTION);
+ expectedHeightPx = (int) (stableBounds.height() * DEFAULT_HEIGHT_FRACTION);
} else {
final int densityDpi = mDisplay.getDpi();
expectedWidthPx = dpToPx(DEFAULT_WIDTH_DP, densityDpi);
expectedHeightPx = dpToPx(DEFAULT_HEIGHT_DP, densityDpi);
}
- verifyFrameSizeAndPosition(
- vGravity, hGravity, expectedWidthPx, expectedHeightPx, containingRect, appRect);
+ verifyFrameSizeAndPosition(vGravity, hGravity, expectedWidthPx, expectedHeightPx,
+ containingRect, stableBounds);
}
private void getDisplayAndWindowState(ComponentName activityName, boolean checkFocus)
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayActivityLaunchTests.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayActivityLaunchTests.java
index 67f1db8..e30da26 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayActivityLaunchTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayActivityLaunchTests.java
@@ -19,13 +19,17 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
import static android.server.wm.ActivityLauncher.KEY_LAUNCH_ACTIVITY;
import static android.server.wm.ActivityLauncher.KEY_NEW_TASK;
import static android.server.wm.ActivityManagerState.STATE_RESUMED;
import static android.server.wm.ActivityManagerState.STATE_STOPPED;
import static android.server.wm.ComponentNameUtils.getActivityName;
+import static android.server.wm.UiDeviceUtils.pressHomeButton;
import static android.server.wm.app.Components.ALT_LAUNCHING_ACTIVITY;
import static android.server.wm.app.Components.BROADCAST_RECEIVER_ACTIVITY;
import static android.server.wm.app.Components.LAUNCHING_ACTIVITY;
@@ -35,6 +39,7 @@
import static android.server.wm.app.Components.SINGLE_TASK_INSTANCE_DISPLAY_ACTIVITY2;
import static android.server.wm.app.Components.SINGLE_TASK_INSTANCE_DISPLAY_ACTIVITY3;
import static android.server.wm.app.Components.TEST_ACTIVITY;
+import static android.server.wm.app.Components.TOP_ACTIVITY;
import static android.server.wm.app.Components.VIRTUAL_DISPLAY_ACTIVITY;
import static android.server.wm.second.Components.SECOND_ACTIVITY;
import static android.server.wm.second.Components.SECOND_LAUNCH_BROADCAST_ACTION;
@@ -42,6 +47,8 @@
import static android.server.wm.third.Components.THIRD_ACTIVITY;
import static android.view.Display.DEFAULT_DISPLAY;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
@@ -49,8 +56,11 @@
import static org.junit.Assume.assumeTrue;
import android.app.ActivityOptions;
+import android.app.PendingIntent;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
+import android.hardware.display.DisplayManager;
import android.os.Bundle;
import android.platform.test.annotations.Presubmit;
import android.server.wm.ActivityManagerState.ActivityDisplay;
@@ -257,12 +267,11 @@
// Try to move the non-resizeable activity to the top of stack on secondary display.
moveActivityToStack(NON_RESIZEABLE_ACTIVITY, externalFrontStackId);
// Wait for a while to check that it will move.
- mAmWmState.waitForWithAmState(state ->
- newDisplay.mId == state.getDisplayByActivity(NON_RESIZEABLE_ACTIVITY),
- "Waiting to see if activity is moved");
- assertEquals("Non-resizeable activity should be moved",
- newDisplay.mId,
- mAmWmState.getAmState().getDisplayByActivity(NON_RESIZEABLE_ACTIVITY));
+ assertTrue("Non-resizeable activity should be moved",
+ mAmWmState.waitForWithAmState(
+ state -> newDisplay.mId == state
+ .getDisplayByActivity(NON_RESIZEABLE_ACTIVITY),
+ "seeing if activity won't be moved"));
waitAndAssertTopResumedActivity(NON_RESIZEABLE_ACTIVITY, newDisplay.mId,
"The moved non-resizeable activity must be focused");
@@ -492,6 +501,7 @@
@Test
public void testLaunchPendingActivityOnSecondaryDisplay() throws Exception {
try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ pressHomeButton();
// Create new simulated display.
final ActivityDisplay newDisplay = virtualDisplaySession.setSimulateDisplay(true)
.createDisplay();
@@ -504,8 +514,7 @@
.putExtra(KEY_NEW_TASK, true);
mContext.startActivity(intent, bundle);
- // ActivityManagerTestBase.setup would press home key event, which would cause
- // PhoneWindowManager.startDockOrHome to call AMS.stopAppSwitches.
+ // If home key was pressed, stopAppSwitches will be called.
// Since this test case is not start activity from shell, it won't grant
// STOP_APP_SWITCHES and this activity should be put into pending activity queue
// and this activity should been launched after
@@ -848,4 +857,88 @@
SINGLE_TASK_INSTANCE_DISPLAY_ACTIVITY3));
}
}
+
+ @Test
+ public void testLaunchPendingIntentActivity() throws Exception {
+ final DisplayManager displayManager =
+ getInstrumentation().getContext().getSystemService(DisplayManager.class);
+
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ final ActivityDisplay activityDisplay =
+ virtualDisplaySession.setSimulateDisplay(true).createDisplay();
+
+ // Activity should be launched on primary display by default.
+ getPendingIntentActivity(TEST_ACTIVITY).send();
+ waitAndAssertTopResumedActivity(TEST_ACTIVITY, DEFAULT_DISPLAY,
+ "Activity launched on primary display and on top");
+
+ // Activity should be launched on target display according to the caller context.
+ final Context displayContext =
+ mContext.createDisplayContext(displayManager.getDisplay(activityDisplay.mId));
+ getPendingIntentActivity(TOP_ACTIVITY).send(displayContext, 1 /* code */,
+ null /* intent */);
+ waitAndAssertTopResumedActivity(TOP_ACTIVITY, activityDisplay.mId,
+ "Activity launched on secondary display and on top");
+
+ // Activity should be brought to front on the same display if it already existed.
+ getPendingIntentActivity(TEST_ACTIVITY).send(displayContext, 1 /* code */,
+ null /* intent */);
+ waitAndAssertTopResumedActivity(TEST_ACTIVITY, DEFAULT_DISPLAY,
+ "Activity launched on primary display and on top");
+
+ // Activity should be moved to target display.
+ ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchDisplayId(activityDisplay.mId);
+ getPendingIntentActivity(TEST_ACTIVITY).send(getInstrumentation().getContext(),
+ 1 /* code */, null /* intent */, null /* onFinished */, null /* handler */,
+ null /* requiredPermission */, options.toBundle());
+ waitAndAssertTopResumedActivity(TEST_ACTIVITY, activityDisplay.mId,
+ "Activity launched on secondary display and on top");
+ }
+ }
+
+ @Test
+ public void testLaunchActivityClearTask() throws Exception {
+ assertBroughtExistingTaskToAnotherDisplay(FLAG_ACTIVITY_CLEAR_TASK, LAUNCHING_ACTIVITY);
+ }
+
+ @Test
+ public void testLaunchActivityClearTop() throws Exception {
+ assertBroughtExistingTaskToAnotherDisplay(FLAG_ACTIVITY_CLEAR_TOP, LAUNCHING_ACTIVITY);
+ }
+
+ @Test
+ public void testLaunchActivitySingleTop() throws Exception {
+ assertBroughtExistingTaskToAnotherDisplay(FLAG_ACTIVITY_SINGLE_TOP, TEST_ACTIVITY);
+ }
+
+ private void assertBroughtExistingTaskToAnotherDisplay(int flags, ComponentName topActivity)
+ throws Exception {
+ // Start TEST_ACTIVITY on top of LAUNCHING_ACTIVITY within the same task
+ getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY).execute();
+
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ final ActivityDisplay newDisplay =
+ virtualDisplaySession.setSimulateDisplay(true).createDisplay();
+
+ // Start LAUNCHING_ACTIVITY on secondary display with target flags, verify the task
+ // be reparented to secondary display
+ getLaunchActivityBuilder()
+ .setUseInstrumentation()
+ .setTargetActivity(LAUNCHING_ACTIVITY)
+ .setIntentFlags(flags)
+ .allowMultipleInstances(false)
+ .setDisplayId(newDisplay.mId).execute();
+ waitAndAssertTopResumedActivity(topActivity, newDisplay.mId,
+ "Activity launched on secondary display and on top");
+ }
+ }
+
+ private PendingIntent getPendingIntentActivity(ComponentName activity) {
+ final Intent intent = new Intent();
+ intent.setClassName(activity.getPackageName(), activity.getClassName());
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ return PendingIntent.getActivity(mContext, 1 /* requestCode */, intent,
+ PendingIntent.FLAG_CANCEL_CURRENT);
+ }
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayClientTests.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayClientTests.java
index df087db..4d4d120 100755
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayClientTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayClientTests.java
@@ -20,6 +20,7 @@
import static android.server.wm.CommandSession.ActivityCallback.ON_CONFIGURATION_CHANGED;
import static android.server.wm.CommandSession.ActivityCallback.ON_RESUME;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
@@ -28,6 +29,7 @@
import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
import android.app.Activity;
@@ -36,15 +38,17 @@
import android.content.Context;
import android.content.Intent;
import android.hardware.display.DisplayManager;
-import android.platform.test.annotations.Presubmit;
import android.os.Bundle;
+import android.platform.test.annotations.Presubmit;
import android.view.Display;
+import android.view.View;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.LinearLayout;
import androidx.test.filters.FlakyTest;
+import androidx.test.filters.MediumTest;
import androidx.test.rule.ActivityTestRule;
import com.android.cts.mockime.ImeEventStream;
@@ -60,6 +64,7 @@
* atest CtsActivityManagerDeviceTestCases:MultiDisplayClientTests
*/
@Presubmit
+@MediumTest
public class MultiDisplayClientTests extends MultiDisplayTestBase {
private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(10); // 10 seconds
@@ -73,13 +78,11 @@
}
@Test
- @FlakyTest(bugId = 130260102, detail = "Promote to presubmit once proved stable")
public void testDisplayIdUpdateOnMove_RelaunchActivity() throws Exception {
testDisplayIdUpdateOnMove(ClientTestActivity.class, false /* handlesConfigChange */);
}
@Test
- @FlakyTest(bugId = 130260102, detail = "Promote to presubmit once proved stable")
public void testDisplayIdUpdateOnMove_NoRelaunchActivity() throws Exception {
testDisplayIdUpdateOnMove(NoRelaunchActivity.class, true /* handlesConfigChange */);
}
@@ -103,12 +106,13 @@
// Move the activity to the new secondary display.
separateTestJournal();
final ActivityOptions launchOptions = ActivityOptions.makeBasic();
- launchOptions.setLaunchDisplayId(newDisplay.mId);
+ final int displayId = newDisplay.mId;
+ launchOptions.setLaunchDisplayId(displayId);
final Intent newDisplayIntent = new Intent(mContext, activityClass);
newDisplayIntent.setFlags(FLAG_ACTIVITY_NEW_TASK);
getInstrumentation().getTargetContext().startActivity(newDisplayIntent,
launchOptions.toBundle());
- waitAndAssertTopResumedActivity(activityName, newDisplay.mId,
+ waitAndAssertTopResumedActivity(activityName, displayId,
"Activity moved to secondary display must be focused");
if (handlesConfigChange) {
@@ -119,48 +123,30 @@
waitAndAssertResume(activityName);
activity = activityTestRule.getActivity();
}
- final String message = "Display id must be updated";
- assertEquals(message, newDisplay.mId, activity.getDisplayId());
- assertEquals(message, newDisplay.mId, activity.getDisplay().getDisplayId());
+
+ final String suffix = " must be updated.";
+ assertEquals("Activity#getDisplayId()" + suffix, displayId, activity.getDisplayId());
+ assertEquals("Activity#getDisplay" + suffix,
+ displayId, activity.getDisplay().getDisplayId());
+
final WindowManager wm = activity.getWindowManager();
- assertEquals(message, newDisplay.mId, wm.getDefaultDisplay().getDisplayId());
+ assertEquals("WM#getDefaultDisplay()" + suffix,
+ displayId, wm.getDefaultDisplay().getDisplayId());
+
+ final View view = activity.getWindow().getDecorView();
+ assertEquals("View#getDisplay()" + suffix,
+ displayId, view.getDisplay().getDisplayId());
}
}
- private void waitAndAssertConfigurationChange(ComponentName activityName) {
- mAmWmState.waitForWithAmState((state) ->
- getCallbackCount(activityName, ON_CONFIGURATION_CHANGED) == 1,
- "waitForConfigurationChange");
- assertEquals("Must receive a single configuration change", 1,
- getCallbackCount(activityName, ON_CONFIGURATION_CHANGED));
- }
-
- private void waitAndAssertResume(ComponentName activityName) {
- mAmWmState.waitForWithAmState((state) ->
- getCallbackCount(activityName, ON_RESUME) == 1, "waitForResume");
- assertEquals("Must be resumed once", 1, getCallbackCount(activityName, ON_RESUME));
- }
-
- private int getCallbackCount(ComponentName activityName,
- CommandSession.ActivityCallback callback) {
- final ActivityLifecycleCounts lifecycles = new ActivityLifecycleCounts(activityName);
- return lifecycles.getCount(callback);
- }
-
@Test
- @FlakyTest(bugId = 130379901, detail = "Promote to presubmit once proved stable")
public void testDisplayIdUpdateWhenImeMove_RelaunchActivity() throws Exception {
- try (final TestActivitySession<ClientTestActivity> session = new TestActivitySession<>()) {
- testDisplayIdUpdateWhenImeMove(ClientTestActivity.class);
- }
+ testDisplayIdUpdateWhenImeMove(ClientTestActivity.class);
}
@Test
- @FlakyTest(bugId = 130379901, detail = "Promote to presubmit once proved stable")
public void testDisplayIdUpdateWhenImeMove_NoRelaunchActivity() throws Exception {
- try (final TestActivitySession<NoRelaunchActivity> session = new TestActivitySession<>()) {
- testDisplayIdUpdateWhenImeMove(NoRelaunchActivity.class);
- }
+ testDisplayIdUpdateWhenImeMove(NoRelaunchActivity.class);
}
private void testDisplayIdUpdateWhenImeMove(Class<? extends ImeTestActivity> activityClass)
@@ -196,13 +182,13 @@
expectEvent(stream, event -> "showSoftInput".equals(event.getEventName()), TIMEOUT);
mAmWmState.waitAndAssertImeWindowShownOnDisplay(targetDisplayId);
- final int displayId = expectCommand(stream, imeSession.callGetDisplayId(), TIMEOUT)
+ final int imeDisplayId = expectCommand(stream, imeSession.callGetDisplayId(), TIMEOUT)
.getReturnIntegerValue();
- assertEquals("Display ID must match", targetDisplayId, displayId);
+ assertEquals("IME#getDisplayId() must match when IME move.",
+ targetDisplayId, imeDisplayId);
}
@Test
- @FlakyTest(bugId = 130379901, detail = "Promote to presubmit once proved stable")
public void testInputMethodManagerDisplayId() throws Exception {
try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
// Create a simulated display.
@@ -215,10 +201,93 @@
final InputMethodManager imm =
newDisplayContext.getSystemService(InputMethodManager.class);
- assertEquals(newDisplay.mId, imm.getDisplayId());
+ assertEquals("IMM#getDisplayId() must match.", newDisplay.mId, imm.getDisplayId());
}
}
+ @Test
+ public void testViewGetDisplayOnPrimaryDisplay() throws Exception {
+ testViewGetDisplay(true /* isPrimary */);
+ }
+
+ @Test
+ public void testViewGetDisplayOnSecondaryDisplay() throws Exception {
+ testViewGetDisplay(false /* isPrimary */);
+ }
+
+ private void testViewGetDisplay(boolean isPrimary) throws Exception {
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession();
+ final TestActivitySession<ClientTestActivity> activitySession =
+ new TestActivitySession<>()) {
+
+ final ActivityManagerState.ActivityDisplay newDisplay = virtualDisplaySession
+ .setSimulateDisplay(true).createDisplay();
+ final int displayId = (isPrimary) ? DEFAULT_DISPLAY : newDisplay.mId;
+
+ separateTestJournal();
+ activitySession.launchTestActivityOnDisplaySync(
+ ClientTestActivity.class, displayId);
+
+ final Activity activity = activitySession.getActivity();
+ final ComponentName activityName = activity.getComponentName();
+
+ waitAndAssertTopResumedActivity(activityName, displayId,
+ "Activity launched on display:" + displayId + " must be focused");
+
+ // Test View#getdisplay() from activity
+ final View view = activity.getWindow().getDecorView();
+ assertEquals("View#getDisplay() must match.",
+ displayId, view.getDisplay().getDisplayId());
+
+ final int[] resultDisplayId = new int[] { INVALID_DISPLAY };
+ activitySession.runOnMainAndAssertWithTimeout(
+ () -> {
+ // Test View#getdisplay() from WM#addView()
+ final WindowManager wm = activity.getWindowManager();
+ final View addedView = new View(activity);
+ wm.addView(addedView, new WindowManager.LayoutParams());
+
+ // Get display ID from callback in case the added view has not be attached.
+ addedView.addOnAttachStateChangeListener(
+ new View.OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(View view) {
+ resultDisplayId[0] = view.getDisplay().getDisplayId();
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View view) {}
+ });
+
+ return displayId == resultDisplayId[0];
+ }, TIMEOUT, "Display from added view must match. "
+ + "Should be display:" + displayId
+ + ", but was display:" + resultDisplayId[0]
+ );
+
+ }
+ }
+
+ private void waitAndAssertConfigurationChange(ComponentName activityName) {
+ assertTrue("Must receive a single configuration change",
+ mAmWmState.waitForWithAmState(
+ state -> getCallbackCount(activityName, ON_CONFIGURATION_CHANGED) == 1,
+ activityName + " receives configuration change"));
+ }
+
+ private void waitAndAssertResume(ComponentName activityName) {
+ assertTrue("Must be resumed once",
+ mAmWmState.waitForWithAmState(
+ state -> getCallbackCount(activityName, ON_RESUME) == 1,
+ activityName + " performs resume"));
+ }
+
+ private static int getCallbackCount(ComponentName activityName,
+ CommandSession.ActivityCallback callback) {
+ final ActivityLifecycleCounts lifecycles = new ActivityLifecycleCounts(activityName);
+ return lifecycles.getCount(callback);
+ }
+
public static class ClientTestActivity extends ImeTestActivity { }
public static class NoRelaunchActivity extends ImeTestActivity { }
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayKeyguardTests.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayKeyguardTests.java
index 9a51039..fb77815 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayKeyguardTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayKeyguardTests.java
@@ -80,11 +80,10 @@
final ActivityDisplay publicDisplay = virtualDisplaySession.setPublicDisplay(true)
.createDisplay();
lockScreenSession.gotoKeyguard();
- mAmWmState.waitForWithWmState((state) -> isKeyguardOnDisplay(state, publicDisplay.mId),
- "Waiting for keyguard window to show");
-
assertTrue("KeyguardDialog must show on external public display",
- isKeyguardOnDisplay(mAmWmState.getWmState(), publicDisplay.mId));
+ mAmWmState.waitForWithWmState(
+ state -> isKeyguardOnDisplay(state, publicDisplay.mId),
+ "keyguard window to show"));
// Keyguard dialog mustn't be removed when press back key
pressBackButton();
@@ -108,11 +107,11 @@
.createDisplay();
lockScreenSession.gotoKeyguard();
- mAmWmState.waitForWithWmState((state) -> isKeyguardOnDisplay(state, publicDisplay.mId),
- "Waiting for keyguard window to show");
-
assertTrue("KeyguardDialog must show on external public display",
- isKeyguardOnDisplay(mAmWmState.getWmState(), publicDisplay.mId));
+ mAmWmState.waitForWithWmState(
+ state -> isKeyguardOnDisplay(state, publicDisplay.mId),
+ "keyguard window to show"));
+
assertFalse("KeyguardDialog must not show on external private display",
isKeyguardOnDisplay(mAmWmState.getWmState(), privateDisplay.mId));
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayLockedKeyguardTests.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayLockedKeyguardTests.java
index 642a3f0..580af40 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayLockedKeyguardTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayLockedKeyguardTests.java
@@ -56,7 +56,6 @@
* Test that virtual display content is hidden when device is locked.
*/
@Test
- @FlakyTest(bugId = 131005232)
public void testVirtualDisplayHidesContentWhenLocked() throws Exception {
try (final LockScreenSession lockScreenSession = new LockScreenSession();
final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
@@ -137,6 +136,7 @@
}
}
+ @FlakyTest(bugId = 141674516)
@Test
public void testDismissKeyguard_whileOccluded_secondaryDisplay() throws Exception {
try (final LockScreenSession lockScreenSession =
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayPolicyTests.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayPolicyTests.java
index d3aabb7..dae2607 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayPolicyTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayPolicyTests.java
@@ -21,7 +21,6 @@
import static android.server.wm.ActivityManagerState.STATE_RESUMED;
import static android.server.wm.ActivityManagerState.STATE_STOPPED;
import static android.server.wm.ComponentNameUtils.getWindowName;
-import static android.server.wm.StateLogger.logAlways;
import static android.server.wm.StateLogger.logE;
import static android.server.wm.WindowManagerState.TRANSIT_TASK_CLOSE;
import static android.server.wm.WindowManagerState.TRANSIT_TASK_OPEN;
@@ -49,7 +48,6 @@
import static org.junit.Assume.assumeTrue;
import android.content.ComponentName;
-import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
import android.server.wm.ActivityManagerState.ActivityDisplay;
import android.server.wm.ActivityManagerState.ActivityStack;
@@ -58,13 +56,9 @@
import android.server.wm.CommandSession.SizeInfo;
import android.util.SparseArray;
-import androidx.test.filters.FlakyTest;
-
import org.junit.Before;
import org.junit.Test;
-import java.util.concurrent.TimeUnit;
-
/**
* Build/Install/Run:
* atest CtsWindowManagerDeviceTestCases:MultiDisplayPolicyTests
@@ -175,7 +169,7 @@
logE("Error waiting for valid state: " + e.getMessage());
return false;
}
- }, "Wait for the configuration change to happen and for activity to be resumed.");
+ }, "the configuration change to happen and activity to be resumed");
mAmWmState.computeState(false /* compareTaskAndStackBounds */,
new WaitForValidActivityState(RESIZEABLE_ACTIVITY),
@@ -282,20 +276,11 @@
// Wait for the fullscreen stack to start sleeping, and then make sure the
// test activity is still resumed.
- int retry = 0;
- int stopCount = 0;
- do {
- stopCount = (new ActivityLifecycleCounts(RESIZEABLE_ACTIVITY))
- .getCount(ActivityCallback.ON_STOP);
- if (stopCount == 1) {
- break;
- }
- logAlways("***testExternalDisplayActivityTurnPrimaryOff... retry=" + retry);
- SystemClock.sleep(TimeUnit.SECONDS.toMillis(1));
- } while (retry++ < 5);
-
- if (stopCount != 1) {
- fail(RESIZEABLE_ACTIVITY + " has received " + stopCount
+ final ActivityLifecycleCounts counts = new ActivityLifecycleCounts(RESIZEABLE_ACTIVITY);
+ if (!Condition.waitFor(counts.countWithRetry(RESIZEABLE_ACTIVITY + " to be stopped",
+ countSpec(ActivityCallback.ON_STOP, CountSpec.EQUALS, 1)))) {
+ fail(RESIZEABLE_ACTIVITY + " has received "
+ + counts.getCount(ActivityCallback.ON_STOP)
+ " onStop() calls, expecting 1");
}
// For this test we create this virtual display with flag showContentWhenLocked, so it
@@ -359,7 +344,7 @@
VIRTUAL_DISPLAY_ACTIVITY, newDisplay.mId);
}
mAmWmState.waitFor((amState, wmState) -> amState.getDisplayCount() == displayCount,
- "Waiting for external displays to be removed");
+ "external displays to be removed");
assertEquals(displayCount, mAmWmState.getAmState().getDisplayCount());
assertEquals(displayCount, mAmWmState.getAmState().getKeyguardControllerState().
mKeyguardOccludedStates.size());
@@ -755,7 +740,6 @@
* Tests that toast works on a secondary display.
*/
@Test
- @FlakyTest(bugId = 131005232)
public void testSecondaryDisplayShowToast() throws Exception {
try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
final ActivityDisplay newDisplay =
@@ -764,11 +748,9 @@
launchActivityOnDisplay(TOAST_ACTIVITY, newDisplay.mId);
waitAndAssertTopResumedActivity(TOAST_ACTIVITY, newDisplay.mId,
"Activity launched on external display must be resumed");
- mAmWmState.waitForWithWmState((state) -> state.containsWindow(TOAST_NAME),
- "Waiting for toast window to show");
- assertTrue("Toast window must be shown",
- mAmWmState.getWmState().containsWindow(TOAST_NAME));
+ assertTrue("Toast window must be shown", mAmWmState.waitForWithWmState(
+ state -> state.containsWindow(TOAST_NAME), "toast window to show"));
assertTrue("Toast window must be visible",
mAmWmState.getWmState().isWindowVisible(TOAST_NAME));
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayPrivateDisplayTests.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayPrivateDisplayTests.java
new file mode 100644
index 0000000..ee278d8
--- /dev/null
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayPrivateDisplayTests.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.wm;
+
+import static android.server.wm.app.Components.LAUNCH_BROADCAST_RECEIVER;
+import static android.server.wm.app.Components.LaunchBroadcastReceiver.LAUNCH_BROADCAST_ACTION;
+import static android.server.wm.app.Components.TEST_ACTIVITY;
+import static android.view.Display.FLAG_PRIVATE;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.Intent;
+import android.platform.test.annotations.Presubmit;
+import android.server.wm.ActivityManagerState.ActivityDisplay;
+import android.server.wm.TestJournalProvider.TestJournalContainer;
+import android.server.wm.WindowManagerState.Display;
+import android.util.Log;
+import android.view.View;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+import java.util.ArrayList;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Build/Install/Run:
+ * atest CtsWindowManagerDeviceTestCases:MultiDisplayPrivateDisplayTests
+ *
+ * Tests if be allowed to launch/access an activity on private display
+ * in multi-display environment.
+ */
+@Presubmit
+public class MultiDisplayPrivateDisplayTests extends MultiDisplayTestBase {
+ private static final String TAG = "MultiDisplayPrivateDisplayTests";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private static final String INTERNAL_SYSTEM_WINDOW =
+ "android.permission.INTERNAL_SYSTEM_WINDOW";
+ private ArrayList<Integer> mPrivateDisplayIds = new ArrayList<>();
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ assumeTrue(supportsMultiDisplay());
+ findPrivateDisplays();
+ assumeFalse("Skipping test: no physical private display found.",
+ mPrivateDisplayIds.isEmpty());
+ }
+
+ /** Saves physical private displays in mPrivateDisplayIds */
+ private void findPrivateDisplays() {
+ mPrivateDisplayIds.clear();
+ mAmWmState.computeState(true);
+
+ for (ActivityDisplay activityDisplay: getDisplaysStates()) {
+ int displayId = activityDisplay.mId;
+ Display display = mAmWmState.getWmState().getDisplay(displayId);
+ if ((display.getFlags() & FLAG_PRIVATE) != 0) {
+ mPrivateDisplayIds.add(displayId);
+ }
+ }
+ }
+
+ /**
+ * Tests launching an activity on a private display without special permission must not be
+ * allowed.
+ */
+ @Test
+ public void testCantLaunchOnPrivateDisplay() throws Exception {
+ // try on each private display
+ for (int displayId: mPrivateDisplayIds) {
+ separateTestJournal();
+
+ getLaunchActivityBuilder()
+ .setDisplayId(displayId)
+ .setTargetActivity(TEST_ACTIVITY)
+ .execute();
+
+ assertSecurityExceptionFromActivityLauncher();
+
+ mAmWmState.computeState(TEST_ACTIVITY);
+ assertFalse("Activity must not be launched on a private display",
+ mAmWmState.getAmState().containsActivity(TEST_ACTIVITY));
+ }
+ }
+
+ /**
+ * Tests
+ * {@link android.app.ActivityManager#isActivityStartAllowedOnDisplay(Context, int, Intent)}
+ * call to start an activity on private display is not allowed without special permission
+ */
+ @Test
+ public void testCantAccessPrivateDisplay() throws Exception {
+ final ActivityManager activityManager =
+ (ActivityManager) mTargetContext.getSystemService(Context.ACTIVITY_SERVICE);
+ final Intent intent = new Intent(Intent.ACTION_VIEW).setComponent(TEST_ACTIVITY);
+
+ for (int displayId: mPrivateDisplayIds) {
+ assertFalse(activityManager.isActivityStartAllowedOnDisplay(mTargetContext,
+ displayId, intent));
+ }
+ }
+
+ /**
+ * Tests
+ * {@link android.app.ActivityManager#isActivityStartAllowedOnDisplay(Context, int, Intent)}
+ * for a private display with INTERNAL_SYSTEM_WINDOW permission.
+ */
+ @Test
+ public void testCanAccessPrivateDisplayWithInternalPermission() throws Exception {
+ final ActivityManager activityManager =
+ (ActivityManager) mTargetContext.getSystemService(Context.ACTIVITY_SERVICE);
+ final Intent intent = new Intent(Intent.ACTION_VIEW)
+ .setComponent(TEST_ACTIVITY);
+
+ for (int displayId: mPrivateDisplayIds) {
+ SystemUtil.runWithShellPermissionIdentity(() ->
+ assertTrue(activityManager.isActivityStartAllowedOnDisplay(mTargetContext,
+ displayId, intent)), INTERNAL_SYSTEM_WINDOW);
+ }
+ }
+}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySecurityTests.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySecurityTests.java
index b2654ee..9ff341c 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySecurityTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySecurityTests.java
@@ -17,7 +17,6 @@
package android.server.wm;
import static android.server.wm.ComponentNameUtils.getActivityName;
-import static android.server.wm.StateLogger.logAlways;
import static android.server.wm.app.Components.DISPLAY_ACCESS_CHECK_EMBEDDING_ACTIVITY;
import static android.server.wm.app.Components.LAUNCHING_ACTIVITY;
import static android.server.wm.app.Components.LAUNCH_BROADCAST_RECEIVER;
@@ -40,7 +39,7 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import static androidx.test.InstrumentationRegistry.getInstrumentation;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -55,26 +54,23 @@
import android.content.Intent;
import android.hardware.display.DisplayManager;
import android.os.Bundle;
-import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
import android.server.wm.ActivityManagerState.ActivityDisplay;
import android.server.wm.ActivityManagerState.ActivityStack;
import android.server.wm.CommandSession.ActivitySession;
+import android.server.wm.TestJournalProvider.TestJournalContainer;
import android.util.SparseArray;
import android.view.Display;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
-import androidx.test.filters.FlakyTest;
-
import com.android.compatibility.common.util.SystemUtil;
import com.android.compatibility.common.util.TestUtils;
import org.junit.Before;
import org.junit.Test;
-
/**
* Build/Install/Run:
* atest CtsWindowManagerDeviceTestCases:MultiDisplaySecurityTests
@@ -405,17 +401,16 @@
private void assertActivityStartCheckResult(boolean expected) {
final String component = ActivityLauncher.TAG;
- for (int retry = 1; retry <= 5; retry++) {
- final Bundle extras = TestJournalProvider.TestJournalContainer.get(component).extras;
- if (extras.containsKey(ActivityLauncher.KEY_IS_ACTIVITY_START_ALLOWED_ON_DISPLAY)) {
- assertEquals("Activity start check must match", expected, extras
- .getBoolean(ActivityLauncher.KEY_IS_ACTIVITY_START_ALLOWED_ON_DISPLAY));
- return;
- }
-
- logAlways("***Waiting for activity start check for " + component
- + " ... retry=" + retry);
- SystemClock.sleep(500);
+ final Bundle resultExtras = Condition.waitForResult(
+ new Condition<Bundle>("activity start check for " + component)
+ .setRetryIntervalMs(500)
+ .setResultSupplier(() -> TestJournalContainer.get(component).extras)
+ .setResultValidator(extras -> extras.containsKey(
+ ActivityLauncher.KEY_IS_ACTIVITY_START_ALLOWED_ON_DISPLAY)));
+ if (resultExtras != null) {
+ assertEquals("Activity start check must match", expected, resultExtras
+ .getBoolean(ActivityLauncher.KEY_IS_ACTIVITY_START_ALLOWED_ON_DISPLAY));
+ return;
}
fail("Expected activity start check from " + component + " not found");
}
@@ -692,19 +687,6 @@
}
}
- private void assertSecurityExceptionFromActivityLauncher() {
- final String component = ActivityLauncher.TAG;
- for (int retry = 1; retry <= 5; retry++) {
- if (ActivityLauncher.hasCaughtSecurityException()) {
- return;
- }
-
- logAlways("***Waiting for SecurityException from " + component + " ... retry=" + retry);
- SystemClock.sleep(500);
- }
- fail("Expected exception from " + component + " not found");
- }
-
/**
* Test that only private virtual display can show content with insecure keyguard.
*/
@@ -727,7 +709,6 @@
* Test setting system decoration flag and show IME flag without sufficient permissions.
*/
@Test
- @FlakyTest(bugId = 130284250)
public void testSettingFlagWithoutInternalSystemPermission() throws Exception {
try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
// The reason to use a trusted display is that we can guarantee the security exception
@@ -771,7 +752,6 @@
* Test getting system decoration flag and show IME flag without sufficient permissions.
*/
@Test
- @FlakyTest(bugId = 130284250)
public void testGettingFlagWithoutInternalSystemPermission() throws Exception {
try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
// The reason to use a trusted display is that we can guarantee the security exception
@@ -802,7 +782,6 @@
* Test setting system decoration flag and show IME flag to the untrusted display.
*/
@Test
- @FlakyTest(bugId = 130284250)
public void testSettingFlagToUntrustedDisplay() throws Exception {
try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
final ActivityDisplay untrustedDisplay = virtualDisplaySession.createDisplay();
@@ -849,7 +828,6 @@
* Test getting system decoration flag and show IME flag from the untrusted display.
*/
@Test
- @FlakyTest(bugId = 130284250)
public void testGettingFlagFromUntrustedDisplay() throws Exception {
try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
final ActivityDisplay untrustedDisplay = virtualDisplaySession.createDisplay();
@@ -871,7 +849,6 @@
* Test setting system decoration flag and show IME flag to the trusted display.
*/
@Test
- @FlakyTest(bugId = 130284250)
public void testSettingFlagToTrustedDisplay() throws Exception {
try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
final ActivityDisplay trustedDisplay = virtualDisplaySession
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySystemDecorationTests.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySystemDecorationTests.java
index 07a8dba..53e3a93 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySystemDecorationTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySystemDecorationTests.java
@@ -64,8 +64,6 @@
import android.widget.EditText;
import android.widget.LinearLayout;
-import androidx.test.filters.FlakyTest;
-
import com.android.compatibility.common.util.ImeAwareEditText;
import com.android.compatibility.common.util.SystemUtil;
import com.android.compatibility.common.util.TestUtils;
@@ -84,7 +82,7 @@
/**
* Build/Install/Run:
- * atest CtsWindowManagerDeviceTestCases:SystemDecorationMultiDisplayTests
+ * atest CtsWindowManagerDeviceTestCases:MultiDisplaySystemDecorationTests
*
* This tests that verify the following should not be run for OEM device verification:
* Wallpaper added if display supports system decorations (and not added otherwise)
@@ -130,7 +128,6 @@
* Tests that wallpaper shows on secondary displays.
*/
@Test
- @FlakyTest(bugId = 131005232)
public void testWallpaperShowOnSecondaryDisplays() throws Exception {
try (final ChangeWallpaperSession wallpaperSession = new ChangeWallpaperSession();
final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession();
@@ -145,12 +142,10 @@
final Bitmap tmpWallpaper = wallpaperSession.getTestBitmap();
wallpaperSession.setImageWallpaper(tmpWallpaper);
- mAmWmState.waitForWithWmState(
- (state) -> isWallpaperOnDisplay(state, decoredSystemDisplay.mId),
- "Waiting for wallpaper window to show");
-
assertTrue("Wallpaper must be displayed on system owned display with system decor flag",
- isWallpaperOnDisplay(mAmWmState.getWmState(), decoredSystemDisplay.mId));
+ mAmWmState.waitForWithWmState(
+ state -> isWallpaperOnDisplay(state, decoredSystemDisplay.mId),
+ "wallpaper window to show"));
assertFalse("Wallpaper must not be displayed on the untrusted display",
isWallpaperOnDisplay(mAmWmState.getWmState(), untrustedDisplay.mId));
@@ -217,11 +212,10 @@
* Test that navigation bar should not show on display without system decoration.
*/
@Test
- @FlakyTest(bugId = 131005232)
public void testNavBarNotShowingOnDisplayWithoutDecor() throws Exception {
try (final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession()) {
- // Wait navigation bar show on default display and record the states.
- mAmWmState.waitAndAssertNavBarShownOnDisplay(DEFAULT_DISPLAY);
+ // Wait for system decoration showing and record current nav states.
+ mAmWmState.waitForHomeActivityVisible();
final List<WindowState> expected = mAmWmState.getWmState().getAllNavigationBarStates();
externalDisplaySession.setPublicDisplay(true)
@@ -236,11 +230,10 @@
* supports system decoration.
*/
@Test
- @FlakyTest(bugId = 131005232)
public void testNavBarNotShowingOnPrivateDisplay() throws Exception {
try (final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession()) {
- // Wait navigation bar show on default display and record the states.
- mAmWmState.waitAndAssertNavBarShownOnDisplay(DEFAULT_DISPLAY);
+ // Wait for system decoration showing and record current nav states.
+ mAmWmState.waitForHomeActivityVisible();
final List<WindowState> expected = mAmWmState.getWmState().getAllNavigationBarStates();
externalDisplaySession.setPublicDisplay(false)
@@ -385,7 +378,6 @@
// IME related tests
@Test
- @FlakyTest(bugId = 131005232)
public void testImeWindowCanSwitchToDifferentDisplays() throws Exception {
try (final TestActivitySession<ImeTestActivity> imeTestActivitySession = new
TestActivitySession<>();
@@ -436,7 +428,6 @@
}
@Test
- @FlakyTest(bugId = 131005232)
public void testImeApiForBug118341760() throws Exception {
final long TIMEOUT_START_INPUT = TimeUnit.SECONDS.toMillis(5);
@@ -476,7 +467,6 @@
}
@Test
- @FlakyTest(bugId = 131005232)
public void testImeWindowCanSwitchWhenTopFocusedDisplayChange() throws Exception {
// If config_perDisplayFocusEnabled, the focus will not move even if touching on
// the Activity in the different display.
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayTestBase.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayTestBase.java
index ef78bc7..a805e7e 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayTestBase.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayTestBase.java
@@ -17,10 +17,8 @@
package android.server.wm;
import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
-import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
import static android.server.wm.ComponentNameUtils.getActivityName;
import static android.server.wm.StateLogger.log;
-import static android.server.wm.StateLogger.logAlways;
import static android.server.wm.UiDeviceUtils.pressSleepButton;
import static android.server.wm.UiDeviceUtils.pressWakeupButton;
import static android.server.wm.app.Components.VIRTUAL_DISPLAY_ACTIVITY;
@@ -40,34 +38,31 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
-import static androidx.test.InstrumentationRegistry.getInstrumentation;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasSize;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
import android.content.ComponentName;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
-import android.os.SystemClock;
import android.provider.Settings;
import android.server.wm.ActivityManagerState.ActivityDisplay;
import android.server.wm.CommandSession.ActivitySession;
import android.server.wm.CommandSession.ActivitySessionClient;
import android.server.wm.settings.SettingsSession;
import android.util.Size;
-import android.util.SparseBooleanArray;
import android.view.WindowManager;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.compatibility.common.util.SystemUtil;
-import com.android.compatibility.common.util.TestUtils;
+
import org.junit.Before;
import java.util.ArrayList;
@@ -245,16 +240,16 @@
tapOnDisplay(bounds.centerX(), bounds.centerY(), displayId);
}
- private void waitForDisplayGone(Predicate<WindowManagerState.Display> displayPredicate) {
- for (int retry = 1; retry <= 5; retry++) {
+ void waitForDisplayGone(Predicate<WindowManagerState.Display> displayPredicate) {
+ waitForOrFail("displays to be removed", () -> {
mAmWmState.computeState(true);
- if (!mAmWmState.getWmState().getDisplays().stream().anyMatch(displayPredicate::test)) {
- return;
- }
- logAlways("Waiting for hosted displays destruction... retry=" + retry);
- SystemClock.sleep(500);
- }
- fail("Waiting for hosted displays destruction failed.");
+ return !mAmWmState.getWmState().getDisplays().stream().anyMatch(displayPredicate::test);
+ });
+ }
+
+ void assertSecurityExceptionFromActivityLauncher() {
+ waitForOrFail("SecurityException from " + ActivityLauncher.TAG,
+ ActivityLauncher::hasCaughtSecurityException);
}
/**
@@ -462,7 +457,7 @@
// TODO(b/112837428): Merge into VirtualDisplaySession when all usages are migrated.
protected class VirtualDisplayLauncher extends VirtualDisplaySession {
- private final ActivitySessionClient mActivitySessionClient = new ActivitySessionClient();
+ private final ActivitySessionClient mActivitySessionClient = ActivitySessionClient.create();
ActivitySession launchActivityOnDisplay(ComponentName activityName,
ActivityDisplay display) {
@@ -510,6 +505,8 @@
super(Settings.Global.getUriFor(Settings.Global.OVERLAY_DISPLAY_DEVICES),
Settings.Global::getString,
Settings.Global::putString);
+ // Remove existing overlay display to avoid display count problem.
+ removeExisting();
mWm = context.getSystemService(WindowManager.class);
}
@@ -534,15 +531,13 @@
display.mId, showSystemDecors, showIme));
if (requestShowSysDecors != showSystemDecors) {
mWm.setShouldShowSystemDecors(display.mId, requestShowSysDecors);
- TestUtils.waitUntil("Waiting for display show system decors",
- 5 /* timeoutSecond */,
+ waitForOrFail("display config show-system-decors to be set",
() -> mWm.shouldShowSystemDecors(
display.mId) == requestShowSysDecors);
}
if (requestShowIme != showIme) {
mWm.setShouldShowIme(display.mId, requestShowIme);
- TestUtils.waitUntil("Waiting for display show Ime",
- 5 /* timeoutSecond */,
+ waitForOrFail("display config show-IME to be set",
() -> mWm.shouldShowIme(display.mId) == requestShowIme);
}
}
@@ -555,8 +550,7 @@
mWm.setShouldShowIme(state.mId, state.mShouldShowIme);
// Only need to wait the last flag to be set.
- TestUtils.waitUntil("Waiting for the show IME flag to be set",
- 5 /* timeoutSecond */,
+ waitForOrFail("display config show-IME to be restored",
() -> mWm.shouldShowIme(state.mId) == state.mShouldShowIme);
}));
}
@@ -570,6 +564,12 @@
.anyMatch(state -> state.mId == display.getDisplayId()));
}
+ private void removeExisting() {
+ if (mHasInitialValue && mInitialValue != null) {
+ delete(mUri);
+ }
+ }
+
private class OverlayDisplayState {
int mId;
boolean mShouldShowSystemDecors;
@@ -585,20 +585,12 @@
/** Wait for provided number of displays and report their configurations. */
List<ActivityDisplay> getDisplayStateAfterChange(int expectedDisplayCount) {
- List<ActivityDisplay> ds = getDisplaysStates();
-
- int retriesLeft = 5;
- while (!areDisplaysValid(ds, expectedDisplayCount) && retriesLeft-- > 0) {
- log("***Waiting for the correct number of displays...");
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- log(e.toString());
- }
- ds = getDisplaysStates();
- }
-
- return ds;
+ return Condition.waitForResult("the correct number of displays=" + expectedDisplayCount,
+ condition -> condition
+ .setReturnLastResult(true)
+ .setResultSupplier(this::getDisplaysStates)
+ .setResultValidator(
+ displays -> areDisplaysValid(displays, expectedDisplayCount)));
}
private boolean areDisplaysValid(List<ActivityDisplay> displays, int expectedDisplayCount) {
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java b/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
index 98669b7..96649f9 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
@@ -16,6 +16,7 @@
package android.server.wm;
+import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.app.ActivityTaskManager.INVALID_STACK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -64,7 +65,7 @@
import static android.server.wm.app27.Components.SDK_27_PIP_ACTIVITY;
import static android.view.Display.DEFAULT_DISPLAY;
-import static androidx.test.InstrumentationRegistry.getInstrumentation;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static org.hamcrest.Matchers.lessThan;
import static org.junit.Assert.assertEquals;
@@ -107,13 +108,13 @@
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
/**
* Build/Install/Run:
* atest CtsWindowManagerDeviceTestCases:PinnedStackTests
*/
@Presubmit
-@FlakyTest(bugId = 71792368)
public class PinnedStackTests extends ActivityManagerTestBase {
private static final String TAG = PinnedStackTests.class.getSimpleName();
@@ -215,12 +216,7 @@
try (final RotationSession rotationSession = new RotationSession()) {
rotationSession.set(ROTATION_0);
- mAmWmState.waitForWithWmState((wmState1) -> {
- Rect db = wmState1.getDefaultPinnedStackBounds();
- Rect sb = wmState1.getStableBounds();
- return (db.width() > 0 && db.height() > 0) &&
- (sb.contains(db));
- }, "Waiting for valid bounds..");
+ waitForValidPinnedStackBounds(WindowManagerState::getDefaultPinnedStackBounds);
WindowManagerState wmState = mAmWmState.getWmState();
wmState.computeState();
Rect defaultPipBounds = wmState.getDefaultPinnedStackBounds();
@@ -229,12 +225,7 @@
assertTrue(stableBounds.contains(defaultPipBounds));
rotationSession.set(ROTATION_90);
- mAmWmState.waitForWithWmState((wmState1) -> {
- Rect db = wmState1.getDefaultPinnedStackBounds();
- Rect sb = wmState1.getStableBounds();
- return (db.width() > 0 && db.height() > 0) &&
- (sb.contains(db));
- }, "Waiting for valid bounds...");
+ waitForValidPinnedStackBounds(WindowManagerState::getDefaultPinnedStackBounds);
wmState = mAmWmState.getWmState();
wmState.computeState();
defaultPipBounds = wmState.getDefaultPinnedStackBounds();
@@ -253,12 +244,7 @@
try (final RotationSession rotationSession = new RotationSession()) {
rotationSession.set(ROTATION_0);
- mAmWmState.waitForWithWmState((wmState1) -> {
- Rect db = wmState1.getPinnedStackMovementBounds();
- Rect sb = wmState1.getStableBounds();
- return (db.width() > 0 && db.height() > 0) &&
- (sb.contains(db));
- }, "Waiting for valid bounds...");
+ waitForValidPinnedStackBounds(WindowManagerState::getPinnedStackMovementBounds);
WindowManagerState wmState = mAmWmState.getWmState();
wmState.computeState();
Rect pipMovementBounds = wmState.getPinnedStackMovementBounds();
@@ -267,12 +253,7 @@
assertTrue(stableBounds.contains(pipMovementBounds));
rotationSession.set(ROTATION_90);
- mAmWmState.waitForWithWmState((wmState1) -> {
- Rect db = wmState1.getPinnedStackMovementBounds();
- Rect sb = wmState1.getStableBounds();
- return (db.width() > 0 && db.height() > 0) &&
- (sb.contains(db));
- }, "Waiting for valid bounds...");
+ waitForValidPinnedStackBounds(WindowManagerState::getPinnedStackMovementBounds);
wmState = mAmWmState.getWmState();
wmState.computeState();
pipMovementBounds = wmState.getPinnedStackMovementBounds();
@@ -282,8 +263,16 @@
}
}
+ private void waitForValidPinnedStackBounds(Function<WindowManagerState, Rect> boundsFunc) {
+ mAmWmState.waitForWithWmState(wmState -> {
+ final Rect bounds = boundsFunc.apply(wmState);
+ final Rect displayStableBounds = wmState.getStableBounds();
+ return bounds.width() > 0 && bounds.height() > 0
+ && displayStableBounds.contains(bounds);
+ }, "valid pinned stack bounds");
+ }
+
@Test
- @FlakyTest // TODO: Reintroduce to presubmit once b/71508234 is resolved.
public void testPinnedStackOutOfBoundsInsetsNonNegative() throws Exception {
final WindowManagerState wmState = mAmWmState.getWmState();
@@ -302,7 +291,7 @@
final int stackId = getPinnedStack().mStackId;
final int top = 0;
final int left = displayRect.width() - 200;
- resizeStack(stackId, left, top, left + 500, top + 500);
+ resizePinnedStack(stackId, left, top, left + 500, top + 500);
// Ensure that the surface insets are not negative
windowState = getWindowState(PIP_ACTIVITY);
@@ -640,7 +629,6 @@
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
}
- @FlakyTest(bugId = 70746098)
@Test
public void testRemovePipWithHiddenFullscreenStack() throws Exception {
// Launch a fullscreen activity, return home and while the fullscreen stack is hidden,
@@ -675,7 +663,6 @@
WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME);
}
- @FlakyTest(bugId = 70906499)
@Test
public void testMovePipToBackWithVisibleFullscreenStack() throws Exception {
// Launch a fullscreen activity, and a pip activity over that
@@ -691,7 +678,6 @@
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
}
- @FlakyTest(bugId = 70906499)
@Test
public void testMovePipToBackWithHiddenFullscreenStack() throws Exception {
// Launch a fullscreen activity, return home and while the fullscreen stack is hidden,
@@ -816,6 +802,9 @@
SystemUtil.runWithShellPermissionIdentity(() -> {
try {
mAtm.startSystemLockTaskMode(task.mTaskId);
+ waitForOrFail("Task in lock mode", () -> {
+ return mAm.getLockTaskModeState() != LOCK_TASK_MODE_NONE;
+ });
mBroadcastActionTrigger.doAction(ACTION_ENTER_PIP);
waitForEnterPip(PIP_ACTIVITY);
assertPinnedStackDoesNotExist();
@@ -827,7 +816,7 @@
});
}
- @FlakyTest(bugId = 70328524)
+ @FlakyTest(bugId = 139111392)
@Test
public void testConfigurationChangeOrderDuringTransition() throws Exception {
// Launch a PiP activity and ensure configuration change only happened once, and that the
@@ -1024,7 +1013,8 @@
// got resumed.
separateTestJournal();
SystemUtil.runWithShellPermissionIdentity(
- () -> mAtm.resizeStack(stackId, new Rect(20, 20, 500, 500), true /* animate */));
+ () -> mAtm.resizePinnedStack(stackId, new Rect(20, 20, 500, 500),
+ true /* animate */));
mBroadcastActionTrigger.doAction(TEST_ACTIVITY_ACTION_FINISH_SELF);
mAmWmState.waitFor((amState, wmState) ->
!amState.containsActivity(TRANSLUCENT_TEST_ACTIVITY),
@@ -1123,6 +1113,7 @@
TEST_ACTIVITY).mTaskId);
}
+ @FlakyTest(bugId = 139111392)
@Test
public void testLaunchTaskByAffinityMatchSingleTask() throws Exception {
// Launch an activity into the pinned stack with a fixed affinity
@@ -1145,7 +1136,6 @@
}
/** Test that reported display size corresponds to fullscreen after exiting PiP. */
- @FlakyTest
@Test
public void testDisplayMetricsPinUnpin() throws Exception {
separateTestJournal();
@@ -1180,6 +1170,7 @@
finalAppSize);
}
+ @FlakyTest(bugId = 139111392)
@Test
public void testEnterPictureInPictureSavePosition() throws Exception {
// Ensure we have static shelf offset by running this test over a non-home activity
@@ -1228,7 +1219,6 @@
}
@Test
- @FlakyTest(bugId = 71792368)
public void testEnterPictureInPictureDiscardSavedPositionOnFinish() throws Exception {
// Ensure we have static shelf offset by running this test over a non-home activity
launchActivity(NO_RELAUNCH_ACTIVITY);
@@ -1278,7 +1268,7 @@
} else {
offsetBoundsOut.offset(0, -offsetY);
}
- resizeStack(stack.mStackId, offsetBoundsOut.left, offsetBoundsOut.top,
+ resizePinnedStack(stack.mStackId, offsetBoundsOut.left, offsetBoundsOut.top,
offsetBoundsOut.right, offsetBoundsOut.bottom);
}
@@ -1413,7 +1403,7 @@
mAmWmState.waitFor((amState, wmState) -> {
WindowStack stack = wmState.getStandardStackByWindowingMode(WINDOWING_MODE_PINNED);
return stack != null && !stack.mAnimatingBounds;
- }, "Waiting for pinned stack bounds animation to finish");
+ }, "pinned stack bounds animation to finish");
}
/**
@@ -1423,7 +1413,7 @@
mAmWmState.waitFor((amState, wmState) -> {
return !amState.containsStack(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD)
&& !wmState.containsStack(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
- }, "Waiting for pinned stack to be removed...");
+ }, "pinned stack to be removed");
}
/**
@@ -1445,7 +1435,7 @@
return lifecycles.getCount(ActivityCallback.ON_CONFIGURATION_CHANGED) == 1
&& lifecycles.getCount(ActivityCallback.ON_PICTURE_IN_PICTURE_MODE_CHANGED) == 1
&& lifecycles.getCount(ActivityCallback.ON_MULTI_WINDOW_MODE_CHANGED) == 1;
- }, "Waiting for picture-in-picture activity callbacks...");
+ }, "picture-in-picture activity callbacks...");
}
private void waitForValidAspectRatio(int num, int denom) {
@@ -1454,7 +1444,7 @@
mAmWmState.waitForWithAmState((state) -> {
Rect bounds = state.getStandardStackByWindowingMode(WINDOWING_MODE_PINNED).getBounds();
return floatEquals((float) bounds.width() / bounds.height(), (float) num / denom);
- }, "waitForValidAspectRatio");
+ }, "valid aspect ratio");
}
/**
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/SplitScreenTests.java b/tests/framework/base/windowmanager/src/android/server/wm/SplitScreenTests.java
index 2dfd254..a4857ba 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/SplitScreenTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/SplitScreenTests.java
@@ -96,7 +96,6 @@
}
@Test
- @FlakyTest(bugId = 71792393)
public void testStackList() throws Exception {
launchActivity(TEST_ACTIVITY);
mAmWmState.computeState(TEST_ACTIVITY);
@@ -118,7 +117,6 @@
}
@Test
- @FlakyTest(bugId = 131005232)
public void testNonResizeableNotDocked() throws Exception {
launchActivityInSplitScreenWithRecents(NON_RESIZEABLE_ACTIVITY);
@@ -161,7 +159,6 @@
}
@Test
- @FlakyTest(bugId = 72956284)
public void testNoUserLeaveHintOnMultiWindowModeChanged() throws Exception {
launchActivity(TEST_ACTIVITY, WINDOWING_MODE_FULLSCREEN);
@@ -223,7 +220,6 @@
}
@Test
- @FlakyTest(bugId = 71792393)
public void testLaunchToSideMultiple() throws Exception {
launchActivitiesInSplitScreen(
getLaunchActivityBuilder().setTargetActivity(LAUNCHING_ACTIVITY),
@@ -249,7 +245,6 @@
}
@Test
- @FlakyTest(bugId = 73808815)
public void testLaunchToSideSingleInstance() throws Exception {
launchTargetToSide(SINGLE_INSTANCE_ACTIVITY, false);
}
@@ -259,7 +254,6 @@
launchTargetToSide(SINGLE_TASK_ACTIVITY, false);
}
- @FlakyTest(bugId = 71792393)
@Test
public void testLaunchToSideMultipleWithDifferentIntent() throws Exception {
launchTargetToSide(TEST_ACTIVITY, true);
@@ -414,7 +408,6 @@
}
@Test
- @FlakyTest
public void testMinimizedFromEachDockedSide() throws Exception {
try (final RotationSession rotationSession = new RotationSession()) {
for (int i = 0; i < 2; i++) {
@@ -432,7 +425,6 @@
}
@Test
- @FlakyTest(bugId = 131005232)
public void testRotationWhileDockMinimized() throws Exception {
launchActivityInDockStackAndMinimize(TEST_ACTIVITY);
@@ -461,7 +453,6 @@
}
@Test
- @FlakyTest(bugId = 131005232)
public void testMinimizeAndUnminimizeThenGoingHome() throws Exception {
// Rotate the screen to check that minimize, unminimize, dismiss the docked stack and then
// going home has the correct app transition
@@ -488,8 +479,7 @@
// Go home and check the app transition
assertNotEquals(TRANSIT_WALLPAPER_OPEN,
mAmWmState.getWmState().getDefaultDisplayLastTransition());
- pressHomeButton();
- mAmWmState.waitForHomeActivityVisible();
+ launchHomeActivity();
mAmWmState.waitForAppTransitionIdleOnDisplay(DEFAULT_DISPLAY);
assertEquals(TRANSIT_WALLPAPER_OPEN,
@@ -498,7 +488,6 @@
}
}
- @FlakyTest(bugId = 73813034)
@Test
public void testFinishDockActivityWhileMinimized() throws Exception {
launchActivityInDockStackAndMinimize(TEST_ACTIVITY);
@@ -552,7 +541,6 @@
* Verify split screen mode visibility after stack resize occurs.
*/
@Test
- @FlakyTest(bugId = 110276714)
public void testResizeDockedStack() throws Exception {
launchActivitiesInSplitScreen(
getLaunchActivityBuilder().setTargetActivity(DOCKED_ACTIVITY),
@@ -581,7 +569,6 @@
}
@Test
- @FlakyTest(bugId = 131005232)
public void testDifferentProcessActivityResumedPreQ() {
launchActivitiesInSplitScreen(
getLaunchActivityBuilder().setTargetActivity(SDK_27_TEST_ACTIVITY),
@@ -655,7 +642,6 @@
}
@Test
- @FlakyTest(bugId = 131005232)
public void testStackListOrderOnSplitScreenDismissed() throws Exception {
launchActivitiesInSplitScreen(
getLaunchActivityBuilder().setTargetActivity(DOCKED_ACTIVITY),
@@ -686,7 +672,7 @@
private void launchActivityInDockStackAndMinimize(ComponentName activityName, int createMode)
throws Exception {
launchActivityInSplitScreenWithRecents(activityName, createMode);
- pressHomeButton();
+ launchHomeActivityNoWait();
waitForAndAssertDockMinimized();
}
@@ -710,11 +696,11 @@
private void waitForDockMinimized() throws Exception {
mAmWmState.waitForWithWmState(state -> state.isDockedStackMinimized(),
- "***Waiting for Dock stack to be minimized");
+ "Dock stack to be minimized");
}
private void waitForDockNotMinimized() throws Exception {
mAmWmState.waitForWithWmState(state -> !state.isDockedStackMinimized(),
- "***Waiting for Dock stack to not be minimized");
+ "Dock stack to not be minimized");
}
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/StartActivityTests.java b/tests/framework/base/windowmanager/src/android/server/wm/StartActivityTests.java
index 6923afb..86e3578 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/StartActivityTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/StartActivityTests.java
@@ -16,16 +16,28 @@
package android.server.wm;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
+import static android.server.wm.ActivityManagerState.STATE_STOPPED;
+import static android.server.wm.app.Components.LAUNCHING_ACTIVITY;
+import static android.server.wm.app.Components.NO_RELAUNCH_ACTIVITY;
import static android.server.wm.app.Components.TEST_ACTIVITY;
+import static android.server.wm.app.Components.TRANSLUCENT_ACTIVITY;
+import static android.server.wm.app.Components.TestActivity.EXTRA_INTENTS;
+import static android.server.wm.app.Components.TestActivity.COMMAND_START_ACTIVITIES;
import static android.server.wm.app27.Components.SDK_27_LAUNCHING_ACTIVITY;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
import android.platform.test.annotations.Presubmit;
+import android.server.wm.CommandSession.ActivitySession;
+import android.server.wm.CommandSession.ActivitySessionClient;
import androidx.test.rule.ActivityTestRule;
-import androidx.test.filters.FlakyTest;
import org.junit.Rule;
import org.junit.Test;
@@ -39,14 +51,15 @@
@Rule
public final ActivityTestRule<TestActivity2> mTestActivity2Rule =
- new ActivityTestRule<>(TestActivity2.class);
+ new ActivityTestRule<>(TestActivity2.class, false /* initialTouchMode */,
+ false /* launchActivity */);
/**
* Ensures {@link Activity} can only be launched from an {@link Activity}
* {@link android.content.Context}.
*/
@Test
- public void testStartActivityContexts() throws Exception {
+ public void testStartActivityContexts() {
// Launch Activity from application context.
getLaunchActivityBuilder()
.setTargetActivity(TEST_ACTIVITY)
@@ -85,14 +98,37 @@
TEST_ACTIVITY);
}
+ @Test
+ public void testStartActivityTaskLaunchBehind() {
+ // launch an activity
+ getLaunchActivityBuilder()
+ .setTargetActivity(TEST_ACTIVITY)
+ .setUseInstrumentation()
+ .setNewTask(true)
+ .execute();
+
+ // launch an activity behind
+ getLaunchActivityBuilder()
+ .setTargetActivity(TRANSLUCENT_ACTIVITY)
+ .setUseInstrumentation()
+ .setIntentFlags(FLAG_ACTIVITY_NEW_DOCUMENT)
+ .setNewTask(true)
+ .setLaunchTaskBehind(true)
+ .execute();
+
+ waitAndAssertActivityState(TRANSLUCENT_ACTIVITY, STATE_STOPPED,
+ "Activity should be stopped");
+ mAmWmState.assertResumedActivity("Test Activity should be remained on top and resumed",
+ TEST_ACTIVITY);
+ }
+
/**
* Ensures you can start an {@link Activity} from a non {@link Activity}
* {@link android.content.Context} when the target sdk is between N and O Mr1.
* @throws Exception
*/
@Test
- @FlakyTest(bugId = 131005232)
- public void testLegacyStartActivityFromNonActivityContext() throws Exception {
+ public void testLegacyStartActivityFromNonActivityContext() {
getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY)
.setLaunchingActivity(SDK_27_LAUNCHING_ACTIVITY)
.setUseApplicationContext(true)
@@ -103,6 +139,46 @@
TEST_ACTIVITY);
}
+ /**
+ * <pre>
+ * Assume there are 3 activities (X, Y, Z) have different task affinities:
+ * 1. Activity X started.
+ * 2. X launches 2 activities (Y with NEW_TASK, Z) by {@link Activity#startActivities}.
+ * Expect the result should be 2 tasks: [X] and [Y, Z].
+ * </pre>
+ */
+ @Test
+ public void testStartActivitiesInNewAndSameTask() {
+ final ActivitySession activity = ActivitySessionClient.create().startActivity(
+ getLaunchActivityBuilder().setUseInstrumentation()
+ .setTargetActivity(TEST_ACTIVITY));
+
+ final Intent[] intents = {
+ new Intent().setComponent(NO_RELAUNCH_ACTIVITY)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
+ new Intent().setComponent(LAUNCHING_ACTIVITY)
+ };
+
+ final Bundle intentBundle = new Bundle();
+ intentBundle.putParcelableArray(EXTRA_INTENTS, intents);
+ // The {@link Activity#startActivities} cannot be called from the instrumentation
+ // package because the implementation (given by test runner) may be overridden.
+ activity.sendCommand(COMMAND_START_ACTIVITIES, intentBundle);
+
+ // The {@code intents} are started, wait for the last (top) activity to be ready and then
+ // verify their task ids.
+ mAmWmState.computeState(intents[1].getComponent());
+ final ActivityManagerState amState = mAmWmState.getAmState();
+ final int callerTaskId = amState.getTaskByActivity(TEST_ACTIVITY).getTaskId();
+ final int i0TaskId = amState.getTaskByActivity(intents[0].getComponent()).getTaskId();
+ final int i1TaskId = amState.getTaskByActivity(intents[1].getComponent()).getTaskId();
+
+ assertNotEquals("The activities started by startActivities() should have a different task"
+ + " from their caller activity", callerTaskId, i0TaskId);
+ assertEquals("The activities started by startActivities() should be put in the same task",
+ i0TaskId, i1TaskId);
+ }
+
public static class TestActivity2 extends Activity {
}
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/TransitionSelectionTests.java b/tests/framework/base/windowmanager/src/android/server/wm/TransitionSelectionTests.java
index ec666e0..14a7059 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/TransitionSelectionTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/TransitionSelectionTests.java
@@ -39,8 +39,6 @@
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
-import androidx.test.filters.FlakyTest;
-
import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
@@ -58,7 +56,6 @@
* atest CtsWindowManagerDeviceTestCases:TransitionSelectionTests
*/
@Presubmit
-@FlakyTest(bugId = 71792333)
public class TransitionSelectionTests extends ActivityManagerTestBase {
@Override
@@ -118,7 +115,6 @@
false /*slowStop*/, TRANSIT_TASK_OPEN);
}
- @FlakyTest(bugId = 71792333)
@Test
public void testCloseTask_NeitherWallpaper() {
testCloseTask(false /*bottomWallpaper*/, false /*topWallpaper*/,
@@ -177,7 +173,6 @@
// Test task close -- bottom task top activity slow in stopping
// These simulate the case where the bottom activity is resumed
// before AM receives its activitiyStopped
- @FlakyTest(bugId = 71792333)
@Test
public void testCloseTask_NeitherWallpaper_SlowStop() {
testCloseTask(false /*bottomWallpaper*/, false /*topWallpaper*/,
@@ -223,7 +218,6 @@
TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE);
}
- @FlakyTest(bugId = 71792333)
@Test
public void testCloseTask_BottomWallpaper_Translucent() {
testCloseTaskTranslucent(true /*bottomWallpaper*/, false /*topWallpaper*/,
@@ -306,13 +300,15 @@
}
executeShellCommand(topStartCmd);
- SystemClock.sleep(5000);
- if (testOpen) {
- mAmWmState.computeState(topActivity);
- } else {
- mAmWmState.computeState(BOTTOM_ACTIVITY);
- }
-
+ Condition.waitFor("Retrieving correct transition", () -> {
+ if (testOpen) {
+ mAmWmState.computeState(topActivity);
+ } else {
+ mAmWmState.computeState(BOTTOM_ACTIVITY);
+ }
+ return expectedTransit.equals(
+ mAmWmState.getWmState().getDefaultDisplayLastTransition());
+ });
assertEquals("Picked wrong transition", expectedTransit,
mAmWmState.getWmState().getDefaultDisplayLastTransition());
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/VirtualDisplayHelper.java b/tests/framework/base/windowmanager/src/android/server/wm/VirtualDisplayHelper.java
index cbd6816..8faff8d 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/VirtualDisplayHelper.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/VirtualDisplayHelper.java
@@ -20,23 +20,19 @@
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
import static android.server.wm.ActivityManagerTestBase.isDisplayOn;
+import static android.server.wm.ActivityManagerTestBase.waitForOrFail;
import static android.server.wm.StateLogger.logAlways;
import static android.view.Display.DEFAULT_DISPLAY;
-import static androidx.test.InstrumentationRegistry.getInstrumentation;
-
-import static org.junit.Assert.fail;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import android.graphics.PixelFormat;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.media.ImageReader;
-import android.os.SystemClock;
import com.android.compatibility.common.util.SystemUtil;
-import java.util.function.Predicate;
-
/**
* Helper class to create virtual display.
*/
@@ -78,40 +74,29 @@
int createAndWaitForDisplay() {
SystemUtil.runWithShellPermissionIdentity(() -> {
createVirtualDisplay();
- waitForDisplayState(mVirtualDisplay.getDisplay().getDisplayId() /* default */,
- true /* on */);
+ waitForDisplayState(mVirtualDisplay.getDisplay().getDisplayId(), true /* wantOn */);
mCreated = true;
});
return mVirtualDisplay.getDisplay().getDisplayId();
}
void turnDisplayOff() {
- SystemUtil.runWithShellPermissionIdentity(() -> {
- mVirtualDisplay.setSurface(null);
- waitForDisplayState(mVirtualDisplay.getDisplay().getDisplayId() /* displayId */,
- false /* on */);
- });
+ mVirtualDisplay.setSurface(null);
+ waitForDisplayState(mVirtualDisplay.getDisplay().getDisplayId(), false /* wantOn */);
}
void turnDisplayOn() {
- SystemUtil.runWithShellPermissionIdentity(() -> {
- mVirtualDisplay.setSurface(mReader.getSurface());
- waitForDisplayState(mVirtualDisplay.getDisplay().getDisplayId() /* displayId */,
- true /* on */);
- });
+ mVirtualDisplay.setSurface(mReader.getSurface());
+ waitForDisplayState(mVirtualDisplay.getDisplay().getDisplayId(), true /* wantOn */);
}
void releaseDisplay() {
- SystemUtil.runWithShellPermissionIdentity(() -> {
- if (mCreated) {
- mVirtualDisplay.release();
- mReader.close();
- waitForDisplayCondition(mVirtualDisplay.getDisplay().getDisplayId() /* displayId */,
- onState -> onState != null && onState == false,
- "Waiting for virtual display destroy");
- }
- mCreated = false;
- });
+ if (mCreated) {
+ mVirtualDisplay.release();
+ mReader.close();
+ waitForDisplayState(mVirtualDisplay.getDisplay().getDisplayId(), false /* wantOn */);
+ }
+ mCreated = false;
}
private void createVirtualDisplay() {
@@ -142,25 +127,12 @@
}
static void waitForDefaultDisplayState(boolean wantOn) {
- waitForDisplayState(DEFAULT_DISPLAY /* default */, wantOn);
+ waitForDisplayState(DEFAULT_DISPLAY, wantOn);
}
private static void waitForDisplayState(int displayId, boolean wantOn) {
- waitForDisplayCondition(displayId, state -> state != null && state == wantOn,
- "Waiting for " + ((displayId == DEFAULT_DISPLAY) ? "default" : "virtual")
- + " display "
- + (wantOn ? "on" : "off"));
- }
-
- private static void waitForDisplayCondition(int displayId,
- Predicate<Boolean> condition, String message) {
- for (int retry = 1; retry <= 10; retry++) {
- if (condition.test(isDisplayOn(displayId))) {
- return;
- }
- logAlways(message + "... retry=" + retry);
- SystemClock.sleep(500);
- }
- fail(message + " failed");
+ final String message = (displayId == DEFAULT_DISPLAY ? "default" : "virtual")
+ + " display " + (wantOn ? "on" : "off");
+ waitForOrFail(message, () -> isDisplayOn(displayId) == wantOn);
}
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowFocusTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowFocusTests.java
index a361011..4886925 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowFocusTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowFocusTests.java
@@ -39,8 +39,8 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assume.assumeTrue;
import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
import android.content.Context;
import android.content.res.Configuration;
@@ -140,7 +140,6 @@
* - The window which lost top-focus can receive display-unspecified cancel events.
*/
@Test
- @FlakyTest(bugId = 131005232)
public void testKeyReceiving() throws InterruptedException {
final PrimaryActivity primaryActivity = startActivity(PrimaryActivity.class,
DEFAULT_DISPLAY);
@@ -148,8 +147,6 @@
sendAndAssertTargetConsumedKey(primaryActivity, KEYCODE_1, DEFAULT_DISPLAY);
assumeTrue(supportsMultiDisplay());
- // If config_perDisplayFocusEnabled, tapping on a display will not move the focus.
- assumeFalse(perDisplayFocusEnabled());
try (VirtualDisplaySession displaySession = new VirtualDisplaySession()) {
final int secondaryDisplayId = displaySession.createDisplay(
getInstrumentation().getTargetContext()).getDisplayId();
@@ -158,7 +155,13 @@
sendAndAssertTargetConsumedKey(secondaryActivity, KEYCODE_2, INVALID_DISPLAY);
sendAndAssertTargetConsumedKey(secondaryActivity, KEYCODE_3, secondaryDisplayId);
- primaryActivity.waitAndAssertWindowFocusState(false /* hasFocus */);
+ final boolean perDisplayFocusEnabled = perDisplayFocusEnabled();
+ if (perDisplayFocusEnabled) {
+ primaryActivity.assertWindowFocusState(true /* hasFocus */);
+ sendAndAssertTargetConsumedKey(primaryActivity, KEYCODE_4, DEFAULT_DISPLAY);
+ } else {
+ primaryActivity.waitAndAssertWindowFocusState(false /* hasFocus */);
+ }
// Press display-unspecified keys and a display-specified key but not release them.
sendKey(ACTION_DOWN, KEYCODE_5, INVALID_DISPLAY);
@@ -175,7 +178,9 @@
// key events sent to secondary activity would be cancelled.
secondaryActivity.waitAssertAndConsumeKeyEvent(ACTION_UP, KEYCODE_5, FLAG_CANCELED);
secondaryActivity.waitAssertAndConsumeKeyEvent(ACTION_UP, KEYCODE_7, FLAG_CANCELED);
- secondaryActivity.waitAssertAndConsumeKeyEvent(ACTION_UP, KEYCODE_6, FLAG_CANCELED);
+ if (!perDisplayFocusEnabled) {
+ secondaryActivity.waitAssertAndConsumeKeyEvent(ACTION_UP, KEYCODE_6, FLAG_CANCELED);
+ }
assertEquals(secondaryActivity.getLogTag() + " must only receive expected events.",
0 /* expected event count */, secondaryActivity.getKeyEventCount());
@@ -188,7 +193,6 @@
* Test if a display targeted by a key event can be moved to top in a single-focus system.
*/
@Test
- @FlakyTest(bugId = 131005232)
public void testMovingDisplayToTopByKeyEvent() throws InterruptedException {
assumeTrue(supportsMultiDisplay());
assumeFalse(perDisplayFocusEnabled());
@@ -241,7 +245,6 @@
primaryActivity.waitAndAssertPointerCaptureState(true /* hasCapture */);
assumeTrue(supportsMultiDisplay());
- assumeFalse(perDisplayFocusEnabled());
try (VirtualDisplaySession displaySession = new VirtualDisplaySession()) {
final int secondaryDisplayId = displaySession.createDisplay(
getInstrumentation().getTargetContext()).getDisplayId();
@@ -316,7 +319,6 @@
* window on that display.
*/
@Test
- @FlakyTest(bugId = 130467737)
public void testTapNonFocusableWindow() throws InterruptedException {
assumeTrue(supportsMultiDisplay());
assumeFalse(perDisplayFocusEnabled());
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInputTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInputTests.java
index 55932cc..390de30 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInputTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInputTests.java
@@ -16,10 +16,12 @@
package android.server.wm;
-import static android.server.wm.UiDeviceUtils.pressHomeButton;
+import static android.server.wm.ActivityManagerTestBase.launchHomeActivityNoWait;
import static android.server.wm.UiDeviceUtils.pressUnlockButton;
import static android.server.wm.UiDeviceUtils.pressWakeupButton;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
import static org.junit.Assert.assertEquals;
import android.app.Activity;
@@ -31,8 +33,6 @@
import android.view.WindowInsets;
import android.view.WindowManager;
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.FlakyTest;
import androidx.test.rule.ActivityTestRule;
import com.android.compatibility.common.util.CtsTouchUtils;
@@ -48,7 +48,6 @@
* Build/Install/Run:
* atest CtsWindowManagerDeviceTestCases:WindowInputTests
*/
-@FlakyTest
public class WindowInputTests {
private final int TOTAL_NUMBER_OF_CLICKS = 100;
private final ActivityTestRule<Activity> mActivityRule = new ActivityTestRule<>(Activity.class);
@@ -64,9 +63,9 @@
public void setUp() {
pressWakeupButton();
pressUnlockButton();
- pressHomeButton();
+ launchHomeActivityNoWait();
- mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mInstrumentation = getInstrumentation();
mActivity = mActivityRule.launchActivity(null);
mInstrumentation.waitForIdleSync();
mClickCount = 0;
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowManagerTestBase.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowManagerTestBase.java
index 3490e00..10358dc 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowManagerTestBase.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowManagerTestBase.java
@@ -17,12 +17,12 @@
package android.server.wm;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-import static android.server.wm.UiDeviceUtils.pressHomeButton;
+import static android.server.wm.ActivityManagerTestBase.launchHomeActivityNoWait;
import static android.server.wm.UiDeviceUtils.pressUnlockButton;
import static android.server.wm.UiDeviceUtils.pressWakeupButton;
import static android.view.Display.DEFAULT_DISPLAY;
-import static androidx.test.InstrumentationRegistry.getInstrumentation;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static org.junit.Assert.assertEquals;
@@ -42,7 +42,7 @@
public void setupBase() {
pressWakeupButton();
pressUnlockButton();
- pressHomeButton();
+ launchHomeActivityNoWait();
}
static <T extends FocusableActivity> T startActivity(Class<T> cls) throws InterruptedException {
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowlessWmTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowlessWmTests.java
new file mode 100644
index 0000000..f061aeb
--- /dev/null
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowlessWmTests.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.wm;
+
+import static android.server.wm.UiDeviceUtils.pressHomeButton;
+import static android.server.wm.UiDeviceUtils.pressUnlockButton;
+import static android.server.wm.UiDeviceUtils.pressWakeupButton;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.SurfaceHolder;
+import android.view.SurfaceHolder.Callback;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+import android.view.WindowlessViewRoot;
+import android.widget.FrameLayout;
+import android.widget.Button;
+
+import android.view.SurfaceView;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.FlakyTest;
+import androidx.test.rule.ActivityTestRule;
+
+import com.android.compatibility.common.util.CtsTouchUtils;
+
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Ensure end-to-end functionality of the WindowlessWindowManager.
+ *
+ * Build/Install/Run:
+ * atest CtsWindowManagerDeviceTestCases:WindowlessWmTests
+ */
+@Presubmit
+@FlakyTest
+public class WindowlessWmTests implements SurfaceHolder.Callback {
+ private final ActivityTestRule<Activity> mActivityRule = new ActivityTestRule<>(Activity.class);
+
+ private Instrumentation mInstrumentation;
+ private Activity mActivity;
+ private SurfaceView mSurfaceView;
+
+ private WindowlessViewRoot mVr;
+ private View mEmbeddedView;
+
+ private boolean mClicked = false;
+
+ /*
+ * Configurable state to control how the surfaceCreated callback
+ * will initialize the embedded view hierarchy.
+ */
+ int mEmbeddedViewWidth = 100;
+ int mEmbeddedViewHeight = 100;
+
+ private static final int DEFAULT_SURFACE_VIEW_WIDTH = 100;
+ private static final int DEFAULT_SURFACE_VIEW_HEIGHT = 100;
+
+ @Before
+ public void setUp() {
+ pressWakeupButton();
+ pressUnlockButton();
+ pressHomeButton();
+
+ mClicked = false;
+
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mActivity = mActivityRule.launchActivity(null);
+ mInstrumentation.waitForIdleSync();
+ }
+
+ private void addSurfaceView(int width, int height) throws Throwable {
+ mActivityRule.runOnUiThread(() -> {
+ final FrameLayout content = new FrameLayout(mActivity);
+ mSurfaceView = new SurfaceView(mActivity);
+ mSurfaceView.setZOrderOnTop(true);
+ content.addView(mSurfaceView, new FrameLayout.LayoutParams(
+ width, height, Gravity.LEFT | Gravity.TOP));
+ mActivity.setContentView(content, new ViewGroup.LayoutParams(width, height));
+ mSurfaceView.getHolder().addCallback(this);
+ });
+ }
+
+ private void addViewToSurfaceView(SurfaceView sv, View v, int width, int height) {
+ mVr = new WindowlessViewRoot(mActivity, mActivity.getDisplay(),
+ sv.getSurfaceControl());
+ WindowManager.LayoutParams lp = new WindowManager.LayoutParams(width, height,
+ WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.OPAQUE);
+ mVr.addView(v, lp);
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ addViewToSurfaceView(mSurfaceView, mEmbeddedView,
+ mEmbeddedViewWidth, mEmbeddedViewHeight);
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width,
+ int height) {
+ }
+
+ @Test
+ public void testEmbeddedViewReceivesInput() throws Throwable {
+ mEmbeddedView = new Button(mActivity);
+ mEmbeddedView.setOnClickListener((View v) -> {
+ mClicked = true;
+ });
+
+ addSurfaceView(DEFAULT_SURFACE_VIEW_WIDTH, DEFAULT_SURFACE_VIEW_HEIGHT);
+ mInstrumentation.waitForIdleSync();
+
+ CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mActivityRule, mSurfaceView);
+ assertTrue(mClicked);
+ }
+
+ @Test
+ public void testEmbeddedViewResizes() throws Throwable {
+ mEmbeddedView = new Button(mActivity);
+ mEmbeddedView.setOnClickListener((View v) -> {
+ mClicked = true;
+ });
+
+ final int bigEdgeLength = mEmbeddedViewWidth * 3;
+
+ // We make the SurfaceView more than twice as big as the embedded view
+ // so that a touch in the middle of the SurfaceView won't land
+ // on the embedded view.
+ addSurfaceView(bigEdgeLength, bigEdgeLength);
+ mInstrumentation.waitForIdleSync();
+
+ CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mActivityRule, mSurfaceView);
+ assertFalse(mClicked);
+
+ mActivityRule.runOnUiThread(() -> {
+ WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ bigEdgeLength, bigEdgeLength,
+ WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.OPAQUE);
+ mVr.relayout(lp);
+ });
+ mInstrumentation.waitForIdleSync();
+
+ // But after the click should hit.
+ CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mActivityRule, mSurfaceView);
+ assertTrue(mClicked);
+ }
+}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/intent/Activities.java b/tests/framework/base/windowmanager/src/android/server/wm/intent/Activities.java
index 6b5600c..9ba003c 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/intent/Activities.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/intent/Activities.java
@@ -77,4 +77,10 @@
public static class LauncherActivity extends Activity {
}
+
+ public static class RelinquishTaskIdentityActivity extends Activity {
+ }
+
+ public static class TaskAffinity1RelinquishTaskIdentityActivity extends Activity {
+ }
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/intent/IntentGenerationTests.java b/tests/framework/base/windowmanager/src/android/server/wm/intent/IntentGenerationTests.java
index fcbd865..396ead6 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/intent/IntentGenerationTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/intent/IntentGenerationTests.java
@@ -63,6 +63,17 @@
*
* Build/Install/Run:
* atest CtsWindowManagerDeviceTestCases:IntentGenerationTests
+ *
+ *
+ * <p>NOTE: It is also possible to use this to manually verify a single test (useful for local
+ * debugging). To do that:
+ * 1. Put the correct test name in {@link #verifySingle()} and remove the @Ignore annotation
+ * 2. Push the file under test to device, e.g.
+ * adb shell mkdir /storage/emulated/0/Documents/relinquishTaskIdentity/
+ * adb push cts/tests/framework/base/windowmanager/intent_tests/relinquishTaskIdentity/test-0.json /storage/emulated/0/Documents/relinquishTaskIdentity/
+ * 3. Run the test
+ * atest CtsWindowManagerDeviceTestCases:IntentGenerationTests#verifySingle
+ * </p>
*/
public class IntentGenerationTests extends IntentTestBase {
private static final Cases CASES = new Cases();
@@ -106,9 +117,11 @@
* @throws JSONException if the file has invalid json in it.
*/
@Test
+ // Comment the following line to test locally
@Ignore
public void verifySingle() throws IOException, JSONException {
- String test = "forResult/test-1.json";
+ // Replace this line with the test you need
+ String test = "relinquishTaskIdentity/test-0.json";
TestCase testCase = readFromStorage(test);
mLaunchRunner.verify(mTargetContext, testCase);
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/intent/IntentTestBase.java b/tests/framework/base/windowmanager/src/android/server/wm/intent/IntentTestBase.java
index 887d599..a2eeac8 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/intent/IntentTestBase.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/intent/IntentTestBase.java
@@ -37,12 +37,12 @@
*/
public void cleanUp(List<ComponentName> activitiesInUsedInTest) throws Exception {
super.tearDown();
- UiDeviceUtils.pressHomeButton();
+ launchHomeActivityNoWait();
removeStacksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME);
this.getAmWmState().waitForWithAmState(
state -> state.containsNoneOf(activitiesInUsedInTest),
- "Waiting for activity to be removed");
+ "activity to be removed");
}
@After
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/intent/LaunchRunner.java b/tests/framework/base/windowmanager/src/android/server/wm/intent/LaunchRunner.java
index b7b34d8..9357e99 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/intent/LaunchRunner.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/intent/LaunchRunner.java
@@ -317,7 +317,7 @@
SystemClock.sleep(BEFORE_DUMP_TIMEOUT);
mTestBase.getAmWmState().waitForWithAmState(
am -> StateDump.fromStacks(am.getStacks(), mBaseStacks).equals(expected),
- "Wait until the activity states match up with what we recorded");
+ "the activity states match up with what we recorded");
mTestBase.getAmWmState().computeState(activity.getComponentName());
List<ActivityManagerState.ActivityStack> endStateStacks =
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleClientTestBase.java b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleClientTestBase.java
index d2bb3ef..67b4f7f 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleClientTestBase.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleClientTestBase.java
@@ -16,6 +16,9 @@
package android.server.wm.lifecycle;
+import static android.app.Activity.RESULT_OK;
+import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.server.wm.StateLogger.log;
import static android.server.wm.app.Components.PipActivity.EXTRA_ENTER_PIP;
import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_ACTIVITY_RESULT;
@@ -36,6 +39,7 @@
import static androidx.test.InstrumentationRegistry.getInstrumentation;
import android.app.Activity;
+import android.app.ActivityOptions;
import android.app.PictureInPictureParams;
import android.content.ComponentName;
import android.content.Context;
@@ -46,8 +50,13 @@
import android.os.Handler;
import android.server.wm.MultiDisplayTestBase;
import android.server.wm.lifecycle.LifecycleLog.ActivityCallback;
+import android.transition.Transition;
+import android.transition.TransitionListenerAdapter;
import android.util.Pair;
+import android.server.wm.cts.R;
+
+import androidx.annotation.Nullable;
import androidx.test.rule.ActivityTestRule;
import org.junit.Before;
@@ -58,8 +67,12 @@
public class ActivityLifecycleClientTestBase extends MultiDisplayTestBase {
static final String EXTRA_RECREATE = "recreate";
+ static final String EXTRA_FINISH_IN_ON_CREATE = "finish_in_on_create";
+ static final String EXTRA_FINISH_IN_ON_START = "finish_in_on_start";
static final String EXTRA_FINISH_IN_ON_RESUME = "finish_in_on_resume";
static final String EXTRA_FINISH_AFTER_RESUME = "finish_after_resume";
+ static final String EXTRA_FINISH_IN_ON_PAUSE = "finish_in_on_pause";
+ static final String EXTRA_FINISH_IN_ON_STOP = "finish_in_on_stop";
static final ComponentName CALLBACK_TRACKING_ACTIVITY =
getComponentName(CallbackTrackingActivity.class);
@@ -115,6 +128,12 @@
final ActivityTestRule mSlowActivityTestRule = new ActivityTestRule<>(
SlowActivity.class, true /* initialTouchMode */, false /* launchActivity */);
+ final ActivityTestRule mResultActivityTestRule = new ActivityTestRule(
+ ResultActivity.class, true /* initialTouchMode */, false /* launchActivity */);
+
+ final ActivityTestRule mNoDisplayActivityTestRule = new ActivityTestRule(
+ NoDisplayActivity.class, true /* initialTouchMode */, false /* launchActivity */);
+
private static LifecycleLog mLifecycleLog;
protected Context mTargetContext;
@@ -136,8 +155,16 @@
/** Launch an activity given a class. */
protected Activity launchActivity(Class<? extends Activity> activityClass) {
+ return launchActivity(activityClass, null /* options */);
+ }
+
+ /** Launch an activity given a class and options. */
+ protected Activity launchActivity(Class<? extends Activity> activityClass,
+ @Nullable ActivityOptions options) {
final Intent intent = new Intent(mTargetContext, activityClass);
- return getInstrumentation().startActivitySync(intent);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ return getInstrumentation().startActivitySync(intent,
+ options != null ? options.toBundle() : null);
}
/**
@@ -322,6 +349,10 @@
}
}
+ // Just another callback tracking activity, nothing special.
+ public static class SecondCallbackTrackingActivity extends CallbackTrackingActivity {
+ }
+
// Translucent callback tracking test activity
public static class TranslucentCallbackTrackingActivity extends CallbackTrackingActivity {
}
@@ -339,6 +370,10 @@
* Test activity that launches {@link ResultActivity} for result.
*/
public static class LaunchForResultActivity extends CallbackTrackingActivity {
+ public static String EXTRA_LAUNCH_ON_RESULT = "LAUNCH_ON_RESULT";
+ public static String EXTRA_LAUNCH_ON_RESUME_AFTER_RESULT = "LAUNCH_ON_RESUME_AFTER_RESULT";
+
+ boolean mReceivedResultOk;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -346,6 +381,24 @@
startForResult();
}
+ @Override
+ protected void onResume() {
+ super.onResume();
+ if (mReceivedResultOk
+ && getIntent().getBooleanExtra(EXTRA_LAUNCH_ON_RESUME_AFTER_RESULT, false)) {
+ startActivity(new Intent(this, CallbackTrackingActivity.class));
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ mReceivedResultOk = resultCode == RESULT_OK;
+ if (mReceivedResultOk && getIntent().getBooleanExtra(EXTRA_LAUNCH_ON_RESULT, false)) {
+ startActivity(new Intent(this, CallbackTrackingActivity.class));
+ }
+ }
+
private void startForResult() {
final Intent intent = new Intent(this, ResultActivity.class);
intent.putExtras(getIntent());
@@ -353,12 +406,28 @@
}
}
- /** Test activity that is started for result and finishes itself in ON_RESUME. */
+ /** Test activity that is started for result and finishes itself. */
public static class ResultActivity extends CallbackTrackingActivity {
@Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setResult(RESULT_OK);
+ if (getIntent().getBooleanExtra(EXTRA_FINISH_IN_ON_CREATE, false)) {
+ finish();
+ }
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ if (getIntent().getBooleanExtra(EXTRA_FINISH_IN_ON_START, false)) {
+ finish();
+ }
+ }
+
+ @Override
protected void onResume() {
super.onResume();
- setResult(RESULT_OK);
final Intent intent = getIntent();
if (intent.getBooleanExtra(EXTRA_FINISH_IN_ON_RESUME, false)) {
finish();
@@ -366,6 +435,41 @@
new Handler().postDelayed(() -> finish(), 2000);
}
}
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ if (getIntent().getBooleanExtra(EXTRA_FINISH_IN_ON_PAUSE, false)) {
+ finish();
+ }
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ if (getIntent().getBooleanExtra(EXTRA_FINISH_IN_ON_STOP, false)) {
+ finish();
+ }
+ }
+ }
+
+ /** Test activity with NoDisplay theme that can finish itself. */
+ public static class NoDisplayActivity extends ResultActivity {
+ static final String EXTRA_LAUNCH_ACTIVITY = "extra_launch_activity";
+ static final String EXTRA_NEW_TASK = "extra_new_task";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (getIntent().getBooleanExtra(EXTRA_LAUNCH_ACTIVITY, false)) {
+ final Intent intent = new Intent(this, CallbackTrackingActivity.class);
+ if (getIntent().getBooleanExtra(EXTRA_NEW_TASK, false)) {
+ intent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK);
+ }
+ startActivity(intent);
+ }
+ }
}
/** Test activity that can call {@link Activity#recreate()} if requested in a new intent. */
@@ -448,6 +552,41 @@
}
}
+ public static class DifferentAffinityActivity extends LifecycleTrackingActivity {
+ }
+
+ public static class TransitionSourceActivity extends LifecycleTrackingActivity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.transition_source_layout);
+ }
+
+ void launchActivityWithTransition() {
+ final ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this,
+ findViewById(R.id.transitionView), "sharedTransition");
+ final Intent intent = new Intent(this, TransitionDestinationActivity.class);
+ startActivity(intent, options.toBundle());
+ }
+ }
+
+ public static class TransitionDestinationActivity extends LifecycleTrackingActivity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.transition_destination_layout);
+ final Transition sharedElementEnterTransition =
+ getWindow().getSharedElementEnterTransition();
+ sharedElementEnterTransition.addListener(new TransitionListenerAdapter() {
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ super.onTransitionEnd(transition);
+ finishAfterTransition();
+ }
+ });
+ }
+ }
+
static ComponentName getComponentName(Class<? extends Activity> activity) {
return new ComponentName(getInstrumentation().getContext(), activity);
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleFreeformTests.java b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleFreeformTests.java
index 767097b..b5cbccd 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleFreeformTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleFreeformTests.java
@@ -243,7 +243,7 @@
public void testPreQTopProcessResumedActivityInFreeform() throws Exception {
// Resume app switches, so the activities that we are going to launch won't be deferred
// since Home activity was started in #setUp().
- SystemUtil.runWithShellPermissionIdentity(ActivityManager::resumeAppSwitches);
+ resumeAppSwitches();
final ActivityOptions launchOptions = ActivityOptions.makeBasic();
launchOptions.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleKeyguardTests.java b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleKeyguardTests.java
index 5ea5526..bd17ce7 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleKeyguardTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleKeyguardTests.java
@@ -31,7 +31,6 @@
import android.content.Intent;
import android.platform.test.annotations.Presubmit;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.MediumTest;
import org.junit.Test;
@@ -47,7 +46,6 @@
public class ActivityLifecycleKeyguardTests extends ActivityLifecycleClientTestBase {
@Test
- @FlakyTest(bugId = 131005232)
public void testSingleLaunch() throws Exception {
assumeTrue(supportsSecureLock());
try (final LockScreenSession lockScreenSession = new LockScreenSession()) {
@@ -61,7 +59,6 @@
}
@Test
- @FlakyTest(bugId = 131005232)
public void testKeyguardShowHide() throws Exception {
assumeTrue(supportsSecureLock());
@@ -84,7 +81,6 @@
}
@Test
- @FlakyTest(bugId = 131005232)
public void testKeyguardShowHideOverSplitScreen() throws Exception {
assumeTrue(supportsSecureLock());
assumeTrue(supportsSplitScreenMultiWindow());
@@ -124,7 +120,6 @@
}
@Test
- @FlakyTest(bugId = 131005232)
public void testKeyguardShowHideOverPip() throws Exception {
if (!supportsPip()) {
// Skipping test: no Picture-In-Picture support
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecyclePipTests.java b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecyclePipTests.java
index 9024c9a..0d0278e 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecyclePipTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecyclePipTests.java
@@ -37,7 +37,6 @@
import android.content.Intent;
import android.platform.test.annotations.Presubmit;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.MediumTest;
import org.junit.Before;
@@ -50,7 +49,6 @@
* Build/Install/Run:
* atest CtsWindowManagerDeviceTestCases:ActivityLifecyclePipTests
*/
-@FlakyTest(bugId = 77652261)
@MediumTest
@Presubmit
public class ActivityLifecyclePipTests extends ActivityLifecycleClientTestBase {
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleSplitScreenTests.java b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleSplitScreenTests.java
index 64e34fb..5ec2c11 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleSplitScreenTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleSplitScreenTests.java
@@ -70,8 +70,8 @@
assumeTrue(supportsSplitScreenMultiWindow());
}
+ @FlakyTest(bugId = 137329632)
@Test
- @FlakyTest(bugId = 131005232)
public void testResumedWhenRecreatedFromInNonFocusedStack() throws Exception {
// Launch first activity
final Activity firstActivity =
@@ -93,17 +93,25 @@
getLifecycleLog().clear();
// Start an activity in separate task (will be placed in secondary stack)
- getLaunchActivityBuilder().execute();
+ final Intent thirdIntent = new Intent();
+ thirdIntent.setFlags(FLAG_ACTIVITY_MULTIPLE_TASK | FLAG_ACTIVITY_NEW_TASK);
+ mThirdActivityTestRule.launchActivity(thirdIntent);
+
+ // Wait for SecondActivity in primary split screen leave minimize dock.
+ waitAndAssertActivityStates(state(secondActivity, ON_RESUME));
// Finish top activity
secondActivity.finish();
- waitAndAssertActivityStates(state(secondActivity, ON_DESTROY));
- waitAndAssertActivityStates(state(firstActivity, ON_RESUME));
+ waitAndAssertActivityStates(state(secondActivity, ON_DESTROY),
+ state(firstActivity, ON_RESUME));
// Verify that the first activity was recreated to resume as it was created before
// windowing mode was switched
LifecycleVerifier.assertRecreateAndResumeSequence(FirstActivity.class, getLifecycleLog());
+
+ // Verify that the lifecycle state did not change for activity in non-focused stack
+ LifecycleVerifier.assertLaunchSequence(ThirdActivity.class, getLifecycleLog());
}
@Test
@@ -353,8 +361,7 @@
waitForActivityTransitions(CallbackTrackingActivity.class, expectedEnterSequence);
LifecycleVerifier.assertOrder(getLifecycleLog(), CallbackTrackingActivity.class,
Arrays.asList(ON_TOP_POSITION_LOST, ON_PAUSE, ON_STOP, ON_DESTROY, ON_CREATE,
- ON_RESUME, ON_TOP_POSITION_GAINED, ON_TOP_POSITION_LOST),
- "moveToSplitScreen");
+ ON_RESUME), "moveToSplitScreen");
LifecycleVerifier.assertTransitionObserved(getLifecycleLog(),
transition(CallbackTrackingActivity.class, ON_MULTI_WINDOW_MODE_CHANGED),
"moveToSplitScreen");
@@ -395,9 +402,9 @@
// Wait for the activity to receive the change
waitForActivityTransitions(ConfigChangeHandlingActivity.class,
- Arrays.asList(ON_MULTI_WINDOW_MODE_CHANGED, ON_TOP_POSITION_LOST));
+ Arrays.asList(ON_TOP_POSITION_LOST, ON_MULTI_WINDOW_MODE_CHANGED));
LifecycleVerifier.assertOrder(getLifecycleLog(), ConfigChangeHandlingActivity.class,
- Arrays.asList(ON_MULTI_WINDOW_MODE_CHANGED, ON_TOP_POSITION_LOST),
+ Arrays.asList(ON_TOP_POSITION_LOST, ON_MULTI_WINDOW_MODE_CHANGED),
"moveToSplitScreen");
// Exit split-screen
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleTests.java b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleTests.java
index 1df98db..cf8e125 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleTests.java
@@ -21,6 +21,10 @@
import static android.server.wm.ActivityManagerState.STATE_PAUSED;
import static android.server.wm.ActivityManagerState.STATE_STOPPED;
import static android.server.wm.UiDeviceUtils.pressBackButton;
+import static android.server.wm.lifecycle.ActivityLifecycleClientTestBase.LaunchForResultActivity.EXTRA_LAUNCH_ON_RESULT;
+import static android.server.wm.lifecycle.ActivityLifecycleClientTestBase.LaunchForResultActivity.EXTRA_LAUNCH_ON_RESUME_AFTER_RESULT;
+import static android.server.wm.lifecycle.ActivityLifecycleClientTestBase.NoDisplayActivity.EXTRA_LAUNCH_ACTIVITY;
+import static android.server.wm.lifecycle.ActivityLifecycleClientTestBase.NoDisplayActivity.EXTRA_NEW_TASK;
import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_ACTIVITY_RESULT;
import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_CREATE;
import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_DESTROY;
@@ -34,6 +38,7 @@
import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_TOP_POSITION_GAINED;
import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_TOP_POSITION_LOST;
import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.PRE_ON_CREATE;
+import static android.server.wm.lifecycle.LifecycleVerifier.transition;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
@@ -51,8 +56,10 @@
import androidx.test.filters.FlakyTest;
import androidx.test.filters.MediumTest;
+import androidx.test.rule.ActivityTestRule;
import com.android.compatibility.common.util.AmUtils;
+import com.android.compatibility.common.util.SystemUtil;
import org.junit.Test;
@@ -63,7 +70,6 @@
* Build/Install/Run:
* atest CtsWindowManagerDeviceTestCases:ActivityLifecycleTests
*/
-@FlakyTest(bugId = 77652261)
@MediumTest
@Presubmit
public class ActivityLifecycleTests extends ActivityLifecycleClientTestBase {
@@ -273,6 +279,85 @@
Arrays.asList(ON_RESUME), "secondDestroy");
}
+ @FlakyTest(bugId=137329632)
+ @Test
+ public void testFinishBottom() throws Exception {
+ final Activity bottomActivity = mFirstActivityTestRule.launchActivity(new Intent());
+ final Activity topActivity = mSecondActivityTestRule.launchActivity(new Intent());
+ waitAndAssertActivityStates(state(bottomActivity, ON_STOP),
+ state(topActivity, ON_RESUME));
+
+ // Finish the activity on the bottom
+ getLifecycleLog().clear();
+ bottomActivity.finish();
+
+ // Assert that activity on the bottom went directly to destroyed state, and activity on top
+ // did not get any lifecycle changes.
+ waitAndAssertActivityStates(state(bottomActivity, ON_DESTROY));
+ LifecycleVerifier.assertSequence(FirstActivity.class, getLifecycleLog(),
+ Arrays.asList(ON_DESTROY), "destroyOnBottom");
+ LifecycleVerifier.assertEmptySequence(SecondActivity.class, getLifecycleLog(),
+ "destroyOnBottom");
+ }
+
+ @FlakyTest(bugId=137329632)
+ @Test
+ public void testFinishAndLaunchOnResult() throws Exception {
+ testLaunchForResultAndLaunchAfterResultSequence(EXTRA_LAUNCH_ON_RESULT);
+ }
+
+ @FlakyTest(bugId=137329632)
+ @Test
+ public void testFinishAndLaunchAfterOnResultInOnResume() throws Exception {
+ testLaunchForResultAndLaunchAfterResultSequence(EXTRA_LAUNCH_ON_RESUME_AFTER_RESULT);
+ }
+
+ /**
+ * This triggers launch of an activity for result, which immediately finishes. After receiving
+ * result new activity launch is triggered automatically.
+ * @see android.server.wm.lifecycle.ActivityLifecycleClientTestBase.LaunchForResultActivity
+ */
+ private void testLaunchForResultAndLaunchAfterResultSequence(String flag) {
+ final Intent intent = new Intent();
+ intent.putExtra(flag, true);
+ intent.putExtra(EXTRA_FINISH_IN_ON_RESUME, true);
+ mLaunchForResultActivityTestRule.launchActivity(intent);
+
+ waitAndAssertActivityStates(state(CallbackTrackingActivity.class, ON_TOP_POSITION_GAINED),
+ state(ResultActivity.class, ON_DESTROY),
+ state(LaunchForResultActivity.class, ON_STOP));
+ LifecycleVerifier.assertOrder(getLifecycleLog(), Arrays.asList(
+ // Base launching activity starting.
+ transition(LaunchForResultActivity.class, PRE_ON_CREATE),
+ transition(LaunchForResultActivity.class, ON_CREATE),
+ transition(LaunchForResultActivity.class, ON_START),
+ transition(LaunchForResultActivity.class, ON_POST_CREATE),
+ transition(LaunchForResultActivity.class, ON_RESUME),
+ transition(LaunchForResultActivity.class, ON_TOP_POSITION_GAINED),
+ // An activity is automatically launched for result.
+ transition(LaunchForResultActivity.class, ON_TOP_POSITION_LOST),
+ transition(LaunchForResultActivity.class, ON_PAUSE),
+ transition(ResultActivity.class, PRE_ON_CREATE),
+ transition(ResultActivity.class, ON_CREATE),
+ transition(ResultActivity.class, ON_START),
+ transition(ResultActivity.class, ON_RESUME),
+ transition(ResultActivity.class, ON_TOP_POSITION_GAINED),
+ // Activity that was launched for result is finished automatically - the base
+ // launching activity is brought to front.
+ transition(LaunchForResultActivity.class, ON_ACTIVITY_RESULT),
+ transition(LaunchForResultActivity.class, ON_RESUME),
+ transition(LaunchForResultActivity.class, ON_TOP_POSITION_GAINED),
+ // New activity is launched after receiving result in base activity.
+ transition(LaunchForResultActivity.class, ON_TOP_POSITION_LOST),
+ transition(LaunchForResultActivity.class, ON_PAUSE),
+ transition(CallbackTrackingActivity.class, PRE_ON_CREATE),
+ transition(CallbackTrackingActivity.class, ON_CREATE),
+ transition(CallbackTrackingActivity.class, ON_START),
+ transition(CallbackTrackingActivity.class, ON_RESUME),
+ transition(CallbackTrackingActivity.class, ON_TOP_POSITION_GAINED)),
+ "launchForResultAndLaunchAfterOnResult");
+ }
+
@Test
public void testLaunchAndDestroy() throws Exception {
final Activity activity = mFirstActivityTestRule.launchActivity(new Intent());
@@ -283,6 +368,53 @@
LifecycleVerifier.assertLaunchAndDestroySequence(FirstActivity.class, getLifecycleLog());
}
+ @FlakyTest(bugId = 139808754)
+ @Test
+ public void testTrampoline() throws Exception {
+ testTrampolineLifecycle(false /* newTask */);
+ }
+
+ @FlakyTest(bugId = 139808754)
+ @Test
+ public void testTrampolineNewTask() throws Exception {
+ testTrampolineLifecycle(true /* newTask */);
+ }
+
+ /**
+ * Verifies that activity start from a trampoline will have the correct lifecycle and complete
+ * in time. The expected lifecycle is that the trampoline will skip ON_START - ON_STOP part of
+ * the usual sequence, and will go straight to ON_DESTROY after creation.
+ */
+ private void testTrampolineLifecycle(boolean newTask) {
+ // Run activity start manually (without using instrumentation) to make it async and measure
+ // time from the request correctly.
+ final Intent newTaskIntent = new Intent(mTargetContext, NoDisplayActivity.class);
+ newTaskIntent.putExtra(EXTRA_LAUNCH_ACTIVITY, true);
+ newTaskIntent.putExtra(EXTRA_FINISH_IN_ON_CREATE, true);
+ if (newTask) {
+ newTaskIntent.putExtra(EXTRA_NEW_TASK, true);
+ }
+ newTaskIntent.addFlags(FLAG_ACTIVITY_NEW_TASK);
+ // Using Shell permission identity to allow task switching and avoid delay.
+ SystemUtil.runWithShellPermissionIdentity(
+ () -> mTargetContext.startActivity(newTaskIntent)
+ );
+ waitAndAssertActivityStates(state(NoDisplayActivity.class, ON_DESTROY),
+ state(CallbackTrackingActivity.class, ON_TOP_POSITION_GAINED));
+
+ LifecycleVerifier.assertEntireSequence(Arrays.asList(
+ transition(NoDisplayActivity.class, PRE_ON_CREATE),
+ transition(NoDisplayActivity.class, ON_CREATE),
+ transition(CallbackTrackingActivity.class, PRE_ON_CREATE),
+ transition(CallbackTrackingActivity.class, ON_CREATE),
+ transition(CallbackTrackingActivity.class, ON_START),
+ transition(CallbackTrackingActivity.class, ON_POST_CREATE),
+ transition(CallbackTrackingActivity.class, ON_RESUME),
+ transition(CallbackTrackingActivity.class, ON_TOP_POSITION_GAINED),
+ transition(NoDisplayActivity.class, ON_DESTROY)),
+ getLifecycleLog(), "trampolineLaunch");
+ }
+
@Test
public void testRelaunchResumed() throws Exception {
final Activity activity = mFirstActivityTestRule.launchActivity(new Intent());
@@ -730,4 +862,91 @@
LifecycleVerifier.assertSequence(SingleTopActivity.class, getLifecycleLog(),
expectedSequence, "newIntent");
}
+
+ @Test
+ public void testFinishInOnCreate() throws Exception {
+ verifyFinishAtStage(mResultActivityTestRule, ResultActivity.class,
+ EXTRA_FINISH_IN_ON_CREATE, Arrays.asList(PRE_ON_CREATE, ON_CREATE, ON_DESTROY),
+ "onCreate");
+ }
+
+ @Test
+ public void testFinishInOnCreateNoDisplay() throws Exception {
+ verifyFinishAtStage(mNoDisplayActivityTestRule, NoDisplayActivity.class,
+ EXTRA_FINISH_IN_ON_CREATE, Arrays.asList(PRE_ON_CREATE, ON_CREATE, ON_DESTROY),
+ "onCreate");
+ }
+
+ @Test
+ public void testFinishInOnStart() throws Exception {
+ verifyFinishAtStage(mResultActivityTestRule, ResultActivity.class,
+ EXTRA_FINISH_IN_ON_START, Arrays.asList(PRE_ON_CREATE, ON_CREATE, ON_START,
+ ON_POST_CREATE, ON_STOP, ON_DESTROY), "onStart");
+ }
+
+ @Test
+ public void testFinishInOnStartNoDisplay() throws Exception {
+ verifyFinishAtStage(mNoDisplayActivityTestRule, NoDisplayActivity.class,
+ EXTRA_FINISH_IN_ON_START, Arrays.asList(PRE_ON_CREATE, ON_CREATE, ON_START,
+ ON_POST_CREATE, ON_STOP, ON_DESTROY), "onStart");
+ }
+
+ @Test
+ public void testFinishInOnResume() throws Exception {
+ verifyFinishAtStage(mResultActivityTestRule, ResultActivity.class,
+ EXTRA_FINISH_IN_ON_RESUME, Arrays.asList(PRE_ON_CREATE, ON_CREATE, ON_START,
+ ON_POST_CREATE, ON_RESUME, ON_TOP_POSITION_GAINED, ON_TOP_POSITION_LOST,
+ ON_PAUSE, ON_STOP, ON_DESTROY), "onResume");
+ }
+
+ @Test
+ public void testFinishInOnResumeNoDisplay() throws Exception {
+ verifyFinishAtStage(mNoDisplayActivityTestRule, NoDisplayActivity.class,
+ EXTRA_FINISH_IN_ON_RESUME, Arrays.asList(PRE_ON_CREATE, ON_CREATE, ON_START,
+ ON_POST_CREATE, ON_RESUME, ON_TOP_POSITION_GAINED, ON_TOP_POSITION_LOST,
+ ON_PAUSE, ON_STOP, ON_DESTROY), "onResume");
+ }
+
+ private void verifyFinishAtStage(ActivityTestRule rule, Class<? extends Activity> activityClass,
+ String finishStageExtra, List<LifecycleLog.ActivityCallback> expectedSequence,
+ String stageName) {
+ final Intent intent = new Intent();
+ intent.putExtra(finishStageExtra, true);
+ rule.launchActivity(intent);
+
+ waitAndAssertActivityTransitions(activityClass, expectedSequence, "finish in " + stageName);
+ }
+
+ @Test
+ public void testFinishInOnPause() throws Exception {
+ verifyFinishAtStage(mResultActivityTestRule, ResultActivity.class,
+ EXTRA_FINISH_IN_ON_PAUSE, "onPause", mTranslucentActivityTestRule);
+ }
+
+ @Test
+ public void testFinishInOnStop() throws Exception {
+ verifyFinishAtStage(mResultActivityTestRule, ResultActivity.class,
+ EXTRA_FINISH_IN_ON_STOP, "onStop", mFirstActivityTestRule);
+ }
+
+ private void verifyFinishAtStage(ActivityTestRule rule, Class<? extends Activity> activityClass,
+ String finishStageExtra, String stageName, ActivityTestRule launchOnTopRule) {
+ final Intent intent = new Intent();
+ intent.putExtra(finishStageExtra, true);
+
+ // Activity will finish itself after onResume, so need to launch an extra activity on
+ // top to get it there.
+ final Activity testActivity = rule.launchActivity(intent);
+
+ // Wait for the activity to resume and gain top position
+ waitAndAssertActivityStates(state(testActivity, ON_TOP_POSITION_GAINED));
+
+ // Launch an activity on top, which will make the first one paused or stopped.
+ launchOnTopRule.launchActivity(new Intent());
+
+ final List<LifecycleLog.ActivityCallback> expectedSequence =
+ LifecycleVerifier.getLaunchAndDestroySequence(activityClass);
+ waitAndAssertActivityTransitions(activityClass, expectedSequence, "finish in " + stageName);
+
+ }
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleTopResumedStateTests.java b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleTopResumedStateTests.java
index b047127..92c5251 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleTopResumedStateTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleTopResumedStateTests.java
@@ -16,9 +16,11 @@
package android.server.wm.lifecycle;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_REORDER_TO_FRONT;
import static android.server.wm.UiDeviceUtils.pressHomeButton;
import static android.server.wm.app.Components.PipActivity.EXTRA_ENTER_PIP;
import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_ACTIVITY_RESULT;
@@ -68,7 +70,6 @@
* Build/Install/Run:
* atest CtsWindowManagerDeviceTestCases:ActivityLifecycleTopResumedStateTests
*/
-@FlakyTest(bugId = 117135575)
@MediumTest
@Presubmit
public class ActivityLifecycleTopResumedStateTests extends ActivityLifecycleClientTestBase {
@@ -444,15 +445,13 @@
waitAndAssertActivityStates(state(singleTopActivity, ON_TOP_POSITION_GAINED),
state(topActivity, ON_DESTROY));
- LifecycleVerifier.assertEntireSequence(Arrays.asList(
+ LifecycleVerifier.assertOrder(getLifecycleLog(), Arrays.asList(
transition(TranslucentCallbackTrackingActivity.class, ON_TOP_POSITION_LOST),
transition(TranslucentCallbackTrackingActivity.class, ON_PAUSE),
transition(SingleTopActivity.class, ON_NEW_INTENT),
transition(SingleTopActivity.class, ON_RESUME),
- transition(SingleTopActivity.class, ON_TOP_POSITION_GAINED),
- transition(TranslucentCallbackTrackingActivity.class, ON_STOP),
- transition(TranslucentCallbackTrackingActivity.class, ON_DESTROY)),
- getLifecycleLog(), "Single top resolution sequence must match");
+ transition(SingleTopActivity.class, ON_TOP_POSITION_GAINED)),
+ "Single top resolution sequence must match");
}
@Test
@@ -988,6 +987,93 @@
}
}
+ @FlakyTest(bugId=137329632)
+ @Test
+ public void testFinishOnDifferentDisplay_nonFocused() throws Exception {
+ assumeTrue(supportsMultiDisplay());
+
+ // Launch activity on some display.
+ final Activity callbackTrackingActivity =
+ mCallbackTrackingActivityTestRule.launchActivity(new Intent());
+
+ waitAndAssertTopResumedActivity(getComponentName(CallbackTrackingActivity.class),
+ DEFAULT_DISPLAY, "Activity launched on default display must be focused");
+
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ // Create new simulated display
+ final ActivityManagerState.ActivityDisplay newDisplay
+ = virtualDisplaySession.setSimulateDisplay(true).createDisplay();
+
+ // Launch another activity on new secondary display.
+ getLifecycleLog().clear();
+ final ActivityOptions launchOptions = ActivityOptions.makeBasic();
+ launchOptions.setLaunchDisplayId(newDisplay.mId);
+ final Intent newDisplayIntent = new Intent(mContext, SingleTopActivity.class);
+ newDisplayIntent.setFlags(FLAG_ACTIVITY_NEW_TASK);
+ mTargetContext.startActivity(newDisplayIntent, launchOptions.toBundle());
+ waitAndAssertTopResumedActivity(getComponentName(SingleTopActivity.class),
+ newDisplay.mId, "Activity launched on secondary display must be focused");
+
+ // Finish the activity on the default display
+ getLifecycleLog().clear();
+ callbackTrackingActivity.finish();
+
+ // Verify that activity was actually destroyed
+ waitAndAssertActivityStates(state(CallbackTrackingActivity.class, ON_DESTROY));
+ // Verify that the activity on a different display lost the top focused state
+ LifecycleVerifier.assertSequence(SingleTopActivity.class, getLifecycleLog(),
+ Arrays.asList(ON_TOP_POSITION_LOST), "destructionOnDifferentDisplay");
+ }
+ }
+
+ @FlakyTest(bugId=137329632)
+ @Test
+ public void testFinishOnDifferentDisplay_focused() throws Exception {
+ assumeTrue(supportsMultiDisplay());
+
+ // Launch activity on some display.
+ final Activity bottomActivity = mSecondActivityTestRule.launchActivity(new Intent());
+ final Activity callbackTrackingActivity =
+ mCallbackTrackingActivityTestRule.launchActivity(new Intent());
+
+ waitAndAssertTopResumedActivity(getComponentName(CallbackTrackingActivity.class),
+ DEFAULT_DISPLAY, "Activity launched on default display must be focused");
+
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ // Create new simulated display
+ final ActivityManagerState.ActivityDisplay newDisplay
+ = virtualDisplaySession.setSimulateDisplay(true).createDisplay();
+
+ // Launch another activity on new secondary display.
+ getLifecycleLog().clear();
+ final ActivityOptions launchOptions = ActivityOptions.makeBasic();
+ launchOptions.setLaunchDisplayId(newDisplay.mId);
+ final Intent newDisplayIntent = new Intent(mContext, SingleTopActivity.class);
+ newDisplayIntent.setFlags(FLAG_ACTIVITY_NEW_TASK);
+ mTargetContext.startActivity(newDisplayIntent, launchOptions.toBundle());
+ waitAndAssertTopResumedActivity(getComponentName(SingleTopActivity.class),
+ newDisplay.mId, "Activity launched on secondary display must be focused");
+
+ // Bring the focus back
+ final Intent sameInstanceIntent = new Intent(mContext, CallbackTrackingActivity.class);
+ sameInstanceIntent.setFlags(FLAG_ACTIVITY_REORDER_TO_FRONT);
+ bottomActivity.startActivity(sameInstanceIntent, null);
+ waitAndAssertActivityStates(
+ state(CallbackTrackingActivity.class, ON_TOP_POSITION_GAINED));
+
+ // Finish the focused activity
+ getLifecycleLog().clear();
+ callbackTrackingActivity.finish();
+
+ // Verify that lifecycle of the activity on a different display did not change.
+ // Top resumed state will be given to home activity on that display.
+ waitAndAssertActivityStates(state(CallbackTrackingActivity.class, ON_DESTROY),
+ state(SecondActivity.class, ON_RESUME));
+ LifecycleVerifier.assertEmptySequence(SingleTopActivity.class, getLifecycleLog(),
+ "destructionOnDifferentDisplay");
+ }
+ }
+
@Test
public void testTopPositionNotSwitchedToPip() throws Exception {
assumeTrue(supportsPip());
@@ -1005,8 +1091,21 @@
// Wait and assert lifecycle
waitAndAssertActivityStates(state(pipActivity, ON_PAUSE));
- // PipMenuActivity will start and briefly get the top position, so we ignore the rest
- // of the possibilities.
+
+ // The PipMenuActivity could start anytime after moving pipActivity to pinned stack,
+ // however, we cannot control when would it start or finish, so this test could fail when
+ // PipMenuActivity just start and pipActivity call finish almost at the same time.
+ // So the strategy here is to wait the PipMenuActivity start and finish after pipActivity
+ // moved to pinned stack and paused, because pipActivity is not focusable but the
+ // PipMenuActivity is focusable, when the pinned stack gain top focus means the
+ // PipMenuActivity is launched and resumed, then when pinned stack lost top focus means the
+ // PipMenuActivity is finished.
+ mAmWmState.waitWindowingModeTopFocus(WINDOWING_MODE_PINNED, true /* topFocus */
+ , "wait PipMenuActivity get top focus");
+ mAmWmState.waitWindowingModeTopFocus(WINDOWING_MODE_PINNED, false /* topFocus */
+ , "wait PipMenuActivity lost top focus");
+ waitAndAssertActivityStates(state(activity, ON_TOP_POSITION_GAINED));
+
LifecycleVerifier.assertOrder(getLifecycleLog(), Arrays.asList(
transition(CallbackTrackingActivity.class, ON_TOP_POSITION_LOST),
transition(CallbackTrackingActivity.class, ON_PAUSE),
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityStarterTests.java b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityStarterTests.java
index b6fb802..86c3d93 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityStarterTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityStarterTests.java
@@ -31,12 +31,16 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.os.Bundle;
import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.FlakyTest;
+
import android.server.wm.ActivityLauncher;
import org.junit.Test;
@@ -206,6 +210,7 @@
* - Instance of single task activity is only one in its task.
*/
@Test
+ @FlakyTest(bugId = 127741025)
public void testLaunchSingleTaskActivity() {
// Launch a standard activity.
launchActivity(STANDARD_ACTIVITY);
@@ -354,11 +359,9 @@
mAmWmState.getAmState().getTaskByActivity(STANDARD_ACTIVITY).getTaskId());
// Make sure the second standard activity is finished.
final String waitFinishMsg = "Instance of second standard activity must not exist";
- mAmWmState.waitForWithAmState((amState) ->
- 0 == amState.getActivityCountInTask(taskId, SECOND_STANDARD_ACTIVITY),
- waitFinishMsg);
- assertEquals(waitFinishMsg, 0,
- mAmWmState.getAmState().getActivityCountInTask(taskId, SECOND_STANDARD_ACTIVITY));
+ assertTrue(waitFinishMsg, mAmWmState.waitForWithAmState(
+ amState -> 0 == amState.getActivityCountInTask(taskId, SECOND_STANDARD_ACTIVITY),
+ waitFinishMsg));
}
@Test
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityTests.java b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityTests.java
new file mode 100644
index 0000000..f30ed4e
--- /dev/null
+++ b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityTests.java
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.server.wm.lifecycle;
+
+import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_DESTROY;
+import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_PAUSE;
+import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_RESTART;
+import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_RESUME;
+import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_START;
+import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_STOP;
+import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_TOP_POSITION_GAINED;
+import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_TOP_POSITION_LOST;
+import static android.server.wm.lifecycle.LifecycleVerifier.transition;
+
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.MediumTest;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+
+/**
+ * Tests for {@link Activity} class APIs.
+ *
+ * Build/Install/Run:
+ * atest CtsWindowManagerDeviceTestCases:ActivityTests
+ */
+@Presubmit
+@MediumTest
+@FlakyTest(bugId=137329632)
+public class ActivityTests extends ActivityLifecycleClientTestBase {
+ @Test
+ public void testReleaseActivityInstance_visible() {
+ final Activity activity = launchActivity(FirstActivity.class);
+ waitAndAssertActivityStates(state(activity, ON_RESUME));
+
+ getLifecycleLog().clear();
+ assertFalse("Launched and visible activity must be released", activity.releaseInstance());
+ LifecycleVerifier.assertEmptySequence(FirstActivity.class, getLifecycleLog(),
+ "tryReleaseInstance");
+ }
+
+ @Test
+ public void testReleaseActivityInstance_invisible() {
+ // Launch two activities - second one to cover the first one and make it invisible.
+ final Activity firstActivity = launchActivity(FirstActivity.class);
+ final Activity secondActivity = launchActivity(SecondActivity.class);
+ waitAndAssertActivityStates(state(secondActivity, ON_RESUME),
+ state(firstActivity, ON_STOP));
+ // Wait for activity to report saved state to the server.
+ getInstrumentation().waitForIdleSync();
+
+ // Release the instance of the non-visible activity below.
+ getLifecycleLog().clear();
+ assertTrue("It must be possible to release an instance of an invisible activity",
+ firstActivity.releaseInstance());
+ waitAndAssertActivityStates(state(firstActivity, ON_DESTROY));
+ LifecycleVerifier.assertEmptySequence(SecondActivity.class, getLifecycleLog(),
+ "releaseInstance");
+
+ // Finish the top activity to navigate back to the first one and re-create it.
+ getLifecycleLog().clear();
+ secondActivity.finish();
+ waitAndAssertActivityStates(state(secondActivity, ON_DESTROY));
+ LifecycleVerifier.assertLaunchSequence(FirstActivity.class, getLifecycleLog());
+ }
+
+ /**
+ * Verify that {@link Activity#finishAndRemoveTask()} removes all activities in task if called
+ * for root of task.
+ */
+ @FlakyTest(bugId=137329632)
+ @Test
+ public void testFinishTask_FromRoot() throws Exception {
+ final Class<? extends Activity> rootActivityClass = CallbackTrackingActivity.class;
+ final Activity rootActivity = launchActivity(rootActivityClass);
+ final Class<? extends Activity> topActivityClass = SecondCallbackTrackingActivity.class;
+ final Activity topActivity = launchActivity(topActivityClass);
+ waitAndAssertActivityStates(state(rootActivity, ON_STOP),
+ state(topActivity, ON_TOP_POSITION_GAINED));
+
+ getLifecycleLog().clear();
+ rootActivity.finishAndRemoveTask();
+
+ waitAndAssertActivityStates(state(rootActivity, ON_DESTROY),
+ state(topActivity, ON_DESTROY));
+ // Cannot guarantee exact sequence among top and bottom activities, so verifying
+ // independently
+ LifecycleVerifier.assertSequence(rootActivityClass, getLifecycleLog(),
+ Arrays.asList(ON_DESTROY), "finishAndRemoveTask");
+ LifecycleVerifier.assertSequence(topActivityClass, getLifecycleLog(),
+ Arrays.asList(ON_TOP_POSITION_LOST, ON_PAUSE, ON_STOP, ON_DESTROY),
+ "finishAndRemoveTask");
+ }
+
+ /**
+ * Verify that {@link Activity#finishAndRemoveTask()} removes all activities in task if called
+ * for root of task. This version verifies lifecycle when top activity is translucent
+ */
+ @FlakyTest(bugId=137329632)
+ @Test
+ public void testFinishTask_FromRoot_TranslucentOnTop() throws Exception {
+ final Class<? extends Activity> rootActivityClass = CallbackTrackingActivity.class;
+ final Activity rootActivity = launchActivity(rootActivityClass);
+ final Class<? extends Activity> topActivityClass =
+ TranslucentCallbackTrackingActivity.class;
+ final Activity topActivity = launchActivity(topActivityClass);
+ waitAndAssertActivityStates(state(rootActivity, ON_PAUSE),
+ state(topActivity, ON_TOP_POSITION_GAINED));
+
+ getLifecycleLog().clear();
+ rootActivity.finishAndRemoveTask();
+
+ waitAndAssertActivityStates(state(rootActivity, ON_DESTROY),
+ state(topActivity, ON_DESTROY));
+ // Cannot guarantee exact sequence among top and bottom activities, so verifying
+ // independently
+ LifecycleVerifier.assertSequence(rootActivityClass, getLifecycleLog(),
+ Arrays.asList(ON_STOP, ON_DESTROY), "finishAndRemoveTask");
+ LifecycleVerifier.assertSequence(topActivityClass, getLifecycleLog(),
+ Arrays.asList(ON_TOP_POSITION_LOST, ON_PAUSE, ON_STOP, ON_DESTROY),
+ "finishAndRemoveTask");
+ }
+
+ /**
+ * Verify that {@link Activity#finishAndRemoveTask()} only removes one activity in task if
+ * called not for root of task.
+ */
+ @FlakyTest(bugId=137329632)
+ @Test
+ public void testFinishTask_NotFromRoot() throws Exception {
+ final Class<? extends Activity> rootActivityClass = CallbackTrackingActivity.class;
+ final Activity rootActivity = launchActivity(rootActivityClass);
+ final Class<? extends Activity> midActivityClass = SecondActivity.class;
+ final Activity midActivity = launchActivity(midActivityClass);
+ final Class<? extends Activity> topActivityClass = SecondCallbackTrackingActivity.class;
+ final Activity topActivity = launchActivity(topActivityClass);
+ waitAndAssertActivityStates(state(rootActivity, ON_STOP), state(midActivity, ON_STOP),
+ state(topActivity, ON_TOP_POSITION_GAINED));
+
+ getLifecycleLog().clear();
+ midActivity.finishAndRemoveTask();
+
+ waitAndAssertActivityStates(state(midActivity, ON_DESTROY));
+ LifecycleVerifier.assertEntireSequence(Arrays.asList(
+ transition(midActivityClass, ON_DESTROY)), getLifecycleLog(),
+ "finishAndRemoveTask");
+ }
+
+ /**
+ * Verify the lifecycle of {@link Activity#finishAfterTransition()} for activity that has a
+ * transition set.
+ */
+ @FlakyTest(bugId=137329632)
+ @Test
+ public void testFinishAfterTransition() throws Exception {
+ final TransitionSourceActivity rootActivity =
+ (TransitionSourceActivity) launchActivity(TransitionSourceActivity.class);
+ waitAndAssertActivityStates(state(rootActivity, ON_RESUME));
+
+ // Launch activity with configured shared element transition. It will call
+ // finishAfterTransition() on its own after transition completes.
+ rootActivity.runOnUiThread(() -> rootActivity.launchActivityWithTransition());
+ waitAndAssertActivityStates(state(TransitionDestinationActivity.class, ON_DESTROY),
+ state(rootActivity, ON_RESUME));
+ LifecycleVerifier.assertLaunchAndDestroySequence(TransitionDestinationActivity.class,
+ getLifecycleLog());
+ }
+
+ /**
+ * Verify the lifecycle of {@link Activity#finishAfterTransition()} for activity with no
+ * transition set (root of task).
+ */
+ @FlakyTest(bugId=137329632)
+ @Test
+ public void testFinishAfterTransition_noTransition_rootOfTask() throws Exception {
+ final Activity activity = launchActivity(FirstActivity.class);
+ waitAndAssertActivityStates(state(activity, ON_RESUME));
+
+ getLifecycleLog().clear();
+ activity.finishAfterTransition();
+ waitAndAssertActivityStates(state(FirstActivity.class, ON_DESTROY));
+ LifecycleVerifier.assertSequence(FirstActivity.class, getLifecycleLog(),
+ Arrays.asList(ON_PAUSE, ON_STOP, ON_DESTROY), "finishAfterTransition");
+ }
+
+ /**
+ * Verify the lifecycle of {@link Activity#finishAfterTransition()} for activity with no
+ * transition set.
+ */
+ @FlakyTest(bugId=137329632)
+ @Test
+ public void testFinishAfterTransition_noTransition() throws Exception {
+ final Activity rootActivity = launchActivity(FirstActivity.class);
+ final Activity topActivity = launchActivity(SecondActivity.class);
+ waitAndAssertActivityStates(state(topActivity, ON_RESUME), state(rootActivity, ON_STOP));
+
+ getLifecycleLog().clear();
+ topActivity.finishAfterTransition();
+ waitAndAssertActivityStates(state(SecondActivity.class, ON_DESTROY));
+ LifecycleVerifier.assertSequence(SecondActivity.class, getLifecycleLog(),
+ Arrays.asList(ON_PAUSE, ON_STOP, ON_DESTROY), "finishAfterTransition");
+ }
+
+ /**
+ * Verify that {@link Activity#finishAffinity()} will finish all activities with the same
+ * affinity below the target activity.
+ */
+ @FlakyTest(bugId=137329632)
+ @Test
+ public void testFinishAffinity() throws Exception {
+ final Activity firstActivity = launchActivity(FirstActivity.class);
+ final Activity secondActivity = launchActivity(SecondActivity.class);
+ final Activity thirdActivity = launchActivity(ThirdActivity.class);
+ waitAndAssertActivityStates(state(thirdActivity, ON_RESUME), state(secondActivity, ON_STOP),
+ state(firstActivity, ON_STOP));
+
+ getLifecycleLog().clear();
+ secondActivity.finishAffinity();
+ waitAndAssertActivityStates(state(FirstActivity.class, ON_DESTROY),
+ state(SecondActivity.class, ON_DESTROY));
+ LifecycleVerifier.assertEmptySequence(ThirdActivity.class, getLifecycleLog(),
+ "finishAffinityBelow");
+ }
+
+ /**
+ * Verify that {@link Activity#finishAffinity()} will not finish activities with different
+ * affinities in the same task.
+ */
+ @FlakyTest(bugId=137329632)
+ @Test
+ public void testFinishAffinity_differentAffinity() throws Exception {
+ final Activity firstActivity = launchActivity(FirstActivity.class);
+ final Activity differentAffinityActivity = launchActivity(DifferentAffinityActivity.class);
+ waitAndAssertActivityStates(state(differentAffinityActivity, ON_RESUME),
+ state(firstActivity, ON_STOP));
+
+ getLifecycleLog().clear();
+ differentAffinityActivity.finishAffinity();
+ waitAndAssertActivityStates(state(DifferentAffinityActivity.class, ON_DESTROY));
+ LifecycleVerifier.assertSequence(FirstActivity.class, getLifecycleLog(),
+ Arrays.asList(ON_RESTART, ON_START, ON_RESUME), "finishAffinity");
+ }
+
+ /**
+ * Verify that {@link Activity#finishAffinity()} will not finish activities with the same
+ * affinity in different tasks.
+ */
+ @FlakyTest(bugId=137329632)
+ @Test
+ public void testFinishAffinity_multiTask() throws Exception {
+ final Activity firstActivity = launchActivity(FirstActivity.class);
+ final Intent newTaskIntent = new Intent();
+ newTaskIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK);
+ final Activity secondActivity = mSecondActivityTestRule.launchActivity(newTaskIntent);
+ waitAndAssertActivityStates(state(secondActivity, ON_RESUME),
+ state(firstActivity, ON_STOP));
+
+ getLifecycleLog().clear();
+ secondActivity.finishAffinity();
+ waitAndAssertActivityStates(state(SecondActivity.class, ON_DESTROY),
+ state(firstActivity, ON_RESUME));
+ }
+}
\ No newline at end of file
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/LifecycleVerifier.java b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/LifecycleVerifier.java
index 32df6b7..2aecea9 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/LifecycleVerifier.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/LifecycleVerifier.java
@@ -72,6 +72,14 @@
: Arrays.asList(PRE_ON_CREATE, ON_CREATE, ON_START, ON_RESUME);
}
+ static List<ActivityCallback> getLaunchAndDestroySequence(
+ Class<? extends Activity> activityClass) {
+ final List<ActivityCallback> expectedTransitions = new ArrayList<>();
+ expectedTransitions.addAll(getLaunchSequence(activityClass));
+ expectedTransitions.addAll(getResumeToDestroySequence(activityClass));
+ return expectedTransitions;
+ }
+
static void assertLaunchSequence(Class<? extends Activity> launchingActivity,
Class<? extends Activity> existingActivity, LifecycleLog lifecycleLog,
boolean launchingIsTranslucent) {
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityAndWindowManagersState.java b/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityAndWindowManagersState.java
index a3102c5..d50bed9 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityAndWindowManagersState.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityAndWindowManagersState.java
@@ -46,7 +46,6 @@
import android.content.ComponentName;
import android.graphics.Rect;
-import android.os.SystemClock;
import android.server.wm.ActivityManagerState.ActivityStack;
import android.server.wm.ActivityManagerState.ActivityTask;
import android.server.wm.WindowManagerState.Display;
@@ -59,9 +58,7 @@
import java.util.List;
import java.util.Objects;
import java.util.function.BiPredicate;
-import java.util.function.BooleanSupplier;
import java.util.function.Predicate;
-import java.util.function.Supplier;
import java.util.stream.Collectors;
/**
@@ -143,53 +140,45 @@
*/
private void waitForValidState(boolean compareTaskAndStackBounds,
WaitForValidActivityState... waitForActivitiesVisible) {
- for (int retry = 1; retry <= 5; retry++) {
+ if (!Condition.waitFor("valid stacks and activities states", () -> {
// TODO: Get state of AM and WM at the same time to avoid mismatches caused by
// requesting dump in some intermediate state.
mAmState.computeState();
mWmState.computeState();
- if (shouldWaitForSanityCheck(compareTaskAndStackBounds)
+ return !(shouldWaitForSanityCheck(compareTaskAndStackBounds)
|| shouldWaitForValidStacks(compareTaskAndStackBounds)
|| shouldWaitForActivities(waitForActivitiesVisible)
- || shouldWaitForWindows()) {
- logAlways("***Waiting for valid stacks and activities states... retry=" + retry);
- SystemClock.sleep(1000);
- } else {
- return;
- }
+ || shouldWaitForWindows());
+ })) {
+ logE("***Waiting for states failed: " + Arrays.toString(waitForActivitiesVisible));
}
- logE("***Waiting for states failed: " + Arrays.toString(waitForActivitiesVisible));
}
/**
* Ensures all exiting windows have been removed.
*/
void waitForAllExitingWindows() {
- List<WindowState> exitingWindows = null;
- for (int retry = 1; retry <= 5; retry++) {
- mWmState.computeState();
- exitingWindows = mWmState.getExitingWindows();
- if (exitingWindows.isEmpty()) {
- return;
- }
- logAlways("***Waiting for all exiting windows have been removed... retry=" + retry);
- SystemClock.sleep(1000);
- }
- fail("All exiting windows have been removed, actual=" + exitingWindows.stream()
- .map(WindowState::getName)
- .collect(Collectors.joining(",")));
+ Condition.<List<WindowState>>waitForResult("all exiting windows to be removed",
+ condition -> condition
+ .setResultSupplier(() -> {
+ mWmState.computeState();
+ return mWmState.getExitingWindows();
+ })
+ .setResultValidator(List<WindowState>::isEmpty)
+ .setOnFailure(exitingWindows -> {
+ fail("All exiting windows have been removed, actual="
+ + exitingWindows.stream().map(WindowState::getName)
+ .collect(Collectors.joining(",")));
+ }));
}
void waitForAllStoppedActivities() {
- for (int retry = 1; retry <= 5; retry++) {
+ if (!Condition.waitFor("all started activities have been removed", () -> {
mAmState.computeState();
- if (!mAmState.containsStartedActivities()) {
- return;
- }
- logAlways("***Waiting for all started activities have been removed... retry=" + retry);
- SystemClock.sleep(1500);
+ return !mAmState.containsStartedActivities();
+ })) {
+ fail("All started activities have been removed");
}
- fail("All started activities have been removed");
}
/**
@@ -201,33 +190,12 @@
* for debugger.
*/
void waitForDebuggerWindowVisible(ComponentName activityName) {
- for (int retry = 1; retry <= 5; retry++) {
+ Condition.waitFor("debugger window", () -> {
mAmState.computeState();
mWmState.computeState();
- if (shouldWaitForDebuggerWindow(activityName)
- || shouldWaitForActivityRecords(activityName)) {
- logAlways("***Waiting for debugger window... retry=" + retry);
- SystemClock.sleep(1000);
- } else {
- return;
- }
- }
- logE("***Waiting for debugger window failed");
- }
-
- <T> T waitForValidProduct(Supplier<T> supplier, String productName, Predicate<T> tester) {
- T product = null;
- for (int retry = 1; retry <= 5; retry++) {
- product = supplier.get();
- if (product != null) {
- if (tester.test(product)) {
- break;
- }
- }
- logAlways("***Waiting for valid " + productName + "... retry=" + retry);
- SystemClock.sleep(1000);
- }
- return product;
+ return !shouldWaitForDebuggerWindow(activityName)
+ && !shouldWaitForActivityRecords(activityName);
+ });
}
void waitForHomeActivityVisible() {
@@ -247,37 +215,34 @@
waitForHomeActivityVisible();
} else {
waitForWithAmState(ActivityManagerState::isRecentsActivityVisible,
- "***Waiting for recents activity to be visible...");
+ "recents activity to be visible");
}
}
void waitForKeyguardShowingAndNotOccluded() {
waitForWithAmState(state -> state.getKeyguardControllerState().keyguardShowing
&& !state.getKeyguardControllerState().isKeyguardOccluded(DEFAULT_DISPLAY),
- "***Waiting for Keyguard showing...");
+ "Keyguard showing");
}
void waitForKeyguardShowingAndOccluded() {
waitForWithAmState(state -> state.getKeyguardControllerState().keyguardShowing
&& state.getKeyguardControllerState().isKeyguardOccluded(DEFAULT_DISPLAY),
- "***Waiting for Keyguard showing and occluded...");
+ "Keyguard showing and occluded");
}
void waitForAodShowing() {
- waitForWithAmState(state -> state.getKeyguardControllerState().aodShowing,
- "***Waiting for AOD showing...");
-
+ waitForWithAmState(state -> state.getKeyguardControllerState().aodShowing, "AOD showing");
}
void waitForKeyguardGone() {
waitForWithAmState(state -> !state.getKeyguardControllerState().keyguardShowing,
- "***Waiting for Keyguard gone...");
+ "Keyguard gone");
}
/** Wait for specific rotation for the default display. Values are Surface#Rotation */
void waitForRotation(int rotation) {
- waitForWithWmState(state -> state.getRotation() == rotation,
- "***Waiting for Rotation: " + rotation);
+ waitForWithWmState(state -> state.getRotation() == rotation, "Rotation: " + rotation);
}
/**
@@ -286,7 +251,7 @@
*/
void waitForLastOrientation(int orientation) {
waitForWithWmState(state -> state.getLastOrientation() == orientation,
- "***Waiting for LastOrientation: " + orientation);
+ "LastOrientation: " + orientation);
}
/**
@@ -299,30 +264,28 @@
return false;
}
return task.mFullConfiguration.orientation == orientation;
- }, "***Waiting for Activity orientation: " + orientation);
+ }, "orientation of " + getActivityName(activityName) + " to be " + orientation);
}
void waitForDisplayUnfrozen() {
- waitForWithWmState(state -> !state.isDisplayFrozen(),
- "***Waiting for Display unfrozen");
+ waitForWithWmState(state -> !state.isDisplayFrozen(), "Display unfrozen");
}
public void waitForActivityState(ComponentName activityName, String activityState) {
waitForWithAmState(state -> state.hasActivityState(activityName, activityState),
- "***Waiting for Activity State: " + activityState);
+ "state of " + getActivityName(activityName) + " to be " + activityState);
}
public void waitForActivityRemoved(ComponentName activityName) {
waitForWithAmState((state) -> !state.containsActivity(activityName),
- "Waiting for activity to be removed");
+ "activity to be removed");
waitForWithWmState((state) -> !state.containsWindow(getWindowName(activityName)),
- "Waiting for activity window to be gone");
+ "activity window to be gone");
}
@Deprecated
void waitForFocusedStack(int stackId) {
- waitForWithAmState(state -> state.getFocusedStackId() == stackId,
- "***Waiting for focused stack...");
+ waitForWithAmState(state -> state.getFocusedStackId() == stackId, "focused stack");
}
void waitForFocusedStack(int windowingMode, int activityType) {
@@ -331,59 +294,54 @@
|| state.getFocusedStackActivityType() == activityType)
&& (windowingMode == WINDOWING_MODE_UNDEFINED
|| state.getFocusedStackWindowingMode() == windowingMode),
- "***Waiting for focused stack...");
+ "focused stack");
}
void waitForPendingActivityContain(ComponentName activity) {
waitForWithAmState(state -> state.pendingActivityContain(activity),
- "***Waiting for activity in pending list...");
+ getActivityName(activity) + " in pending list");
}
void waitForAppTransitionIdleOnDisplay(int displayId) {
waitForWithWmState(
state -> WindowManagerState.APP_STATE_IDLE.equals(
state.getDisplay(displayId).getAppTransitionState()),
- "***Waiting for app transition idle on Display " + displayId + " ...");
+ "app transition idle on Display " + displayId);
}
-
void waitAndAssertNavBarShownOnDisplay(int displayId) {
- waitForWithWmState(
+ assertTrue(waitForWithWmState(
state -> state.getAndAssertSingleNavBarWindowOnDisplay(displayId) != null,
- "***Waiting for navigation bar #" + displayId + " show...");
- final WindowState ws = getWmState().getAndAssertSingleNavBarWindowOnDisplay(displayId);
-
- assertNotNull(ws);
+ "navigation bar #" + displayId + " show"));
}
- public void waitForWithAmState(Predicate<ActivityManagerState> waitCondition, String message) {
- waitFor((amState, wmState) -> waitCondition.test(amState), message);
+ public boolean waitForWithAmState(Predicate<ActivityManagerState> waitCondition,
+ String message) {
+ return waitFor((amState, wmState) -> waitCondition.test(amState), message);
}
- public void waitForWithWmState(Predicate<WindowManagerState> waitCondition, String message) {
- waitFor((amState, wmState) -> waitCondition.test(wmState), message);
+ public boolean waitForWithWmState(Predicate<WindowManagerState> waitCondition, String message) {
+ return waitFor((amState, wmState) -> waitCondition.test(wmState), message);
}
- void waitFor(
+ public void waitWindowingModeTopFocus(int windowingMode, boolean topFocus, String message) {
+ waitForWithAmState(amState -> {
+ final ActivityStack stack = amState.getStandardStackByWindowingMode(windowingMode);
+ return stack != null
+ && topFocus == (amState.getFocusedStackId() == stack.getStackId());
+ }, message);
+ }
+
+ /** @return {@code true} if the wait is successful; {@code false} if timeout occurs. */
+ boolean waitFor(
BiPredicate<ActivityManagerState, WindowManagerState> waitCondition, String message) {
- waitFor(message, () -> {
+ return Condition.waitFor(message, () -> {
mAmState.computeState();
mWmState.computeState();
return waitCondition.test(mAmState, mWmState);
});
}
- void waitFor(String message, BooleanSupplier waitCondition) {
- for (int retry = 1; retry <= 5; retry++) {
- if (waitCondition.getAsBoolean()) {
- return;
- }
- logAlways(message + " retry=" + retry);
- SystemClock.sleep(1000);
- }
- logE(message + " failed");
- }
-
/**
* @return true if should wait for valid stacks state.
*/
@@ -955,9 +913,12 @@
}
void waitAndAssertImeWindowShownOnDisplay(int displayId) {
- final WindowManagerState.WindowState imeWinState = waitForValidProduct(
- this::getImeWindowState, "IME window",
- w -> w.isShown() && w.getDisplayId() == displayId);
+ final WindowManagerState.WindowState imeWinState = Condition.waitForResult("IME window",
+ condition -> condition
+ .setResultSupplier(this::getImeWindowState)
+ .setResultValidator(
+ w -> w != null && w.isShown() && w.getDisplayId() == displayId));
+
assertNotNull("IME window must exist", imeWinState);
assertTrue("IME window must be shown", imeWinState.isShown());
assertEquals("IME window must be on the given display", displayId,
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityLauncher.java b/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityLauncher.java
index b75eeea..7f2e144 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityLauncher.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityLauncher.java
@@ -24,7 +24,6 @@
import android.app.ActivityManager;
import android.app.ActivityOptions;
-import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -67,6 +66,11 @@
*/
public static final String KEY_REORDER_TO_FRONT = "reorder_to_front";
/**
+ * Key for boolean extra, indicates if launch task without presented to user.
+ * {@link ActivityOptions#makeTaskLaunchBehind()}.
+ */
+ public static final String KEY_LAUNCH_TASK_BEHIND = "launch_task_behind";
+ /**
* Key for string extra with string representation of target component.
*/
public static final String KEY_TARGET_COMPONENT = "target_component";
@@ -82,12 +86,6 @@
*/
public static final String KEY_USE_APPLICATION_CONTEXT = "use_application_context";
/**
- * Key for boolean extra, indicates if instrumentation context will be used for launch. This
- * means that {@link PendingIntent} should be used instead of a regular one, because application
- * switch will not be allowed otherwise.
- */
- public static final String KEY_USE_INSTRUMENTATION = "use_instrumentation";
- /**
* Key for boolean extra, indicates if any exceptions thrown during launch other then
* {@link SecurityException} should be suppressed. A {@link SecurityException} is never thrown,
* it's always written to logs.
@@ -167,12 +165,15 @@
newIntent.putExtras(intentExtras);
}
- ActivityOptions options = null;
+ ActivityOptions options = extras.getBoolean(KEY_LAUNCH_TASK_BEHIND)
+ ? ActivityOptions.makeTaskLaunchBehind() : null;
final int displayId = extras.getInt(KEY_DISPLAY_ID, -1);
if (displayId != -1) {
- options = ActivityOptions.makeBasic();
+ if (options == null) {
+ options = ActivityOptions.makeBasic();
+ }
options.setLaunchDisplayId(displayId);
- if (extras.getBoolean(KEY_MULTIPLE_INSTANCES, true)) {
+ if (extras.getBoolean(KEY_MULTIPLE_INSTANCES)) {
newIntent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK);
}
}
@@ -196,30 +197,9 @@
context.getApplicationContext() : context;
try {
- if (extras.getBoolean(KEY_USE_INSTRUMENTATION)) {
- // Using PendingIntent for Instrumentation launches, because otherwise we won't
- // be allowed to switch the current activity with ours with different uid.
- // android.permission.STOP_APP_SWITCHES is needed to do this directly.
- // PendingIntent.FLAG_CANCEL_CURRENT is needed here, or we may get an existing
- // PendingIntent if it is same kind of PendingIntent request to previous one.
- // Note: optionsBundle is not taking into account for PendingIntentRecord.Key
- // hashcode calculation.
- final PendingIntent pendingIntent = PendingIntent.getActivity(launchContext, 0,
- newIntent, PendingIntent.FLAG_CANCEL_CURRENT, optionsBundle);
- pendingIntent.send();
- } else {
- launchContext.startActivity(newIntent, optionsBundle);
- }
+ launchContext.startActivity(newIntent, optionsBundle);
} catch (SecurityException e) {
handleSecurityException(context, e);
- } catch (PendingIntent.CanceledException e) {
- if (extras.getBoolean(KEY_SUPPRESS_EXCEPTIONS)) {
- Log.e(TAG, "Exception launching activity with pending intent");
- } else {
- throw new RuntimeException(e);
- }
- // Bypass the exception although it is not SecurityException.
- handleSecurityException(context, e);
} catch (Exception e) {
if (extras.getBoolean(KEY_SUPPRESS_EXCEPTIONS)) {
Log.e(TAG, "Exception launching activity");
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerState.java b/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerState.java
index d7733dc..0ebee7b 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerState.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerState.java
@@ -807,7 +807,7 @@
public boolean translucent;
Activity(ActivityRecordProto proto) {
- super(proto.configurationContainer);
+ super(proto.appWindowToken.windowToken.windowContainer.configurationContainer);
name = proto.identifier.title;
state = proto.state;
visible = proto.visible;
@@ -827,6 +827,7 @@
}
}
+ // TODO: Switch to extending WindowContainer once unification is done.
static abstract class ActivityContainer extends WindowManagerState.ConfigurationContainer {
protected boolean mFullscreen;
protected Rect mBounds;
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java b/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java
index f7337e9..6e27a27 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java
@@ -47,6 +47,7 @@
import static android.server.wm.ActivityLauncher.KEY_INTENT_EXTRAS;
import static android.server.wm.ActivityLauncher.KEY_INTENT_FLAGS;
import static android.server.wm.ActivityLauncher.KEY_LAUNCH_ACTIVITY;
+import static android.server.wm.ActivityLauncher.KEY_LAUNCH_TASK_BEHIND;
import static android.server.wm.ActivityLauncher.KEY_LAUNCH_TO_SIDE;
import static android.server.wm.ActivityLauncher.KEY_MULTIPLE_INSTANCES;
import static android.server.wm.ActivityLauncher.KEY_MULTIPLE_TASK;
@@ -56,13 +57,11 @@
import static android.server.wm.ActivityLauncher.KEY_SUPPRESS_EXCEPTIONS;
import static android.server.wm.ActivityLauncher.KEY_TARGET_COMPONENT;
import static android.server.wm.ActivityLauncher.KEY_USE_APPLICATION_CONTEXT;
-import static android.server.wm.ActivityLauncher.KEY_USE_INSTRUMENTATION;
import static android.server.wm.ActivityLauncher.launchActivityFromExtras;
import static android.server.wm.ActivityManagerState.STATE_RESUMED;
import static android.server.wm.ComponentNameUtils.getActivityName;
import static android.server.wm.ComponentNameUtils.getLogTag;
import static android.server.wm.StateLogger.log;
-import static android.server.wm.StateLogger.logAlways;
import static android.server.wm.StateLogger.logE;
import static android.server.wm.UiDeviceUtils.pressAppSwitchButton;
import static android.server.wm.UiDeviceUtils.pressBackButton;
@@ -93,7 +92,7 @@
import static android.view.Display.INVALID_DISPLAY;
import static android.view.Surface.ROTATION_0;
-import static androidx.test.InstrumentationRegistry.getInstrumentation;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -123,7 +122,6 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.os.SystemClock;
-import android.os.UserHandle;
import android.provider.Settings;
import android.server.wm.CommandSession.ActivityCallback;
import android.server.wm.CommandSession.ActivitySession;
@@ -157,13 +155,11 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.Callable;
-import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BooleanSupplier;
import java.util.function.Consumer;
@@ -430,7 +426,7 @@
pressWakeupButton();
pressUnlockButton();
- pressHomeButton();
+ launchHomeActivityNoWait();
removeStacksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME);
// Clear launch params for all test packages to make sure each test is run in a clean state.
@@ -446,8 +442,16 @@
stopTestPackage(TEST_PACKAGE);
stopTestPackage(SECOND_TEST_PACKAGE);
stopTestPackage(THIRD_TEST_PACKAGE);
- pressHomeButton();
+ launchHomeActivityNoWait();
+ }
+ /**
+ * After home key is pressed ({@link #pressHomeButton} is called), the later launch may be
+ * deferred if the calling uid doesn't have android.permission.STOP_APP_SWITCHES. This method
+ * will resume the temporary stopped state, so the launch won't be affected.
+ */
+ protected void resumeAppSwitches() {
+ SystemUtil.runWithShellPermissionIdentity(ActivityManager::resumeAppSwitches);
}
protected void moveTopActivityToPinnedStack(int stackId) {
@@ -498,6 +502,13 @@
final long upTime = SystemClock.uptimeMillis();
injectMotion(downTime, upTime, MotionEvent.ACTION_UP, x, y, displayId);
+
+ mAmWmState.waitForWithWmState(state -> state.getFocusedDisplayId() == displayId,
+ "top focused displayId: " + displayId);
+ // This is needed after a tap in multi-display to ensure that the display focus has really
+ // changed, if needed. The call to syncInputTransaction will wait until focus change has
+ // propagated from WMS to native input before returning.
+ getInstrumentation().getUiAutomation().syncInputTransactions();
}
protected void tapOnStackCenter(ActivityManagerState.ActivityStack stack) {
@@ -563,15 +574,10 @@
getInstrumentation().waitForIdleSync();
}
- /** Returns the set of stack ids. */
- private HashSet<Integer> getStackIds() {
- mAmWmState.computeState(true);
- final List<ActivityManagerState.ActivityStack> stacks = mAmWmState.getAmState().getStacks();
- final HashSet<Integer> stackIds = new HashSet<>();
- for (ActivityManagerState.ActivityStack s : stacks) {
- stackIds.add(s.mStackId);
- }
- return stackIds;
+ static void waitForOrFail(String message, BooleanSupplier condition) {
+ Condition.waitFor(new Condition<>(message, condition)
+ .setRetryIntervalMs(500)
+ .setOnFailure(unusedResult -> fail("FAILED because unsatisfied: " + message)));
}
/** Returns the stack that contains the provided task. */
@@ -588,8 +594,17 @@
return null;
}
- protected void launchHomeActivity() {
+ /**
+ * Launches the home activity directly. If there is no specific reason to simulate a home key
+ * (which will trigger stop-app-switches), it is the recommended method to go home.
+ */
+ protected static void launchHomeActivityNoWait() {
executeShellCommand(AM_START_HOME_ACTIVITY_COMMAND);
+ }
+
+ /** Launches the home activity directly with waiting for it to be visible. */
+ protected void launchHomeActivity() {
+ launchHomeActivityNoWait();
mAmWmState.waitForHomeActivityVisible();
}
@@ -762,10 +777,10 @@
new Rect(0, 0, taskWidth, taskHeight)));
}
- protected void resizeStack(int stackId, int stackLeft, int stackTop, int stackWidth,
- int stackHeight) {
- SystemUtil.runWithShellPermissionIdentity(() -> mAtm.resizeStack(stackId,
- new Rect(stackLeft, stackTop, stackWidth, stackHeight)));
+ protected void resizePinnedStack(
+ int stackId, int stackLeft, int stackTop, int stackWidth, int stackHeight) {
+ SystemUtil.runWithShellPermissionIdentity(() -> mAtm.resizePinnedStack(stackId,
+ new Rect(stackLeft, stackTop, stackWidth, stackHeight), false /* animate */));
}
protected void pressAppSwitchButtonAndWaitForRecents() {
@@ -833,9 +848,8 @@
mAmWmState.waitForValidState(activityName);
mAmWmState.waitForActivityState(activityName, STATE_RESUMED);
final String activityClassName = getActivityName(activityName);
- mAmWmState.waitForWithAmState(state ->
- activityClassName.equals(state.getFocusedActivity()),
- "Waiting for activity to be on top");
+ mAmWmState.waitForWithAmState(state -> activityClassName.equals(state.getFocusedActivity()),
+ "activity to be on top");
mAmWmState.assertSanity();
mAmWmState.assertFocusedActivity(message, activityName);
@@ -1092,10 +1106,7 @@
android.os.Process.myUserHandle().getIdentifier())) {
mAmWmState.waitForAodShowing();
} else {
- for (int retry = 1; isDisplayOn(DEFAULT_DISPLAY) && retry <= 5; retry++) {
- logAlways("***Waiting for display to turn off... retry=" + retry);
- SystemClock.sleep(TimeUnit.SECONDS.toMillis(1));
- }
+ Condition.waitFor("display to turn off", () -> !isDisplayOn(DEFAULT_DISPLAY));
}
return this;
}
@@ -1215,7 +1226,10 @@
mPreviousDegree = value;
if (waitSystemUI) {
- waitForRotationNotified();
+ Condition.waitFor(new Condition<>("rotation notified",
+ // There will receive USER_ROTATION changed twice because when the device
+ // rotates to 0deg, RotationContextButton will also set ROTATION_0 again.
+ () -> mRotationObserver.count == 2).setRetryIntervalMs(500));
}
// Wait for settling rotation.
mAmWmState.waitForRotation(value);
@@ -1233,19 +1247,6 @@
super.close();
}
- private void waitForRotationNotified() {
- for (int retry = 1; retry <= 5; retry++) {
- // There will receive USER_ROTATION changed twice because when the device rotates to
- // 0deg, RotationContextButton will also set ROTATION_0 again.
- if (mRotationObserver.count == 2) {
- return;
- }
- logAlways("waitForRotationNotified retry=" + retry);
- SystemClock.sleep(500);
- }
- logE("waitForRotationNotified skip");
- }
-
private class SettingsObserver extends ContentObserver {
int count;
@@ -1404,40 +1405,6 @@
FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS);
}
- /**
- * Base helper class for retrying validator success.
- */
- private abstract static class RetryValidator {
-
- private static final int RETRY_LIMIT = 5;
- private static final long RETRY_INTERVAL = TimeUnit.SECONDS.toMillis(1);
-
- /**
- * @return Error string if validation is failed, null if everything is fine.
- **/
- @Nullable
- protected abstract String validate();
-
- /**
- * Executes {@link #validate()}. Retries {@link #RETRY_LIMIT} times with
- * {@link #RETRY_INTERVAL} interval.
- *
- * @param waitingMessage logging message while waiting validation.
- */
- void assertValidator(String waitingMessage) {
- String resultString = null;
- for (int retry = 1; retry <= RETRY_LIMIT; retry++) {
- resultString = validate();
- if (resultString == null) {
- return;
- }
- logAlways(waitingMessage + ": " + resultString);
- SystemClock.sleep(RETRY_INTERVAL);
- }
- fail(resultString);
- }
- }
-
static class CountSpec<T> {
static final int DONT_CARE = Integer.MIN_VALUE;
static final int EQUALS = 1;
@@ -1458,13 +1425,13 @@
} else {
switch (rule) {
case EQUALS:
- mMessage = event + " + must equal to " + count;
+ mMessage = event + " must equal to " + count;
break;
case GREATER_THAN:
- mMessage = event + " + must be greater than " + count;
+ mMessage = event + " must be greater than " + count;
break;
case LESS_THAN:
- mMessage = event + " + must be less than " + count;
+ mMessage = event + " must be less than " + count;
break;
default:
mMessage = "Don't care";
@@ -1522,7 +1489,7 @@
static void assertSingleLaunch(ComponentName activityName) {
assertLifecycleCounts(activityName,
- "***Waiting for activity create, start, and resume",
+ "activity create, start, and resume",
1 /* createCount */, 1 /* startCount */, 1 /* resumeCount */,
0 /* pauseCount */, 0 /* stopCount */, 0 /* destroyCount */,
CountSpec.DONT_CARE /* configChangeCount */);
@@ -1530,7 +1497,7 @@
static void assertSingleLaunchAndStop(ComponentName activityName) {
assertLifecycleCounts(activityName,
- "***Waiting for activity create, start, resume, pause, and stop",
+ "activity create, start, resume, pause, and stop",
1 /* createCount */, 1 /* startCount */, 1 /* resumeCount */,
1 /* pauseCount */, 1 /* stopCount */, 0 /* destroyCount */,
CountSpec.DONT_CARE /* configChangeCount */);
@@ -1538,7 +1505,7 @@
static void assertSingleStartAndStop(ComponentName activityName) {
assertLifecycleCounts(activityName,
- "***Waiting for activity start, resume, pause, and stop",
+ "activity start, resume, pause, and stop",
0 /* createCount */, 1 /* startCount */, 1 /* resumeCount */,
1 /* pauseCount */, 1 /* stopCount */, 0 /* destroyCount */,
CountSpec.DONT_CARE /* configChangeCount */);
@@ -1546,7 +1513,7 @@
static void assertSingleStart(ComponentName activityName) {
assertLifecycleCounts(activityName,
- "***Waiting for activity start and resume",
+ "activity start and resume",
0 /* createCount */, 1 /* startCount */, 1 /* resumeCount */,
0 /* pauseCount */, 0 /* stopCount */, 0 /* destroyCount */,
CountSpec.DONT_CARE /* configChangeCount */);
@@ -1554,20 +1521,12 @@
/** Assert the activity is either relaunched or received configuration changed. */
static void assertActivityLifecycle(ComponentName activityName, boolean relaunched) {
- new RetryValidator() {
-
- @Nullable
- @Override
- protected String validate() {
- final String failedReason = checkActivityIsRelaunchedOrConfigurationChanged(
+ Condition.<String>waitForResult(activityName + " relaunched", condition -> condition
+ .setResultSupplier(() -> checkActivityIsRelaunchedOrConfigurationChanged(
getActivityName(activityName),
- TestJournalContainer.get(activityName).callbacks, relaunched);
- if (failedReason != null) {
- return failedReason;
- }
- return null;
- }
- }.assertValidator("***Waiting for valid lifecycle state");
+ TestJournalContainer.get(activityName).callbacks, relaunched))
+ .setResultValidator(failedReasons -> failedReasons == null)
+ .setOnFailure(failedReasons -> fail(failedReasons)));
}
/** Assert the activity is either relaunched or received configuration changed. */
@@ -1604,8 +1563,7 @@
static void assertRelaunchOrConfigChanged(ComponentName activityName, int numRelaunch,
int numConfigChange) {
- new ActivityLifecycleCounts(activityName).assertCountWithRetry(
- "***Waiting for relaunch or config changed",
+ new ActivityLifecycleCounts(activityName).assertCountWithRetry("relaunch or config changed",
countSpec(ActivityCallback.ON_DESTROY, CountSpec.EQUALS, numRelaunch),
countSpec(ActivityCallback.ON_CREATE, CountSpec.EQUALS, numRelaunch),
countSpec(ActivityCallback.ON_CONFIGURATION_CHANGED, CountSpec.EQUALS,
@@ -1613,8 +1571,7 @@
}
static void assertActivityDestroyed(ComponentName activityName) {
- new ActivityLifecycleCounts(activityName).assertCountWithRetry(
- "***Waiting for activity destroyed",
+ new ActivityLifecycleCounts(activityName).assertCountWithRetry("activity destroyed",
countSpec(ActivityCallback.ON_DESTROY, CountSpec.EQUALS, 1),
countSpec(ActivityCallback.ON_CREATE, CountSpec.EQUALS, 0),
countSpec(ActivityCallback.ON_CONFIGURATION_CHANGED, CountSpec.EQUALS, 0));
@@ -1626,16 +1583,11 @@
@Nullable
SizeInfo getLastReportedSizesForActivity(ComponentName activityName) {
- for (int retry = 1; retry <= 5; retry++) {
- final ConfigInfo result = TestJournalContainer.get(activityName).lastConfigInfo;
- if (result != null && result.sizeInfo != null) {
- return result.sizeInfo;
- }
- logAlways("***Waiting for sizes to be reported... retry=" + retry);
- SystemClock.sleep(1000);
- }
- logE("***Waiting for activity size failed: activityName=" + getActivityName(activityName));
- return null;
+ return Condition.waitForResult("sizes of " + activityName + " to be reported",
+ condition -> condition.setResultSupplier(() -> {
+ final ConfigInfo info = TestJournalContainer.get(activityName).lastConfigInfo;
+ return info != null ? info.sizeInfo : null;
+ }).setResultValidator(sizeInfo -> sizeInfo != null));
}
/** Check if a device has display cutout. */
@@ -1652,7 +1604,7 @@
mBroadcastActionTrigger.finishBroadcastReceiverActivity();
mAmWmState.waitForWithAmState(
(state) -> !state.containsActivity(BROADCAST_RECEIVER_ACTIVITY),
- "Waiting for activity to be removed");
+ "activity to be removed");
return displayCutoutPresent;
}
@@ -1663,37 +1615,26 @@
*/
@Nullable
private Boolean getCutoutStateForActivity(ComponentName activityName) {
- final String logTag = getLogTag(activityName);
- for (int retry = 1; retry <= 5; retry++) {
- final Bundle extras = TestJournalContainer.get(activityName).extras;
- if (extras.containsKey(EXTRA_CUTOUT_EXISTS)) {
- return extras.getBoolean(EXTRA_CUTOUT_EXISTS);
- }
- logAlways("***Waiting for cutout state to be reported... retry=" + retry);
- SystemClock.sleep(1000);
- }
- logE("***Waiting for activity cutout state failed: activityName=" + logTag);
- return null;
+ return Condition.waitForResult("cutout state to be reported", condition -> condition
+ .setResultSupplier(() -> {
+ final Bundle extras = TestJournalContainer.get(activityName).extras;
+ return extras.containsKey(EXTRA_CUTOUT_EXISTS)
+ ? extras.getBoolean(EXTRA_CUTOUT_EXISTS)
+ : null;
+ }).setResultValidator(cutoutExists -> cutoutExists != null));
}
/** Waits for at least one onMultiWindowModeChanged event. */
ActivityLifecycleCounts waitForOnMultiWindowModeChanged(ComponentName activityName) {
- int retry = 1;
- ActivityLifecycleCounts result;
- do {
- result = new ActivityLifecycleCounts(activityName);
- if (result.getCount(ActivityCallback.ON_MULTI_WINDOW_MODE_CHANGED) >= 1) {
- return result;
- }
- logAlways("***waitForOnMultiWindowModeChanged... retry=" + retry);
- SystemClock.sleep(TimeUnit.SECONDS.toMillis(1));
- } while (retry++ <= 5);
- return result;
+ final ActivityLifecycleCounts counts = new ActivityLifecycleCounts(activityName);
+ Condition.waitFor(counts.countWithRetry("waitForOnMultiWindowModeChanged", countSpec(
+ ActivityCallback.ON_MULTI_WINDOW_MODE_CHANGED, CountSpec.GREATER_THAN, 0)));
+ return counts;
}
static class ActivityLifecycleCounts {
- final int[] mCounts = new int[ActivityCallback.SIZE];
- final int[] mLastIndexes = new int[ActivityCallback.SIZE];
+ private final int[] mCounts = new int[ActivityCallback.SIZE];
+ private final int[] mLastIndexes = new int[ActivityCallback.SIZE];
private ComponentName mActivityName;
ActivityLifecycleCounts(ComponentName componentName) {
@@ -1727,20 +1668,30 @@
}
@SafeVarargs
+ final Condition<String> countWithRetry(String message,
+ CountSpec<ActivityCallback>... countSpecs) {
+ if (mActivityName == null) {
+ throw new IllegalStateException(
+ "It is meaningless to retry without specified activity");
+ }
+ return new Condition<String>(message)
+ .setOnRetry(() -> {
+ Arrays.fill(mCounts, 0);
+ Arrays.fill(mLastIndexes, 0);
+ updateCount(TestJournalContainer.get(mActivityName).callbacks);
+ })
+ .setResultSupplier(() -> validateCount(countSpecs))
+ .setResultValidator(failedReasons -> failedReasons == null);
+ }
+
+ @SafeVarargs
final void assertCountWithRetry(String message, CountSpec<ActivityCallback>... countSpecs) {
if (mActivityName == null) {
throw new IllegalStateException(
"It is meaningless to retry without specified activity");
}
- new RetryValidator() {
- @Override
- protected String validate() {
- Arrays.fill(mCounts, 0);
- Arrays.fill(mLastIndexes, 0);
- updateCount(TestJournalContainer.get(mActivityName).callbacks);
- return validateCount(countSpecs);
- }
- }.assertValidator(message);
+ Condition.<String>waitForResult(countWithRetry(message, countSpecs)
+ .setOnFailure(failedReasons -> fail(message + ": " + failedReasons)));
}
@SafeVarargs
@@ -1752,7 +1703,7 @@
if (failedReasons == null) {
failedReasons = new ArrayList<>();
}
- failedReasons.add(spec.mMessage);
+ failedReasons.add(spec.mMessage + " (got " + realCount + ")");
}
}
return failedReasons == null ? null : String.join("\n", failedReasons);
@@ -1778,6 +1729,7 @@
private boolean mNewTask;
private boolean mMultipleTask;
private boolean mAllowMultipleInstances = true;
+ private boolean mLaunchTaskBehind;
private int mDisplayId = INVALID_DISPLAY;
private int mActivityType = ACTIVITY_TYPE_UNDEFINED;
// A proxy activity that launches other activities including mTargetActivityName
@@ -1831,6 +1783,11 @@
return this;
}
+ public LaunchActivityBuilder setLaunchTaskBehind(boolean launchTaskBehind) {
+ mLaunchTaskBehind = launchTaskBehind;
+ return this;
+ }
+
public LaunchActivityBuilder setReorderToFront(boolean reorderToFront) {
mReorderToFront = reorderToFront;
return this;
@@ -1954,13 +1911,13 @@
/** Launch an activity using instrumentation. */
private void launchUsingInstrumentation() {
final Bundle b = new Bundle();
- b.putBoolean(KEY_USE_INSTRUMENTATION, true);
b.putBoolean(KEY_LAUNCH_ACTIVITY, true);
b.putBoolean(KEY_LAUNCH_TO_SIDE, mToSide);
b.putBoolean(KEY_RANDOM_DATA, mRandomData);
b.putBoolean(KEY_NEW_TASK, mNewTask);
b.putBoolean(KEY_MULTIPLE_TASK, mMultipleTask);
b.putBoolean(KEY_MULTIPLE_INSTANCES, mAllowMultipleInstances);
+ b.putBoolean(KEY_LAUNCH_TASK_BEHIND, mLaunchTaskBehind);
b.putBoolean(KEY_REORDER_TO_FRONT, mReorderToFront);
b.putInt(KEY_DISPLAY_ID, mDisplayId);
b.putInt(KEY_ACTIVITY_TYPE, mActivityType);
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/CommandSession.java b/tests/framework/base/windowmanager/util/src/android/server/wm/CommandSession.java
index 468bd30..6675c68 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/CommandSession.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/CommandSession.java
@@ -16,7 +16,7 @@
package android.server.wm;
-import static androidx.test.InstrumentationRegistry.getInstrumentation;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import android.app.Activity;
import android.content.BroadcastReceiver;
@@ -371,8 +371,12 @@
private final ArrayMap<String, ActivitySession> mSessions = new ArrayMap<>();
private boolean mClosed;
- public ActivitySessionClient() {
- this(getInstrumentation().getContext());
+ /**
+ * Creates a {#link ActivitySessionClient} instance with instrumentation context. It is used
+ * when the caller doen't need try-with-resource.
+ */
+ public static ActivitySessionClient create() {
+ return new ActivitySessionClient(getInstrumentation().getContext());
}
public ActivitySessionClient(Context context) {
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/Condition.java b/tests/framework/base/windowmanager/util/src/android/server/wm/Condition.java
new file mode 100644
index 0000000..3680ce3
--- /dev/null
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/Condition.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.wm;
+
+import static android.server.wm.StateLogger.logAlways;
+import static android.server.wm.StateLogger.logE;
+
+import android.os.SystemClock;
+
+import java.util.concurrent.TimeUnit;
+import java.util.function.BooleanSupplier;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+
+/**
+ * The utility class to wait a condition with customized options.
+ * The default retry policy is 5 times with interval 1 second.
+ *
+ * @param <T> The type of the object to validate.
+ *
+ * <p>Sample:</p>
+ * <pre>
+ * // Simple case.
+ * if (Condition.waitFor("true value", () -> true)) {
+ * println("Success");
+ * }
+ * // Wait for customized result with customized validation.
+ * String result = Condition.waitForResult(new Condition<String>("string comparison")
+ * .setResultSupplier(() -> "Result string")
+ * .setResultValidator(str -> str.equals("Expected string"))
+ * .setRetryIntervalMs(500)
+ * .setRetryLimit(3)
+ * .setOnFailure(str -> println("Failed on " + str)));
+ * </pre>
+ */
+public class Condition<T> {
+ private final String mMessage;
+
+ // TODO(b/112837428): Implement a incremental retry policy to reduce the unnecessary constant
+ // time, currently keep the default as 5*1s because most of the original code uses it, and some
+ // tests might be sensitive to the waiting interval.
+ private long mRetryIntervalMs = TimeUnit.SECONDS.toMillis(1);
+ private int mRetryLimit = 5;
+ private boolean mReturnLastResult;
+
+ /** It decides whether this condition is satisfied. */
+ private BooleanSupplier mSatisfier;
+ /**
+ * It is used when the condition is not a simple boolean expression, such as the caller may want
+ * to get the validated product as the return value.
+ */
+ private Supplier<T> mResultSupplier;
+ /** It validates the result from {@link #mResultSupplier}. */
+ private Predicate<T> mResultValidator;
+ private Consumer<T> mOnFailure;
+ private Runnable mOnRetry;
+ private T mLastResult;
+ private T mValidatedResult;
+
+ /**
+ * When using this constructor, it is expected that the condition will be configured with
+ * {@link #setResultSupplier} and {@link #setResultValidator}.
+ */
+ public Condition(String message) {
+ this(message, null /* satisfier */);
+ }
+
+ /**
+ * Constructs with a simple boolean condition.
+ *
+ * @param message The message to show what is waiting for.
+ * @param satisfier If it returns true, that means the condition is satisfied.
+ */
+ public Condition(String message, BooleanSupplier satisfier) {
+ mMessage = message;
+ mSatisfier = satisfier;
+ }
+
+ /** Set the supplier which provides the result object to validate. */
+ public Condition<T> setResultSupplier(Supplier<T> supplier) {
+ mResultSupplier = supplier;
+ return this;
+ }
+
+ /** Set the validator which tests the object provided by the supplier. */
+ public Condition<T> setResultValidator(Predicate<T> validator) {
+ mResultValidator = validator;
+ return this;
+ }
+
+ /**
+ * If true, when using {@link #waitForResult(Condition)}, the method will return the last result
+ * provided by {@link #mResultSupplier} even it is not valid (by {@link #mResultValidator}).
+ */
+ public Condition<T> setReturnLastResult(boolean returnLastResult) {
+ mReturnLastResult = returnLastResult;
+ return this;
+ }
+
+ /**
+ * Executes the action when the condition does not satisfy within the time limit. The passed
+ * object to the consumer will be the last result from the supplier.
+ */
+ public Condition<T> setOnFailure(Consumer<T> onFailure) {
+ mOnFailure = onFailure;
+ return this;
+ }
+
+ public Condition<T> setOnRetry(Runnable onRetry) {
+ mOnRetry = onRetry;
+ return this;
+ }
+
+ public Condition<T> setRetryIntervalMs(long millis) {
+ mRetryIntervalMs = millis;
+ return this;
+ }
+
+ public Condition<T> setRetryLimit(int limit) {
+ mRetryLimit = limit;
+ return this;
+ }
+
+ /** Build the condition by {@link #mResultSupplier} and {@link #mResultValidator}. */
+ private void prepareSatisfier() {
+ if (mResultSupplier == null || mResultValidator == null) {
+ throw new IllegalArgumentException("No specified condition");
+ }
+
+ mSatisfier = () -> {
+ final T result = mResultSupplier.get();
+ mLastResult = result;
+ if (mResultValidator.test(result)) {
+ mValidatedResult = result;
+ return true;
+ }
+ return false;
+ };
+ }
+
+ /**
+ * @see #waitFor(Condition)
+ * @see #Condition(String, BooleanSupplier)
+ */
+ public static boolean waitFor(String message, BooleanSupplier satisfier) {
+ return waitFor(new Condition<>(message, satisfier));
+ }
+
+ /** @return {@code false} if the condition does not satisfy within the time limit. */
+ public static <T> boolean waitFor(Condition<T> condition) {
+ if (condition.mSatisfier == null) {
+ condition.prepareSatisfier();
+ }
+
+ final long startTime = SystemClock.elapsedRealtime();
+ for (int i = 1; i <= condition.mRetryLimit; i++) {
+ if (condition.mSatisfier.getAsBoolean()) {
+ return true;
+ } else {
+ SystemClock.sleep(condition.mRetryIntervalMs);
+ logAlways("***Waiting for " + condition.mMessage + " ... retry=" + i
+ + " elapsed=" + (SystemClock.elapsedRealtime() - startTime) + "ms");
+ if (condition.mOnRetry != null && i < condition.mRetryLimit) {
+ condition.mOnRetry.run();
+ }
+ }
+ }
+ if (condition.mSatisfier.getAsBoolean()) {
+ return true;
+ }
+
+ if (condition.mOnFailure == null) {
+ logE("Condition is not satisfied: " + condition.mMessage);
+ } else {
+ condition.mOnFailure.accept(condition.mLastResult);
+ }
+ return false;
+ }
+
+ /** @see #waitForResult(Condition) */
+ public static <T> T waitForResult(String message, Consumer<Condition<T>> setup) {
+ final Condition<T> condition = new Condition<>(message);
+ setup.accept(condition);
+ return waitForResult(condition);
+ }
+
+ /**
+ * @return {@code null} if the condition does not satisfy within the time limit or the result
+ * supplier returns {@code null}.
+ */
+ public static <T> T waitForResult(Condition<T> condition) {
+ condition.mLastResult = condition.mValidatedResult = null;
+ condition.prepareSatisfier();
+ waitFor(condition);
+ if (condition.mValidatedResult != null) {
+ return condition.mValidatedResult;
+ }
+ return condition.mReturnLastResult ? condition.mLastResult : null;
+ }
+}
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/TestJournalProvider.java b/tests/framework/base/windowmanager/util/src/android/server/wm/TestJournalProvider.java
index b2d92a3..aa3d63f 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/TestJournalProvider.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/TestJournalProvider.java
@@ -274,9 +274,9 @@
* Perform the action which may have thread safety concerns when accessing the fields of
* {@link TestJournal}.
*/
- public static void withThreadSafeAccess(Runnable aciton) {
+ public static void withThreadSafeAccess(Runnable action) {
synchronized (getInstance()) {
- aciton.run();
+ action.run();
}
}
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/UiDeviceUtils.java b/tests/framework/base/windowmanager/util/src/android/server/wm/UiDeviceUtils.java
index 5da87dd..03fdf04 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/UiDeviceUtils.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/UiDeviceUtils.java
@@ -23,7 +23,7 @@
import static android.view.KeyEvent.KEYCODE_WAKEUP;
import static android.view.KeyEvent.KEYCODE_WINDOW;
-import static androidx.test.InstrumentationRegistry.getInstrumentation;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import android.app.KeyguardManager;
import android.graphics.Point;
@@ -66,6 +66,10 @@
getDevice().pressEnter();
}
+ /**
+ * Simulates a pressed event of {@link KeyEvent#KEYCODE_HOME}. Note this will stop app switches
+ * for 5s (see android.permission.STOP_APP_SWITCHES).
+ */
public static void pressHomeButton() {
if (DEBUG) Log.d(TAG, "pressHomeButton");
getDevice().pressHome();
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerState.java b/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerState.java
index 0ca2748..f80ffee 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerState.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerState.java
@@ -117,6 +117,7 @@
private List<Display> mDisplays = new ArrayList();
private String mFocusedWindow = null;
private String mFocusedApp = null;
+ private int mFocusedDisplayId = DEFAULT_DISPLAY;
private String mInputMethodWindowAppToken = null;
private Rect mDefaultPinnedStackBounds = new Rect();
private Rect mPinnedStackMovementBounds = new Rect();
@@ -235,6 +236,7 @@
mDisplayFrozen = state.displayFrozen;
mRotation = state.rotation;
mLastOrientation = state.lastOrientation;
+ mFocusedDisplayId = state.focusedDisplayId;
}
static String appStateToString(int appState) {
@@ -445,6 +447,10 @@
return mLastOrientation;
}
+ int getFocusedDisplayId() {
+ return mFocusedDisplayId;
+ }
+
boolean containsStack(int stackId) {
for (WindowStack stack : mStacks) {
if (stackId == stack.mStackId) {
@@ -595,6 +601,7 @@
mPinnedStackMovementBounds.setEmpty();
mRotation = 0;
mLastOrientation = 0;
+ mFocusedDisplayId = DEFAULT_DISPLAY;
mDisplayFrozen = false;
}
@@ -602,7 +609,6 @@
int mStackId;
ArrayList<WindowTask> mTasks = new ArrayList<>();
- boolean mWindowAnimationBackgroundSurfaceShowing;
boolean mAnimatingBounds;
WindowStack(StackProto proto) {
@@ -616,7 +622,6 @@
mTasks.add(task);
mSubWindows.addAll(task.getWindows());
}
- mWindowAnimationBackgroundSurfaceShowing = proto.animationBackgroundSurfaceIsDimming;
mAnimatingBounds = proto.animatingBounds;
}
@@ -628,10 +633,6 @@
}
return null;
}
-
- boolean isWindowAnimationBackgroundSurfaceShowing() {
- return mWindowAnimationBackgroundSurfaceShowing;
- }
}
static class WindowTask extends WindowContainer {
@@ -730,6 +731,7 @@
private Rect mDisplayRect = new Rect();
private Rect mAppRect = new Rect();
private int mDpi;
+ private int mFlags;
private Rect mStableBounds;
private String mName;
private int mSurfaceSize;
@@ -755,6 +757,7 @@
mDisplayRect.set(0, 0, infoProto.logicalWidth, infoProto.logicalHeight);
mAppRect.set(0, 0, infoProto.appWidth, infoProto.appHeight);
mName = infoProto.name;
+ mFlags = infoProto.flags;
}
final DisplayFramesProto displayFramesProto = proto.displayFrames;
if (displayFramesProto != null) {
@@ -795,14 +798,18 @@
return mDisplayRect;
}
- Rect getAppRect() {
- return mAppRect;
+ Rect getStableBounds() {
+ return mStableBounds;
}
String getName() {
return mName;
}
+ int getFlags() {
+ return mFlags;
+ }
+
int getSurfaceSize() {
return mSurfaceSize;
}
@@ -818,7 +825,7 @@
@Override
public String toString() {
return "Display #" + mDisplayId + ": name=" + mName + " mDisplayRect=" + mDisplayRect
- + " mAppRect=" + mAppRect;
+ + " mAppRect=" + mAppRect + " mFlags=" + mFlags;
}
}
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/settings/SettingsSession.java b/tests/framework/base/windowmanager/util/src/android/server/wm/settings/SettingsSession.java
index fa27ee3..e5194c8 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/settings/SettingsSession.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/settings/SettingsSession.java
@@ -80,10 +80,10 @@
private static final SessionCounters sSessionCounters = new SessionCounters();
protected final Uri mUri;
+ protected final boolean mHasInitialValue;
+ protected final T mInitialValue;
private final SettingsGetter<T> mGetter;
private final SettingsSetter<T> mSetter;
- private final boolean mHasInitialValue;
- private final T mInitialValue;
public SettingsSession(final Uri uri, final SettingsGetter<T> getter,
final SettingsSetter<T> setter) {
@@ -152,7 +152,7 @@
return getter.get(getContentResolver(), uri.getLastPathSegment());
}
- private static void delete(final Uri uri) {
+ protected static void delete(final Uri uri) {
final List<String> segments = uri.getPathSegments();
if (segments.size() != 2) {
Log.w(TAG, "Unsupported uri for deletion: " + uri, new Throwable());
diff --git a/tests/inputmethod/AndroidTest.xml b/tests/inputmethod/AndroidTest.xml
index f64bd17..ce5e679 100644
--- a/tests/inputmethod/AndroidTest.xml
+++ b/tests/inputmethod/AndroidTest.xml
@@ -20,6 +20,7 @@
<option name="config-descriptor:metadata" key="component" value="inputmethod" />
<option name="config-descriptor:metadata" key="parameter" value="instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
<!--
TODO(yukawa): come up with a proper way to take care of devices that do not support
installable IMEs. Ideally target_preparer should have an option to annotate required
diff --git a/tests/inputmethod/mockime/Android.bp b/tests/inputmethod/mockime/Android.bp
index 7ee21d3..42f057a 100644
--- a/tests/inputmethod/mockime/Android.bp
+++ b/tests/inputmethod/mockime/Android.bp
@@ -34,7 +34,7 @@
optimize: {
enabled: false,
},
- sdk_version: "test_current",
+ sdk_version: "current",
min_sdk_version: "19",
// tag this module as a cts test artifact
test_suites: [
diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSession.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSession.java
index 8ad306b..5e653c6 100644
--- a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSession.java
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSession.java
@@ -18,7 +18,7 @@
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
import android.app.UiAutomation;
import android.content.BroadcastReceiver;
@@ -26,6 +26,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
@@ -40,7 +41,6 @@
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputContentInfo;
import android.view.inputmethod.InputMethodManager;
-import android.view.inputmethod.InputMethodSystemProperty;
import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
@@ -48,6 +48,8 @@
import com.android.compatibility.common.util.PollingCheck;
+import org.junit.AssumptionViolatedException;
+
import java.io.IOException;
import java.util.concurrent.TimeUnit;
@@ -238,10 +240,10 @@
@NonNull Context context,
@NonNull UiAutomation uiAutomation,
@Nullable ImeSettings.Builder imeSettings) throws Exception {
- // Currently, MockIme doesn't fully support multi-client IME. Skip tests until it does.
- // TODO: Re-enable when MockIme supports multi-client IME.
- assumeFalse("MockIme session doesn't support Multi-Client IME, skip it",
- InputMethodSystemProperty.MULTI_CLIENT_IME_ENABLED);
+ final String unavailabilityReason = getUnavailabilityReason(context);
+ if (unavailabilityReason != null) {
+ throw new AssumptionViolatedException(unavailabilityReason);
+ }
final MockImeSession client = new MockImeSession(context, uiAutomation);
client.initialize(imeSettings);
@@ -249,6 +251,19 @@
}
/**
+ * Checks if the {@link MockIme} can be used in this device.
+ *
+ * @return {@code null} if it can be used, or message describing why if it cannot.
+ */
+ @Nullable
+ public static String getUnavailabilityReason(@NonNull Context context) {
+ if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_INPUT_METHODS)) {
+ return "Device must support installable IMEs that implement InputMethodService API";
+ }
+ return null;
+ }
+
+ /**
* @return {@link ImeEventStream} object that stores events sent from {@link MockIme} since the
* session is created.
*/
diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSessionRule.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSessionRule.java
index 041d2b8..3eccac3 100644
--- a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSessionRule.java
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockImeSessionRule.java
@@ -62,7 +62,12 @@
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Creating MockImeSession on " + description.getDisplayName());
}
- mMockImeSession = MockImeSession.create(mContext, mUiAutomation, mImeSettings);
+ final String errorMsg = MockImeSession.getUnavailabilityReason(mContext);
+ if (errorMsg != null) {
+ Log.w(TAG, "Mock IME not available: " + errorMsg);
+ } else {
+ mMockImeSession = MockImeSession.create(mContext, mUiAutomation, mImeSettings);
+ }
try {
base.evaluate();
} finally {
diff --git a/tests/leanbackjank/OWNERS b/tests/leanbackjank/OWNERS
new file mode 100644
index 0000000..729b9b6
--- /dev/null
+++ b/tests/leanbackjank/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 188489
+dake@google.com
\ No newline at end of file
diff --git a/tests/perfetto/OWNERS b/tests/perfetto/OWNERS
new file mode 100644
index 0000000..05798d7
--- /dev/null
+++ b/tests/perfetto/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 323270
+primiano@google.com
+lalitm@google.com
diff --git a/tests/rollback/Android.bp b/tests/rollback/Android.bp
index 080a2d5..3b3e617 100644
--- a/tests/rollback/Android.bp
+++ b/tests/rollback/Android.bp
@@ -15,7 +15,7 @@
android_test {
name: "CtsRollbackManagerTestCases",
srcs: ["src/**/*.java"],
- static_libs: ["androidx.test.rules", "cts-rollback-lib"],
+ static_libs: ["androidx.test.rules", "cts-rollback-lib", "cts-install-lib"],
test_suites: ["general-tests"],
sdk_version: "test_current",
}
diff --git a/tests/rollback/AndroidManifest.xml b/tests/rollback/AndroidManifest.xml
index 5de7f87..3203f25 100644
--- a/tests/rollback/AndroidManifest.xml
+++ b/tests/rollback/AndroidManifest.xml
@@ -19,7 +19,7 @@
package="com.android.cts.rollback" >
<application>
- <receiver android:name="com.android.cts.rollback.lib.LocalIntentSender"
+ <receiver android:name="com.android.cts.install.lib.LocalIntentSender"
android:exported="true" />
<uses-library android:name="android.test.runner" />
</application>
diff --git a/tests/rollback/AndroidTest.xml b/tests/rollback/AndroidTest.xml
index 2fca9d0..534ed1e 100644
--- a/tests/rollback/AndroidTest.xml
+++ b/tests/rollback/AndroidTest.xml
@@ -23,8 +23,8 @@
<option name="test-file-name" value="CtsRollbackManagerTestCases.apk"/>
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
- <option name="run-command" value="pm uninstall com.android.cts.rollback.lib.testapp.A" />
- <option name="teardown-command" value="pm uninstall com.android.cts.rollback.lib.testapp.A" />
+ <option name="run-command" value="pm uninstall com.android.cts.install.lib.testapp.A" />
+ <option name="teardown-command" value="pm uninstall com.android.cts.install.lib.testapp.A" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest">
<option name="package" value="com.android.cts.rollback"/>
diff --git a/tests/rollback/OWNERS b/tests/rollback/OWNERS
new file mode 100644
index 0000000..5a631b6
--- /dev/null
+++ b/tests/rollback/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 557916
+include /hostsidetests/rollback/OWNERS
diff --git a/tests/rollback/src/android/server/cts/rollback/RollbackManagerTest.java b/tests/rollback/src/com/android/cts/rollback/RollbackManagerTest.java
similarity index 70%
rename from tests/rollback/src/android/server/cts/rollback/RollbackManagerTest.java
rename to tests/rollback/src/com/android/cts/rollback/RollbackManagerTest.java
index f7ab15d..2df3912 100644
--- a/tests/rollback/src/android/server/cts/rollback/RollbackManagerTest.java
+++ b/tests/rollback/src/com/android/cts/rollback/RollbackManagerTest.java
@@ -25,10 +25,12 @@
import androidx.test.InstrumentationRegistry;
-import com.android.cts.rollback.lib.Install;
+import com.android.cts.install.lib.Install;
+import com.android.cts.install.lib.InstallUtils;
+import com.android.cts.install.lib.TestApp;
+import com.android.cts.install.lib.Uninstall;
import com.android.cts.rollback.lib.Rollback;
-import com.android.cts.rollback.lib.TestApp;
-import com.android.cts.rollback.lib.Utils;
+import com.android.cts.rollback.lib.RollbackUtils;
import org.junit.After;
import org.junit.Before;
@@ -56,7 +58,7 @@
Manifest.permission.DELETE_PACKAGES,
Manifest.permission.TEST_MANAGE_ROLLBACKS);
- Utils.uninstall(TestApp.A);
+ Uninstall.packages(TestApp.A);
}
/**
@@ -64,7 +66,7 @@
*/
@After
public void teardown() throws InterruptedException, IOException {
- Utils.uninstall(TestApp.A);
+ Uninstall.packages(TestApp.A);
InstrumentationRegistry.getInstrumentation().getUiAutomation()
.dropShellPermissionIdentity();
@@ -76,23 +78,24 @@
@Test
public void testBasic() throws Exception {
Install.single(TestApp.A1).commit();
- assertThat(Utils.getInstalledVersion(TestApp.A)).isEqualTo(1);
- assertThat(Utils.getAvailableRollback(TestApp.A)).isNull();
- assertThat(Utils.getCommittedRollback(TestApp.A)).isNull();
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+ assertThat(RollbackUtils.getAvailableRollback(TestApp.A)).isNull();
+ assertThat(RollbackUtils.getCommittedRollback(TestApp.A)).isNull();
Install.single(TestApp.A2).setEnableRollback().commit();
- assertThat(Utils.getInstalledVersion(TestApp.A)).isEqualTo(2);
- RollbackInfo available = Utils.getAvailableRollback(TestApp.A);
- assertThat(available).isNotNull();
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
+ InstallUtils.processUserData(TestApp.A);
+ RollbackInfo available = RollbackUtils.waitForAvailableRollback(TestApp.A);
assertThat(available).isNotStaged();
assertThat(available).packagesContainsExactly(
Rollback.from(TestApp.A2).to(TestApp.A1));
- assertThat(Utils.getCommittedRollback(TestApp.A)).isNull();
+ assertThat(RollbackUtils.getCommittedRollback(TestApp.A)).isNull();
- Utils.rollback(available.getRollbackId(), TestApp.A2);
- assertThat(Utils.getInstalledVersion(TestApp.A)).isEqualTo(1);
- assertThat(Utils.getAvailableRollback(TestApp.A)).isNull();
- RollbackInfo committed = Utils.getCommittedRollback(TestApp.A);
+ RollbackUtils.rollback(available.getRollbackId(), TestApp.A2);
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+ InstallUtils.processUserData(TestApp.A);
+ RollbackUtils.waitForUnavailableRollback(TestApp.A);
+ RollbackInfo committed = RollbackUtils.getCommittedRollback(TestApp.A);
assertThat(committed).isNotNull();
assertThat(committed).hasRollbackId(available.getRollbackId());
assertThat(committed).isNotStaged();
diff --git a/tests/sample/OWNERS b/tests/sample/OWNERS
new file mode 100644
index 0000000..7782fbe
--- /dev/null
+++ b/tests/sample/OWNERS
@@ -0,0 +1,10 @@
+# Bug component: 346961
+diqian@google.com
+fdeng@google.com
+moonk@google.com
+normancheung@google.com
+williamgoh@google.com
+peykov@google.com
+yichunli@google.com
+yimingpan@google.com
+
diff --git a/tests/security/src/android/keystore/cts/KeyGenerationUtils.java b/tests/security/src/android/keystore/cts/KeyGenerationUtils.java
index 27c9a8c..feadc04 100644
--- a/tests/security/src/android/keystore/cts/KeyGenerationUtils.java
+++ b/tests/security/src/android/keystore/cts/KeyGenerationUtils.java
@@ -43,18 +43,28 @@
.build();
}
+ private static AttestedKeyPair generateRsaKeyPair(DevicePolicyManager dpm, ComponentName admin,
+ int deviceIdAttestationFlags, String alias) {
+ return dpm.generateKeyPair(
+ admin, "RSA", buildRsaKeySpecWithKeyAttestation(alias),
+ deviceIdAttestationFlags);
+ }
+
private static void generateKeyWithIdFlagsExpectingSuccess(DevicePolicyManager dpm,
ComponentName admin, int deviceIdAttestationFlags) {
try {
- AttestedKeyPair generated = dpm.generateKeyPair(
- admin, "RSA", buildRsaKeySpecWithKeyAttestation(ALIAS),
- deviceIdAttestationFlags);
+ AttestedKeyPair generated =
+ generateRsaKeyPair(dpm, admin, deviceIdAttestationFlags, ALIAS);
assertThat(generated).isNotNull();
} finally {
assertThat(dpm.removeKeyPair(admin, ALIAS)).isTrue();
}
}
+ public static void generateRsaKey(DevicePolicyManager dpm, ComponentName admin, String alias) {
+ assertThat(generateRsaKeyPair(dpm, admin, 0, alias)).isNotNull();
+ }
+
public static void generateKeyWithDeviceIdAttestationExpectingSuccess(DevicePolicyManager dpm,
ComponentName admin) {
generateKeyWithIdFlagsExpectingSuccess(dpm, admin, ID_TYPE_SERIAL);
diff --git a/tests/signature/api-check/hidden-api-blacklist-27-api/OWNERS b/tests/signature/api-check/hidden-api-blacklist-27-api/OWNERS
index d658bff..e840a10 100644
--- a/tests/signature/api-check/hidden-api-blacklist-27-api/OWNERS
+++ b/tests/signature/api-check/hidden-api-blacklist-27-api/OWNERS
@@ -1,4 +1,2 @@
-# Bug component: 86431
-dbrazdil@google.com
-mathewi@google.com
-ngeoffray@google.com
+# Bug component: 610774
+include ../hidden-api-blacklist-current-api/OWNERS
\ No newline at end of file
diff --git a/tests/signature/api-check/hidden-api-blacklist-28-api/OWNERS b/tests/signature/api-check/hidden-api-blacklist-28-api/OWNERS
index d658bff..e840a10 100644
--- a/tests/signature/api-check/hidden-api-blacklist-28-api/OWNERS
+++ b/tests/signature/api-check/hidden-api-blacklist-28-api/OWNERS
@@ -1,4 +1,2 @@
-# Bug component: 86431
-dbrazdil@google.com
-mathewi@google.com
-ngeoffray@google.com
+# Bug component: 610774
+include ../hidden-api-blacklist-current-api/OWNERS
\ No newline at end of file
diff --git a/tests/signature/api-check/hidden-api-blacklist-current-api/OWNERS b/tests/signature/api-check/hidden-api-blacklist-current-api/OWNERS
index d658bff..39dbe17 100644
--- a/tests/signature/api-check/hidden-api-blacklist-current-api/OWNERS
+++ b/tests/signature/api-check/hidden-api-blacklist-current-api/OWNERS
@@ -1,4 +1,7 @@
-# Bug component: 86431
-dbrazdil@google.com
+# Bug component: 610774
+platform-compat-eng+reviews@google.com
+andreionea@google.com
+atrost@google.com
mathewi@google.com
ngeoffray@google.com
+satayev@google.com
diff --git a/tests/signature/api-check/hidden-api-blacklist-debug-class/OWNERS b/tests/signature/api-check/hidden-api-blacklist-debug-class/OWNERS
index d658bff..e840a10 100644
--- a/tests/signature/api-check/hidden-api-blacklist-debug-class/OWNERS
+++ b/tests/signature/api-check/hidden-api-blacklist-debug-class/OWNERS
@@ -1,4 +1,2 @@
-# Bug component: 86431
-dbrazdil@google.com
-mathewi@google.com
-ngeoffray@google.com
+# Bug component: 610774
+include ../hidden-api-blacklist-current-api/OWNERS
\ No newline at end of file
diff --git a/tests/signature/api-check/hidden-api-killswitch-debug-class/OWNERS b/tests/signature/api-check/hidden-api-killswitch-debug-class/OWNERS
index d658bff..e840a10 100644
--- a/tests/signature/api-check/hidden-api-killswitch-debug-class/OWNERS
+++ b/tests/signature/api-check/hidden-api-killswitch-debug-class/OWNERS
@@ -1,4 +1,2 @@
-# Bug component: 86431
-dbrazdil@google.com
-mathewi@google.com
-ngeoffray@google.com
+# Bug component: 610774
+include ../hidden-api-blacklist-current-api/OWNERS
\ No newline at end of file
diff --git a/tests/signature/api-check/hidden-api-killswitch-whitelist/OWNERS b/tests/signature/api-check/hidden-api-killswitch-whitelist/OWNERS
index d658bff..e840a10 100644
--- a/tests/signature/api-check/hidden-api-killswitch-whitelist/OWNERS
+++ b/tests/signature/api-check/hidden-api-killswitch-whitelist/OWNERS
@@ -1,4 +1,2 @@
-# Bug component: 86431
-dbrazdil@google.com
-mathewi@google.com
-ngeoffray@google.com
+# Bug component: 610774
+include ../hidden-api-blacklist-current-api/OWNERS
\ No newline at end of file
diff --git a/tests/signature/api-check/hidden-api-killswitch-wildcard/OWNERS b/tests/signature/api-check/hidden-api-killswitch-wildcard/OWNERS
index d658bff..e840a10 100644
--- a/tests/signature/api-check/hidden-api-killswitch-wildcard/OWNERS
+++ b/tests/signature/api-check/hidden-api-killswitch-wildcard/OWNERS
@@ -1,4 +1,2 @@
-# Bug component: 86431
-dbrazdil@google.com
-mathewi@google.com
-ngeoffray@google.com
+# Bug component: 610774
+include ../hidden-api-blacklist-current-api/OWNERS
\ No newline at end of file
diff --git a/tests/tests/animation/OWNERS b/tests/tests/animation/OWNERS
new file mode 100644
index 0000000..475f1b8
--- /dev/null
+++ b/tests/tests/animation/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 47085
+tianliu@google.com
+mount@google.com
+andreykulikov@google.com
diff --git a/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java b/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java
index 5d666d2..7fe1b0a 100644
--- a/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java
+++ b/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java
@@ -587,6 +587,20 @@
mUiDevice.pressBack();
}
+ @AppModeFull(reason = "No usage events access in instant apps")
+ @Test
+ public void testUserUnlockedEventExists() throws Exception {
+ final UsageEvents events = mUsageStatsManager.queryEvents(0, System.currentTimeMillis());
+ while (events.hasNextEvent()) {
+ final Event event = new Event();
+ events.getNextEvent(event);
+ if (event.mEventType == Event.USER_UNLOCKED) {
+ return;
+ }
+ }
+ fail("Couldn't find a user unlocked event.");
+ }
+
static final int[] INTERACTIVE_EVENTS = new int[] {
Event.SCREEN_INTERACTIVE,
Event.SCREEN_NON_INTERACTIVE
diff --git a/tests/tests/appenumeration/Android.bp b/tests/tests/appenumeration/Android.bp
new file mode 100644
index 0000000..5bb2e24
--- /dev/null
+++ b/tests/tests/appenumeration/Android.bp
@@ -0,0 +1,32 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test {
+ name: "CtsAppEnumerationTestCases",
+ defaults: ["cts_defaults"],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+ static_libs: [
+ "ctstestrunner-axt",
+ "compatibility-device-util-axt",
+ "androidx.test.ext.junit",
+ ],
+
+ srcs: ["src/**/*.java"],
+ sdk_version: "test_current",
+}
diff --git a/tests/tests/appenumeration/AndroidManifest.xml b/tests/tests/appenumeration/AndroidManifest.xml
new file mode 100644
index 0000000..7777a9f7
--- /dev/null
+++ b/tests/tests/appenumeration/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.appenumeration.cts">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.appenumeration.cts"
+ android:label="CTS tests for app enumeration">
+ <meta-data android:name="listener"
+ android:value="com.android.cts.runner.CtsTestRunListener" />
+ </instrumentation>
+</manifest>
+
diff --git a/tests/tests/appenumeration/AndroidTest.xml b/tests/tests/appenumeration/AndroidTest.xml
new file mode 100644
index 0000000..eb77b32
--- /dev/null
+++ b/tests/tests/appenumeration/AndroidTest.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for app enumeration CTS test cases">
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="framework" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+
+ <!-- Force service to be installed as non-instant mode, always -->
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsAppEnumerationTestCases.apk" />
+ <option name="test-file-name" value="CtsAppEnumerationForceQueryable.apk" />
+ <option name="test-file-name" value="CtsAppEnumerationFilters.apk" />
+ <option name="test-file-name" value="CtsAppEnumerationNoApi.apk" />
+ <option name="test-file-name" value="CtsAppEnumerationQueriesNothing.apk" />
+ <option name="test-file-name" value="CtsAppEnumerationQueriesActivityViaAction.apk" />
+ <option name="test-file-name" value="CtsAppEnumerationQueriesServiceViaAction.apk" />
+ <option name="test-file-name" value="CtsAppEnumerationQueriesProviderViaAuthority.apk" />
+ <option name="test-file-name" value="CtsAppEnumerationQueriesPackage.apk" />
+ <option name="test-file-name" value="CtsAppEnumerationQueriesNothingTargetsQ.apk" />
+ <option name="test-file-name" value="CtsAppEnumerationQueriesNothingHasPermission.apk" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.appenumeration.cts" />
+ <option name="runtime-hint" value="12m30s" />
+ </test>
+</configuration>
diff --git a/tests/tests/appenumeration/OWNERS b/tests/tests/appenumeration/OWNERS
new file mode 100644
index 0000000..8a44fb2
--- /dev/null
+++ b/tests/tests/appenumeration/OWNERS
@@ -0,0 +1,5 @@
+# Bug component: 36137
+patb@google.com
+toddke@google.com
+chiuwinson@google.com
+rtmitchell@google.com
diff --git a/tests/tests/appenumeration/app/source/Android.bp b/tests/tests/appenumeration/app/source/Android.bp
new file mode 100644
index 0000000..1d23254
--- /dev/null
+++ b/tests/tests/appenumeration/app/source/Android.bp
@@ -0,0 +1,111 @@
+// 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.
+
+android_test_helper_app {
+ name: "CtsAppEnumerationQueriesNothing",
+ manifest: "AndroidManifest-queriesNothing.xml",
+ defaults: ["cts_support_defaults"],
+ srcs: ["src/**/*.java"],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+ sdk_version: "test_current",
+}
+
+android_test_helper_app {
+ name: "CtsAppEnumerationQueriesActivityViaAction",
+ manifest: "AndroidManifest-queriesActivityAction.xml",
+ defaults: ["cts_support_defaults"],
+ srcs: ["src/**/*.java"],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+ sdk_version: "test_current",
+}
+
+android_test_helper_app {
+ name: "CtsAppEnumerationQueriesServiceViaAction",
+ manifest: "AndroidManifest-queriesServiceAction.xml",
+ defaults: ["cts_support_defaults"],
+ srcs: ["src/**/*.java"],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+ sdk_version: "test_current",
+}
+
+android_test_helper_app {
+ name: "CtsAppEnumerationQueriesProviderViaAuthority",
+ manifest: "AndroidManifest-queriesProviderAuthority.xml",
+ defaults: ["cts_support_defaults"],
+ srcs: ["src/**/*.java"],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+ sdk_version: "test_current",
+}
+
+android_test_helper_app {
+ name: "CtsAppEnumerationQueriesPackage",
+ manifest: "AndroidManifest-queriesPackage.xml",
+ defaults: ["cts_support_defaults"],
+ srcs: ["src/**/*.java"],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+ sdk_version: "test_current",
+}
+
+android_test_helper_app {
+ name: "CtsAppEnumerationQueriesNothingTargetsQ",
+ manifest: "AndroidManifest-queriesNothing-targetsQ.xml",
+ defaults: ["cts_support_defaults"],
+ srcs: ["src/**/*.java"],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+ sdk_version: "test_current",
+}
+
+android_test_helper_app {
+ name: "CtsAppEnumerationQueriesNothingHasPermission",
+ manifest: "AndroidManifest-queriesNothing-hasPermission.xml",
+ defaults: ["cts_support_defaults"],
+ srcs: ["src/**/*.java"],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+ sdk_version: "test_current",
+}
\ No newline at end of file
diff --git a/tests/tests/appenumeration/app/source/AndroidManifest-queriesActivityAction.xml b/tests/tests/appenumeration/app/source/AndroidManifest-queriesActivityAction.xml
new file mode 100644
index 0000000..2eba524
--- /dev/null
+++ b/tests/tests/appenumeration/app/source/AndroidManifest-queriesActivityAction.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.appenumeration.queries.activity.action">
+
+ <queries>
+ <intent>
+ <action android:name="android.appenumeration.action.ACTIVITY" />
+ </intent>
+ </queries>
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ <activity android:name="android.appenumeration.cts.query.TestActivity"
+ android:exported="true" />
+ </application>
+</manifest>
diff --git a/tests/tests/appenumeration/app/source/AndroidManifest-queriesNothing-hasPermission.xml b/tests/tests/appenumeration/app/source/AndroidManifest-queriesNothing-hasPermission.xml
new file mode 100644
index 0000000..09c75ae
--- /dev/null
+++ b/tests/tests/appenumeration/app/source/AndroidManifest-queriesNothing-hasPermission.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.appenumeration.queries.nothing.haspermission">
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
+ <application>
+ <uses-library android:name="android.test.runner" />
+ <activity android:name="android.appenumeration.cts.query.TestActivity"
+ android:exported="true" />
+ </application>
+</manifest>
diff --git a/tests/tests/appenumeration/app/source/AndroidManifest-queriesNothing-targetsQ.xml b/tests/tests/appenumeration/app/source/AndroidManifest-queriesNothing-targetsQ.xml
new file mode 100644
index 0000000..2810c87
--- /dev/null
+++ b/tests/tests/appenumeration/app/source/AndroidManifest-queriesNothing-targetsQ.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.appenumeration.queries.nothing.q">
+ <uses-sdk android:targetSdkVersion="29" />
+ <application>
+ <uses-library android:name="android.test.runner" />
+ <activity android:name="android.appenumeration.cts.query.TestActivity"
+ android:exported="true" />
+ </application>
+</manifest>
diff --git a/tests/tests/appenumeration/app/source/AndroidManifest-queriesNothing.xml b/tests/tests/appenumeration/app/source/AndroidManifest-queriesNothing.xml
new file mode 100644
index 0000000..ce56a77
--- /dev/null
+++ b/tests/tests/appenumeration/app/source/AndroidManifest-queriesNothing.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.appenumeration.queries.nothing">
+ <application>
+ <uses-library android:name="android.test.runner" />
+ <activity android:name="android.appenumeration.cts.query.TestActivity"
+ android:exported="true" />
+ </application>
+</manifest>
diff --git a/tests/tests/appenumeration/app/source/AndroidManifest-queriesPackage.xml b/tests/tests/appenumeration/app/source/AndroidManifest-queriesPackage.xml
new file mode 100644
index 0000000..e4bc398
--- /dev/null
+++ b/tests/tests/appenumeration/app/source/AndroidManifest-queriesPackage.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.appenumeration.queries.pkg">
+
+ <queries>
+ <package android:name="android.appenumeration.noapi" />
+ </queries>
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ <activity android:name="android.appenumeration.cts.query.TestActivity"
+ android:exported="true" />
+ </application>
+</manifest>
diff --git a/tests/tests/appenumeration/app/source/AndroidManifest-queriesProviderAuthority.xml b/tests/tests/appenumeration/app/source/AndroidManifest-queriesProviderAuthority.xml
new file mode 100644
index 0000000..6ec5a96
--- /dev/null
+++ b/tests/tests/appenumeration/app/source/AndroidManifest-queriesProviderAuthority.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.appenumeration.queries.provider.authority">
+
+ <queries>
+ <intent>
+ <data android:scheme="content" android:host="android.appenumeration.testapp" />
+ </intent>
+ </queries>
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ <activity android:name="android.appenumeration.cts.query.TestActivity"
+ android:exported="true" />
+ </application>
+</manifest>
diff --git a/tests/tests/appenumeration/app/source/AndroidManifest-queriesServiceAction.xml b/tests/tests/appenumeration/app/source/AndroidManifest-queriesServiceAction.xml
new file mode 100644
index 0000000..b451455
--- /dev/null
+++ b/tests/tests/appenumeration/app/source/AndroidManifest-queriesServiceAction.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.appenumeration.queries.service.action">
+
+ <queries>
+ <intent>
+ <action android:name="android.appenumeration.action.SERVICE" />
+ </intent>
+ </queries>
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ <activity android:name="android.appenumeration.cts.query.TestActivity"
+ android:exported="true" />
+ </application>
+</manifest>
diff --git a/tests/tests/appenumeration/app/source/src/android/appenumeration/cts/query/TestActivity.java b/tests/tests/appenumeration/app/source/src/android/appenumeration/cts/query/TestActivity.java
new file mode 100644
index 0000000..03b288d
--- /dev/null
+++ b/tests/tests/appenumeration/app/source/src/android/appenumeration/cts/query/TestActivity.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.appenumeration.cts.query;
+
+import static android.content.Intent.EXTRA_RETURN_RESULT;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.RemoteCallback;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.SparseArray;
+
+import java.util.Random;
+
+public class TestActivity extends Activity {
+
+ SparseArray<RemoteCallback> callbacks = new SparseArray<>();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ handleIntent(getIntent());
+ }
+
+ private void handleIntent(Intent intent) {
+ RemoteCallback remoteCallback = intent.getParcelableExtra("remoteCallback");
+ Bundle result = new Bundle();
+ final String action = intent.getAction();
+ final String packageName = intent.getStringExtra(
+ Intent.EXTRA_PACKAGE_NAME);
+ if ("android.appenumeration.cts.action.GET_PACKAGE_INFO".equals(action)) {
+ sendPackageInfo(remoteCallback, packageName);
+ } else if ("android.appenumeration.cts.action.START_FOR_RESULT".equals(action)) {
+ int requestCode = RESULT_FIRST_USER + callbacks.size();
+ callbacks.put(requestCode, remoteCallback);
+ startActivityForResult(
+ new Intent("android.appenumeration.cts.action.SEND_RESULT").setComponent(
+ new ComponentName(packageName, getClass().getCanonicalName())),
+ requestCode);
+ // don't send anything... await result callback
+ } else if ("android.appenumeration.cts.action.SEND_RESULT".equals(action)) {
+ try {
+ setResult(RESULT_OK,
+ getIntent().putExtra(
+ Intent.EXTRA_RETURN_RESULT,
+ getPackageManager().getPackageInfo(getCallingPackage(), 0)));
+ } catch (PackageManager.NameNotFoundException e) {
+ setResult(RESULT_FIRST_USER, new Intent().putExtra("error", e));
+ }
+ finish();
+ } else {
+ sendError(remoteCallback, new Exception("unknown action " + action));
+ }
+ }
+
+ private void sendError(RemoteCallback remoteCallback, Exception failure) {
+ Bundle result = new Bundle();
+ result.putSerializable("error", failure);
+ remoteCallback.sendResult(result);
+ finish();
+ }
+
+ private void sendPackageInfo(RemoteCallback remoteCallback, String packageName) {
+ final PackageInfo pi;
+ try {
+ pi = getPackageManager().getPackageInfo(packageName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ sendError(remoteCallback, e);
+ return;
+ }
+ Bundle result = new Bundle();
+ result.putParcelable(EXTRA_RETURN_RESULT, pi);
+ remoteCallback.sendResult(result);
+ finish();
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ final RemoteCallback remoteCallback = callbacks.get(requestCode);
+ if (resultCode != RESULT_OK) {
+ Exception e = (Exception) data.getSerializableExtra("error");
+ sendError(remoteCallback, e == null ? new Exception("Result was " + resultCode) : e);
+ return;
+ }
+ final Bundle result = new Bundle();
+ result.putParcelable(EXTRA_RETURN_RESULT, data.getParcelableExtra(EXTRA_RETURN_RESULT));
+ remoteCallback.sendResult(result);
+ finish();
+ }
+}
\ No newline at end of file
diff --git a/tests/tests/appenumeration/app/target/Android.bp b/tests/tests/appenumeration/app/target/Android.bp
new file mode 100644
index 0000000..d5f9757
--- /dev/null
+++ b/tests/tests/appenumeration/app/target/Android.bp
@@ -0,0 +1,55 @@
+// 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.
+
+android_test_helper_app {
+ name: "CtsAppEnumerationForceQueryable",
+ manifest: "AndroidManifest-forceQueryable.xml",
+ defaults: ["cts_support_defaults"],
+ srcs: ["src/**/*.java"],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+ sdk_version: "test_current",
+}
+
+android_test_helper_app {
+ name: "CtsAppEnumerationFilters",
+ manifest: "AndroidManifest-filters.xml",
+ defaults: ["cts_support_defaults"],
+ srcs: ["src/**/*.java"],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+ sdk_version: "test_current",
+}
+
+android_test_helper_app {
+ name: "CtsAppEnumerationNoApi",
+ manifest: "AndroidManifest-noapi.xml",
+ defaults: ["cts_support_defaults"],
+ srcs: ["src/**/*.java"],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+ sdk_version: "test_current",
+}
\ No newline at end of file
diff --git a/tests/tests/appenumeration/app/target/AndroidManifest-filters.xml b/tests/tests/appenumeration/app/target/AndroidManifest-filters.xml
new file mode 100644
index 0000000..e6e61f7
--- /dev/null
+++ b/tests/tests/appenumeration/app/target/AndroidManifest-filters.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.appenumeration.filters">
+ <application>
+ <uses-library android:name="android.test.runner" />
+ <activity android:name="android.appenumeration.testapp.DummyActivity">
+ <intent-filter>
+ <action android:name="android.appenumeration.action.ACTIVITY" />
+ </intent-filter>
+ </activity>
+ <service android:name="android.appenumeration.testapp.DummyService">
+ <intent-filter>
+ <action android:name="android.appenumeration.action.SERVICE" />
+ </intent-filter>
+ </service>
+ <provider android:name="android.appenumeration.testapp.DummyProvider"
+ android:authorities="android.appenumeration.testapp"
+ android:exported="true" />
+ <receiver android:name="android.appenumeration.testapp.DummyReceiver">
+ <intent-filter>
+ <action android:name="android.appenumeration.action.BROADCAST" />
+ </intent-filter>
+ </receiver>
+ </application>
+</manifest>
diff --git a/tests/tests/appenumeration/app/target/AndroidManifest-forceQueryable.xml b/tests/tests/appenumeration/app/target/AndroidManifest-forceQueryable.xml
new file mode 100644
index 0000000..e6535b3
--- /dev/null
+++ b/tests/tests/appenumeration/app/target/AndroidManifest-forceQueryable.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.appenumeration.forcequeryable">
+ <application android:forceQueryable="true">
+ <uses-library android:name="android.test.runner" />
+ </application>
+</manifest>
diff --git a/tests/tests/appenumeration/app/target/AndroidManifest-noapi.xml b/tests/tests/appenumeration/app/target/AndroidManifest-noapi.xml
new file mode 100644
index 0000000..9b25acc
--- /dev/null
+++ b/tests/tests/appenumeration/app/target/AndroidManifest-noapi.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.appenumeration.noapi">
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+</manifest>
diff --git a/tests/tests/appenumeration/src/android/appenumeration/cts/AppEnumerationTests.java b/tests/tests/appenumeration/src/android/appenumeration/cts/AppEnumerationTests.java
new file mode 100644
index 0000000..d3a74ea
--- /dev/null
+++ b/tests/tests/appenumeration/src/android/appenumeration/cts/AppEnumerationTests.java
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.appenumeration.cts;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.ConditionVariable;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.RemoteCallback;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+import org.hamcrest.core.IsNull;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestName;
+import org.junit.runner.RunWith;
+
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicReference;
+
+@RunWith(AndroidJUnit4.class)
+public class AppEnumerationTests {
+
+ private static final String PKG_BASE = "android.appenumeration.";
+
+ /** A package with no published API and so isn't queryable by anything but package name */
+ private static final String TARGET_NO_API = PKG_BASE + "noapi";
+ /** A package that declares itself force queryable, making it visible to all other packages */
+ private static final String TARGET_FORCEQUERYABLE = PKG_BASE + "forcequeryable";
+ /** A package that exposes itself via various intent filters (activities, services, etc.) */
+ private static final String TARGET_FILTERS = PKG_BASE + "filters";
+
+ /** A package that has no queries tag or permission to query any specific packages */
+ private static final String QUERIES_NOTHING = PKG_BASE + "queries.nothing";
+ /** A package that has no queries tag or permissions but targets Q */
+ private static final String QUERIES_NOTHING_Q = PKG_BASE + "queries.nothing.q";
+ /** A package that has no queries but gets the QUERY_ALL_PACKAGES permission */
+ private static final String QUERIES_NOTHING_PERM = PKG_BASE + "queries.nothing.haspermission";
+ /** A package that queries for the action in {@link #TARGET_FILTERS} activity filter */
+ private static final String QUERIES_ACTIVITY_ACTION = PKG_BASE + "queries.activity.action";
+ /** A package that queries for the action in {@link #TARGET_FILTERS} service filter */
+ private static final String QUERIES_SERVICE_ACTION = PKG_BASE + "queries.service.action";
+ /** A package that queries for the authority in {@link #TARGET_FILTERS} provider */
+ private static final String QUERIES_PROVIDER_AUTH = PKG_BASE + "queries.provider.authority";
+ /** A package that queries for {@link #TARGET_NO_API} package */
+ private static final String QUERIES_PACKAGE = PKG_BASE + "queries.pkg";
+
+ private static final String[] ALL_QUERIES_PACKAGES = {
+ QUERIES_NOTHING,
+ QUERIES_NOTHING_Q,
+ QUERIES_NOTHING_PERM,
+ QUERIES_ACTIVITY_ACTION,
+ QUERIES_SERVICE_ACTION,
+ QUERIES_PROVIDER_AUTH,
+ QUERIES_PACKAGE,
+ };
+
+ private static Context sContext;
+ private static Handler sResponseHandler;
+ private static HandlerThread sResponseThread;
+
+ private static boolean sGlobalFeatureEnabled;
+
+ @Rule
+ public TestName name = new TestName();
+
+ @BeforeClass
+ public static void setup() {
+ final String deviceConfigResponse =
+ SystemUtil.runShellCommand(
+ "device_config get package_manager_service "
+ + "package_query_filtering_enabled")
+ .trim();
+ if ("null".equalsIgnoreCase(deviceConfigResponse) || deviceConfigResponse.isEmpty()) {
+ sGlobalFeatureEnabled = true;
+ } else {
+ sGlobalFeatureEnabled = Boolean.parseBoolean(deviceConfigResponse);
+ }
+ System.out.println("Feature enabled: " + sGlobalFeatureEnabled);
+ if (!sGlobalFeatureEnabled) return;
+
+ sContext = InstrumentationRegistry.getInstrumentation().getContext();
+ sResponseThread = new HandlerThread("response");
+ sResponseThread.start();
+ sResponseHandler = new Handler(sResponseThread.getLooper());
+ }
+
+ @Before
+ public void setupTest() {
+ if (!sGlobalFeatureEnabled) return;
+
+ setFeatureEnabledForAll(true);
+ }
+
+ @AfterClass
+ public static void tearDown() {
+ if (!sGlobalFeatureEnabled) return;
+ sResponseThread.quit();
+ }
+
+ @Test
+ public void all_canSeeForceQueryable() throws Exception {
+ assertVisible(QUERIES_NOTHING, TARGET_FORCEQUERYABLE);
+ assertVisible(QUERIES_ACTIVITY_ACTION, TARGET_FORCEQUERYABLE);
+ assertVisible(QUERIES_SERVICE_ACTION, TARGET_FORCEQUERYABLE);
+ assertVisible(QUERIES_PROVIDER_AUTH, TARGET_FORCEQUERYABLE);
+ assertVisible(QUERIES_PACKAGE, TARGET_FORCEQUERYABLE);
+ }
+
+ @Test
+ public void queriesNothing_cannotSeeNonForceQueryable() throws Exception {
+ assertNotVisible(QUERIES_NOTHING, TARGET_NO_API);
+ assertNotVisible(QUERIES_NOTHING, TARGET_FILTERS);
+ }
+
+ @Test
+ public void queriesNothing_featureOff_canSeeAll() throws Exception {
+ setFeatureEnabledForAll(QUERIES_NOTHING, false);
+ assertVisible(QUERIES_NOTHING, TARGET_NO_API);
+ assertVisible(QUERIES_NOTHING, TARGET_FILTERS);
+ }
+
+ @Test
+ public void queriesNothingTargetsQ_canSeeAll() throws Exception {
+ assertVisible(QUERIES_NOTHING_Q, TARGET_FORCEQUERYABLE);
+ assertVisible(QUERIES_NOTHING_Q, TARGET_NO_API);
+ assertVisible(QUERIES_NOTHING_Q, TARGET_FILTERS);
+ }
+
+ @Test
+ public void queriesNothingHasPermission_canSeeAll() throws Exception {
+ assertVisible(QUERIES_NOTHING_PERM, TARGET_FORCEQUERYABLE);
+ assertVisible(QUERIES_NOTHING_PERM, TARGET_NO_API);
+ assertVisible(QUERIES_NOTHING_PERM, TARGET_FILTERS);
+ }
+
+ @Test
+ public void queriesSomething_cannotSeeNoApi() throws Exception {
+ assertNotVisible(QUERIES_ACTIVITY_ACTION, TARGET_NO_API);
+ assertNotVisible(QUERIES_SERVICE_ACTION, TARGET_NO_API);
+ assertNotVisible(QUERIES_PROVIDER_AUTH, TARGET_NO_API);
+ }
+
+ @Test
+ public void queriesActivityAction_canSeeTarget() throws Exception {
+ assertVisible(QUERIES_ACTIVITY_ACTION, TARGET_FILTERS);
+ }
+
+ @Test
+ public void queriesServiceAction_canSeeTarget() throws Exception {
+ assertVisible(QUERIES_SERVICE_ACTION, TARGET_FILTERS);
+ }
+
+ @Test
+ public void queriesProviderAuthority_canSeeTarget() throws Exception {
+ assertVisible(QUERIES_PROVIDER_AUTH, TARGET_FILTERS);
+ }
+
+ @Test
+ public void queriesPackage_canSeeTarget() throws Exception {
+ assertVisible(QUERIES_PACKAGE, TARGET_NO_API);
+
+ }
+ @Test
+ public void whenStarted_canSeeCaller() throws Exception {
+ // let's first make sure that the target cannot see the caller.
+ assertNotVisible(QUERIES_NOTHING, QUERIES_NOTHING_PERM);
+ // now let's start the target and make sure that it can see the caller as part of that call
+ PackageInfo packageInfo = startForResult(QUERIES_NOTHING_PERM, QUERIES_NOTHING);
+ assertThat(packageInfo, IsNull.notNullValue());
+ assertThat(packageInfo.packageName, is(QUERIES_NOTHING_PERM));
+ // and finally let's re-run the last check to make sure that the target can still see the
+ // caller
+ assertVisible(QUERIES_NOTHING, QUERIES_NOTHING_PERM);
+ }
+
+ private void assertVisible(String sourcePackageName, String targetPackageName)
+ throws Exception {
+ if (!sGlobalFeatureEnabled) return;
+ Assert.assertNotNull(sourcePackageName + " should be able to see " + targetPackageName,
+ getPackageInfo(sourcePackageName, targetPackageName));
+ }
+
+
+ private void setFeatureEnabledForAll(Boolean enabled) {
+ for (String pkgName : ALL_QUERIES_PACKAGES) {
+ setFeatureEnabledForAll(pkgName, enabled);
+ }
+ }
+
+ private void setFeatureEnabledForAll(String packageName, Boolean enabled) {
+ SystemUtil.runShellCommand(
+ "am compat " + (enabled == null ? "reset" : enabled ? "enable" : "disable")
+ + " 135549675 " + packageName);
+ }
+
+ private void assertNotVisible(String sourcePackageName, String targetPackageName)
+ throws Exception {
+ if (!sGlobalFeatureEnabled) return;
+ try {
+ getPackageInfo(sourcePackageName, targetPackageName);
+ fail(sourcePackageName + " should not be able to see " + targetPackageName);
+ } catch (PackageManager.NameNotFoundException ignored) {
+ }
+ }
+
+ private PackageInfo getPackageInfo(String sourcePackageName, String targetPackageName)
+ throws Exception {
+ Bundle response = sendCommand(sourcePackageName, targetPackageName,
+ PKG_BASE + "cts.action.GET_PACKAGE_INFO");
+ return response.getParcelable(Intent.EXTRA_RETURN_RESULT);
+ }
+
+ private PackageInfo startForResult(String sourcePackageName, String targetPackageName)
+ throws Exception {
+ Bundle response = sendCommand(sourcePackageName, targetPackageName,
+ PKG_BASE + "cts.action.START_FOR_RESULT");
+ return response.getParcelable(Intent.EXTRA_RETURN_RESULT);
+ }
+
+ private Bundle sendCommand(String sourcePackageName, String targetPackageName, String action)
+ throws Exception {
+ Intent intent = new Intent(action)
+ .setComponent(new ComponentName(
+ sourcePackageName, PKG_BASE + "cts.query.TestActivity"))
+ // data uri unique to each activity start to ensure actual launch and not just
+ // redisplay
+ .setData(Uri.parse("test://" + name.getMethodName() + targetPackageName))
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NEW_DOCUMENT)
+ .putExtra(Intent.EXTRA_PACKAGE_NAME, targetPackageName);
+ final ConditionVariable latch = new ConditionVariable();
+ final AtomicReference<Bundle> resultReference = new AtomicReference<>();
+ final RemoteCallback callback = new RemoteCallback(
+ bundle -> {
+ resultReference.set(bundle);
+ latch.open();
+ },
+ sResponseHandler);
+ intent.putExtra("remoteCallback", callback);
+ sContext.startActivity(intent);
+ if (!latch.block(TimeUnit.SECONDS.toMillis(10))) {
+ throw new TimeoutException(
+ "Latch timed out while awiating a response from " + targetPackageName);
+ }
+ final Bundle bundle = resultReference.get();
+ if (bundle != null && bundle.containsKey("error")) {
+ throw (Exception) Objects.requireNonNull(bundle.getSerializable("error"));
+ }
+ return bundle;
+ }
+
+}
diff --git a/tests/tests/appop/Android.bp b/tests/tests/appop/Android.bp
index e4ca806..9463744 100644
--- a/tests/tests/appop/Android.bp
+++ b/tests/tests/appop/Android.bp
@@ -12,13 +12,36 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+cc_test_library {
+ name: "libCtsAppOpsTestCases_jni",
+
+ stl: "libc++_static",
+ gtest: false,
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-unused-parameter",
+ ],
+
+ srcs: ["jni/**/*.cpp"],
+
+ shared_libs: [
+ "libbinder",
+ "libutils",
+ "liblog",
+ ],
+}
+
android_test {
name: "CtsAppOpsTestCases",
- sdk_version: "test_current",
+
+ compile_multilib: "both",
srcs: ["src/**/*.kt"],
static_libs: [
+ "appops-test-util-lib",
+ "AppOpsUserServiceAidl",
"androidx.test.rules",
"compatibility-device-util-axt",
"androidx.legacy_legacy-support-v4",
@@ -27,9 +50,48 @@
"androidx.test.uiautomator_uiautomator"
],
+ jni_libs: [
+ "ld-android",
+ "libartbase",
+ "libbacktrace",
+ "libbase",
+ "libbinder",
+ "libbinderthreadstate",
+ "libbpf",
+ "libbpf_android",
+ "libc++",
+ "libcgrouprc",
+ "libcrypto",
+ "libcutils",
+ "libdexfile",
+ "libdl_android",
+ "libhidl-gen-utils",
+ "libhidlbase",
+ "libjsoncpp",
+ "liblog",
+ "liblzma",
+ "libnativehelper",
+ "libnetdbpf",
+ "libnetdutils",
+ "libnetworkstatsfactorytestjni",
+ "libpackagelistparser",
+ "libpcre2",
+ "libprocessgroup",
+ "libselinux",
+ "libtinyxml2",
+ "libui",
+ "libunwindstack",
+ "libutils",
+ "libutilscallstack",
+ "libvndksupport",
+ "libziparchive",
+ "libz",
+ "libCtsAppOpsTestCases_jni",
+ ],
+
test_suites: [
"cts",
"vts",
"general-tests",
],
-}
\ No newline at end of file
+}
diff --git a/tests/tests/appop/AndroidTest.xml b/tests/tests/appop/AndroidTest.xml
index 9f489a9..18e4538 100644
--- a/tests/tests/appop/AndroidTest.xml
+++ b/tests/tests/appop/AndroidTest.xml
@@ -16,10 +16,11 @@
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="framework" />
<option name="config-descriptor:metadata" key="parameter" value="instant_app" />
- <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsAppOpsTestCases.apk" />
+ <option name="test-file-name" value="CtsAppThatUsesAppOps.apk" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="hidden-api-checks" value="true" />
diff --git a/tests/tests/appop/AppThatUsesAppOps/Android.bp b/tests/tests/appop/AppThatUsesAppOps/Android.bp
new file mode 100644
index 0000000..31f4a95
--- /dev/null
+++ b/tests/tests/appop/AppThatUsesAppOps/Android.bp
@@ -0,0 +1,63 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_test_library {
+ name: "libAppThatUsesAppOps_jni",
+ sdk_version: "current",
+ stl: "c++_static",
+
+ srcs: [
+ "jni/**/*.cpp"
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-unused-parameter"
+ ],
+
+ shared_libs: [
+ "liblog",
+ "libbinder_ndk"
+ ],
+
+ static_libs: [
+ "AppOpsUserServiceAidlNative-ndk"
+ ]
+}
+
+android_test_helper_app {
+ name: "CtsAppThatUsesAppOps",
+
+ compile_multilib: "both",
+
+ srcs: ["src/**/*.kt"],
+
+ static_libs: [
+ "appops-test-util-lib",
+ "AppOpsUserServiceAidl",
+ "truth-prebuilt",
+ "junit",
+ ],
+
+ jni_libs: [
+ "libAppThatUsesAppOps_jni"
+ ],
+
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ]
+}
\ No newline at end of file
diff --git a/tests/tests/appop/AppThatUsesAppOps/AndroidManifest.xml b/tests/tests/appop/AppThatUsesAppOps/AndroidManifest.xml
new file mode 100644
index 0000000..0d524a9
--- /dev/null
+++ b/tests/tests/appop/AppThatUsesAppOps/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.app.appops.cts.appthatusesappops">
+
+ <application>
+ <service android:name=".AppOpsUserService" android:exported="true" />
+ </application>
+
+</manifest>
diff --git a/tests/tests/appop/AppThatUsesAppOps/jni/android/app/appops/cts/appthatusesappops/AppOpsUserService.cpp b/tests/tests/appop/AppThatUsesAppOps/jni/android/app/appops/cts/appthatusesappops/AppOpsUserService.cpp
new file mode 100644
index 0000000..8c97f65
--- /dev/null
+++ b/tests/tests/appop/AppThatUsesAppOps/jni/android/app/appops/cts/appthatusesappops/AppOpsUserService.cpp
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <jni.h>
+#include <android/binder_ibinder_jni.h>
+
+#include "aidl/android/app/appops/cts/IAppOpsUserClient.h"
+
+using ::ndk::SpAIBinder;
+using ::aidl::android::app::appops::cts::IAppOpsUserClient;
+
+// Call binder method IAppOpsUserClient#noteSyncOp from native code
+extern "C" JNIEXPORT void JNICALL
+Java_android_app_appops_cts_appthatusesappops_AppOpsUserServiceKt_noteSyncOpFromNativeCode(
+ JNIEnv* env, jclass clazz, jobject binder) {
+ SpAIBinder native_binder = SpAIBinder(AIBinder_fromJavaBinder(env, binder));
+ std::shared_ptr<IAppOpsUserClient> service = IAppOpsUserClient::fromBinder(native_binder);
+
+ service->noteSyncOp();
+}
diff --git a/tests/tests/appop/AppThatUsesAppOps/src/android/app/appops/cts/appthatusesappops/AppOpsUserService.kt b/tests/tests/appop/AppThatUsesAppOps/src/android/app/appops/cts/appthatusesappops/AppOpsUserService.kt
new file mode 100644
index 0000000..de2729c
--- /dev/null
+++ b/tests/tests/appop/AppThatUsesAppOps/src/android/app/appops/cts/appthatusesappops/AppOpsUserService.kt
@@ -0,0 +1,390 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.appops.cts.appthatusesappops
+
+import android.app.AppOpsManager
+import android.app.AppOpsManager.OPSTR_COARSE_LOCATION
+import android.app.AppOpsManager.OPSTR_FINE_LOCATION
+import android.app.AppOpsManager.OPSTR_GET_ACCOUNTS
+import android.app.AsyncNotedAppOp
+import android.app.Service
+import android.app.SyncNotedAppOp
+import android.app.appops.cts.IAppOpsUserClient
+import android.app.appops.cts.IAppOpsUserService
+import android.app.appops.cts.eventually
+import android.content.Intent
+import android.os.IBinder
+import android.os.Process.FIRST_APPLICATION_UID
+import com.google.common.truth.Truth.assertThat
+import java.io.PrintWriter
+import java.io.StringWriter
+import java.lang.IllegalArgumentException
+
+private external fun noteSyncOpFromNativeCode(binder: IBinder)
+
+class AppOpsUserService : Service() {
+ override fun onCreate() {
+ super.onCreate()
+
+ System.loadLibrary("AppThatUsesAppOps_jni")
+ }
+
+ override fun onBind(intent: Intent?): IBinder? {
+ return object : IAppOpsUserService.Stub() {
+ private val appOpsManager = getSystemService(AppOpsManager::class.java)
+
+ // Collected note-op calls inside of this process
+ private val noted = mutableListOf<Pair<SyncNotedAppOp, Array<StackTraceElement>>>()
+ private val selfNoted = mutableListOf<Pair<SyncNotedAppOp, Array<StackTraceElement>>>()
+ private val asyncNoted = mutableListOf<AsyncNotedAppOp>()
+
+ private fun setNotedAppOpsCollector() {
+ appOpsManager.setNotedAppOpsCollector(
+ object : AppOpsManager.AppOpsCollector() {
+ override fun onNoted(op: SyncNotedAppOp) {
+ noted.add(op to Throwable().stackTrace)
+ }
+
+ override fun onSelfNoted(op: SyncNotedAppOp) {
+ selfNoted.add(op to Throwable().stackTrace)
+ }
+
+ override fun onAsyncNoted(asyncOp: AsyncNotedAppOp) {
+ asyncNoted.add(asyncOp)
+ }
+ })
+ }
+
+ init {
+ setNotedAppOpsCollector()
+ }
+
+ /**
+ * Cheapo variant of {@link ParcelableException}
+ */
+ inline fun forwardThrowableFrom(r: () -> Unit) {
+ try {
+ r()
+ } catch (t: Throwable) {
+ val sw = StringWriter()
+ t.printStackTrace(PrintWriter(sw))
+
+ throw IllegalArgumentException("\n" + sw.toString() + "called by")
+ }
+ }
+
+ override fun disableCollectorAndCallSyncOpsWhichWillNotBeCollected(
+ client: IAppOpsUserClient
+ ) {
+ forwardThrowableFrom {
+ appOpsManager.setNotedAppOpsCollector(null)
+
+ client.noteSyncOp()
+
+ assertThat(asyncNoted).isEmpty()
+
+ setNotedAppOpsCollector()
+
+ assertThat(noted).isEmpty()
+ assertThat(selfNoted).isEmpty()
+ eventually {
+ assertThat(asyncNoted.map { it.op }).containsExactly(OPSTR_COARSE_LOCATION)
+ }
+ }
+ }
+
+ override fun disableCollectorAndCallASyncOpsWhichWillBeCollected(
+ client: IAppOpsUserClient
+ ) {
+ forwardThrowableFrom {
+ appOpsManager.setNotedAppOpsCollector(null)
+
+ client.noteAsyncOp()
+
+ setNotedAppOpsCollector()
+
+ eventually {
+ assertThat(asyncNoted.map { it.op }).containsExactly(OPSTR_COARSE_LOCATION)
+ }
+ assertThat(noted).isEmpty()
+ assertThat(selfNoted).isEmpty()
+ }
+ }
+
+ override fun callApiThatNotesSyncOpAndCheckLog(client: IAppOpsUserClient) {
+ forwardThrowableFrom {
+ client.noteSyncOp()
+
+ assertThat(noted.map { it.first.op }).containsExactly(OPSTR_COARSE_LOCATION)
+ assertThat(noted[0].second.map { it.methodName })
+ .contains("callApiThatNotesSyncOpAndCheckLog")
+ assertThat(selfNoted).isEmpty()
+ assertThat(asyncNoted).isEmpty()
+ }
+ }
+
+ override fun callApiThatNotesSyncOpAndClearLog(client: IAppOpsUserClient) {
+ forwardThrowableFrom {
+ client.noteSyncOp()
+
+ assertThat(noted.map { it.first.op }).containsExactly(OPSTR_COARSE_LOCATION)
+ assertThat(noted[0].second.map { it.methodName })
+ .contains("callApiThatNotesSyncOpAndClearLog")
+ assertThat(selfNoted).isEmpty()
+ assertThat(asyncNoted).isEmpty()
+
+ noted.clear()
+ }
+ }
+
+ override fun callApiThatCallsBackIntoServiceAndCheckLog(client: IAppOpsUserClient) {
+ forwardThrowableFrom {
+ // This calls back into the service via callApiThatNotesSyncOpAndClearLog
+ client.callBackIntoService()
+
+ // The noteSyncOp called in callApiThatNotesSyncOpAndClearLog should not have
+ // affected the callBackIntoService call
+ assertThat(noted.map { it.first.op }).containsExactly(OPSTR_FINE_LOCATION)
+ assertThat(noted[0].second.map { it.methodName })
+ .contains("callApiThatCallsBackIntoServiceAndCheckLog")
+ assertThat(selfNoted).isEmpty()
+ assertThat(asyncNoted).isEmpty()
+ }
+ }
+
+ override fun callApiThatNotesSyncOpFromNativeCodeAndCheckLog(
+ client: IAppOpsUserClient
+ ) {
+ forwardThrowableFrom {
+ noteSyncOpFromNativeCode(client.asBinder())
+
+ eventually {
+ assertThat(asyncNoted.map { it.op }).containsExactly(OPSTR_COARSE_LOCATION)
+ }
+ assertThat(noted).isEmpty()
+ assertThat(selfNoted).isEmpty()
+ }
+ }
+
+ override fun callApiThatNotesSyncOpFromNativeCodeAndCheckMessage(
+ client: IAppOpsUserClient
+ ) {
+ forwardThrowableFrom {
+ noteSyncOpFromNativeCode(client.asBinder())
+
+ eventually {
+ assertThat(asyncNoted[0].notingPackageName).isEqualTo(
+ "android.app.appops.cts")
+ assertThat(asyncNoted[0].notingUid).isAtLeast(FIRST_APPLICATION_UID)
+ assertThat(asyncNoted[0].message).isNotEmpty()
+ }
+ }
+ }
+
+ override fun callApiThatNotesSyncOpAndCheckStackTrace(client: IAppOpsUserClient) {
+ forwardThrowableFrom {
+ client.noteSyncOp()
+ assertThat(noted[0].second.map { it.methodName }).contains(
+ "callApiThatNotesSyncOpAndCheckStackTrace")
+ }
+ }
+
+ override fun callApiThatNotesNonPermissionSyncOpAndCheckLog(
+ client: IAppOpsUserClient
+ ) {
+ forwardThrowableFrom {
+ client.noteNonPermissionSyncOp()
+
+ assertThat(selfNoted).isEmpty()
+ assertThat(asyncNoted).isEmpty()
+ assertThat(noted).isEmpty()
+ }
+ }
+
+ override fun callApiThatNotesTwiceSyncOpAndCheckLog(client: IAppOpsUserClient) {
+ forwardThrowableFrom {
+ client.noteSyncOpTwice()
+
+ // Ops noted twice are only reported once
+ assertThat(noted.map { it.first.op }).containsExactly(OPSTR_COARSE_LOCATION)
+ assertThat(noted[0].second.map { it.methodName })
+ .contains("callApiThatNotesTwiceSyncOpAndCheckLog")
+ assertThat(selfNoted).isEmpty()
+ assertThat(asyncNoted).isEmpty()
+ }
+ }
+
+ override fun callApiThatNotesTwoSyncOpAndCheckLog(client: IAppOpsUserClient) {
+ forwardThrowableFrom {
+ client.noteTwoSyncOp()
+
+ assertThat(noted.map { it.first.op }).containsExactly(
+ OPSTR_COARSE_LOCATION, OPSTR_GET_ACCOUNTS)
+ assertThat(noted[0].second.map { it.methodName })
+ .contains("callApiThatNotesTwoSyncOpAndCheckLog")
+ assertThat(noted[1].second.map { it.methodName })
+ .contains("callApiThatNotesTwoSyncOpAndCheckLog")
+ assertThat(selfNoted).isEmpty()
+ assertThat(asyncNoted).isEmpty()
+ }
+ }
+
+ override fun callApiThatNotesSyncOpNativelyAndCheckLog(client: IAppOpsUserClient) {
+ forwardThrowableFrom {
+ client.noteSyncOpNative()
+
+ // All native notes will be reported as async notes
+ eventually {
+ assertThat(asyncNoted.map { it.op }).containsExactly(OPSTR_COARSE_LOCATION)
+ }
+ assertThat(noted).isEmpty()
+ assertThat(selfNoted).isEmpty()
+ }
+ }
+
+ override fun callApiThatNotesNonPermissionSyncOpNativelyAndCheckLog(
+ client: IAppOpsUserClient
+ ) {
+ forwardThrowableFrom {
+ client.noteNonPermissionSyncOpNative()
+
+ // All native notes will be reported as async notes
+ assertThat(noted).isEmpty()
+ assertThat(selfNoted).isEmpty()
+ assertThat(asyncNoted).isEmpty()
+ }
+ }
+
+ override fun callOnewayApiThatNotesSyncOpAndCheckLog(client: IAppOpsUserClient) {
+ forwardThrowableFrom {
+ client.noteSyncOpOneway()
+
+ // There is not return value from a one-way call, hence async note is the only
+ // option
+ eventually {
+ assertThat(asyncNoted.map { it.op }).containsExactly(OPSTR_COARSE_LOCATION)
+ }
+ assertThat(noted).isEmpty()
+ assertThat(selfNoted).isEmpty()
+ }
+ }
+
+ override fun callOnewayApiThatNotesSyncOpNativelyAndCheckLog(
+ client: IAppOpsUserClient
+ ) {
+ forwardThrowableFrom {
+ client.noteSyncOpOnewayNative()
+
+ // There is not return value from a one-way call, hence async note is the only
+ // option
+ eventually {
+ assertThat(asyncNoted.map { it.op }).containsExactly(OPSTR_COARSE_LOCATION)
+ }
+ assertThat(noted).isEmpty()
+ assertThat(selfNoted).isEmpty()
+ }
+ }
+
+ override fun callApiThatNotesSyncOpOtherUidAndCheckLog(client: IAppOpsUserClient) {
+ forwardThrowableFrom {
+ client.noteSyncOpOtherUid()
+
+ assertThat(noted).isEmpty()
+ assertThat(selfNoted).isEmpty()
+ assertThat(asyncNoted).isEmpty()
+ }
+ }
+
+ override fun callApiThatNotesSyncOpOtherUidNativelyAndCheckLog(
+ client: IAppOpsUserClient
+ ) {
+ forwardThrowableFrom {
+ client.noteSyncOpOtherUidNative()
+
+ assertThat(noted).isEmpty()
+ assertThat(selfNoted).isEmpty()
+ assertThat(asyncNoted).isEmpty()
+ }
+ }
+
+ override fun callApiThatNotesAsyncOpAndCheckLog(client: IAppOpsUserClient) {
+ forwardThrowableFrom {
+ client.noteAsyncOp()
+
+ eventually {
+ assertThat(asyncNoted.map { it.op }).containsExactly(OPSTR_COARSE_LOCATION)
+ }
+ assertThat(noted).isEmpty()
+ assertThat(selfNoted).isEmpty()
+ }
+ }
+
+ override fun callApiThatNotesAsyncOpAndCheckDefaultMessage(client: IAppOpsUserClient) {
+ forwardThrowableFrom {
+ client.noteAsyncOp()
+
+ eventually {
+ assertThat(asyncNoted[0].notingPackageName).isEqualTo(
+ "android.app.appops.cts")
+ assertThat(asyncNoted[0].notingUid).isAtLeast(FIRST_APPLICATION_UID)
+ assertThat(asyncNoted[0].message).contains(
+ "AppOpsLoggingTest\$AppOpsUserClient\$noteAsyncOp")
+ }
+ }
+ }
+
+ override fun callApiThatNotesAsyncOpAndCheckCustomMessage(client: IAppOpsUserClient) {
+ forwardThrowableFrom {
+ client.noteAsyncOpWithCustomMessage()
+
+ eventually {
+ assertThat(asyncNoted[0].notingPackageName).isEqualTo(
+ "android.app.appops.cts")
+ assertThat(asyncNoted[0].notingUid).isAtLeast(FIRST_APPLICATION_UID)
+ assertThat(asyncNoted[0].message).isEqualTo("custom msg")
+ }
+ }
+ }
+
+ override fun callApiThatNotesAsyncOpNativelyAndCheckCustomMessage(
+ client: IAppOpsUserClient
+ ) {
+ forwardThrowableFrom {
+ client.noteAsyncOpNativeWithCustomMessage()
+
+ eventually {
+ assertThat(asyncNoted[0].notingPackageName).isNull()
+ assertThat(asyncNoted[0].notingUid).isAtLeast(FIRST_APPLICATION_UID)
+ assertThat(asyncNoted[0].message).isEqualTo("native custom msg")
+ }
+ }
+ }
+
+ override fun callApiThatNotesAsyncOpNativelyAndCheckLog(client: IAppOpsUserClient) {
+ forwardThrowableFrom {
+ client.noteAsyncOpNative()
+
+ eventually {
+ assertThat(asyncNoted.map { it.op }).containsExactly(OPSTR_COARSE_LOCATION)
+ }
+ assertThat(noted).isEmpty()
+ assertThat(selfNoted).isEmpty()
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/tests/appop/OWNERS b/tests/tests/appop/OWNERS
new file mode 100644
index 0000000..d21846d
--- /dev/null
+++ b/tests/tests/appop/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 137825
+include ../toast/OWNERS
diff --git a/common/device-side/util/tests/Android.bp b/tests/tests/appop/aidl/Android.bp
similarity index 60%
copy from common/device-side/util/tests/Android.bp
copy to tests/tests/appop/aidl/Android.bp
index 2abba37..ed5b3ff 100644
--- a/common/device-side/util/tests/Android.bp
+++ b/tests/tests/appop/aidl/Android.bp
@@ -1,4 +1,4 @@
-// Copyright (C) 2015 The Android Open Source Project
+// Copyright (C) 2019 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,17 +12,26 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-java_test {
- name: "compatibility-device-util-tests",
+aidl_interface {
+ name: "AppOpsUserServiceAidlNative",
- srcs: ["src/**/*.java"],
+ local_include_dir: "src",
- static_libs: [
- "compatibility-device-util",
- "junit",
- "testng", // TODO: remove once Android migrates to JUnit 4.12, which provide assertThrows
- "truth-prebuilt"
+ srcs: [
+ "src/**/*.aidl"
],
- sdk_version: "test_current",
+ backend: {
+ java: {
+ sdk_version: "current",
+ }
+ }
}
+
+java_library {
+ name: "AppOpsUserServiceAidl",
+
+ srcs: [
+ "src/**/*.aidl"
+ ],
+}
\ No newline at end of file
diff --git a/tests/tests/appop/aidl/src/android/app/appops/cts/IAppOpsUserClient.aidl b/tests/tests/appop/aidl/src/android/app/appops/cts/IAppOpsUserClient.aidl
new file mode 100644
index 0000000..76641ea
--- /dev/null
+++ b/tests/tests/appop/aidl/src/android/app/appops/cts/IAppOpsUserClient.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.appops.cts;
+
+interface IAppOpsUserClient {
+ void noteSyncOp();
+ void callBackIntoService();
+ void noteNonPermissionSyncOp();
+ void noteSyncOpTwice();
+ void noteTwoSyncOp();
+ void noteSyncOpNative();
+ void noteNonPermissionSyncOpNative();
+ oneway void noteSyncOpOneway();
+ oneway void noteSyncOpOnewayNative();
+ void noteSyncOpOtherUid();
+ void noteSyncOpOtherUidNative();
+ void noteAsyncOp();
+ void noteAsyncOpWithCustomMessage();
+ void noteAsyncOpNative();
+ void noteAsyncOpNativeWithCustomMessage();
+}
diff --git a/tests/tests/appop/aidl/src/android/app/appops/cts/IAppOpsUserService.aidl b/tests/tests/appop/aidl/src/android/app/appops/cts/IAppOpsUserService.aidl
new file mode 100644
index 0000000..cddc91e
--- /dev/null
+++ b/tests/tests/appop/aidl/src/android/app/appops/cts/IAppOpsUserService.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.appops.cts;
+
+import android.app.appops.cts.IAppOpsUserClient;
+
+interface IAppOpsUserService {
+ void disableCollectorAndCallSyncOpsWhichWillNotBeCollected(in IAppOpsUserClient client);
+ void disableCollectorAndCallASyncOpsWhichWillBeCollected(in IAppOpsUserClient client);
+ void callApiThatNotesSyncOpAndCheckLog(in IAppOpsUserClient client);
+ void callApiThatNotesSyncOpAndClearLog(in IAppOpsUserClient client);
+ void callApiThatCallsBackIntoServiceAndCheckLog(in IAppOpsUserClient client);
+ void callApiThatNotesSyncOpFromNativeCodeAndCheckLog(in IAppOpsUserClient client);
+ void callApiThatNotesSyncOpFromNativeCodeAndCheckMessage(in IAppOpsUserClient client);
+ void callApiThatNotesSyncOpAndCheckStackTrace(in IAppOpsUserClient client);
+ void callApiThatNotesNonPermissionSyncOpAndCheckLog(in IAppOpsUserClient client);
+ void callApiThatNotesTwiceSyncOpAndCheckLog(in IAppOpsUserClient client);
+ void callApiThatNotesTwoSyncOpAndCheckLog(in IAppOpsUserClient client);
+ void callApiThatNotesSyncOpNativelyAndCheckLog(in IAppOpsUserClient client);
+ void callApiThatNotesNonPermissionSyncOpNativelyAndCheckLog(in IAppOpsUserClient client);
+ void callOnewayApiThatNotesSyncOpAndCheckLog(in IAppOpsUserClient client);
+ void callOnewayApiThatNotesSyncOpNativelyAndCheckLog(in IAppOpsUserClient client);
+ void callApiThatNotesSyncOpOtherUidAndCheckLog(in IAppOpsUserClient client);
+ void callApiThatNotesSyncOpOtherUidNativelyAndCheckLog(in IAppOpsUserClient client);
+ void callApiThatNotesAsyncOpAndCheckLog(in IAppOpsUserClient client);
+ void callApiThatNotesAsyncOpAndCheckDefaultMessage(in IAppOpsUserClient client);
+ void callApiThatNotesAsyncOpAndCheckCustomMessage(in IAppOpsUserClient client);
+ void callApiThatNotesAsyncOpNativelyAndCheckCustomMessage(in IAppOpsUserClient client);
+ void callApiThatNotesAsyncOpNativelyAndCheckLog(in IAppOpsUserClient client);
+}
diff --git a/common/device-side/util/tests/Android.bp b/tests/tests/appop/appopsTestUtilLib/Android.bp
similarity index 61%
copy from common/device-side/util/tests/Android.bp
copy to tests/tests/appop/appopsTestUtilLib/Android.bp
index 2abba37..935909c 100644
--- a/common/device-side/util/tests/Android.bp
+++ b/tests/tests/appop/appopsTestUtilLib/Android.bp
@@ -1,10 +1,10 @@
-// Copyright (C) 2015 The Android Open Source Project
+// Copyright (C) 2019 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -12,16 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-java_test {
- name: "compatibility-device-util-tests",
+java_library {
+ name: "appops-test-util-lib",
- srcs: ["src/**/*.java"],
+ srcs: ["src/**/*.kt"],
static_libs: [
- "compatibility-device-util",
- "junit",
- "testng", // TODO: remove once Android migrates to JUnit 4.12, which provide assertThrows
- "truth-prebuilt"
+ "androidx.test.rules",
+ "compatibility-device-util-axt",
+ "androidx.legacy_legacy-support-v4",
+ "platform-test-annotations",
],
sdk_version: "test_current",
diff --git a/tests/tests/appop/appopsTestUtilLib/src/android/app/appops/cts/AppOpsUtils.kt b/tests/tests/appop/appopsTestUtilLib/src/android/app/appops/cts/AppOpsUtils.kt
new file mode 100644
index 0000000..a62cc00
--- /dev/null
+++ b/tests/tests/appop/appopsTestUtilLib/src/android/app/appops/cts/AppOpsUtils.kt
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2018 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.appops.cts
+
+import android.app.AppOpsManager.MODE_ALLOWED
+import android.app.AppOpsManager.MODE_DEFAULT
+import android.app.AppOpsManager.MODE_ERRORED
+import android.app.AppOpsManager.MODE_IGNORED
+import android.util.Log
+import androidx.test.InstrumentationRegistry
+import com.android.compatibility.common.util.SystemUtil
+
+private const val LOG_TAG = "AppOpsUtils"
+private const val TIMEOUT_MILLIS = 10000L
+
+/**
+ * Resets a package's app ops configuration to the device default. See AppOpsManager for the
+ * default op settings.
+ *
+ * <p>
+ * It's recommended to call this in setUp() and tearDown() of your test so the test starts and
+ * ends with a reproducible default state, and so doesn't affect other tests.
+ *
+ * <p>
+ * Some app ops are configured to be non-resettable, which means that the state of these will
+ * not be reset even when calling this method.
+ */
+fun reset(packageName: String): String {
+ return runCommand("appops reset $packageName")
+}
+
+/**
+ * Sets the app op mode (e.g. allowed, denied) for a single package and operation.
+ */
+fun setOpMode(packageName: String, opStr: String, mode: Int): String {
+ val modeStr = when (mode) {
+ MODE_ALLOWED -> "allow"
+ MODE_ERRORED -> "deny"
+ MODE_IGNORED -> "ignore"
+ MODE_DEFAULT -> "default"
+ else -> throw IllegalArgumentException("Unexpected app op type")
+ }
+ val command = "appops set $packageName $opStr $modeStr"
+ return runCommand(command)
+}
+
+/**
+ * Get the app op mode (e.g. MODE_ALLOWED, MODE_DEFAULT) for a single package and operation.
+ */
+fun getOpMode(packageName: String, opStr: String): Int {
+ val opState = getOpState(packageName, opStr)
+ return when {
+ opState.contains(" allow") -> MODE_ALLOWED
+ opState.contains(" deny") -> MODE_ERRORED
+ opState.contains(" ignore") -> MODE_IGNORED
+ opState.contains(" default") -> MODE_DEFAULT
+ else -> throw IllegalStateException("Unexpected app op mode returned $opState")
+ }
+}
+
+/**
+ * Returns whether an allowed operation has been logged by the AppOpsManager for a
+ * package. Operations are noted when the app attempts to perform them and calls e.g.
+ * {@link AppOpsManager#noteOperation}.
+ *
+ * @param opStr The public string constant of the operation (e.g. OPSTR_READ_SMS).
+ */
+fun allowedOperationLogged(packageName: String, opStr: String): Boolean {
+ return getOpState(packageName, opStr).contains(" time=")
+}
+
+/**
+ * Returns whether a rejected operation has been logged by the AppOpsManager for a
+ * package. Operations are noted when the app attempts to perform them and calls e.g.
+ * {@link AppOpsManager#noteOperation}.
+ *
+ * @param opStr The public string constant of the operation (e.g. OPSTR_READ_SMS).
+ */
+fun rejectedOperationLogged(packageName: String, opStr: String): Boolean {
+ return getOpState(packageName, opStr).contains(" rejectTime=")
+}
+
+/**
+ * Runs a lambda adopting Shell's permissions.
+ */
+inline fun <T> runWithShellPermissionIdentity(runnable: () -> T): T {
+ val uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ uiAutomation.adoptShellPermissionIdentity()
+ try {
+ return runnable.invoke()
+ } finally {
+ uiAutomation.dropShellPermissionIdentity()
+ }
+}
+
+/**
+ * Returns the app op state for a package. Includes information on when the operation
+ * was last attempted to be performed by the package.
+ *
+ * Format: "SEND_SMS: allow; time=+23h12m54s980ms ago; rejectTime=+1h10m23s180ms"
+ */
+private fun getOpState(packageName: String, opStr: String): String {
+ return runCommand("appops get $packageName $opStr")
+}
+
+private fun runCommand(command: String): String {
+ return SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(), command)
+}
+/**
+ * Make sure that a lambda eventually finishes without throwing an exception.
+ *
+ * @param r The lambda to run.
+ * @param timeout the maximum time to wait
+ *
+ * @return the return value from the lambda
+ *
+ * @throws NullPointerException If the return value never becomes non-null
+ */
+fun <T> eventually(timeout: Long = TIMEOUT_MILLIS, r: () -> T): T {
+ val start = System.currentTimeMillis()
+
+ while (true) {
+ try {
+ return r()
+ } catch (e: Throwable) {
+ val elapsed = System.currentTimeMillis() - start
+
+ if (elapsed < timeout) {
+ Log.d(LOG_TAG, "Ignoring exception", e)
+
+ Thread.sleep(minOf(100, timeout - elapsed))
+ } else {
+ throw e
+ }
+ }
+ }
+}
diff --git a/tests/tests/appop/jni/android/app/appops/cts/AppOpsLoggingTest.cpp b/tests/tests/appop/jni/android/app/appops/cts/AppOpsLoggingTest.cpp
new file mode 100644
index 0000000..83c2fd5
--- /dev/null
+++ b/tests/tests/appop/jni/android/app/appops/cts/AppOpsLoggingTest.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <jni.h>
+#include <binder/AppOpsManager.h>
+#include <utils/String16.h>
+
+using namespace android;
+
+#include "android/log.h"
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+#define LOG_TAG "AppOpsLoggingTest"
+
+// Note op from native code without supplying a message
+extern "C" JNIEXPORT void JNICALL
+Java_android_app_appops_cts_AppOpsLoggingTestKt_nativeNoteOp(JNIEnv* env, jobject obj,
+ jint op, jint uid, jstring callingPackageName) {
+ AppOpsManager appOpsManager;
+
+ const char *nativeCallingPackageName = env->GetStringUTFChars(callingPackageName, 0);
+
+ appOpsManager.noteOp(op, uid, String16(nativeCallingPackageName));
+
+ env->ReleaseStringUTFChars(callingPackageName, nativeCallingPackageName);
+}
+
+// Note op from native code
+extern "C" JNIEXPORT void JNICALL
+Java_android_app_appops_cts_AppOpsLoggingTestKt_nativeNoteOpWithMessage(JNIEnv* env, jobject obj,
+ jint op, jint uid, jstring callingPackageName, jstring message) {
+ AppOpsManager appOpsManager;
+
+ const char *nativeCallingPackageName = env->GetStringUTFChars(callingPackageName, 0);
+ const char *nativeMessage = env->GetStringUTFChars(message, 0);
+
+ appOpsManager.noteOp(op, uid, String16(nativeCallingPackageName), String16(nativeMessage));
+
+ env->ReleaseStringUTFChars(callingPackageName, nativeCallingPackageName);
+ env->ReleaseStringUTFChars(message, nativeMessage);
+}
diff --git a/tests/tests/appop/src/android/app/appops/cts/AppOpsLoggingTest.kt b/tests/tests/appop/src/android/app/appops/cts/AppOpsLoggingTest.kt
new file mode 100644
index 0000000..eec81df
--- /dev/null
+++ b/tests/tests/appop/src/android/app/appops/cts/AppOpsLoggingTest.kt
@@ -0,0 +1,473 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.appops.cts
+
+import android.app.AppOpsManager
+import android.app.AppOpsManager.AppOpsCollector
+import android.app.AppOpsManager.OPSTR_ACCESS_ACCESSIBILITY
+import android.app.AppOpsManager.OPSTR_COARSE_LOCATION
+import android.app.AppOpsManager.OPSTR_FINE_LOCATION
+import android.app.AppOpsManager.OPSTR_GET_ACCOUNTS
+import android.app.AppOpsManager.strOpToOp
+import android.app.AsyncNotedAppOp
+import android.app.SyncNotedAppOp
+import android.content.ComponentName
+import android.content.Context
+import android.content.Context.BIND_AUTO_CREATE
+import android.content.Intent
+import android.content.ServiceConnection
+import android.os.Handler
+import android.os.IBinder
+import android.os.Looper
+import android.platform.test.annotations.AppModeFull
+import androidx.test.platform.app.InstrumentationRegistry
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Assert.fail
+import org.junit.Before
+import org.junit.Test
+import java.util.concurrent.CompletableFuture
+import java.util.concurrent.Executor
+import java.util.concurrent.TimeUnit.MILLISECONDS
+
+private const val TEST_SERVICE_PKG = "android.app.appops.cts.appthatusesappops"
+private const val TIMEOUT_MILLIS = 10000L
+
+private external fun nativeNoteOp(op: Int, uid: Int, packageName: String)
+private external fun nativeNoteOpWithMessage(
+ op: Int,
+ uid: Int,
+ packageName: String,
+ message: String
+)
+
+@AppModeFull(reason = "Test relies on other app to connect to. Instant apps can't see other apps")
+class AppOpsLoggingTest {
+ private val context = InstrumentationRegistry.getInstrumentation().targetContext
+ private val appOpsManager = context.getSystemService(AppOpsManager::class.java)
+
+ private val myUid = android.os.Process.myUid()
+ private val myPackage = context.packageName
+
+ private lateinit var testService: IAppOpsUserService
+ private lateinit var serviceConnection: ServiceConnection
+
+ // Collected note-op calls inside of this process
+ private val noted = mutableListOf<Pair<SyncNotedAppOp, Array<StackTraceElement>>>()
+ private val selfNoted = mutableListOf<Pair<SyncNotedAppOp, Array<StackTraceElement>>>()
+ private val asyncNoted = mutableListOf<AsyncNotedAppOp>()
+
+ @Before
+ fun loadNativeCode() {
+ System.loadLibrary("CtsAppOpsTestCases_jni")
+ }
+
+ @Before
+ fun setNotedAppOpsCollectorAndClearCollectedNoteOps() {
+ setNotedAppOpsCollector()
+ clearCollectedNotedOps()
+ }
+
+ @Before
+ fun connectToService() {
+ val serviceIntent = Intent()
+ serviceIntent.component = ComponentName(TEST_SERVICE_PKG,
+ TEST_SERVICE_PKG + ".AppOpsUserService")
+
+ val newService = CompletableFuture<IAppOpsUserService>()
+ serviceConnection = object : ServiceConnection {
+ override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
+ newService.complete(IAppOpsUserService.Stub.asInterface(service))
+ }
+
+ override fun onServiceDisconnected(name: ComponentName?) {
+ fail("test service disconnected")
+ }
+ }
+
+ context.bindService(serviceIntent, serviceConnection, BIND_AUTO_CREATE)
+ testService = newService.get(TIMEOUT_MILLIS, MILLISECONDS)
+ }
+
+ private fun clearCollectedNotedOps() {
+ noted.clear()
+ selfNoted.clear()
+ asyncNoted.clear()
+ }
+
+ private fun setNotedAppOpsCollector() {
+ appOpsManager.setNotedAppOpsCollector(
+ object : AppOpsCollector() {
+ override fun onNoted(op: SyncNotedAppOp) {
+ noted.add(op to Throwable().stackTrace)
+ }
+
+ override fun onSelfNoted(op: SyncNotedAppOp) {
+ selfNoted.add(op to Throwable().stackTrace)
+ }
+
+ override fun onAsyncNoted(asyncOp: AsyncNotedAppOp) {
+ asyncNoted.add(asyncOp)
+ }
+
+ override fun getAsyncNotedExecutor(): Executor {
+ // Execute callbacks immediately
+ return Executor { it.run() }
+ }
+ })
+ }
+
+ private inline fun rethrowThrowableFrom(r: () -> Unit) {
+ try {
+ r()
+ } catch (e: Throwable) {
+ throw e.cause ?: e
+ }
+ }
+
+ @Test
+ fun selfNoteAndCheckLog() {
+ appOpsManager.noteOpNoThrow(OPSTR_COARSE_LOCATION, myUid, myPackage)
+
+ assertThat(noted).isEmpty()
+ assertThat(asyncNoted).isEmpty()
+
+ assertThat(selfNoted.map { it.first.op }).containsExactly(OPSTR_COARSE_LOCATION)
+ }
+
+ @Test
+ fun nativeSelfNoteAndCheckLog() {
+ nativeNoteOp(strOpToOp(OPSTR_COARSE_LOCATION), myUid, myPackage)
+
+ assertThat(noted).isEmpty()
+ assertThat(selfNoted).isEmpty()
+
+ // All native notes will be reported as async notes
+ eventually {
+ assertThat(asyncNoted.map { it.op }).containsExactly(OPSTR_COARSE_LOCATION)
+ }
+ }
+
+ @Test
+ fun selfNotesAreDeliveredAsAsyncOpsWhenCollectorIsRegistered() {
+ appOpsManager.setNotedAppOpsCollector(null)
+
+ appOpsManager.noteOpNoThrow(OPSTR_COARSE_LOCATION, myUid, myPackage)
+
+ assertThat(noted).isEmpty()
+ assertThat(selfNoted).isEmpty()
+ assertThat(asyncNoted).isEmpty()
+
+ setNotedAppOpsCollector()
+
+ assertThat(noted).isEmpty()
+ assertThat(selfNoted).isEmpty()
+ assertThat(asyncNoted.map { it.op }).containsExactly(OPSTR_COARSE_LOCATION)
+ }
+
+ @Test
+ fun disableCollectedAndNoteSyncOpAndCheckLog() {
+ rethrowThrowableFrom {
+ testService.disableCollectorAndCallSyncOpsWhichWillNotBeCollected(
+ AppOpsUserClient(context))
+ }
+ }
+
+ @Test
+ fun disableCollectedAndNoteASyncOpAndCheckLog() {
+ rethrowThrowableFrom {
+ testService.disableCollectorAndCallASyncOpsWhichWillBeCollected(
+ AppOpsUserClient(context))
+ }
+ }
+
+ @Test
+ fun noteSyncOpAndCheckLog() {
+ rethrowThrowableFrom {
+ testService.callApiThatNotesSyncOpAndCheckLog(AppOpsUserClient(context))
+ }
+ }
+
+ @Test
+ fun callsBackIntoServiceAndCheckLog() {
+ rethrowThrowableFrom {
+ testService.callApiThatCallsBackIntoServiceAndCheckLog(
+ AppOpsUserClient(context, testService))
+ }
+ }
+
+ @Test
+ fun noteSyncOpFromNativeCodeAndCheckLog() {
+ rethrowThrowableFrom {
+ testService.callApiThatNotesSyncOpFromNativeCodeAndCheckLog(
+ AppOpsUserClient(context))
+ }
+ }
+
+ @Test
+ fun noteSyncOpFromNativeCodeAndCheckMessage() {
+ rethrowThrowableFrom {
+ testService.callApiThatNotesSyncOpFromNativeCodeAndCheckMessage(
+ AppOpsUserClient(context))
+ }
+ }
+
+ @Test
+ fun noteSyncOpAndCheckStackTrace() {
+ rethrowThrowableFrom {
+ testService.callApiThatNotesSyncOpAndCheckStackTrace(AppOpsUserClient(context))
+ }
+ }
+
+ @Test
+ fun noteNonPermissionSyncOpAndCheckLog() {
+ rethrowThrowableFrom {
+ testService.callApiThatNotesNonPermissionSyncOpAndCheckLog(AppOpsUserClient(context))
+ }
+ }
+
+ @Test
+ fun noteSyncOpTwiceAndCheckLog() {
+ rethrowThrowableFrom {
+ testService.callApiThatNotesTwiceSyncOpAndCheckLog(AppOpsUserClient(context))
+ }
+ }
+
+ @Test
+ fun noteTwoSyncOpAndCheckLog() {
+ rethrowThrowableFrom {
+ testService.callApiThatNotesTwoSyncOpAndCheckLog(AppOpsUserClient(context))
+ }
+ }
+
+ @Test
+ fun noteSyncOpNativeAndCheckLog() {
+ rethrowThrowableFrom {
+ testService.callApiThatNotesSyncOpNativelyAndCheckLog(AppOpsUserClient(context))
+ }
+ }
+
+ @Test
+ fun noteNonPermissionSyncOpNativeAndCheckLog() {
+ rethrowThrowableFrom {
+ testService.callApiThatNotesNonPermissionSyncOpNativelyAndCheckLog(
+ AppOpsUserClient(context))
+ }
+ }
+
+ @Test
+ fun noteSyncOpOneway() {
+ rethrowThrowableFrom {
+ testService.callOnewayApiThatNotesSyncOpAndCheckLog(AppOpsUserClient(context))
+ }
+ }
+
+ @Test
+ fun noteSyncOpOnewayNative() {
+ rethrowThrowableFrom {
+ testService.callOnewayApiThatNotesSyncOpNativelyAndCheckLog(AppOpsUserClient(context))
+ }
+ }
+
+ @Test
+ fun noteSyncOpOtherUidAndCheckLog() {
+ rethrowThrowableFrom {
+ testService.callApiThatNotesSyncOpOtherUidAndCheckLog(AppOpsUserClient(context))
+ }
+ }
+
+ @Test
+ fun noteSyncOpOtherUidNativeAndCheckLog() {
+ rethrowThrowableFrom {
+ testService.callApiThatNotesSyncOpOtherUidNativelyAndCheckLog(AppOpsUserClient(context))
+ }
+ }
+
+ @Test
+ fun noteAsyncOpAndCheckLog() {
+ rethrowThrowableFrom {
+ testService.callApiThatNotesAsyncOpAndCheckLog(AppOpsUserClient(context))
+ }
+ }
+
+ @Test
+ fun noteAsyncOpAndCheckDefaultMessage() {
+ rethrowThrowableFrom {
+ testService.callApiThatNotesAsyncOpAndCheckDefaultMessage(AppOpsUserClient(context))
+ }
+ }
+
+ @Test
+ fun noteAsyncOpAndCheckCustomMessage() {
+ rethrowThrowableFrom {
+ testService.callApiThatNotesAsyncOpAndCheckCustomMessage(AppOpsUserClient(context))
+ }
+ }
+
+ @Test
+ fun noteAsyncOpNativelyAndCheckCustomMessage() {
+ rethrowThrowableFrom {
+ testService.callApiThatNotesAsyncOpNativelyAndCheckCustomMessage(
+ AppOpsUserClient(context))
+ }
+ }
+
+ @Test
+ fun noteAsyncOpNativeAndCheckLog() {
+ rethrowThrowableFrom {
+ testService.callApiThatNotesAsyncOpNativelyAndCheckLog(AppOpsUserClient(context))
+ }
+ }
+
+ @After
+ fun removeNotedAppOpsCollector() {
+ appOpsManager.setNotedAppOpsCollector(null)
+ }
+
+ @After
+ fun disconnectFromService() {
+ context.unbindService(serviceConnection)
+ }
+
+ /**
+ * Calls various noteOp-like methods in binder calls called by
+ * {@link android.app.appops.cts.appthatusesappops.AppOpsUserService}
+ */
+ private class AppOpsUserClient(
+ context: Context,
+ val testService: IAppOpsUserService? = null
+ ) : IAppOpsUserClient.Stub() {
+ private val handler = Handler(Looper.getMainLooper())
+ private val appOpsManager = context.getSystemService(AppOpsManager::class.java)
+
+ private val myUid = android.os.Process.myUid()
+ private val myPackage = context.packageName
+
+ override fun noteSyncOp() {
+ runWithShellPermissionIdentity {
+ appOpsManager.noteOpNoThrow(OPSTR_COARSE_LOCATION, getCallingUid(),
+ TEST_SERVICE_PKG)
+ }
+ }
+
+ override fun callBackIntoService() {
+ runWithShellPermissionIdentity {
+ appOpsManager.noteOpNoThrow(OPSTR_FINE_LOCATION, getCallingUid(),
+ TEST_SERVICE_PKG)
+ }
+
+ testService?.callApiThatNotesSyncOpAndClearLog(this)
+ }
+
+ override fun noteNonPermissionSyncOp() {
+ runWithShellPermissionIdentity {
+ appOpsManager.noteOpNoThrow(OPSTR_ACCESS_ACCESSIBILITY, getCallingUid(),
+ TEST_SERVICE_PKG)
+ }
+ }
+
+ override fun noteSyncOpTwice() {
+ noteSyncOp()
+ noteSyncOp()
+ }
+
+ override fun noteTwoSyncOp() {
+ runWithShellPermissionIdentity {
+ appOpsManager.noteOpNoThrow(OPSTR_COARSE_LOCATION, getCallingUid(),
+ TEST_SERVICE_PKG)
+
+ appOpsManager.noteOpNoThrow(OPSTR_GET_ACCOUNTS, getCallingUid(), TEST_SERVICE_PKG)
+ }
+ }
+
+ override fun noteSyncOpNative() {
+ runWithShellPermissionIdentity {
+ nativeNoteOp(strOpToOp(OPSTR_COARSE_LOCATION), getCallingUid(), TEST_SERVICE_PKG)
+ }
+ }
+
+ override fun noteNonPermissionSyncOpNative() {
+ runWithShellPermissionIdentity {
+ nativeNoteOp(strOpToOp(OPSTR_ACCESS_ACCESSIBILITY), getCallingUid(),
+ TEST_SERVICE_PKG)
+ }
+ }
+
+ override fun noteSyncOpOneway() {
+ runWithShellPermissionIdentity {
+ appOpsManager.noteOpNoThrow(OPSTR_COARSE_LOCATION, getCallingUid(),
+ TEST_SERVICE_PKG)
+ }
+ }
+
+ override fun noteSyncOpOnewayNative() {
+ runWithShellPermissionIdentity {
+ nativeNoteOp(strOpToOp(OPSTR_COARSE_LOCATION), getCallingUid(), TEST_SERVICE_PKG)
+ }
+ }
+
+ override fun noteSyncOpOtherUid() {
+ appOpsManager.noteOpNoThrow(OPSTR_COARSE_LOCATION, myUid, myPackage)
+ }
+
+ override fun noteSyncOpOtherUidNative() {
+ nativeNoteOp(strOpToOp(OPSTR_COARSE_LOCATION), myUid, myPackage)
+ }
+
+ override fun noteAsyncOp() {
+ val callingUid = getCallingUid()
+
+ handler.post {
+ runWithShellPermissionIdentity {
+ appOpsManager.noteOpNoThrow(OPSTR_COARSE_LOCATION, callingUid, TEST_SERVICE_PKG)
+ }
+ }
+ }
+
+ override fun noteAsyncOpWithCustomMessage() {
+ val callingUid = getCallingUid()
+
+ handler.post {
+ runWithShellPermissionIdentity {
+ appOpsManager.noteOpNoThrow(OPSTR_COARSE_LOCATION, callingUid, TEST_SERVICE_PKG,
+ "custom msg")
+ }
+ }
+ }
+
+ override fun noteAsyncOpNative() {
+ val callingUid = getCallingUid()
+
+ handler.post {
+ runWithShellPermissionIdentity {
+ nativeNoteOp(strOpToOp(OPSTR_COARSE_LOCATION), callingUid, TEST_SERVICE_PKG)
+ }
+ }
+ }
+
+ override fun noteAsyncOpNativeWithCustomMessage() {
+ val callingUid = getCallingUid()
+
+ handler.post {
+ runWithShellPermissionIdentity {
+ nativeNoteOpWithMessage(strOpToOp(OPSTR_COARSE_LOCATION), callingUid,
+ TEST_SERVICE_PKG, "native custom msg")
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/tests/appop/src/android/app/appops/cts/AppOpsTest.kt b/tests/tests/appop/src/android/app/appops/cts/AppOpsTest.kt
index c358913..ea600d0 100644
--- a/tests/tests/appop/src/android/app/appops/cts/AppOpsTest.kt
+++ b/tests/tests/appop/src/android/app/appops/cts/AppOpsTest.kt
@@ -31,12 +31,7 @@
import android.app.AppOpsManager.OPSTR_RECORD_AUDIO
import android.app.AppOpsManager.OPSTR_WRITE_CALENDAR
-import android.app.appops.cts.AppOpsUtils.Companion.allowedOperationLogged
-import android.app.appops.cts.AppOpsUtils.Companion.rejectedOperationLogged
-import android.app.appops.cts.AppOpsUtils.Companion.setOpMode
-
import org.mockito.Mockito.mock
-import org.mockito.Mockito.reset
import org.mockito.Mockito.timeout
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyZeroInteractions
@@ -50,9 +45,9 @@
import androidx.test.InstrumentationRegistry
import org.junit.Before
-import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mockito
import java.util.HashMap
import java.util.HashSet
@@ -137,7 +132,7 @@
mOpPackageName = mContext.opPackageName
assertNotNull(mAppOps)
// Reset app ops state for this test package to the system default.
- AppOpsUtils.reset(mOpPackageName)
+ reset(mOpPackageName)
}
@Test
@@ -261,19 +256,19 @@
mAppOps.startWatchingMode(OPSTR_WRITE_CALENDAR, mOpPackageName, watcher)
// Make a change to the app op's mode.
- reset(watcher)
+ Mockito.reset(watcher)
setOpMode(mOpPackageName, OPSTR_WRITE_CALENDAR, MODE_ERRORED)
verify(watcher, timeout(MODE_WATCHER_TIMEOUT_MS))
.onOpChanged(OPSTR_WRITE_CALENDAR, mOpPackageName)
// Make another change to the app op's mode.
- reset(watcher)
+ Mockito.reset(watcher)
setOpMode(mOpPackageName, OPSTR_WRITE_CALENDAR, MODE_ALLOWED)
verify(watcher, timeout(MODE_WATCHER_TIMEOUT_MS))
.onOpChanged(OPSTR_WRITE_CALENDAR, mOpPackageName)
// Set mode to the same value as before - expect no call to the listener.
- reset(watcher)
+ Mockito.reset(watcher)
setOpMode(mOpPackageName, OPSTR_WRITE_CALENDAR, MODE_ALLOWED)
verifyZeroInteractions(watcher)
@@ -281,7 +276,7 @@
// Make a change to the app op's mode. Since we already stopped watching the mode, the
// listener shouldn't be called.
- reset(watcher)
+ Mockito.reset(watcher)
setOpMode(mOpPackageName, OPSTR_WRITE_CALENDAR, MODE_ERRORED)
verifyZeroInteractions(watcher)
} finally {
@@ -404,6 +399,43 @@
}
}
+ @Test
+ fun noteOpForBadUid() {
+ runWithShellPermissionIdentity {
+ val mode = mAppOps.noteOpNoThrow(OPSTR_RECORD_AUDIO, Process.myUid() + 1,
+ mOpPackageName)
+ assertEquals(mode, MODE_ERRORED)
+ }
+ }
+
+ @Test
+ fun startOpForBadUid() {
+ runWithShellPermissionIdentity {
+ val mode = mAppOps.startOpNoThrow(OPSTR_RECORD_AUDIO, Process.myUid() + 1,
+ mOpPackageName)
+ assertEquals(mode, MODE_ERRORED)
+ }
+ }
+
+ @Test
+ fun checkOpForBadUid() {
+ val defaultMode = AppOpsManager.opToDefaultMode(OPSTR_RECORD_AUDIO)
+
+ runWithShellPermissionIdentity {
+ mAppOps.setUidMode(OPSTR_RECORD_AUDIO, Process.myUid(), MODE_ERRORED)
+ try {
+ val mode = mAppOps.unsafeCheckOpNoThrow(OPSTR_RECORD_AUDIO, Process.myUid() + 1,
+ mOpPackageName)
+
+ // For invalid uids checkOp return the default mode
+ assertEquals(mode, defaultMode)
+ } finally {
+ // Clear the uid state
+ mAppOps.setUidMode(OPSTR_RECORD_AUDIO, Process.myUid(), defaultMode)
+ }
+ }
+ }
+
private fun runWithShellPermissionIdentity(command: () -> Unit) {
val uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation()
uiAutomation.adoptShellPermissionIdentity()
diff --git a/tests/tests/appop/src/android/app/appops/cts/AppOpsUtils.kt b/tests/tests/appop/src/android/app/appops/cts/AppOpsUtils.kt
deleted file mode 100644
index abdee1e..0000000
--- a/tests/tests/appop/src/android/app/appops/cts/AppOpsUtils.kt
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2018 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.appops.cts
-
-import androidx.test.InstrumentationRegistry
-import com.android.compatibility.common.util.SystemUtil
-
-import android.app.AppOpsManager.MODE_ALLOWED
-import android.app.AppOpsManager.MODE_DEFAULT
-import android.app.AppOpsManager.MODE_ERRORED
-import android.app.AppOpsManager.MODE_IGNORED
-import com.android.compatibility.common.util.ThrowingRunnable
-
-/**
- * Utilities for controlling App Ops settings, and testing whether ops are logged.
- */
-class AppOpsUtils {
- companion object {
- /**
- * Resets a package's app ops configuration to the device default. See AppOpsManager for the
- * default op settings.
- *
- * <p>
- * It's recommended to call this in setUp() and tearDown() of your test so the test starts and
- * ends with a reproducible default state, and so doesn't affect other tests.
- *
- * <p>
- * Some app ops are configured to be non-resettable, which means that the state of these will
- * not be reset even when calling this method.
- */
- fun reset(packageName: String): String {
- return runCommand("appops reset $packageName")
- }
-
- /**
- * Sets the app op mode (e.g. allowed, denied) for a single package and operation.
- */
- fun setOpMode(packageName: String, opStr: String, mode: Int) : String {
- val modeStr: String
- when (mode) {
- MODE_ALLOWED -> modeStr = "allow"
- MODE_ERRORED -> modeStr = "deny"
- MODE_IGNORED -> modeStr = "ignore"
- MODE_DEFAULT -> modeStr = "default"
- else -> throw IllegalArgumentException("Unexpected app op type")
- }
- val command = "appops set $packageName $opStr $modeStr"
- return runCommand(command)
- }
-
- /**
- * Get the app op mode (e.g. MODE_ALLOWED, MODE_DEFAULT) for a single package and operation.
- */
- fun getOpMode(packageName: String, opStr: String) : Int {
- val opState = getOpState(packageName, opStr)
- when {
- opState.contains(" allow") -> return MODE_ALLOWED
- opState.contains(" deny") -> return MODE_ERRORED
- opState.contains(" ignore") -> return MODE_IGNORED
- opState.contains(" default") -> return MODE_DEFAULT
- else -> throw IllegalStateException ("Unexpected app op mode returned $opState")
- }
- }
-
- /**
- * Returns whether an allowed operation has been logged by the AppOpsManager for a
- * package. Operations are noted when the app attempts to perform them and calls e.g.
- * {@link AppOpsManager#noteOperation}.
- *
- * @param opStr The public string constant of the operation (e.g. OPSTR_READ_SMS).
- */
- fun allowedOperationLogged(packageName: String, opStr: String): Boolean {
- return getOpState(packageName, opStr).contains(" time=")
- }
-
- /**
- * Returns whether a rejected operation has been logged by the AppOpsManager for a
- * package. Operations are noted when the app attempts to perform them and calls e.g.
- * {@link AppOpsManager#noteOperation}.
- *
- * @param opStr The public string constant of the operation (e.g. OPSTR_READ_SMS).
- */
- fun rejectedOperationLogged(packageName: String, opStr: String) : Boolean {
- return getOpState(packageName, opStr).contains(" rejectTime=")
- }
-
- /**
- * Runs a [ThrowingRunnable] adopting Shell's permissions.
- */
- fun runWithShellPermissionIdentity(runnable: ThrowingRunnable) {
- val uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation()
- uiAutomation.adoptShellPermissionIdentity()
- try {
- runnable.run()
- } catch (e: Exception) {
- throw RuntimeException("Caught exception", e)
- } finally {
- uiAutomation.dropShellPermissionIdentity()
- }
- }
-
- /**
- * Returns the app op state for a package. Includes information on when the operation
- * was last attempted to be performed by the package.
- *
- * Format: "SEND_SMS: allow; time=+23h12m54s980ms ago; rejectTime=+1h10m23s180ms"
- */
- private fun getOpState(packageName: String, opStr: String) : String {
- return runCommand("appops get $packageName $opStr")
- }
-
- private fun runCommand(command: String ) : String {
- return SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(), command)
- }
- }
-}
diff --git a/tests/tests/appop/src/android/app/appops/cts/HistoricalAppopsTest.kt b/tests/tests/appop/src/android/app/appops/cts/HistoricalAppopsTest.kt
index 73a4516..62bc995 100644
--- a/tests/tests/appop/src/android/app/appops/cts/HistoricalAppopsTest.kt
+++ b/tests/tests/appop/src/android/app/appops/cts/HistoricalAppopsTest.kt
@@ -19,31 +19,32 @@
import android.app.AppOpsManager
import android.app.AppOpsManager.HistoricalOp
import android.app.AppOpsManager.HistoricalOps
-import android.app.Instrumentation
-import android.content.Context
import android.os.Process
import android.os.SystemClock
+import android.provider.DeviceConfig
import androidx.test.InstrumentationRegistry
-import androidx.test.rule.ActivityTestRule
-import androidx.test.uiautomator.UiDevice
import androidx.test.runner.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
-import org.junit.Ignore
-import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import java.util.ArrayList
import java.util.concurrent.TimeUnit
import java.util.concurrent.locks.ReentrantLock
import java.util.function.Consumer
+import androidx.test.rule.ActivityTestRule
+import androidx.test.uiautomator.UiDevice
+import org.junit.Rule
+
+const val PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_enabled"
@RunWith(AndroidJUnit4::class)
class HistoricalAppopsTest {
private val uid = Process.myUid()
- private var appOpsManager: AppOpsManager? = null
- private var packageName: String? = null
+ private lateinit var appOpsManager: AppOpsManager
+ private lateinit var packageName: String
+
+ private var wasPermissionsHubEnabled = false
// Start an activity to make sure this app counts as being in the foreground
@Rule @JvmField
@@ -51,58 +52,57 @@
@Before
fun wakeScreenUp() {
- val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
- device.wakeUp()
- device.executeShellCommand("wm dismiss-keyguard")
+ val uiDevice = UiDevice.getInstance(instrumentation)
+ uiDevice.wakeUp()
+ uiDevice.executeShellCommand("wm dismiss-keyguard")
}
@Before
fun setUpTest() {
- appOpsManager = getContext().getSystemService(AppOpsManager::class.java)
- packageName = getContext().packageName
- val uiAutomation = getInstrumentation().getUiAutomation()
+ appOpsManager = context.getSystemService(AppOpsManager::class.java)!!
+ packageName = context.packageName!!
uiAutomation.adoptShellPermissionIdentity()
- appOpsManager!!.clearHistory()
- appOpsManager!!.resetHistoryParameters()
+ wasPermissionsHubEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+ PROPERTY_PERMISSIONS_HUB_ENABLED, false)
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_PRIVACY, PROPERTY_PERMISSIONS_HUB_ENABLED,
+ true.toString(), false)
+ appOpsManager.clearHistory()
+ appOpsManager.resetHistoryParameters()
}
@After
fun tearDownTest() {
- appOpsManager!!.clearHistory()
- appOpsManager!!.resetHistoryParameters()
- val uiAutomation = getInstrumentation().getUiAutomation()
+ appOpsManager.clearHistory()
+ appOpsManager.resetHistoryParameters()
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_PRIVACY, PROPERTY_PERMISSIONS_HUB_ENABLED,
+ wasPermissionsHubEnabled.toString(), false)
uiAutomation.dropShellPermissionIdentity()
}
- @Ignore("Feature is disabled in Android Q")
@Test
fun testGetHistoricalPackageOpsForegroundAccessInMemoryBucket() {
testGetHistoricalPackageOpsForegroundAtDepth(0)
}
- @Ignore("Feature is disabled in Android Q")
@Test
fun testGetHistoricalPackageOpsForegroundAccessFirstOnDiskBucket() {
testGetHistoricalPackageOpsForegroundAtDepth(1)
}
- @Ignore("Feature is disabled in Android Q")
@Test
fun testHistoricalAggregationOneLevelsDeep() {
testHistoricalAggregationSomeLevelsDeep(0)
}
- @Ignore("Feature is disabled in Android Q")
@Test
fun testHistoricalAggregationTwoLevelsDeep() {
testHistoricalAggregationSomeLevelsDeep(1)
}
- @Ignore("Feature is disabled in Android Q")
@Test
fun testHistoricalAggregationOverflow() {
// Configure historical registry behavior.
- appOpsManager!!.setHistoryParameters(
+ appOpsManager.setHistoryParameters(
AppOpsManager.HISTORICAL_MODE_ENABLED_PASSIVE,
SNAPSHOT_INTERVAL_MILLIS,
INTERVAL_COMPRESSION_MULTIPLIER)
@@ -111,36 +111,35 @@
val chunk = createDataChunk()
val chunkCount = (INTERVAL_COMPRESSION_MULTIPLIER * 2) + 3
for (i in 0 until chunkCount) {
- appOpsManager!!.addHistoricalOps(chunk)
+ appOpsManager.addHistoricalOps(chunk)
}
// Validate the data for the first interval
val firstIntervalBeginMillis = computeIntervalBeginRawMillis(0)
val firstIntervalEndMillis = computeIntervalBeginRawMillis(1)
- val firstOps = getHistoricalOpsFromDiskRaw(appOpsManager!!, uid, packageName!!,
- null /*opNames*/, firstIntervalBeginMillis, firstIntervalEndMillis)
+ val firstOps = getHistoricalOpsFromDiskRaw(uid, packageName, null /*opNames*/,
+ firstIntervalBeginMillis, firstIntervalEndMillis)
assertHasCounts(firstOps!!, 197)
// Validate the data for the second interval
val secondIntervalBeginMillis = computeIntervalBeginRawMillis(1)
val secondIntervalEndMillis = computeIntervalBeginRawMillis(2)
- val secondOps = getHistoricalOpsFromDiskRaw(appOpsManager!!, uid, packageName!!,
- null /*opNames*/, secondIntervalBeginMillis, secondIntervalEndMillis)
+ val secondOps = getHistoricalOpsFromDiskRaw(uid, packageName, null /*opNames*/,
+ secondIntervalBeginMillis, secondIntervalEndMillis)
assertHasCounts(secondOps!!, 33)
// Validate the data for both intervals
val thirdIntervalBeginMillis = firstIntervalEndMillis - SNAPSHOT_INTERVAL_MILLIS
val thirdIntervalEndMillis = secondIntervalBeginMillis + SNAPSHOT_INTERVAL_MILLIS
- val thirdOps = getHistoricalOpsFromDiskRaw(appOpsManager!!, uid, packageName!!,
- null /*opNames*/, thirdIntervalBeginMillis, thirdIntervalEndMillis)
+ val thirdOps = getHistoricalOpsFromDiskRaw(uid, packageName, null /*opNames*/,
+ thirdIntervalBeginMillis, thirdIntervalEndMillis)
assertHasCounts(thirdOps!!, 33)
}
- @Ignore("Feature is disabled in Android Q")
@Test
fun testHistoryTimeTravel() {
// Configure historical registry behavior.
- appOpsManager!!.setHistoryParameters(
+ appOpsManager.setHistoryParameters(
AppOpsManager.HISTORICAL_MODE_ENABLED_PASSIVE,
SNAPSHOT_INTERVAL_MILLIS,
INTERVAL_COMPRESSION_MULTIPLIER)
@@ -149,18 +148,18 @@
val chunk = createDataChunk()
val chunkCount = computeSlotCount(2) * SNAPSHOT_INTERVAL_MILLIS / chunk.endTimeMillis
for (i in 0 until chunkCount) {
- appOpsManager!!.addHistoricalOps(chunk)
+ appOpsManager.addHistoricalOps(chunk)
}
// Move history in past with the first interval duration
val firstIntervalDurationMillis = computeIntervalDurationMillis(0)
- appOpsManager!!.offsetHistory(firstIntervalDurationMillis)
+ appOpsManager.offsetHistory(firstIntervalDurationMillis)
// Validate the data for the first interval
val firstIntervalBeginMillis = computeIntervalBeginRawMillis(0)
val firstIntervalEndMillis = firstIntervalBeginMillis + firstIntervalDurationMillis
- val firstOps = getHistoricalOpsFromDiskRaw(appOpsManager!!, uid, packageName!!,
- null /*opNames*/, firstIntervalBeginMillis, firstIntervalEndMillis)
+ val firstOps = getHistoricalOpsFromDiskRaw(uid, packageName, null /*opNames*/,
+ firstIntervalBeginMillis, firstIntervalEndMillis)
assertThat(firstOps).isNotNull()
assertThat(firstOps!!.uidCount).isEqualTo(0)
@@ -168,8 +167,8 @@
val secondIntervalBeginMillis = computeIntervalBeginRawMillis(1)
val secondIntervalDurationMillis = computeIntervalDurationMillis(1)
val secondIntervalEndMillis = secondIntervalBeginMillis + secondIntervalDurationMillis
- val secondOps = getHistoricalOpsFromDiskRaw(appOpsManager!!, uid, packageName!!,
- null /*opNames*/, secondIntervalBeginMillis, secondIntervalEndMillis)
+ val secondOps = getHistoricalOpsFromDiskRaw(uid, packageName, null /*opNames*/,
+ secondIntervalBeginMillis, secondIntervalEndMillis)
val secondChunkCount = ((computeSlotCount(2) - computeSlotCount(1))
.times(SNAPSHOT_INTERVAL_MILLIS) / chunk.endTimeMillis)
assertHasCounts(secondOps!!, 10 * secondChunkCount)
@@ -178,22 +177,22 @@
val thirdIntervalBeginMillis = computeIntervalBeginRawMillis(2)
val thirdIntervalDurationMillis = computeIntervalDurationMillis(2)
val thirdIntervalEndMillis = thirdIntervalBeginMillis + thirdIntervalDurationMillis
- val thirdOps = getHistoricalOpsFromDiskRaw(appOpsManager!!, uid, packageName!!,
- null /*opNames*/, thirdIntervalBeginMillis, thirdIntervalEndMillis)
+ val thirdOps = getHistoricalOpsFromDiskRaw(uid, packageName, null /*opNames*/,
+ thirdIntervalBeginMillis, thirdIntervalEndMillis)
val thirdChunkCount = secondChunkCount / INTERVAL_COMPRESSION_MULTIPLIER
assertHasCounts(thirdOps!!, 10 * thirdChunkCount)
// Move history in future with the first interval duration
- appOpsManager!!.offsetHistory(- (2.5f * firstIntervalDurationMillis).toLong())
+ appOpsManager.offsetHistory(- (2.5f * firstIntervalDurationMillis).toLong())
// Validate the data for the first interval
- val fourthOps = getHistoricalOpsFromDiskRaw(appOpsManager!!, uid, packageName!!,
- null /*opNames*/, firstIntervalBeginMillis, firstIntervalEndMillis)
+ val fourthOps = getHistoricalOpsFromDiskRaw(uid, packageName, null /*opNames*/,
+ firstIntervalBeginMillis, firstIntervalEndMillis)
assertHasCounts(fourthOps!!, 194)
// Validate the data for the second interval
- val fifthOps = getHistoricalOpsFromDiskRaw(appOpsManager!!, uid, packageName!!,
- null /*opNames*/, secondIntervalBeginMillis, secondIntervalEndMillis)
+ val fifthOps = getHistoricalOpsFromDiskRaw(uid, packageName, null /*opNames*/,
+ secondIntervalBeginMillis, secondIntervalEndMillis)
assertThat(fifthOps).isNotNull()
assertHasCounts(fifthOps!!, 1703)
@@ -201,7 +200,7 @@
private fun testHistoricalAggregationSomeLevelsDeep(depth: Int) {
// Configure historical registry behavior.
- appOpsManager!!.setHistoryParameters(
+ appOpsManager.setHistoryParameters(
AppOpsManager.HISTORICAL_MODE_ENABLED_PASSIVE,
SNAPSHOT_INTERVAL_MILLIS,
INTERVAL_COMPRESSION_MULTIPLIER)
@@ -211,14 +210,14 @@
val chunkCount = (computeSlotCount(depth + 1)
.times(SNAPSHOT_INTERVAL_MILLIS) / chunk.endTimeMillis)
for (i in 0 until chunkCount) {
- appOpsManager!!.addHistoricalOps(chunk)
+ appOpsManager.addHistoricalOps(chunk)
}
// Validate the data for the full interval
val intervalBeginMillis = computeIntervalBeginRawMillis(depth)
val intervalEndMillis = computeIntervalBeginRawMillis(depth + 1)
- val ops = getHistoricalOpsFromDiskRaw(appOpsManager!!, uid, packageName!!,
- null /*opNames*/, intervalBeginMillis, intervalEndMillis)
+ val ops = getHistoricalOpsFromDiskRaw(uid, packageName, null /*opNames*/,
+ intervalBeginMillis, intervalEndMillis)
val expectedOpCount = ((computeSlotCount(depth + 1) - computeSlotCount(depth))
.times(SNAPSHOT_INTERVAL_MILLIS) / chunk.endTimeMillis) * 10
assertHasCounts(ops!!, expectedOpCount)
@@ -226,14 +225,14 @@
private fun testGetHistoricalPackageOpsForegroundAtDepth(depth: Int) {
// Configure historical registry behavior.
- appOpsManager!!.setHistoryParameters(
+ appOpsManager.setHistoryParameters(
AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE,
SNAPSHOT_INTERVAL_MILLIS,
INTERVAL_COMPRESSION_MULTIPLIER)
- appOpsManager!!.setUidMode(AppOpsManager.OPSTR_START_FOREGROUND, uid,
+ appOpsManager.setUidMode(AppOpsManager.OPSTR_START_FOREGROUND, uid,
AppOpsManager.MODE_ALLOWED)
- appOpsManager!!.setUidMode(AppOpsManager.OPSTR_START_FOREGROUND, 2000,
+ appOpsManager.setUidMode(AppOpsManager.OPSTR_START_FOREGROUND, 2000,
AppOpsManager.MODE_ALLOWED)
activityRule.activity.waitForResumed()
@@ -247,7 +246,7 @@
// Note ops such that we have data at all levels
for (d in depth downTo 0) {
for (i in 0 until noteCount) {
- appOpsManager!!.noteOp(AppOpsManager.OPSTR_START_FOREGROUND, uid, packageName!!)
+ appOpsManager.noteOp(AppOpsManager.OPSTR_START_FOREGROUND, uid, packageName)
}
if (d > 0) {
@@ -272,7 +271,7 @@
}
// Get all ops for the package
- val allOps = getHistoricalOps(appOpsManager!!, uid, packageName!!,
+ val allOps = getHistoricalOps(appOpsManager, uid, packageName,
null, beginTimeMillis, endTimeMillis)
assertThat(allOps).isNotNull()
@@ -287,7 +286,7 @@
val packageOps = uidOps.getPackageOpsAt(0)
assertThat(packageOps).isNotNull()
- assertThat(packageOps.packageName).isEqualTo(getContext().packageName)
+ assertThat(packageOps.packageName).isEqualTo(packageName)
assertThat(packageOps.opCount).isEqualTo(1)
val op = packageOps.getOpAt(0)
@@ -337,9 +336,9 @@
assertThat(getRejectCount(op, AppOpsManager.UID_STATE_BACKGROUND)).isEqualTo(0)
assertThat(getRejectCount(op, AppOpsManager.UID_STATE_CACHED)).isEqualTo(0)
} finally {
- appOpsManager!!.setUidMode(AppOpsManager.OPSTR_START_FOREGROUND, uid,
+ appOpsManager.setUidMode(AppOpsManager.OPSTR_START_FOREGROUND, uid,
AppOpsManager.MODE_FOREGROUND)
- appOpsManager!!.setUidMode(AppOpsManager.OPSTR_START_FOREGROUND, 2000,
+ appOpsManager.setUidMode(AppOpsManager.OPSTR_START_FOREGROUND, 2000,
AppOpsManager.MODE_FOREGROUND)
}
}
@@ -348,23 +347,28 @@
val chunk = HistoricalOps(SNAPSHOT_INTERVAL_MILLIS / 4,
SNAPSHOT_INTERVAL_MILLIS / 2)
chunk.increaseAccessCount(AppOpsManager.OP_START_FOREGROUND, uid,
- packageName!!, AppOpsManager.UID_STATE_TOP, AppOpsManager.OP_FLAG_SELF, 10)
+ packageName, AppOpsManager.UID_STATE_TOP, AppOpsManager.OP_FLAG_SELF, 10)
chunk.increaseAccessCount(AppOpsManager.OP_START_FOREGROUND, uid,
- packageName!!, AppOpsManager.UID_STATE_BACKGROUND, AppOpsManager.OP_FLAG_SELF, 10)
+ packageName, AppOpsManager.UID_STATE_BACKGROUND, AppOpsManager.OP_FLAG_SELF, 10)
chunk.increaseRejectCount(AppOpsManager.OP_START_FOREGROUND, uid,
- packageName!!, AppOpsManager.UID_STATE_TOP, AppOpsManager.OP_FLAG_SELF, 10)
+ packageName, AppOpsManager.UID_STATE_TOP, AppOpsManager.OP_FLAG_SELF, 10)
chunk.increaseRejectCount(AppOpsManager.OP_START_FOREGROUND, uid,
- packageName!!, AppOpsManager.UID_STATE_BACKGROUND, AppOpsManager.OP_FLAG_SELF, 10)
+ packageName, AppOpsManager.UID_STATE_BACKGROUND, AppOpsManager.OP_FLAG_SELF, 10)
chunk.increaseAccessDuration(AppOpsManager.OP_START_FOREGROUND, uid,
- packageName!!, AppOpsManager.UID_STATE_TOP, AppOpsManager.OP_FLAG_SELF, 10)
+ packageName, AppOpsManager.UID_STATE_TOP, AppOpsManager.OP_FLAG_SELF, 10)
chunk.increaseAccessDuration(AppOpsManager.OP_START_FOREGROUND, uid,
- packageName!!, AppOpsManager.UID_STATE_BACKGROUND, AppOpsManager.OP_FLAG_SELF, 10)
+ packageName, AppOpsManager.UID_STATE_BACKGROUND, AppOpsManager.OP_FLAG_SELF, 10)
return chunk
}
- private fun getHistoricalOps(appOpsManager: AppOpsManager, uid: Int,
- packageName: String, opNames: List<String>?, beginTimeMillis: Long,
- endTimeMillis: Long): HistoricalOps? {
+ private fun getHistoricalOps(
+ appOpsManager: AppOpsManager,
+ uid: Int,
+ packageName: String,
+ opNames: List<String>?,
+ beginTimeMillis: Long,
+ endTimeMillis: Long
+ ): HistoricalOps? {
val array = arrayOfNulls<HistoricalOps>(1)
val lock = ReentrantLock()
val condition = lock.newCondition()
@@ -374,10 +378,9 @@
beginTimeMillis, endTimeMillis)
.setUid(uid)
.setPackageName(packageName)
- .setOpNames(if (opNames != null) ArrayList(opNames) else null)
+ .setOpNames(opNames?.toList())
.build()
- appOpsManager.getHistoricalOps(request, getContext().getMainExecutor(),
- Consumer { ops ->
+ appOpsManager.getHistoricalOps(request, context.mainExecutor, Consumer { ops ->
array[0] = ops
try {
lock.lock()
@@ -426,9 +429,13 @@
return op.getAccessDuration(uidState, uidState, AppOpsManager.OP_FLAGS_ALL)
}
- private fun getHistoricalOpsFromDiskRaw(appOpsManager: AppOpsManager, uid: Int,
- packageName: String, opNames: List<String>?, beginTimeMillis: Long,
- endTimeMillis: Long): HistoricalOps? {
+ private fun getHistoricalOpsFromDiskRaw(
+ uid: Int,
+ packageName: String,
+ opNames: List<String>?,
+ beginTimeMillis: Long,
+ endTimeMillis: Long
+ ): HistoricalOps? {
val array = arrayOfNulls<HistoricalOps>(1)
val lock = ReentrantLock()
val condition = lock.newCondition()
@@ -438,18 +445,18 @@
beginTimeMillis, endTimeMillis)
.setUid(uid)
.setPackageName(packageName)
- .setOpNames(if (opNames != null) ArrayList(opNames) else null)
+ .setOpNames(opNames?.toList())
.build()
- appOpsManager.getHistoricalOpsFromDiskRaw(request, getContext().getMainExecutor(),
- Consumer { ops ->
- array[0] = ops
- try {
- lock.lock()
- condition.signalAll()
- } finally {
- lock.unlock()
- }
- })
+ appOpsManager.getHistoricalOpsFromDiskRaw(request, context.mainExecutor,
+ Consumer { ops ->
+ array[0] = ops
+ try {
+ lock.lock()
+ condition.signalAll()
+ } finally {
+ lock.unlock()
+ }
+ })
condition.await(5, TimeUnit.SECONDS)
return array[0]
} finally {
@@ -461,6 +468,10 @@
const val INTERVAL_COMPRESSION_MULTIPLIER = 10
const val SNAPSHOT_INTERVAL_MILLIS = 1000L
+ val instrumentation get() = InstrumentationRegistry.getInstrumentation()
+ val context get() = instrumentation.context
+ val uiAutomation get() = instrumentation.uiAutomation
+
private fun computeIntervalDurationMillis(depth: Int): Long {
return Math.pow(INTERVAL_COMPRESSION_MULTIPLIER.toDouble(),
(depth + 1).toDouble()).toLong() * SNAPSHOT_INTERVAL_MILLIS
@@ -482,13 +493,5 @@
}
return beginTimeMillis * SNAPSHOT_INTERVAL_MILLIS
}
-
- private fun getInstrumentation(): Instrumentation {
- return InstrumentationRegistry.getInstrumentation()
- }
-
- private fun getContext(): Context {
- return getInstrumentation().context
- }
}
}
diff --git a/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java b/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java
index 0e84505..6a79aba 100644
--- a/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java
+++ b/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java
@@ -74,6 +74,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.IntConsumer;
public class AppWidgetTest extends AppWidgetTestCase {
@@ -739,6 +740,88 @@
@AppModeFull(reason = "Instant apps cannot provide or host app widgets")
@Test
+ public void testAppWidgetRemoved() throws Exception {
+
+ // We want to bind widgets.
+ grantBindAppWidgetPermission();
+
+ final AtomicInteger onAppWidgetRemovedCounter = new AtomicInteger();
+ IntConsumer callback = mock(IntConsumer.class);
+
+ // Create a host and start listening.
+ AppWidgetHost host = new AppWidgetHost(
+ getInstrumentation().getTargetContext(), 0) {
+ @Override
+ public void onAppWidgetRemoved(int widgetId) {
+ synchronized (mLock) {
+ onAppWidgetRemovedCounter.incrementAndGet();
+ mLock.notifyAll();
+ callback.accept(widgetId);
+ }
+ }
+ };
+ host.deleteHost();
+ host.startListening();
+
+ int firstAppWidgetId = 0;
+ int secondAppWidgetId = 0;
+
+ try {
+ // Grab the provider we defined to be bound.
+ AppWidgetProviderInfo firstProviderInfo = getFirstAppWidgetProviderInfo();
+ AppWidgetProviderInfo secondProviderInfo = getSecondAppWidgetProviderInfo();
+
+ // Allocate widget id to bind.
+ firstAppWidgetId = host.allocateAppWidgetId();
+ secondAppWidgetId = host.allocateAppWidgetId();
+
+ //create listeners
+ MyAppWidgetHostView.OnUpdateAppWidgetListener secondAppHostViewListener =
+ mock(MyAppWidgetHostView.OnUpdateAppWidgetListener.class);
+
+ // Bind the first app widget.
+ getAppWidgetManager().bindAppWidgetIdIfAllowed(firstAppWidgetId,
+ firstProviderInfo.getProfile(), firstProviderInfo.provider, null);
+ getAppWidgetManager().bindAppWidgetIdIfAllowed(secondAppWidgetId,
+ secondProviderInfo.getProfile(), secondProviderInfo.provider, null);
+
+ // Disable the first widget while host is listening
+ PackageManager packageManager = getInstrumentation().getTargetContext()
+ .getApplicationContext().getPackageManager();
+ packageManager.setComponentEnabledSetting(firstProviderInfo.provider,
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+ PackageManager.DONT_KILL_APP);
+
+ waitForCallCount(onAppWidgetRemovedCounter, 1);
+
+ // Disable the second widget while host is paused
+ host.stopListening();
+ packageManager.setComponentEnabledSetting(secondProviderInfo.provider,
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+ PackageManager.DONT_KILL_APP);
+
+
+ assertEquals(onAppWidgetRemovedCounter.get(),1);
+ verify(callback).accept(eq(firstAppWidgetId));
+
+ // resume listening
+ host.startListening();
+
+ // Wait for the package change to propagate.
+ waitForCallCount(onAppWidgetRemovedCounter, 2);
+ verify(callback).accept(eq(secondAppWidgetId));
+
+ } finally {
+ // Clean up.
+ host.deleteAppWidgetId(firstAppWidgetId);
+ host.deleteAppWidgetId(secondAppWidgetId);
+ host.deleteHost();
+ revokeBindAppWidgetPermission();
+ }
+ }
+
+ @AppModeFull(reason = "Instant apps cannot provide or host app widgets")
+ @Test
public void testUpdateAppWidgetViaComponentName() throws Exception {
// We want to bind widgets.
grantBindAppWidgetPermission();
@@ -1502,7 +1585,6 @@
private static class MyAppWidgetHostView extends AppWidgetHostView {
private OnUpdateAppWidgetListener mOnUpdateAppWidgetListener;
-
public interface OnUpdateAppWidgetListener {
public void onUpdateAppWidget(RemoteViews remoteViews);
}
diff --git a/tests/tests/assist/Android.bp b/tests/tests/assist/Android.bp
index 632c19e..83be674 100644
--- a/tests/tests/assist/Android.bp
+++ b/tests/tests/assist/Android.bp
@@ -25,11 +25,9 @@
"CtsAssistCommon",
"ctstestrunner-axt",
"compatibility-device-util-axt",
+ "androidx.test.ext.junit",
],
- libs: [
- "android.test.runner.stubs",
- "android.test.base.stubs",
- ],
+
srcs: ["src/**/*.java"],
sdk_version: "test_current",
}
diff --git a/tests/tests/assist/AndroidTest.xml b/tests/tests/assist/AndroidTest.xml
index 3c08038..c8014e6 100644
--- a/tests/tests/assist/AndroidTest.xml
+++ b/tests/tests/assist/AndroidTest.xml
@@ -30,9 +30,6 @@
<option name="test-file-name" value="CtsAssistApp.apk" />
<option name="test-file-name" value="CtsAssistTestCases.apk" />
</target_preparer>
- <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
- <option name="run-command" value="settings put secure voice_interaction_service android.assist.service/.MainInteractionService" />
- </target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.assist.cts" />
<option name="runtime-hint" value="12m30s" />
diff --git a/tests/tests/assist/service/src/android/assist/service/MainInteractionSession.java b/tests/tests/assist/service/src/android/assist/service/MainInteractionSession.java
index 7706bd4..2c96f80 100644
--- a/tests/tests/assist/service/src/android/assist/service/MainInteractionSession.java
+++ b/tests/tests/assist/service/src/android/assist/service/MainInteractionSession.java
@@ -82,7 +82,13 @@
Log.i(TAG, "onDestroy()");
super.onDestroy();
if (mReceiver != null) {
- mContext.unregisterReceiver(mReceiver);
+ try {
+ mContext.unregisterReceiver(mReceiver);
+ } catch (IllegalArgumentException e) {
+ // Ignore this exception when unregisterReceiver fails. Due to there will be timing
+ // case to destroy VoiceInteractionSessionService before VoiceInteractionSession.
+ Log.e(TAG, "Failed to unregister receiver in onDestroy.", e);
+ }
}
}
diff --git a/tests/tests/assist/src/android/assist/cts/AssistStructureTest.java b/tests/tests/assist/src/android/assist/cts/AssistStructureTest.java
index 0506bc9..69150a4 100755
--- a/tests/tests/assist/src/android/assist/cts/AssistStructureTest.java
+++ b/tests/tests/assist/src/android/assist/cts/AssistStructureTest.java
@@ -16,12 +16,17 @@
package android.assist.cts;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.fail;
+
import android.assist.common.AutoResetLatch;
import android.assist.common.Utils;
import android.util.Log;
-import java.util.concurrent.TimeUnit;
+import org.junit.Test;
+import java.util.concurrent.TimeUnit;
/**
* Test that the AssistStructure returned is properly formatted.
@@ -34,12 +39,12 @@
private AutoResetLatch mHasDrawedLatch;
@Override
- protected void setUp() throws Exception {
- super.setUp();
+ protected void customSetup() throws Exception {
mHasDrawedLatch = new AutoResetLatch(1);
mActionLatchReceiver = new ActionLatchReceiver(Utils.APP_3P_HASDRAWED, mHasDrawedLatch);
startTestActivity(TEST_CASE_TYPE);
}
+
private void waitForOnDraw() throws Exception {
Log.i(TAG, "waiting for onDraw() before continuing");
if (!mHasDrawedLatch.await(Utils.ACTIVITY_ONRESUME_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
@@ -47,6 +52,7 @@
}
}
+ @Test
public void testAssistStructure() throws Throwable {
if (!mContext.getPackageManager().hasSystemFeature(FEATURE_VOICE_RECOGNIZERS)) {
Log.d(TAG, "Not running assist tests - voice_recognizers feature is not supported");
@@ -59,9 +65,10 @@
final AutoResetLatch latch = startSession();
waitForContext(latch);
getInstrumentation().waitForIdleSync();
- runTestOnUiThread(() -> {
+ getInstrumentation().runOnMainSync(() -> {
verifyAssistDataNullness(false, false, false, false);
- verifyAssistStructure(Utils.getTestAppComponent(TEST_CASE_TYPE), false /*FLAG_SECURE set*/);
+ verifyAssistStructure(Utils.getTestAppComponent(TEST_CASE_TYPE),
+ false /*FLAG_SECURE set*/);
});
}
}
diff --git a/tests/tests/assist/src/android/assist/cts/AssistTestBase.java b/tests/tests/assist/src/android/assist/cts/AssistTestBase.java
index 9971f74..967101f 100644
--- a/tests/tests/assist/src/android/assist/cts/AssistTestBase.java
+++ b/tests/tests/assist/src/android/assist/cts/AssistTestBase.java
@@ -16,8 +16,15 @@
package android.assist.cts;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.fail;
+
import android.app.ActivityManager;
import android.app.assist.AssistContent;
import android.app.assist.AssistStructure;
@@ -34,7 +41,6 @@
import android.os.LocaleList;
import android.os.RemoteCallback;
import android.provider.Settings;
-import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
import android.util.Pair;
import android.view.Display;
@@ -45,19 +51,32 @@
import android.widget.TextView;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.rule.ActivityTestRule;
+import com.android.compatibility.common.util.SettingsStateChangerRule;
+import com.android.compatibility.common.util.SettingsStateManager;
import com.android.compatibility.common.util.SettingsUtils;
+import com.android.compatibility.common.util.StateKeeperRule;
import com.android.compatibility.common.util.ThrowingRunnable;
import com.android.compatibility.common.util.Timeout;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.rules.RuleChain;
+import org.junit.runner.RunWith;
+
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
-import javax.annotation.Nullable;
-
-public class AssistTestBase extends ActivityInstrumentationTestCase2<TestStartActivity> {
+@RunWith(AndroidJUnit4.class)
+abstract class AssistTestBase {
private static final String TAG = "AssistTestBase";
protected static final String FEATURE_VOICE_RECOGNIZERS = "android.software.voice_recognizers";
@@ -66,10 +85,6 @@
private static final String ASSIST_STRUCTURE_ENABLED = "assist_structure_enabled";
private static final String ASSIST_SCREENSHOT_ENABLED = "assist_screenshot_enabled";
- // TODO: once tests are migrated to JUnit 4, use a @BeforeClass method or StateChangerRule
- // to avoid this hack
- private static boolean mFirstTest = true;
-
private static final Timeout TIMEOUT = new Timeout(
"AssistTestBaseTimeout",
10000,
@@ -79,6 +94,30 @@
private static final long SLEEP_BEFORE_RETRY_MS = 250L;
+ private static final Context sContext = getInstrumentation().getTargetContext();
+
+ private static final SettingsStateManager sStructureEnabledMgr = new SettingsStateManager(
+ sContext, SettingsUtils.NAMESPACE_SECURE, ASSIST_STRUCTURE_ENABLED);
+ private static final SettingsStateManager sScreenshotEnabledMgr = new SettingsStateManager(
+ sContext, SettingsUtils.NAMESPACE_SECURE, ASSIST_SCREENSHOT_ENABLED);
+
+ private final SettingsStateChangerRule mServiceSetterRule = new SettingsStateChangerRule(
+ sContext, Settings.Secure.VOICE_INTERACTION_SERVICE,
+ "android.assist.service/.MainInteractionService");
+ private final StateKeeperRule<String> mStructureEnabledKeeperRule = new StateKeeperRule<>(
+ sStructureEnabledMgr);
+ private final StateKeeperRule<String> mScreenshotEnabledKeeperRule = new StateKeeperRule<>(
+ sScreenshotEnabledMgr);
+ private final ActivityTestRule<TestStartActivity> mActivityTestRule =
+ new ActivityTestRule<>(TestStartActivity.class, false, false);
+
+ @Rule
+ public final RuleChain mLookAllTheseRules = RuleChain
+ .outerRule(mServiceSetterRule)
+ .around(mStructureEnabledKeeperRule)
+ .around(mScreenshotEnabledKeeperRule)
+ .around(mActivityTestRule);
+
protected ActivityManager mActivityManager;
private TestStartActivity mTestActivity;
protected AssistContent mAssistContent;
@@ -108,20 +147,15 @@
private String mTestName;
private View mView;
- public AssistTestBase() {
- super(TestStartActivity.class);
+ @BeforeClass
+ public static void setFeatures() {
+ setFeaturesEnabled(StructureEnabled.TRUE, ScreenshotEnabled.TRUE);
+ logContextAndScreenshotSetting();
}
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mContext = getInstrumentation().getTargetContext();
-
- if (mFirstTest) {
- setFeaturesEnabled(StructureEnabled.TRUE, ScreenshotEnabled.TRUE);
- logContextAndScreenshotSetting();
- mFirstTest = false;
- }
+ @Before
+ public final void setUp() throws Exception {
+ mContext = sContext;
// reset old values
mScreenshotMatches = false;
@@ -134,10 +168,19 @@
prepareDevice();
registerForAsyncReceivingCallback();
+
+ customSetup();
}
- @Override
- protected void tearDown() throws Exception {
+ /**
+ * Test-specific setup - doesn't need to call {@code super} neither use <code>@Before</code>.
+ */
+ protected void customSetup() throws Exception {
+ }
+
+ @After
+ public final void tearDown() throws Exception {
+ customTearDown();
mTestActivity.finish();
mContext.sendBroadcast(new Intent(Utils.HIDE_SESSION));
@@ -146,10 +189,15 @@
m3pActivityCallback.sendResult(Utils.bundleOfRemoteAction(Utils.ACTION_END_OF_TEST));
}
- super.tearDown();
mSessionCompletedLatch.await(3, TimeUnit.SECONDS);
}
+ /**
+ * Test-specific teardown - doesn't need to call {@code super} neither use <code>@After</code>.
+ */
+ protected void customTearDown() throws Exception {
+ }
+
private void prepareDevice() throws Exception {
Log.d(TAG, "prepareDevice()");
@@ -213,8 +261,7 @@
intent.setAction("android.intent.action.TEST_START_ACTIVITY_" + testName);
intent.putExtra(Utils.TESTCASE_TYPE, testName);
intent.putExtra(Utils.EXTRA_REMOTE_CALLBACK, mRemoteCallback);
- setActivityIntent(intent);
- mTestActivity = getActivity();
+ mTestActivity = mActivityTestRule.launchActivity(intent);
mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
}
@@ -339,23 +386,21 @@
*/
protected void verifyAssistStructure(ComponentName backgroundApp, boolean isSecureWindow) {
// Check component name matches
- assertEquals(backgroundApp.flattenToString(),
- mAssistStructure.getActivityComponent().flattenToString());
+ assertThat(mAssistStructure.getActivityComponent().flattenToString())
+ .isEqualTo(backgroundApp.flattenToString());
long acquisitionStart = mAssistStructure.getAcquisitionStartTime();
long acquisitionEnd = mAssistStructure.getAcquisitionEndTime();
- assertTrue(acquisitionStart > 0);
- assertTrue(acquisitionEnd > 0);
- assertTrue(acquisitionEnd >= acquisitionStart);
+ assertThat(acquisitionStart).isGreaterThan(0L);
+ assertThat(acquisitionEnd).isGreaterThan(0L);
+ assertThat(acquisitionEnd).isAtLeast(acquisitionStart);
Log.i(TAG, "Traversing down structure for: " + backgroundApp.flattenToString());
mView = mTestActivity.findViewById(android.R.id.content).getRootView();
verifyHierarchy(mAssistStructure, isSecureWindow);
}
- protected void logContextAndScreenshotSetting() {
- Log.i(TAG, "Context is: " + Settings.Secure.getString(
- mContext.getContentResolver(), "assist_structure_enabled"));
- Log.i(TAG, "Screenshot is: " + Settings.Secure.getString(
- mContext.getContentResolver(), "assist_screenshot_enabled"));
+ protected static void logContextAndScreenshotSetting() {
+ Log.i(TAG, "Context is: " + sStructureEnabledMgr.get());
+ Log.i(TAG, "Screenshot is: " + sScreenshotEnabledMgr.get());
}
/**
@@ -366,7 +411,7 @@
int numWindows = structure.getWindowNodeCount();
// TODO: multiple windows?
- assertEquals("Number of windows don't match", 1, numWindows);
+ assertWithMessage("Number of windows don't match").that(numWindows).isEqualTo(1);
int[] appLocationOnScreen = new int[2];
mView.getLocationOnScreen(appLocationOnScreen);
@@ -375,10 +420,10 @@
Log.i(TAG, "Title: " + windowNode.getTitle());
// Verify top level window bounds are as big as the app and pinned to its top-left
// corner.
- assertEquals("Window left position wrong: was " + windowNode.getLeft(),
- windowNode.getLeft(), appLocationOnScreen[0]);
- assertEquals("Window top position wrong: was " + windowNode.getTop(),
- windowNode.getTop(), appLocationOnScreen[1]);
+ assertWithMessage("Window left position wrong: was %s", windowNode.getLeft())
+ .that(appLocationOnScreen[0]).isEqualTo(windowNode.getLeft());
+ assertWithMessage("Window top position wrong: was %s", windowNode.getTop())
+ .that(appLocationOnScreen[1]).isEqualTo(windowNode.getTop());
traverseViewAndStructure(
mView,
windowNode.getRootViewNode(),
@@ -421,7 +466,7 @@
}
Log.i(TAG, "Node ID: " + parentNode.getIdEntry());
- assertEquals("IDs do not match", parentViewId, parentNode.getIdEntry());
+ assertWithMessage("IDs do not match").that(parentNode.getIdEntry()).isEqualTo(parentViewId);
int numViewChildren = 0;
int numNodeChildren = 0;
@@ -431,22 +476,26 @@
numNodeChildren = parentNode.getChildCount();
if (isSecureWindow) {
- assertTrue("ViewNode property isAssistBlocked is false", parentNode.isAssistBlocked());
- assertEquals("Secure window should only traverse root node.", 0, numNodeChildren);
+ assertWithMessage("ViewNode property isAssistBlocked is false")
+ .that(parentNode.isAssistBlocked()).isTrue();
+ assertWithMessage("Secure window should only traverse root node")
+ .that(numNodeChildren).isEqualTo(0);
isSecureWindow = false;
} else if (parentNode.getClassName().equals("android.webkit.WebView")) {
// WebView will also appear to have no children while the node does, traverse node
- assertTrue("AssistStructure returned a WebView where the view wasn't one",
- parentView instanceof WebView);
+ assertWithMessage("AssistStructure returned a WebView where the view wasn't one").that(
+ parentView instanceof WebView).isTrue();
boolean textInWebView = false;
for (int i = numNodeChildren - 1; i >= 0; i--) {
textInWebView |= traverseWebViewForText(parentNode.getChildAt(i));
}
- assertTrue("Did not find expected strings inside WebView", textInWebView);
+ assertWithMessage("Did not find expected strings inside WebView").that(textInWebView)
+ .isTrue();
} else {
- assertEquals("Number of children did not match.", numViewChildren, numNodeChildren);
+ assertWithMessage("Number of children did not match").that(numNodeChildren)
+ .isEqualTo(numViewChildren);
verifyViewProperties(parentView, parentNode);
@@ -459,7 +508,7 @@
ViewNode childNode = parentNode.getChildAt(i);
// if isSecureWindow, should not have reached this point.
- assertFalse(isSecureWindow);
+ assertThat(isSecureWindow).isFalse();
traverseViewAndStructure(childView, childNode, isSecureWindow);
}
}
@@ -485,18 +534,18 @@
* Return true if the expected domain is found in the WebView, else fail.
*/
protected void verifyAssistStructureHasWebDomain(String domain) {
- assertTrue(traverse(mAssistStructure.getWindowNodeAt(0).getRootViewNode(), (n) -> {
+ assertThat(traverse(mAssistStructure.getWindowNodeAt(0).getRootViewNode(), (n) -> {
return n.getWebDomain() != null && domain.equals(n.getWebDomain());
- }));
+ })).isTrue();
}
/**
* Return true if the expected LocaleList is found in the WebView, else fail.
*/
protected void verifyAssistStructureHasLocaleList(LocaleList localeList) {
- assertTrue(traverse(mAssistStructure.getWindowNodeAt(0).getRootViewNode(), (n) -> {
+ assertThat(traverse(mAssistStructure.getWindowNodeAt(0).getRootViewNode(), (n) -> {
return n.getLocaleList() != null && localeList.equals(n.getLocaleList());
- }));
+ })).isTrue();
}
interface ViewNodeVisitor {
@@ -515,30 +564,34 @@
return false;
}
- protected void setFeaturesEnabled(StructureEnabled structure, ScreenshotEnabled screenshot) {
+ protected static void setFeaturesEnabled(StructureEnabled structure,
+ ScreenshotEnabled screenshot) {
Log.i(TAG, "setFeaturesEnabled(" + structure + ", " + screenshot + ")");
- SettingsUtils.syncSet(mContext, ASSIST_STRUCTURE_ENABLED, structure.value);
- SettingsUtils.syncSet(mContext, ASSIST_SCREENSHOT_ENABLED, screenshot.value);
+ sStructureEnabledMgr.set(structure.value);
+ sScreenshotEnabledMgr.set(screenshot.value);
}
/**
* Compare view properties of the view hierarchy with that reported in the assist structure.
*/
private void verifyViewProperties(View parentView, ViewNode parentNode) {
- assertEquals("Left positions do not match.", parentView.getLeft(), parentNode.getLeft());
- assertEquals("Top positions do not match.", parentView.getTop(), parentNode.getTop());
- assertEquals("Opaque flags do not match.", parentView.isOpaque(), parentNode.isOpaque());
+ assertWithMessage("Left positions do not match").that(parentNode.getLeft())
+ .isEqualTo(parentView.getLeft());
+ assertWithMessage("Top positions do not match").that(parentNode.getTop())
+ .isEqualTo(parentView.getTop());
+ assertWithMessage("Opaque flags do not match").that(parentNode.isOpaque())
+ .isEqualTo(parentView.isOpaque());
int viewId = parentView.getId();
if (viewId > 0) {
if (parentNode.getIdEntry() != null) {
- assertEquals("View IDs do not match.",
- mTestActivity.getResources().getResourceEntryName(viewId),
- parentNode.getIdEntry());
+ assertWithMessage("View IDs do not match.").that(parentNode.getIdEntry())
+ .isEqualTo(mTestActivity.getResources().getResourceEntryName(viewId));
}
} else {
- assertNull("View Node should not have an ID.", parentNode.getIdEntry());
+ assertWithMessage("View Node should not have an ID").that(parentNode.getIdEntry())
+ .isNull();
}
Log.i(TAG, "parent text: " + parentNode.getText());
@@ -546,23 +599,26 @@
Log.i(TAG, "view text: " + ((TextView) parentView).getText());
}
-
- assertEquals("Scroll X does not match.", parentView.getScrollX(), parentNode.getScrollX());
- assertEquals("Scroll Y does not match.", parentView.getScrollY(), parentNode.getScrollY());
- assertEquals("Heights do not match.", parentView.getHeight(), parentNode.getHeight());
- assertEquals("Widths do not match.", parentView.getWidth(), parentNode.getWidth());
+ assertWithMessage("Scroll X does not match").that(parentNode.getScrollX())
+ .isEqualTo(parentView.getScrollX());
+ assertWithMessage("Scroll Y does not match").that(parentNode.getScrollY())
+ .isEqualTo(parentView.getScrollY());
+ assertWithMessage("Heights do not match").that(parentNode.getHeight())
+ .isEqualTo(parentView.getHeight());
+ assertWithMessage("Widths do not match").that(parentNode.getWidth())
+ .isEqualTo(parentView.getWidth());
if (parentView instanceof TextView) {
if (parentView instanceof EditText) {
- assertEquals("Text selection start does not match",
- ((EditText) parentView).getSelectionStart(),
- parentNode.getTextSelectionStart());
- assertEquals("Text selection end does not match",
- ((EditText) parentView).getSelectionEnd(),
- parentNode.getTextSelectionEnd());
+ assertWithMessage("Text selection start does not match",
+ parentNode.getTextSelectionStart(),
+ ((EditText) parentView).getSelectionStart());
+ assertWithMessage("Text selection end does not match",
+ parentNode.getTextSelectionEnd(),
+ ((EditText) parentView).getSelectionEnd());
}
TextView textView = (TextView) parentView;
- assertEquals(textView.getTextSize(), parentNode.getTextSize());
+ assertThat(parentNode.getTextSize()).isWithin(0.01F).of(textView.getTextSize());
String viewString = textView.getText().toString();
String nodeString = parentNode.getText().toString();
@@ -570,23 +626,21 @@
Log.i(TAG, "Verifying text within TextView at the beginning");
Log.i(TAG, "view string: " + viewString);
Log.i(TAG, "node string: " + nodeString);
- assertTrue("String length is unexpected: original string - " + viewString.length() +
- ", string in AssistData - " + nodeString.length(),
- viewString.length() >= nodeString.length());
- assertTrue("Expected a longer string to be shown. expected: "
- + Math.min(viewString.length(), 30) + " was: " + nodeString
- .length(),
- nodeString.length() >= Math.min(viewString.length(), 30));
+ assertWithMessage("String length is unexpected: original string - %s, "
+ + "string in AssistData - %s", viewString.length(), nodeString.length())
+ .that(viewString.length()).isAtLeast(nodeString.length());
+ assertWithMessage("Expected a longer string to be shown").that(
+ nodeString.length()).isAtLeast(Math.min(viewString.length(), 30));
for (int x = 0; x < parentNode.getText().length(); x++) {
- assertEquals("Char not equal at index: " + x,
- ((TextView) parentView).getText().toString().charAt(x),
- parentNode.getText().charAt(x));
+ assertWithMessage("Char not equal at index: %s", x).that(
+ parentNode.getText().charAt(x)).isEqualTo(
+ ((TextView) parentView).getText().toString().charAt(x));
}
} else if (parentNode.getScrollX() == parentView.getWidth()) {
}
} else {
- assertNull(parentNode.getText());
+ assertThat(parentNode.getText()).isNull();
}
}
diff --git a/tests/tests/assist/src/android/assist/cts/AssistantContentViewTest.java b/tests/tests/assist/src/android/assist/cts/AssistantContentViewTest.java
index a4a28d7..cd1c8bc 100644
--- a/tests/tests/assist/src/android/assist/cts/AssistantContentViewTest.java
+++ b/tests/tests/assist/src/android/assist/cts/AssistantContentViewTest.java
@@ -16,12 +16,18 @@
package android.assist.cts;
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
+
import android.assist.common.AutoResetLatch;
import android.assist.common.Utils;
import android.graphics.Point;
import android.os.Bundle;
import android.util.Log;
+import org.junit.Test;
+
import java.util.concurrent.TimeUnit;
/** Test verifying the Content View of the Assistant */
@@ -31,16 +37,14 @@
private Bundle mBundle;
@Override
- public void setUp() throws Exception {
- super.setUp();
+ protected void customSetup() throws Exception {
mActionLatchReceiver = new AssistantReceiver();
startTestActivity(Utils.VERIFY_CONTENT_VIEW);
}
@Override
- public void tearDown() throws Exception {
+ protected void customTearDown() throws Exception {
mBundle = null;
- super.tearDown();
}
private void waitForContentView() throws Exception {
@@ -50,6 +54,7 @@
}
}
+ @Test
public void testAssistantContentViewDimens() throws Exception {
if (mActivityManager.isLowRamDevice()) {
Log.d(TAG, "Not running assist tests on low-RAM device.");
@@ -62,8 +67,8 @@
int height = mBundle.getInt(Utils.EXTRA_CONTENT_VIEW_HEIGHT, 0);
int width = mBundle.getInt(Utils.EXTRA_CONTENT_VIEW_WIDTH, 0);
Point displayPoint = mBundle.getParcelable(Utils.EXTRA_DISPLAY_POINT);
- assertEquals(displayPoint.y, height);
- assertEquals(displayPoint.x, width);
+ assertThat(height).isEqualTo(displayPoint.y);
+ assertThat(width).isEqualTo(displayPoint.x);
}
private class AssistantReceiver extends ActionLatchReceiver {
diff --git a/tests/tests/assist/src/android/assist/cts/DisableContextTest.java b/tests/tests/assist/src/android/assist/cts/DisableContextTest.java
index 11e404f..912c5d7 100644
--- a/tests/tests/assist/src/android/assist/cts/DisableContextTest.java
+++ b/tests/tests/assist/src/android/assist/cts/DisableContextTest.java
@@ -19,6 +19,8 @@
import android.assist.common.Utils;
import android.util.Log;
+import org.junit.Test;
+
/** Test we receive proper assist data when context is disabled or enabled */
public class DisableContextTest extends AssistTestBase {
static final String TAG = "DisableContextTest";
@@ -26,18 +28,17 @@
private static final String TEST_CASE_TYPE = Utils.DISABLE_CONTEXT;
@Override
- public void setUp() throws Exception {
- super.setUp();
+ protected void customSetup() throws Exception {
startTestActivity(TEST_CASE_TYPE);
}
@Override
- public void tearDown() throws Exception {
+ public void customTearDown() throws Exception {
setFeaturesEnabled(StructureEnabled.TRUE, ScreenshotEnabled.TRUE);
logContextAndScreenshotSetting();
- super.tearDown();
}
+ @Test
public void testContextAndScreenshotOff() throws Exception {
if (!mContext.getPackageManager().hasSystemFeature(FEATURE_VOICE_RECOGNIZERS)) {
Log.d(TAG, "Not running assist tests - voice_recognizers feature is not supported");
@@ -53,6 +54,7 @@
verifyAssistDataNullness(true, true, true, true);
}
+ @Test
public void testContextOff() throws Exception {
if (!mContext.getPackageManager().hasSystemFeature(FEATURE_VOICE_RECOGNIZERS)) {
Log.d(TAG, "Not running assist tests - voice_recognizers feature is not supported");
@@ -68,6 +70,7 @@
verifyAssistDataNullness(false, false, false, true);
}
+ @Test
public void testScreenshotOff() throws Exception {
if (!mContext.getPackageManager().hasSystemFeature(FEATURE_VOICE_RECOGNIZERS)) {
Log.d(TAG, "Not running assist tests - voice_recognizers feature is not supported");
diff --git a/tests/tests/assist/src/android/assist/cts/ExtraAssistDataTest.java b/tests/tests/assist/src/android/assist/cts/ExtraAssistDataTest.java
index 2acf4e3..d465880 100644
--- a/tests/tests/assist/src/android/assist/cts/ExtraAssistDataTest.java
+++ b/tests/tests/assist/src/android/assist/cts/ExtraAssistDataTest.java
@@ -21,16 +21,19 @@
import android.os.Bundle;
import android.util.Log;
+import org.junit.Test;
+
+import static com.google.common.truth.Truth.assertWithMessage;
public class ExtraAssistDataTest extends AssistTestBase {
private static final String TAG = "ExtraAssistDataTest";
private static final String TEST_CASE_TYPE = Utils.EXTRA_ASSIST;
@Override
- public void setUp() throws Exception {
- super.setUp();
+ protected void customSetup() throws Exception {
startTestActivity(TEST_CASE_TYPE);
}
+ @Test
public void testAssistContentAndAssistData() throws Exception {
if (mActivityManager.isLowRamDevice()) {
Log.d(TAG, "Not running assist tests on low-RAM device.");
@@ -46,20 +49,22 @@
Log.i(TAG, "assist bundle is: " + Utils.toBundleString(mAssistBundle));
// first tests that the assist content's structured data is the expected
- assertEquals("AssistContent structured data did not match data in onProvideAssistContent",
- Utils.getStructuredJSON(), mAssistContent.getStructuredData());
+ assertWithMessage(
+ "AssistContent structured data did not match data in onProvideAssistContent").that(
+ mAssistContent.getStructuredData()).isEqualTo(Utils.getStructuredJSON());
Bundle extraExpectedBundle = Utils.getExtraAssistBundle();
Bundle extraAssistBundle = mAssistBundle.getBundle(Intent.EXTRA_ASSIST_CONTEXT);
for (String key : extraExpectedBundle.keySet()) {
- assertTrue("Assist bundle does not contain expected extra context key: " + key,
- extraAssistBundle.containsKey(key));
- assertEquals("Extra assist context bundle values do not match for key: " + key,
- extraExpectedBundle.get(key), extraAssistBundle.get(key));
+ assertWithMessage("Assist bundle does not contain expected extra context key: %s", key)
+ .that(extraAssistBundle.containsKey(key)).isTrue();
+ assertWithMessage("Extra assist context bundle values do not match for key: %s", key)
+ .that(extraAssistBundle.get(key)).isEqualTo(extraExpectedBundle.get(key));
}
// then test the EXTRA_ASSIST_UID
int expectedUid = Utils.getExpectedUid(extraAssistBundle);
int actualUid = mAssistBundle.getInt(Intent.EXTRA_ASSIST_UID);
- assertEquals("Wrong value for EXTRA_ASSIST_UID", expectedUid, actualUid);
+ assertWithMessage("Wrong value for EXTRA_ASSIST_UID").that(actualUid)
+ .isEqualTo(expectedUid);
}
}
diff --git a/tests/tests/assist/src/android/assist/cts/FlagSecureTest.java b/tests/tests/assist/src/android/assist/cts/FlagSecureTest.java
index 97573d8..d5724b9 100644
--- a/tests/tests/assist/src/android/assist/cts/FlagSecureTest.java
+++ b/tests/tests/assist/src/android/assist/cts/FlagSecureTest.java
@@ -20,6 +20,8 @@
import android.assist.common.Utils;
import android.util.Log;
+import org.junit.Test;
+
/**
* Test we receive proper assist data (root assistStructure with no children) when the assistant is
* invoked on an app with FLAG_SECURE set.
@@ -30,11 +32,11 @@
private static final String TEST_CASE_TYPE = Utils.FLAG_SECURE;
@Override
- public void setUp() throws Exception {
- super.setUp();
+ protected void customSetup() throws Exception {
startTestActivity(TEST_CASE_TYPE);
}
+ @Test
public void testSecureActivity() throws Exception {
if (!mContext.getPackageManager().hasSystemFeature(FEATURE_VOICE_RECOGNIZERS)) {
Log.d(TAG, "Not running assist tests - voice_recognizers feature is not supported");
diff --git a/tests/tests/assist/src/android/assist/cts/FocusChangeTest.java b/tests/tests/assist/src/android/assist/cts/FocusChangeTest.java
index bc63a43..e4e530a 100644
--- a/tests/tests/assist/src/android/assist/cts/FocusChangeTest.java
+++ b/tests/tests/assist/src/android/assist/cts/FocusChangeTest.java
@@ -16,11 +16,15 @@
package android.assist.cts;
+import static org.junit.Assert.fail;
+
import android.assist.common.AutoResetLatch;
import android.assist.common.Utils;
import android.util.Log;
import android.util.Pair;
+import org.junit.Test;
+
import java.util.concurrent.TimeUnit;
/** Test that triggering the Assistant causes the underlying Activity to lose focus **/
@@ -32,8 +36,7 @@
private AutoResetLatch mHasLostFocusLatch = new AutoResetLatch(1);
@Override
- public void setUp() throws Exception {
- super.setUp();
+ protected void customSetup() throws Exception {
mActionLatchReceiver = new ActionLatchReceiver(
Pair.create(Utils.GAINED_FOCUS, mHasGainedFocusLatch),
Pair.create(Utils.LOST_FOCUS, mHasLostFocusLatch)
@@ -56,6 +59,7 @@
}
}
+ @Test
public void testLayerCausesUnderlyingActivityToLoseFocus() throws Exception {
if (mActivityManager.isLowRamDevice()) {
Log.d(TAG, "Not running assist tests on low-RAM device.");
diff --git a/tests/tests/assist/src/android/assist/cts/LargeViewHierarchyTest.java b/tests/tests/assist/src/android/assist/cts/LargeViewHierarchyTest.java
index 05a473e..80647e5 100644
--- a/tests/tests/assist/src/android/assist/cts/LargeViewHierarchyTest.java
+++ b/tests/tests/assist/src/android/assist/cts/LargeViewHierarchyTest.java
@@ -20,6 +20,8 @@
import android.assist.common.Utils;
import android.util.Log;
+import org.junit.Test;
+
/**
* Test that the AssistStructure returned is properly formatted.
*/
@@ -28,11 +30,11 @@
private static final String TEST_CASE_TYPE = Utils.LARGE_VIEW_HIERARCHY;
@Override
- protected void setUp() throws Exception {
- super.setUp();
+ protected void customSetup() throws Exception {
startTestActivity(TEST_CASE_TYPE);
}
+ @Test
public void testTextView() throws Exception {
if (mActivityManager.isLowRamDevice()) {
Log.d(TAG, "Not running assist tests on low-RAM device.");
diff --git a/tests/tests/assist/src/android/assist/cts/LifecycleTest.java b/tests/tests/assist/src/android/assist/cts/LifecycleTest.java
index c35f8f0..35cec57 100644
--- a/tests/tests/assist/src/android/assist/cts/LifecycleTest.java
+++ b/tests/tests/assist/src/android/assist/cts/LifecycleTest.java
@@ -16,11 +16,15 @@
package android.assist.cts;
+import static org.junit.Assert.fail;
+
import android.assist.common.AutoResetLatch;
import android.assist.common.Utils;
import android.os.Bundle;
import android.util.Log;
+import org.junit.Test;
+
import java.util.concurrent.TimeUnit;
/** Test we receive proper assist data when context is disabled or enabled */
@@ -41,8 +45,7 @@
private boolean mLostFocusIsLifecycle;
@Override
- public void setUp() throws Exception {
- super.setUp();
+ protected void customSetup() throws Exception {
mActionLatchReceiver = new LifecycleTestReceiver();
mLostFocusIsLifecycle = false;
startTestActivity(TEST_CASE_TYPE);
@@ -75,6 +78,7 @@
}
}
+ @Test
public void testLayerDoesNotTriggerLifecycleMethods() throws Exception {
if (!mContext.getPackageManager().hasSystemFeature(FEATURE_VOICE_RECOGNIZERS)) {
Log.d(TAG, "Not running assist tests - voice_recognizers feature is not supported");
@@ -100,6 +104,7 @@
waitForDestroy();
}
+ @Test
public void testNoUiLayerDoesNotTriggerLifecycleMethods() throws Exception {
if (mActivityManager.isLowRamDevice()) {
Log.d(TAG, "Not running assist tests on low-RAM device.");
diff --git a/tests/tests/assist/src/android/assist/cts/ScreenshotTest.java b/tests/tests/assist/src/android/assist/cts/ScreenshotTest.java
index dcc3be0..c9d16c8 100644
--- a/tests/tests/assist/src/android/assist/cts/ScreenshotTest.java
+++ b/tests/tests/assist/src/android/assist/cts/ScreenshotTest.java
@@ -16,24 +16,28 @@
package android.assist.cts;
+import static com.google.common.truth.Truth.assertThat;
+
import android.assist.common.AutoResetLatch;
import android.assist.common.Utils;
import android.graphics.Color;
import android.os.Bundle;
import android.util.Log;
+import org.junit.Test;
+
public class ScreenshotTest extends AssistTestBase {
static final String TAG = "ScreenshotTest";
private static final String TEST_CASE_TYPE = Utils.SCREENSHOT;
@Override
- protected void setUp() throws Exception {
- super.setUp();
+ protected void customSetup() throws Exception {
// start test start activity
startTestActivity(TEST_CASE_TYPE);
}
+ @Test
public void testRedScreenshot() throws Throwable {
if (mActivityManager.isLowRamDevice()) {
Log.d(TAG, "Not running assist tests on low-RAM device.");
@@ -50,10 +54,11 @@
eventuallyWithSessionClose(() -> {
delayAndStartSession(Color.RED);
verifyAssistDataNullness(false, false, false, false);
- assertTrue(mScreenshotMatches);
+ assertThat(mScreenshotMatches).isTrue();
});
}
+ @Test
public void testGreenScreenshot() throws Throwable {
if (mActivityManager.isLowRamDevice()) {
Log.d(TAG, "Not running assist tests on low-RAM device.");
@@ -70,10 +75,11 @@
eventuallyWithSessionClose(() -> {
delayAndStartSession(Color.GREEN);
verifyAssistDataNullness(false, false, false, false);
- assertTrue(mScreenshotMatches);
+ assertThat(mScreenshotMatches).isTrue();
});
}
+ @Test
public void testBlueScreenshot() throws Throwable {
if (mActivityManager.isLowRamDevice()) {
Log.d(TAG, "Not running assist tests on low-RAM device.");
@@ -90,7 +96,7 @@
eventuallyWithSessionClose(() -> {
delayAndStartSession(Color.BLUE);
verifyAssistDataNullness(false, false, false, false);
- assertTrue(mScreenshotMatches);
+ assertThat(mScreenshotMatches).isTrue();
});
}
diff --git a/tests/tests/assist/src/android/assist/cts/TextViewTest.java b/tests/tests/assist/src/android/assist/cts/TextViewTest.java
index 6b06442..78c0a52 100644
--- a/tests/tests/assist/src/android/assist/cts/TextViewTest.java
+++ b/tests/tests/assist/src/android/assist/cts/TextViewTest.java
@@ -21,6 +21,8 @@
import android.os.Bundle;
import android.util.Log;
+import org.junit.Test;
+
/**
* Test that the AssistStructure returned is properly formatted.
*/
@@ -29,11 +31,11 @@
private static final String TEST_CASE_TYPE = Utils.TEXTVIEW;
@Override
- protected void setUp() throws Exception {
- super.setUp();
+ protected void customSetup() throws Exception {
startTestActivity(TEST_CASE_TYPE);
}
+ @Test
public void testTextView() throws Exception {
if (mActivityManager.isLowRamDevice()) {
Log.d(TAG, "Not running assist tests on low-RAM device.");
diff --git a/tests/tests/assist/src/android/assist/cts/WebViewTest.java b/tests/tests/assist/src/android/assist/cts/WebViewTest.java
index 75fc0ba..a7f9329 100644
--- a/tests/tests/assist/src/android/assist/cts/WebViewTest.java
+++ b/tests/tests/assist/src/android/assist/cts/WebViewTest.java
@@ -16,11 +16,15 @@
package android.assist.cts;
+import static org.junit.Assert.fail;
+
import android.assist.common.AutoResetLatch;
import android.assist.common.Utils;
import android.content.pm.PackageManager;
import android.util.Log;
+import org.junit.Test;
+
import java.util.concurrent.TimeUnit;
/**
@@ -33,8 +37,7 @@
private final AutoResetLatch mTestWebViewLatch = new AutoResetLatch();
@Override
- protected void setUp() throws Exception {
- super.setUp();
+ protected void customSetup() throws Exception {
mActionLatchReceiver = new ActionLatchReceiver(Utils.TEST_ACTIVITY_WEBVIEW_LOADED, mTestWebViewLatch);
startTestActivity(TEST_CASE_TYPE);
}
@@ -46,6 +49,7 @@
}
}
+ @Test
public void testWebView() throws Throwable {
if (mActivityManager.isLowRamDevice()) {
Log.d(TAG, "Not running assist tests on low-RAM device.");
diff --git a/tests/tests/background/OWNERS b/tests/tests/background/OWNERS
new file mode 100644
index 0000000..ec12f79
--- /dev/null
+++ b/tests/tests/background/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 330055
+include /tests/app/OWNERS
diff --git a/tests/tests/batterysaving/Android.bp b/tests/tests/batterysaving/Android.bp
index 032d2bf..b0007e2 100644
--- a/tests/tests/batterysaving/Android.bp
+++ b/tests/tests/batterysaving/Android.bp
@@ -22,6 +22,7 @@
"mockito-target-minus-junit4",
"compatibility-device-util-axt",
"ctstestrunner-axt",
+ "platformprotosnano",
"truth-prebuilt",
"ub-uiautomator",
],
diff --git a/tests/tests/batterysaving/OWNERS b/tests/tests/batterysaving/OWNERS
new file mode 100644
index 0000000..1fe2293
--- /dev/null
+++ b/tests/tests/batterysaving/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 330055
+omakoto@google.com
+yamasani@google.com
diff --git a/tests/tests/batterysaving/src/android/os/cts/batterysaving/BatterySavingTestBase.java b/tests/tests/batterysaving/src/android/os/cts/batterysaving/BatterySavingTestBase.java
index e2cf479..3db9dbb 100644
--- a/tests/tests/batterysaving/src/android/os/cts/batterysaving/BatterySavingTestBase.java
+++ b/tests/tests/batterysaving/src/android/os/cts/batterysaving/BatterySavingTestBase.java
@@ -34,6 +34,9 @@
import com.android.compatibility.common.util.BatteryUtils;
import com.android.compatibility.common.util.BeforeAfterRule;
import com.android.compatibility.common.util.OnFailureRule;
+import com.android.compatibility.common.util.ProtoUtils;
+import com.android.server.job.nano.JobSchedulerServiceDumpProto;
+import com.android.server.job.nano.StateControllerProto;
import org.junit.Rule;
import org.junit.rules.RuleChain;
@@ -96,9 +99,19 @@
}
public void waitUntilJobForceAppStandby(boolean expected) throws Exception {
- waitUntil("Force all apps standby still " + !expected + " (job)", () ->
- runShellCommand("dumpsys jobscheduler")
- .contains("Force all apps standby: " + expected));
+ waitUntil("Force all apps standby still " + !expected + " (job)", () -> {
+ JobSchedulerServiceDumpProto proto = ProtoUtils.getProto(
+ InstrumentationRegistry.getInstrumentation().getUiAutomation(),
+ JobSchedulerServiceDumpProto.class,
+ ProtoUtils.DUMPSYS_JOB_SCHEDULER);
+ for (StateControllerProto controllerProto : proto.controllers) {
+ if (controllerProto.hasBackground()) {
+ return controllerProto.getBackground().appStateTracker.forceAllAppsStandby
+ == expected;
+ }
+ }
+ return false;
+ });
}
public void waitUntilForceBackgroundCheck(boolean expected) throws Exception {
diff --git a/tests/tests/batterysaving/src/android/os/cts/deviceidle/DeviceIdleTest.java b/tests/tests/batterysaving/src/android/os/cts/deviceidle/DeviceIdleTest.java
new file mode 100644
index 0000000..20410e7
--- /dev/null
+++ b/tests/tests/batterysaving/src/android/os/cts/deviceidle/DeviceIdleTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.os.cts.deviceidle;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.os.PowerManager;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class DeviceIdleTest {
+ @Test
+ public void testDeviceIdleManager() {
+ Context context = InstrumentationRegistry.getInstrumentation().getContext();
+ assertNotNull(context.getSystemService(Context.DEVICE_IDLE_CONTROLLER));
+ }
+
+ @Test
+ public void testPowerManagerIgnoringBatteryOptimizations() {
+ Context context = InstrumentationRegistry.getInstrumentation().getContext();
+
+ assertTrue(context.getSystemService(PowerManager.class)
+ .isIgnoringBatteryOptimizations("com.android.shell"));
+ assertFalse(context.getSystemService(PowerManager.class)
+ .isIgnoringBatteryOptimizations("no.such.package.!!!"));
+ }
+
+}
diff --git a/tests/tests/car/TEST_MAPPING b/tests/tests/car/TEST_MAPPING
index ef3ec4a..d4ff46d 100644
--- a/tests/tests/car/TEST_MAPPING
+++ b/tests/tests/car/TEST_MAPPING
@@ -1,8 +1,7 @@
{
- "presubmit": [
+ "auto-presubmit": [
{
- "name": "CtsCarTestCases",
- "keywords": ["auto-cf"]
+ "name": "CtsCarTestCases"
}
]
}
diff --git a/tests/tests/car/src/android/car/cts/CarPropertyManagerTest.java b/tests/tests/car/src/android/car/cts/CarPropertyManagerTest.java
index 56e2401..ceabc3d 100644
--- a/tests/tests/car/src/android/car/cts/CarPropertyManagerTest.java
+++ b/tests/tests/car/src/android/car/cts/CarPropertyManagerTest.java
@@ -33,6 +33,8 @@
import androidx.test.runner.AndroidJUnit4;
+import com.android.compatibility.common.util.CddTest;
+
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertTrue;
@@ -117,7 +119,38 @@
List<CarPropertyConfig> requiredConfigs = mCarPropertyManager.getPropertyList(mPropertyIds);
// Vehicles need to implement all of those properties
assertEquals(mPropertyIds.size(), requiredConfigs.size());
+ }
+ @CddTest(requirement="2.5.1")
+ @Test
+ public void testMustSupportGearSelection() throws Exception {
+ assertTrue("Must support GEAR_SELECTION",
+ mCarPropertyManager.getPropertyList().stream().anyMatch(cfg -> cfg.getPropertyId() ==
+ VehiclePropertyIds.GEAR_SELECTION));
+ }
+
+ @CddTest(requirement="2.5.1")
+ @Test
+ public void testMustSupportNightMode() {
+ assertTrue("Must support NIGHT_MODE",
+ mCarPropertyManager.getPropertyList().stream().anyMatch(cfg -> cfg.getPropertyId() ==
+ VehiclePropertyIds.NIGHT_MODE));
+ }
+
+ @CddTest(requirement="2.5.1")
+ @Test
+ public void testMustSupportPerfVehicleSpeed() throws Exception {
+ assertTrue("Must support PERF_VEHICLE_SPEED",
+ mCarPropertyManager.getPropertyList().stream().anyMatch(cfg -> cfg.getPropertyId() ==
+ VehiclePropertyIds.PERF_VEHICLE_SPEED));
+ }
+
+ @CddTest(requirement = "2.5.1")
+ @Test
+ public void testMustSupportParkingBrakeOn() throws Exception {
+ assertTrue("Must support PARKING_BRAKE_ON",
+ mCarPropertyManager.getPropertyList().stream().anyMatch(cfg -> cfg.getPropertyId() ==
+ VehiclePropertyIds.PARKING_BRAKE_ON));
}
@SuppressWarnings("unchecked")
@@ -297,5 +330,4 @@
return config.getAreaIds();
}
}
-
}
diff --git a/tests/tests/car/src/android/car/cts/CarUxRestrictionsManagerTest.java b/tests/tests/car/src/android/car/cts/CarUxRestrictionsManagerTest.java
index ca7364b..4f80147 100644
--- a/tests/tests/car/src/android/car/cts/CarUxRestrictionsManagerTest.java
+++ b/tests/tests/car/src/android/car/cts/CarUxRestrictionsManagerTest.java
@@ -15,9 +15,13 @@
*/
package android.car.cts;
+
+import static android.car.drivingstate.CarUxRestrictionsManager.UX_RESTRICTION_MODE_BASELINE;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import android.car.Car;
import android.car.drivingstate.CarUxRestrictions;
@@ -27,6 +31,8 @@
import androidx.test.runner.AndroidJUnit4;
+import com.google.common.collect.ImmutableList;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -87,4 +93,37 @@
mManager.registerListener(restrictions -> {});
mManager.unregisterListener();
}
+
+ @Test
+ public void testSetRestrictionMode_missingPermission_throwsException() {
+ try {
+ mManager.setRestrictionMode(UX_RESTRICTION_MODE_BASELINE);
+ fail("Expected SecurityException. App does not have the change UX Restrictions "
+ + "permission.");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testGetRestrictionMode_missingPermission_throwsException() {
+ try {
+ mManager.getRestrictionMode();
+ fail("Expected SecurityException. App does not have the change UX Restrictions "
+ + "permission.");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testSaveUxRestrictionsConfigurationForNextBoot_missingPermission_throwsException() {
+ try {
+ mManager.saveUxRestrictionsConfigurationForNextBoot(ImmutableList.of());
+ fail("Expected SecurityException. App does not have the change UX Restrictions "
+ + "permission.");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
}
diff --git a/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java b/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
index 69b364e..e73c11a 100644
--- a/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
+++ b/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
@@ -481,6 +481,7 @@
mTelephonyManager.getVoiceMailAlphaTag();
mTelephonyManager.getForbiddenPlmns();
mTelephonyManager.getServiceState();
+ mTelephonyManager.setForbiddenPlmns(new ArrayList<String>());
} catch (SecurityException e) {
failMessage();
}
@@ -902,8 +903,6 @@
* This test verifies that {@link TelephonyManager#setVoiceMailNumber(String, String)} correctly
* sets the VoiceMail alpha tag and number when called.
*/
- /* Disabling the test for now due to a bug in the code. Will re-enable it when the bug is
- fixed.
public void testVoiceMailNumber() {
if (!hasCellular) return;
@@ -923,7 +922,7 @@
// Reset original alpha tag and number values.
mTelephonyManager.setVoiceMailNumber(originalAlphaTag, originalNumber);
}
- } */
+ }
/**
* This test verifies that {@link SubscriptionManager#createSubscriptionGroup(List)} correctly
diff --git a/tests/tests/classloaderfactory/test-memcl/OWNERS b/tests/tests/classloaderfactory/test-memcl/OWNERS
index bda93bc..fa9c94d 100644
--- a/tests/tests/classloaderfactory/test-memcl/OWNERS
+++ b/tests/tests/classloaderfactory/test-memcl/OWNERS
@@ -1,2 +1,2 @@
# Bug component: 86431
-dbrazdil@google.com
\ No newline at end of file
+ngeoffray@google.com
\ No newline at end of file
diff --git a/tests/tests/classloaderfactory/test-pathcl/OWNERS b/tests/tests/classloaderfactory/test-pathcl/OWNERS
index bda93bc..fa9c94d 100644
--- a/tests/tests/classloaderfactory/test-pathcl/OWNERS
+++ b/tests/tests/classloaderfactory/test-pathcl/OWNERS
@@ -1,2 +1,2 @@
# Bug component: 86431
-dbrazdil@google.com
\ No newline at end of file
+ngeoffray@google.com
\ No newline at end of file
diff --git a/tests/tests/content/AndroidManifest.xml b/tests/tests/content/AndroidManifest.xml
index 7e5d2e9..66eb8a4 100644
--- a/tests/tests/content/AndroidManifest.xml
+++ b/tests/tests/content/AndroidManifest.xml
@@ -259,6 +259,11 @@
android:process=":testpagingprovider"
android:multiprocess="false" />
+ <provider android:name="android.content.cts.MockBuggyProvider"
+ android:authorities="android.content.cts.mockbuggyprovider"
+ android:process=":mockbuggyprovider"
+ android:multiprocess="false" />
+
<service android:name="android.content.cts.MockService" />
<service android:name="android.content.cts.MockSyncAdapterService" android:exported="true">
diff --git a/tests/tests/content/AndroidTest.xml b/tests/tests/content/AndroidTest.xml
index 418c201..88a1aec 100644
--- a/tests/tests/content/AndroidTest.xml
+++ b/tests/tests/content/AndroidTest.xml
@@ -22,6 +22,8 @@
<!-- The framework has some native code involved. -->
<option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="run-command" value="mkdir -p /data/local/tmp/cts/content" />
<option name="teardown-command" value="rm -rf /data/local/tmp/cts"/>
diff --git a/tests/tests/content/CtsSyncAccountAccessOtherCertTests/OWNERS b/tests/tests/content/CtsSyncAccountAccessOtherCertTests/OWNERS
new file mode 100644
index 0000000..c5fc344
--- /dev/null
+++ b/tests/tests/content/CtsSyncAccountAccessOtherCertTests/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 197138
+include /tests/app/OWNERS
diff --git a/tests/tests/content/res/color-night/testcolor_daynight.xml b/tests/tests/content/res/color-night/testcolor_daynight.xml
new file mode 100644
index 0000000..a63f89a
--- /dev/null
+++ b/tests/tests/content/res/color-night/testcolor_daynight.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/black"/>
+</selector>
\ No newline at end of file
diff --git a/tests/tests/content/res/color-notnight/testcolor_daynight.xml b/tests/tests/content/res/color-notnight/testcolor_daynight.xml
new file mode 100644
index 0000000..2f4ecb8
--- /dev/null
+++ b/tests/tests/content/res/color-notnight/testcolor_daynight.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/white"/>
+</selector>
\ No newline at end of file
diff --git a/tests/tests/content/res/color/testcolor_daynight.xml b/tests/tests/content/res/color/testcolor_daynight.xml
new file mode 100644
index 0000000..0f13433
--- /dev/null
+++ b/tests/tests/content/res/color/testcolor_daynight.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="#777777"/>
+</selector>
\ No newline at end of file
diff --git a/tests/tests/content/src/android/content/cts/BuggyProviderTest.java b/tests/tests/content/src/android/content/cts/BuggyProviderTest.java
new file mode 100644
index 0000000..0c17256
--- /dev/null
+++ b/tests/tests/content/src/android/content/cts/BuggyProviderTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.cts;
+
+import android.app.ActivityManager;
+import android.content.ContentResolver;
+import android.os.UserHandle;
+import android.test.AndroidTestCase;
+
+/**
+ * Test system behavior of a buggy provider.
+ *
+ * see @{@link MockBuggyProvider}
+ */
+public class BuggyProviderTest extends AndroidTestCase {
+
+ public void testGetTypeDoesntCrashSystem() {
+ // ensure the system doesn't crash when a provider takes too long to respond
+ try {
+ ActivityManager.getService().getProviderMimeType(
+ MockBuggyProvider.CONTENT_URI, UserHandle.USER_CURRENT);
+ } catch (Exception e) {
+ fail("Unexpected exception while fetching type: " + e.getMessage());
+ }
+ }
+
+ public void testGetTypeViaResolverDoesntCrashSystem() {
+ // ensure the system doesn't crash when a provider takes too long to respond
+ ContentResolver resolver = mContext.getContentResolver();
+ try {
+ resolver.getType(MockBuggyProvider.CONTENT_URI);
+ } catch (Exception e) {
+ fail("Unexpected exception while fetching type: " + e.getMessage());
+ }
+ }
+}
diff --git a/tests/tests/content/src/android/content/cts/ContentProviderOperationTest.java b/tests/tests/content/src/android/content/cts/ContentProviderOperationTest.java
new file mode 100644
index 0000000..4f11d6d
--- /dev/null
+++ b/tests/tests/content/src/android/content/cts/ContentProviderOperationTest.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.content.cts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.notNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.ContentProvider;
+import android.content.ContentProviderOperation;
+import android.content.ContentProviderResult;
+import android.content.ContentValues;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.os.Bundle;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class ContentProviderOperationTest {
+ private static final Uri TEST_URI = Uri.parse("content://com.example");
+ private static final Uri TEST_URI_RESULT = Uri.parse("content://com.example/12");
+ private static final String TEST_SELECTION = "foo=?";
+ private static final String[] TEST_SELECTION_ARGS = new String[] { "bar" };
+ private static final String TEST_METHOD = "test_method";
+ private static final String TEST_ARG = "test_arg";
+
+ private static final ContentValues TEST_VALUES = new ContentValues();
+ static {
+ TEST_VALUES.put("test_key", "test_value");
+ }
+
+ private static final Bundle TEST_EXTRAS = new Bundle();
+ static {
+ TEST_EXTRAS.putString("test_key", "test_value");
+ }
+
+ private static final Bundle TEST_EXTRAS_RESULT = new Bundle();
+ static {
+ TEST_EXTRAS_RESULT.putString("test_result", "42");
+ }
+
+ private static final ContentProviderResult[] TEST_RESULTS = new ContentProviderResult[] {
+ new ContentProviderResult(TEST_URI_RESULT),
+ new ContentProviderResult(84),
+ new ContentProviderResult(TEST_EXTRAS_RESULT),
+ new ContentProviderResult(new IllegalArgumentException()),
+ };
+
+ private ContentProvider provider;
+
+ private ContentProviderOperation op;
+ private ContentProviderResult res;
+
+ @Before
+ public void setUp() throws Exception {
+ provider = mock(ContentProvider.class);
+ }
+
+ @Test
+ public void testInsert() throws Exception {
+ op = ContentProviderOperation.newInsert(TEST_URI)
+ .withValues(TEST_VALUES)
+ .build();
+
+ assertEquals(TEST_URI, op.getUri());
+ assertTrue(op.isInsert());
+ assertTrue(op.isWriteOperation());
+
+ when(provider.insert(eq(TEST_URI), eq(TEST_VALUES)))
+ .thenReturn(TEST_URI_RESULT);
+ res = op.apply(provider, null, 0);
+ assertEquals(TEST_URI_RESULT, res.uri);
+ }
+
+ @Test
+ public void testUpdate() throws Exception {
+ op = ContentProviderOperation.newUpdate(TEST_URI)
+ .withSelection(TEST_SELECTION, TEST_SELECTION_ARGS)
+ .withValues(TEST_VALUES)
+ .build();
+
+ assertEquals(TEST_URI, op.getUri());
+ assertTrue(op.isUpdate());
+ assertTrue(op.isWriteOperation());
+
+ when(provider.update(eq(TEST_URI), eq(TEST_VALUES),
+ eq(TEST_SELECTION), eq(TEST_SELECTION_ARGS)))
+ .thenReturn(1);
+ res = op.apply(provider, null, 0);
+ assertEquals(1, (int) res.count);
+ }
+
+ @Test
+ public void testDelete() throws Exception {
+ op = ContentProviderOperation.newDelete(TEST_URI)
+ .withSelection(TEST_SELECTION, TEST_SELECTION_ARGS)
+ .build();
+
+ assertEquals(TEST_URI, op.getUri());
+ assertTrue(op.isDelete());
+ assertTrue(op.isWriteOperation());
+
+ when(provider.delete(eq(TEST_URI),
+ eq(TEST_SELECTION), eq(TEST_SELECTION_ARGS)))
+ .thenReturn(1);
+ res = op.apply(provider, null, 0);
+ assertEquals(1, (int) res.count);
+ }
+
+ @Test
+ public void testAssertQuery() throws Exception {
+ op = ContentProviderOperation.newAssertQuery(TEST_URI)
+ .withSelection(TEST_SELECTION, TEST_SELECTION_ARGS)
+ .withValues(TEST_VALUES)
+ .build();
+
+ assertEquals(TEST_URI, op.getUri());
+ assertTrue(op.isAssertQuery());
+ assertTrue(op.isReadOperation());
+
+ final MatrixCursor cursor = new MatrixCursor(new String[] { "test_key" });
+ cursor.addRow(new Object[] { "test_value" });
+
+ when(provider.query(eq(TEST_URI), eq(new String[] { "test_key" }),
+ eq(TEST_SELECTION), eq(TEST_SELECTION_ARGS), eq(null)))
+ .thenReturn(cursor);
+ op.apply(provider, null, 0);
+ }
+
+ @Test
+ public void testCall() throws Exception {
+ op = ContentProviderOperation.newCall(TEST_URI, TEST_METHOD, TEST_ARG)
+ .withExtras(TEST_EXTRAS)
+ .build();
+
+ assertEquals(TEST_URI, op.getUri());
+ assertTrue(op.isCall());
+
+ when(provider.call(eq(TEST_URI.getAuthority()), eq(TEST_METHOD),
+ eq(TEST_ARG), notNull()))
+ .thenReturn(TEST_EXTRAS_RESULT);
+ res = op.apply(provider, null, 0);
+ assertEquals(TEST_EXTRAS_RESULT, res.extras);
+ }
+
+ @Test
+ public void testBackReferenceSelection() throws Exception {
+ op = ContentProviderOperation.newDelete(TEST_URI)
+ .withSelection(null, new String[] { "a", "b", "c", "d" })
+ .withSelectionBackReference(0, 0)
+ .withSelectionBackReference(1, 1)
+ .withSelectionBackReference(2, 2, "test_result")
+ .build();
+
+ final String[] res = op.resolveSelectionArgsBackReferences(TEST_RESULTS,
+ TEST_RESULTS.length);
+ assertEquals("12", res[0]);
+ assertEquals("84", res[1]);
+ assertEquals("42", res[2]);
+ assertEquals("d", res[3]);
+ }
+
+ @Test
+ public void testBackReferenceValue() throws Exception {
+ final ContentValues values = new ContentValues();
+ values.put("a", "a");
+ values.put("b", "b");
+ values.put("c", "c");
+ values.put("d", "d");
+
+ op = ContentProviderOperation.newUpdate(TEST_URI)
+ .withValues(values)
+ .withValueBackReference("a", 0)
+ .withValueBackReference("b", 1)
+ .withValueBackReference("c", 2, "test_result")
+ .build();
+
+ final ContentValues res = op.resolveValueBackReferences(TEST_RESULTS,
+ TEST_RESULTS.length);
+ assertEquals(12L, (long) res.get("a"));
+ assertEquals(84L, (long) res.get("b"));
+ assertEquals("42", res.get("c"));
+ assertEquals("d", res.get("d"));
+ }
+
+ @Test
+ public void testBackReferenceExtra() throws Exception {
+ final Bundle extras = new Bundle();
+ extras.putString("a", "a");
+ extras.putString("b", "b");
+ extras.putString("c", "c");
+ extras.putString("d", "d");
+
+ op = ContentProviderOperation.newCall(TEST_URI, TEST_METHOD, TEST_ARG)
+ .withExtras(extras)
+ .withExtraBackReference("a", 0)
+ .withExtraBackReference("b", 1)
+ .withExtraBackReference("c", 2, "test_result")
+ .build();
+
+ final Bundle res = op.resolveExtrasBackReferences(TEST_RESULTS,
+ TEST_RESULTS.length);
+ assertEquals(12L, (long) res.get("a"));
+ assertEquals(84L, (long) res.get("b"));
+ assertEquals("42", res.get("c"));
+ assertEquals("d", res.get("d"));
+ }
+
+ @Test
+ public void testExceptionAllowed() throws Exception {
+ op = ContentProviderOperation.newCall(TEST_URI, TEST_METHOD, TEST_ARG)
+ .withExtras(TEST_EXTRAS)
+ .withExceptionAllowed(true)
+ .build();
+
+ assertTrue(op.isExceptionAllowed());
+
+ when(provider.call(eq(TEST_URI.getAuthority()), eq(TEST_METHOD),
+ eq(TEST_ARG), notNull()))
+ .thenThrow(new IllegalArgumentException());
+ res = op.apply(provider, null, 0);
+ assertTrue((res.exception instanceof IllegalArgumentException));
+ }
+}
diff --git a/tests/tests/content/src/android/content/cts/ContentProviderResultTest.java b/tests/tests/content/src/android/content/cts/ContentProviderResultTest.java
new file mode 100644
index 0000000..7e98296
--- /dev/null
+++ b/tests/tests/content/src/android/content/cts/ContentProviderResultTest.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.content.cts;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.ContentProviderResult;
+import android.net.Uri;
+import android.os.Bundle;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class ContentProviderResultTest {
+ private final Uri TEST_URI = Uri.EMPTY;
+ private final Bundle TEST_BUNDLE = Bundle.EMPTY;
+ private final Exception TEST_EXCEPTION = new IllegalArgumentException();
+
+ @Test
+ public void testUri() throws Exception {
+ assertEquals(TEST_URI, new ContentProviderResult(TEST_URI).uri);
+ }
+
+ @Test
+ public void testCount() throws Exception {
+ assertEquals(42, (int) new ContentProviderResult(42).count);
+ }
+
+ @Test
+ public void testExtras() throws Exception {
+ assertEquals(TEST_BUNDLE, new ContentProviderResult(TEST_BUNDLE).extras);
+ }
+
+ @Test
+ public void testException() throws Exception {
+ assertEquals(TEST_EXCEPTION, new ContentProviderResult(TEST_EXCEPTION).exception);
+ }
+}
diff --git a/tests/tests/content/src/android/content/cts/ContentResolverTest.java b/tests/tests/content/src/android/content/cts/ContentResolverTest.java
index d06514f..a3f03b0 100644
--- a/tests/tests/content/src/android/content/cts/ContentResolverTest.java
+++ b/tests/tests/content/src/android/content/cts/ContentResolverTest.java
@@ -25,6 +25,8 @@
import android.content.res.AssetFileDescriptor;
import android.database.ContentObserver;
import android.database.Cursor;
+import android.icu.text.Collator;
+import android.icu.util.ULocale;
import android.net.Uri;
import android.os.Bundle;
import android.os.CancellationSignal;
@@ -46,6 +48,9 @@
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -427,6 +432,47 @@
mCursor.close();
}
+ public void testQuery_SqlSortingFromBundleArgs_Locale() {
+ mContentResolver.delete(TABLE1_URI, null, null);
+
+ final List<String> data = Arrays.asList(
+ "ABC", "abc", "pinyin", "가나다", "바사", "테스트", "马",
+ "嘛", "妈", "骂", "吗", "码", "玛", "麻", "中", "梵", "苹果", "久了", "伺候");
+
+ for (String s : data) {
+ final ContentValues values = new ContentValues();
+ values.put(COLUMN_KEY_NAME, s.hashCode());
+ values.put(COLUMN_VALUE_NAME, s);
+ mContentResolver.insert(TABLE1_URI, values);
+ }
+
+ String[] sortCols = new String[] { COLUMN_VALUE_NAME };
+ Bundle queryArgs = new Bundle();
+ queryArgs.putStringArray(ContentResolver.QUERY_ARG_SORT_COLUMNS, sortCols);
+
+ for (String locale : new String[] {
+ "zh",
+ "zh@collation=pinyin",
+ "zh@collation=stroke",
+ "zh@collation=zhuyin",
+ }) {
+ // Assert that sorting is identical between SQLite and ICU4J
+ queryArgs.putString(ContentResolver.QUERY_ARG_SORT_LOCALE, locale);
+ try (Cursor c = mContentResolver.query(TABLE1_URI, sortCols, queryArgs, null)) {
+ data.sort(Collator.getInstance(new ULocale(locale)));
+ assertEquals(data, collect(c));
+ }
+ }
+ }
+
+ private static List<String> collect(Cursor c) {
+ List<String> res = new ArrayList<>();
+ while (c.moveToNext()) {
+ res.add(c.getString(0));
+ }
+ return res;
+ }
+
/**
* Verifies that paging information is correctly relayed, and that
* honored arguments from a supporting client are returned correctly.
diff --git a/tests/tests/content/src/android/content/cts/ContentValuesTest.java b/tests/tests/content/src/android/content/cts/ContentValuesTest.java
index ea5577d..e6bc2d6 100644
--- a/tests/tests/content/src/android/content/cts/ContentValuesTest.java
+++ b/tests/tests/content/src/android/content/cts/ContentValuesTest.java
@@ -551,4 +551,14 @@
// expected, test success.
}
}
+
+ @Test
+ public void testIsEmpty() {
+ final ContentValues values = new ContentValues();
+ assertTrue(values.isEmpty());
+ values.put("k", "v");
+ assertFalse(values.isEmpty());
+ values.clear();
+ assertTrue(values.isEmpty());
+ }
}
diff --git a/tests/tests/content/src/android/content/cts/MockBuggyProvider.java b/tests/tests/content/src/android/content/cts/MockBuggyProvider.java
new file mode 100644
index 0000000..47d83ee
--- /dev/null
+++ b/tests/tests/content/src/android/content/cts/MockBuggyProvider.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.cts;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Mocks a buggy provider which stalls on the {@link #getType(Uri)} call.
+ *
+ * see {@link BuggyProviderTest}
+ */
+public class MockBuggyProvider extends ContentProvider {
+ public static final String AUTHORITY = "android.content.cts.mockbuggyprovider";
+ public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY);
+
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ return null;
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ try {
+ TimeUnit.SECONDS.sleep(10); // stall for enough time such that an ANR is thrown
+ } catch (Exception ignore) { }
+ return "buggy";
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ return null;
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ return 0;
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ return 0;
+ }
+}
diff --git a/tests/tests/content/src/android/content/cts/MockContentProvider.java b/tests/tests/content/src/android/content/cts/MockContentProvider.java
index d2b613c..ee6d0be 100644
--- a/tests/tests/content/src/android/content/cts/MockContentProvider.java
+++ b/tests/tests/content/src/android/content/cts/MockContentProvider.java
@@ -22,6 +22,7 @@
import android.annotation.Nullable;
import android.content.ContentProvider;
import android.content.ContentProvider.PipeDataWriter;
+import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
@@ -253,6 +254,33 @@
}
@Override
+ public @Nullable Cursor query(@NonNull Uri uri, @Nullable String[] projection,
+ @Nullable Bundle queryArgs, @Nullable CancellationSignal cancellationSignal) {
+ if (queryArgs != null && queryArgs.containsKey(ContentResolver.QUERY_ARG_SORT_LOCALE)) {
+ final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+ final String locale = queryArgs.getString(ContentResolver.QUERY_ARG_SORT_LOCALE);
+ final String safeLocale = locale.replaceAll("[^a-zA-Z]", "");
+ try (Cursor c = db.rawQuery("SELECT icu_load_collation(?, ?);",
+ new String[] { locale, safeLocale }, cancellationSignal)) {
+ while (c.moveToNext()) {
+ }
+ }
+
+ final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+ qb.setTables("TestTable1");
+ qb.setProjectionMap(CTSDBTABLE1_LIST_PROJECTION_MAP);
+
+ final String sortOrder = TextUtils.join(", ",
+ queryArgs.getStringArray(ContentResolver.QUERY_ARG_SORT_COLUMNS));
+ return qb.query(db, projection, null, null, null, null,
+ sortOrder + " COLLATE " + safeLocale,
+ null, cancellationSignal);
+ } else {
+ return super.query(uri, projection, queryArgs, cancellationSignal);
+ }
+ }
+
+ @Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
return query(uri, projection, selection, selectionArgs, sortOrder, null);
diff --git a/tests/tests/content/src/android/content/cts/SharedPreferencesTest.java b/tests/tests/content/src/android/content/cts/SharedPreferencesTest.java
index 24b56c9..67ec257 100644
--- a/tests/tests/content/src/android/content/cts/SharedPreferencesTest.java
+++ b/tests/tests/content/src/android/content/cts/SharedPreferencesTest.java
@@ -47,8 +47,6 @@
private File mPrefsFile;
- private static volatile CountDownLatch sSharedPrefsListenerLatch;
-
@Override
protected void setUp() throws Exception {
super.setUp();
@@ -356,35 +354,88 @@
public void testSharedPrefsChangeListenerIsCalledOnCommit() throws InterruptedException {
// Setup on change listener
+ final CountDownLatch latch = new CountDownLatch(2);
+ final SharedPreferences.OnSharedPreferenceChangeListener listener =
+ (sharedPreferences, key) -> latch.countDown();
final SharedPreferences prefs = getPrefs();
- prefs.registerOnSharedPreferenceChangeListener(
- (sharedPreferences, key) -> sSharedPrefsListenerLatch.countDown());
- // Verify listener is called for #putString
- sSharedPrefsListenerLatch = new CountDownLatch(1);
- prefs.edit().putString("test-key", "test-value").commit();
- assertTrue(sSharedPrefsListenerLatch.await(1, TimeUnit.SECONDS));
+ try {
+ prefs.registerOnSharedPreferenceChangeListener(listener);
+ prefs.edit().putString("test-key", "test-value").commit(); // latch--
+ prefs.edit().remove("test-key").commit(); // latch--
- // Verify listener is called for #remove
- sSharedPrefsListenerLatch = new CountDownLatch(1);
- prefs.edit().remove("test-key").commit();
- assertTrue(sSharedPrefsListenerLatch.await(1, TimeUnit.SECONDS));
+ assertTrue("OnSharedPreferenceChangeListener was not fired on #commit",
+ latch.await(10, TimeUnit.SECONDS));
+ } finally {
+ prefs.unregisterOnSharedPreferenceChangeListener(listener);
+ }
}
public void testSharedPrefsChangeListenerIsCalledOnApply() throws InterruptedException {
// Setup on change listener
+ final CountDownLatch latch = new CountDownLatch(2);
+ final SharedPreferences.OnSharedPreferenceChangeListener listener =
+ (sharedPreferences, key) -> latch.countDown();
final SharedPreferences prefs = getPrefs();
- prefs.registerOnSharedPreferenceChangeListener(
- (sharedPreferences, key) -> sSharedPrefsListenerLatch.countDown());
- // Verify listener is called for #putString
- sSharedPrefsListenerLatch = new CountDownLatch(1);
- prefs.edit().putString("test-key", "test-value").apply();
- assertTrue(sSharedPrefsListenerLatch.await(1, TimeUnit.SECONDS));
+ try {
+ prefs.registerOnSharedPreferenceChangeListener(listener);
+ prefs.edit().putString("test-key", "test-value").apply(); // latch--
+ prefs.edit().remove("test-key").apply(); // latch--
- // Verify listener is called for #remove
- sSharedPrefsListenerLatch = new CountDownLatch(1);
- prefs.edit().remove("test-key").apply();
- assertTrue(sSharedPrefsListenerLatch.await(1, TimeUnit.SECONDS));
+ assertTrue("OnSharedPreferenceChangeListener was not fired on #apply",
+ latch.await(10, TimeUnit.SECONDS));
+ } finally {
+ prefs.unregisterOnSharedPreferenceChangeListener(listener);
+ }
+ }
+
+ public void testSharedPrefsChangeListenerIsCalledForClearOnCommit()
+ throws InterruptedException {
+ // Setup on change listener
+ final CountDownLatch latch = new CountDownLatch(1);
+ final SharedPreferences.OnSharedPreferenceChangeListener listener =
+ (sharedPreferences, key) -> {
+ if (key == null) {
+ latch.countDown();
+ }
+ };
+ final SharedPreferences prefs = getPrefs();
+
+ try {
+ prefs.registerOnSharedPreferenceChangeListener(listener);
+ prefs.edit().putString("test-key", "test-value").commit();
+ assertEquals("test-value", prefs.getString("test-key", null));
+ prefs.edit().clear().commit(); // latch--
+
+ assertTrue("OnSharedPreferenceChangeListener was not fired for clear() on #commit",
+ latch.await(10, TimeUnit.SECONDS));
+ } finally {
+ prefs.unregisterOnSharedPreferenceChangeListener(listener);
+ }
+ }
+
+ public void testSharedPrefsChangeListenerIsCalledForClearOnApply() throws InterruptedException {
+ // Setup on change listener
+ final CountDownLatch latch = new CountDownLatch(1);
+ final SharedPreferences.OnSharedPreferenceChangeListener listener =
+ (sharedPreferences, key) -> {
+ if (key == null) {
+ latch.countDown();
+ }
+ };
+ final SharedPreferences prefs = getPrefs();
+
+ try {
+ prefs.registerOnSharedPreferenceChangeListener(listener);
+ prefs.edit().putString("test-key", "test-value").commit();
+ assertEquals("test-value", prefs.getString("test-key", null));
+ prefs.edit().clear().apply(); // latch--
+
+ assertTrue("OnSharedPreferenceChangeListener was not fired for clear() on #apply",
+ latch.await(10, TimeUnit.SECONDS));
+ } finally {
+ prefs.unregisterOnSharedPreferenceChangeListener(listener);
+ }
}
}
diff --git a/tests/tests/content/src/android/content/cts/SyncStorageEngineTest.java b/tests/tests/content/src/android/content/cts/SyncStorageEngineTest.java
index 457c1b6..47bc102 100644
--- a/tests/tests/content/src/android/content/cts/SyncStorageEngineTest.java
+++ b/tests/tests/content/src/android/content/cts/SyncStorageEngineTest.java
@@ -17,30 +17,16 @@
package android.content.cts;
import android.accounts.Account;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.Intent;
-import android.content.PeriodicSync;
-import android.content.res.Resources;
import android.content.SyncStatusInfo;
-import android.os.Bundle;
import android.os.Looper;
import android.platform.test.annotations.AppModeFull;
import android.test.AndroidTestCase;
-import android.test.RenamingDelegatingContext;
-import android.test.mock.MockContentResolver;
-import android.test.mock.MockContext;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.util.AtomicFile;
+
import com.android.server.content.SyncStorageEngine;
-import com.android.internal.os.AtomicFile;
import java.io.File;
import java.io.FileOutputStream;
-import java.util.List;
@AppModeFull(reason = "Sync manager not supported")
public class SyncStorageEngineTest extends AndroidTestCase {
diff --git a/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java b/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java
index 2c4d0067..d0e24c6 100644
--- a/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java
+++ b/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java
@@ -871,7 +871,7 @@
return;
}
PackageInfo packageInfo = mPackageManager.getPackageInfo(SHIM_APEX_PACKAGE_NAME,
- PackageManager.MATCH_APEX);
+ PackageManager.MATCH_APEX | PackageManager.MATCH_FACTORY_ONLY);
assertShimApexInfoIsCorrect(packageInfo);
}
@@ -924,7 +924,7 @@
return;
}
List<PackageInfo> installedPackages = mPackageManager.getInstalledPackages(
- PackageManager.MATCH_APEX);
+ PackageManager.MATCH_APEX | PackageManager.MATCH_FACTORY_ONLY);
List<PackageInfo> shimApex = installedPackages.stream().filter(
packageInfo -> packageInfo.packageName.equals(SHIM_APEX_PACKAGE_NAME)).collect(
Collectors.toList());
diff --git a/tests/tests/content/src/android/content/res/cts/ResourcesTest.java b/tests/tests/content/src/android/content/res/cts/ResourcesTest.java
index 7d0a276..81116e5 100644
--- a/tests/tests/content/src/android/content/res/cts/ResourcesTest.java
+++ b/tests/tests/content/src/android/content/res/cts/ResourcesTest.java
@@ -35,7 +35,6 @@
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.LocaleList;
-import android.platform.test.annotations.AppModeFull;
import android.test.AndroidTestCase;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
@@ -46,8 +45,6 @@
import android.view.View;
import android.view.WindowManager;
-import androidx.test.InstrumentationRegistry;
-
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -338,6 +335,60 @@
assertNull(mResources.getDrawable(R.drawable.fake_image_will_not_decode));
}
+ public void testGetDrawable_ColorResource() {
+ final Drawable drawable = mResources.getDrawable(R.color.testcolor1, null);
+ assertTrue(drawable instanceof ColorDrawable);
+ assertEquals(
+ mResources.getColor(R.color.testcolor1, null),
+ ((ColorDrawable) drawable).getColor()
+ );
+ }
+
+ public void testGetDrawable_ColorStateListResource() {
+ final Drawable drawable = mResources.getDrawable(R.color.testcolor, null);
+ assertTrue(drawable instanceof ColorStateListDrawable);
+
+ final ColorStateList colorStateList = mResources.getColorStateList(
+ R.color.testcolor, null);
+ assertEquals(
+ colorStateList.getDefaultColor(),
+ ((ColorStateListDrawable) drawable).getColorStateList().getDefaultColor());
+ }
+
+ public void testGetDrawable_ColorStateListConfigurations() {
+ final Configuration dayConfiguration = new Configuration(mResources.getConfiguration());
+ final Configuration nightConfiguration = new Configuration(mResources.getConfiguration());
+
+ dayConfiguration.uiMode = dayConfiguration.uiMode
+ & (~Configuration.UI_MODE_NIGHT_MASK)
+ | Configuration.UI_MODE_NIGHT_NO;
+
+ nightConfiguration.uiMode = nightConfiguration.uiMode
+ & (~Configuration.UI_MODE_NIGHT_MASK)
+ | Configuration.UI_MODE_NIGHT_YES;
+
+ final ColorStateListDrawable dayDrawable = (ColorStateListDrawable) getContext()
+ .createConfigurationContext(dayConfiguration)
+ .getResources()
+ .getDrawable(R.color.testcolor_daynight, null);
+
+ final ColorStateListDrawable nightDrawable = (ColorStateListDrawable) getContext()
+ .createConfigurationContext(nightConfiguration)
+ .getResources()
+ .getDrawable(R.color.testcolor_daynight, null);
+
+ assertEquals(
+ mResources.getColor(android.R.color.white, null),
+ dayDrawable.getColorStateList().getDefaultColor());
+
+ assertEquals(
+ mResources.getColor(android.R.color.black, null),
+ nightDrawable.getColorStateList().getDefaultColor());
+
+ assertEquals(ActivityInfo.CONFIG_UI_MODE, dayDrawable.getChangingConfigurations());
+ assertEquals(ActivityInfo.CONFIG_UI_MODE, nightDrawable.getChangingConfigurations());
+ }
+
public void testGetDrawable_StackOverflowErrorDrawable() {
try {
mResources.getDrawable(R.drawable.drawable_recursive);
@@ -958,14 +1009,12 @@
mResources.getFont(R.font.sample_bolditalic_family).getStyle());
}
- // TODO Figure out why it fails in the instant mode.
- @AppModeFull
- public void testComplextColorDrawableAttrInflation() {
- Context context = InstrumentationRegistry.getTargetContext();
- LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(
+ public void testComplexColorDrawableAttributeInflation() {
+ final LayoutInflater layoutInflater = (LayoutInflater) getContext().getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
- View view = layoutInflater.inflate(R.layout.complex_color_drawable_attr_layout, null);
+ final View view = layoutInflater.inflate(
+ R.layout.complex_color_drawable_attr_layout, null);
assertTrue(view.getBackground() instanceof ColorStateListDrawable);
}
diff --git a/tests/tests/database/src/android/database/sqlite/cts/SQLiteQueryBuilderTest.java b/tests/tests/database/src/android/database/sqlite/cts/SQLiteQueryBuilderTest.java
index 2475af8..f197968 100644
--- a/tests/tests/database/src/android/database/sqlite/cts/SQLiteQueryBuilderTest.java
+++ b/tests/tests/database/src/android/database/sqlite/cts/SQLiteQueryBuilderTest.java
@@ -40,6 +40,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
@@ -48,6 +49,8 @@
@RunWith(AndroidJUnit4.class)
public class SQLiteQueryBuilderTest {
private SQLiteDatabase mDatabase;
+ private SQLiteQueryBuilder mStrictBuilder;
+
private final String TEST_TABLE_NAME = "test";
private final String EMPLOYEE_TABLE_NAME = "employee";
private static final String DATABASE_FILE = "database_test.db";
@@ -59,6 +62,9 @@
context.deleteDatabase(DATABASE_FILE);
mDatabase = Objects.requireNonNull(
context.openOrCreateDatabase(DATABASE_FILE, Context.MODE_PRIVATE, null));
+
+ createEmployeeTable();
+ createStrictQueryBuilder();
}
@After
@@ -238,8 +244,6 @@
@Test
public void testQuery() {
- createEmployeeTable();
-
SQLiteQueryBuilder sqliteQueryBuilder = new SQLiteQueryBuilder();
sqliteQueryBuilder.setTables("Employee");
Cursor cursor = sqliteQueryBuilder.query(mDatabase,
@@ -314,8 +318,6 @@
@Test
public void testCancelableQuery_WhenNotCanceled_ReturnsResultSet() {
- createEmployeeTable();
-
CancellationSignal cancellationSignal = new CancellationSignal();
SQLiteQueryBuilder sqliteQueryBuilder = new SQLiteQueryBuilder();
sqliteQueryBuilder.setTables("Employee");
@@ -328,8 +330,6 @@
@Test
public void testCancelableQuery_WhenCanceledBeforeQuery_ThrowsImmediately() {
- createEmployeeTable();
-
CancellationSignal cancellationSignal = new CancellationSignal();
SQLiteQueryBuilder sqliteQueryBuilder = new SQLiteQueryBuilder();
sqliteQueryBuilder.setTables("Employee");
@@ -347,8 +347,6 @@
@Test
public void testCancelableQuery_WhenCanceledAfterQuery_ThrowsWhenExecuted() {
- createEmployeeTable();
-
CancellationSignal cancellationSignal = new CancellationSignal();
SQLiteQueryBuilder sqliteQueryBuilder = new SQLiteQueryBuilder();
sqliteQueryBuilder.setTables("Employee");
@@ -368,8 +366,6 @@
@Test
public void testCancelableQuery_WhenCanceledDueToContention_StopsWaitingAndThrows() {
- createEmployeeTable();
-
for (int i = 0; i < 5; i++) {
final CancellationSignal cancellationSignal = new CancellationSignal();
final Semaphore barrier1 = new Semaphore(0);
@@ -504,8 +500,6 @@
@Test
public void testUpdate() throws Exception {
- createEmployeeTable();
-
final ContentValues values = new ContentValues();
values.put("name", "Anonymous");
values.put("salary", 0);
@@ -525,8 +519,6 @@
@Test
public void testDelete() throws Exception {
- createEmployeeTable();
-
{
final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
qb.setTables("employee");
@@ -544,12 +536,7 @@
@Test
public void testStrictQuery() throws Exception {
- createEmployeeTable();
-
- final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
- qb.setTables("employee");
- qb.setStrict(true);
- qb.appendWhere("month=2");
+ final SQLiteQueryBuilder qb = mStrictBuilder;
// Should normally only be able to see one row
try (Cursor c = qb.query(mDatabase, null, null, null, null, null, null)) {
@@ -578,16 +565,10 @@
@Test
public void testStrictUpdate() throws Exception {
- createEmployeeTable();
+ final SQLiteQueryBuilder qb = mStrictBuilder;
final ContentValues values = new ContentValues();
values.put("name", "Anonymous");
- values.put("salary", 0);
-
- final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
- qb.setTables("employee");
- qb.setStrict(true);
- qb.appendWhere("month=2");
// Should normally only be able to update one row
assertEquals(1, qb.update(mDatabase, values, null, null));
@@ -614,10 +595,7 @@
@Test
public void testStrictDelete() throws Exception {
- final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
- qb.setTables("employee");
- qb.setStrict(true);
- qb.appendWhere("month=2");
+ final SQLiteQueryBuilder qb = mStrictBuilder;
// Should normally only be able to update one row
createEmployeeTable();
@@ -647,6 +625,186 @@
}
}
+ private static final String[] COLUMNS_VALID = new String[] {
+ "_id",
+ };
+
+ private static final String[] COLUMNS_INVALID = new String[] {
+ "salary",
+ "MAX(salary)",
+ "undefined",
+ "(secret_column IN secret_table)",
+ "(SELECT secret_column FROM secret_table)",
+ };
+
+ @Test
+ public void testStrictQueryProjection() throws Exception {
+ for (String column : COLUMNS_VALID) {
+ assertStrictQueryValid(
+ new String[] { column }, null, null, null, null, null, null);
+ }
+ for (String column : COLUMNS_INVALID) {
+ assertStrictQueryInvalid(
+ new String[] { column }, null, null, null, null, null, null);
+ }
+ }
+
+ @Test
+ public void testStrictQueryWhere() throws Exception {
+ for (String column : COLUMNS_VALID) {
+ assertStrictQueryValid(
+ null, column + ">0", null, null, null, null, null);
+ assertStrictQueryValid(
+ null, "_id>" + column, null, null, null, null, null);
+ }
+ for (String column : COLUMNS_INVALID) {
+ assertStrictQueryInvalid(
+ null, column + ">0", null, null, null, null, null);
+ assertStrictQueryInvalid(
+ null, "_id>" + column, null, null, null, null, null);
+ }
+ }
+
+ @Test
+ public void testStrictQueryGroupBy() {
+ for (String column : COLUMNS_VALID) {
+ assertStrictQueryValid(
+ null, null, null, column, null, null, null);
+ assertStrictQueryValid(
+ null, null, null, "_id," + column, null, null, null);
+ }
+ for (String column : COLUMNS_INVALID) {
+ assertStrictQueryInvalid(
+ null, null, null, column, null, null, null);
+ assertStrictQueryInvalid(
+ null, null, null, "_id," + column, null, null, null);
+ }
+ }
+
+ @Test
+ public void testStrictQueryHaving() {
+ for (String column : COLUMNS_VALID) {
+ assertStrictQueryValid(
+ null, null, null, "_id", column, null, null);
+ }
+ for (String column : COLUMNS_INVALID) {
+ assertStrictQueryInvalid(
+ null, null, null, "_id", column, null, null);
+ }
+ }
+
+ @Test
+ public void testStrictQueryOrderBy() {
+ for (String column : COLUMNS_VALID) {
+ assertStrictQueryValid(
+ null, null, null, null, null, column, null);
+ assertStrictQueryValid(
+ null, null, null, null, null, column + " ASC", null);
+ assertStrictQueryValid(
+ null, null, null, null, null, "_id COLLATE NOCASE ASC," + column, null);
+ }
+ for (String column : COLUMNS_INVALID) {
+ assertStrictQueryInvalid(
+ null, null, null, null, null, column, null);
+ assertStrictQueryInvalid(
+ null, null, null, null, null, column + " ASC", null);
+ assertStrictQueryInvalid(
+ null, null, null, null, null, "_id COLLATE NOCASE ASC," + column, null);
+ }
+ }
+
+ @Test
+ public void testStrictQueryLimit() {
+ assertStrictQueryValid(
+ null, null, null, null, null, null, "32");
+ assertStrictQueryValid(
+ null, null, null, null, null, null, "0,32");
+ assertStrictQueryValid(
+ null, null, null, null, null, null, "32 OFFSET 0");
+
+ for (String column : COLUMNS_VALID) {
+ assertStrictQueryInvalid(
+ null, null, null, null, null, null, column);
+ }
+ for (String column : COLUMNS_INVALID) {
+ assertStrictQueryInvalid(
+ null, null, null, null, null, null, column);
+ }
+ }
+
+ @Test
+ public void testStrictInsertValues() throws Exception {
+ final ContentValues values = new ContentValues();
+ for (String column : COLUMNS_VALID) {
+ values.clear();
+ values.put(column, 42);
+ assertStrictInsertValid(values);
+ }
+ for (String column : COLUMNS_INVALID) {
+ values.clear();
+ values.put(column, 42);
+ assertStrictInsertInvalid(values);
+ }
+ }
+
+ @Test
+ public void testStrictUpdateValues() throws Exception {
+ final ContentValues values = new ContentValues();
+ for (String column : COLUMNS_VALID) {
+ values.clear();
+ values.put(column, 42);
+ assertStrictUpdateValid(values, null, null);
+ }
+ for (String column : COLUMNS_INVALID) {
+ values.clear();
+ values.put(column, 42);
+ assertStrictUpdateInvalid(values, null, null);
+ }
+ }
+
+ private void assertStrictInsertValid(ContentValues values) {
+ mStrictBuilder.insert(mDatabase, values);
+ }
+
+ private void assertStrictInsertInvalid(ContentValues values) {
+ try {
+ mStrictBuilder.insert(mDatabase, values);
+ fail(Arrays.asList(values).toString());
+ } catch (Exception expected) {
+ }
+ }
+
+ private void assertStrictUpdateValid(ContentValues values, String selection,
+ String[] selectionArgs) {
+ mStrictBuilder.update(mDatabase, values, selection, selectionArgs);
+ }
+
+ private void assertStrictUpdateInvalid(ContentValues values, String selection,
+ String[] selectionArgs) {
+ try {
+ mStrictBuilder.update(mDatabase, values, selection, selectionArgs);
+ fail(Arrays.asList(values, selection, selectionArgs).toString());
+ } catch (Exception expected) {
+ }
+ }
+
+ private void assertStrictQueryValid(String[] projectionIn, String selection,
+ String[] selectionArgs, String groupBy, String having, String sortOrder, String limit) {
+ try (Cursor c = mStrictBuilder.query(mDatabase, projectionIn, selection, selectionArgs,
+ groupBy, having, sortOrder, limit, null)) {
+ }
+ }
+
+ private void assertStrictQueryInvalid(String[] projectionIn, String selection,
+ String[] selectionArgs, String groupBy, String having, String sortOrder, String limit) {
+ try (Cursor c = mStrictBuilder.query(mDatabase, projectionIn, selection, selectionArgs,
+ groupBy, having, sortOrder, limit, null)) {
+ fail(Arrays.asList(projectionIn, selection, selectionArgs,
+ groupBy, having, sortOrder, limit).toString());
+ } catch (Exception expected) {
+ }
+ }
+
private void createEmployeeTable() {
mDatabase.execSQL("DROP TABLE IF EXISTS employee;");
mDatabase.execSQL("CREATE TABLE employee (_id INTEGER PRIMARY KEY, " +
@@ -664,4 +822,19 @@
mDatabase.execSQL("INSERT INTO employee (name, month, salary) " +
"VALUES ('Jim', '3', '3500');");
}
+
+ private void createStrictQueryBuilder() {
+ mStrictBuilder = new SQLiteQueryBuilder();
+ mStrictBuilder.setTables("employee");
+ mStrictBuilder.setStrict(true);
+ mStrictBuilder.setStrictColumns(true);
+ mStrictBuilder.setStrictGrammar(true);
+ mStrictBuilder.appendWhere("month=2");
+
+ final Map<String, String> projectionMap = new HashMap<>();
+ projectionMap.put("_id", "_id");
+ projectionMap.put("name", "name");
+ projectionMap.put("month", "month");
+ mStrictBuilder.setProjectionMap(projectionMap);
+ }
}
diff --git a/tests/tests/dpi/OWNERS b/tests/tests/dpi/OWNERS
new file mode 100644
index 0000000..6d63a63
--- /dev/null
+++ b/tests/tests/dpi/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 25700
+adamp@google.com
diff --git a/tests/tests/externalservice/OWNERS b/tests/tests/externalservice/OWNERS
new file mode 100644
index 0000000..210a568
--- /dev/null
+++ b/tests/tests/externalservice/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 76427
+maco@google.com
+narayan@google.com
diff --git a/tests/tests/gesture/OWNERS b/tests/tests/gesture/OWNERS
new file mode 100644
index 0000000..6d63a63
--- /dev/null
+++ b/tests/tests/gesture/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 25700
+adamp@google.com
diff --git a/tests/tests/graphics/Android.bp b/tests/tests/graphics/Android.bp
index beed115..9ed18c2 100644
--- a/tests/tests/graphics/Android.bp
+++ b/tests/tests/graphics/Android.bp
@@ -28,6 +28,7 @@
"ctstestrunner-axt",
"androidx.annotation_annotation",
"junit",
+ "junit-params",
"testng",
"androidx.core_core",
],
diff --git a/tests/tests/graphics/assets/passthrough_fsh.glsl b/tests/tests/graphics/assets/shaders/passthrough_fsh.glsl
similarity index 100%
rename from tests/tests/graphics/assets/passthrough_fsh.glsl
rename to tests/tests/graphics/assets/shaders/passthrough_fsh.glsl
diff --git a/tests/tests/graphics/assets/passthrough_fsh.spv b/tests/tests/graphics/assets/shaders/passthrough_fsh.spv
similarity index 100%
rename from tests/tests/graphics/assets/passthrough_fsh.spv
rename to tests/tests/graphics/assets/shaders/passthrough_fsh.spv
Binary files differ
diff --git a/tests/tests/graphics/assets/passthrough_vsh.glsl b/tests/tests/graphics/assets/shaders/passthrough_vsh.glsl
similarity index 100%
rename from tests/tests/graphics/assets/passthrough_vsh.glsl
rename to tests/tests/graphics/assets/shaders/passthrough_vsh.glsl
diff --git a/tests/tests/graphics/assets/passthrough_vsh.spv b/tests/tests/graphics/assets/shaders/passthrough_vsh.spv
similarity index 100%
rename from tests/tests/graphics/assets/passthrough_vsh.spv
rename to tests/tests/graphics/assets/shaders/passthrough_vsh.spv
Binary files differ
diff --git a/tests/tests/graphics/jni/VulkanTestHelpers.cpp b/tests/tests/graphics/jni/VulkanTestHelpers.cpp
index 17d952f..bfe94ee 100644
--- a/tests/tests/graphics/jni/VulkanTestHelpers.cpp
+++ b/tests/tests/graphics/jni/VulkanTestHelpers.cpp
@@ -647,7 +647,7 @@
{
AAsset *vertFile =
AAssetManager_open(AAssetManager_fromJava(env, assetMgr),
- "passthrough_vsh.spv", AASSET_MODE_BUFFER);
+ "shaders/passthrough_vsh.spv", AASSET_MODE_BUFFER);
ASSERT(vertFile);
size_t vertShaderLength = AAsset_getLength(vertFile);
std::vector<uint8_t> vertShader;
@@ -658,7 +658,7 @@
AAsset *pixelFile =
AAssetManager_open(AAssetManager_fromJava(env, assetMgr),
- "passthrough_fsh.spv", AASSET_MODE_BUFFER);
+ "shaders/passthrough_fsh.spv", AASSET_MODE_BUFFER);
ASSERT(pixelFile);
size_t pixelShaderLength = AAsset_getLength(pixelFile);
std::vector<uint8_t> pixelShader;
diff --git a/tests/tests/graphics/jni/android_graphics_cts_BitmapTest.cpp b/tests/tests/graphics/jni/android_graphics_cts_BitmapTest.cpp
index d44c14f..beecac0 100644
--- a/tests/tests/graphics/jni/android_graphics_cts_BitmapTest.cpp
+++ b/tests/tests/graphics/jni/android_graphics_cts_BitmapTest.cpp
@@ -37,7 +37,7 @@
ASSERT_EQ(format, info.format);
}
-static void validateNdkAccessAfterRecycle(JNIEnv* env, jclass, jobject jbitmap) {
+static void validateNdkAccessFails(JNIEnv* env, jclass, jobject jbitmap) {
void* pixels = nullptr;
int err = AndroidBitmap_lockPixels(env, jbitmap, &pixels);
ASSERT_EQ(err, ANDROID_BITMAP_RESULT_JNI_EXCEPTION);
@@ -79,14 +79,51 @@
return info.format;
}
+static void testNullBitmap(JNIEnv* env, jclass) {
+ ASSERT_NE(nullptr, env);
+ AndroidBitmapInfo info;
+ int err = AndroidBitmap_getInfo(env, nullptr, &info);
+ ASSERT_EQ(err, ANDROID_BITMAP_RESULT_BAD_PARAMETER);
+
+ void* pixels = nullptr;
+ err = AndroidBitmap_lockPixels(env, nullptr, &pixels);
+ ASSERT_EQ(err, ANDROID_BITMAP_RESULT_BAD_PARAMETER);
+
+ err = AndroidBitmap_unlockPixels(env, nullptr);
+ ASSERT_EQ(err, ANDROID_BITMAP_RESULT_BAD_PARAMETER);
+}
+
+static void testInfo(JNIEnv* env, jclass, jobject jbitmap, jint androidBitmapFormat,
+ jint width, jint height, jboolean hasAlpha, jboolean premultiplied) {
+ AndroidBitmapInfo info;
+ int err = AndroidBitmap_getInfo(env, jbitmap, &info);
+ ASSERT_EQ(err, ANDROID_BITMAP_RESULT_SUCCESS);
+
+ ASSERT_EQ(androidBitmapFormat, info.format);
+ ASSERT_EQ(width, info.width);
+ ASSERT_EQ(height, info.height);
+
+ int ndkAlpha = (info.flags << ANDROID_BITMAP_FLAGS_ALPHA_SHIFT)
+ & ANDROID_BITMAP_FLAGS_ALPHA_MASK;
+ if (!hasAlpha) {
+ ASSERT_EQ(ndkAlpha, ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE);
+ } else if (premultiplied) {
+ ASSERT_EQ(ndkAlpha, ANDROID_BITMAP_FLAGS_ALPHA_PREMUL);
+ } else {
+ ASSERT_EQ(ndkAlpha, ANDROID_BITMAP_FLAGS_ALPHA_UNPREMUL);
+ }
+}
+
static JNINativeMethod gMethods[] = {
{ "nValidateBitmapInfo", "(Landroid/graphics/Bitmap;IIZ)V",
(void*) validateBitmapInfo },
- { "nValidateNdkAccessAfterRecycle", "(Landroid/graphics/Bitmap;)V",
- (void*) validateNdkAccessAfterRecycle },
+ { "nValidateNdkAccessFails", "(Landroid/graphics/Bitmap;)V",
+ (void*) validateNdkAccessFails },
{ "nFillRgbaHwBuffer", "(Landroid/hardware/HardwareBuffer;)V",
(void*) fillRgbaHardwareBuffer },
{ "nGetFormat", "(Landroid/graphics/Bitmap;)I", (void*) getFormat },
+ { "nTestNullBitmap", "()V", (void*) testNullBitmap },
+ { "nTestInfo", "(Landroid/graphics/Bitmap;IIIZZ)V", (void*) testInfo },
};
int register_android_graphics_cts_BitmapTest(JNIEnv* env) {
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_clamp_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_clamp_golden.png
index e3be1d1..e67fce8 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_clamp_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_clamp_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_golden.png
index ce18075..8a48104 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_2_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_2_golden.png
index f991189..2145eec 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_2_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_2_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_2_repeat_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_2_repeat_golden.png
index f2798b4..5428052 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_2_repeat_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_2_repeat_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_golden.png
index aee71ec..70ee76a 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_mirror_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_mirror_golden.png
index a879e3c..0a195a8 100644
--- a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_mirror_golden.png
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_mirror_golden.png
Binary files differ
diff --git a/tests/tests/packageinstaller/atomicinstall/testdata/apk/src/com/android/tests/atomicinstall/testapp/MainActivity.java b/tests/tests/graphics/res/drawable/gradientdrawable_no_angle.xml
similarity index 67%
copy from tests/tests/packageinstaller/atomicinstall/testdata/apk/src/com/android/tests/atomicinstall/testapp/MainActivity.java
copy to tests/tests/graphics/res/drawable/gradientdrawable_no_angle.xml
index 37276e2..de316cf 100644
--- a/tests/tests/packageinstaller/atomicinstall/testdata/apk/src/com/android/tests/atomicinstall/testapp/MainActivity.java
+++ b/tests/tests/graphics/res/drawable/gradientdrawable_no_angle.xml
@@ -1,4 +1,5 @@
-/*
+<?xml version="1.0" encoding="utf-8"?>
+<!--
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -12,17 +13,11 @@
* WITHOUT 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.tests.atomicinstall.testapp;
-
-import android.app.Activity;
-import android.os.Bundle;
-
-public class MainActivity extends Activity {
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- }
-}
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <gradient
+ android:endColor="#00ff00"
+ android:startColor="#ff0000"
+ android:type="linear" />
+</shape>
diff --git a/tests/tests/graphics/src/android/graphics/cts/BitmapFactoryTest.java b/tests/tests/graphics/src/android/graphics/cts/BitmapFactoryTest.java
index b507bf9..1b934ac 100644
--- a/tests/tests/graphics/src/android/graphics/cts/BitmapFactoryTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/BitmapFactoryTest.java
@@ -36,6 +36,7 @@
import android.graphics.Rect;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
+import android.platform.test.annotations.LargeTest;
import android.system.ErrnoException;
import android.system.Os;
import android.util.DisplayMetrics;
@@ -43,7 +44,6 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.compatibility.common.util.CddTest;
@@ -61,29 +61,36 @@
import java.io.RandomAccessFile;
import java.util.concurrent.CountDownLatch;
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(JUnitParamsRunner.class)
public class BitmapFactoryTest {
// height and width of start.jpg
private static final int START_HEIGHT = 31;
private static final int START_WIDTH = 31;
- // The test images, including baseline JPEG, a PNG, a GIF, a BMP AND a WEBP.
- private static final int[] RES_IDS = new int[] {
- R.drawable.baseline_jpeg, R.drawable.png_test, R.drawable.gif_test,
- R.drawable.bmp_test, R.drawable.webp_test
- };
+ static class TestImage {
+ TestImage(int id, int width, int height) {
+ this.id = id;
+ this.width = width;
+ this.height = height;
+ }
+ public final int id;
+ public final int width;
+ public final int height;
+ }
- // The width and height of the above image.
- private static final int WIDTHS[] = new int[] { 1280, 640, 320, 320, 640 };
- private static final int HEIGHTS[] = new int[] { 960, 480, 240, 240, 480 };
-
- // Configurations for BitmapFactory.Options
- private static final Config[] COLOR_CONFIGS = new Config[] {Config.ARGB_8888, Config.RGB_565};
- private static final int[] COLOR_TOLS = new int[] {16, 49, 576};
-
- private static final Config[] COLOR_CONFIGS_RGBA = new Config[] {Config.ARGB_8888};
- private static final int[] COLOR_TOLS_RGBA = new int[] {72, 124};
+ private Object[] testImages() {
+ return new Object[] {
+ new TestImage(R.drawable.baseline_jpeg, 1280, 960),
+ new TestImage(R.drawable.png_test, 640, 480),
+ new TestImage(R.drawable.gif_test, 320, 240),
+ new TestImage(R.drawable.bmp_test, 320, 240),
+ new TestImage(R.drawable.webp_test, 640, 480),
+ };
+ }
private static final int[] RAW_COLORS = new int[] {
// raw data from R.drawable.premul_data
@@ -206,88 +213,102 @@
}
@Test
- public void testDecodeStream3() {
- for (int i = 0; i < RES_IDS.length; ++i) {
- InputStream is = obtainInputStream(RES_IDS[i]);
- Bitmap b = BitmapFactory.decodeStream(is);
- assertNotNull(b);
- // Test the bitmap size
- assertEquals(WIDTHS[i], b.getWidth());
- assertEquals(HEIGHTS[i], b.getHeight());
- }
+ @Parameters(method = "testImages")
+ public void testDecodeStream3(TestImage testImage) {
+ InputStream is = obtainInputStream(testImage.id);
+ Bitmap b = BitmapFactory.decodeStream(is);
+ assertNotNull(b);
+ // Test the bitmap size
+ assertEquals(testImage.width, b.getWidth());
+ assertEquals(testImage.height, b.getHeight());
+ }
+
+ private Object[] paramsForWebpDecodeEncode() {
+ return new Object[] {
+ new Object[] {Config.ARGB_8888, 16},
+ new Object[] {Config.RGB_565, 49}
+ };
+ }
+
+ private Bitmap decodeOpaqueImage(int resId, BitmapFactory.Options options) {
+ return decodeOpaqueImage(obtainInputStream(resId), options);
+ }
+
+ private Bitmap decodeOpaqueImage(InputStream stream, BitmapFactory.Options options) {
+ Bitmap bitmap = BitmapFactory.decodeStream(stream, null, options);
+ assertNotNull(bitmap);
+ assertFalse(bitmap.isPremultiplied());
+ assertFalse(bitmap.hasAlpha());
+ return bitmap;
}
@Test
- public void testDecodeStream4() {
+ @Parameters(method = "paramsForWebpDecodeEncode")
+ public void testWebpStreamDecode(Config config, int tolerance) {
BitmapFactory.Options options = new BitmapFactory.Options();
- for (int k = 0; k < COLOR_CONFIGS.length; ++k) {
- options.inPreferredConfig = COLOR_CONFIGS[k];
+ options.inPreferredConfig = config;
- // Decode the PNG & WebP test images. The WebP test image has been encoded from PNG test
- // image and should have same similar (within some error-tolerance) Bitmap data.
- InputStream iStreamPng = obtainInputStream(R.drawable.png_test);
- Bitmap bPng = BitmapFactory.decodeStream(iStreamPng, null, options);
- assertNotNull(bPng);
- assertEquals(bPng.getConfig(), COLOR_CONFIGS[k]);
- assertFalse(bPng.isPremultiplied());
- assertFalse(bPng.hasAlpha());
+ // Decode the PNG & WebP test images. The WebP test image has been encoded from PNG test
+ // image and should have same similar (within some error-tolerance) Bitmap data.
+ Bitmap bPng = decodeOpaqueImage(R.drawable.png_test, options);
+ assertEquals(bPng.getConfig(), config);
+ Bitmap bWebp = decodeOpaqueImage(R.drawable.webp_test, options);
+ compareBitmaps(bPng, bWebp, tolerance, true, bPng.isPremultiplied());
+ }
- InputStream iStreamWebp1 = obtainInputStream(R.drawable.webp_test);
- Bitmap bWebp1 = BitmapFactory.decodeStream(iStreamWebp1, null, options);
- assertNotNull(bWebp1);
- assertFalse(bWebp1.isPremultiplied());
- assertFalse(bWebp1.hasAlpha());
- compareBitmaps(bPng, bWebp1, COLOR_TOLS[k], true, bPng.isPremultiplied());
+ @Test
+ @Parameters(method = "paramsForWebpDecodeEncode")
+ public void testWebpStreamEncode(Config config, int tolerance) {
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inPreferredConfig = config;
- // Compress the PNG image to WebP format (Quality=90) and decode it back.
- // This will test end-to-end WebP encoding and decoding.
- ByteArrayOutputStream oStreamWebp = new ByteArrayOutputStream();
- assertTrue(bPng.compress(CompressFormat.WEBP, 90, oStreamWebp));
- InputStream iStreamWebp2 = new ByteArrayInputStream(oStreamWebp.toByteArray());
- Bitmap bWebp2 = BitmapFactory.decodeStream(iStreamWebp2, null, options);
- assertNotNull(bWebp2);
- assertFalse(bWebp2.isPremultiplied());
- assertFalse(bWebp2.hasAlpha());
- compareBitmaps(bPng, bWebp2, COLOR_TOLS[k], true, bPng.isPremultiplied());
- }
+ Bitmap bPng = decodeOpaqueImage(R.drawable.png_test, options);
+ assertEquals(bPng.getConfig(), config);
+
+ // Compress the PNG image to WebP format (Quality=90) and decode it back.
+ // This will test end-to-end WebP encoding and decoding.
+ ByteArrayOutputStream oStreamWebp = new ByteArrayOutputStream();
+ assertTrue(bPng.compress(CompressFormat.WEBP, 90, oStreamWebp));
+ InputStream iStreamWebp = new ByteArrayInputStream(oStreamWebp.toByteArray());
+ Bitmap bWebp2 = decodeOpaqueImage(iStreamWebp, options);
+ compareBitmaps(bPng, bWebp2, tolerance, true, bPng.isPremultiplied());
}
@Test
public void testDecodeStream5() {
+ final int tolerance = 72;
BitmapFactory.Options options = new BitmapFactory.Options();
- for (int k = 0; k < COLOR_CONFIGS_RGBA.length; ++k) {
- options.inPreferredConfig = COLOR_CONFIGS_RGBA[k];
+ options.inPreferredConfig = Config.ARGB_8888;
- // Decode the PNG & WebP (google_logo) images. The WebP image has
- // been encoded from PNG image.
- InputStream iStreamPng = obtainInputStream(R.drawable.google_logo_1);
- Bitmap bPng = BitmapFactory.decodeStream(iStreamPng, null, options);
- assertNotNull(bPng);
- assertEquals(bPng.getConfig(), COLOR_CONFIGS_RGBA[k]);
- assertTrue(bPng.isPremultiplied());
- assertTrue(bPng.hasAlpha());
+ // Decode the PNG & WebP (google_logo) images. The WebP image has
+ // been encoded from PNG image.
+ InputStream iStreamPng = obtainInputStream(R.drawable.google_logo_1);
+ Bitmap bPng = BitmapFactory.decodeStream(iStreamPng, null, options);
+ assertNotNull(bPng);
+ assertEquals(bPng.getConfig(), Config.ARGB_8888);
+ assertTrue(bPng.isPremultiplied());
+ assertTrue(bPng.hasAlpha());
- // Decode the corresponding WebP (transparent) image (google_logo_2.webp).
- InputStream iStreamWebP1 = obtainInputStream(R.drawable.google_logo_2);
- Bitmap bWebP1 = BitmapFactory.decodeStream(iStreamWebP1, null, options);
- assertNotNull(bWebP1);
- assertEquals(bWebP1.getConfig(), COLOR_CONFIGS_RGBA[k]);
- assertTrue(bWebP1.isPremultiplied());
- assertTrue(bWebP1.hasAlpha());
- compareBitmaps(bPng, bWebP1, COLOR_TOLS_RGBA[k], true, bPng.isPremultiplied());
+ // Decode the corresponding WebP (transparent) image (google_logo_2.webp).
+ InputStream iStreamWebP1 = obtainInputStream(R.drawable.google_logo_2);
+ Bitmap bWebP1 = BitmapFactory.decodeStream(iStreamWebP1, null, options);
+ assertNotNull(bWebP1);
+ assertEquals(bWebP1.getConfig(), Config.ARGB_8888);
+ assertTrue(bWebP1.isPremultiplied());
+ assertTrue(bWebP1.hasAlpha());
+ compareBitmaps(bPng, bWebP1, tolerance, true, bPng.isPremultiplied());
- // Compress the PNG image to WebP format (Quality=90) and decode it back.
- // This will test end-to-end WebP encoding and decoding.
- ByteArrayOutputStream oStreamWebp = new ByteArrayOutputStream();
- assertTrue(bPng.compress(CompressFormat.WEBP, 90, oStreamWebp));
- InputStream iStreamWebp2 = new ByteArrayInputStream(oStreamWebp.toByteArray());
- Bitmap bWebP2 = BitmapFactory.decodeStream(iStreamWebp2, null, options);
- assertNotNull(bWebP2);
- assertEquals(bWebP2.getConfig(), COLOR_CONFIGS_RGBA[k]);
- assertTrue(bWebP2.isPremultiplied());
- assertTrue(bWebP2.hasAlpha());
- compareBitmaps(bPng, bWebP2, COLOR_TOLS_RGBA[k], true, bPng.isPremultiplied());
- }
+ // Compress the PNG image to WebP format (Quality=90) and decode it back.
+ // This will test end-to-end WebP encoding and decoding.
+ ByteArrayOutputStream oStreamWebp = new ByteArrayOutputStream();
+ assertTrue(bPng.compress(CompressFormat.WEBP, 90, oStreamWebp));
+ InputStream iStreamWebp2 = new ByteArrayInputStream(oStreamWebp.toByteArray());
+ Bitmap bWebP2 = BitmapFactory.decodeStream(iStreamWebp2, null, options);
+ assertNotNull(bWebP2);
+ assertEquals(bWebP2.getConfig(), Config.ARGB_8888);
+ assertTrue(bWebP2.isPremultiplied());
+ assertTrue(bWebP2.hasAlpha());
+ compareBitmaps(bPng, bWebP2, tolerance, true, bPng.isPremultiplied());
}
@Test
@@ -315,47 +336,48 @@
assertEquals(START_WIDTH, b.getWidth());
}
+
+ // TODO: Better parameterize this and split it up.
@Test
- public void testDecodeFileDescriptor3() throws IOException {
+ @Parameters(method = "testImages")
+ public void testDecodeFileDescriptor3(TestImage testImage) throws IOException {
// Arbitrary offsets to use. If the offset of the FD matches the offset of the image,
// decoding should succeed, but if they do not match, decoding should fail.
- long ACTUAL_OFFSETS[] = new long[] { 0, 17 };
- for (int RES_ID : RES_IDS) {
- for (int j = 0; j < ACTUAL_OFFSETS.length; ++j) {
- // FIXME: The purgeable test should attempt to purge the memory
- // to force a re-decode.
- for (boolean TEST_PURGEABLE : new boolean[] { false, true }) {
- BitmapFactory.Options opts = new BitmapFactory.Options();
- opts.inPurgeable = TEST_PURGEABLE;
- opts.inInputShareable = TEST_PURGEABLE;
+ final long[] actual_offsets = new long[] { 0, 17 };
+ for (int j = 0; j < actual_offsets.length; ++j) {
+ // FIXME: The purgeable test should attempt to purge the memory
+ // to force a re-decode.
+ for (boolean purgeable : new boolean[] { false, true }) {
+ BitmapFactory.Options opts = new BitmapFactory.Options();
+ opts.inPurgeable = purgeable;
+ opts.inInputShareable = purgeable;
- long actualOffset = ACTUAL_OFFSETS[j];
- String path = obtainPath(RES_ID, actualOffset);
- RandomAccessFile file = new RandomAccessFile(path, "r");
- FileDescriptor fd = file.getFD();
- assertTrue(fd.valid());
+ long actualOffset = actual_offsets[j];
+ String path = obtainPath(testImage.id, actualOffset);
+ RandomAccessFile file = new RandomAccessFile(path, "r");
+ FileDescriptor fd = file.getFD();
+ assertTrue(fd.valid());
- // Set the offset to ACTUAL_OFFSET
- file.seek(actualOffset);
- assertEquals(file.getFilePointer(), actualOffset);
+ // Set the offset to ACTUAL_OFFSET
+ file.seek(actualOffset);
+ assertEquals(file.getFilePointer(), actualOffset);
- // Now decode. This should be successful and leave the offset
- // unchanged.
- Bitmap b = BitmapFactory.decodeFileDescriptor(fd, null, opts);
- assertNotNull(b);
- assertEquals(file.getFilePointer(), actualOffset);
+ // Now decode. This should be successful and leave the offset
+ // unchanged.
+ Bitmap b = BitmapFactory.decodeFileDescriptor(fd, null, opts);
+ assertNotNull(b);
+ assertEquals(file.getFilePointer(), actualOffset);
- // Now use the other offset. It should fail to decode, and
- // the offset should remain unchanged.
- long otherOffset = ACTUAL_OFFSETS[(j + 1) % ACTUAL_OFFSETS.length];
- assertFalse(otherOffset == actualOffset);
- file.seek(otherOffset);
- assertEquals(file.getFilePointer(), otherOffset);
+ // Now use the other offset. It should fail to decode, and
+ // the offset should remain unchanged.
+ long otherOffset = actual_offsets[(j + 1) % actual_offsets.length];
+ assertFalse(otherOffset == actualOffset);
+ file.seek(otherOffset);
+ assertEquals(file.getFilePointer(), otherOffset);
- b = BitmapFactory.decodeFileDescriptor(fd, null, opts);
- assertNull(b);
- assertEquals(file.getFilePointer(), otherOffset);
- }
+ b = BitmapFactory.decodeFileDescriptor(fd, null, opts);
+ assertNull(b);
+ assertEquals(file.getFilePointer(), otherOffset);
}
}
}
@@ -493,18 +515,17 @@
}
@Test
- public void testDecodeReuseFormats() {
+ @Parameters(method = "testImages")
+ public void testDecodeReuseFormats(TestImage testImage) {
// reuse should support all image formats
- for (int i = 0; i < RES_IDS.length; ++i) {
- Bitmap reuseBuffer = Bitmap.createBitmap(1000000, 1, Bitmap.Config.ALPHA_8);
+ Bitmap reuseBuffer = Bitmap.createBitmap(1000000, 1, Bitmap.Config.ALPHA_8);
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inBitmap = reuseBuffer;
- options.inSampleSize = 4;
- options.inScaled = false;
- Bitmap decoded = BitmapFactory.decodeResource(mRes, RES_IDS[i], options);
- assertSame(reuseBuffer, decoded);
- }
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inBitmap = reuseBuffer;
+ options.inSampleSize = 4;
+ options.inScaled = false;
+ Bitmap decoded = BitmapFactory.decodeResource(mRes, testImage.id, options);
+ assertSame(reuseBuffer, decoded);
}
@Test
@@ -792,27 +813,29 @@
@Test
@CddTest(requirement = "5.1.5/C-0-6")
- public void testDng() {
- DNG[] dngs = new DNG[]{
- new DNG(R.raw.sample_1mp, 600, 338),
- new DNG(R.raw.sample_arw, 1616, 1080),
- new DNG(R.raw.sample_cr2, 2304, 1536),
- new DNG(R.raw.sample_nef, 4608, 3072),
- new DNG(R.raw.sample_nrw, 4000, 3000),
- new DNG(R.raw.sample_orf, 3200, 2400),
- new DNG(R.raw.sample_pef, 4928, 3264),
- new DNG(R.raw.sample_raf, 2048, 1536),
- new DNG(R.raw.sample_rw2, 1920, 1440),
- new DNG(R.raw.sample_srw, 5472, 3648),
- };
+ @Parameters(method = "parametersForTestDng")
+ @LargeTest
+ public void testDng(DNG dng) {
+ // No scaling
+ Bitmap bm = BitmapFactory.decodeResource(mRes, dng.resId, mOpt1);
+ assertNotNull(bm);
+ assertEquals(dng.width, bm.getWidth());
+ assertEquals(dng.height, bm.getHeight());
+ }
- for (DNG dng : dngs) {
- // No scaling
- Bitmap bm = BitmapFactory.decodeResource(mRes, dng.resId, mOpt1);
- assertNotNull(bm);
- assertEquals(dng.width, bm.getWidth());
- assertEquals(dng.height, bm.getHeight());
- }
+ private Object[] parametersForTestDng() {
+ return new Object[]{
+ new DNG(R.raw.sample_1mp, 600, 338),
+ new DNG(R.raw.sample_arw, 1616, 1080),
+ new DNG(R.raw.sample_cr2, 2304, 1536),
+ new DNG(R.raw.sample_nef, 4608, 3072),
+ new DNG(R.raw.sample_nrw, 4000, 3000),
+ new DNG(R.raw.sample_orf, 3200, 2400),
+ new DNG(R.raw.sample_pef, 4928, 3264),
+ new DNG(R.raw.sample_raf, 2048, 1536),
+ new DNG(R.raw.sample_rw2, 1920, 1440),
+ new DNG(R.raw.sample_srw, 5472, 3648),
+ };
}
@Test
diff --git a/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java b/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java
index a2025bd..fc510a7 100644
--- a/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java
@@ -46,7 +46,6 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.compatibility.common.util.ColorUtils;
import com.android.compatibility.common.util.WidgetTestUtils;
@@ -66,8 +65,11 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(JUnitParamsRunner.class)
public class BitmapTest {
// small alpha values cause color values to be pre-multiplied down, losing accuracy
private static final int PREMUL_COLOR = Color.argb(2, 255, 254, 253);
@@ -1959,7 +1961,7 @@
nValidateBitmapInfo(bitmap, 10, 20, true);
bitmap.recycle();
nValidateBitmapInfo(bitmap, 10, 20, true);
- nValidateNdkAccessAfterRecycle(bitmap);
+ nValidateNdkAccessFails(bitmap);
}
@Test
@@ -2022,15 +2024,15 @@
Debug.MemoryInfo meminfoStart = new Debug.MemoryInfo();
Debug.MemoryInfo meminfoEnd = new Debug.MemoryInfo();
int fdCount = -1;
+ // Do a warmup to reach steady-state memory usage
+ for (int i = 0; i < 50; i++) {
+ test.run();
+ }
+ runGcAndFinalizersSync();
+ Debug.getMemoryInfo(meminfoStart);
+ fdCount = getFdCount();
+ // Now run the test
for (int i = 0; i < 2000; i++) {
- if (i == 4) {
- // Not really the "start" but by having done a couple
- // we've fully initialized any state that may be required,
- // so memory usage should be stable now
- runGcAndFinalizersSync();
- Debug.getMemoryInfo(meminfoStart);
- fdCount = getFdCount();
- }
if (i % 100 == 5) {
assertNotLeaking(i, meminfoStart, meminfoEnd);
final int curFdCount = getFdCount();
@@ -2161,6 +2163,83 @@
}
}
+ @Test
+ public void testNdkFormats() {
+ for (ConfigToFormat pair : CONFIG_TO_FORMAT) {
+ Bitmap bm = Bitmap.createBitmap(10, 10, pair.config);
+ assertNotNull(bm);
+ int nativeFormat = nGetFormat(bm);
+ assertEquals("Config: " + pair.config, pair.format, nativeFormat);
+ }
+ }
+
+ @Test
+ public void testNdkFormatsHardware() {
+ for (ConfigToFormat pair : CONFIG_TO_FORMAT) {
+ Bitmap bm = Bitmap.createBitmap(10, 10, pair.config);
+ bm = bm.copy(Bitmap.Config.HARDWARE, false);
+
+ // ALPHA_8 is not supported in HARDWARE.
+ if (bm == null) {
+ assertEquals(Bitmap.Config.ALPHA_8, pair.config);
+ continue;
+ }
+ assertNotEquals(Bitmap.Config.ALPHA_8, pair.config);
+
+ int nativeFormat = nGetFormat(bm);
+ if (pair.config == Bitmap.Config.RGBA_F16) {
+ // It is possible the system does not support RGBA_F16 in HARDWARE.
+ // In that case, it will fall back to ARGB_8888.
+ assertTrue(nativeFormat == ANDROID_BITMAP_FORMAT_RGBA_8888
+ || nativeFormat == ANDROID_BITMAP_FORMAT_RGBA_F16);
+ } else {
+ assertEquals("Config: " + pair.config, pair.format, nativeFormat);
+ }
+
+ nValidateNdkAccessFails(bm);
+ }
+ }
+
+ @Test
+ public void testNullBitmapNdk() {
+ nTestNullBitmap();
+ }
+
+ private Object[] parametersForTestNdkInfo() {
+ return new Object[] {
+ new Object[] { Config.ALPHA_8, 8 /* ANDROID_BITMAP_FORMAT_A_8 */ },
+ new Object[] { Config.ARGB_8888, 1 /* ANDROID_BITMAP_FORMAT_RGBA_8888 */ },
+ new Object[] { Config.RGB_565, 4 /* ANDROID_BITMAP_FORMAT_RGB_565 */ },
+ new Object[] { Config.RGBA_F16, 9 /* ANDROID_BITMAP_FORMAT_RGBA_F16*/ },
+ };
+ }
+
+ @Test
+ @Parameters(method = "parametersForTestNdkInfo")
+ public void testNdkInfo(Config config, int androidBitmapFormat) {
+ // Arbitrary width and height.
+ final int width = 13;
+ final int height = 7;
+ boolean[] trueFalse = new boolean[] { true, false };
+ for (boolean hasAlpha : trueFalse) {
+ for (boolean premultiplied : trueFalse) {
+ Bitmap bm = Bitmap.createBitmap(width, height, config, hasAlpha);
+ bm.setPremultiplied(premultiplied);
+ nTestInfo(bm, androidBitmapFormat, width, height, bm.hasAlpha(),
+ bm.isPremultiplied());
+ bm = bm.copy(Bitmap.Config.HARDWARE, false);
+ if (config == Bitmap.Config.ALPHA_8) {
+ // ALPHA_8 is not supported in HARDWARE. b/141480329
+ assertNull(bm);
+ } else {
+ assertNotNull(bm);
+ nTestInfo(bm, androidBitmapFormat, width, height, bm.hasAlpha(),
+ bm.isPremultiplied());
+ }
+ }
+ }
+ }
+
private void strictModeTest(Runnable runnable) {
StrictMode.ThreadPolicy originalPolicy = StrictMode.getThreadPolicy();
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
@@ -2177,13 +2256,39 @@
private static native void nValidateBitmapInfo(Bitmap bitmap, int width, int height,
boolean is565);
- private static native void nValidateNdkAccessAfterRecycle(Bitmap bitmap);
+ private static native void nValidateNdkAccessFails(Bitmap bitmap);
private static native void nFillRgbaHwBuffer(HardwareBuffer hwBuffer);
+ private static native void nTestNullBitmap();
- private static final int ANDROID_BITMAP_FORMAT_RGBA_8888 = 1;
+ static final int ANDROID_BITMAP_FORMAT_RGBA_8888 = 1;
private static final int ANDROID_BITMAP_FORMAT_RGB_565 = 4;
- private static native int nGetFormat(Bitmap bitmap);
+ private static final int ANDROID_BITMAP_FORMAT_A_8 = 8;
+ private static final int ANDROID_BITMAP_FORMAT_RGBA_F16 = 9;
+
+ private static class ConfigToFormat {
+ public final Config config;
+ public final int format;
+
+ ConfigToFormat(Config c, int f) {
+ this.config = c;
+ this.format = f;
+ }
+ }
+
+ private static final ConfigToFormat[] CONFIG_TO_FORMAT = new ConfigToFormat[] {
+ new ConfigToFormat(Bitmap.Config.ARGB_8888, ANDROID_BITMAP_FORMAT_RGBA_8888),
+ // ARGB_4444 is deprecated, and createBitmap converts to 8888.
+ new ConfigToFormat(Bitmap.Config.ARGB_4444, ANDROID_BITMAP_FORMAT_RGBA_8888),
+ new ConfigToFormat(Bitmap.Config.RGB_565, ANDROID_BITMAP_FORMAT_RGB_565),
+ new ConfigToFormat(Bitmap.Config.ALPHA_8, ANDROID_BITMAP_FORMAT_A_8),
+ new ConfigToFormat(Bitmap.Config.RGBA_F16, ANDROID_BITMAP_FORMAT_RGBA_F16),
+ };
+
+ static native int nGetFormat(Bitmap bitmap);
+
+ private static native void nTestInfo(Bitmap bm, int androidBitmapFormat, int width, int height,
+ boolean hasAlpha, boolean premultiplied);
private static HardwareBuffer createTestBuffer(int width, int height, boolean cpuAccess) {
long usage = HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE;
diff --git a/tests/tests/graphics/src/android/graphics/cts/CameraTest.java b/tests/tests/graphics/src/android/graphics/cts/CameraTest.java
index ff8f77c..92f6322 100644
--- a/tests/tests/graphics/src/android/graphics/cts/CameraTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/CameraTest.java
@@ -91,8 +91,8 @@
float[] f = new float[9];
m2.getValues(f);
assertArrayEquals(new float[] {
- 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -0.0017361111f, 1.0f
- }, f, 0.0f);
+ 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f
+ }, f, 0.0017361111f);
}
@Test
@@ -107,8 +107,8 @@
float[] f = new float[9];
m2.getValues(f);
assertArrayEquals(new float[] {
- 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0017361111f, 0.0f, 1.0f
- }, f, 0.0f);
+ 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f
+ }, f, 0.0017361111f);
}
@Test
@@ -124,7 +124,7 @@
m2.getValues(f);
assertArrayEquals(new float[] {
0.0f, 1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f
- }, f, 0.0f);
+ }, f, 0.0017361111f);
}
@Test
diff --git a/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java b/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java
index 596e939..4591268 100644
--- a/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java
@@ -51,11 +51,9 @@
import androidx.core.content.FileProvider;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.compatibility.common.util.BitmapUtils;
-import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -71,11 +69,11 @@
import java.util.function.Supplier;
import java.util.function.ToIntFunction;
-@RunWith(AndroidJUnit4.class)
-public class ImageDecoderTest {
- private Resources mRes;
- private ContentResolver mContentResolver;
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+@RunWith(JUnitParamsRunner.class)
+public class ImageDecoderTest {
private static final class Record {
public final int resId;
public final int width;
@@ -94,17 +92,19 @@
private static final ColorSpace sSRGB = ColorSpace.get(ColorSpace.Named.SRGB);
- private static final Record[] RECORDS = new Record[] {
- new Record(R.drawable.baseline_jpeg, 1280, 960, "image/jpeg", sSRGB),
- new Record(R.drawable.png_test, 640, 480, "image/png", sSRGB),
- new Record(R.drawable.gif_test, 320, 240, "image/gif", sSRGB),
- new Record(R.drawable.bmp_test, 320, 240, "image/bmp", sSRGB),
- new Record(R.drawable.webp_test, 640, 480, "image/webp", sSRGB),
- new Record(R.drawable.google_chrome, 256, 256, "image/x-ico", sSRGB),
- new Record(R.drawable.color_wheel, 128, 128, "image/x-ico", sSRGB),
- new Record(R.raw.sample_1mp, 600, 338, "image/x-adobe-dng", sSRGB),
- new Record(R.raw.heifwriter_input, 1920, 1080, "image/heif", sSRGB),
- };
+ private Object[] getRecords() {
+ return new Record[] {
+ new Record(R.drawable.baseline_jpeg, 1280, 960, "image/jpeg", sSRGB),
+ new Record(R.drawable.png_test, 640, 480, "image/png", sSRGB),
+ new Record(R.drawable.gif_test, 320, 240, "image/gif", sSRGB),
+ new Record(R.drawable.bmp_test, 320, 240, "image/bmp", sSRGB),
+ new Record(R.drawable.webp_test, 640, 480, "image/webp", sSRGB),
+ new Record(R.drawable.google_chrome, 256, 256, "image/x-ico", sSRGB),
+ new Record(R.drawable.color_wheel, 128, 128, "image/x-ico", sSRGB),
+ new Record(R.raw.sample_1mp, 600, 338, "image/x-adobe-dng", sSRGB),
+ new Record(R.raw.heifwriter_input, 1920, 1080, "image/heif", sSRGB),
+ };
+ }
// offset is how many bytes to offset the beginning of the image.
// extra is how many bytes to append at the end.
@@ -115,7 +115,7 @@
}
private void writeToStream(OutputStream output, int resId, int offset, int extra) {
- InputStream input = mRes.openRawResource(resId);
+ InputStream input = getResources().openRawResource(resId);
byte[] buffer = new byte[4096];
int bytesRead;
try {
@@ -194,11 +194,12 @@
}
private Uri getAsResourceUri(int resId) {
+ Resources res = getResources();
return new Uri.Builder()
.scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
- .authority(mRes.getResourcePackageName(resId))
- .appendPath(mRes.getResourceTypeName(resId))
- .appendPath(mRes.getResourceEntryName(resId))
+ .authority(res.getResourcePackageName(resId))
+ .appendPath(res.getResourceTypeName(resId))
+ .appendPath(res.getResourceEntryName(resId))
.build();
}
@@ -229,102 +230,105 @@
};
@Test
- public void testUris() {
- for (Record record : RECORDS) {
- int resId = record.resId;
- String name = mRes.getResourceEntryName(resId);
- for (UriCreator f : mUriCreators) {
- ImageDecoder.Source src = null;
- Uri uri = f.apply(resId);
- String fullName = name + ": " + uri.toString();
- src = ImageDecoder.createSource(mContentResolver, uri);
+ @Parameters(method = "getRecords")
+ public void testUris(Record record) {
+ int resId = record.resId;
+ String name = getResources().getResourceEntryName(resId);
+ for (UriCreator f : mUriCreators) {
+ ImageDecoder.Source src = null;
+ Uri uri = f.apply(resId);
+ String fullName = name + ": " + uri.toString();
+ src = ImageDecoder.createSource(getContentResolver(), uri);
- assertNotNull("failed to create Source for " + fullName, src);
- try {
- Drawable d = ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
- decoder.setOnPartialImageListener((e) -> {
- fail("error for image " + fullName + ":\n" + e);
- return false;
- });
+ assertNotNull("failed to create Source for " + fullName, src);
+ try {
+ Drawable d = ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
+ decoder.setOnPartialImageListener((e) -> {
+ fail("error for image " + fullName + ":\n" + e);
+ return false;
});
- assertNotNull("failed to create drawable for " + fullName, d);
- } catch (IOException e) {
- fail("exception for image " + fullName + ":\n" + e);
- }
+ });
+ assertNotNull("failed to create drawable for " + fullName, d);
+ } catch (IOException e) {
+ fail("exception for image " + fullName + ":\n" + e);
}
}
}
- @Before
- public void setup() {
- mRes = InstrumentationRegistry.getTargetContext().getResources();
- mContentResolver = InstrumentationRegistry.getTargetContext().getContentResolver();
+ private Resources getResources() {
+ return InstrumentationRegistry.getTargetContext().getResources();
+ }
+
+ private ContentResolver getContentResolver() {
+ return InstrumentationRegistry.getTargetContext().getContentResolver();
}
@Test
- public void testInfo() {
- for (Record record : RECORDS) {
- for (SourceCreator f : mCreators) {
- ImageDecoder.Source src = f.apply(record.resId);
- assertNotNull(src);
- try {
- ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
- assertEquals(record.width, info.getSize().getWidth());
- assertEquals(record.height, info.getSize().getHeight());
- assertEquals(record.mimeType, info.getMimeType());
- assertSame(record.colorSpace, info.getColorSpace());
- });
- } catch (IOException e) {
- fail("Failed " + getAsResourceUri(record.resId) + " with exception " + e);
- }
+ @Parameters(method = "getRecords")
+ public void testInfo(Record record) {
+ for (SourceCreator f : mCreators) {
+ ImageDecoder.Source src = f.apply(record.resId);
+ assertNotNull(src);
+ try {
+ ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
+ assertEquals(record.width, info.getSize().getWidth());
+ assertEquals(record.height, info.getSize().getHeight());
+ assertEquals(record.mimeType, info.getMimeType());
+ assertSame(record.colorSpace, info.getColorSpace());
+ });
+ } catch (IOException e) {
+ fail("Failed " + getAsResourceUri(record.resId) + " with exception " + e);
}
}
}
@Test
- public void testDecodeDrawable() {
- for (Record record : RECORDS) {
- for (SourceCreator f : mCreators) {
- ImageDecoder.Source src = f.apply(record.resId);
- assertNotNull(src);
+ @Parameters(method = "getRecords")
+ public void testDecodeDrawable(Record record) {
+ for (SourceCreator f : mCreators) {
+ ImageDecoder.Source src = f.apply(record.resId);
+ assertNotNull(src);
- try {
- Drawable drawable = ImageDecoder.decodeDrawable(src);
- assertNotNull(drawable);
- assertEquals(record.width, drawable.getIntrinsicWidth());
- assertEquals(record.height, drawable.getIntrinsicHeight());
- } catch (IOException e) {
- fail("Failed with exception " + e);
- }
+ try {
+ Drawable drawable = ImageDecoder.decodeDrawable(src);
+ assertNotNull(drawable);
+ assertEquals(record.width, drawable.getIntrinsicWidth());
+ assertEquals(record.height, drawable.getIntrinsicHeight());
+ } catch (IOException e) {
+ fail("Failed with exception " + e);
}
}
}
@Test
- public void testDecodeBitmap() {
- for (Record record : RECORDS) {
- for (SourceCreator f : mCreators) {
- ImageDecoder.Source src = f.apply(record.resId);
- assertNotNull(src);
+ @Parameters(method = "getRecords")
+ public void testDecodeBitmap(Record record) {
+ for (SourceCreator f : mCreators) {
+ ImageDecoder.Source src = f.apply(record.resId);
+ assertNotNull(src);
- try {
- Bitmap bm = ImageDecoder.decodeBitmap(src);
- assertNotNull(bm);
- assertEquals(record.width, bm.getWidth());
- assertEquals(record.height, bm.getHeight());
- assertFalse(bm.isMutable());
- // FIXME: This may change for small resources, etc.
- assertEquals(Bitmap.Config.HARDWARE, bm.getConfig());
- } catch (IOException e) {
- fail("Failed with exception " + e);
- }
+ try {
+ Bitmap bm = ImageDecoder.decodeBitmap(src);
+ assertNotNull(bm);
+ assertEquals(record.width, bm.getWidth());
+ assertEquals(record.height, bm.getHeight());
+ assertFalse(bm.isMutable());
+ // FIXME: This may change for small resources, etc.
+ assertEquals(Bitmap.Config.HARDWARE, bm.getConfig());
+ } catch (IOException e) {
+ fail("Failed with exception " + e);
}
}
}
+ // Return a single Record for simple tests.
+ private Record getRecord() {
+ return ((Record[]) getRecords())[0];
+ }
+
@Test(expected = IllegalArgumentException.class)
public void testSetBogusAllocator() {
- ImageDecoder.Source src = mCreators[0].apply(RECORDS[0].resId);
+ ImageDecoder.Source src = mCreators[0].apply(getRecord().resId);
try {
ImageDecoder.decodeBitmap(src, (decoder, info, s) -> decoder.setAllocator(15));
} catch (IOException e) {
@@ -341,7 +345,7 @@
@Test
public void testGetAllocator() {
- final int resId = RECORDS[0].resId;
+ final int resId = getRecord().resId;
ImageDecoder.Source src = mCreators[0].apply(resId);
try {
ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
@@ -357,7 +361,8 @@
}
@Test
- public void testSetAllocatorDecodeBitmap() {
+ @Parameters(method = "getRecords")
+ public void testSetAllocatorDecodeBitmap(Record record) {
class Listener implements ImageDecoder.OnHeaderDecodedListener {
public int allocator;
public boolean doCrop;
@@ -378,48 +383,45 @@
Listener l = new Listener();
boolean trueFalse[] = new boolean[] { true, false };
- for (Record record : RECORDS) {
- for (SourceCreator f : mCreators) {
- for (int allocator : ALLOCATORS) {
- for (boolean doCrop : trueFalse) {
- for (boolean doScale : trueFalse) {
- l.doCrop = doCrop;
- l.doScale = doScale;
- l.allocator = allocator;
- ImageDecoder.Source src = f.apply(record.resId);
- assertNotNull(src);
+ Resources res = getResources();
+ ImageDecoder.Source src = ImageDecoder.createSource(res, record.resId);
+ assertNotNull(src);
+ for (int allocator : ALLOCATORS) {
+ for (boolean doCrop : trueFalse) {
+ for (boolean doScale : trueFalse) {
+ l.doCrop = doCrop;
+ l.doScale = doScale;
+ l.allocator = allocator;
- Bitmap bm = null;
- try {
- bm = ImageDecoder.decodeBitmap(src, l);
- } catch (IOException e) {
- fail("Failed " + getAsResourceUri(record.resId) +
- " with exception " + e);
+ Bitmap bm = null;
+ try {
+ bm = ImageDecoder.decodeBitmap(src, l);
+ } catch (IOException e) {
+ fail("Failed " + getAsResourceUri(record.resId)
+ + " with exception " + e);
+ }
+ assertNotNull(bm);
+
+ switch (allocator) {
+ case ImageDecoder.ALLOCATOR_SOFTWARE:
+ // TODO: Once Bitmap provides access to its
+ // SharedMemory, confirm that ALLOCATOR_SHARED_MEMORY
+ // worked.
+ case ImageDecoder.ALLOCATOR_SHARED_MEMORY:
+ assertNotEquals(Bitmap.Config.HARDWARE, bm.getConfig());
+
+ if (!doScale && !doCrop) {
+ Bitmap reference = BitmapFactory.decodeResource(res,
+ record.resId, null);
+ assertNotNull(reference);
+ BitmapUtils.compareBitmaps(bm, reference);
}
- assertNotNull(bm);
-
- switch (allocator) {
- case ImageDecoder.ALLOCATOR_SOFTWARE:
- // TODO: Once Bitmap provides access to its
- // SharedMemory, confirm that ALLOCATOR_SHARED_MEMORY
- // worked.
- case ImageDecoder.ALLOCATOR_SHARED_MEMORY:
- assertNotEquals(Bitmap.Config.HARDWARE, bm.getConfig());
-
- if (!doScale && !doCrop) {
- Bitmap reference = BitmapFactory.decodeResource(mRes,
- record.resId, null);
- assertNotNull(reference);
- BitmapUtils.compareBitmaps(bm, reference);
- }
- break;
- default:
- String name = getAsResourceUri(record.resId).toString();
- assertEquals("image " + name + "; allocator: " + allocator,
- Bitmap.Config.HARDWARE, bm.getConfig());
- break;
- }
- }
+ break;
+ default:
+ String name = getAsResourceUri(record.resId).toString();
+ assertEquals("image " + name + "; allocator: " + allocator,
+ Bitmap.Config.HARDWARE, bm.getConfig());
+ break;
}
}
}
@@ -428,7 +430,7 @@
@Test
public void testGetUnpremul() {
- final int resId = RECORDS[0].resId;
+ final int resId = getRecord().resId;
ImageDecoder.Source src = mCreators[0].apply(resId);
try {
ImageDecoder.decodeBitmap(src, (decoder, info, s) -> {
@@ -484,7 +486,7 @@
(canvas) -> PixelFormat.UNKNOWN,
null,
};
- final int resId = RECORDS[0].resId;
+ final int resId = getRecord().resId;
ImageDecoder.Source src = mCreators[0].apply(resId);
try {
ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
@@ -501,7 +503,8 @@
}
@Test
- public void testPostProcessor() {
+ @Parameters(method = "getRecords")
+ public void testPostProcessor(Record record) {
class Listener implements ImageDecoder.OnHeaderDecodedListener {
public boolean requireSoftware;
@Override
@@ -518,44 +521,41 @@
};
Listener l = new Listener();
boolean trueFalse[] = new boolean[] { true, false };
- for (Record record : RECORDS) {
- for (SourceCreator f : mCreators) {
- for (boolean requireSoftware : trueFalse) {
- l.requireSoftware = requireSoftware;
- ImageDecoder.Source src = f.apply(record.resId);
- assertNotNull(src);
+ ImageDecoder.Source src = ImageDecoder.createSource(getResources(), record.resId);
+ assertNotNull(src);
+ for (boolean requireSoftware : trueFalse) {
+ l.requireSoftware = requireSoftware;
- Bitmap bitmap = null;
- try {
- bitmap = ImageDecoder.decodeBitmap(src, l);
- } catch (IOException e) {
- fail("Failed with exception " + e);
- }
- assertNotNull(bitmap);
- assertFalse(bitmap.isMutable());
- if (requireSoftware) {
- assertNotEquals(Bitmap.Config.HARDWARE, bitmap.getConfig());
- for (int x = 0; x < bitmap.getWidth(); ++x) {
- for (int y = 0; y < bitmap.getHeight(); ++y) {
- int color = bitmap.getPixel(x, y);
- assertEquals("pixel at (" + x + ", " + y + ") does not match!",
- color, Color.BLACK);
- }
- }
- } else {
- assertEquals(bitmap.getConfig(), Bitmap.Config.HARDWARE);
+ Bitmap bitmap = null;
+ try {
+ bitmap = ImageDecoder.decodeBitmap(src, l);
+ } catch (IOException e) {
+ fail("Failed with exception " + e);
+ }
+ assertNotNull(bitmap);
+ assertFalse(bitmap.isMutable());
+ if (requireSoftware) {
+ assertNotEquals(Bitmap.Config.HARDWARE, bitmap.getConfig());
+ for (int x = 0; x < bitmap.getWidth(); ++x) {
+ for (int y = 0; y < bitmap.getHeight(); ++y) {
+ int color = bitmap.getPixel(x, y);
+ assertEquals("pixel at (" + x + ", " + y + ") does not match!",
+ color, Color.BLACK);
}
}
+ } else {
+ assertEquals(bitmap.getConfig(), Bitmap.Config.HARDWARE);
}
}
}
@Test
public void testNinepatchWithDensityNone() {
+ Resources res = getResources();
TypedValue value = new TypedValue();
- InputStream is = mRes.openRawResource(R.drawable.ninepatch_nodpi, value);
+ InputStream is = res.openRawResource(R.drawable.ninepatch_nodpi, value);
// This does not call ImageDecoder directly because this entry point is not public.
- Drawable dr = Drawable.createFromResourceStream(mRes, value, is, null, null);
+ Drawable dr = Drawable.createFromResourceStream(res, value, is, null, null);
assertNotNull(dr);
assertEquals(5, dr.getIntrinsicWidth());
assertEquals(5, dr.getIntrinsicHeight());
@@ -632,7 +632,8 @@
}
@Test
- public void testPostProcessorAndAddedTransparency() {
+ @Parameters(method = "getRecords")
+ public void testPostProcessorAndAddedTransparency(Record record) {
class Listener implements ImageDecoder.OnHeaderDecodedListener {
public boolean requireSoftware;
@Override
@@ -646,18 +647,16 @@
};
Listener l = new Listener();
boolean trueFalse[] = new boolean[] { true, false };
- for (Record record : RECORDS) {
- for (SourceCreator f : mCreators) {
- for (boolean requireSoftware : trueFalse) {
- l.requireSoftware = requireSoftware;
- ImageDecoder.Source src = f.apply(record.resId);
- try {
- Bitmap bm = ImageDecoder.decodeBitmap(src, l);
- assertTrue(bm.hasAlpha());
- assertTrue(bm.isPremultiplied());
- } catch (IOException e) {
- fail("Failed with exception " + e);
- }
+ for (SourceCreator f : mCreators) {
+ for (boolean requireSoftware : trueFalse) {
+ l.requireSoftware = requireSoftware;
+ ImageDecoder.Source src = f.apply(record.resId);
+ try {
+ Bitmap bm = ImageDecoder.decodeBitmap(src, l);
+ assertTrue(bm.hasAlpha());
+ assertTrue(bm.isPremultiplied());
+ } catch (IOException e) {
+ fail("Failed with exception " + e);
}
}
}
@@ -677,7 +676,7 @@
@Test(expected = IllegalArgumentException.class)
public void testPostProcessorInvalidReturn() {
- ImageDecoder.Source src = mCreators[0].apply(RECORDS[0].resId);
+ ImageDecoder.Source src = mCreators[0].apply(getRecord().resId);
try {
ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
decoder.setPostProcessor((c) -> 42);
@@ -689,7 +688,7 @@
@Test(expected = IllegalStateException.class)
public void testPostProcessorAndUnpremul() {
- ImageDecoder.Source src = mCreators[0].apply(RECORDS[0].resId);
+ ImageDecoder.Source src = mCreators[0].apply(getRecord().resId);
try {
ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
decoder.setUnpremultipliedRequired(true);
@@ -701,7 +700,8 @@
}
@Test
- public void testPostProcessorAndScale() {
+ @Parameters(method = "getRecords")
+ public void testPostProcessorAndScale(Record record) {
class PostProcessorWithSize implements PostProcessor {
public int width;
public int height;
@@ -713,21 +713,19 @@
};
};
final PostProcessorWithSize pp = new PostProcessorWithSize();
- for (Record record : RECORDS) {
- pp.width = record.width / 2;
- pp.height = record.height / 2;
- for (SourceCreator f : mCreators) {
- ImageDecoder.Source src = f.apply(record.resId);
- try {
- Drawable drawable = ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
- decoder.setTargetSize(pp.width, pp.height);
- decoder.setPostProcessor(pp);
- });
- assertEquals(pp.width, drawable.getIntrinsicWidth());
- assertEquals(pp.height, drawable.getIntrinsicHeight());
- } catch (IOException e) {
- fail("Failed " + getAsResourceUri(record.resId) + " with exception " + e);
- }
+ pp.width = record.width / 2;
+ pp.height = record.height / 2;
+ for (SourceCreator f : mCreators) {
+ ImageDecoder.Source src = f.apply(record.resId);
+ try {
+ Drawable drawable = ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
+ decoder.setTargetSize(pp.width, pp.height);
+ decoder.setPostProcessor(pp);
+ });
+ assertEquals(pp.width, drawable.getIntrinsicWidth());
+ assertEquals(pp.height, drawable.getIntrinsicHeight());
+ } catch (IOException e) {
+ fail("Failed " + getAsResourceUri(record.resId) + " with exception " + e);
}
}
}
@@ -748,21 +746,20 @@
}
@Test
- public void testSampleSize() {
- for (Record record : RECORDS) {
- final String name = getAsResourceUri(record.resId).toString();
- for (int sampleSize : new int[] { 2, 3, 4, 8, 32 }) {
- ImageDecoder.Source src = mCreators[0].apply(record.resId);
- try {
- Drawable dr = ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
- decoder.setTargetSampleSize(sampleSize);
- });
+ @Parameters(method = "getRecords")
+ public void testSampleSize(Record record) {
+ final String name = getAsResourceUri(record.resId).toString();
+ for (int sampleSize : new int[] { 2, 3, 4, 8, 32 }) {
+ ImageDecoder.Source src = mCreators[0].apply(record.resId);
+ try {
+ Drawable dr = ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
+ decoder.setTargetSampleSize(sampleSize);
+ });
- checkSampleSize(name, record.width, sampleSize, dr.getIntrinsicWidth());
- checkSampleSize(name, record.height, sampleSize, dr.getIntrinsicHeight());
- } catch (IOException e) {
- fail("Failed " + name + " with exception " + e);
- }
+ checkSampleSize(name, record.width, sampleSize, dr.getIntrinsicWidth());
+ checkSampleSize(name, record.height, sampleSize, dr.getIntrinsicHeight());
+ } catch (IOException e) {
+ fail("Failed " + name + " with exception " + e);
}
}
}
@@ -770,25 +767,23 @@
private interface SampleSizeSupplier extends ToIntFunction<Size> {};
@Test
- public void testLargeSampleSize() {
- for (Record record : RECORDS) {
- for (SourceCreator f : mCreators) {
- for (SampleSizeSupplier supplySampleSize : new SampleSizeSupplier[] {
- (size) -> size.getWidth(),
- (size) -> size.getWidth() + 5,
- (size) -> size.getWidth() * 5,
- }) {
- ImageDecoder.Source src = f.apply(record.resId);
- try {
- Drawable dr = ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
- int sampleSize = supplySampleSize.applyAsInt(info.getSize());
- decoder.setTargetSampleSize(sampleSize);
- });
- assertEquals(1, dr.getIntrinsicWidth());
- } catch (IOException e) {
- fail("Failed with exception " + e);
- }
- }
+ @Parameters(method = "getRecords")
+ public void testLargeSampleSize(Record record) {
+ ImageDecoder.Source src = mCreators[0].apply(record.resId);
+ for (SampleSizeSupplier supplySampleSize : new SampleSizeSupplier[] {
+ (size) -> size.getWidth(),
+ (size) -> size.getWidth() + 5,
+ (size) -> size.getWidth() * 5,
+ }) {
+ try {
+ Drawable dr = ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
+ int sampleSize = supplySampleSize.applyAsInt(info.getSize());
+ decoder.setTargetSampleSize(sampleSize);
+ });
+ assertEquals(1, dr.getIntrinsicWidth());
+ } catch (Exception e) {
+ String file = getAsResourceUri(record.resId).toString();
+ fail("Failed to decode " + file + " with exception " + e);
}
}
}
@@ -851,7 +846,7 @@
null,
};
- final int resId = RECORDS[0].resId;
+ final int resId = getRecord().resId;
ImageDecoder.Source src = mCreators[0].apply(resId);
try {
ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
@@ -897,7 +892,7 @@
int mPosition;
ExceptionStream(int resId, int exceptionPosition) {
- mInputStream = mRes.openRawResource(resId);
+ mInputStream = getResources().openRawResource(resId);
mExceptionPosition = exceptionPosition;
mPosition = 0;
}
@@ -930,7 +925,8 @@
@Test
public void testExceptionInStream() throws Throwable {
InputStream is = new ExceptionStream(R.drawable.animated, 27570);
- ImageDecoder.Source src = ImageDecoder.createSource(mRes, is, Bitmap.DENSITY_NONE);
+ ImageDecoder.Source src = ImageDecoder.createSource(getResources(), is,
+ Bitmap.DENSITY_NONE);
Drawable dr = null;
try {
dr = ImageDecoder.decodeDrawable(src);
@@ -947,7 +943,8 @@
}
@Test
- public void testOnPartialImage() {
+ @Parameters(method = "getRecords")
+ public void testOnPartialImage(Record record) {
class PartialImageCallback implements OnPartialImageListener {
public boolean wasCalled;
public boolean returnDrawable;
@@ -962,46 +959,44 @@
};
final PartialImageCallback callback = new PartialImageCallback();
boolean abortDecode[] = new boolean[] { true, false };
- for (Record record : RECORDS) {
- byte[] bytes = getAsByteArray(record.resId);
- int truncatedLength = bytes.length / 2;
- if (record.mimeType.equals("image/x-ico")
- || record.mimeType.equals("image/x-adobe-dng")
- || record.mimeType.equals("image/heif")) {
- // FIXME (scroggo): Some codecs currently do not support incomplete images.
- continue;
- }
- for (boolean abort : abortDecode) {
- ImageDecoder.Source src = ImageDecoder.createSource(
- ByteBuffer.wrap(bytes, 0, truncatedLength));
- callback.wasCalled = false;
- callback.returnDrawable = !abort;
- callback.source = src;
- try {
- Drawable drawable = ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
- decoder.setOnPartialImageListener(callback);
- });
- assertFalse(abort);
- assertNotNull(drawable);
- assertEquals(record.width, drawable.getIntrinsicWidth());
- assertEquals(record.height, drawable.getIntrinsicHeight());
- } catch (IOException e) {
- assertTrue(abort);
- }
- assertTrue(callback.wasCalled);
- }
-
- // null listener behaves as if onPartialImage returned false.
+ byte[] bytes = getAsByteArray(record.resId);
+ int truncatedLength = bytes.length / 2;
+ if (record.mimeType.equals("image/x-ico")
+ || record.mimeType.equals("image/x-adobe-dng")
+ || record.mimeType.equals("image/heif")) {
+ // FIXME (scroggo): Some codecs currently do not support incomplete images.
+ return;
+ }
+ for (boolean abort : abortDecode) {
ImageDecoder.Source src = ImageDecoder.createSource(
ByteBuffer.wrap(bytes, 0, truncatedLength));
+ callback.wasCalled = false;
+ callback.returnDrawable = !abort;
+ callback.source = src;
try {
- ImageDecoder.decodeDrawable(src);
- fail("Should have thrown an exception!");
- } catch (DecodeException incomplete) {
- // This is the correct behavior.
+ Drawable drawable = ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
+ decoder.setOnPartialImageListener(callback);
+ });
+ assertFalse(abort);
+ assertNotNull(drawable);
+ assertEquals(record.width, drawable.getIntrinsicWidth());
+ assertEquals(record.height, drawable.getIntrinsicHeight());
} catch (IOException e) {
- fail("Failed with exception " + e);
+ assertTrue(abort);
}
+ assertTrue(callback.wasCalled);
+ }
+
+ // null listener behaves as if onPartialImage returned false.
+ ImageDecoder.Source src = ImageDecoder.createSource(
+ ByteBuffer.wrap(bytes, 0, truncatedLength));
+ try {
+ ImageDecoder.decodeDrawable(src);
+ fail("Should have thrown an exception!");
+ } catch (DecodeException incomplete) {
+ // This is the correct behavior.
+ } catch (IOException e) {
+ fail("Failed with exception " + e);
}
}
@@ -1061,7 +1056,7 @@
@Test
public void testGetMutable() {
- final int resId = RECORDS[0].resId;
+ final int resId = getRecord().resId;
ImageDecoder.Source src = mCreators[0].apply(resId);
try {
ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
@@ -1079,7 +1074,8 @@
}
@Test
- public void testMutable() {
+ @Parameters(method = "getRecords")
+ public void testMutable(Record record) {
int allocators[] = new int[] { ImageDecoder.ALLOCATOR_DEFAULT,
ImageDecoder.ALLOCATOR_SOFTWARE,
ImageDecoder.ALLOCATOR_SHARED_MEMORY };
@@ -1099,22 +1095,19 @@
};
HeaderListener l = new HeaderListener();
boolean trueFalse[] = new boolean[] { true, false };
- for (Record record : RECORDS) {
- for (SourceCreator f : mCreators) {
- for (boolean postProcess : trueFalse) {
- for (int allocator : allocators) {
- l.allocator = allocator;
- l.postProcess = postProcess;
+ ImageDecoder.Source src = mCreators[0].apply(record.resId);
+ for (boolean postProcess : trueFalse) {
+ for (int allocator : allocators) {
+ l.allocator = allocator;
+ l.postProcess = postProcess;
- ImageDecoder.Source src = f.apply(record.resId);
- try {
- Bitmap bm = ImageDecoder.decodeBitmap(src, l);
- assertTrue(bm.isMutable());
- assertNotEquals(Bitmap.Config.HARDWARE, bm.getConfig());
- } catch (IOException e) {
- fail("Failed with exception " + e);
- }
- }
+ try {
+ Bitmap bm = ImageDecoder.decodeBitmap(src, l);
+ assertTrue(bm.isMutable());
+ assertNotEquals(Bitmap.Config.HARDWARE, bm.getConfig());
+ } catch (Exception e) {
+ String file = getAsResourceUri(record.resId).toString();
+ fail("Failed to decode " + file + " with exception " + e);
}
}
}
@@ -1122,7 +1115,7 @@
@Test(expected = IllegalStateException.class)
public void testMutableHardware() {
- ImageDecoder.Source src = mCreators[0].apply(RECORDS[0].resId);
+ ImageDecoder.Source src = mCreators[0].apply(getRecord().resId);
try {
ImageDecoder.decodeBitmap(src, (decoder, info, s) -> {
decoder.setMutableRequired(true);
@@ -1135,7 +1128,7 @@
@Test(expected = IllegalStateException.class)
public void testMutableDrawable() {
- ImageDecoder.Source src = mCreators[0].apply(RECORDS[0].resId);
+ ImageDecoder.Source src = mCreators[0].apply(getRecord().resId);
try {
ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
decoder.setMutableRequired(true);
@@ -1205,7 +1198,8 @@
}
@Test
- public void testTargetSize() {
+ @Parameters(method = "getRecords")
+ public void testTargetSize(Record record) {
class ResizeListener implements ImageDecoder.OnHeaderDecodedListener {
public int width;
public int height;
@@ -1218,49 +1212,41 @@
ResizeListener l = new ResizeListener();
float[] scales = new float[] { .0625f, .125f, .25f, .5f, .75f, 1.1f, 2.0f };
- for (Record record : RECORDS) {
- for (SourceCreator f : mCreators) {
- for (int j = 0; j < scales.length; ++j) {
- l.width = (int) (scales[j] * record.width);
- l.height = (int) (scales[j] * record.height);
+ ImageDecoder.Source src = mCreators[0].apply(record.resId);
+ for (int j = 0; j < scales.length; ++j) {
+ l.width = (int) (scales[j] * record.width);
+ l.height = (int) (scales[j] * record.height);
- ImageDecoder.Source src = f.apply(record.resId);
+ try {
+ Drawable drawable = ImageDecoder.decodeDrawable(src, l);
+ assertEquals(l.width, drawable.getIntrinsicWidth());
+ assertEquals(l.height, drawable.getIntrinsicHeight());
- try {
- Drawable drawable = ImageDecoder.decodeDrawable(src, l);
- assertEquals(l.width, drawable.getIntrinsicWidth());
- assertEquals(l.height, drawable.getIntrinsicHeight());
-
- src = f.apply(record.resId);
- Bitmap bm = ImageDecoder.decodeBitmap(src, l);
- assertEquals(l.width, bm.getWidth());
- assertEquals(l.height, bm.getHeight());
- } catch (IOException e) {
- fail("Failed " + getAsResourceUri(record.resId) + " with exception " + e);
- }
- }
-
- try {
- // Arbitrary square.
- l.width = 50;
- l.height = 50;
- ImageDecoder.Source src = f.apply(record.resId);
- Drawable drawable = ImageDecoder.decodeDrawable(src, l);
- assertEquals(50, drawable.getIntrinsicWidth());
- assertEquals(50, drawable.getIntrinsicHeight());
-
- // Swap width and height, for different scales.
- l.height = record.width;
- l.width = record.height;
- src = f.apply(record.resId);
- drawable = ImageDecoder.decodeDrawable(src, l);
- assertEquals(record.height, drawable.getIntrinsicWidth());
- assertEquals(record.width, drawable.getIntrinsicHeight());
- } catch (IOException e) {
- fail("Failed with exception " + e);
- }
+ Bitmap bm = ImageDecoder.decodeBitmap(src, l);
+ assertEquals(l.width, bm.getWidth());
+ assertEquals(l.height, bm.getHeight());
+ } catch (IOException e) {
+ fail("Failed " + getAsResourceUri(record.resId) + " with exception " + e);
}
}
+
+ try {
+ // Arbitrary square.
+ l.width = 50;
+ l.height = 50;
+ Drawable drawable = ImageDecoder.decodeDrawable(src, l);
+ assertEquals(50, drawable.getIntrinsicWidth());
+ assertEquals(50, drawable.getIntrinsicHeight());
+
+ // Swap width and height, for different scales.
+ l.height = record.width;
+ l.width = record.height;
+ drawable = ImageDecoder.decodeDrawable(src, l);
+ assertEquals(record.height, drawable.getIntrinsicWidth());
+ assertEquals(record.width, drawable.getIntrinsicHeight());
+ } catch (IOException e) {
+ fail("Failed with exception " + e);
+ }
}
@Test
@@ -1332,7 +1318,7 @@
@Test
public void testGetCrop() {
- final int resId = RECORDS[0].resId;
+ final int resId = getRecord().resId;
ImageDecoder.Source src = mCreators[0].apply(resId);
try {
ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
@@ -1352,7 +1338,8 @@
}
@Test
- public void testCrop() {
+ @Parameters(method = "getRecords")
+ public void testCrop(Record record) {
class Listener implements ImageDecoder.OnHeaderDecodedListener {
public boolean doScale;
public boolean requireSoftware;
@@ -1381,22 +1368,20 @@
};
Listener l = new Listener();
boolean trueFalse[] = new boolean[] { true, false };
- for (Record record : RECORDS) {
- for (SourceCreator f : mCreators) {
- for (boolean doScale : trueFalse) {
- l.doScale = doScale;
- for (boolean requireSoftware : trueFalse) {
- l.requireSoftware = requireSoftware;
- ImageDecoder.Source src = f.apply(record.resId);
+ for (SourceCreator f : mCreators) {
+ for (boolean doScale : trueFalse) {
+ l.doScale = doScale;
+ for (boolean requireSoftware : trueFalse) {
+ l.requireSoftware = requireSoftware;
+ ImageDecoder.Source src = f.apply(record.resId);
- try {
- Drawable drawable = ImageDecoder.decodeDrawable(src, l);
- assertEquals(l.cropRect.width(), drawable.getIntrinsicWidth());
- assertEquals(l.cropRect.height(), drawable.getIntrinsicHeight());
- } catch (IOException e) {
- fail("Failed " + getAsResourceUri(record.resId) +
- " with exception " + e);
- }
+ try {
+ Drawable drawable = ImageDecoder.decodeDrawable(src, l);
+ assertEquals(l.cropRect.width(), drawable.getIntrinsicWidth());
+ assertEquals(l.cropRect.height(), drawable.getIntrinsicHeight());
+ } catch (IOException e) {
+ fail("Failed " + getAsResourceUri(record.resId)
+ + " with exception " + e);
}
}
}
@@ -1753,7 +1738,7 @@
@Test
public void testGetConserveMemory() {
- final int resId = RECORDS[0].resId;
+ final int resId = getRecord().resId;
ImageDecoder.Source src = mCreators[0].apply(resId);
try {
ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
@@ -1914,7 +1899,7 @@
reference = null;
}
Uri uri = getAsResourceUri(resId);
- ImageDecoder.Source src = ImageDecoder.createSource(mContentResolver, uri);
+ ImageDecoder.Source src = ImageDecoder.createSource(getContentResolver(), uri);
try {
Bitmap bm = ImageDecoder.decodeBitmap(src, (decoder, info, s) -> {
// Use software allocator so we can compare.
@@ -1945,107 +1930,105 @@
private interface ByteBufferSupplier extends Supplier<ByteBuffer> {};
@Test
- public void testOffsetByteArray() {
- for (Record record : RECORDS) {
- int offset = 10;
- int extra = 15;
- byte[] array = getAsByteArray(record.resId, offset, extra);
- int length = array.length - extra - offset;
- // Used for SourceCreators that set both a position and an offset.
- int myOffset = 3;
- int myPosition = 7;
- assertEquals(offset, myOffset + myPosition);
+ @Parameters(method = "getRecords")
+ public void testOffsetByteArray(Record record) {
+ int offset = 10;
+ int extra = 15;
+ byte[] array = getAsByteArray(record.resId, offset, extra);
+ int length = array.length - extra - offset;
+ // Used for SourceCreators that set both a position and an offset.
+ int myOffset = 3;
+ int myPosition = 7;
+ assertEquals(offset, myOffset + myPosition);
- ByteBufferSupplier[] suppliers = new ByteBufferSupplier[] {
- // Internally, this gives the buffer a position, but not an offset.
- () -> ByteBuffer.wrap(array, offset, length),
+ ByteBufferSupplier[] suppliers = new ByteBufferSupplier[] {
+ // Internally, this gives the buffer a position, but not an offset.
+ () -> ByteBuffer.wrap(array, offset, length),
+ // Same, but make it readOnly to ensure that we test the
+ // ByteBufferSource rather than the ByteArraySource.
+ () -> ByteBuffer.wrap(array, offset, length).asReadOnlyBuffer(),
+ () -> {
+ // slice() to give the buffer an offset.
+ ByteBuffer buf = ByteBuffer.wrap(array, 0, array.length - extra);
+ buf.position(offset);
+ return buf.slice();
+ },
+ () -> {
// Same, but make it readOnly to ensure that we test the
// ByteBufferSource rather than the ByteArraySource.
- () -> ByteBuffer.wrap(array, offset, length).asReadOnlyBuffer(),
- () -> {
- // slice() to give the buffer an offset.
- ByteBuffer buf = ByteBuffer.wrap(array, 0, array.length - extra);
- buf.position(offset);
- return buf.slice();
- },
- () -> {
- // Same, but make it readOnly to ensure that we test the
- // ByteBufferSource rather than the ByteArraySource.
- ByteBuffer buf = ByteBuffer.wrap(array, 0, array.length - extra);
- buf.position(offset);
- return buf.slice().asReadOnlyBuffer();
- },
- () -> {
- // Use both a position and an offset.
- ByteBuffer buf = ByteBuffer.wrap(array, myOffset,
+ ByteBuffer buf = ByteBuffer.wrap(array, 0, array.length - extra);
+ buf.position(offset);
+ return buf.slice().asReadOnlyBuffer();
+ },
+ () -> {
+ // Use both a position and an offset.
+ ByteBuffer buf = ByteBuffer.wrap(array, myOffset,
array.length - extra - myOffset);
- buf = buf.slice();
- buf.position(myPosition);
- return buf;
- },
- () -> {
- // Same, as readOnly.
- ByteBuffer buf = ByteBuffer.wrap(array, myOffset,
- array.length - extra - myOffset);
- buf = buf.slice();
- buf.position(myPosition);
- return buf.asReadOnlyBuffer();
- },
- () -> {
- // Direct ByteBuffer with a position.
- ByteBuffer buf = ByteBuffer.allocateDirect(array.length);
- buf.put(array);
- buf.position(offset);
- return buf;
- },
- () -> {
- // Sliced direct ByteBuffer, for an offset.
- ByteBuffer buf = ByteBuffer.allocateDirect(array.length);
- buf.put(array);
- buf.position(offset);
- return buf.slice();
- },
- () -> {
- // Direct ByteBuffer with position and offset.
- ByteBuffer buf = ByteBuffer.allocateDirect(array.length);
- buf.put(array);
- buf.position(myOffset);
- buf = buf.slice();
- buf.position(myPosition);
- return buf;
- },
- };
- for (int i = 0; i < suppliers.length; ++i) {
- ByteBuffer buffer = suppliers[i].get();
- final int position = buffer.position();
- ImageDecoder.Source src = ImageDecoder.createSource(buffer);
- try {
- Drawable drawable = ImageDecoder.decodeDrawable(src);
- assertNotNull(drawable);
- } catch (IOException e) {
- fail("Failed with exception " + e);
- }
- assertEquals("Mismatch for supplier " + i,
- position, buffer.position());
- }
- }
- }
-
- @Test
- public void testResourceSource() {
- for (Record record : RECORDS) {
- ImageDecoder.Source src = ImageDecoder.createSource(mRes, record.resId);
+ buf = buf.slice();
+ buf.position(myPosition);
+ return buf;
+ },
+ () -> {
+ // Same, as readOnly.
+ ByteBuffer buf = ByteBuffer.wrap(array, myOffset,
+ array.length - extra - myOffset);
+ buf = buf.slice();
+ buf.position(myPosition);
+ return buf.asReadOnlyBuffer();
+ },
+ () -> {
+ // Direct ByteBuffer with a position.
+ ByteBuffer buf = ByteBuffer.allocateDirect(array.length);
+ buf.put(array);
+ buf.position(offset);
+ return buf;
+ },
+ () -> {
+ // Sliced direct ByteBuffer, for an offset.
+ ByteBuffer buf = ByteBuffer.allocateDirect(array.length);
+ buf.put(array);
+ buf.position(offset);
+ return buf.slice();
+ },
+ () -> {
+ // Direct ByteBuffer with position and offset.
+ ByteBuffer buf = ByteBuffer.allocateDirect(array.length);
+ buf.put(array);
+ buf.position(myOffset);
+ buf = buf.slice();
+ buf.position(myPosition);
+ return buf;
+ },
+ };
+ for (int i = 0; i < suppliers.length; ++i) {
+ ByteBuffer buffer = suppliers[i].get();
+ final int position = buffer.position();
+ ImageDecoder.Source src = ImageDecoder.createSource(buffer);
try {
Drawable drawable = ImageDecoder.decodeDrawable(src);
assertNotNull(drawable);
} catch (IOException e) {
- fail("Failed " + getAsResourceUri(record.resId) + " with " + e);
+ fail("Failed with exception " + e);
}
+ assertEquals("Mismatch for supplier " + i,
+ position, buffer.position());
+ }
+ }
+
+ @Test
+ @Parameters(method = "getRecords")
+ public void testResourceSource(Record record) {
+ ImageDecoder.Source src = ImageDecoder.createSource(getResources(), record.resId);
+ try {
+ Drawable drawable = ImageDecoder.decodeDrawable(src);
+ assertNotNull(drawable);
+ } catch (IOException e) {
+ fail("Failed " + getAsResourceUri(record.resId) + " with " + e);
}
}
private BitmapDrawable decodeBitmapDrawable(int resId) {
- ImageDecoder.Source src = ImageDecoder.createSource(mRes, resId);
+ ImageDecoder.Source src = ImageDecoder.createSource(getResources(), resId);
try {
Drawable drawable = ImageDecoder.decodeDrawable(src);
assertNotNull(drawable);
@@ -2058,54 +2041,54 @@
}
@Test
- public void testUpscale() {
- final int originalDensity = mRes.getDisplayMetrics().densityDpi;
+ @Parameters(method = "getRecords")
+ public void testUpscale(Record record) {
+ Resources res = getResources();
+ final int originalDensity = res.getDisplayMetrics().densityDpi;
try {
- for (Record record : RECORDS) {
- final int resId = record.resId;
+ final int resId = record.resId;
- // Set a high density. This will result in a larger drawable, but
- // not a larger Bitmap.
- mRes.getDisplayMetrics().densityDpi = DisplayMetrics.DENSITY_XXXHIGH;
- BitmapDrawable drawable = decodeBitmapDrawable(resId);
+ // Set a high density. This will result in a larger drawable, but
+ // not a larger Bitmap.
+ res.getDisplayMetrics().densityDpi = DisplayMetrics.DENSITY_XXXHIGH;
+ BitmapDrawable drawable = decodeBitmapDrawable(resId);
- Bitmap bm = drawable.getBitmap();
- assertEquals(record.width, bm.getWidth());
- assertEquals(record.height, bm.getHeight());
+ Bitmap bm = drawable.getBitmap();
+ assertEquals(record.width, bm.getWidth());
+ assertEquals(record.height, bm.getHeight());
- assertTrue(drawable.getIntrinsicWidth() > record.width);
- assertTrue(drawable.getIntrinsicHeight() > record.height);
+ assertTrue(drawable.getIntrinsicWidth() > record.width);
+ assertTrue(drawable.getIntrinsicHeight() > record.height);
- // Set a low density. This will result in a smaller drawable and
- // Bitmap, unless the true density is DENSITY_MEDIUM, which matches
- // the density of the asset.
- mRes.getDisplayMetrics().densityDpi = DisplayMetrics.DENSITY_LOW;
- drawable = decodeBitmapDrawable(resId);
- bm = drawable.getBitmap();
+ // Set a low density. This will result in a smaller drawable and
+ // Bitmap, unless the true density is DENSITY_MEDIUM, which matches
+ // the density of the asset.
+ res.getDisplayMetrics().densityDpi = DisplayMetrics.DENSITY_LOW;
+ drawable = decodeBitmapDrawable(resId);
+ bm = drawable.getBitmap();
- if (originalDensity == DisplayMetrics.DENSITY_MEDIUM) {
- // Although we've modified |densityDpi|, ImageDecoder knows the
- // true density matches the asset, so it will not downscale at
- // decode time.
- assertEquals(bm.getWidth(), record.width);
- assertEquals(bm.getHeight(), record.height);
+ if (originalDensity == DisplayMetrics.DENSITY_MEDIUM) {
+ // Although we've modified |densityDpi|, ImageDecoder knows the
+ // true density matches the asset, so it will not downscale at
+ // decode time.
+ assertEquals(bm.getWidth(), record.width);
+ assertEquals(bm.getHeight(), record.height);
- // The drawable should still be smaller.
- assertTrue(bm.getWidth() > drawable.getIntrinsicWidth());
- assertTrue(bm.getHeight() > drawable.getIntrinsicHeight());
- } else {
- // The bitmap is scaled down at decode time, so it matches the
- // drawable size, and is smaller than the original.
- assertTrue(bm.getWidth() < record.width);
- assertTrue(bm.getHeight() < record.height);
+ // The drawable should still be smaller.
+ assertTrue(bm.getWidth() > drawable.getIntrinsicWidth());
+ assertTrue(bm.getHeight() > drawable.getIntrinsicHeight());
+ } else {
+ // The bitmap is scaled down at decode time, so it matches the
+ // drawable size, and is smaller than the original.
+ assertTrue(bm.getWidth() < record.width);
+ assertTrue(bm.getHeight() < record.height);
- assertEquals(bm.getWidth(), drawable.getIntrinsicWidth());
- assertEquals(bm.getHeight(), drawable.getIntrinsicHeight());
- }
+ assertEquals(bm.getWidth(), drawable.getIntrinsicWidth());
+ assertEquals(bm.getHeight(), drawable.getIntrinsicHeight());
}
} finally {
- mRes.getDisplayMetrics().densityDpi = originalDensity;
+ res.getDisplayMetrics().densityDpi = originalDensity;
}
}
@@ -2148,7 +2131,8 @@
}
}
- private static final AssetRecord[] ASSETS = new AssetRecord[] {
+ private Object [] getAssetRecords() {
+ return new Object [] {
// A null ColorSpace means that the color space is "Unknown".
new AssetRecord("almost-red-adobe.png", 1, 1, false, false, null),
new AssetRecord("green-p3.png", 64, 64, false, false,
@@ -2166,51 +2150,82 @@
ColorSpace.get(ColorSpace.Named.DISPLAY_P3)),
new AssetRecord("grayscale-linearSrgb.png", 32, 32, false, true,
ColorSpace.get(ColorSpace.Named.LINEAR_SRGB)),
- };
+ };
+ }
@Test
- public void testAssetSource() {
- AssetManager assets = mRes.getAssets();
- for (AssetRecord record : ASSETS) {
- ImageDecoder.Source src = ImageDecoder.createSource(assets, record.name);
+ @Parameters(method = "getAssetRecords")
+ public void testAssetSource(AssetRecord record) {
+ AssetManager assets = getResources().getAssets();
+ ImageDecoder.Source src = ImageDecoder.createSource(assets, record.name);
+ try {
+ Bitmap bm = ImageDecoder.decodeBitmap(src, (decoder, info, s) -> {
+ if (record.isF16) {
+ // CTS infrastructure fails to create F16 HARDWARE Bitmaps, so this
+ // switches to using software.
+ decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+ }
+
+ record.checkColorSpace(null, info.getColorSpace());
+ });
+ assertEquals(record.name, record.width, bm.getWidth());
+ assertEquals(record.name, record.height, bm.getHeight());
+ record.checkColorSpace(null, bm.getColorSpace());
+ } catch (IOException e) {
+ fail("Failed to decode asset " + record.name + " with " + e);
+ }
+ }
+
+ @Test
+ @Parameters(method = "getAssetRecords")
+ public void testTargetColorSpace(AssetRecord record) {
+ AssetManager assets = getResources().getAssets();
+ ImageDecoder.Source src = ImageDecoder.createSource(assets, record.name);
+ for (ColorSpace cs : BitmapTest.getRgbColorSpaces()) {
try {
Bitmap bm = ImageDecoder.decodeBitmap(src, (decoder, info, s) -> {
- if (record.isF16) {
- // CTS infrastructure fails to create F16 HARDWARE Bitmaps, so this
- // switches to using software.
+ if (record.isF16 || isExtended(cs)) {
+ // CTS infrastructure and some devices fail to create F16
+ // HARDWARE Bitmaps, so this switches to using software.
decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
}
-
- record.checkColorSpace(null, info.getColorSpace());
+ decoder.setTargetColorSpace(cs);
});
- assertEquals(record.name, record.width, bm.getWidth());
- assertEquals(record.name, record.height, bm.getHeight());
- record.checkColorSpace(null, bm.getColorSpace());
+ record.checkColorSpace(cs, bm.getColorSpace());
} catch (IOException e) {
- fail("Failed to decode asset " + record.name + " with " + e);
+ fail("Failed to decode asset " + record.name + " to " + cs + " with " + e);
}
}
}
@Test
- public void testTargetColorSpace() {
- AssetManager assets = mRes.getAssets();
- for (AssetRecord record : ASSETS) {
- ImageDecoder.Source src = ImageDecoder.createSource(assets, record.name);
- for (ColorSpace cs : BitmapTest.getRgbColorSpaces()) {
- try {
- Bitmap bm = ImageDecoder.decodeBitmap(src, (decoder, info, s) -> {
- if (record.isF16) {
- // CTS infrastructure fails to create F16 HARDWARE Bitmaps, so this
- // switches to using software.
- decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
- }
- decoder.setTargetColorSpace(cs);
- });
- record.checkColorSpace(cs, bm.getColorSpace());
- } catch (IOException e) {
- fail("Failed to decode asset " + record.name + " to " + cs + " with " + e);
+ @Parameters(method = "getAssetRecords")
+ public void testTargetColorSpaceNoF16HARDWARE(AssetRecord record) {
+ final ColorSpace EXTENDED_SRGB = ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB);
+ final ColorSpace LINEAR_EXTENDED_SRGB = ColorSpace.get(
+ ColorSpace.Named.LINEAR_EXTENDED_SRGB);
+ AssetManager assets = getResources().getAssets();
+ ImageDecoder.Source src = ImageDecoder.createSource(assets, record.name);
+ for (ColorSpace cs : new ColorSpace[] { EXTENDED_SRGB, LINEAR_EXTENDED_SRGB }) {
+ try {
+ Bitmap bm = ImageDecoder.decodeBitmap(src, (decoder, info, s) -> {
+ decoder.setTargetColorSpace(cs);
+ });
+ // If the ColorSpace does not match the request, it should be because
+ // F16 + HARDWARE is not supported. In that case, it should match the non-
+ // EXTENDED variant.
+ ColorSpace actual = bm.getColorSpace();
+ if (actual != cs) {
+ assertEquals(BitmapTest.ANDROID_BITMAP_FORMAT_RGBA_8888,
+ BitmapTest.nGetFormat(bm));
+ if (cs == EXTENDED_SRGB) {
+ assertSame(ColorSpace.get(ColorSpace.Named.SRGB), actual);
+ } else {
+ assertSame(ColorSpace.get(ColorSpace.Named.LINEAR_SRGB), actual);
+ }
}
+ } catch (IOException e) {
+ fail("Failed to decode asset " + record.name + " to " + cs + " with " + e);
}
}
}
@@ -2221,84 +2236,83 @@
}
@Test
- public void testTargetColorSpaceUpconvert() {
+ @Parameters(method = "getAssetRecords")
+ public void testTargetColorSpaceUpconvert(AssetRecord record) {
// Verify that decoding an asset to EXTENDED upconverts to F16.
- AssetManager assets = mRes.getAssets();
+ AssetManager assets = getResources().getAssets();
boolean[] trueFalse = new boolean[] { true, false };
final ColorSpace linearExtended = ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB);
final ColorSpace linearSrgb = ColorSpace.get(ColorSpace.Named.LINEAR_SRGB);
- for (AssetRecord record : ASSETS) {
- if (record.isF16) {
- // These assets decode to F16 by default.
- continue;
- }
- ImageDecoder.Source src = ImageDecoder.createSource(assets, record.name);
- for (ColorSpace cs : BitmapTest.getRgbColorSpaces()) {
- for (boolean alphaMask : trueFalse) {
- try {
- Bitmap bm = ImageDecoder.decodeBitmap(src, (decoder, info, s) -> {
- // Force software so we can check the Config.
- decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
- decoder.setTargetColorSpace(cs);
- // This has no effect on non-gray assets.
- decoder.setDecodeAsAlphaMaskEnabled(alphaMask);
- });
+ if (record.isF16) {
+ // These assets decode to F16 by default.
+ return;
+ }
+ ImageDecoder.Source src = ImageDecoder.createSource(assets, record.name);
+ for (ColorSpace cs : BitmapTest.getRgbColorSpaces()) {
+ for (boolean alphaMask : trueFalse) {
+ try {
+ Bitmap bm = ImageDecoder.decodeBitmap(src, (decoder, info, s) -> {
+ // Force software so we can check the Config.
+ decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+ decoder.setTargetColorSpace(cs);
+ // This has no effect on non-gray assets.
+ decoder.setDecodeAsAlphaMaskEnabled(alphaMask);
+ });
- if (record.isGray && alphaMask) {
- assertSame(Bitmap.Config.ALPHA_8, bm.getConfig());
- assertNull(bm.getColorSpace());
+ if (record.isGray && alphaMask) {
+ assertSame(Bitmap.Config.ALPHA_8, bm.getConfig());
+ assertNull(bm.getColorSpace());
+ } else {
+ assertSame(cs, bm.getColorSpace());
+ if (isExtended(cs)) {
+ assertSame(Bitmap.Config.RGBA_F16, bm.getConfig());
} else {
- assertSame(cs, bm.getColorSpace());
- if (isExtended(cs)) {
- assertSame(Bitmap.Config.RGBA_F16, bm.getConfig());
+ assertSame(Bitmap.Config.ARGB_8888, bm.getConfig());
+ }
+ }
+ } catch (IOException e) {
+ fail("Failed to decode asset " + record.name + " to " + cs + " with " + e);
+ }
+
+ // Using MEMORY_POLICY_LOW_RAM prevents upconverting.
+ try {
+ Bitmap bm = ImageDecoder.decodeBitmap(src, (decoder, info, s) -> {
+ // Force software so we can check the Config.
+ decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+ decoder.setTargetColorSpace(cs);
+ decoder.setMemorySizePolicy(ImageDecoder.MEMORY_POLICY_LOW_RAM);
+ // This has no effect on non-gray assets.
+ decoder.setDecodeAsAlphaMaskEnabled(alphaMask);
+ });
+
+ assertNotEquals(Bitmap.Config.RGBA_F16, bm.getConfig());
+
+ if (record.isGray && alphaMask) {
+ assertSame(Bitmap.Config.ALPHA_8, bm.getConfig());
+ assertNull(bm.getColorSpace());
+ } else {
+ ColorSpace actual = bm.getColorSpace();
+ if (isExtended(cs)) {
+ if (cs == ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB)) {
+ assertSame(ColorSpace.get(ColorSpace.Named.SRGB), actual);
+ } else if (cs == linearExtended) {
+ assertSame(linearSrgb, actual);
} else {
+ fail("Test error: did isExtended() change?");
+ }
+ } else {
+ assertSame(cs, actual);
+ if (bm.hasAlpha()) {
assertSame(Bitmap.Config.ARGB_8888, bm.getConfig());
- }
- }
- } catch (IOException e) {
- fail("Failed to decode asset " + record.name + " to " + cs + " with " + e);
- }
-
- // Using MEMORY_POLICY_LOW_RAM prevents upconverting.
- try {
- Bitmap bm = ImageDecoder.decodeBitmap(src, (decoder, info, s) -> {
- // Force software so we can check the Config.
- decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
- decoder.setTargetColorSpace(cs);
- decoder.setMemorySizePolicy(ImageDecoder.MEMORY_POLICY_LOW_RAM);
- // This has no effect on non-gray assets.
- decoder.setDecodeAsAlphaMaskEnabled(alphaMask);
- });
-
- assertNotEquals(Bitmap.Config.RGBA_F16, bm.getConfig());
-
- if (record.isGray && alphaMask) {
- assertSame(Bitmap.Config.ALPHA_8, bm.getConfig());
- assertNull(bm.getColorSpace());
- } else {
- ColorSpace actual = bm.getColorSpace();
- if (isExtended(cs)) {
- if (cs == ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB)) {
- assertSame(ColorSpace.get(ColorSpace.Named.SRGB), actual);
- } else if (cs == linearExtended) {
- assertSame(linearSrgb, actual);
- } else {
- fail("Test error: did isExtended() change?");
- }
} else {
- assertSame(cs, actual);
- if (bm.hasAlpha()) {
- assertSame(Bitmap.Config.ARGB_8888, bm.getConfig());
- } else {
- assertSame(Bitmap.Config.RGB_565, bm.getConfig());
- }
+ assertSame(Bitmap.Config.RGB_565, bm.getConfig());
}
}
- } catch (IOException e) {
- fail("Failed to decode asset " + record.name
- + " with MEMORY_POLICY_LOW_RAM to " + cs + " with " + e);
}
+ } catch (IOException e) {
+ fail("Failed to decode asset " + record.name
+ + " with MEMORY_POLICY_LOW_RAM to " + cs + " with " + e);
}
}
}
@@ -2380,52 +2394,92 @@
}
}
+
+ private Object[] crossProduct(Object[] a, Object[] b) {
+ final int length = a.length * b.length;
+ Object[] ret = new Object[length];
+ for (int i = 0; i < a.length; i++) {
+ for (int j = 0; j < b.length; j++) {
+ int index = i * b.length + j;
+ assertNull(ret[index]);
+ ret[index] = new Object[] { a[i], b[j] };
+ }
+ }
+ return ret;
+ }
+
+ private Object[] getRecordsAsSources() {
+ return crossProduct(getRecords(), mCreators);
+ }
+
+
@Test
@LargeTest
- public void testReuse() {
- for (Record record : RECORDS) {
- if (record.mimeType.equals("image/heif")) {
- // This image takes too long for this test.
- continue;
- }
-
- String name = getAsResourceUri(record.resId).toString();
- for (SourceCreator f : mCreators) {
- ImageDecoder.Source src = f.apply(record.resId);
- testReuse(src, name);
- }
-
- {
- ImageDecoder.Source src = ImageDecoder.createSource(mRes, record.resId);
- testReuse(src, name);
- }
-
- for (UriCreator f : mUriCreators) {
- Uri uri = f.apply(record.resId);
- ImageDecoder.Source src = ImageDecoder.createSource(mContentResolver, uri);
- testReuse(src, uri.toString());
- }
-
- {
- ImageDecoder.Source src = ImageDecoder.createSource(getAsFile(record.resId));
- testReuse(src, name);
- }
+ @Parameters(method = "getRecordsAsSources")
+ public void testReuse(Record record, SourceCreator f) {
+ if (record.mimeType.equals("image/heif")) {
+ // This image takes too long for this test.
+ return;
}
- AssetManager assets = mRes.getAssets();
- for (AssetRecord record : ASSETS) {
- ImageDecoder.Source src = ImageDecoder.createSource(assets, record.name);
- testReuse(src, record.name);
+ String name = getAsResourceUri(record.resId).toString();
+ ImageDecoder.Source src = f.apply(record.resId);
+ testReuse(src, name);
+ }
+
+ @Test
+ @Parameters(method = "getRecords")
+ public void testReuse2(Record record) {
+ if (record.mimeType.equals("image/heif")) {
+ // This image takes too long for this test.
+ return;
}
+ String name = getAsResourceUri(record.resId).toString();
+ ImageDecoder.Source src = ImageDecoder.createSource(getResources(), record.resId);
+ testReuse(src, name);
+ src = ImageDecoder.createSource(getAsFile(record.resId));
+ testReuse(src, name);
+ }
+
+ private Object[] getRecordsAsUris() {
+ return crossProduct(getRecords(), mUriCreators);
+ }
+
+
+ @Test
+ @Parameters(method = "getRecordsAsUris")
+ public void testReuseUri(Record record, UriCreator f) {
+ if (record.mimeType.equals("image/heif")) {
+ // This image takes too long for this test.
+ return;
+ }
+
+ Uri uri = f.apply(record.resId);
+ ImageDecoder.Source src = ImageDecoder.createSource(getContentResolver(), uri);
+ testReuse(src, uri.toString());
+ }
+
+ @Test
+ @Parameters(method = "getAssetRecords")
+ public void testReuseAssetRecords(AssetRecord record) {
+ AssetManager assets = getResources().getAssets();
+ ImageDecoder.Source src = ImageDecoder.createSource(assets, record.name);
+ testReuse(src, record.name);
+ }
+
+
+ @Test
+ public void testReuseAnimated() {
ImageDecoder.Source src = mCreators[0].apply(R.drawable.animated);
testReuse(src, "animated.gif");
}
@Test
public void testIsMimeTypeSupported() {
- for (Record record : RECORDS) {
+ for (Object r : getRecords()) {
+ Record record = (Record) r;
assertTrue(record.mimeType, ImageDecoder.isMimeTypeSupported(record.mimeType));
}
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedImageDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedImageDrawableTest.java
index 3f5c692..24f44f2 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedImageDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedImageDrawableTest.java
@@ -56,7 +56,10 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
import java.util.function.BiFunction;
@RunWith(AndroidJUnit4.class)
@@ -670,4 +673,91 @@
aid = (AnimatedImageDrawable) drawable;
assertEquals(AnimatedImageDrawable.REPEAT_INFINITE, aid.getRepeatCount());
}
+
+ // Verify that decoding on the AnimatedImageThread works.
+ private void decodeInBackground(AnimatedImageDrawable drawable) throws Throwable {
+ final Callback cb = new Callback(drawable);
+ mActivityRule.runOnUiThread(() -> {
+ setContentView(drawable);
+ drawable.registerAnimationCallback(cb);
+ drawable.start();
+ });
+
+ // The first frame was decoded in the thread that created the
+ // AnimatedImageDrawable. Wait long enough to decode further threads on
+ // the AnimatedImageThread, which was not created with a JNI interface
+ // pointer.
+ cb.waitForStart();
+ cb.waitForEnd(DURATION * 2);
+ }
+
+ @Test
+ public void testInputStream() throws Throwable {
+ try (InputStream in = mRes.openRawResource(R.drawable.animated)) {
+ ImageDecoder.Source src =
+ ImageDecoder.createSource(mRes, in, Bitmap.DENSITY_NONE);
+ AnimatedImageDrawable drawable =
+ (AnimatedImageDrawable) ImageDecoder.decodeDrawable(src);
+ decodeInBackground(drawable);
+ }
+
+ }
+
+ private byte[] getAsByteArray() {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ try (InputStream in = mRes.openRawResource(RES_ID)) {
+ byte[] buf = new byte[4096];
+ int bytesRead;
+ while ((bytesRead = in.read(buf)) != -1) {
+ outputStream.write(buf, 0, bytesRead);
+ }
+ } catch (IOException e) {
+ fail("Failed to read resource: " + e);
+ }
+
+ return outputStream.toByteArray();
+ }
+
+ private ByteBuffer getAsDirectByteBuffer() {
+ byte[] array = getAsByteArray();
+ ByteBuffer byteBuffer = ByteBuffer.allocateDirect(array.length);
+ byteBuffer.put(array);
+ byteBuffer.position(0);
+ return byteBuffer;
+ }
+
+ private AnimatedImageDrawable createFromByteBuffer(ByteBuffer byteBuffer) {
+ ImageDecoder.Source src = ImageDecoder.createSource(byteBuffer);
+ try {
+ return (AnimatedImageDrawable) ImageDecoder.decodeDrawable(src);
+ } catch (IOException e) {
+ fail("Failed to create decoder: " + e);
+ return null;
+ }
+ }
+
+ @Test
+ public void testByteBuffer() throws Throwable {
+ // Natively, this tests ByteArrayStream.
+ byte[] array = getAsByteArray();
+ ByteBuffer byteBuffer = ByteBuffer.wrap(array);
+ final AnimatedImageDrawable drawable = createFromByteBuffer(byteBuffer);
+ decodeInBackground(drawable);
+ }
+
+ @Test
+ public void testReadOnlyByteBuffer() throws Throwable {
+ // Natively, this tests ByteBufferStream.
+ byte[] array = getAsByteArray();
+ ByteBuffer byteBuffer = ByteBuffer.wrap(array).asReadOnlyBuffer();
+ final AnimatedImageDrawable drawable = createFromByteBuffer(byteBuffer);
+ decodeInBackground(drawable);
+ }
+
+ @Test
+ public void testDirectByteBuffer() throws Throwable {
+ ByteBuffer byteBuffer = getAsDirectByteBuffer();
+ final AnimatedImageDrawable drawable = createFromByteBuffer(byteBuffer);
+ decodeInBackground(drawable);
+ }
}
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/GradientDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/GradientDrawableTest.java
index 8650f00..eb92789 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/GradientDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/GradientDrawableTest.java
@@ -48,6 +48,7 @@
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.xmlpull.v1.XmlPullParser;
@@ -750,6 +751,28 @@
assertEquals(Orientation.TOP_BOTTOM, drawable.getOrientation());
}
+
+ @Ignore("Disabling temporarily while actual fix to maintain behavioral differences of "
+ + "orientation xml and programmatically defined GradientDrawables")
+ @Test
+ public void testGradientNoAngle() {
+ // Verify that the default orientation for a GradientDrawable defined in xml is
+ // LEFT_RIGHT. This differs from the default behavior of programmatically defined
+ // GradientDrawables
+ final Context context = InstrumentationRegistry.getTargetContext();
+ GradientDrawable drawable = (GradientDrawable)
+ context.getDrawable(R.drawable.gradientdrawable_no_angle);
+ assertEquals(Orientation.LEFT_RIGHT, drawable.getOrientation());
+ }
+
+ @Test
+ public void testDynamicGradientDefaultOrientation() {
+ // Verify that the default orientation for a programmatically defined GradientDrawable is
+ // TOP_BOTTOM. This differs from the default behavior of xml inflated GradientDrawables
+ // that default to LEFT_RIGHT
+ assertEquals(Orientation.TOP_BOTTOM, new GradientDrawable().getOrientation());
+ }
+
@Test
public void testGradientDrawableOrientationConstructor() {
GradientDrawable drawable = new GradientDrawable(Orientation.TOP_BOTTOM, null);
diff --git a/tests/tests/hardware/res/raw/asus_gamepad_register.json b/tests/tests/hardware/res/raw/asus_gamepad_register.json
index cfd71eb..dd422a4 100644
--- a/tests/tests/hardware/res/raw/asus_gamepad_register.json
+++ b/tests/tests/hardware/res/raw/asus_gamepad_register.json
@@ -1,9 +1,9 @@
{
"id": 1,
"command": "register",
- "name": "Odie (Test)",
- "vid": 0x18d1,
- "pid": 0x2c40,
+ "name": "Asus Gamepad (Test)",
+ "vid": 0x0b05,
+ "pid": 0x4500,
"descriptor": [0x05, 0x01, 0x09, 0x05, 0xa1, 0x01, 0x85, 0x01, 0x05, 0x09, 0x0a, 0x01, 0x00,
0x0a, 0x02, 0x00, 0x0a, 0x04, 0x00, 0x0a, 0x05, 0x00, 0x0a, 0x07, 0x00, 0x0a, 0x08, 0x00,
0x0a, 0x0e, 0x00, 0x0a, 0x0f, 0x00, 0x0a, 0x0d, 0x00, 0x05, 0x0c, 0x0a, 0x24, 0x02, 0x0a,
diff --git a/tests/tests/jni/OWNERS b/tests/tests/jni/OWNERS
new file mode 100644
index 0000000..29fea99
--- /dev/null
+++ b/tests/tests/jni/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 86431
+include /hostsidetests/classloaders/OWNERS
diff --git a/tests/tests/keystore/OWNERS b/tests/tests/keystore/OWNERS
new file mode 100644
index 0000000..30685c8
--- /dev/null
+++ b/tests/tests/keystore/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 36824
+swillden@google.com
+jdanis@google.com
+jbires@google.com
diff --git a/tests/tests/keystore/src/android/server/am/ActivityManagerState.java b/tests/tests/keystore/src/android/server/am/ActivityManagerState.java
index debe8fc..b56d745 100644
--- a/tests/tests/keystore/src/android/server/am/ActivityManagerState.java
+++ b/tests/tests/keystore/src/android/server/am/ActivityManagerState.java
@@ -341,7 +341,7 @@
int procId = -1;
Activity(ActivityRecordProto proto) {
- super(proto.configurationContainer);
+ super(proto.appWindowToken.windowToken.windowContainer.configurationContainer);
name = proto.identifier.title;
state = proto.state;
visible = proto.visible;
diff --git a/tests/tests/location/OWNERS b/tests/tests/location/OWNERS
index e875815..99ad4f3 100644
--- a/tests/tests/location/OWNERS
+++ b/tests/tests/location/OWNERS
@@ -1,5 +1,6 @@
# Bug component: 32850
-wyattriley@google.com
-patrickor@google.com
-aadmal@google.com
+lifu@google.com
sooniln@google.com
+weiwa@google.com
+wyattriley@google.com
+yuhany@google.com
diff --git a/tests/tests/location/src/android/location/cts/LocationManagerTest.java b/tests/tests/location/src/android/location/cts/LocationManagerTest.java
index 89ee843..a46b19c 100644
--- a/tests/tests/location/src/android/location/cts/LocationManagerTest.java
+++ b/tests/tests/location/src/android/location/cts/LocationManagerTest.java
@@ -856,21 +856,9 @@
assertTrue(mManager.isProviderEnabled(TEST_MOCK_PROVIDER_NAME));
mManager.clearTestProviderEnabled(TEST_MOCK_PROVIDER_NAME);
- //onSetEnabled in LMS is handle in thread, it's not synchronized ,
- //need add delay here or will cause check failed
- try {
- Thread.sleep(100);
- } catch (Exception e) {
- Log.e(TAG, "fail in testIsProviderEnabled");
- }
assertFalse(mManager.isProviderEnabled(TEST_MOCK_PROVIDER_NAME));
mManager.setTestProviderEnabled(TEST_MOCK_PROVIDER_NAME, true);
- try {
- Thread.sleep(100);
- } catch (Exception e) {
- Log.e(TAG, "fail in testIsProviderEnabled");
- }
assertTrue(mManager.isProviderEnabled(TEST_MOCK_PROVIDER_NAME));
try {
diff --git a/tests/tests/media/Android.bp b/tests/tests/media/Android.bp
index 99902c5..0d2f5b9 100644
--- a/tests/tests/media/Android.bp
+++ b/tests/tests/media/Android.bp
@@ -40,7 +40,6 @@
"truth-prebuilt",
"mockito-target-minus-junit4",
"androidx.heifwriter_heifwriter",
- "androidx.media2_media2",
],
jni_libs: [
"libaudio_jni",
diff --git a/tests/tests/media/res/values/exifinterface.xml b/tests/tests/media/res/values/exifinterface.xml
index 11c2efd..25a42b1 100644
--- a/tests/tests/media/res/values/exifinterface.xml
+++ b/tests/tests/media/res/values/exifinterface.xml
@@ -53,6 +53,44 @@
<item />
<item />
</array>
+ <array name="exifbyteorderii_standalone">
+ <item>true</item>
+ <item>3494</item>
+ <item>6265</item>
+ <item>512</item>
+ <item>288</item>
+ <item>true</item>
+ <item>false</item>
+ <item>0</item>
+ <item>0</item>
+ <item>0.0</item>
+ <item>0.0</item>
+ <item>0.0</item>
+ <item>SAMSUNG</item>
+ <item>SM-N900S</item>
+ <item>2.200</item>
+ <item>2016:01:29 18:32:27</item>
+ <item>0.033</item>
+ <item>0</item>
+ <item>413/100</item>
+ <item />
+ <item />
+ <item />
+ <item />
+ <item />
+ <item />
+ <item />
+ <item />
+ <item />
+ <item>480</item>
+ <item>640</item>
+ <item>50</item>
+ <item>6</item>
+ <item>0</item>
+ <item>false</item>
+ <item>0</item>
+ <item>0</item>
+ </array>
<array name="exifbyteordermm_jpg">
<item>false</item>
<item />
@@ -91,6 +129,44 @@
<item />
<item />
</array>
+ <array name="exifbyteordermm_standalone">
+ <item>false</item>
+ <item>0</item>
+ <item>0</item>
+ <item>0</item>
+ <item>0</item>
+ <item>false</item>
+ <item>true</item>
+ <item>572</item>
+ <item>24</item>
+ <item>0.0</item>
+ <item>0.0</item>
+ <item>0.0</item>
+ <item>LGE</item>
+ <item>Nexus 5</item>
+ <item>2.400</item>
+ <item>2016:01:29 15:44:58</item>
+ <item>0.017</item>
+ <item>0</item>
+ <item>3970/1000</item>
+ <item>0/1000</item>
+ <item>0</item>
+ <item>1970:01:01</item>
+ <item>0/1,0/1,0/10000</item>
+ <item>N</item>
+ <item>0/1,0/1,0/10000</item>
+ <item>E</item>
+ <item>GPS</item>
+ <item>00:00:00</item>
+ <item>0</item>
+ <item>0</item>
+ <item>146</item>
+ <item>0</item>
+ <item>0</item>
+ <item>false</item>
+ <item>0</item>
+ <item>0</item>
+ </array>
<array name="lg_g4_iso_800_dng">
<item>true</item>
<item>0</item>
diff --git a/tests/tests/media/src/android/media/cts/.goutputstream-9KZYJZ b/tests/tests/media/src/android/media/cts/.goutputstream-9KZYJZ
deleted file mode 100644
index c1769ac..0000000
--- a/tests/tests/media/src/android/media/cts/.goutputstream-9KZYJZ
+++ /dev/null
@@ -1,814 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.media.cts;
-
-import android.media.BufferingParams;
-import android.media.DataSourceDesc;
-import android.media.MediaFormat;
-import android.media.MediaPlayer2;
-import android.media.MediaPlayer2.TrackInfo;
-import android.media.TimedMetaData;
-import android.media.cts.TestUtils.Monitor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Looper;
-import android.os.PowerManager;
-import android.os.SystemClock;
-import android.platform.test.annotations.AppModeFull;
-import android.test.InstrumentationTestRunner;
-import android.util.Log;
-import android.webkit.cts.CtsTestServer;
-
-import com.android.compatibility.common.util.DynamicConfigDeviceSide;
-import com.android.compatibility.common.util.MediaUtils;
-
-import java.net.HttpCookie;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.HashMap;
-import java.util.List;
-
-/**
- * Tests of MediaPlayer2 streaming capabilities.
- */
-@AppModeFull(reason = "TODO: evaluate and port to instant")
-public class StreamingMediaPlayer2Test extends MediaPlayer2TestBase {
- // TODO: remove this flag to enable tests.
- private static final boolean IGNORE_TESTS = true;
-
- private static final String TAG = "StreamingMediaPlayer2Test";
-
- private static final String HTTP_H263_AMR_VIDEO_1_KEY =
- "streaming_media_player_test_http_h263_amr_video1";
- private static final String HTTP_H263_AMR_VIDEO_2_KEY =
- "streaming_media_player_test_http_h263_amr_video2";
- private static final String HTTP_H264_BASE_AAC_VIDEO_1_KEY =
- "streaming_media_player_test_http_h264_base_aac_video1";
- private static final String HTTP_H264_BASE_AAC_VIDEO_2_KEY =
- "streaming_media_player_test_http_h264_base_aac_video2";
- private static final String HTTP_MPEG4_SP_AAC_VIDEO_1_KEY =
- "streaming_media_player_test_http_mpeg4_sp_aac_video1";
- private static final String HTTP_MPEG4_SP_AAC_VIDEO_2_KEY =
- "streaming_media_player_test_http_mpeg4_sp_aac_video2";
- private static final String MODULE_NAME = "CtsMediaTestCases";
- private DynamicConfigDeviceSide dynamicConfig;
-
- private CtsTestServer mServer;
-
- private String mInputUrl;
-
- @Override
- protected void setUp() throws Exception {
- // if launched with InstrumentationTestRunner to pass a command line argument
- if (getInstrumentation() instanceof InstrumentationTestRunner) {
- InstrumentationTestRunner testRunner =
- (InstrumentationTestRunner)getInstrumentation();
-
- Bundle arguments = testRunner.getArguments();
- mInputUrl = arguments.getString("url");
- Log.v(TAG, "setUp: arguments: " + arguments);
- if (mInputUrl != null) {
- Log.v(TAG, "setUp: arguments[url] " + mInputUrl);
- }
- }
-
- super.setUp();
- dynamicConfig = new DynamicConfigDeviceSide(MODULE_NAME);
- }
-
-/* RTSP tests are more flaky and vulnerable to network condition.
- Disable until better solution is available
- // Streaming RTSP video from YouTube
- public void testRTSP_H263_AMR_Video1() throws Exception {
- playVideoTest("rtsp://v2.cache7.c.youtube.com/video.3gp?cid=0x271de9756065677e"
- + "&fmt=13&user=android-device-test", 176, 144);
- }
- public void testRTSP_H263_AMR_Video2() throws Exception {
- playVideoTest("rtsp://v2.cache7.c.youtube.com/video.3gp?cid=0xc80658495af60617"
- + "&fmt=13&user=android-device-test", 176, 144);
- }
-
- public void testRTSP_MPEG4SP_AAC_Video1() throws Exception {
- playVideoTest("rtsp://v2.cache7.c.youtube.com/video.3gp?cid=0x271de9756065677e"
- + "&fmt=17&user=android-device-test", 176, 144);
- }
- public void testRTSP_MPEG4SP_AAC_Video2() throws Exception {
- playVideoTest("rtsp://v2.cache7.c.youtube.com/video.3gp?cid=0xc80658495af60617"
- + "&fmt=17&user=android-device-test", 176, 144);
- }
-
- public void testRTSP_H264Base_AAC_Video1() throws Exception {
- playVideoTest("rtsp://v2.cache7.c.youtube.com/video.3gp?cid=0x271de9756065677e"
- + "&fmt=18&user=android-device-test", 480, 270);
- }
- public void testRTSP_H264Base_AAC_Video2() throws Exception {
- playVideoTest("rtsp://v2.cache7.c.youtube.com/video.3gp?cid=0xc80658495af60617"
- + "&fmt=18&user=android-device-test", 480, 270);
- }
-*/
- // Streaming HTTP video from YouTube
- public void testHTTP_H263_AMR_Video1() throws Exception {
- if (IGNORE_TESTS) {
- return;
- }
- if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_H263,
- MediaFormat.MIMETYPE_AUDIO_AMR_NB)) {
- return; // skip
- }
-
- String urlString = dynamicConfig.getValue(HTTP_H263_AMR_VIDEO_1_KEY);
- playVideoTest(urlString, 176, 144);
- }
-
- public void testHTTP_H263_AMR_Video2() throws Exception {
- if (IGNORE_TESTS) {
- return;
- }
- if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_H263,
- MediaFormat.MIMETYPE_AUDIO_AMR_NB)) {
- return; // skip
- }
-
- String urlString = dynamicConfig.getValue(HTTP_H263_AMR_VIDEO_2_KEY);
- playVideoTest(urlString, 176, 144);
- }
-
- public void testHTTP_MPEG4SP_AAC_Video1() throws Exception {
- if (IGNORE_TESTS) {
- return;
- }
- if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_MPEG4)) {
- return; // skip
- }
-
- String urlString = dynamicConfig.getValue(HTTP_MPEG4_SP_AAC_VIDEO_1_KEY);
- playVideoTest(urlString, 176, 144);
- }
-
- public void testHTTP_MPEG4SP_AAC_Video2() throws Exception {
- if (IGNORE_TESTS) {
- return;
- }
- if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_MPEG4)) {
- return; // skip
- }
-
- String urlString = dynamicConfig.getValue(HTTP_MPEG4_SP_AAC_VIDEO_2_KEY);
- playVideoTest(urlString, 176, 144);
- }
-
- public void testHTTP_H264Base_AAC_Video1() throws Exception {
- if (IGNORE_TESTS) {
- return;
- }
- if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
- return; // skip
- }
-
- String urlString = dynamicConfig.getValue(HTTP_H264_BASE_AAC_VIDEO_1_KEY);
- playVideoTest(urlString, 640, 360);
- }
-
- public void testHTTP_H264Base_AAC_Video2() throws Exception {
- if (IGNORE_TESTS) {
- return;
- }
- if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
- return; // skip
- }
-
- String urlString = dynamicConfig.getValue(HTTP_H264_BASE_AAC_VIDEO_2_KEY);
- playVideoTest(urlString, 640, 360);
- }
-
- // Streaming HLS video from YouTube
- public void testHLS() throws Exception {
- if (IGNORE_TESTS) {
- return;
- }
- if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
- return; // skip
- }
-
- // Play stream for 60 seconds
- playLiveVideoTest("http://www.youtube.com/api/manifest/hls_variant/id/"
- + "0168724d02bd9945/itag/5/source/youtube/playlist_type/DVR/ip/"
- + "0.0.0.0/ipbits/0/expire/19000000000/sparams/ip,ipbits,expire"
- + ",id,itag,source,playlist_type/signature/773AB8ACC68A96E5AA48"
- + "1996AD6A1BBCB70DCB87.95733B544ACC5F01A1223A837D2CF04DF85A336"
- + "0/key/ik0/file/m3u8", 60 * 1000);
- }
-
- public void testHlsWithHeadersCookies() throws Exception {
- if (IGNORE_TESTS) {
- return;
- }
- if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
- return; // skip
- }
-
- final Uri uri = Uri.parse(
- "http://www.youtube.com/api/manifest/hls_variant/id/"
- + "0168724d02bd9945/itag/5/source/youtube/playlist_type/DVR/ip/"
- + "0.0.0.0/ipbits/0/expire/19000000000/sparams/ip,ipbits,expire"
- + ",id,itag,source,playlist_type/signature/773AB8ACC68A96E5AA48"
- + "1996AD6A1BBCB70DCB87.95733B544ACC5F01A1223A837D2CF04DF85A336"
- + "0/key/ik0/file/m3u8");
-
- // TODO: dummy values for headers/cookies till we find a server that actually needs them
- HashMap<String, String> headers = new HashMap<>();
- headers.put("header0", "value0");
- headers.put("header1", "value1");
-
- String cookieName = "auth_1234567";
- String cookieValue = "0123456789ABCDEF0123456789ABCDEF";
- HttpCookie cookie = new HttpCookie(cookieName, cookieValue);
- cookie.setHttpOnly(true);
- cookie.setDomain("www.youtube.com");
- cookie.setPath("/"); // all paths
- cookie.setSecure(false);
- cookie.setDiscard(false);
- cookie.setMaxAge(24 * 3600); // 24hrs
-
- java.util.Vector<HttpCookie> cookies = new java.util.Vector<HttpCookie>();
- cookies.add(cookie);
-
- // Play stream for 60 seconds
- playLiveVideoTest(uri, headers, cookies, 60 * 1000);
- }
-
- public void testHlsSampleAes_bbb_audio_only_overridable() throws Exception {
- if (IGNORE_TESTS) {
- return;
- }
- if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
- return; // skip
- }
-
- String defaultUrl = "http://storage.googleapis.com/wvmedia/cenc/hls/sample_aes/" +
- "bbb_1080p_30fps_11min/audio_only/prog_index.m3u8";
-
- // if url override provided
- String testUrl = (mInputUrl != null) ? mInputUrl : defaultUrl;
-
- // Play stream for 60 seconds
- playLiveAudioOnlyTest(
- testUrl,
- 60 * 1000);
- }
-
- public void testHlsSampleAes_bbb_unmuxed_1500k() throws Exception {
- if (IGNORE_TESTS) {
- return;
- }
- if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
- return; // skip
- }
-
- // Play stream for 60 seconds
- playLiveVideoTest(
- "http://storage.googleapis.com/wvmedia/cenc/hls/sample_aes/" +
- "bbb_1080p_30fps_11min/unmuxed_1500k/prog_index.m3u8",
- 60 * 1000);
- }
-
-
- // Streaming audio from local HTTP server
- public void testPlayMp3Stream1() throws Throwable {
- if (IGNORE_TESTS) {
- return;
- }
- localHttpAudioStreamTest("ringer.mp3", false, false);
- }
- public void testPlayMp3Stream2() throws Throwable {
- if (IGNORE_TESTS) {
- return;
- }
- localHttpAudioStreamTest("ringer.mp3", false, false);
- }
- public void testPlayMp3StreamRedirect() throws Throwable {
- if (IGNORE_TESTS) {
- return;
- }
- localHttpAudioStreamTest("ringer.mp3", true, false);
- }
- public void testPlayMp3StreamNoLength() throws Throwable {
- if (IGNORE_TESTS) {
- return;
- }
- localHttpAudioStreamTest("noiseandchirps.mp3", false, true);
- }
- public void testPlayOggStream() throws Throwable {
- if (IGNORE_TESTS) {
- return;
- }
- localHttpAudioStreamTest("noiseandchirps.ogg", false, false);
- }
- public void testPlayOggStreamRedirect() throws Throwable {
- if (IGNORE_TESTS) {
- return;
- }
- localHttpAudioStreamTest("noiseandchirps.ogg", true, false);
- }
- public void testPlayOggStreamNoLength() throws Throwable {
- if (IGNORE_TESTS) {
- return;
- }
- localHttpAudioStreamTest("noiseandchirps.ogg", false, true);
- }
- public void testPlayMp3Stream1Ssl() throws Throwable {
- if (IGNORE_TESTS) {
- return;
- }
- localHttpsAudioStreamTest("ringer.mp3", false, false);
- }
-
- private void localHttpAudioStreamTest(final String name, boolean redirect, boolean nolength)
- throws Throwable {
- mServer = new CtsTestServer(mContext);
- try {
- String stream_url = null;
- if (redirect) {
- // Stagefright doesn't have a limit, but we can't test support of infinite redirects
- // Up to 4 redirects seems reasonable though.
- stream_url = mServer.getRedirectingAssetUrl(name, 4);
- } else {
- stream_url = mServer.getAssetUrl(name);
- }
- if (nolength) {
- stream_url = stream_url + "?" + CtsTestServer.NOLENGTH_POSTFIX;
- }
-
- if (!MediaUtils.checkCodecsForPath(mContext, stream_url)) {
- return; // skip
- }
-
- final Uri uri = Uri.parse(stream_url);
- mPlayer.setDataSource(new DataSourceDesc.Builder()
- .setDataSource(mContext, uri)
- .build());
-
- mPlayer.setDisplay(getActivity().getSurfaceHolder());
- mPlayer.setScreenOnWhilePlaying(true);
-
- mOnBufferingUpdateCalled.reset();
- MediaPlayer2.MediaPlayer2EventCallback ecb =
- new MediaPlayer2.MediaPlayer2EventCallback() {
- @Override
- public void onError(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
- fail("Media player had error " + what + " playing " + name);
- }
-
- @Override
- public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
- if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
- mOnPrepareCalled.signal();
- } else if (what == MediaPlayer2.MEDIA_INFO_BUFFERING_UPDATE) {
- mOnBufferingUpdateCalled.signal();
- }
- }
- };
- mPlayer.setMediaPlayer2EventCallback(mExecutor, ecb);
-
- assertFalse(mOnBufferingUpdateCalled.isSignalled());
-
- mPlayer.prepare();
- mOnPrepareCalled.waitForSignal();
-
- if (nolength) {
- mPlayer.play();
- Thread.sleep(LONG_SLEEP_TIME);
- assertFalse(mPlayer.isPlaying());
- } else {
- mOnBufferingUpdateCalled.waitForSignal();
- mPlayer.play();
- Thread.sleep(SLEEP_TIME);
- }
- mPlayer.stop();
- mPlayer.reset();
- } finally {
- mServer.shutdown();
- }
- }
- private void localHttpsAudioStreamTest(final String name, boolean redirect, boolean nolength)
- throws Throwable {
- mServer = new CtsTestServer(mContext, true);
- try {
- String stream_url = null;
- if (redirect) {
- // Stagefright doesn't have a limit, but we can't test support of infinite redirects
- // Up to 4 redirects seems reasonable though.
- stream_url = mServer.getRedirectingAssetUrl(name, 4);
- } else {
- stream_url = mServer.getAssetUrl(name);
- }
- if (nolength) {
- stream_url = stream_url + "?" + CtsTestServer.NOLENGTH_POSTFIX;
- }
-
- final Uri uri = Uri.parse(stream_url);
- mPlayer.setDataSource(new DataSourceDesc.Builder()
- .setDataSource(mContext, uri)
- .build());
-
- mPlayer.setDisplay(getActivity().getSurfaceHolder());
- mPlayer.setScreenOnWhilePlaying(true);
-
- mOnBufferingUpdateCalled.reset();
-
- MediaPlayer2.MediaPlayer2EventCallback ecb =
- new MediaPlayer2.MediaPlayer2EventCallback() {
- @Override
- public void onError(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
- mOnErrorCalled.signal();
- }
-
- @Override
- public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
- if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
- mOnPrepareCalled.signal();
- } else if (what == MediaPlayer2.MEDIA_INFO_BUFFERING_UPDATE) {
- mOnBufferingUpdateCalled.signal();
- }
- }
- };
- mPlayer.setMediaPlayer2EventCallback(mExecutor, ecb);
-
- assertFalse(mOnBufferingUpdateCalled.isSignalled());
- try {
- mPlayer.prepare();
- mOnErrorCalled.waitForSignal();
- } catch (Exception ex) {
- return;
- }
- } finally {
- mServer.shutdown();
- }
- }
-
- // TODO: unhide this test when we sort out how to expose buffering control API.
- private void doTestBuffering() throws Throwable {
- final String name = "ringer.mp3";
- mServer = new CtsTestServer(mContext);
- try {
- String stream_url = mServer.getAssetUrl(name);
-
- if (!MediaUtils.checkCodecsForPath(mContext, stream_url)) {
- Log.w(TAG, "can not find stream " + stream_url + ", skipping test");
- return; // skip
- }
-
- Monitor onSetBufferingParamsCalled = new Monitor();
- MediaPlayer2.MediaPlayer2EventCallback ecb =
- new MediaPlayer2.MediaPlayer2EventCallback() {
- @Override
- public void onError(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
- fail("Media player had error " + what + " playing " + name);
- }
- @Override
- public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
- if (what == MediaPlayer2.MEDIA_INFO_BUFFERING_UPDATE) {
- mOnBufferingUpdateCalled.signal();
- }
- }
- @Override
- public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd,
- int what, int status) {
- if (what == MediaPlayer2.CALL_COMPLETED_SET_BUFFERING_PARAMS) {
- mCallStatus = status;
- onSetBufferingParamsCalled.signal();
- }
- }
- };
- mPlayer.setMediaPlayer2EventCallback(mExecutor, ecb);
-
- // getBufferingParams should be called after setDataSource.
- try {
- BufferingParams params = mPlayer.getBufferingParams();
- fail("MediaPlayer2 failed to check state for getBufferingParams");
- } catch (IllegalStateException e) {
- // expected
- }
-
- // setBufferingParams should be called after setDataSource.
- BufferingParams params = new BufferingParams.Builder()
- .setInitialMarkMs(2)
- .setResumePlaybackMarkMs(3)
- .build();
- mCallStatus = MediaPlayer2.CALL_STATUS_NO_ERROR;
- onSetBufferingParamsCalled.reset();
- mPlayer.setBufferingParams(params);
- onSetBufferingParamsCalled.waitForSignal();
- assertTrue(mCallStatus != MediaPlayer2.CALL_STATUS_NO_ERROR);
-
- final Uri uri = Uri.parse(stream_url);
- mPlayer.setDataSource(new DataSourceDesc.Builder()
- .setDataSource(mContext, uri)
- .build());
-
- mPlayer.setDisplay(getActivity().getSurfaceHolder());
- mPlayer.setScreenOnWhilePlaying(true);
-
- mOnBufferingUpdateCalled.reset();
-
- assertFalse(mOnBufferingUpdateCalled.isSignalled());
-
- params = mPlayer.getBufferingParams();
-
- int newMark = params.getInitialMarkMs() + 2;
- BufferingParams newParams =
- new BufferingParams.Builder(params).setInitialMarkMs(newMark).build();
-
- onSetBufferingParamsCalled.reset();
- mPlayer.setBufferingParams(newParams);
- onSetBufferingParamsCalled.waitForSignal();
-
- int checkMark = -1;
- BufferingParams checkParams = mPlayer.getBufferingParams();
- checkMark = checkParams.getInitialMarkMs();
- assertEquals("marks do not match", newMark, checkMark);
-
- // TODO: add more dynamic checking, e.g., buffering shall not exceed pre-set mark.
-
- mPlayer.reset();
- } finally {
- mServer.shutdown();
- }
- }
-
- public void testPlayHlsStream() throws Throwable {
- if (IGNORE_TESTS) {
- return;
- }
- if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
- return; // skip
- }
- localHlsTest("hls.m3u8", false, false);
- }
-
- public void testPlayHlsStreamWithQueryString() throws Throwable {
- if (IGNORE_TESTS) {
- return;
- }
- if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
- return; // skip
- }
- localHlsTest("hls.m3u8", true, false);
- }
-
- public void testPlayHlsStreamWithRedirect() throws Throwable {
- if (IGNORE_TESTS) {
- return;
- }
- if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
- return; // skip
- }
- localHlsTest("hls.m3u8", false, true);
- }
-
- public void testPlayHlsStreamWithTimedId3() throws Throwable {
- if (IGNORE_TESTS) {
- return;
- }
- if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
- Log.d(TAG, "Device doesn't have video codec, skipping test");
- return;
- }
-
- mServer = new CtsTestServer(mContext);
- try {
- // counter must be final if we want to access it inside onTimedMetaData;
- // use AtomicInteger so we can have a final counter object with mutable integer value.
- final AtomicInteger counter = new AtomicInteger();
- String stream_url = mServer.getAssetUrl("prog_index.m3u8");
- final Uri uri = Uri.parse(stream_url);
- mPlayer.setDataSource(new DataSourceDesc.Builder()
- .setDataSource(mContext, uri)
- .build());
- mPlayer.setDisplay(getActivity().getSurfaceHolder());
- mPlayer.setScreenOnWhilePlaying(true);
- mPlayer.setWakeMode(mContext, PowerManager.PARTIAL_WAKE_LOCK);
-
- final Object completion = new Object();
- MediaPlayer2.MediaPlayer2EventCallback ecb =
- new MediaPlayer2.MediaPlayer2EventCallback() {
- int run;
- @Override
- public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
- if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
- mOnPrepareCalled.signal();
- } else if (what == MediaPlayer2.MEDIA_INFO_PLAYBACK_COMPLETE) {
- if (run++ == 0) {
- mPlayer.seekTo(0, MediaPlayer2.SEEK_PREVIOUS_SYNC);
- mPlayer.play();
- } else {
- mPlayer.stop();
- synchronized (completion) {
- completion.notify();
- }
- }
- }
- }
-
- @Override
- public void onTimedMetaDataAvailable(MediaPlayer2 mp, DataSourceDesc dsd,
- TimedMetaData md) {
- counter.incrementAndGet();
- long pos = mp.getCurrentPosition();
- long timeUs = md.getTimestamp();
- byte[] rawData = md.getMetaData();
- // Raw data contains an id3 tag holding the decimal string representation of
- // the associated time stamp rounded to the closest half second.
-
- int offset = 0;
- offset += 3; // "ID3"
- offset += 2; // version
- offset += 1; // flags
- offset += 4; // size
- offset += 4; // "TXXX"
- offset += 4; // frame size
- offset += 2; // frame flags
- offset += 1; // "\x03" : UTF-8 encoded Unicode
- offset += 1; // "\x00" : null-terminated empty description
-
- int length = rawData.length;
- length -= offset;
- length -= 1; // "\x00" : terminating null
-
- String data = new String(rawData, offset, length);
- int dataTimeUs = Integer.parseInt(data);
- assertTrue("Timed ID3 timestamp does not match content",
- Math.abs(dataTimeUs - timeUs) < 500000);
- assertTrue("Timed ID3 arrives after timestamp", pos * 1000 < timeUs);
- }
-
- @Override
- public void onCallCompleted(MediaPlayer2 mp, DataSourceDesc dsd,
- int what, int status) {
- if (what == MediaPlayer2.CALL_COMPLETED_PLAY) {
- mOnPlayCalled.signal();
- }
- }
- };
- mPlayer.setMediaPlayer2EventCallback(mExecutor, ecb);
-
- mPlayer.prepare();
- mOnPrepareCalled.waitForSignal();
-
- mOnPlayCalled.reset();
- mPlayer.play();
- mOnPlayCalled.waitForSignal();
- assertTrue("MediaPlayer2 not playing", mPlayer.isPlaying());
-
- int i = -1;
- List<TrackInfo> trackInfos = mPlayer.getTrackInfo();
- for (i = 0; i < trackInfos.size(); i++) {
- TrackInfo trackInfo = trackInfos.get(i);
- if (trackInfo.getTrackType() == TrackInfo.MEDIA_TRACK_TYPE_METADATA) {
- break;
- }
- }
- assertTrue("Stream has no timed ID3 track", i >= 0);
- mPlayer.selectTrack(i);
-
- synchronized (completion) {
- completion.wait();
- }
-
- // There are a total of 19 metadata access units in the test stream; every one of them
- // should be received twice: once before the seek and once after.
- assertTrue("Incorrect number of timed ID3s recieved", counter.get() == 38);
- } finally {
- mServer.shutdown();
- }
- }
-
- private static class WorkerWithPlayer implements Runnable {
- private final Object mLock = new Object();
- private Looper mLooper;
- private MediaPlayer2 mPlayer;
-
- /**
- * Creates a worker thread with the given name. The thread
- * then runs a {@link android.os.Looper}.
- * @param name A name for the new thread
- */
- WorkerWithPlayer(String name) {
- Thread t = new Thread(null, this, name);
- t.setPriority(Thread.MIN_PRIORITY);
- t.start();
- synchronized (mLock) {
- while (mLooper == null) {
- try {
- mLock.wait();
- } catch (InterruptedException ex) {
- }
- }
- }
- }
-
- public MediaPlayer2 getPlayer() {
- return mPlayer;
- }
-
- @Override
- public void run() {
- synchronized (mLock) {
- Looper.prepare();
- mLooper = Looper.myLooper();
- mPlayer = MediaPlayer2.create();
- mLock.notifyAll();
- }
- Looper.loop();
- }
-
- public void quit() {
- mLooper.quit();
- mPlayer.close();
- }
- }
-
- public void testBlockingReadRelease() throws Throwable {
- if (IGNORE_TESTS) {
- return;
- }
-
- mServer = new CtsTestServer(mContext);
-
- WorkerWithPlayer worker = new WorkerWithPlayer("player");
- final MediaPlayer2 mp = worker.getPlayer();
-
- try {
- String path = mServer.getDelayedAssetUrl("noiseandchirps.ogg", 15000);
- final Uri uri = Uri.parse(path);
- mp.setDataSource(new DataSourceDesc.Builder()
- .setDataSource(mContext, uri)
- .build());
-
- MediaPlayer2.MediaPlayer2EventCallback ecb =
- new MediaPlayer2.MediaPlayer2EventCallback() {
- @Override
- public void onInfo(MediaPlayer2 mp, DataSourceDesc dsd, int what, int extra) {
- if (what == MediaPlayer2.MEDIA_INFO_PREPARED) {
- fail("prepare should not succeed");
- }
- }
- };
- mp.setMediaPlayer2EventCallback(mExecutor, ecb);
-
- mp.prepare();
- Thread.sleep(1000);
- long start = SystemClock.elapsedRealtime();
- mp.close();
- long end = SystemClock.elapsedRealtime();
- long releaseDuration = (end - start);
- assertTrue("release took too long: " + releaseDuration, releaseDuration < 1000);
- } catch (IllegalArgumentException e) {
- fail(e.getMessage());
- } catch (SecurityException e) {
- fail(e.getMessage());
- } catch (IllegalStateException e) {
- fail(e.getMessage());
- } catch (InterruptedException e) {
- fail(e.getMessage());
- } finally {
- mServer.shutdown();
- }
-
- // give the worker a bit of time to start processing the message before shutting it down
- Thread.sleep(5000);
- worker.quit();
- }
-
- private void localHlsTest(final String name, boolean appendQueryString, boolean redirect)
- throws Throwable {
- mServer = new CtsTestServer(mContext);
- try {
- String stream_url = null;
- if (redirect) {
- stream_url = mServer.getQueryRedirectingAssetUrl(name);
- } else {
- stream_url = mServer.getAssetUrl(name);
- }
- if (appendQueryString) {
- stream_url += "?foo=bar/baz";
- }
-
- playLiveVideoTest(stream_url, 10);
- } finally {
- mServer.shutdown();
- }
- }
-}
diff --git a/tests/tests/media/src/android/media/cts/AudioManagerTest.java b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
index 47b3337..1316db6 100644
--- a/tests/tests/media/src/android/media/cts/AudioManagerTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
@@ -189,13 +189,6 @@
@AppModeFull(reason = "Instant apps cannot hold android.permission.MODIFY_AUDIO_SETTINGS")
public void testMicrophoneMuteIntent() throws Exception {
- // Skip this test for automotive.
- // This tests listens for ACTION_MICROPHONE_MUTE_CHANGED which AudioService only broadcasts
- // to system user. Automotive devices, which runs in secondary user, will fail this test.
- if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
- return;
- }
-
if (!mDoNotCheckUnmute) {
final MyBlockingIntentReceiver receiver = new MyBlockingIntentReceiver(
AudioManager.ACTION_MICROPHONE_MUTE_CHANGED);
@@ -1283,6 +1276,41 @@
mAudioManager.isStreamMute(AudioManager.STREAM_SYSTEM));
assertTrue("Alarm stream should be muted",
mAudioManager.isStreamMute(AudioManager.STREAM_ALARM));
+ assertFalse("Ringer stream should not be muted",
+ mAudioManager.isStreamMute(AudioManager.STREAM_RING));
+ } finally {
+ setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
+ }
+ }
+
+ public void testPriorityOnlySystemDisallowedWithRingerMuted() throws Exception {
+ if (mSkipRingerTests || !mSupportNotificationPolicyAccess) {
+ return;
+ }
+
+ Utils.toggleNotificationPolicyAccess(
+ mContext.getPackageName(), getInstrumentation(), true);
+ try {
+ // ensure volume is not muted/0 to start test, but then mute ringer
+ mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 1, 0);
+ mAudioManager.setStreamVolume(AudioManager.STREAM_ALARM, 1, 0);
+ mAudioManager.setStreamVolume(AudioManager.STREAM_SYSTEM, 1, 0);
+ mAudioManager.setStreamVolume(AudioManager.STREAM_RING, 0, 0);
+ mAudioManager.setRingerMode(RINGER_MODE_SILENT);
+
+ // allow only system in priority only
+ mNm.setNotificationPolicy(new NotificationManager.Policy(
+ NotificationManager.Policy.PRIORITY_CATEGORY_SYSTEM, 0, 0));
+ setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY);
+ // delay for streams to get into correct mute states
+ Thread.sleep(ASYNC_TIMING_TOLERANCE_MS);
+
+ assertTrue("Music (media) stream should be muted",
+ mAudioManager.isStreamMute(AudioManager.STREAM_MUSIC));
+ assertTrue("System stream should be muted",
+ mAudioManager.isStreamMute(AudioManager.STREAM_SYSTEM));
+ assertTrue("Alarm stream should be muted",
+ mAudioManager.isStreamMute(AudioManager.STREAM_ALARM));
// Test requires that the phone's default state has no channels that can bypass dnd
assertTrue("Ringer stream should be muted",
diff --git a/tests/tests/media/src/android/media/cts/AudioPlaybackConfigurationTest.java b/tests/tests/media/src/android/media/cts/AudioPlaybackConfigurationTest.java
index 6256175..21f7a1b 100644
--- a/tests/tests/media/src/android/media/cts/AudioPlaybackConfigurationTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioPlaybackConfigurationTest.java
@@ -33,6 +33,7 @@
import android.media.AudioPlaybackConfiguration;
import com.android.compatibility.common.util.CtsAndroidTestCase;
+import com.android.internal.annotations.GuardedBy;
import java.lang.reflect.Method;
import java.util.ArrayList;
@@ -361,16 +362,16 @@
}
private static class MyAudioPlaybackCallback extends AudioManager.AudioPlaybackCallback {
- private int mCalled = 0;
- private int mNbConfigs = 0;
- private List<AudioPlaybackConfiguration> mConfigs;
private final Object mCbLock = new Object();
+ @GuardedBy("mCbLock")
+ private int mCalled;
+ @GuardedBy("mCbLock")
+ private List<AudioPlaybackConfiguration> mConfigs;
void reset() {
synchronized (mCbLock) {
mCalled = 0;
- mNbConfigs = 0;
- mConfigs.clear();
+ mConfigs = new ArrayList<AudioPlaybackConfiguration>();
}
}
@@ -381,9 +382,7 @@
}
int getNbConfigs() {
- synchronized (mCbLock) {
- return mNbConfigs;
- }
+ return getConfigs().size();
}
List<AudioPlaybackConfiguration> getConfigs() {
@@ -393,13 +392,13 @@
}
MyAudioPlaybackCallback() {
+ reset();
}
@Override
public void onPlaybackConfigChanged(List<AudioPlaybackConfiguration> configs) {
synchronized (mCbLock) {
mCalled++;
- mNbConfigs = configs.size();
mConfigs = configs;
}
}
diff --git a/tests/tests/media/src/android/media/cts/AudioRecordAppOpTest.java b/tests/tests/media/src/android/media/cts/AudioRecordAppOpTest.java
index 6ead39e..70da3d3 100644
--- a/tests/tests/media/src/android/media/cts/AudioRecordAppOpTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioRecordAppOpTest.java
@@ -16,7 +16,7 @@
package android.media.cts;
-import static android.app.AppOpsManager.OP_RECORD_AUDIO;
+import static android.app.AppOpsManager.OPSTR_RECORD_AUDIO;
import static com.google.common.truth.Truth.assertThat;
@@ -76,11 +76,12 @@
.build();
// The app op should be reported as not started
- assertThat(appOpsManager.isOperationActive(OP_RECORD_AUDIO,
+ assertThat(appOpsManager.isOpActive(OPSTR_RECORD_AUDIO,
uid, packageName)).isFalse();
// Start watching for app op active
- appOpsManager.startWatchingActive(new int[] {OP_RECORD_AUDIO}, listener);
+ appOpsManager.startWatchingActive(new String[] { OPSTR_RECORD_AUDIO },
+ getContext().getMainExecutor(), listener);
// Start recording
candidateRecorder.startRecording();
@@ -88,11 +89,11 @@
// The app op should start
verify(listener, timeout(APP_OP_CHANGE_TIMEOUT_MILLIS)
- .only()).onOpActiveChanged(eq(OP_RECORD_AUDIO),
+ .only()).onOpActiveChanged(eq(OPSTR_RECORD_AUDIO),
eq(uid), eq(packageName), eq(true));
// The app op should be reported as started
- assertThat(appOpsManager.isOperationActive(OP_RECORD_AUDIO,
+ assertThat(appOpsManager.isOpActive(OPSTR_RECORD_AUDIO,
uid, packageName)).isTrue();
@@ -106,11 +107,11 @@
// The app op should stop
verify(listener, timeout(APP_OP_CHANGE_TIMEOUT_MILLIS)
- .only()).onOpActiveChanged(eq(OP_RECORD_AUDIO),
+ .only()).onOpActiveChanged(eq(OPSTR_RECORD_AUDIO),
eq(uid), eq(packageName), eq(false));
// The app op should be reported as not started
- assertThat(appOpsManager.isOperationActive(OP_RECORD_AUDIO,
+ assertThat(appOpsManager.isOpActive(OPSTR_RECORD_AUDIO,
uid, packageName)).isFalse();
} finally {
if (recorder != null) {
diff --git a/tests/tests/media/src/android/media/cts/AudioRecordTest.java b/tests/tests/media/src/android/media/cts/AudioRecordTest.java
index 90a5a3f..a38f8a7 100644
--- a/tests/tests/media/src/android/media/cts/AudioRecordTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioRecordTest.java
@@ -35,12 +35,14 @@
import android.media.AudioTrack;
import android.media.MediaRecorder;
import android.media.MediaSyncEvent;
+import android.media.MicrophoneDirection;
import android.media.MicrophoneInfo;
import android.media.cts.AudioRecordingConfigurationTest.MyAudioRecordingCallback;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.PersistableBundle;
+import android.os.Process;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
import android.util.Log;
@@ -630,12 +632,14 @@
}
AudioRecord recorder = null;
+ String packageName = InstrumentationRegistry.getTargetContext().getPackageName();
+ int currentUserId = Process.myUserHandle().getIdentifier();
// We will record audio for 20 sec from active and idle state expecting
// the recording from active state to have data while from idle silence.
try {
// Ensure no race and UID active
- makeMyUidStateActive();
+ makeMyUidStateActive(packageName, currentUserId);
// Setup a recorder
final AudioRecord candidateRecorder = new AudioRecord.Builder()
@@ -669,7 +673,7 @@
// Start clean
buffer.clear();
// Force idle the package
- makeMyUidStateIdle();
+ makeMyUidStateIdle(packageName, currentUserId);
// Read five seconds of data
readDataTimed(recorder, 5000, buffer);
// Ensure we read empty bytes
@@ -678,7 +682,7 @@
// Start clean
buffer.clear();
// Reset to active
- makeMyUidStateActive();
+ makeMyUidStateActive(packageName, currentUserId);
// Read five seconds of data
readDataTimed(recorder, 5000, buffer);
// Ensure we read non-empty bytes
@@ -688,7 +692,7 @@
recorder.stop();
recorder.release();
}
- resetMyUidState();
+ resetMyUidState(packageName, currentUserId);
}
}
@@ -1628,25 +1632,67 @@
return totalSilenceCount > valueCount / 2;
}
- private static void makeMyUidStateActive() throws IOException {
- final String command = "cmd media.audio_policy set-uid-state "
- + InstrumentationRegistry.getTargetContext().getPackageName() + " active";
+ private static void makeMyUidStateActive(String packageName, int userId) throws IOException {
+ final String command = String.format(
+ "cmd media.audio_policy set-uid-state %s active --user %d", packageName, userId);
SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(), command);
}
- private static void makeMyUidStateIdle() throws IOException {
- final String command = "cmd media.audio_policy set-uid-state "
- + InstrumentationRegistry.getTargetContext().getPackageName() + " idle";
+ private static void makeMyUidStateIdle(String packageName, int userId) throws IOException {
+ final String command = String.format(
+ "cmd media.audio_policy set-uid-state %s idle --user %d", packageName, userId);
SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(), command);
}
- private static void resetMyUidState() throws IOException {
- final String command = "cmd media.audio_policy reset-uid-state "
- + InstrumentationRegistry.getTargetContext().getPackageName();
+ private static void resetMyUidState(String packageName, int userId) throws IOException {
+ final String command = String.format(
+ "cmd media.audio_policy reset-uid-state %s --user %d", packageName, userId);
SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(), command);
}
private static Context getContext() {
return InstrumentationRegistry.getInstrumentation().getTargetContext();
}
+
+ /*
+ * Microphone Direction API tests
+ */
+ public void testSetPreferredMicrophoneDirection() {
+ if (!hasMicrophone()) {
+ return;
+ }
+
+ try {
+ boolean success =
+ mAudioRecord.setPreferredMicrophoneDirection(
+ MicrophoneDirection.MIC_DIRECTION_TOWARDS_USER);
+
+ // Can't actually test this as HAL may not have implemented it
+ // Just verify that it doesn't crash or throw an exception
+ // assertTrue(success);
+ } catch (Exception ex) {
+ Log.e(TAG, "testSetPreferredMicrophoneDirection() exception:" + ex);
+ assertTrue(false);
+ }
+ return;
+ }
+
+ public void testSetPreferredMicrophoneFieldDimension() {
+ if (!hasMicrophone()) {
+ return;
+ }
+
+ try {
+ boolean success = mAudioRecord.setPreferredMicrophoneFieldDimension(1.0f);
+
+ // Can't actually test this as HAL may not have implemented it
+ // Just verify that it doesn't crash or throw an exception
+ // assertTrue(success);
+ } catch (Exception ex) {
+ Log.e(TAG, "testSetPreferredMicrophoneFieldDimension() exception:" + ex);
+ assertTrue(false);
+ }
+ return;
+ }
+
}
diff --git a/tests/tests/media/src/android/media/cts/AudioTrackTest.java b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
index fa20770..09e0014 100755
--- a/tests/tests/media/src/android/media/cts/AudioTrackTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
@@ -2652,6 +2652,46 @@
}
}
+ @Test
+ public void testMaxAudioTracks() throws Exception {
+ if (!hasAudioOutput()) {
+ return;
+ }
+
+ // The framework must not give more than MAX_TRACKS tracks per UID.
+ final int MAX_TRACKS = 512; // an arbitrary large number > 40
+ final int FRAMES = 1024;
+
+ final AudioTrack[] tracks = new AudioTrack[MAX_TRACKS];
+ final AudioTrack.Builder builder = new AudioTrack.Builder()
+ .setAudioFormat(new AudioFormat.Builder()
+ .setEncoding(AudioFormat.ENCODING_PCM_8BIT)
+ .setSampleRate(8000)
+ .setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
+ .build())
+ .setBufferSizeInBytes(FRAMES)
+ .setTransferMode(AudioTrack.MODE_STATIC);
+
+ int n = 0;
+ try {
+ for (; n < MAX_TRACKS; ++n) {
+ tracks[n] = builder.build();
+ }
+ } catch (UnsupportedOperationException e) {
+ ; // we expect this when we hit the uid track limit.
+ }
+
+ // release all the tracks created.
+ for (int i = 0; i < n; ++i) {
+ tracks[i].release();
+ tracks[i] = null;
+ }
+ Log.d(TAG, "" + n + " tracks were created");
+ assertTrue("should be able to create at least one static track", n > 0);
+ assertTrue("was able to create " + MAX_TRACKS + " tracks - that's too many!",
+ n < MAX_TRACKS);
+ }
+
/* Do not run in JB-MR1. will be re-opened in the next platform release.
public void testResourceLeakage() throws Exception {
final int BUFFER_SIZE = 600 * 1024;
diff --git a/tests/tests/media/src/android/media/cts/ExifInterfaceTest.java b/tests/tests/media/src/android/media/cts/ExifInterfaceTest.java
index d25c76a..ba4836b 100644
--- a/tests/tests/media/src/android/media/cts/ExifInterfaceTest.java
+++ b/tests/tests/media/src/android/media/cts/ExifInterfaceTest.java
@@ -21,6 +21,7 @@
import android.media.ExifInterface;
import android.os.Environment;
import android.os.FileUtils;
+import android.os.StrictMode;
import android.platform.test.annotations.AppModeFull;
import android.system.ErrnoException;
import android.system.Os;
@@ -32,6 +33,7 @@
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
+import java.io.EOFException;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
@@ -358,6 +360,30 @@
}
}
+ private void testExifInterfaceForStandalone(String fileName, int typedArrayResourceId)
+ throws IOException {
+ ExpectedValue expectedValue = new ExpectedValue(
+ getContext().getResources().obtainTypedArray(typedArrayResourceId));
+
+ // Test for reading from external data storage.
+ fileName = EXTERNAL_BASE_DIRECTORY + fileName;
+
+ File imageFile = new File(Environment.getExternalStorageDirectory(), fileName);
+ String verboseTag = imageFile.getName();
+
+ FileInputStream fis = new FileInputStream(imageFile);
+ // Skip the following marker bytes (0xff, 0xd8, 0xff, 0xe1)
+ fis.skip(4);
+ // Read the value of the length of the exif data
+ short length = readShort(fis);
+ byte[] exifBytes = new byte[length];
+ fis.read(exifBytes);
+
+ ByteArrayInputStream bin = new ByteArrayInputStream(exifBytes);
+ ExifInterface exifInterface = ExifInterface.fromStandalone(bin);
+ compareWithExpectedValue(exifInterface, expectedValue, verboseTag, true);
+ }
+
private void testExifInterfaceCommon(String fileName, ExpectedValue expectedValue)
throws IOException {
File imageFile = new File(Environment.getExternalStorageDirectory(), fileName);
@@ -481,6 +507,16 @@
// about writing back in here.
}
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
+ .detectUnbufferedIo()
+ .penaltyDeath()
+ .build());
+ }
+
public void testReadExifDataFromExifByteOrderIIJpeg() throws Throwable {
testExifInterfaceForJpeg(EXIF_BYTE_ORDER_II_JPEG, R.array.exifbyteorderii_jpg);
}
@@ -552,6 +588,11 @@
testExifInterfaceForRaw(SAMSUNG_NX3000_SRW, R.array.samsung_nx3000_srw);
}
+ public void testReadExifDataFromStandaloneData() throws Throwable {
+ testExifInterfaceForStandalone(EXIF_BYTE_ORDER_II_JPEG, R.array.exifbyteorderii_standalone);
+ testExifInterfaceForStandalone(EXIF_BYTE_ORDER_MM_JPEG, R.array.exifbyteordermm_standalone);
+ }
+
public void testSetDateTime() throws IOException {
final String dateTimeValue = "2017:02:02 22:22:22";
final String dateTimeOriginalValue = "2017:01:01 11:11:11";
@@ -594,4 +635,13 @@
FileUtils.copyFileOrThrow(original, cloned);
return cloned;
}
+
+ private short readShort(InputStream is) throws IOException {
+ int ch1 = is.read();
+ int ch2 = is.read();
+ if ((ch1 | ch2) < 0) {
+ throw new EOFException();
+ }
+ return (short) ((ch1 << 8) + (ch2));
+ }
}
diff --git a/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java b/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java
index 94f334c..8b5d628 100644
--- a/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java
@@ -36,11 +36,13 @@
import android.platform.test.annotations.AppModeFull;
import android.platform.test.annotations.RequiresDevice;
import android.test.AndroidTestCase;
+import android.util.Log;
import androidx.test.filters.SmallTest;
import com.android.compatibility.common.util.MediaUtils;
-
+import java.io.FileOutputStream;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
@@ -137,10 +139,25 @@
"Test album",
mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUM));
+ assertNull("Album artist was unexpectedly present",
+ mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST));
+
+ assertNull("Author was unexpectedly present",
+ mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_AUTHOR));
+
+ assertNull("Composer was unexpectedly present",
+ mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_COMPOSER));
+
assertEquals("Track number was other than expected",
"10",
mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER));
+ assertNull("Disc number was unexpectedly present",
+ mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DISC_NUMBER));
+
+ assertNull("Compilation was unexpectedly present",
+ mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_COMPILATION));
+
assertEquals("Year was other than expected",
"2013", mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_YEAR));
@@ -148,7 +165,59 @@
"19040101T000000.000Z",
mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DATE));
- assertNull("Writer was unexpected present",
+ assertEquals("Bitrate was other than expected",
+ "365018", // = 504045 (file size in byte) * 8e6 / 11047000 (duration in us)
+ mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_BITRATE));
+
+ assertNull("Capture frame rate was unexpectedly present",
+ mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_CAPTURE_FRAMERATE));
+
+ assertEquals("Duration was other than expected",
+ "11047",
+ mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION));
+
+ assertEquals("Number of tracks was other than expected",
+ "4",
+ mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS));
+
+ assertEquals("Has audio was other than expected",
+ "yes",
+ mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_AUDIO));
+
+ assertEquals("Has video was other than expected",
+ "yes",
+ mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO));
+
+ assertEquals("Video frame count was other than expected",
+ "172",
+ mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_FRAME_COUNT));
+
+ assertEquals("Video height was other than expected",
+ "288",
+ mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT));
+
+ assertEquals("Video width was other than expected",
+ "352",
+ mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH));
+
+ assertEquals("Video rotation was other than expected",
+ "0",
+ mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION));
+
+ assertEquals("Mime type was other than expected",
+ "video/mp4",
+ mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_MIMETYPE));
+
+ assertNull("Location was unexpectedly present",
+ mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_LOCATION));
+
+ assertNull("EXIF length was unexpectedly present",
+ mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_EXIF_LENGTH));
+
+ assertNull("EXIF offset was unexpectedly present",
+ mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_EXIF_OFFSET));
+
+ assertNull("Writer was unexpectedly present",
mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_WRITER));
}
@@ -166,10 +235,25 @@
"Test album",
mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUM));
+ assertNull("Album artist was unexpectedly present",
+ mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST));
+
+ assertNull("Author was unexpectedly present",
+ mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_AUTHOR));
+
+ assertNull("Composer was unexpectedly present",
+ mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_COMPOSER));
+
assertEquals("Track number was other than expected",
"10",
mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER));
+ assertNull("Disc number was unexpectedly present",
+ mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DISC_NUMBER));
+
+ assertNull("Compilation was unexpectedly present",
+ mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_COMPILATION));
+
assertEquals("Year was other than expected",
"2013", mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_YEAR));
@@ -177,6 +261,58 @@
"19700101T000000.000Z",
mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DATE));
+ assertEquals("Bitrate was other than expected",
+ "499895", // = 624869 (file size in byte) * 8e6 / 10000000 (duration in us)
+ mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_BITRATE));
+
+ assertNull("Capture frame rate was unexpectedly present",
+ mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_CAPTURE_FRAMERATE));
+
+ assertEquals("Duration was other than expected",
+ "10000",
+ mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION));
+
+ assertEquals("Number of tracks was other than expected",
+ "2",
+ mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS));
+
+ assertEquals("Has audio was other than expected",
+ "yes",
+ mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_AUDIO));
+
+ assertEquals("Has video was other than expected",
+ "yes",
+ mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO));
+
+ assertEquals("Video frame count was other than expected",
+ "240",
+ mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_FRAME_COUNT));
+
+ assertEquals("Video height was other than expected",
+ "360",
+ mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT));
+
+ assertEquals("Video width was other than expected",
+ "480",
+ mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH));
+
+ assertEquals("Video rotation was other than expected",
+ "0",
+ mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION));
+
+ assertEquals("Mime type was other than expected",
+ "video/mp4",
+ mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_MIMETYPE));
+
+ assertNull("Location was unexpectedly present",
+ mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_LOCATION));
+
+ assertNull("EXIF length was unexpectedly present",
+ mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_EXIF_LENGTH));
+
+ assertNull("EXIF offset was unexpectedly present",
+ mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_EXIF_OFFSET));
+
assertNull("Writer was unexpectedly present",
mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_WRITER));
}
@@ -194,7 +330,7 @@
}
- public void testLargeAlbumArt() {
+ public void testGetEmbeddedPicture() {
setDataSourceFd(R.raw.largealbumart);
assertNotNull("couldn't retrieve album art", mRetriever.getEmbeddedPicture());
@@ -246,24 +382,44 @@
}
private void testThumbnail(int resId) {
+ testThumbnail(resId, null /*outPath*/);
+ }
+
+ private void testThumbnail(int resId, String outPath) {
+ Stopwatch timer = new Stopwatch();
+
if (!MediaUtils.hasCodecForResourceAndDomain(getContext(), resId, "video/")) {
MediaUtils.skipTest("no video codecs for resource");
return;
}
- MediaMetadataRetriever retriever = new MediaMetadataRetriever();
- Resources resources = getContext().getResources();
- AssetFileDescriptor afd = resources.openRawResourceFd(resId);
+ setDataSourceFd(resId);
- retriever.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
+ timer.start();
+ Bitmap thumbnail = mRetriever.getFrameAtTime(-1 /* timeUs (any) */);
+ timer.end();
+ timer.printDuration("getFrameAtTime");
- try {
- afd.close();
- } catch (IOException e) {
- fail("Unable to open file");
+ assertNotNull(thumbnail);
+
+ // save output file if needed
+ if (outPath != null) {
+ FileOutputStream out = null;
+ try {
+ out = new FileOutputStream(outPath);
+ } catch (FileNotFoundException e) {
+ fail("Can't open output file");
+ }
+
+ thumbnail.compress(Bitmap.CompressFormat.PNG, 100, out);
+
+ try {
+ out.flush();
+ out.close();
+ } catch (IOException e) {
+ fail("Can't close file");
+ }
}
-
- assertNotNull(retriever.getFrameAtTime(-1 /* timeUs (any) */));
}
public void testThumbnailH264() {
@@ -290,6 +446,17 @@
testThumbnail(R.raw.bbb_s1_720x480_mp4_hevc_mp3_1600kbps_30fps_aac_he_6ch_240kbps_48000hz);
}
+ public void testThumbnailVP9Hdr() {
+ testThumbnail(R.raw.video_1280x720_vp9_hdr_static_3mbps);
+ }
+
+ public void testThumbnailAV1Hdr() {
+ testThumbnail(R.raw.video_1280x720_av1_hdr_static_3mbps);
+ }
+
+ public void testThumbnailHDR10() {
+ testThumbnail(R.raw.video_1280x720_hevc_hdr10_static_3mbps);
+ }
/**
* The following tests verifies MediaMetadataRetriever.getFrameAtTime behavior.
@@ -365,10 +532,15 @@
}
private void testGetFrameAtTimeEditList(int option, int[][] testCases) {
+ MediaMetadataRetriever.BitmapParams params = new MediaMetadataRetriever.BitmapParams();
+ params.setPreferredConfig(Bitmap.Config.ARGB_8888);
+
testGetFrameAtEditList(testCases, (r) -> {
List<Bitmap> bitmaps = new ArrayList<>();
for (int i = 0; i < testCases.length; i++) {
- bitmaps.add(r.getFrameAtTime(testCases[i][0], option));
+ Bitmap bitmap = r.getFrameAtTime(testCases[i][0], option, params);
+ assertEquals(Bitmap.Config.ARGB_8888, params.getActualConfig());
+ bitmaps.add(bitmap);
}
return bitmaps;
});
@@ -422,56 +594,30 @@
private void testGetFrameAt(int[][] testCases,
Function<MediaMetadataRetriever, List<Bitmap> > bitmapRetriever) {
- int resId = R.raw.binary_counter_320x240_30fps_600frames;
- if (!MediaUtils.hasCodecForResourceAndDomain(getContext(), resId, "video/")
- && mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
- MediaUtils.skipTest("no video codecs for resource on watch");
- return;
- }
-
- MediaMetadataRetriever retriever = new MediaMetadataRetriever();
- Resources resources = getContext().getResources();
- AssetFileDescriptor afd = resources.openRawResourceFd(resId);
-
- retriever.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
- try {
- afd.close();
- } catch (IOException e) {
- fail("Unable to close file");
- }
-
- List<Bitmap> bitmaps = bitmapRetriever.apply(retriever);
-
- for (int i = 0; i < testCases.length; i++) {
- verifyVideoFrame(bitmaps.get(i), testCases[i]);
- }
- retriever.release();
+ testGetFrameAt(R.raw.binary_counter_320x240_30fps_600frames,
+ testCases, bitmapRetriever);
}
private void testGetFrameAtEditList(int[][] testCases,
Function<MediaMetadataRetriever, List<Bitmap> > bitmapRetriever) {
- int resId = R.raw.binary_counter_320x240_30fps_600frames_editlist;
+ testGetFrameAt(R.raw.binary_counter_320x240_30fps_600frames_editlist,
+ testCases, bitmapRetriever);
+ }
+
+ private void testGetFrameAt(int resId, int[][] testCases,
+ Function<MediaMetadataRetriever, List<Bitmap> > bitmapRetriever) {
if (!MediaUtils.hasCodecForResourceAndDomain(getContext(), resId, "video/")
&& mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
MediaUtils.skipTest("no video codecs for resource on watch");
return;
}
- MediaMetadataRetriever retriever = new MediaMetadataRetriever();
- Resources resources = getContext().getResources();
- AssetFileDescriptor afd = resources.openRawResourceFd(resId);
- retriever.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
- try {
- afd.close();
- } catch (IOException e) {
- fail("Unable to close file");
- }
+ setDataSourceFd(resId);
- List<Bitmap> bitmaps = bitmapRetriever.apply(retriever);
+ List<Bitmap> bitmaps = bitmapRetriever.apply(mRetriever);
for (int i = 0; i < testCases.length; i++) {
verifyVideoFrame(bitmaps.get(i), testCases[i]);
}
- retriever.release();
}
private void verifyVideoFrame(Bitmap bitmap, int[] testCase) {
@@ -491,6 +637,74 @@
/**
* The following tests verifies MediaMetadataRetriever.getScaledFrameAtTime behavior.
*/
+ public void testGetScaledFrameAtTimeWithInvalidResolutions() {
+ int[] resIds = {R.raw.binary_counter_320x240_30fps_600frames,
+ R.raw.binary_counter_320x240_30fps_600frames_editlist,
+ R.raw.bbb_s4_1280x720_mp4_h264_mp31_8mbps_30fps_aac_he_mono_40kbps_44100hz,
+ R.raw.video_176x144_3gp_h263_56kbps_12fps_aac_mono_24kbps_11025hz,
+ R.raw.video_1280x720_mp4_mpeg4_1000kbps_25fps_aac_stereo_128kbps_44100hz,
+ R.raw.bbb_s1_640x360_webm_vp8_2mbps_30fps_vorbis_5ch_320kbps_48000hz,
+ R.raw.bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz,
+ R.raw.bbb_s1_720x480_mp4_hevc_mp3_1600kbps_30fps_aac_he_6ch_240kbps_48000hz,
+ R.raw.video_1280x720_vp9_hdr_static_3mbps,
+ R.raw.video_1280x720_av1_hdr_static_3mbps,
+ R.raw.video_1280x720_hevc_hdr10_static_3mbps};
+ int[][] resolutions = {{0, 120}, {-1, 0}, {-1, 120}, {140, -1}, {-1, -1}};
+ int[] options =
+ {OPTION_CLOSEST, OPTION_CLOSEST_SYNC, OPTION_NEXT_SYNC, OPTION_PREVIOUS_SYNC};
+
+ for (int resId : resIds) {
+ if (!MediaUtils.hasCodecForResourceAndDomain(getContext(), resId, "video/")
+ && mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+ MediaUtils.skipTest("no video codecs for resource on watch");
+ continue;
+ }
+
+ setDataSourceFd(resId);
+
+ for (int i = 0; i < resolutions.length; i++) {
+ int width = resolutions[i][0];
+ int height = resolutions[i][1];
+ for (int option : options) {
+ try {
+ Bitmap bitmap = mRetriever.getScaledFrameAtTime(
+ 2066666 /*timeUs*/, option, width, height);
+ fail("Failed to receive exception");
+ } catch (IllegalArgumentException e) {
+ // Expect exception
+ }
+ }
+ }
+ }
+ }
+
+ private void testGetScaledFrameAtTime(int scaleToWidth, int scaleToHeight,
+ int expectedWidth, int expectedHeight, Bitmap.Config config) {
+ MediaMetadataRetriever.BitmapParams params = null;
+ Bitmap bitmap = null;
+ if (config != null) {
+ params = new MediaMetadataRetriever.BitmapParams();
+ params.setPreferredConfig(config);
+ bitmap = mRetriever.getScaledFrameAtTime(
+ 2066666 /*timeUs */, OPTION_CLOSEST, scaleToWidth, scaleToHeight, params);
+ } else {
+ bitmap = mRetriever.getScaledFrameAtTime(
+ 2066666 /*timeUs */, OPTION_CLOSEST, scaleToWidth, scaleToHeight);
+ }
+ if (bitmap == null) {
+ fail("Failed to get scaled bitmap");
+ }
+ if (SAVE_BITMAP_OUTPUT) {
+ CodecUtils.saveBitmapToFile(bitmap, String.format("test_%dx%d.jpg",
+ expectedWidth, expectedHeight));
+ }
+ if (config != null) {
+ assertEquals("Actual config is wrong", config, params.getActualConfig());
+ }
+ assertEquals("Bitmap width is wrong", expectedWidth, bitmap.getWidth());
+ assertEquals("Bitmap height is wrong", expectedHeight, bitmap.getHeight());
+ }
+
public void testGetScaledFrameAtTime() {
int resId = R.raw.binary_counter_320x240_30fps_600frames;
if (!MediaUtils.hasCodecForResourceAndDomain(getContext(), resId, "video/")
@@ -499,175 +713,26 @@
return;
}
- MediaMetadataRetriever retriever = new MediaMetadataRetriever();
- Resources resources = getContext().getResources();
- AssetFileDescriptor afd = resources.openRawResourceFd(resId);
-
- retriever.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
- try {
- afd.close();
- } catch (IOException e) {
- fail("Unable to close file");
- }
-
- try {
- Bitmap bitmap = retriever.getScaledFrameAtTime(
- 2066666 /*timeUs*/ , OPTION_CLOSEST, 0 /*width*/, 120 /*height*/);
- fail("Failed to receive exception");
- } catch (IllegalArgumentException e) {
- // Expect exception
- }
-
- try {
- Bitmap bitmap = retriever.getScaledFrameAtTime(
- 2066666 /*timeUs*/ , OPTION_CLOSEST, -1 /*width*/, 0 /*height*/);
- fail("Failed to receive exception");
- } catch (IllegalArgumentException e) {
- // Expect exception
- }
-
- try {
- Bitmap bitmap = retriever.getScaledFrameAtTime(
- 2066666 /*timeUs*/ , OPTION_CLOSEST, -1 /*width*/, 120 /*height*/);
- fail("Failed to receive exception");
- } catch (IllegalArgumentException e) {
- // Expect exception
- }
-
- try {
- Bitmap bitmap = retriever.getScaledFrameAtTime(
- 2066666 /*timeUs */, OPTION_CLOSEST, 140 /*width*/, -1 /*height*/);
- fail("Failed to receive exception");
- } catch (IllegalArgumentException e) {
- // Expect exception
- }
-
- try {
- Bitmap bitmap = retriever.getScaledFrameAtTime(
- 2066666 /*timeUs */, OPTION_CLOSEST, -1 /*width*/, -1 /*height*/);
- fail("Failed to receive exception");
- } catch (IllegalArgumentException e) {
- // Expect exception
- }
+ setDataSourceFd(resId);
+ MediaMetadataRetriever.BitmapParams params = new MediaMetadataRetriever.BitmapParams();
// Test desided size of 160 x 120. Return should be 160 x 120
- try {
- Bitmap bitmap = retriever.getScaledFrameAtTime(
- 2066666 /*timeUs */, OPTION_CLOSEST, 160 /*width*/, 120 /*height*/);
- if (bitmap == null) {
- fail("Failed to get scaled bitmap");
- }
- if (SAVE_BITMAP_OUTPUT) {
- CodecUtils.saveBitmapToFile(bitmap, "test_160x120" + ".jpg");
- }
-
- if (bitmap.getWidth() != 160 /* width */) {
- fail("Bitmap width is " + bitmap.getWidth() + "Expect: 160");
- }
- if (bitmap.getHeight() != 120 /* height */) {
- fail("Bitmap height is " + bitmap.getHeight() + "Expect: 120");
- }
-
- } catch (Exception e) {
- fail("Exception getting bitmap: " + e);
- }
+ testGetScaledFrameAtTime(160, 120, 160, 120, Bitmap.Config.ARGB_8888);
// Test scaled up bitmap to 640 x 480. Return should be 640 x 480
- try {
- Bitmap bitmap = retriever.getScaledFrameAtTime(
- 2066666 /*timeUs */, OPTION_CLOSEST, 640 /*width*/, 480 /*height*/);
- if (bitmap == null) {
- fail("Failed to get scaled bitmap");
- }
- if (SAVE_BITMAP_OUTPUT) {
- CodecUtils.saveBitmapToFile(bitmap, "test_640x480" + ".jpg");
- }
-
- if (bitmap.getWidth() != 640 /* width */) {
- fail("Bitmap width is " + bitmap.getWidth() + "Expect: 640");
- }
- if (bitmap.getHeight() != 480 /* height */) {
- fail("Bitmap height is " + bitmap.getHeight() + "Expect: 480");
- }
-
- } catch (Exception e) {
- fail("Exception getting bitmap: " + e);
- }
+ testGetScaledFrameAtTime(640, 480, 640, 480, Bitmap.Config.ARGB_8888);
// Test scaled up bitmap to 320 x 120. Return should be 160 x 120
- try {
- Bitmap bitmap = retriever.getScaledFrameAtTime(
- 2066666 /*timeUs */, OPTION_CLOSEST, 320 /*width*/, 120 /*height*/);
- if (bitmap == null) {
- fail("Failed to get scaled bitmap");
- }
- if (SAVE_BITMAP_OUTPUT) {
- CodecUtils.saveBitmapToFile(bitmap, "test_320x120" + ".jpg");
- }
-
- if (bitmap.getWidth() != 160 /* width */) {
- fail("Bitmap width is " + bitmap.getWidth() + "Expect: 160");
- }
- if (bitmap.getHeight() != 120 /* height */) {
- fail("Bitmap height is " + bitmap.getHeight() + "Expect: 120");
- }
-
- } catch (Exception e) {
- fail("Exception getting bitmap: " + e);
- }
+ testGetScaledFrameAtTime(320, 120, 160, 120, Bitmap.Config.RGB_565);
// Test scaled up bitmap to 160 x 240. Return should be 160 x 120
- try {
- Bitmap bitmap = retriever.getScaledFrameAtTime(
- 2066666 /*timeUs */, OPTION_CLOSEST, 160 /*width*/, 240 /*height*/);
- if (bitmap == null) {
- fail("Failed to get scaled bitmap");
- }
- if (SAVE_BITMAP_OUTPUT) {
- CodecUtils.saveBitmapToFile(bitmap, "test_160x240" + ".jpg");
- }
-
- if (bitmap.getWidth() != 160 /* width */) {
- fail("Bitmap width is " + bitmap.getWidth() + "Expect: 160");
- }
- if (bitmap.getHeight() != 120 /* height */) {
- fail("Bitmap height is " + bitmap.getHeight() + "Expect: 120");
- }
-
- } catch (Exception e) {
- fail("Exception getting bitmap: " + e);
- }
+ testGetScaledFrameAtTime(160, 240, 160, 120, Bitmap.Config.RGB_565);
// Test scaled the video with aspect ratio
resId = R.raw.binary_counter_320x240_720x240_30fps_600frames;
- afd = resources.openRawResourceFd(resId);
+ setDataSourceFd(resId);
- retriever.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
- try {
- afd.close();
- } catch (IOException e) {
- fail("Unable to close file");
- }
- try {
- Bitmap bitmap = retriever.getScaledFrameAtTime(
- 2066666 /*timeUs */, OPTION_CLOSEST, 330 /*width*/, 240 /*height*/);
- if (bitmap == null) {
- fail("Failed to get scaled bitmap");
- }
- if (SAVE_BITMAP_OUTPUT) {
- CodecUtils.saveBitmapToFile(bitmap, "test_330x240" + ".jpg");
- }
-
- if (bitmap.getWidth() != 330 /* width */) {
- fail("Bitmap width is " + bitmap.getWidth() + "Expect: 330");
- }
- if (bitmap.getHeight() != 110 /* height */) {
- fail("Bitmap height is " + bitmap.getHeight() + "Expect: 110");
- }
-
- } catch (Exception e) {
- fail("Exception getting bitmap: " + e);
- }
+ testGetScaledFrameAtTime(330, 240, 330, 110, null);
}
public void testGetImageAtIndex() throws Exception {
@@ -701,37 +766,32 @@
int resId, int width, int height, int rotation,
int imageCount, int primary, boolean useGrid, boolean checkColor)
throws Exception {
- MediaMetadataRetriever retriever = null;
+ Stopwatch timer = new Stopwatch();
MediaExtractor extractor = null;
AssetFileDescriptor afd = null;
InputStream inputStream = null;
try {
- retriever = new MediaMetadataRetriever();
-
- Resources resources = getContext().getResources();
- afd = resources.openRawResourceFd(resId);
-
- retriever.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
+ setDataSourceFd(resId);
// Verify image related meta keys.
- String hasImage = retriever.extractMetadata(
+ String hasImage = mRetriever.extractMetadata(
MediaMetadataRetriever.METADATA_KEY_HAS_IMAGE);
assertTrue("No images found in resId " + resId, "yes".equals(hasImage));
assertEquals("Wrong width", width,
- Integer.parseInt(retriever.extractMetadata(
+ Integer.parseInt(mRetriever.extractMetadata(
MediaMetadataRetriever.METADATA_KEY_IMAGE_WIDTH)));
assertEquals("Wrong height", height,
- Integer.parseInt(retriever.extractMetadata(
+ Integer.parseInt(mRetriever.extractMetadata(
MediaMetadataRetriever.METADATA_KEY_IMAGE_HEIGHT)));
assertEquals("Wrong rotation", rotation,
- Integer.parseInt(retriever.extractMetadata(
+ Integer.parseInt(mRetriever.extractMetadata(
MediaMetadataRetriever.METADATA_KEY_IMAGE_ROTATION)));
assertEquals("Wrong image count", imageCount,
- Integer.parseInt(retriever.extractMetadata(
+ Integer.parseInt(mRetriever.extractMetadata(
MediaMetadataRetriever.METADATA_KEY_IMAGE_COUNT)));
assertEquals("Wrong primary index", primary,
- Integer.parseInt(retriever.extractMetadata(
+ Integer.parseInt(mRetriever.extractMetadata(
MediaMetadataRetriever.METADATA_KEY_IMAGE_PRIMARY)));
if (checkColor) {
@@ -740,7 +800,10 @@
// Also check the position of the color block, which should move left-to-right
// with the index.
for (int imageIndex = 0; imageIndex < imageCount; imageIndex++) {
- bitmap = retriever.getImageAtIndex(imageIndex);
+ timer.start();
+ bitmap = mRetriever.getImageAtIndex(imageIndex);
+ timer.end();
+ timer.printDuration("getImageAtIndex");
for (int barIndex = 0; barIndex < COLOR_BARS.length; barIndex++) {
Rect r = getColorBarRect(barIndex, width, height);
@@ -759,7 +822,12 @@
// Check the color block position on the primary image.
Rect r = getColorBlockRect(primary, width, height);
- bitmap = retriever.getPrimaryImage();
+
+ timer.start();
+ bitmap = mRetriever.getPrimaryImage();
+ timer.end();
+ timer.printDuration("getPrimaryImage");
+
assertTrue("Color block for primary image doesn't match",
approxEquals(COLOR_BLOCK, Color.valueOf(
bitmap.getPixel(r.centerX(), height - r.centerY()))));
@@ -778,6 +846,8 @@
// Check the grid configuration related keys.
if (useGrid) {
extractor = new MediaExtractor();
+ Resources resources = getContext().getResources();
+ afd = resources.openRawResourceFd(resId);
extractor.setDataSource(
afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
MediaFormat format = extractor.getTrackFormat(0);
@@ -793,9 +863,6 @@
} catch (IOException e) {
fail("Unable to open file");
} finally {
- if (retriever != null) {
- retriever.release();
- }
if (extractor != null) {
extractor.release();
}
@@ -807,4 +874,34 @@
}
}
}
+
+ private class Stopwatch {
+ private long startTimeMs;
+ private long endTimeMs;
+ private boolean isStartCalled;
+
+ public Stopwatch() {
+ startTimeMs = endTimeMs = 0;
+ isStartCalled = false;
+ }
+
+ public void start() {
+ startTimeMs = System.currentTimeMillis();
+ isStartCalled = true;
+ }
+
+ public void end() {
+ endTimeMs = System.currentTimeMillis();
+ if (!isStartCalled) {
+ Log.e(TAG, "Error: end() must be called after start()!");
+ return;
+ }
+ isStartCalled = false;
+ }
+
+ public void printDuration(String functionName) {
+ long duration = endTimeMs - startTimeMs;
+ Log.i(TAG, String.format("%s() took %d ms.", functionName, duration));
+ }
+ }
}
diff --git a/tests/tests/media/src/android/media/cts/MediaMuxerTest.java b/tests/tests/media/src/android/media/cts/MediaMuxerTest.java
index c873691..670bb16 100644
--- a/tests/tests/media/src/android/media/cts/MediaMuxerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaMuxerTest.java
@@ -314,7 +314,8 @@
// No start offsets for any track.
cloneMediaWithSamplesDropAndStartOffsets(sourceId, outputFilePath,
MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4, null, null);
- verifyTimestampsWithSamplesDropSet(sourceId, outputFilePath, null, null);
+ verifyTSWithSamplesDropAndStartOffset(
+ sourceId, true /* has B frames */, outputFilePath, null, null);
} finally {
new File(outputFilePath).delete();
}
@@ -335,7 +336,8 @@
// No start offsets for any track.
cloneMediaWithSamplesDropAndStartOffsets(sourceId, outputFilePath,
MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4, samplesDropSet, null);
- verifyTimestampsWithSamplesDropSet(sourceId, outputFilePath, samplesDropSet, null);
+ verifyTSWithSamplesDropAndStartOffset(
+ sourceId, true /* has B frames */, outputFilePath, samplesDropSet, null);
} finally {
new File(outputFilePath).delete();
}
@@ -358,7 +360,8 @@
// No start offsets for any track.
cloneMediaWithSamplesDropAndStartOffsets(sourceId, outputFilePath,
MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4, samplesDropSet, null);
- verifyTimestampsWithSamplesDropSet(sourceId, outputFilePath, samplesDropSet, null);
+ verifyTSWithSamplesDropAndStartOffset(
+ sourceId, true /* has B frames */, outputFilePath, samplesDropSet, null);
} finally {
new File(outputFilePath).delete();
}
@@ -379,7 +382,8 @@
// No start offsets for any track.
cloneMediaWithSamplesDropAndStartOffsets(sourceId, outputFilePath,
MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4, samplesDropSet, null);
- verifyTimestampsWithSamplesDropSet(sourceId, outputFilePath, samplesDropSet, null);
+ verifyTSWithSamplesDropAndStartOffset(
+ sourceId, true /* has B frames */, outputFilePath, samplesDropSet, null);
} finally {
new File(outputFilePath).delete();
}
@@ -402,7 +406,8 @@
// No start offsets for any track.
cloneMediaWithSamplesDropAndStartOffsets(sourceId, outputFilePath,
MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4, samplesDropSet, null);
- verifyTimestampsWithSamplesDropSet(sourceId, outputFilePath, samplesDropSet, null);
+ verifyTSWithSamplesDropAndStartOffset(
+ sourceId, true /* has B frames */, outputFilePath, samplesDropSet, null);
} finally {
new File(outputFilePath).delete();
}
@@ -424,7 +429,8 @@
startOffsetUsVect.add(0);
cloneMediaWithSamplesDropAndStartOffsets(sourceId, outputFilePath,
MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4, null, startOffsetUsVect);
- verifyTimestampsWithSamplesDropSet(sourceId, outputFilePath, null, startOffsetUsVect);
+ verifyTSWithSamplesDropAndStartOffset(
+ sourceId, true /* has B frames */, outputFilePath, null, startOffsetUsVect);
} finally {
new File(outputFilePath).delete();
}
@@ -446,7 +452,104 @@
startOffsetUsVect.add(400000);
cloneMediaWithSamplesDropAndStartOffsets(sourceId, outputFilePath,
MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4, null, startOffsetUsVect);
- verifyTimestampsWithSamplesDropSet(sourceId, outputFilePath, null, startOffsetUsVect);
+ verifyTSWithSamplesDropAndStartOffset(
+ sourceId, true /* has B frames */, outputFilePath, null, startOffsetUsVect);
+ } finally {
+ new File(outputFilePath).delete();
+ }
+ }
+
+ /**
+ * Test: make sure if audio/video muxing using MPEG4Writer works when audio
+ * samples start later than video.
+ */
+ public void testTimestampsStartOffsetAudio() throws Exception {
+ int sourceId = R.raw.video_480x360_mp4_h264_500kbps_30fps_aac_stereo_128kbps_44100hz;
+ String outputFilePath =
+ File.createTempFile("MediaMuxerTest_testTimestampsStartOffsetAudio", ".mp4")
+ .getAbsolutePath();
+ try {
+ Vector<Integer> startOffsetUsVect = new Vector<Integer>();
+ // Video starts at 0us.
+ startOffsetUsVect.add(0);
+ // Audio starts at 50000us.
+ startOffsetUsVect.add(50000);
+ cloneMediaWithSamplesDropAndStartOffsets(sourceId, outputFilePath,
+ MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4, null, startOffsetUsVect);
+ verifyTSWithSamplesDropAndStartOffset(
+ sourceId, false /* no B frames */, outputFilePath, null, startOffsetUsVect);
+ } finally {
+ new File(outputFilePath).delete();
+ }
+ }
+
+ /**
+ * Test: makes sure if audio/video muxing using MPEG4Writer works when video
+ * samples start later than audio.
+ */
+ public void testTimestampsStartOffsetVideo() throws Exception {
+ int sourceId = R.raw.video_480x360_mp4_h264_500kbps_30fps_aac_stereo_128kbps_44100hz;
+ String outputFilePath =
+ File.createTempFile("MediaMuxerTest_testTimestampsStartOffsetVideo", ".mp4")
+ .getAbsolutePath();
+ try {
+ Vector<Integer> startOffsetUsVect = new Vector<Integer>();
+ // Video starts at 500000us.
+ startOffsetUsVect.add(500000);
+ // Audio starts at 0us.
+ startOffsetUsVect.add(0);
+ cloneMediaWithSamplesDropAndStartOffsets(sourceId, outputFilePath,
+ MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4, null, startOffsetUsVect);
+ verifyTSWithSamplesDropAndStartOffset(
+ sourceId, false /* no B frames */, outputFilePath, null, startOffsetUsVect);
+ } finally {
+ new File(outputFilePath).delete();
+ }
+ }
+
+ /**
+ * Test: makes sure if audio/video muxing using MPEG4Writer works when audio and video
+ * tracks start at non-zero start offset and audio samples start later than video.
+ */
+ public void testTimestampsStartOffsetVideoAudio() throws Exception {
+ int sourceId = R.raw.video_480x360_mp4_h264_500kbps_30fps_aac_stereo_128kbps_44100hz;
+ String outputFilePath =
+ File.createTempFile("MediaMuxerTest_testTimestampsStartOffsetAudio", ".mp4")
+ .getAbsolutePath();
+ try {
+ Vector<Integer> startOffsetUsVect = new Vector<Integer>();
+ // Video starts at 250000us.
+ startOffsetUsVect.add(250000);
+ // Audio starts at 500000us.
+ startOffsetUsVect.add(500000);
+ cloneMediaWithSamplesDropAndStartOffsets(sourceId, outputFilePath,
+ MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4, null, startOffsetUsVect);
+ verifyTSWithSamplesDropAndStartOffset(
+ sourceId, false /* no B frames */, outputFilePath, null, startOffsetUsVect);
+ } finally {
+ new File(outputFilePath).delete();
+ }
+ }
+
+ /**
+ * Test: makes sure if audio/video muxing using MPEG4Writer works when audio and video
+ * tracks start at non-zero start offset and video samples start later than audio.
+ */
+ public void testTimestampsStartOffsetAudioVideo() throws Exception {
+ int sourceId = R.raw.video_480x360_mp4_h264_500kbps_30fps_aac_stereo_128kbps_44100hz;
+ String outputFilePath =
+ File.createTempFile("MediaMuxerTest_testTimestampsStartOffsetVideo", ".mp4")
+ .getAbsolutePath();
+ try {
+ Vector<Integer> startOffsetUsVect = new Vector<Integer>();
+ // Video starts at 500000us.
+ startOffsetUsVect.add(500000);
+ // Audio starts at 250000us.
+ startOffsetUsVect.add(250000);
+ cloneMediaWithSamplesDropAndStartOffsets(sourceId, outputFilePath,
+ MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4, null, startOffsetUsVect);
+ verifyTSWithSamplesDropAndStartOffset(
+ sourceId, false /* no B frames */, outputFilePath, null, startOffsetUsVect);
} finally {
new File(outputFilePath).delete();
}
@@ -467,7 +570,8 @@
verifyLocationInFile(outputMediaFile);
}
// Verify timestamp of all samples.
- verifyTimestampsWithSamplesDropSet(srcMedia, outputMediaFile, null, null);
+ verifyTSWithSamplesDropAndStartOffset(
+ srcMedia, false /* no B frames */,outputMediaFile, null, null);
} finally {
new File(outputMediaFile).delete();
}
@@ -781,7 +885,7 @@
if (trackIndex == 0) {
++videoSampleCount;
if (VERBOSE) {
- Log.i(TAG, "videoSampleCount : " + videoSampleCount);
+ Log.v(TAG, "videoSampleCount : " + videoSampleCount);
}
if (videoSampleCount <= muxAllTypeVideoFramesUntilIndex
|| videoSampleCount == bFrameAfterPFrameIndex) {
@@ -959,10 +1063,15 @@
bufferInfo.presentationTimeUs = extractor.getSampleTime();
bufferInfo.flags = extractor.getSampleFlags();
int trackIndex = extractor.getSampleTrackIndex();
+ if (VERBOSE) {
+ Log.v(TAG, "TrackIndex:" + trackIndex + " PresentationTimeUs:" +
+ bufferInfo.presentationTimeUs + " Flags:" + bufferInfo.flags +
+ " Size(bytes)" + bufferInfo.size);
+ }
if (trackIndex == videoTrackIndex) {
++videoSampleCount;
if (VERBOSE) {
- Log.i(TAG, "videoSampleCount : " + videoSampleCount);
+ Log.v(TAG, "videoSampleCount : " + videoSampleCount);
}
if (samplesDropSet == null || (!samplesDropSet.contains(videoSampleCount))) {
// Write video frame with start offset adjustment.
@@ -971,7 +1080,7 @@
}
else {
if (VERBOSE) {
- Log.i(TAG, "skipped this frame");
+ Log.v(TAG, "skipped this frame");
}
}
} else {
@@ -1003,8 +1112,9 @@
* Uses MediaExtractors and checks whether timestamps of all samples except in samplesDropSet
* and with start offsets adjustments for each track match.
*/
- private void verifyTimestampsWithSamplesDropSet(int srcMediaId, String testMediaPath,
- HashSet<Integer> samplesDropSet, Vector<Integer> startOffsetUsVect) throws IOException {
+ private void verifyTSWithSamplesDropAndStartOffset(int srcMediaId, boolean hasBframes,
+ String testMediaPath, HashSet<Integer> samplesDropSet,
+ Vector<Integer> startOffsetUsVect) throws IOException {
AssetFileDescriptor srcFd = mResources.openRawResourceFd(srcMediaId);
MediaExtractor extractorSrc = new MediaExtractor();
extractorSrc.setDataSource(srcFd.getFileDescriptor(),
@@ -1014,8 +1124,19 @@
int videoTrackIndex = -1;
int videoStartOffsetUs = 0;
+ int minStartOffsetUs = Integer.MAX_VALUE;
int trackCount = extractorSrc.getTrackCount();
+ // MPEG4Writer makes the start timestamp of an earliest track as zero and adjusts all
+ // other tracks' timestamp accordingly.
+ if (startOffsetUsVect != null) {
+ for (int startOffsetUs : startOffsetUsVect) {
+ minStartOffsetUs = Math.min(startOffsetUs, minStartOffsetUs);
+ }
+ } else {
+ minStartOffsetUs = 0;
+ }
+
// Select video track.
for (int i = 0; i < trackCount; i++) {
MediaFormat format = extractorSrc.getTrackFormat(i);
@@ -1026,8 +1147,8 @@
}
extractorSrc.selectTrack(videoTrackIndex);
extractorTest.selectTrack(videoTrackIndex);
- checkVideoSamplesTimeStamps(extractorSrc, extractorTest, samplesDropSet,
- videoStartOffsetUs);
+ checkVideoSamplesTimeStamps(extractorSrc, hasBframes, extractorTest, samplesDropSet,
+ videoStartOffsetUs - minStartOffsetUs);
extractorSrc.unselectTrack(videoTrackIndex);
extractorTest.unselectTrack(videoTrackIndex);
}
@@ -1046,7 +1167,8 @@
}
extractorSrc.selectTrack(audioTrackIndex);
extractorTest.selectTrack(audioTrackIndex);
- checkAudioSamplesTimestamps(extractorSrc, extractorTest, audioStartOffsetUs);
+ checkAudioSamplesTimestamps(
+ extractorSrc, extractorTest, audioStartOffsetUs - minStartOffsetUs);
}
}
@@ -1056,9 +1178,8 @@
}
// Check timestamps of all video samples.
- private void checkVideoSamplesTimeStamps(MediaExtractor extractorSrc,
- MediaExtractor extractorTest, HashSet<Integer> samplesDropSet,
- int videoStartOffsetUs) {
+ private void checkVideoSamplesTimeStamps(MediaExtractor extractorSrc, boolean hasBFrames,
+ MediaExtractor extractorTest, HashSet<Integer> samplesDropSet, int videoStartOffsetUs) {
long srcSampleTimeUs = -1;
long testSampleTimeUs = -1;
boolean srcAdvance = false;
@@ -1068,43 +1189,64 @@
extractorSrc.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
extractorTest.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
+ if (VERBOSE) {
+ Log.v(TAG, "videoSampleCount:" + videoSampleCount);
+ Log.v(TAG, "srcTrackIndex:" + extractorSrc.getSampleTrackIndex() +
+ " testTrackIndex:" + extractorTest.getSampleTrackIndex());
+ }
+
do {
++videoSampleCount;
srcSampleTimeUs = extractorSrc.getSampleTime();
testSampleTimeUs = extractorTest.getSampleTime();
if (VERBOSE) {
- Log.i(TAG, "videoSampleCount:" + videoSampleCount);
- Log.i(TAG, "srcTrackIndex:" + extractorSrc.getSampleTrackIndex() +
- " testTrackIndex:" + extractorTest.getSampleTrackIndex());
Log.i(TAG, "srcTSus:" + srcSampleTimeUs + " testTSus:" + testSampleTimeUs);
}
if (samplesDropSet == null || !samplesDropSet.contains(videoSampleCount)) {
if (srcSampleTimeUs == -1 || testSampleTimeUs == -1) {
if (VERBOSE) {
- Log.d(TAG, "videoSampleCount:" + videoSampleCount);
- Log.d(TAG, "srcUs:" + srcSampleTimeUs + "testUs:" + testSampleTimeUs);
+ Log.v(TAG, "srcUs:" + srcSampleTimeUs + "testUs:" + testSampleTimeUs);
}
fail("either source or test track reached end of stream");
}
// Stts values within 0.1ms(100us) difference are fudged to save too many
// stts entries in MPEG4Writer.
- else if (Math.abs(srcSampleTimeUs + videoStartOffsetUs - testSampleTimeUs) > 100) {
+ // If Bframes are not present, then we manage start offset of video track in the
+ // duration of the first video sample. Second sample onwards has original timestamp.
+ else if (!hasBFrames
+ && ((videoSampleCount > 1
+ && Math.abs(srcSampleTimeUs + videoStartOffsetUs - testSampleTimeUs) > 100)
+ || (videoSampleCount == 1
+ && Math.abs(srcSampleTimeUs - testSampleTimeUs) > 100))) {
if (VERBOSE) {
- Log.d(TAG, "Fail:video timestamps didn't match");
- Log.d(TAG, "srcTrackIndex:" + extractorSrc.getSampleTrackIndex() +
- " testTrackIndex:" + extractorTest.getSampleTrackIndex());
- Log.d(TAG, "srcTSus:" + srcSampleTimeUs + " testTSus:" + testSampleTimeUs);
- Log.d(TAG, "videoSampleCount:" + videoSampleCount);
+ Log.v(TAG, "Fail:video timestamps didn't match");
+ Log.v(TAG, "srcTrackIndex:" + extractorSrc.getSampleTrackIndex() +
+ " testTrackIndex:" + extractorTest.getSampleTrackIndex());
+ Log.v(TAG, "srcTSus:" + srcSampleTimeUs + " testTSus:" + testSampleTimeUs);
}
fail("video timestamps didn't match");
}
+ // If Bframes are present, then we manage start offsert of a video track in the
+ // corresponding edit list entry.
+ else if (hasBFrames
+ && Math.abs(srcSampleTimeUs + videoStartOffsetUs - testSampleTimeUs)
+ > 100) {
+ if (VERBOSE) {
+ Log.v(TAG, "Fail:video timestamps with B frames didn't match");
+ Log.v(TAG, "srcTrackIndex:" + extractorSrc.getSampleTrackIndex() +
+ " testTrackIndex:" + extractorTest.getSampleTrackIndex());
+ Log.v(TAG, "srcTSus:" + srcSampleTimeUs + " testTSus:" + testSampleTimeUs);
+ }
+ fail("video timestamps with B frames didn't match");
+ }
+
testAdvance = extractorTest.advance();
}
srcAdvance = extractorSrc.advance();
} while(srcAdvance && testAdvance);
if (srcAdvance != testAdvance) {
if (VERBOSE) {
- Log.d(TAG, "videoSampleCount:" + videoSampleCount);
+ Log.v(TAG, "videoSampleCount:" + videoSampleCount);
}
fail("either video track has not reached its last sample");
}
@@ -1120,28 +1262,29 @@
extractorSrc.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
extractorTest.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
-
+ if (VERBOSE) {
+ Log.v(TAG, "audioStartOffsetUs: " + audioStartOffsetUs);
+ Log.v(TAG, "srcTrackIndex:" + extractorSrc.getSampleTrackIndex() +
+ " testTrackIndex:" + extractorTest.getSampleTrackIndex());
+ }
// Check timestamps of all audio samples.
do {
++audioSampleCount;
srcSampleTimeUs = extractorSrc.getSampleTime();
testSampleTimeUs = extractorTest.getSampleTime();
if(VERBOSE) {
- Log.d(TAG, "audioSampleCount:" + audioSampleCount);
- Log.v(TAG, "srcTrackIndex:" + extractorSrc.getSampleTrackIndex() +
- " testTrackIndex:" + extractorTest.getSampleTrackIndex());
+ Log.v(TAG, "audioSampleCount:" + audioSampleCount);
Log.v(TAG, "srcTSus:" + srcSampleTimeUs + " testTSus:" + testSampleTimeUs);
}
if (srcSampleTimeUs == -1 || testSampleTimeUs == -1) {
if (VERBOSE) {
- Log.d(TAG, "audioSampleCount:" + audioSampleCount);
- Log.d(TAG, "srcTSus:" + srcSampleTimeUs + " testTSus:" + testSampleTimeUs);
+ Log.v(TAG, "srcTSus:" + srcSampleTimeUs + " testTSus:" + testSampleTimeUs);
}
fail("either source or test track reached end of stream");
}
- // First audio sample would have zero timestamp and its start offset is implemented
- // by assigning the first audio sample's duration as the offset. Second sample onwards
+ // First audio sample would be at zero. Audio track's start offset is implemented
+ // by assigning the first audio sample's duration as that of the offset. Second sample
// would play after the offset. But video offset is achieved by edit list entry for
// video tracks with BFrames. Need to revert the conditional check for first
// audio sample once we implement empty edit list entry for audio.
diff --git a/tests/tests/media/src/android/media/cts/MediaRecorderTest.java b/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
index be33988..e2d4421 100644
--- a/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
@@ -37,6 +37,7 @@
import android.media.MediaRecorder;
import android.media.MediaRecorder.OnErrorListener;
import android.media.MediaRecorder.OnInfoListener;
+import android.media.MicrophoneDirection;
import android.media.MicrophoneInfo;
import android.media.cts.AudioRecordingConfigurationTest.MyAudioRecordingCallback;
import android.opengl.GLES20;
@@ -231,6 +232,7 @@
int height;
Camera camera = null;
if (!hasCamera()) {
+ MediaUtils.skipTest("no camera");
return;
}
// Try to get camera profile for QUALITY_LOW; if unavailable,
@@ -543,6 +545,7 @@
public void testRecorderVideo() throws Exception {
if (!hasCamera()) {
+ MediaUtils.skipTest("no camera");
return;
}
mCamera = Camera.open(0);
@@ -567,6 +570,7 @@
public void testSetOutputFile() throws Exception {
if (!hasCamera()) {
+ MediaUtils.skipTest("no camera");
return;
}
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
@@ -850,22 +854,22 @@
return startTimeOffset + frameIndex * 1000000 / frameRate;
}
- private void testLevel(String mediaType, int width, int height, int framerate,
- int bitrate, int profile, int requestedLevel, int... expectedLevels) throws Exception {
+ private int testLevel(String mediaType, int width, int height, int framerate, int bitrate,
+ int profile, int requestedLevel, int... expectedLevels) throws Exception {
CodecCapabilities cap = getCapsForPreferredCodecForMediaType(mediaType);
if (cap == null) { // not supported
- return;
+ return 0;
}
MediaCodecInfo.VideoCapabilities vCap = cap.getVideoCapabilities();
if (!vCap.areSizeAndRateSupported(width, height, framerate)
|| !vCap.getBitrateRange().contains(bitrate * 1000)) {
Log.i(TAG, "Skip the test");
- return;
+ return 0;
}
Surface surface = MediaCodec.createPersistentInputSurface();
if (surface == null) {
- return;
+ return 0;
}
InputSurface encSurface = new InputSurface(surface);
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
@@ -950,9 +954,11 @@
encSurface.release();
encSurface = null;
}
+ return 1;
}
public void testProfileAvcBaselineLevel1() throws Exception {
+ int testsRun = 0;
int profile = AVCProfileBaseline;
if (!hasH264()) {
@@ -961,16 +967,18 @@
}
/* W H fps kbps profile request level expected levels */
- testLevel(AVC, 176, 144, 15, 64, profile, AVCLevel1, AVCLevel1);
+ testsRun += testLevel(AVC, 176, 144, 15, 64, profile, AVCLevel1, AVCLevel1);
// Enable them when vendor fixes the failure
//testLevel(AVC, 178, 144, 15, 64, profile, AVCLevel1, AVCLevel11);
//testLevel(AVC, 178, 146, 15, 64, profile, AVCLevel1, AVCLevel11);
//testLevel(AVC, 176, 144, 16, 64, profile, AVCLevel1, AVCLevel11);
//testLevel(AVC, 176, 144, 15, 65, profile, AVCLevel1, AVCLevel1b);
- testLevel(AVC, 176, 144, 15, 64, profile, AVCLevel1b, AVCLevel1,
- AVCLevel1b);
+ testsRun += testLevel(AVC, 176, 144, 15, 64, profile, AVCLevel1b, AVCLevel1, AVCLevel1b);
// testLevel(AVC, 176, 144, 15, 65, profile, AVCLevel2, AVCLevel1b,
// AVCLevel11, AVCLevel12, AVCLevel13, AVCLevel2);
+ if (testsRun == 0) {
+ MediaUtils.skipTest("VideoCapabilities or surface not found");
+ }
}
@@ -1705,5 +1713,45 @@
config.isClientSilenced();
}
+ /*
+ * Microphone Direction API tests
+ */
+ public void testSetPreferredMicrophoneDirection() {
+ if (!hasMicrophone()) {
+ return;
+ }
+
+ try {
+ boolean succecss =
+ mMediaRecorder.setPreferredMicrophoneDirection(
+ MicrophoneDirection.MIC_DIRECTION_TOWARDS_USER);
+
+ // Can't actually test this as HAL may not have implemented it
+ // Just verify that it doesn't crash or throw an exception
+ // assertTrue(succecss);
+ } catch (Exception ex) {
+ Log.e(TAG, "testSetPreferredMicrophoneDirection() exception:" + ex);
+ assertTrue(false);
+ }
+ return;
+ }
+
+ public void testSetPreferredMicrophoneFieldDimension() {
+ if (!hasMicrophone()) {
+ return;
+ }
+
+ try {
+ boolean succecss = mMediaRecorder.setPreferredMicrophoneFieldDimension(1.0f);
+
+ // Can't actually test this as HAL may not have implemented it
+ // Just verify that it doesn't crash or throw an exception
+ // assertTrue(succecss);
+ } catch (Exception ex) {
+ Log.e(TAG, "testSetPreferredMicrophoneFieldDimension() exception:" + ex);
+ assertTrue(false);
+ }
+ return;
+ }
}
diff --git a/tests/tests/media/src/android/media/cts/MediaScannerConnectionTest.java b/tests/tests/media/src/android/media/cts/MediaScannerConnectionTest.java
index 8ca7df7..f46bd24 100644
--- a/tests/tests/media/src/android/media/cts/MediaScannerConnectionTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaScannerConnectionTest.java
@@ -76,13 +76,10 @@
mMediaScannerConnection.connect();
checkConnectionState(true);
- assertTrue(mMediaScannerConnection.mIsOnServiceConnectedCalled);
mMediaScannerConnection.disconnect();
checkConnectionState(false);
- // FIXME: onServiceDisconnected is not called.
- assertFalse(mMediaScannerConnection.mIsOnServiceDisconnectedCalled);
mMediaScannerConnection.connect();
checkConnectionState(true);
@@ -117,25 +114,9 @@
}
class MockMediaScannerConnection extends MediaScannerConnection {
-
- public boolean mIsOnServiceConnectedCalled;
- public boolean mIsOnServiceDisconnectedCalled;
public MockMediaScannerConnection(Context context, MediaScannerConnectionClient client) {
super(context, client);
}
-
- @Override
- public void onServiceConnected(ComponentName className, IBinder service) {
- super.onServiceConnected(className, service);
- mIsOnServiceConnectedCalled = true;
- }
-
- @Override
- public void onServiceDisconnected(ComponentName className) {
- super.onServiceDisconnected(className);
- mIsOnServiceDisconnectedCalled = true;
- // this is not called.
- }
}
class MockMediaScannerConnectionClient implements MediaScannerConnectionClient {
diff --git a/tests/tests/media/src/android/media/cts/SoundPoolTest.java b/tests/tests/media/src/android/media/cts/SoundPoolTest.java
index 2c56acd..c426a31 100644
--- a/tests/tests/media/src/android/media/cts/SoundPoolTest.java
+++ b/tests/tests/media/src/android/media/cts/SoundPoolTest.java
@@ -39,9 +39,9 @@
private static final int SOUNDPOOL_STREAMS = 4;
private static final int PRIORITY = 1;
- private static final int LOUD = 20;
- private static final int QUIET = LOUD / 2;
- private static final int SILENT = 0;
+ private static final float LOUD = 1.f;
+ private static final float QUIET = LOUD / 4.f;
+ private static final float SILENT = 0.f;
private File mFile;
private SoundPool mSoundPool;
diff --git a/tests/tests/media/src/android/media/cts/VideoEncoderTest.java b/tests/tests/media/src/android/media/cts/VideoEncoderTest.java
index c7018a2..81e6a4c 100644
--- a/tests/tests/media/src/android/media/cts/VideoEncoderTest.java
+++ b/tests/tests/media/src/android/media/cts/VideoEncoderTest.java
@@ -75,6 +75,10 @@
private LinkedList<Pair<ByteBuffer, BufferInfo>> mStream;
private MediaFormat mFormat;
private int mInputBufferSize;
+ // Media buffers(no CSD, no EOS) enqueued.
+ private int mMediaBuffersEnqueuedCount;
+ // Media buffers decoded.
+ private int mMediaBuffersDecodedCount;
public VideoStorage() {
mStream = new LinkedList<Pair<ByteBuffer, BufferInfo>>();
@@ -93,6 +97,9 @@
BufferInfo savedInfo = new BufferInfo();
savedInfo.set(0, savedBuffer.position(), info.presentationTimeUs, info.flags);
mStream.addLast(Pair.create(savedBuffer, savedInfo));
+ if (info.size > 0 && (info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
+ ++mMediaBuffersEnqueuedCount;
+ }
}
private void play(MediaCodec decoder, Surface surface) {
@@ -101,6 +108,9 @@
final Iterator<Pair<ByteBuffer, BufferInfo>> it = mStream.iterator();
decoder.setCallback(new MediaCodec.Callback() {
public void onOutputBufferAvailable(MediaCodec codec, int ix, BufferInfo info) {
+ if (info.size > 0) {
+ ++mMediaBuffersDecodedCount;
+ }
codec.releaseOutputBuffer(ix, info.size > 0);
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
synchronized (condition) {
@@ -146,17 +156,25 @@
}
}
decoder.stop();
+ // All enqueued media data buffers should have got decoded.
+ if (mMediaBuffersEnqueuedCount != mMediaBuffersDecodedCount) {
+ Log.i(TAG, "mMediaBuffersEnqueuedCount:" + mMediaBuffersEnqueuedCount);
+ Log.i(TAG, "mMediaBuffersDecodedCount:" + mMediaBuffersDecodedCount);
+ fail("not all enqueued encoded media buffers were decoded");
+ }
+ mMediaBuffersDecodedCount = 0;
}
- public void playAll(Surface surface) {
+ public boolean playAll(Surface surface) {
+ boolean skipped = true;
if (mFormat == null) {
Log.i(TAG, "no stream to play");
- return;
+ return !skipped;
}
String mime = mFormat.getString(MediaFormat.KEY_MIME);
MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
for (MediaCodecInfo info : mcl.getCodecInfos()) {
- if (info.isEncoder()) {
+ if (info.isEncoder() || info.isAlias()) {
continue;
}
MediaCodec codec = null;
@@ -171,7 +189,9 @@
}
play(codec, surface);
codec.release();
+ skipped = false;
}
+ return !skipped;
}
}
@@ -405,9 +425,7 @@
}
}
- public void playBack(Surface surface) {
- mEncodedStream.playAll(surface);
- }
+ public boolean playBack(Surface surface) { return mEncodedStream.playAll(surface); }
public void setFrameAndBitRates(int frameRate, int bitRate) {
mFrameRate = frameRate;
@@ -1147,7 +1165,7 @@
boolean success = processor.processLoop(
SOURCE_URL, mMime, mName, width, height, optional);
if (success) {
- processor.playBack(getActivity().getSurfaceHolder().getSurface());
+ success = processor.playBack(getActivity().getSurfaceHolder().getSurface());
}
return success;
}
@@ -1195,7 +1213,7 @@
ArrayList<Encoder> result = new ArrayList<Encoder>();
for (MediaCodecInfo info : mcl.getCodecInfos()) {
- if (!info.isEncoder() || !info.isVendor() != goog) {
+ if (!info.isEncoder() || !info.isVendor() != goog || info.isAlias()) {
continue;
}
CodecCapabilities caps = null;
diff --git a/tests/tests/midi/OWNERS b/tests/tests/midi/OWNERS
new file mode 100644
index 0000000..e78e1d0
--- /dev/null
+++ b/tests/tests/midi/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 1344
+philburk@google.com
diff --git a/tests/tests/multiuser/OWNERS b/tests/tests/multiuser/OWNERS
new file mode 100644
index 0000000..2b344eb
--- /dev/null
+++ b/tests/tests/multiuser/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 71510
+include /tests/app/OWNERS
+
+bookatz@google.com
diff --git a/tests/tests/nativehardware/jni/AHardwareBufferGLTest.cpp b/tests/tests/nativehardware/jni/AHardwareBufferGLTest.cpp
index 91949db..42d3057 100644
--- a/tests/tests/nativehardware/jni/AHardwareBufferGLTest.cpp
+++ b/tests/tests/nativehardware/jni/AHardwareBufferGLTest.cpp
@@ -1556,10 +1556,7 @@
}
}
} else {
- // TODO(b/123042748): The fact that glEGLImageTargetTexStorageEXT does not work for YUV
- // textures is a bug. The condition for the target should be removed
- // once the bug is fixed.
- if (HasGLExtension("GL_EXT_EGL_image_storage") && mTexTarget != GL_TEXTURE_EXTERNAL_OES) {
+ if (HasGLExtension("GL_EXT_EGL_image_storage")) {
glEGLImageTargetTexStorageEXT(mTexTarget, static_cast<GLeglImageOES>(mEGLImage),
nullptr);
} else {
diff --git a/tests/tests/nativemedia/mediametrics/Android.bp b/tests/tests/nativemedia/mediametrics/Android.bp
new file mode 100644
index 0000000..6e1ecc1
--- /dev/null
+++ b/tests/tests/nativemedia/mediametrics/Android.bp
@@ -0,0 +1,54 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Build the unit tests.
+
+cc_test {
+ name: "CtsNativeMediaMetricsTestCases",
+
+ compile_multilib: "both",
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
+
+ srcs: ["src/MediaMetricsTest.cpp"],
+
+ shared_libs: [
+ "liblog",
+ "libmediametrics",
+ "libutils",
+ ],
+
+ static_libs: [
+ "libgtest",
+ ],
+
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+
+}
diff --git a/tests/tests/nativemedia/mediametrics/AndroidTest.xml b/tests/tests/nativemedia/mediametrics/AndroidTest.xml
new file mode 100644
index 0000000..b84510f
--- /dev/null
+++ b/tests/tests/nativemedia/mediametrics/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for CTS Native Media Metrics test cases">
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="media" />
+ <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="cleanup" value="true" />
+ <option name="push" value="CtsNativeMediaMetricsTestCases->/data/local/tmp/CtsNativeMediaMetricsTestCases" />
+ <option name="append-bitness" value="true" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="CtsNativeMediaMetricsTestCases" />
+ <option name="runtime-hint" value="1m" />
+ </test>
+</configuration>
diff --git a/tests/tests/nativemedia/mediametrics/src/MediaMetricsTest.cpp b/tests/tests/nativemedia/mediametrics/src/MediaMetricsTest.cpp
new file mode 100644
index 0000000..c5b023d
--- /dev/null
+++ b/tests/tests/nativemedia/mediametrics/src/MediaMetricsTest.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "MediaMetricsTest"
+
+#include <gtest/gtest.h>
+#include <utils/Log.h>
+#include <MediaMetrics.h>
+
+//-----------------------------------------------------------------
+class MediaMetricsTest : public ::testing::Test {
+
+protected:
+ MediaMetricsTest() { }
+
+ virtual ~MediaMetricsTest() { }
+
+ /* Test setup*/
+ virtual void SetUp() {
+ handle_ = mediametrics_create("foo");
+ }
+
+ virtual void TearDown() {
+ mediametrics_delete(handle_);
+ }
+
+ mediametrics_handle_t handle_;
+};
+
+//-------------------------------------------------------------------------------------------------
+TEST_F(MediaMetricsTest, testCreateDelete) {
+ // Will be done with SetUp and TearDown.
+}
+
+TEST_F(MediaMetricsTest, testInt32) {
+ mediametrics_setInt32(handle_, "attr1", 100);
+ int32_t value;
+ EXPECT_TRUE(mediametrics_getInt32(handle_, "attr1", &value));
+ EXPECT_EQ(100, value);
+
+ mediametrics_addInt32(handle_, "attr1", 50);
+ EXPECT_TRUE(mediametrics_getInt32(handle_, "attr1", &value));
+ EXPECT_EQ(150, value);
+}
+
+TEST_F(MediaMetricsTest, testInt64) {
+ mediametrics_setInt64(handle_, "attr2", 1e10);
+ int64_t value;
+ EXPECT_TRUE(mediametrics_getInt64(handle_, "attr2", &value));
+ EXPECT_EQ(1e10, value);
+
+ mediametrics_addInt64(handle_, "attr2", 50);
+ EXPECT_TRUE(mediametrics_getInt64(handle_, "attr2", &value));
+ EXPECT_EQ(1e10 + 50, value);
+}
+
+TEST_F(MediaMetricsTest, testDouble) {
+ mediametrics_setDouble(handle_, "attr3", 100.0);
+ double value;
+ EXPECT_TRUE(mediametrics_getDouble(handle_, "attr3", &value));
+ EXPECT_DOUBLE_EQ(100.0, value);
+
+ mediametrics_addDouble(handle_, "attr3", 50.0);
+ EXPECT_TRUE(mediametrics_getDouble(handle_, "attr3", &value));
+ EXPECT_DOUBLE_EQ(150.0, value);
+}
+
+TEST_F(MediaMetricsTest, testRate) {
+ mediametrics_setRate(handle_, "attr4", 30, 1000);
+ int64_t count;
+ int64_t duration;
+ double rate;
+ EXPECT_TRUE(mediametrics_getRate(handle_, "attr4", &count, &duration, &rate));
+ EXPECT_EQ(30, count);
+ EXPECT_EQ(1000, duration);
+ EXPECT_DOUBLE_EQ(30/1000.0, rate);
+
+ mediametrics_addRate(handle_, "attr4", 29, 1000);
+ EXPECT_TRUE(mediametrics_getRate(handle_, "attr4", &count, &duration, &rate));
+ EXPECT_EQ(59, count);
+ EXPECT_EQ(2000, duration);
+ EXPECT_DOUBLE_EQ(59/2000.0, rate);
+}
+
+TEST_F(MediaMetricsTest, testCString) {
+ mediametrics_setCString(handle_, "attr5", "test_string");
+ char *value = nullptr;
+ EXPECT_TRUE(mediametrics_getCString(handle_, "attr5", &value));
+ EXPECT_STREQ("test_string", value);
+ mediametrics_freeCString(value);
+}
+
+TEST_F(MediaMetricsTest, testCount) {
+ mediametrics_setInt32(handle_, "attr1", 100);
+ EXPECT_EQ(1, mediametrics_count(handle_));
+ mediametrics_setInt32(handle_, "attr2", 200);
+ mediametrics_setInt32(handle_, "attr3", 300);
+ EXPECT_EQ(3, mediametrics_count(handle_));
+}
+
+TEST_F(MediaMetricsTest, testReadable) {
+ mediametrics_setInt32(handle_, "attr1", 1);
+ mediametrics_setInt64(handle_, "attr2", 2);
+ mediametrics_setDouble(handle_, "attr3", 3.0);
+ mediametrics_setRate(handle_, "attr4", 4, 5);
+ mediametrics_setCString(handle_, "attr5", "test_string");
+
+ EXPECT_TRUE(strlen(mediametrics_readable(handle_)) > 0);
+}
+
+TEST_F(MediaMetricsTest, testSelfRecord) {
+ mediametrics_setInt32(handle_, "attr1", 100);
+ mediametrics_setInt64(handle_, "attr2", 1e10);
+ mediametrics_setDouble(handle_, "attr3", 100.0);
+ mediametrics_setRate(handle_, "attr4", 30, 1000);
+ mediametrics_setCString(handle_, "attr5", "test_string");
+ mediametrics_setUid(handle_, 10000);
+
+ // An invalid key 'foo' is used here. Expecting FALSE.
+ EXPECT_FALSE(mediametrics_selfRecord(handle_));
+}
+
+TEST_F(MediaMetricsTest, testIsEnabled) {
+ EXPECT_TRUE(mediametrics_isEnabled());
+}
+
+int main(int argc, char **argv) {
+ testing::InitGoogleTest(&argc, argv);
+
+ return RUN_ALL_TESTS();
+}
+
diff --git a/tests/tests/nativemedia/sl/OWNERS b/tests/tests/nativemedia/sl/OWNERS
new file mode 100644
index 0000000..38b4a35
--- /dev/null
+++ b/tests/tests/nativemedia/sl/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 1344
+jmtrivi@google.com
diff --git a/tests/tests/nativemedia/xa/OWNERS b/tests/tests/nativemedia/xa/OWNERS
new file mode 100644
index 0000000..90b75e4
--- /dev/null
+++ b/tests/tests/nativemedia/xa/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 1344
+include ../sl/OWNERS
diff --git a/tests/tests/nativemidi/OWNERS b/tests/tests/nativemidi/OWNERS
new file mode 100644
index 0000000..ce5adf1
--- /dev/null
+++ b/tests/tests/nativemidi/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 48436
+pmclean@google.com
\ No newline at end of file
diff --git a/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTest.java b/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTest.java
index 9a6a7de..32e46ac 100644
--- a/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTest.java
+++ b/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTest.java
@@ -76,6 +76,16 @@
return pm.hasSystemFeature(PackageManager.FEATURE_MIDI);
}
+ public static boolean hasLibAMidi() {
+ try {
+ System.loadLibrary("amidi");
+ } catch (UnsatisfiedLinkError ex) {
+ Log.e(TAG, "libamidi.so not found.");
+ return false;
+ }
+ return true;
+ }
+
private byte[] generateRandomMessage(int len) {
byte[] buffer = new byte[len];
for(int index = 0; index < len; index++) {
@@ -131,7 +141,6 @@
}
protected void setUpEchoServer() throws Exception {
- Log.i(TAG, "++ setUpEchoServer()");
MidiDeviceInfo echoInfo = MidiEchoTestService.findEchoDevice(mContext);
// Open device.
@@ -161,7 +170,6 @@
}
protected void tearDownEchoServer() throws IOException {
- Log.i(TAG, "++ tearDownEchoServer()");
// Query echo service directly to see if it is getting status updates.
MidiEchoTestService echoService = MidiEchoTestService.getInstance();
@@ -209,11 +217,11 @@
//
@Before
public void setUp() throws Exception {
- Log.i(TAG, "++ setUp() mContext:" + mContext);
if (!hasMidiSupport()) {
Assert.assertTrue("FEATURE_MIDI Not Supported.", false);
return; // Not supported so don't test it.
}
+
mMidiManager = (MidiManager)mContext.getSystemService(Context.MIDI_SERVICE);
Assert.assertNotNull("Could not get the MidiManger.", mMidiManager);
@@ -222,16 +230,17 @@
@After
public void tearDown() throws Exception {
+ if (!hasMidiSupport()) {
+ Assert.assertTrue("FEATURE_MIDI Not Supported.", false);
+ return; // Not supported so don't test it.
+ }
tearDownEchoServer();
- Log.i(TAG, "++ tearDown()");
mMidiManager = null;
}
@Test
public void test_A_MidiManager() throws Exception {
- Log.i(TAG, "++++ test_A_MidiManager() this:" + System.identityHashCode(this));
-
if (!hasMidiSupport()) {
return; // Nothing to test
}
@@ -242,13 +251,19 @@
MidiDeviceInfo[] infos = mMidiManager.getDevices();
Assert.assertNotNull("device list was null", infos);
Assert.assertTrue("device list was empty", infos.length >= 1);
+ }
- Log.i(TAG, "++++ test_A_MidiManager() - DONE");
+
+ @Test
+ public void test_AA_LibAMidiExists() throws Exception {
+ Assert.assertTrue("libamidi.so not found.", hasLibAMidi());
}
@Test
public void test_B_SendData() throws Exception {
- Log.i(TAG, "++++ test_B_SendData() this:" + System.identityHashCode(this));
+ if (!hasMidiSupport()) {
+ return; // Nothing to test
+ }
Assert.assertEquals("Didn't start with 0 sends", 0, getNumSends(mTestContext));
Assert.assertEquals("Didn't start with 0 bytes sent", 0, getNumBytesSent(mTestContext));
@@ -262,13 +277,10 @@
Assert.assertTrue("Didn't get 1 send", getNumBytesSent(mTestContext) == buffer.length);
Assert.assertEquals("Didn't get right number of bytes sent",
buffer.length, getNumBytesSent(mTestContext));
-
- Log.i(TAG, "++++ test_B_SendData() - DONE");
}
@Test
public void test_C_EchoSmallMessage() throws Exception {
- Log.i(TAG, "++++ test_C_EchoSmallMessage() this:" + System.identityHashCode(this));
if (!hasMidiSupport()) {
return; // nothing to test
}
@@ -291,13 +303,10 @@
NativeMidiMessage message = getReceivedMessageAt(mTestContext, 0);
compareMessages(buffer, timestamp, message);
-
- Log.i(TAG, "++++ test_C_EchoSmallMessage() - DONE");
}
@Test
public void test_D_EchoNMessages() throws Exception {
- Log.i(TAG, "++++ test_D_EchoNMessages() this:" + System.identityHashCode(this));
if (!hasMidiSupport()) {
return; // nothing to test
}
@@ -325,13 +334,10 @@
NativeMidiMessage message = getReceivedMessageAt(mTestContext, msgIndex);
compareMessages(buffers[msgIndex], timestamps[msgIndex], message);
}
-
- Log.i(TAG, "++++ test_D_EchoNMessages() - DONE");
}
@Test
public void test_E_FlushMessages() throws Exception {
- Log.i(TAG, "++++ test_E_FlushMessages() this:" + System.identityHashCode(this));
if (!hasMidiSupport()) {
return; // nothing to test
}
@@ -362,13 +368,10 @@
NativeMidiMessage message = getReceivedMessageAt(mTestContext, msgIndex);
compareMessages(buffers[msgIndex], timestamps[msgIndex], message);
}
-
- Log.i(TAG, "++++ test_E_FlushMessages() - DONE");
}
@Test
public void test_F_HugeMessage() throws Exception {
- Log.i(TAG, "++++ test_F_HugeMessage() this:" + System.identityHashCode(this));
if (!hasMidiSupport()) {
return; // nothing to test
}
@@ -383,8 +386,6 @@
buffer = generateRandomMessage(kindaHugeMessageLen);
result = writeMidi(mTestContext, buffer, 0, buffer.length);
Assert.assertEquals("Kinda big write failed.", kindaHugeMessageLen, result);
-
- Log.i(TAG, "++++ test_F_HugeMessage() - DONE");
}
/**
@@ -393,7 +394,6 @@
*/
@Test
public void test_G_NativeEchoTime() throws Exception {
- Log.i(TAG, "++++ test_G_NativeEchoTime() this:" + System.identityHashCode(this));
if (!hasMidiSupport()) {
return; // nothing to test
}
@@ -426,13 +426,10 @@
"timestamp:" + message.timestamp + " received:" + message.timeReceived,
(elapsedNanos < maxLatencyNanos));
}
-
- Log.i(TAG, "++++ test_G_NativeEchoTime() - DONE");
}
@Test
public void test_H_EchoNMessages_PureNative() throws Exception {
- Log.i(TAG, "++++ test_H_EchoNMessages_PureNative() this:" + System.identityHashCode(this));
if (!hasMidiSupport()) {
return; // nothing to test
}
@@ -453,8 +450,6 @@
int result = matchNativeMessages(mTestContext);
Assert.assertEquals("Native Compare Test Failed", result, 0);
-
- Log.i(TAG, "++++ test_H_EchoNMessages_PureNative() - DONE");
}
/**
@@ -463,8 +458,6 @@
*/
@Test
public void test_I_NativeEchoTime_PureNative() throws Exception {
- Log.i(TAG, "++++ test_I_NativeEchoTime_PureNative() this:"
- + System.identityHashCode(this));
if (!hasMidiSupport()) {
return; // nothing to test
}
@@ -487,8 +480,6 @@
int result = checkNativeLatency(mTestContext, maxLatencyNanos);
Assert.assertEquals("failed pure native latency test.", 0, result);
-
- Log.i(TAG, "++++ test_I_NativeEchoTime_PureNative() - DONE");
}
// Native Routines
diff --git a/tests/tests/net/src/android/net/rtp/cts/AudioGroupTest.java b/tests/tests/net/src/android/net/rtp/cts/AudioGroupTest.java
index 1bd7fad..fee8621 100644
--- a/tests/tests/net/src/android/net/rtp/cts/AudioGroupTest.java
+++ b/tests/tests/net/src/android/net/rtp/cts/AudioGroupTest.java
@@ -62,7 +62,7 @@
mSocketB.connect(mStreamB.getLocalAddress(), mStreamB.getLocalPort());
mStreamB.associate(mSocketB.getLocalAddress(), mSocketB.getLocalPort());
- mGroup = new AudioGroup();
+ mGroup = new AudioGroup(mContext);
}
@Override
diff --git a/tests/tests/netsecpolicy/OWNERS b/tests/tests/netsecpolicy/OWNERS
new file mode 100644
index 0000000..fdd42d8
--- /dev/null
+++ b/tests/tests/netsecpolicy/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 36824
+include ../networksecurityconfig/OWNERS
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-attributes/OWNERS b/tests/tests/networksecurityconfig/networksecurityconfig-attributes/OWNERS
deleted file mode 100644
index 7705bfe..0000000
--- a/tests/tests/networksecurityconfig/networksecurityconfig-attributes/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-# Inherits OWNERS from parent directory.
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-basic-domain/OWNERS b/tests/tests/networksecurityconfig/networksecurityconfig-basic-domain/OWNERS
deleted file mode 100644
index 7705bfe..0000000
--- a/tests/tests/networksecurityconfig/networksecurityconfig-basic-domain/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-# Inherits OWNERS from parent directory.
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-cleartext-pre-P/OWNERS b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext-pre-P/OWNERS
deleted file mode 100644
index 7705bfe..0000000
--- a/tests/tests/networksecurityconfig/networksecurityconfig-cleartext-pre-P/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-# Inherits OWNERS from parent directory.
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-cleartext/OWNERS b/tests/tests/networksecurityconfig/networksecurityconfig-cleartext/OWNERS
deleted file mode 100644
index 7705bfe..0000000
--- a/tests/tests/networksecurityconfig/networksecurityconfig-cleartext/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-# Inherits OWNERS from parent directory.
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-disabled/OWNERS b/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-disabled/OWNERS
deleted file mode 100644
index 7705bfe..0000000
--- a/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-disabled/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-# Inherits OWNERS from parent directory.
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-enabled/OWNERS b/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-enabled/OWNERS
deleted file mode 100644
index 7705bfe..0000000
--- a/tests/tests/networksecurityconfig/networksecurityconfig-debug-basic-enabled/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-# Inherits OWNERS from parent directory.
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-downloadmanager/OWNERS b/tests/tests/networksecurityconfig/networksecurityconfig-downloadmanager/OWNERS
deleted file mode 100644
index 7705bfe..0000000
--- a/tests/tests/networksecurityconfig/networksecurityconfig-downloadmanager/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-# Inherits OWNERS from parent directory.
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-invalid-pin/OWNERS b/tests/tests/networksecurityconfig/networksecurityconfig-invalid-pin/OWNERS
deleted file mode 100644
index 7705bfe..0000000
--- a/tests/tests/networksecurityconfig/networksecurityconfig-invalid-pin/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-# Inherits OWNERS from parent directory.
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-nested-domains/OWNERS b/tests/tests/networksecurityconfig/networksecurityconfig-nested-domains/OWNERS
deleted file mode 100644
index 7705bfe..0000000
--- a/tests/tests/networksecurityconfig/networksecurityconfig-nested-domains/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-# Inherits OWNERS from parent directory.
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-resourcesrc/OWNERS b/tests/tests/networksecurityconfig/networksecurityconfig-resourcesrc/OWNERS
deleted file mode 100644
index 7705bfe..0000000
--- a/tests/tests/networksecurityconfig/networksecurityconfig-resourcesrc/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-# Inherits OWNERS from parent directory.
diff --git a/tests/tests/notificationlegacy/notificationlegacy20/OWNERS b/tests/tests/notificationlegacy/notificationlegacy20/OWNERS
deleted file mode 100644
index d6078e0..0000000
--- a/tests/tests/notificationlegacy/notificationlegacy20/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-# DO NOT DELETE: OWNERS is intended to be empty as module owner and bug component are defined
-# in parent directory. This file is needed as a placeholder for verification purpose as we
-# need to tell the difference between "no owner is specified for this module" vs "explicitly
-# defer to parent OWNERS file"
diff --git a/tests/tests/notificationlegacy/notificationlegacy27/OWNERS b/tests/tests/notificationlegacy/notificationlegacy27/OWNERS
deleted file mode 100644
index d6078e0..0000000
--- a/tests/tests/notificationlegacy/notificationlegacy27/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-# DO NOT DELETE: OWNERS is intended to be empty as module owner and bug component are defined
-# in parent directory. This file is needed as a placeholder for verification purpose as we
-# need to tell the difference between "no owner is specified for this module" vs "explicitly
-# defer to parent OWNERS file"
diff --git a/tests/tests/notificationlegacy/notificationlegacy28/OWNERS b/tests/tests/notificationlegacy/notificationlegacy28/OWNERS
deleted file mode 100644
index d6078e0..0000000
--- a/tests/tests/notificationlegacy/notificationlegacy28/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-# DO NOT DELETE: OWNERS is intended to be empty as module owner and bug component are defined
-# in parent directory. This file is needed as a placeholder for verification purpose as we
-# need to tell the difference between "no owner is specified for this module" vs "explicitly
-# defer to parent OWNERS file"
diff --git a/tests/tests/notificationlegacy/notificationlegacy29/OWNERS b/tests/tests/notificationlegacy/notificationlegacy29/OWNERS
deleted file mode 100644
index d6078e0..0000000
--- a/tests/tests/notificationlegacy/notificationlegacy29/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-# DO NOT DELETE: OWNERS is intended to be empty as module owner and bug component are defined
-# in parent directory. This file is needed as a placeholder for verification purpose as we
-# need to tell the difference between "no owner is specified for this module" vs "explicitly
-# defer to parent OWNERS file"
diff --git a/tests/tests/os/Android.bp b/tests/tests/os/Android.bp
index 6c48c87..1f9ca09 100644
--- a/tests/tests/os/Android.bp
+++ b/tests/tests/os/Android.bp
@@ -47,7 +47,7 @@
"vts",
"general-tests",
],
- platform_apis: true,
+ sdk_version: "test_current",
libs: [
"android.test.runner.stubs",
"android.test.base.stubs",
diff --git a/tests/tests/os/src/android/os/cts/EnvironmentTest.java b/tests/tests/os/src/android/os/cts/EnvironmentTest.java
index b00e1f2..0b02291 100644
--- a/tests/tests/os/src/android/os/cts/EnvironmentTest.java
+++ b/tests/tests/os/src/android/os/cts/EnvironmentTest.java
@@ -37,6 +37,7 @@
@AppModeFull(reason = "External directory not accessible by instant apps")
public void testEnvironmentExternal() {
+ assertTrue(Environment.getStorageDirectory().isDirectory());
assertTrue(Environment.getExternalStorageDirectory().isDirectory());
}
diff --git a/tests/tests/packageinstaller/atomicinstall/Android.bp b/tests/tests/packageinstaller/atomicinstall/Android.bp
index 95f8126..8b24ee5 100644
--- a/tests/tests/packageinstaller/atomicinstall/Android.bp
+++ b/tests/tests/packageinstaller/atomicinstall/Android.bp
@@ -18,14 +18,12 @@
srcs: ["src/**/*.java"],
java_resources: [
- ":AtomicInstallTestAppAv1",
- ":AtomicInstallTestAppAv2",
- ":AtomicInstallTestAppBv1",
":AtomicInstallCorrupt"
],
static_libs: [
"androidx.test.runner",
"truth-prebuilt",
+ "cts-install-lib",
],
sdk_version: "test_current",
test_suites: [
@@ -41,28 +39,3 @@
srcs: ["testdata/apk/prebuilt/corrupt.apk"],
path: "testdata/apk/prebuilt"
}
-
-android_test_helper_app {
- name: "AtomicInstallTestAppAv1",
-
- srcs: ["testdata/apk/src/**/*java"],
-
- manifest: "testdata/apk/Av1.xml",
-}
-
-android_test_helper_app {
- name: "AtomicInstallTestAppAv2",
-
- srcs: ["testdata/apk/src/**/*java"],
-
- manifest: "testdata/apk/Av2.xml",
-}
-
-android_test_helper_app {
- name: "AtomicInstallTestAppBv1",
-
- srcs: ["testdata/apk/src/**/*java"],
-
- manifest: "testdata/apk/Bv1.xml",
-}
-
diff --git a/tests/tests/packageinstaller/atomicinstall/AndroidManifest.xml b/tests/tests/packageinstaller/atomicinstall/AndroidManifest.xml
index e0edcba..1f8f283 100644
--- a/tests/tests/packageinstaller/atomicinstall/AndroidManifest.xml
+++ b/tests/tests/packageinstaller/atomicinstall/AndroidManifest.xml
@@ -18,7 +18,7 @@
package="com.android.tests.atomicinstall" >
<application>
- <receiver android:name="com.android.tests.atomicinstall.LocalIntentSender"
+ <receiver android:name="com.android.cts.install.lib.LocalIntentSender"
android:exported="true" />
<uses-library android:name="android.test.runner" />
</application>
diff --git a/tests/tests/packageinstaller/atomicinstall/AndroidTest.xml b/tests/tests/packageinstaller/atomicinstall/AndroidTest.xml
index e248a62..88a142d 100644
--- a/tests/tests/packageinstaller/atomicinstall/AndroidTest.xml
+++ b/tests/tests/packageinstaller/atomicinstall/AndroidTest.xml
@@ -24,8 +24,10 @@
<option name="test-file-name" value="CtsAtomicInstallTestCases.apk" />
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
- <option name="teardown-command" value="pm uninstall com.android.tests.atomicinstall.testapp.A" />
- <option name="teardown-command" value="pm uninstall com.android.tests.atomicinstall.testapp.B" />
+ <option name="run-command" value="pm uninstall com.android.cts.install.lib.testapp.A" />
+ <option name="run-command" value="pm uninstall com.android.cts.install.lib.testapp.B" />
+ <option name="teardown-command" value="pm uninstall com.android.cts.install.lib.testapp.A" />
+ <option name="teardown-command" value="pm uninstall com.android.cts.install.lib.testapp.B" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
diff --git a/tests/tests/packageinstaller/atomicinstall/OWNERS b/tests/tests/packageinstaller/atomicinstall/OWNERS
new file mode 100644
index 0000000..25775b8
--- /dev/null
+++ b/tests/tests/packageinstaller/atomicinstall/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 36137
+include /hostsidetests/stagedinstall/OWNERS
diff --git a/tests/tests/packageinstaller/atomicinstall/src/com/android/tests/atomicinstall/AtomicInstallTest.java b/tests/tests/packageinstaller/atomicinstall/src/com/android/tests/atomicinstall/AtomicInstallTest.java
index f99ac2f..67051cc 100644
--- a/tests/tests/packageinstaller/atomicinstall/src/com/android/tests/atomicinstall/AtomicInstallTest.java
+++ b/tests/tests/packageinstaller/atomicinstall/src/com/android/tests/atomicinstall/AtomicInstallTest.java
@@ -16,41 +16,40 @@
package com.android.tests.atomicinstall;
+import static com.android.cts.install.lib.InstallUtils.assertStatusSuccess;
+import static com.android.cts.install.lib.InstallUtils.getInstalledVersion;
+import static com.android.cts.install.lib.InstallUtils.openPackageInstallerSession;
+
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import android.Manifest;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
-import android.content.pm.PackageManager;
import androidx.test.InstrumentationRegistry;
+import com.android.cts.install.lib.Install;
+import com.android.cts.install.lib.InstallUtils;
+import com.android.cts.install.lib.LocalIntentSender;
+import com.android.cts.install.lib.TestApp;
+import com.android.cts.install.lib.Uninstall;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.ArrayList;
-
/**
* Tests for multi-package (a.k.a. atomic) installs.
*/
@RunWith(JUnit4.class)
public class AtomicInstallTest {
- private static final String TEST_APP_A = "com.android.tests.atomicinstall.testapp.A";
- private static final String TEST_APP_B = "com.android.tests.atomicinstall.testapp.B";
- public static final String TEST_APP_A_FILENAME = "AtomicInstallTestAppAv1.apk";
- public static final String TEST_APP_A_V2_FILENAME = "AtomicInstallTestAppAv2.apk";
- public static final String TEST_APP_B_FILENAME = "AtomicInstallTestAppBv1.apk";
public static final String TEST_APP_CORRUPT_FILENAME = "corrupt.apk";
+ private static final TestApp CORRUPT_TESTAPP = new TestApp(
+ "corrupt", "com.corrupt", 1, false, TEST_APP_CORRUPT_FILENAME);
private void adoptShellPermissions() {
InstrumentationRegistry
@@ -64,8 +63,7 @@
public void setup() throws Exception {
adoptShellPermissions();
- uninstall(TEST_APP_A);
- uninstall(TEST_APP_B);
+ Uninstall.packages(TestApp.A, TestApp.B);
}
@After
@@ -78,149 +76,75 @@
@Test
public void testInstallTwoApks() throws Exception {
- installMultiPackage(TEST_APP_A_FILENAME, TEST_APP_B_FILENAME);
- assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(1);
- assertThat(getInstalledVersion(TEST_APP_B)).isEqualTo(1);
+ Install.multi(TestApp.A1, TestApp.B1).commit();
+ assertThat(getInstalledVersion(TestApp.A)).isEqualTo(1);
+ assertThat(getInstalledVersion(TestApp.B)).isEqualTo(1);
}
@Test
public void testInstallTwoApksDowngradeFail() throws Exception {
- installMultiPackage(TEST_APP_A_V2_FILENAME, TEST_APP_B_FILENAME);
- assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(2);
- assertThat(getInstalledVersion(TEST_APP_B)).isEqualTo(1);
+ Install.multi(TestApp.A2, TestApp.B1).commit();
+ assertThat(getInstalledVersion(TestApp.A)).isEqualTo(2);
+ assertThat(getInstalledVersion(TestApp.B)).isEqualTo(1);
- final PackageInstaller.SessionParams parentSessionParams =
- createSessionParams(/*staged*/false, /*multipackage*/true,
- /*enableRollback*/ false, /*inherit*/false);
- final int parentSessionId = createSessionId(/*apkFileName*/null, parentSessionParams);
- final PackageInstaller.Session parentSession = getSessionOrFail(parentSessionId);
- for (String apkFile : new String[]{
- TEST_APP_A_FILENAME, TEST_APP_B_FILENAME}) {
- final PackageInstaller.SessionParams childSessionParams =
- createSessionParams(/*staged*/false, /*multipackage*/false,
- /*enableRollback*/ false, /*inherit*/false);
- final int childSessionId =
- createSessionId(apkFile, childSessionParams);
- parentSession.addChildSessionId(childSessionId);
- }
- parentSession.commit(LocalIntentSender.getIntentSender());
-
- final Intent intent = LocalIntentSender.getIntentSenderResult();
- assertStatusFailure(intent, "INSTALL_FAILED_VERSION_DOWNGRADE");
- assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(2);
- assertThat(getInstalledVersion(TEST_APP_B)).isEqualTo(1);
+ InstallUtils.commitExpectingFailure(AssertionError.class,
+ "INSTALL_FAILED_VERSION_DOWNGRADE", Install.multi(TestApp.A1, TestApp.B1));
+ assertThat(getInstalledVersion(TestApp.A)).isEqualTo(2);
+ assertThat(getInstalledVersion(TestApp.B)).isEqualTo(1);
}
@Test
public void testFailInconsistentMultiPackageCommit() throws Exception {
// Test inconsistency in staged settings
- for (boolean staged : new boolean[]{false, true}) {
- final PackageInstaller.SessionParams parentSessionParams =
- createSessionParams(/*staged*/staged, /*multipackage*/true,
- /*enableRollback*/false, /*inherit*/false);
- final int parentSessionId =
- createSessionId(/*apkFileName*/null, parentSessionParams);
- // Create a child session that differs in the staged parameter
- final PackageInstaller.SessionParams childSessionParams =
- createSessionParams(/*staged*/!staged, /*multipackage*/false,
- /*enableRollback*/false, /*inherit*/false);
- final int childSessionId =
- createSessionId("AtomicInstallTestAppAv1.apk", childSessionParams);
+ Install parentStaged = Install.multi(Install.single(TestApp.A1)).setStaged();
+ Install childStaged = Install.multi(Install.single(TestApp.A1).setStaged());
- final PackageInstaller.Session parentSession = getSessionOrFail(parentSessionId);
- parentSession.addChildSessionId(childSessionId);
- parentSession.commit(LocalIntentSender.getIntentSender());
- assertStatusFailure(LocalIntentSender.getIntentSenderResult(),
- "inconsistent staged settings");
- assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(-1);
- }
+ assertInconsistentStagedSettings(parentStaged);
+ assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1);
+ assertInconsistentStagedSettings(childStaged);
+ assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1);
// Test inconsistency in rollback settings
- for (boolean enableRollback : new boolean[]{false, true}) {
- final PackageInstaller.SessionParams parentSessionParams =
- createSessionParams(/*staged*/false, /*multipackage*/true,
- /*enableRollback*/enableRollback, /*inherit*/false);
- final int parentSessionId =
- createSessionId(/*apkFileName*/null, parentSessionParams);
- // Create a child session that differs in the staged parameter
- final PackageInstaller.SessionParams childSessionParams =
- createSessionParams(/*staged*/false, /*multipackage*/false,
- /*enableRollback*/!enableRollback, /*inherit*/false);
- final int childSessionId =
- createSessionId("AtomicInstallTestAppAv1.apk", childSessionParams);
+ Install parentEnabledRollback = Install.multi(Install.single(TestApp.A1))
+ .setEnableRollback();
+ Install childEnabledRollback = Install.multi(
+ Install.single(TestApp.A1).setEnableRollback());
- final PackageInstaller.Session parentSession = getSessionOrFail(parentSessionId);
- parentSession.addChildSessionId(childSessionId);
- parentSession.commit(LocalIntentSender.getIntentSender());
- assertStatusFailure(LocalIntentSender.getIntentSenderResult(),
- "inconsistent rollback settings");
- assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(-1);
- }
+ assertInconsistentRollbackSettings(parentEnabledRollback);
+ assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1);
+ assertInconsistentRollbackSettings(childEnabledRollback);
+ assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1);
}
@Test
public void testChildFailurePropagated() throws Exception {
- final PackageInstaller.SessionParams parentSessionParams =
- createSessionParams(/*staged*/false, /*multipackage*/true,
- /*enableRollback*/ false, /*inherit*/false);
- final int parentSessionId =
- createSessionId(/*apkFileName*/null, parentSessionParams);
- final PackageInstaller.Session parentSession = getSessionOrFail(parentSessionId);
-
// Create a child session that "inherits" from a non-existent package. This
// causes the session commit to fail with a PackageManagerException.
- final PackageInstaller.SessionParams childSessionParams =
- createSessionParams(/*staged*/false, /*multipackage*/false,
- /*enableRollback*/ false, /*inherit*/true);
- final int childSessionId =
- createSessionId("AtomicInstallTestAppAv1.apk", childSessionParams);
- parentSession.addChildSessionId(childSessionId);
- parentSession.commit(LocalIntentSender.getIntentSender());
+ Install childInstall = Install.single(TestApp.A1).setSessionMode(
+ PackageInstaller.SessionParams.MODE_INHERIT_EXISTING);
+ Install parentInstall = Install.multi(childInstall);
- final Intent intent = LocalIntentSender.getIntentSenderResult();
- assertStatusFailure(intent, "Missing existing base package");
- assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(-1);
+ InstallUtils.commitExpectingFailure(AssertionError.class, "Missing existing base package",
+ parentInstall);
+
+ assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1);
}
@Test
public void testEarlyFailureFailsAll() throws Exception {
- final PackageInstaller.SessionParams parentSessionParams =
- createSessionParams(/*staged*/false, /*multipackage*/true,
- /*enableRollback*/ false, /*inherit*/false);
- final int parentSessionId = createSessionId(/*apkFileName*/null, parentSessionParams);
- final PackageInstaller.Session parentSession = getSessionOrFail(parentSessionId);
- for (String apkFile : new String[]{
- TEST_APP_A_FILENAME, TEST_APP_B_FILENAME, TEST_APP_CORRUPT_FILENAME}) {
- final PackageInstaller.SessionParams childSessionParams =
- createSessionParams(/*staged*/false, /*multipackage*/false,
- /*enableRollback*/ false, /*inherit*/false);
- final int childSessionId =
- createSessionId(apkFile, childSessionParams);
- parentSession.addChildSessionId(childSessionId);
- }
- parentSession.commit(LocalIntentSender.getIntentSender());
-
- final Intent intent = LocalIntentSender.getIntentSenderResult();
- assertStatusFailure(intent, "Failed to parse");
- assertThat(getInstalledVersion(TEST_APP_A)).isEqualTo(-1);
- assertThat(getInstalledVersion(TEST_APP_B)).isEqualTo(-1);
+ InstallUtils.commitExpectingFailure(AssertionError.class, "Failed to parse",
+ Install.multi(TestApp.A1, TestApp.B1, CORRUPT_TESTAPP));
+ assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1);
+ assertThat(getInstalledVersion(TestApp.B)).isEqualTo(-1);
}
@Test
public void testInvalidStateScenarios() throws Exception {
- final PackageInstaller.SessionParams parentSessionParams =
- createSessionParams(/*staged*/false, /*multipackage*/true,
- /*enableRollback*/false, /*inherit*/ false);
- final int parentSessionId = createSessionId(/*apkFileName*/null, parentSessionParams);
- final PackageInstaller.Session parentSession = getSessionOrFail(parentSessionId);
+ int parentSessionId = Install.multi(TestApp.A1, TestApp.B1).createSession();
+ PackageInstaller.Session parentSession = openPackageInstallerSession(parentSessionId);
- final PackageInstaller.SessionParams childSessionParams =
- createSessionParams(/*staged*/false, /*multipackage*/false,
- /*enableRollback*/false, /*inherit*/ false);
- for (String apkFileName : new String[]{TEST_APP_A_FILENAME, TEST_APP_B_FILENAME}) {
- final int childSessionId = createSessionId(apkFileName, childSessionParams);
- parentSession.addChildSessionId(childSessionId);
- PackageInstaller.Session childSession = getSessionOrFail(childSessionId);
+ for (int childSessionId : parentSession.getChildSessionIds()) {
+ PackageInstaller.Session childSession = openPackageInstallerSession(childSessionId);
try {
childSession.commit(LocalIntentSender.getIntentSender());
fail("Should not be able to commit a child session!");
@@ -234,8 +158,8 @@
// ignore
}
}
- int toAbandonSessionId = createSessionId(TEST_APP_A_FILENAME, childSessionParams);
- PackageInstaller.Session toAbandonSession = getSessionOrFail(toAbandonSessionId);
+ int toAbandonSessionId = Install.single(TestApp.A1).createSession();
+ PackageInstaller.Session toAbandonSession = openPackageInstallerSession(toAbandonSessionId);
toAbandonSession.abandon();
try {
parentSession.addChildSessionId(toAbandonSessionId);
@@ -244,140 +168,19 @@
// ignore
}
- // Commit the session (this will start the installation workflow).
parentSession.commit(LocalIntentSender.getIntentSender());
assertStatusSuccess(LocalIntentSender.getIntentSenderResult());
}
- private static long getInstalledVersion(String packageName) {
- Context context = InstrumentationRegistry.getContext();
- PackageManager pm = context.getPackageManager();
- try {
- PackageInfo info = pm.getPackageInfo(packageName, 0);
- return info.getLongVersionCode();
- } catch (PackageManager.NameNotFoundException e) {
- return -1;
- }
+ private static void assertInconsistentStagedSettings(Install install) {
+ assertInconsistentSettings("inconsistent staged settings", install);
}
- private static void installMultiPackage(String... resources) throws Exception {
- final PackageInstaller.SessionParams parentSessionParams =
- createSessionParams(/*staged*/false, /*multipackage*/true,
- /*enableRollback*/false, /*inherit*/ false);
- final int parentSessionId = createSessionId(/*apkFileName*/null, parentSessionParams);
- final PackageInstaller.Session parentSession = getSessionOrFail(parentSessionId);
-
- ArrayList<Integer> childSessionIds = new ArrayList<>(resources.length);
- for (String apkFileName : resources) {
- final PackageInstaller.SessionParams childSessionParams =
- createSessionParams(/*staged*/false, /*multipackage*/false,
- /*enableRollback*/false, /*inherit*/ false);
- final int childSessionId = createSessionId(apkFileName, childSessionParams);
- childSessionIds.add(childSessionId);
- parentSession.addChildSessionId(childSessionId);
-
- PackageInstaller.Session childSession = getSessionOrFail(childSessionId);
- assertThat(childSession.getParentSessionId()).isEqualTo(parentSessionId);
- assertThat(getSessionInfoOrFail(childSessionId).getParentSessionId())
- .isEqualTo(parentSessionId);
- }
- assertThat(parentSession.getChildSessionIds()).asList().containsAllIn(childSessionIds);
- assertThat(getSessionInfoOrFail(parentSessionId).getChildSessionIds()).asList()
- .containsAllIn(childSessionIds);
-
- // Commit the session (this will start the installation workflow).
- parentSession.commit(LocalIntentSender.getIntentSender());
- assertStatusSuccess(LocalIntentSender.getIntentSenderResult());
+ private static void assertInconsistentRollbackSettings(Install install) {
+ assertInconsistentSettings("inconsistent rollback settings", install);
}
- private static PackageInstaller.SessionParams createSessionParams(
- boolean staged, boolean multiPackage, boolean enableRollback, boolean inherit) {
- final int sessionMode = inherit
- ? PackageInstaller.SessionParams.MODE_INHERIT_EXISTING
- : PackageInstaller.SessionParams.MODE_FULL_INSTALL;
- final PackageInstaller.SessionParams params =
- new PackageInstaller.SessionParams(sessionMode);
- if (staged) {
- params.setStaged();
- }
- if (multiPackage) {
- params.setMultiPackage();
- }
- params.setEnableRollback(enableRollback);
- return params;
- }
-
- private static int createSessionId(String apkFileName, PackageInstaller.SessionParams params)
- throws Exception {
- final PackageInstaller packageInstaller = InstrumentationRegistry.getContext()
- .getPackageManager().getPackageInstaller();
- final int sessionId = packageInstaller.createSession(params);
- if (apkFileName == null) {
- return sessionId;
- }
- final PackageInstaller.Session session = packageInstaller.openSession(sessionId);
- try (OutputStream packageInSession = session.openWrite(apkFileName, 0, -1);
- final InputStream is =
- AtomicInstallTest.class.getClassLoader().getResourceAsStream(
- apkFileName)) {
- final byte[] buffer = new byte[4096];
- int n;
- while ((n = is.read(buffer)) >= 0) {
- packageInSession.write(buffer, 0, n);
- }
- }
- return sessionId;
- }
-
- private static PackageInstaller.Session getSessionOrFail(int sessionId) throws Exception {
- final PackageInstaller packageInstaller = InstrumentationRegistry.getContext()
- .getPackageManager().getPackageInstaller();
- return packageInstaller.openSession(sessionId);
- }
-
- private static PackageInstaller.SessionInfo getSessionInfoOrFail(int sessionId)
- throws Exception {
- final PackageInstaller packageInstaller = InstrumentationRegistry.getContext()
- .getPackageManager().getPackageInstaller();
- return packageInstaller.getSessionInfo(sessionId);
- }
-
- private static void assertStatusSuccess(Intent result) {
- final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
- PackageInstaller.STATUS_FAILURE);
- if (status == -1) {
- throw new AssertionError("PENDING USER ACTION");
- } else if (status > 0) {
- String message = result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
- throw new AssertionError(message == null ? "UNKNOWN FAILURE" : message);
- }
- }
-
- private static void assertStatusFailure(Intent result, String errorMessage) {
- final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
- PackageInstaller.STATUS_FAILURE);
- if (status == -1) {
- throw new AssertionError("PENDING USER ACTION");
- } else if (status == 0) {
- throw new AssertionError("Installation unexpectedly succeeded!");
- }
- final String message = result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
- if (message == null || !message.contains(errorMessage)) {
- throw new AssertionError("Unexpected failure:" +
- (message == null ? "UNKNOWN" : message));
- }
- }
-
- private static void uninstall(String packageName) throws Exception {
- // No need to uninstall if the package isn't installed.
- if (getInstalledVersion(packageName) == -1) {
- return;
- }
-
- final PackageInstaller packageInstaller = InstrumentationRegistry.getContext()
- .getPackageManager().getPackageInstaller();
- packageInstaller.uninstall(packageName, LocalIntentSender.getIntentSender());
- // Don't care about status; this is just cleanup
- LocalIntentSender.getIntentSenderResult();
+ private static void assertInconsistentSettings(String failMessage, Install install) {
+ InstallUtils.commitExpectingFailure(AssertionError.class, failMessage, install);
}
}
diff --git a/tests/tests/packageinstaller/atomicinstall/src/com/android/tests/atomicinstall/LocalIntentSender.java b/tests/tests/packageinstaller/atomicinstall/src/com/android/tests/atomicinstall/LocalIntentSender.java
deleted file mode 100644
index e015cd8..0000000
--- a/tests/tests/packageinstaller/atomicinstall/src/com/android/tests/atomicinstall/LocalIntentSender.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.tests.atomicinstall;
-
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentSender;
-
-import androidx.test.InstrumentationRegistry;
-
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.TimeUnit;
-
-public class LocalIntentSender extends BroadcastReceiver {
- private static final BlockingQueue<Intent> sIntentSenderResults = new LinkedBlockingQueue<>();
-
- @Override
- public void onReceive(Context context, Intent intent) {
- sIntentSenderResults.add(intent);
- }
-
- /**
- * Get a LocalIntentSender.
- */
- static IntentSender getIntentSender() {
- Context context = InstrumentationRegistry.getContext();
- Intent intent = new Intent(context, LocalIntentSender.class);
- PendingIntent pending = PendingIntent.getBroadcast(context, 0, intent, 0);
- return pending.getIntentSender();
- }
-
- /**
- * Returns the most recent Intent sent by a LocalIntentSender.
- */
- static Intent getIntentSenderResult() throws InterruptedException {
- return sIntentSenderResults.poll(5, TimeUnit.SECONDS);
- }
-}
diff --git a/tests/tests/packageinstaller/atomicinstall/testdata/apk/Av1.xml b/tests/tests/packageinstaller/atomicinstall/testdata/apk/Av1.xml
deleted file mode 100644
index e3c8c28..0000000
--- a/tests/tests/packageinstaller/atomicinstall/testdata/apk/Av1.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<!--
- ~ Copyright (C) 2019 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.tests.atomicinstall.testapp.A"
- android:versionCode="1"
- android:versionName="1.0" >
-
- <uses-sdk android:minSdkVersion="19" />
-
- <application android:label="AtomicInstall Test App A v1">
- <activity android:name="com.android.tests.atomicinstall.testapp.MainActivity">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
-</manifest>
diff --git a/tests/tests/packageinstaller/atomicinstall/testdata/apk/Bv1.xml b/tests/tests/packageinstaller/atomicinstall/testdata/apk/Bv1.xml
deleted file mode 100644
index dd5db42..0000000
--- a/tests/tests/packageinstaller/atomicinstall/testdata/apk/Bv1.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<!--
- ~ Copyright (C) 2019 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.tests.atomicinstall.testapp.B"
- android:versionCode="1"
- android:versionName="1.0" >
-
- <uses-sdk android:minSdkVersion="19" />
-
- <application android:label="AtomicInstall Test App B v1">
- <activity android:name="com.android.tests.atomicinstall.testapp.MainActivity">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
-</manifest>
diff --git a/tests/tests/packageinstaller/install/AndroidManifest.xml b/tests/tests/packageinstaller/install/AndroidManifest.xml
index 7d2e984..eeef252 100644
--- a/tests/tests/packageinstaller/install/AndroidManifest.xml
+++ b/tests/tests/packageinstaller/install/AndroidManifest.xml
@@ -22,7 +22,7 @@
<application android:label="Cts Package Installer Tests">
<uses-library android:name="android.test.runner" />
- <activity android:name=".InstallConfirmDialogStarter" />
+ <activity android:name="com.android.compatibility.common.util.FutureResultActivity" />
<provider android:authorities="android.packageinstaller.install.cts.fileprovider"
android:name="androidx.core.content.FileProvider"
diff --git a/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/InstallConfirmDialogStarter.kt b/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/InstallConfirmDialogStarter.kt
deleted file mode 100644
index b026943..0000000
--- a/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/InstallConfirmDialogStarter.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.packageinstaller.install.cts
-
-import android.app.Activity
-import android.content.Intent
-import android.os.Bundle
-import java.util.concurrent.LinkedBlockingQueue
-
-val installDialogResults = LinkedBlockingQueue<Int>()
-
-class InstallConfirmDialogStarter : Activity() {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- savedInstanceState ?: installDialogResults.clear()
- }
-
- override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
- installDialogResults.offer(resultCode)
- }
-}
diff --git a/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/IntentTest.kt b/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/IntentTest.kt
index e163a32..a8a8917 100644
--- a/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/IntentTest.kt
+++ b/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/IntentTest.kt
@@ -28,6 +28,7 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import java.util.concurrent.TimeUnit
private const val INSTALL_BUTTON_ID = "button1"
private const val CANCEL_BUTTON_ID = "button2"
@@ -47,11 +48,11 @@
*/
@Test
fun confirmInstallation() {
- startInstallationViaIntent()
+ val installation = startInstallationViaIntent()
clickInstallerUIButton(INSTALL_BUTTON_ID)
// Install should have succeeded
- assertEquals(RESULT_OK, getInstallDialogResult())
+ assertEquals(RESULT_OK, installation.get(TIMEOUT, TimeUnit.MILLISECONDS))
assertInstalled()
}
@@ -61,14 +62,12 @@
*/
@Test
fun cancelInstallation() {
- startInstallationViaIntent()
+ val installation = startInstallationViaIntent()
clickInstallerUIButton(CANCEL_BUTTON_ID)
// Install should have been aborted
- assertEquals(RESULT_CANCELED, getInstallDialogResult())
+ assertEquals(RESULT_CANCELED, installation.get(TIMEOUT, TimeUnit.MILLISECONDS))
assertNotInstalled()
-
- assertNoMoreInstallResults()
}
/**
@@ -85,12 +84,12 @@
intent.putExtra(Intent.EXTRA_RETURN_RESULT, true)
intent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
- installDialogStarter.activity.startActivityForResult(intent, 0)
+ val reinstall = installDialogStarter.activity.startActivityForResult(intent)
clickInstallerUIButton(INSTALL_BUTTON_ID)
// Install should have succeeded
- assertEquals(RESULT_OK, getInstallDialogResult())
+ assertEquals(RESULT_OK, reinstall.get(TIMEOUT, TimeUnit.MILLISECONDS))
assertInstalled()
}
}
diff --git a/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/PackageInstallerTestBase.kt b/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/PackageInstallerTestBase.kt
index f6bbe06..d84ab1b 100644
--- a/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/PackageInstallerTestBase.kt
+++ b/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/PackageInstallerTestBase.kt
@@ -38,12 +38,14 @@
import android.support.test.uiautomator.Until
import androidx.core.content.FileProvider
import com.android.compatibility.common.util.AppOpsUtils
+import com.android.compatibility.common.util.FutureResultActivity
import org.junit.After
import org.junit.Assert
import org.junit.Before
import org.junit.Rule
import java.io.File
import java.lang.IllegalArgumentException
+import java.util.concurrent.CompletableFuture
import java.util.concurrent.LinkedBlockingQueue
import java.util.concurrent.TimeUnit
@@ -62,7 +64,7 @@
open class PackageInstallerTestBase {
@get:Rule
- val installDialogStarter = ActivityTestRule(InstallConfirmDialogStarter::class.java)
+ val installDialogStarter = ActivityTestRule(FutureResultActivity::class.java)
private val context = InstrumentationRegistry.getTargetContext()
private val pm = context.packageManager
@@ -79,7 +81,7 @@
if (status == STATUS_PENDING_USER_ACTION) {
val activityIntent = intent.getParcelableExtra<Intent>(EXTRA_INTENT)
activityIntent!!.addFlags(FLAG_ACTIVITY_CLEAR_TASK or FLAG_ACTIVITY_NEW_TASK)
- installDialogStarter.activity.startActivityForResult(activityIntent, 0)
+ installDialogStarter.activity.startActivityForResult(activityIntent)
}
installSessionResult.offer(status)
@@ -128,7 +130,7 @@
/**
* Start an installation via a session
*/
- protected fun startInstallationViaSession(): PackageInstaller.Session {
+ protected fun startInstallationViaSession(): CompletableFuture<Int> {
val pi = pm.packageInstaller
// Create session
@@ -143,33 +145,28 @@
}
// Commit session
- val pendingIntent = PendingIntent.getBroadcast(context, 0, Intent(INSTALL_ACTION_CB),
- FLAG_UPDATE_CURRENT)
- session.commit(pendingIntent.intentSender)
+ val dialog = FutureResultActivity.doAndAwaitStart {
+ val pendingIntent = PendingIntent.getBroadcast(context, 0, Intent(INSTALL_ACTION_CB),
+ FLAG_UPDATE_CURRENT)
+ session.commit(pendingIntent.intentSender)
+ }
// The system should have asked us to launch the installer
Assert.assertEquals(STATUS_PENDING_USER_ACTION, getInstallSessionResult())
- return session
+ return dialog
}
/**
* Start an installation via a session
*/
- protected fun startInstallationViaIntent() {
+ protected fun startInstallationViaIntent(): CompletableFuture<Int> {
val intent = Intent(Intent.ACTION_INSTALL_PACKAGE)
intent.data = FileProvider.getUriForFile(context, CONTENT_AUTHORITY, apkFile)
intent.putExtra(Intent.EXTRA_RETURN_RESULT, true)
intent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
- installDialogStarter.activity.startActivityForResult(intent, 0)
- }
-
- /**
- * Wait for result of install dialog and return it
- */
- fun getInstallDialogResult(timeout: Long = TIMEOUT): Int? {
- return installDialogResults.poll(timeout, TimeUnit.MILLISECONDS)
+ return installDialogStarter.activity.startActivityForResult(intent)
}
fun assertInstalled() {
@@ -195,14 +192,6 @@
.click()
}
- /**
- * Assert that there are no more callbacks from the install session or install dialog
- */
- fun assertNoMoreInstallResults() {
- Assert.assertNull(getInstallSessionResult(0))
- Assert.assertEquals(0, installDialogResults.size)
- }
-
@After
fun unregisterInstallResultReceiver() {
try {
diff --git a/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/SessionTest.kt b/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/SessionTest.kt
index 34b78c3..111e6a8 100644
--- a/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/SessionTest.kt
+++ b/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/SessionTest.kt
@@ -30,6 +30,7 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import java.util.concurrent.TimeUnit
private const val INSTALL_BUTTON_ID = "button1"
private const val CANCEL_BUTTON_ID = "button2"
@@ -50,7 +51,7 @@
*/
@Test
fun confirmInstallation() {
- startInstallationViaSession()
+ val installation = startInstallationViaSession()
clickInstallerUIButton(INSTALL_BUTTON_ID)
// Install should have succeeded
@@ -58,9 +59,7 @@
assertInstalled()
// Even when the install succeeds the install confirm dialog returns 'canceled'
- assertEquals(RESULT_CANCELED, getInstallDialogResult())
-
- assertNoMoreInstallResults()
+ assertEquals(RESULT_CANCELED, installation.get(TIMEOUT, TimeUnit.MILLISECONDS))
assertTrue(AppOpsUtils.allowedOperationLogged(context.packageName, APP_OP_STR))
}
@@ -70,7 +69,7 @@
*/
@Test
fun setAppCategory() {
- startInstallationViaSession()
+ val installation = startInstallationViaSession()
clickInstallerUIButton(INSTALL_BUTTON_ID)
// Wait for installation to finish
@@ -90,14 +89,12 @@
*/
@Test
fun cancelInstallation() {
- startInstallationViaSession()
+ val installation = startInstallationViaSession()
clickInstallerUIButton(CANCEL_BUTTON_ID)
// Install should have been aborted
assertEquals(STATUS_FAILURE_ABORTED, getInstallSessionResult())
- assertEquals(RESULT_CANCELED, getInstallDialogResult())
+ assertEquals(RESULT_CANCELED, installation.get(TIMEOUT, TimeUnit.MILLISECONDS))
assertNotInstalled()
-
- assertNoMoreInstallResults()
}
}
diff --git a/tests/tests/packageinstaller/tapjacking/OWNERS b/tests/tests/packageinstaller/tapjacking/OWNERS
new file mode 100644
index 0000000..0088192
--- /dev/null
+++ b/tests/tests/packageinstaller/tapjacking/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 36137
+moltmann@google.com
\ No newline at end of file
diff --git a/tests/tests/packageinstaller/uninstall/OWNERS b/tests/tests/packageinstaller/uninstall/OWNERS
new file mode 100644
index 0000000..0088192
--- /dev/null
+++ b/tests/tests/packageinstaller/uninstall/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 36137
+moltmann@google.com
\ No newline at end of file
diff --git a/tests/tests/permission/AndroidTest.xml b/tests/tests/permission/AndroidTest.xml
index c4ee088..a4ee42b 100644
--- a/tests/tests/permission/AndroidTest.xml
+++ b/tests/tests/permission/AndroidTest.xml
@@ -42,6 +42,8 @@
<option name="push" value="CtsAppThatRequestsLocationPermission29v4.apk->/data/local/tmp/cts/permissions/CtsAppThatRequestsLocationPermission29v4.apk" />
<option name="push" value="CtsAppThatRequestsLocationPermission28.apk->/data/local/tmp/cts/permissions/CtsAppThatRequestsLocationPermission28.apk" />
<option name="push" value="CtsAppThatRequestsLocationPermission22.apk->/data/local/tmp/cts/permissions/CtsAppThatRequestsLocationPermission22.apk" />
+ <option name="push" value="CtsAppThatRequestsStoragePermission29.apk->/data/local/tmp/cts/permissions/CtsAppThatRequestsStoragePermission29.apk" />
+ <option name="push" value="CtsAppThatRequestsStoragePermission28.apk->/data/local/tmp/cts/permissions/CtsAppThatRequestsStoragePermission28.apk" />
<option name="push" value="CtsAppThatRequestsLocationAndBackgroundPermission29.apk->/data/local/tmp/cts/permissions/CtsAppThatRequestsLocationAndBackgroundPermission29.apk" />
<option name="push" value="CtsAppThatAccessesLocationOnCommand.apk->/data/local/tmp/cts/permissions/CtsAppThatAccessesLocationOnCommand.apk" />
<option name="push" value="AppThatDoesNotHaveBgLocationAccess.apk->/data/local/tmp/cts/permissions/AppThatDoesNotHaveBgLocationAccess.apk" />
@@ -50,12 +52,23 @@
<option name="push" value="CtsAppWithSharedUidThatRequestsLocationPermission28.apk->/data/local/tmp/cts/permissions/CtsAppWithSharedUidThatRequestsLocationPermission28.apk" />
<option name="push" value="CtsAppWithSharedUidThatRequestsLocationPermission29.apk->/data/local/tmp/cts/permissions/CtsAppWithSharedUidThatRequestsLocationPermission29.apk" />
<option name="push" value="CtsAppThatRequestsCalendarContactsBodySensorCustomPermission.apk->/data/local/tmp/cts/permissions/CtsAppThatRequestsCalendarContactsBodySensorCustomPermission.apk" />
+ <option name="push" value="CtsAppThatRunsRationaleTests.apk->/data/local/tmp/cts/permissions/CtsAppThatRunsRationaleTests.apk" />
+ <option name="push" value="CtsAdversarialPermissionUserApp.apk->/data/local/tmp/cts/permissions/CtsAdversarialPermissionUserApp.apk" />
+ <option name="push" value="CtsAdversarialPermissionDefinerApp.apk->/data/local/tmp/cts/permissions/CtsAdversarialPermissionDefinerApp.apk" />
+ <option name="push" value="CtsVictimPermissionDefinerApp.apk->/data/local/tmp/cts/permissions/CtsVictimPermissionDefinerApp.apk" />
+ <option name="push" value="CtsRuntimePermissionDefinerApp.apk->/data/local/tmp/cts/permissions/CtsRuntimePermissionDefinerApp.apk" />
+ <option name="push" value="CtsRuntimePermissionUserApp.apk->/data/local/tmp/cts/permissions/CtsRuntimePermissionUserApp.apk" />
</target_preparer>
<!-- Remove additional apps if installed -->
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="teardown-command" value="pm uninstall android.permission.cts.appthatrequestpermission" />
<option name="teardown-command" value="pm uninstall android.permission.cts.appthatrequestnopermission" />
+ <option name="teardown-command" value="pm uninstall android.permission.cts.revokepermissionwhenremoved.AdversarialPermissionDefinerApp" />
+ <option name="teardown-command" value="pm uninstall android.permission.cts.revokepermissionwhenremoved.VictimPermissionDefinerApp" />
+ <option name="teardown-command" value="pm uninstall android.permission.cts.revokepermissionwhenremoved.userapp" />
+ <option name="teardown-command" value="pm uninstall android.permission.cts.revokepermissionwhenremoved.runtimepermissiondefinerapp" />
+ <option name="teardown-command" value="pm uninstall android.permission.cts.revokepermissionwhenremoved.runtimepermissionuserapp" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
diff --git a/tests/backup/app/permission22/Android.mk b/tests/tests/permission/AppThatRequestStoragePermission28/Android.mk
similarity index 82%
rename from tests/backup/app/permission22/Android.mk
rename to tests/tests/permission/AppThatRequestStoragePermission28/Android.mk
index 8790e19..ace3264 100644
--- a/tests/backup/app/permission22/Android.mk
+++ b/tests/tests/permission/AppThatRequestStoragePermission28/Android.mk
@@ -1,3 +1,4 @@
+#
# Copyright (C) 2019 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -11,21 +12,19 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-
+#
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
-
-LOCAL_PACKAGE_NAME := CtsPermissionBackupApp22
-LOCAL_SDK_VERSION := current
-
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-include $(BUILD_CTS_SUPPORT_PACKAGE)
+LOCAL_PACKAGE_NAME := CtsAppThatRequestsStoragePermission28
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/permission/AppThatRequestStoragePermission28/AndroidManifest.xml b/tests/tests/permission/AppThatRequestStoragePermission28/AndroidManifest.xml
new file mode 100644
index 0000000..a847f39
--- /dev/null
+++ b/tests/tests/permission/AppThatRequestStoragePermission28/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.permission.cts.appthatrequestpermission"
+ android:versionCode="2">
+
+ <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28" />
+
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+
+ <application />
+</manifest>
+
diff --git a/tests/backup/app/permission22/Android.mk b/tests/tests/permission/AppThatRequestStoragePermission29/Android.mk
similarity index 82%
copy from tests/backup/app/permission22/Android.mk
copy to tests/tests/permission/AppThatRequestStoragePermission29/Android.mk
index 8790e19..aa9fabf 100644
--- a/tests/backup/app/permission22/Android.mk
+++ b/tests/tests/permission/AppThatRequestStoragePermission29/Android.mk
@@ -1,3 +1,4 @@
+#
# Copyright (C) 2019 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -11,21 +12,19 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-
+#
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
-
-LOCAL_PACKAGE_NAME := CtsPermissionBackupApp22
-LOCAL_SDK_VERSION := current
-
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-include $(BUILD_CTS_SUPPORT_PACKAGE)
+LOCAL_PACKAGE_NAME := CtsAppThatRequestsStoragePermission29
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/permission/AppThatRequestStoragePermission29/AndroidManifest.xml b/tests/tests/permission/AppThatRequestStoragePermission29/AndroidManifest.xml
new file mode 100644
index 0000000..c783085
--- /dev/null
+++ b/tests/tests/permission/AppThatRequestStoragePermission29/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.permission.cts.appthatrequestpermission"
+ android:versionCode="1">
+
+ <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29" />
+
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+
+ <application />
+</manifest>
+
diff --git a/common/device-side/util/tests/Android.bp b/tests/tests/permission/AppThatRunsRationaleTests/Android.bp
similarity index 62%
rename from common/device-side/util/tests/Android.bp
rename to tests/tests/permission/AppThatRunsRationaleTests/Android.bp
index 2abba37..3447de4 100644
--- a/common/device-side/util/tests/Android.bp
+++ b/tests/tests/permission/AppThatRunsRationaleTests/Android.bp
@@ -1,28 +1,31 @@
-// Copyright (C) 2015 The Android Open Source Project
+//
+// Copyright (C) 2019 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
+//
-java_test {
- name: "compatibility-device-util-tests",
-
- srcs: ["src/**/*.java"],
-
- static_libs: [
- "compatibility-device-util",
- "junit",
- "testng", // TODO: remove once Android migrates to JUnit 4.12, which provide assertThrows
- "truth-prebuilt"
- ],
+android_test {
+ name: "CtsAppThatRunsRationaleTests",
+ defaults: ["cts_defaults"],
sdk_version: "test_current",
+
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+
+ srcs: ["src/**/*.java"],
}
diff --git a/tests/tests/packageinstaller/atomicinstall/testdata/apk/Av2.xml b/tests/tests/permission/AppThatRunsRationaleTests/AndroidManifest.xml
similarity index 67%
rename from tests/tests/packageinstaller/atomicinstall/testdata/apk/Av2.xml
rename to tests/tests/permission/AppThatRunsRationaleTests/AndroidManifest.xml
index 34472a0..cede468 100644
--- a/tests/tests/packageinstaller/atomicinstall/testdata/apk/Av2.xml
+++ b/tests/tests/permission/AppThatRunsRationaleTests/AndroidManifest.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2019 The Android Open Source Project
~
@@ -13,19 +14,19 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.tests.atomicinstall.testapp.A"
- android:versionCode="2"
- android:versionName="2.0" >
+ package="android.permission.cts.appthatrunsrationaletests">
- <uses-sdk android:minSdkVersion="19" />
+ <uses-permission android:name="android.permission.READ_CONTACTS" />
- <application android:label="AtomicInstall Test App A v2">
- <activity android:name="com.android.tests.atomicinstall.testapp.MainActivity">
+ <application android:label="CtsRationaleTests">
+ <activity android:name=".TestActivity">
<intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
+ <action android:name="CtsRationalTests.intent.action.Launch" />
+ <category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
</manifest>
+
diff --git a/tests/tests/permission/AppThatRunsRationaleTests/src/android/permission/cts/appthatrunsrationaletests/TestActivity.java b/tests/tests/permission/AppThatRunsRationaleTests/src/android/permission/cts/appthatrunsrationaletests/TestActivity.java
new file mode 100644
index 0000000..7544890
--- /dev/null
+++ b/tests/tests/permission/AppThatRunsRationaleTests/src/android/permission/cts/appthatrunsrationaletests/TestActivity.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts.appthatrunsrationaletests;
+
+import android.Manifest;
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.RemoteCallback;
+
+public class TestActivity extends Activity {
+ private static final String CALLBACK_KEY = "testactivitycallback";
+ private static final String RESULT_KEY = "testactivityresult";
+ private static final String PERMISSION_NAME = Manifest.permission.READ_CONTACTS;
+
+ @Override
+ public void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+
+ RemoteCallback cb = (RemoteCallback) getIntent().getExtras().get(CALLBACK_KEY);
+
+ boolean result = shouldShowRequestPermissionRationale(PERMISSION_NAME);
+ Bundle res = new Bundle();
+ res.putBoolean(RESULT_KEY, result);
+
+ finish();
+ cb.sendResult(res);
+ }
+}
diff --git a/tests/tests/permission/OWNERS b/tests/tests/permission/OWNERS
index fccfeef..c44024b 100644
--- a/tests/tests/permission/OWNERS
+++ b/tests/tests/permission/OWNERS
@@ -4,3 +4,4 @@
per-file RequestLocation.java = hallliu@google.com
per-file NoAudioPermissionTest.java = elaurent@google.com
per-file MainlineNetworkStackPermissionTest.java = file: platform/frameworks/base:/services/net/OWNERS
+per-file Camera2PermissionTest.java = file: platform/frameworks/av:/camera/OWNERS
diff --git a/tests/tests/permission/sdk28/OWNERS b/tests/tests/permission/sdk28/OWNERS
new file mode 100644
index 0000000..c126a70
--- /dev/null
+++ b/tests/tests/permission/sdk28/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 137825
+moltmann@google.com
\ No newline at end of file
diff --git a/tests/tests/permission/src/android/permission/cts/ActivityPermissionRationaleTest.java b/tests/tests/permission/src/android/permission/cts/ActivityPermissionRationaleTest.java
new file mode 100644
index 0000000..947e59a
--- /dev/null
+++ b/tests/tests/permission/src/android/permission/cts/ActivityPermissionRationaleTest.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.Manifest;
+import android.app.UiAutomation;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.RemoteCallback;
+import android.platform.test.annotations.AppModeFull;
+
+import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+
+@AppModeFull(reason = "Tests properties of other app. Instant apps cannot interact with other apps")
+@RunWith(AndroidJUnit4ClassRunner.class)
+public class ActivityPermissionRationaleTest {
+ private static final String APK =
+ "/data/local/tmp/cts/permissions/CtsAppThatRunsRationaleTests.apk";
+ private static final String PACKAGE_NAME = "android.permission.cts.appthatrunsrationaletests";
+ private static final String PERMISSION_NAME = Manifest.permission.READ_CONTACTS;
+ private static final String CALLBACK_KEY = "testactivitycallback";
+ private static final String RESULT_KEY = "testactivityresult";
+ private static final int TIMEOUT = 5000;
+
+ private static Context sContext = InstrumentationRegistry.getInstrumentation().getContext();
+ private static UiAutomation sUiAuto =
+ InstrumentationRegistry.getInstrumentation().getUiAutomation();
+
+ @BeforeClass
+ public static void setUp() {
+ runShellCommand("pm install -r " + APK);
+ int flag = PackageManager.FLAG_PERMISSION_USER_SET;
+ PermissionUtils.setPermissionFlags(PACKAGE_NAME, PERMISSION_NAME, flag, flag);
+ }
+
+ @AfterClass
+ public static void unInstallApp() {
+ runShellCommand("pm uninstall " + PACKAGE_NAME);
+ }
+
+ private void assertAppShowRationaleIs(boolean expected) throws Exception {
+ CompletableFuture<Boolean> callbackReturn = new CompletableFuture<>();
+ RemoteCallback cb = new RemoteCallback((Bundle result) ->
+ callbackReturn.complete(result.getBoolean(RESULT_KEY)));
+ Intent intent = new Intent();
+ intent.setComponent(new ComponentName(PACKAGE_NAME, PACKAGE_NAME + ".TestActivity"));
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(CALLBACK_KEY, cb);
+
+ sContext.startActivity(intent);
+ assertThat(callbackReturn.get(TIMEOUT, TimeUnit.MILLISECONDS)).isEqualTo(expected);
+ }
+
+ @Before
+ public void clearData() {
+ runShellCommand("pm clear android.permission.cts.appthatrunsrationaletests");
+ PermissionUtils.setPermissionFlags(PACKAGE_NAME, PERMISSION_NAME,
+ PackageManager.FLAG_PERMISSION_POLICY_FIXED, 0);
+ }
+
+ @Test
+ public void permissionGrantedNoRationale() throws Exception {
+ sUiAuto.grantRuntimePermission(PACKAGE_NAME, PERMISSION_NAME);
+
+ assertAppShowRationaleIs(false);
+ }
+
+ @Test
+ public void policyFixedNoRationale() throws Exception {
+ int flags = PackageManager.FLAG_PERMISSION_POLICY_FIXED;
+ PermissionUtils.setPermissionFlags(PACKAGE_NAME, PERMISSION_NAME, flags, flags);
+
+ assertAppShowRationaleIs(false);
+ }
+
+ @Test
+ public void userFixedNoRationale() throws Exception {
+ int flags = PackageManager.FLAG_PERMISSION_USER_FIXED;
+ PermissionUtils.setPermissionFlags(PACKAGE_NAME, PERMISSION_NAME, flags, flags);
+
+ assertAppShowRationaleIs(false);
+ }
+
+ @Test
+ public void notUserSetNoRationale() throws Exception {
+ PermissionUtils.setPermissionFlags(PACKAGE_NAME, PERMISSION_NAME,
+ PackageManager.FLAG_PERMISSION_USER_SET, 0);
+
+ assertAppShowRationaleIs(false);
+ }
+
+ @Test
+ public void userSetNeedRationale() throws Exception {
+ int flags = PackageManager.FLAG_PERMISSION_USER_SET;
+ PermissionUtils.setPermissionFlags(PACKAGE_NAME, PERMISSION_NAME, flags, flags);
+
+ assertAppShowRationaleIs(true);
+ }
+}
diff --git a/tests/tests/permission/src/android/permission/cts/Camera2PermissionTest.java b/tests/tests/permission/src/android/permission/cts/Camera2PermissionTest.java
index 4076ace..379f478 100644
--- a/tests/tests/permission/src/android/permission/cts/Camera2PermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/Camera2PermissionTest.java
@@ -19,27 +19,28 @@
import static com.android.ex.camera2.blocking.BlockingStateCallback.*;
import android.content.Context;
-import android.hardware.camera2.CameraDevice;
-import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraCharacteristics.Key;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
import android.os.Handler;
import android.os.HandlerThread;
import android.platform.test.annotations.Presubmit;
import android.test.AndroidTestCase;
import android.util.Log;
-import java.util.List;
-
import com.android.ex.camera2.blocking.BlockingCameraManager;
import com.android.ex.camera2.blocking.BlockingStateCallback;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Tests for Camera2 API related Permissions. Currently, this means
* android.permission.CAMERA.
*/
public class Camera2PermissionTest extends AndroidTestCase {
- private static final String TAG = "CameraDeviceTest";
+ private static final String TAG = "Camera2PermissionTest";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
private static final int CAMERA_CLOSE_TIMEOUT_MS = 2000;
@@ -99,6 +100,25 @@
}
/**
+ * Check that no system cameras can be discovered without
+ * {@link android.Manifest.permission#CAMERA} and android.permission.SYSTEM_CAMERA
+ */
+ public void testSystemCameraDiscovery() throws Exception {
+ for (String id : mCameraIds) {
+ Log.i(TAG, "testSystemCameraDiscovery for camera id " + id);
+ CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics(id);
+ assertNotNull("Camera characteristics shouldn't be null", characteristics);
+ int[] availableCapabilities =
+ characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
+ assertTrue("Camera capabilities shouldn't be null", availableCapabilities != null);
+ List<Integer> capList = toList(availableCapabilities);
+ assertFalse("System camera device " + id + " should not be public",
+ capList.contains(
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA));
+ }
+ }
+
+ /**
* Check the absence of camera characteristics keys that require Permission:
* {@link android.Manifest.permission#CAMERA}.
*/
@@ -153,6 +173,14 @@
cameraId, mCameraListener, mHandler);
}
+ private static List<Integer> toList(int[] array) {
+ List<Integer> list = new ArrayList<Integer>();
+ for (int i : array) {
+ list.add(i);
+ }
+ return list;
+ }
+
private void closeCamera() {
if (mCamera != null) {
mCamera.close();
diff --git a/tests/tests/permission/src/android/permission/cts/LocationAccessCheckTest.java b/tests/tests/permission/src/android/permission/cts/LocationAccessCheckTest.java
index 4ecf5b6..af9024f 100644
--- a/tests/tests/permission/src/android/permission/cts/LocationAccessCheckTest.java
+++ b/tests/tests/permission/src/android/permission/cts/LocationAccessCheckTest.java
@@ -56,7 +56,6 @@
import android.os.Debug;
import android.os.IBinder;
import android.os.Looper;
-import android.os.ParcelFileDescriptor;
import android.platform.test.annotations.AppModeFull;
import android.provider.DeviceConfig;
import android.provider.Settings;
@@ -69,6 +68,7 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import com.android.compatibility.common.util.ProtoUtils;
import com.android.server.job.nano.JobSchedulerServiceDumpProto;
import com.android.server.job.nano.JobSchedulerServiceDumpProto.RegisteredJob;
@@ -79,8 +79,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.io.ByteArrayOutputStream;
-import java.io.FileInputStream;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
@@ -215,26 +213,8 @@
* Get the state of the job scheduler
*/
public static JobSchedulerServiceDumpProto getJobSchedulerDump() throws Exception {
- ParcelFileDescriptor pfd = sUiAutomation.executeShellCommand("dumpsys jobscheduler --proto");
-
- try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
- // Copy data from 'is' into 'os'
- try (FileInputStream is = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
- byte[] buffer = new byte[16384];
-
- while (true) {
- int numRead = is.read(buffer);
-
- if (numRead == -1) {
- break;
- } else {
- os.write(buffer, 0, numRead);
- }
- }
- }
-
- return JobSchedulerServiceDumpProto.parseFrom(os.toByteArray());
- }
+ return ProtoUtils.getProto(sUiAutomation, JobSchedulerServiceDumpProto.class,
+ ProtoUtils.DUMPSYS_JOB_SCHEDULER);
}
/**
diff --git a/tests/tests/permission/src/android/permission/cts/PermissionUpdateListenerTest.java b/tests/tests/permission/src/android/permission/cts/PermissionUpdateListenerTest.java
new file mode 100644
index 0000000..afcd9e9
--- /dev/null
+++ b/tests/tests/permission/src/android/permission/cts/PermissionUpdateListenerTest.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
+import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.OnPermissionsChangedListener;
+import android.platform.test.annotations.AppModeFull;
+
+import androidx.annotation.NonNull;
+import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@AppModeFull(reason = "Instant apps cannot access properties of other apps")
+@RunWith(AndroidJUnit4ClassRunner.class)
+public class PermissionUpdateListenerTest {
+ private static final String APK =
+ "/data/local/tmp/cts/permissions/"
+ + "CtsAppThatRequestsCalendarContactsBodySensorCustomPermission.apk";
+ private static final String PACKAGE_NAME =
+ "android.permission.cts.appthatrequestcustompermission";
+ private static final String PERMISSION_NAME = "android.permission.READ_CONTACTS";
+ private static final int TIMEOUT = 30000;
+
+ private static final Context sContext =
+ InstrumentationRegistry.getInstrumentation().getContext();
+ private static final PackageManager sPm = sContext.getPackageManager();
+ private static int sUid;
+
+ @BeforeClass
+ public static void installApp() throws PackageManager.NameNotFoundException {
+ runShellCommand("pm install -r " + APK);
+ sUid = sPm.getPackageUid(PACKAGE_NAME, 0);
+ }
+
+ @AfterClass
+ public static void unInstallApp() {
+ runShellCommand("pm uninstall " + PACKAGE_NAME);
+ }
+
+ private class LatchWithPermissionsChangedListener extends CountDownLatch
+ implements OnPermissionsChangedListener {
+
+ LatchWithPermissionsChangedListener() {
+ super(1);
+ }
+
+ public void onPermissionsChanged(int uid) {
+ if (uid == sUid) {
+ countDown();
+ }
+ }
+ }
+
+ private void waitForLatchAndRemoveListener(@NonNull LatchWithPermissionsChangedListener latch)
+ throws Exception {
+ latch.await(TIMEOUT, TimeUnit.MILLISECONDS);
+ runWithShellPermissionIdentity(() -> sPm.removeOnPermissionsChangeListener(latch));
+ assertThat(latch.getCount()).isEqualTo((long) 0);
+ }
+
+ @Test
+ public void grantNotifiesListener() throws Exception {
+ LatchWithPermissionsChangedListener listenerCalled =
+ new LatchWithPermissionsChangedListener();
+
+ runWithShellPermissionIdentity(() -> {
+ sPm.revokeRuntimePermission(PACKAGE_NAME, PERMISSION_NAME, sContext.getUser());
+ sPm.addOnPermissionsChangeListener(listenerCalled);
+ sPm.grantRuntimePermission(PACKAGE_NAME, PERMISSION_NAME, sContext.getUser());
+ });
+ waitForLatchAndRemoveListener(listenerCalled);
+ }
+
+ @Test
+ public void revokeNotifiesListener() throws Exception {
+ LatchWithPermissionsChangedListener listenerCalled =
+ new LatchWithPermissionsChangedListener();
+
+ runWithShellPermissionIdentity(() -> {
+ sPm.grantRuntimePermission(PACKAGE_NAME, PERMISSION_NAME, sContext.getUser());
+ sPm.addOnPermissionsChangeListener(listenerCalled);
+ sPm.revokeRuntimePermission(PACKAGE_NAME, PERMISSION_NAME, sContext.getUser());
+ });
+ waitForLatchAndRemoveListener(listenerCalled);
+ }
+
+ @Test
+ public void updateFlagsNotifiesListener() throws Exception {
+ LatchWithPermissionsChangedListener listenerCalled =
+ new LatchWithPermissionsChangedListener();
+
+ runWithShellPermissionIdentity(() -> {
+ sPm.addOnPermissionsChangeListener(listenerCalled);
+ int flag = PackageManager.FLAG_PERMISSION_USER_SET;
+ sPm.updatePermissionFlags(PERMISSION_NAME, PACKAGE_NAME, flag, flag,
+ sContext.getUser());
+ });
+ waitForLatchAndRemoveListener(listenerCalled);
+ }
+}
diff --git a/tests/tests/permission/src/android/permission/cts/RemovePermissionTest.java b/tests/tests/permission/src/android/permission/cts/RemovePermissionTest.java
new file mode 100644
index 0000000..ff0204b
--- /dev/null
+++ b/tests/tests/permission/src/android/permission/cts/RemovePermissionTest.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
+import static android.content.pm.PackageManager.GET_PERMISSIONS;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.SecurityTest;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+import org.junit.Before;
+import org.junit.Test;
+
+@AppModeFull(reason = "Instant apps cannot read state of other packages.")
+public class RemovePermissionTest {
+ private static final String APP_PKG_NAME = "android.permission.cts.revokepermissionwhenremoved";
+ private static final String USER_PKG_NAME =
+ "android.permission.cts.revokepermissionwhenremoved.userapp";
+ private static final String TEST_PERMISSION =
+ "android.permission.cts.revokepermissionwhenremoved.TestPermission";
+ private static final String RUNTIME_PERMISSION_USER_PKG_NAME =
+ "android.permission.cts.revokepermissionwhenremoved.runtimepermissionuserapp";
+ private static final String RUNTIME_PERMISSION_DEFINER_PKG_NAME =
+ "android.permission.cts.revokepermissionwhenremoved.runtimepermissiondefinerapp";
+ private static final String TEST_RUNTIME_PERMISSION =
+ "android.permission.cts.revokepermissionwhenremoved.TestRuntimePermission";
+
+ private Context mContext;
+ private Instrumentation mInstrumentation;
+ private Object mMySync = new Object();
+
+ @Before
+ public void setContextAndInstrumentation() {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ }
+
+ @Before
+ public void wakeUpScreen() {
+ SystemUtil.runShellCommand("input keyevent KEYCODE_WAKEUP");
+ }
+
+ private boolean permissionGranted(String pkgName, String permName)
+ throws PackageManager.NameNotFoundException {
+ PackageInfo appInfo = mContext.getPackageManager().getPackageInfo(pkgName,
+ GET_PERMISSIONS);
+
+ for (int i = 0; i < appInfo.requestedPermissions.length; i++) {
+ if (appInfo.requestedPermissions[i].equals(permName)
+ && ((appInfo.requestedPermissionsFlags[i] & REQUESTED_PERMISSION_GRANTED)
+ != 0)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void installApp(String apk) throws InterruptedException {
+ String installResult = SystemUtil.runShellCommand(
+ "pm install -r -d data/local/tmp/cts/permissions/" + apk + ".apk");
+ synchronized (mMySync) {
+ mMySync.wait(10000);
+ }
+ assertEquals("Success", installResult.trim());
+ }
+
+ private void uninstallApp(String pkg) throws InterruptedException {
+ String uninstallResult = SystemUtil.runShellCommand(
+ "pm uninstall " + pkg);
+ synchronized (mMySync) {
+ mMySync.wait(10000);
+ }
+ assertEquals("Success", uninstallResult.trim());
+ }
+
+ private void grantPermission(String pkg, String permission) {
+ mInstrumentation.getUiAutomation().grantRuntimePermission(
+ pkg, permission);
+ }
+
+ @SecurityTest
+ @Test
+ public void permissionShouldBeRevokedIfRemoved() throws Throwable {
+ installApp("CtsAdversarialPermissionDefinerApp");
+ installApp("CtsAdversarialPermissionUserApp");
+
+ grantPermission(USER_PKG_NAME, TEST_PERMISSION);
+ assertTrue(permissionGranted(USER_PKG_NAME, TEST_PERMISSION));
+
+ // Uninstall app which defines a permission with the same name as in victim app.
+ // Install the victim app.
+ uninstallApp(APP_PKG_NAME + ".AdversarialPermissionDefinerApp");
+ installApp("CtsVictimPermissionDefinerApp");
+ assertFalse(permissionGranted(USER_PKG_NAME, TEST_PERMISSION));
+ uninstallApp(APP_PKG_NAME + ".userapp");
+ uninstallApp(APP_PKG_NAME + ".VictimPermissionDefinerApp");
+ }
+
+ @Test
+ public void permissionShouldRemainGrantedAfterAppUpdate() throws Throwable {
+ installApp("CtsRuntimePermissionDefinerApp");
+ installApp("CtsRuntimePermissionUserApp");
+
+ grantPermission(RUNTIME_PERMISSION_USER_PKG_NAME, TEST_RUNTIME_PERMISSION);
+ assertTrue(permissionGranted(RUNTIME_PERMISSION_USER_PKG_NAME, TEST_RUNTIME_PERMISSION));
+
+ // Install app which defines a permission. This is similar to update the app
+ // operation
+ installApp("CtsRuntimePermissionDefinerApp");
+ assertTrue(permissionGranted(RUNTIME_PERMISSION_USER_PKG_NAME, TEST_RUNTIME_PERMISSION));
+ uninstallApp(RUNTIME_PERMISSION_USER_PKG_NAME);
+ uninstallApp(RUNTIME_PERMISSION_DEFINER_PKG_NAME);
+ }
+}
diff --git a/tests/tests/permission/src/android/permission/cts/SplitPermissionTest.java b/tests/tests/permission/src/android/permission/cts/SplitPermissionTest.java
index 7731a11..da075c0 100644
--- a/tests/tests/permission/src/android/permission/cts/SplitPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/SplitPermissionTest.java
@@ -18,8 +18,10 @@
import static android.Manifest.permission.ACCESS_BACKGROUND_LOCATION;
import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
+import static android.Manifest.permission.ACCESS_MEDIA_LOCATION;
import static android.Manifest.permission.READ_CALL_LOG;
import static android.Manifest.permission.READ_CONTACTS;
+import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static android.app.AppOpsManager.MODE_FOREGROUND;
import static android.app.AppOpsManager.MODE_IGNORED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
@@ -71,6 +73,10 @@
TMP_DIR + "CtsAppThatRequestsContactsPermission15.apk";
private static final String APK_CONTACTS_CALLLOG_16 =
TMP_DIR + "CtsAppThatRequestsContactsAndCallLogPermission16.apk";
+ private static final String APK_STORAGE_29 =
+ TMP_DIR + "CtsAppThatRequestsStoragePermission29.apk";
+ private static final String APK_STORAGE_28 =
+ TMP_DIR + "CtsAppThatRequestsStoragePermission28.apk";
private static final String APK_LOCATION_29 =
TMP_DIR + "CtsAppThatRequestsLocationPermission29.apk";
private static final String APK_LOCATION_28 =
@@ -260,6 +266,22 @@
* If a permission was granted before the split happens, the new permission should inherit the
* granted state.
*
+ * This is a duplicate of {@link #inheritGrantedPermissionState} but for the storage permission
+ */
+ @Test
+ public void inheritGrantedPermissionStateStorage() throws Exception {
+ install(APK_STORAGE_29);
+ grantPermission(APP_PKG, READ_EXTERNAL_STORAGE);
+
+ install(APK_STORAGE_28);
+
+ assertPermissionGranted(ACCESS_MEDIA_LOCATION);
+ }
+
+ /**
+ * If a permission was granted before the split happens, the new permission should inherit the
+ * granted state.
+ *
* <p>App using a shared uid
*/
@Test
diff --git a/tests/tests/permission/src/android/permission/cts/SplitPermissionsSystemTest.java b/tests/tests/permission/src/android/permission/cts/SplitPermissionsSystemTest.java
index c15b7a4..de9a30b 100755
--- a/tests/tests/permission/src/android/permission/cts/SplitPermissionsSystemTest.java
+++ b/tests/tests/permission/src/android/permission/cts/SplitPermissionsSystemTest.java
@@ -107,15 +107,11 @@
break;
case READ_EXTERNAL_STORAGE:
assertSplit(split, ACCESS_MEDIA_LOCATION, Build.VERSION_CODES.Q);
- // Remove this split permission from seenSplits, ACCESS_MEDIA_LOCATION is not
- // always available hence removing this permission from seenSplits will
- // avoid seenSplits size check fail.
- seenSplits.remove(split);
break;
}
}
- assertEquals(6, seenSplits.size());
+ assertEquals(7, seenSplits.size());
}
private void assertSplit(SplitPermissionInfo split, String permission, int targetSdk) {
diff --git a/tests/tests/permission/telephony/src/android/permission/cts/telephony/TelephonyManagerPermissionTest.java b/tests/tests/permission/telephony/src/android/permission/cts/telephony/TelephonyManagerPermissionTest.java
index 3671255..f0b8117 100644
--- a/tests/tests/permission/telephony/src/android/permission/cts/telephony/TelephonyManagerPermissionTest.java
+++ b/tests/tests/permission/telephony/src/android/permission/cts/telephony/TelephonyManagerPermissionTest.java
@@ -33,6 +33,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
+
/**
* Test the non-location-related functionality of TelephonyManager.
*/
@@ -302,6 +304,27 @@
}
}
+ /**
+ * Verify that setForbiddenPlmns requires Permission.
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+ * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+ */
+ @Test
+ public void testSetForbiddenPlmns() {
+ if (!mHasTelephony) {
+ return;
+ }
+
+ try {
+ mTelephonyManager.setForbiddenPlmns(new ArrayList<String>());
+ fail("SetForbiddenPlmns did not throw a SecurityException");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
private static Context getContext() {
return InstrumentationRegistry.getContext();
}
diff --git a/tests/backup/app/Android.mk b/tests/tests/permission/testapps/Android.mk
similarity index 91%
rename from tests/backup/app/Android.mk
rename to tests/tests/permission/testapps/Android.mk
index cddf11e..1d314d2 100644
--- a/tests/backup/app/Android.mk
+++ b/tests/tests/permission/testapps/Android.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright (C) 2019 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/tests/backup/app/permission22/Android.mk b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionDefinerApp/Android.mk
similarity index 81%
copy from tests/backup/app/permission22/Android.mk
copy to tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionDefinerApp/Android.mk
index 8790e19..e81acdc 100644
--- a/tests/backup/app/permission22/Android.mk
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionDefinerApp/Android.mk
@@ -11,21 +11,20 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-
+#
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
-
-LOCAL_PACKAGE_NAME := CtsPermissionBackupApp22
-LOCAL_SDK_VERSION := current
-
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-include $(BUILD_CTS_SUPPORT_PACKAGE)
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
+LOCAL_PACKAGE_NAME := CtsAdversarialPermissionDefinerApp
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionDefinerApp/AndroidManifest.xml b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionDefinerApp/AndroidManifest.xml
new file mode 100644
index 0000000..d28fba8
--- /dev/null
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionDefinerApp/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.permission.cts.revokepermissionwhenremoved.AdversarialPermissionDefinerApp">
+
+ <permission android:name="android.permission.cts.revokepermissionwhenremoved.TestPermission"
+ android:protectionLevel="dangerous"
+ android:label="TestPermission"
+ android:description="@string/test_permission" />
+
+ <application>
+ </application>
+</manifest>
diff --git a/libs/rollback/testapp/res_v1/values/values.xml b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionDefinerApp/res/values/strings.xml
similarity index 84%
copy from libs/rollback/testapp/res_v1/values/values.xml
copy to tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionDefinerApp/res/values/strings.xml
index 8e71917..bfb3e1e 100644
--- a/libs/rollback/testapp/res_v1/values/values.xml
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionDefinerApp/res/values/strings.xml
@@ -14,6 +14,6 @@
limitations under the License.
-->
-<resources>
- <integer name="app_version">1</integer>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="test_permission">Test Permission</string>
</resources>
diff --git a/tests/backup/app/permission22/Android.mk b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionUserApp/Android.mk
similarity index 81%
copy from tests/backup/app/permission22/Android.mk
copy to tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionUserApp/Android.mk
index 8790e19..8856eb8 100644
--- a/tests/backup/app/permission22/Android.mk
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionUserApp/Android.mk
@@ -11,21 +11,19 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-
+#
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
-
-LOCAL_PACKAGE_NAME := CtsPermissionBackupApp22
-LOCAL_SDK_VERSION := current
-
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey2
+LOCAL_PACKAGE_NAME := CtsAdversarialPermissionUserApp
-include $(BUILD_CTS_SUPPORT_PACKAGE)
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/packageinstaller/atomicinstall/testdata/apk/src/com/android/tests/atomicinstall/testapp/MainActivity.java b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionUserApp/AndroidManifest.xml
similarity index 63%
copy from tests/tests/packageinstaller/atomicinstall/testdata/apk/src/com/android/tests/atomicinstall/testapp/MainActivity.java
copy to tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionUserApp/AndroidManifest.xml
index 37276e2..f514d54 100644
--- a/tests/tests/packageinstaller/atomicinstall/testdata/apk/src/com/android/tests/atomicinstall/testapp/MainActivity.java
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionUserApp/AndroidManifest.xml
@@ -1,4 +1,5 @@
-/*
+<?xml version="1.0" encoding="utf-8"?>
+<!--
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -12,17 +13,13 @@
* WITHOUT 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.tests.atomicinstall.testapp;
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.permission.cts.revokepermissionwhenremoved.userapp">
-import android.app.Activity;
-import android.os.Bundle;
+ <uses-permission android:name="android.permission.cts.revokepermissionwhenremoved.TestPermission" />
-public class MainActivity extends Activity {
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- }
-}
+ <application>
+ </application>
+</manifest>
diff --git a/tests/backup/app/Android.mk b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/Android.mk
similarity index 91%
copy from tests/backup/app/Android.mk
copy to tests/tests/permission/testapps/RevokePermissionWhenRemoved/Android.mk
index cddf11e..1d314d2 100644
--- a/tests/backup/app/Android.mk
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/Android.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2017 The Android Open Source Project
+# Copyright (C) 2019 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
diff --git a/tests/backup/app/permission22/Android.mk b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionDefinerApp/Android.mk
similarity index 81%
copy from tests/backup/app/permission22/Android.mk
copy to tests/tests/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionDefinerApp/Android.mk
index 8790e19..8e22243 100644
--- a/tests/backup/app/permission22/Android.mk
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionDefinerApp/Android.mk
@@ -11,21 +11,20 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-
+#
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
-
-LOCAL_PACKAGE_NAME := CtsPermissionBackupApp22
-LOCAL_SDK_VERSION := current
-
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-include $(BUILD_CTS_SUPPORT_PACKAGE)
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
+LOCAL_PACKAGE_NAME := CtsRuntimePermissionDefinerApp
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionDefinerApp/AndroidManifest.xml b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionDefinerApp/AndroidManifest.xml
new file mode 100644
index 0000000..d3cf6d0
--- /dev/null
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionDefinerApp/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.permission.cts.revokepermissionwhenremoved.runtimepermissiondefinerapp">
+
+ <permission android:name="android.permission.cts.revokepermissionwhenremoved.TestRuntimePermission"
+ android:protectionLevel="dangerous"
+ android:label="TestPermission"
+ android:description="@string/test_permission" />
+
+ <application>
+ </application>
+</manifest>
diff --git a/libs/rollback/testapp/res_v1/values/values.xml b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionDefinerApp/res/values/strings.xml
similarity index 84%
copy from libs/rollback/testapp/res_v1/values/values.xml
copy to tests/tests/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionDefinerApp/res/values/strings.xml
index 8e71917..bfb3e1e 100644
--- a/libs/rollback/testapp/res_v1/values/values.xml
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionDefinerApp/res/values/strings.xml
@@ -14,6 +14,6 @@
limitations under the License.
-->
-<resources>
- <integer name="app_version">1</integer>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="test_permission">Test Permission</string>
</resources>
diff --git a/tests/backup/app/permission22/Android.mk b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionUserApp/Android.mk
similarity index 81%
copy from tests/backup/app/permission22/Android.mk
copy to tests/tests/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionUserApp/Android.mk
index 8790e19..112a132 100644
--- a/tests/backup/app/permission22/Android.mk
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionUserApp/Android.mk
@@ -11,21 +11,19 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-
+#
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
-
-LOCAL_PACKAGE_NAME := CtsPermissionBackupApp22
-LOCAL_SDK_VERSION := current
-
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey2
+LOCAL_PACKAGE_NAME := CtsRuntimePermissionUserApp
-include $(BUILD_CTS_SUPPORT_PACKAGE)
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/packageinstaller/atomicinstall/testdata/apk/src/com/android/tests/atomicinstall/testapp/MainActivity.java b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionUserApp/AndroidManifest.xml
similarity index 61%
copy from tests/tests/packageinstaller/atomicinstall/testdata/apk/src/com/android/tests/atomicinstall/testapp/MainActivity.java
copy to tests/tests/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionUserApp/AndroidManifest.xml
index 37276e2..d977e46 100644
--- a/tests/tests/packageinstaller/atomicinstall/testdata/apk/src/com/android/tests/atomicinstall/testapp/MainActivity.java
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionUserApp/AndroidManifest.xml
@@ -1,4 +1,5 @@
-/*
+<?xml version="1.0" encoding="utf-8"?>
+<!--
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -12,17 +13,13 @@
* WITHOUT 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.tests.atomicinstall.testapp;
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.permission.cts.revokepermissionwhenremoved.runtimepermissionuserapp">
-import android.app.Activity;
-import android.os.Bundle;
+ <uses-permission android:name="android.permission.cts.revokepermissionwhenremoved.TestRuntimePermission" />
-public class MainActivity extends Activity {
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- }
-}
+ <application>
+ </application>
+</manifest>
diff --git a/tests/backup/app/permission22/Android.mk b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/VictimPermissionDefinerApp/Android.mk
similarity index 81%
copy from tests/backup/app/permission22/Android.mk
copy to tests/tests/permission/testapps/RevokePermissionWhenRemoved/VictimPermissionDefinerApp/Android.mk
index 8790e19..e7f4072 100644
--- a/tests/backup/app/permission22/Android.mk
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/VictimPermissionDefinerApp/Android.mk
@@ -11,21 +11,21 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-
+#
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
-
-LOCAL_PACKAGE_NAME := CtsPermissionBackupApp22
-LOCAL_SDK_VERSION := current
-
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
-include $(BUILD_CTS_SUPPORT_PACKAGE)
+LOCAL_PACKAGE_NAME := CtsVictimPermissionDefinerApp
+
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/VictimPermissionDefinerApp/AndroidManifest.xml b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/VictimPermissionDefinerApp/AndroidManifest.xml
new file mode 100644
index 0000000..3fb0abd
--- /dev/null
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/VictimPermissionDefinerApp/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.permission.cts.revokepermissionwhenremoved.VictimPermissionDefinerApp">
+ <permission android:name="android.permission.cts.revokepermissionwhenremoved.TestPermission"
+ android:protectionLevel="signature"
+ android:label="Test Permission"
+ android:description="@string/test_permission" />
+ <application>
+ </application>
+</manifest>
diff --git a/libs/rollback/testapp/res_v1/values/values.xml b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/VictimPermissionDefinerApp/res/values/strings.xml
similarity index 84%
copy from libs/rollback/testapp/res_v1/values/values.xml
copy to tests/tests/permission/testapps/RevokePermissionWhenRemoved/VictimPermissionDefinerApp/res/values/strings.xml
index 8e71917..bfb3e1e 100644
--- a/libs/rollback/testapp/res_v1/values/values.xml
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/VictimPermissionDefinerApp/res/values/strings.xml
@@ -14,6 +14,6 @@
limitations under the License.
-->
-<resources>
- <integer name="app_version">1</integer>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="test_permission">Test Permission</string>
</resources>
diff --git a/tests/tests/permission2/Android.bp b/tests/tests/permission2/Android.bp
index adea669..840359f 100644
--- a/tests/tests/permission2/Android.bp
+++ b/tests/tests/permission2/Android.bp
@@ -25,6 +25,7 @@
],
libs: ["android.test.base.stubs"],
static_libs: [
+ "androidx.test.core",
"compatibility-device-util-axt",
"ctstestrunner-axt",
"guava",
@@ -32,6 +33,27 @@
"truth-prebuilt",
"permission-test-util-lib"
],
- srcs: ["src/**/*.java"],
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt"
+ ],
sdk_version: "test_current",
+ data: [
+ ":CtsLocationPermissionsUserSdk22",
+ ":CtsLocationPermissionsUserSdk29",
+ ":CtsSMSCallLogPermissionsUserSdk22",
+ ":CtsSMSCallLogPermissionsUserSdk29",
+ ":CtsStoragePermissionsUserDefaultSdk22",
+ ":CtsStoragePermissionsUserDefaultSdk28",
+ ":CtsStoragePermissionsUserDefaultSdk29",
+ ":CtsStoragePermissionsUserOptInSdk22",
+ ":CtsStoragePermissionsUserOptInSdk28",
+ ":CtsStoragePermissionsUserOptOutSdk29",
+ ":CtsLegacyStorageNotIsolatedWithSharedUid",
+ ":CtsLegacyStorageIsolatedWithSharedUid",
+ ":CtsLegacyStorageRestrictedWithSharedUid",
+ ":CtsLegacyStorageRestrictedSdk28WithSharedUid",
+ ":CtsSMSRestrictedWithSharedUid",
+ ":CtsSMSNotRestrictedWithSharedUid",
+ ],
}
diff --git a/tests/tests/permission2/CtsLegacyStorageIsolatedWithSharedUid/Android.bp b/tests/tests/permission2/CtsLegacyStorageIsolatedWithSharedUid/Android.bp
index 6ee6120..ea9e86e 100644
--- a/tests/tests/permission2/CtsLegacyStorageIsolatedWithSharedUid/Android.bp
+++ b/tests/tests/permission2/CtsLegacyStorageIsolatedWithSharedUid/Android.bp
@@ -19,11 +19,4 @@
defaults: ["cts_defaults"],
sdk_version: "current",
-
- test_suites: [
- "cts",
- "vts",
- "general-tests",
- "cts_instant",
- ]
}
\ No newline at end of file
diff --git a/tests/tests/permission2/CtsLegacyStorageNotIsolatedWithSharedUid/Android.bp b/tests/tests/permission2/CtsLegacyStorageNotIsolatedWithSharedUid/Android.bp
index 63c2a14..c44004b 100644
--- a/tests/tests/permission2/CtsLegacyStorageNotIsolatedWithSharedUid/Android.bp
+++ b/tests/tests/permission2/CtsLegacyStorageNotIsolatedWithSharedUid/Android.bp
@@ -19,11 +19,4 @@
defaults: ["cts_defaults"],
sdk_version: "current",
-
- test_suites: [
- "cts",
- "vts",
- "general-tests",
- "cts_instant",
- ]
}
\ No newline at end of file
diff --git a/tests/tests/permission2/CtsLegacyStorageRestrictedSdk28WithSharedUid/Android.bp b/tests/tests/permission2/CtsLegacyStorageRestrictedSdk28WithSharedUid/Android.bp
index 50ac715..f3b9994 100644
--- a/tests/tests/permission2/CtsLegacyStorageRestrictedSdk28WithSharedUid/Android.bp
+++ b/tests/tests/permission2/CtsLegacyStorageRestrictedSdk28WithSharedUid/Android.bp
@@ -19,11 +19,4 @@
defaults: ["cts_defaults"],
sdk_version: "current",
-
- test_suites: [
- "cts",
- "vts",
- "general-tests",
- "cts_instant",
- ]
}
\ No newline at end of file
diff --git a/tests/tests/permission2/CtsLegacyStorageRestrictedWithSharedUid/Android.bp b/tests/tests/permission2/CtsLegacyStorageRestrictedWithSharedUid/Android.bp
index 78068f9..b0b486d 100644
--- a/tests/tests/permission2/CtsLegacyStorageRestrictedWithSharedUid/Android.bp
+++ b/tests/tests/permission2/CtsLegacyStorageRestrictedWithSharedUid/Android.bp
@@ -19,11 +19,4 @@
defaults: ["cts_defaults"],
sdk_version: "current",
-
- test_suites: [
- "cts",
- "vts",
- "general-tests",
- "cts_instant",
- ]
}
\ No newline at end of file
diff --git a/tests/tests/permission2/CtsLocationPermissionsUserSdk22/Android.bp b/tests/tests/permission2/CtsLocationPermissionsUserSdk22/Android.bp
index 35ab9e4..ef1e160 100644
--- a/tests/tests/permission2/CtsLocationPermissionsUserSdk22/Android.bp
+++ b/tests/tests/permission2/CtsLocationPermissionsUserSdk22/Android.bp
@@ -20,13 +20,6 @@
sdk_version: "current",
- test_suites: [
- "cts",
- "vts",
- "general-tests",
- "cts_instant",
- ],
-
// TODO: Uncomment when uncommenting the test
// srcs: ["src/**/*.java"]
diff --git a/tests/tests/permission2/CtsLocationPermissionsUserSdk29/Android.bp b/tests/tests/permission2/CtsLocationPermissionsUserSdk29/Android.bp
index 3804b92..44e9af0 100644
--- a/tests/tests/permission2/CtsLocationPermissionsUserSdk29/Android.bp
+++ b/tests/tests/permission2/CtsLocationPermissionsUserSdk29/Android.bp
@@ -20,13 +20,6 @@
sdk_version: "current",
- test_suites: [
- "cts",
- "vts",
- "general-tests",
- "cts_instant",
- ],
-
// TODO: Uncomment when uncommenting the test
// srcs: ["src/**/*.java"]
diff --git a/tests/tests/permission2/CtsSMSCallLogPermissionsUserSdk22/Android.bp b/tests/tests/permission2/CtsSMSCallLogPermissionsUserSdk22/Android.bp
index 0fe3599..d1fc86a 100644
--- a/tests/tests/permission2/CtsSMSCallLogPermissionsUserSdk22/Android.bp
+++ b/tests/tests/permission2/CtsSMSCallLogPermissionsUserSdk22/Android.bp
@@ -20,13 +20,6 @@
sdk_version: "current",
- test_suites: [
- "cts",
- "vts",
- "general-tests",
- "cts_instant",
- ],
-
// TODO: Uncomment when uncommenting the test
// srcs: ["src/**/*.java"]
diff --git a/tests/tests/permission2/CtsSMSCallLogPermissionsUserSdk29/Android.bp b/tests/tests/permission2/CtsSMSCallLogPermissionsUserSdk29/Android.bp
index a0189ad..3246d76 100644
--- a/tests/tests/permission2/CtsSMSCallLogPermissionsUserSdk29/Android.bp
+++ b/tests/tests/permission2/CtsSMSCallLogPermissionsUserSdk29/Android.bp
@@ -20,13 +20,6 @@
sdk_version: "current",
- test_suites: [
- "cts",
- "vts",
- "general-tests",
- "cts_instant",
- ],
-
// TODO: Uncomment when uncommenting the test
// srcs: ["src/**/*.java"]
diff --git a/tests/tests/permission2/CtsSMSNotRestrictedWithSharedUid/Android.bp b/tests/tests/permission2/CtsSMSNotRestrictedWithSharedUid/Android.bp
index 9806571..34be9bf 100644
--- a/tests/tests/permission2/CtsSMSNotRestrictedWithSharedUid/Android.bp
+++ b/tests/tests/permission2/CtsSMSNotRestrictedWithSharedUid/Android.bp
@@ -19,11 +19,4 @@
defaults: ["cts_defaults"],
sdk_version: "current",
-
- test_suites: [
- "cts",
- "vts",
- "general-tests",
- "cts_instant",
- ]
}
\ No newline at end of file
diff --git a/tests/tests/permission2/CtsSMSRestrictedWithSharedUid/Android.bp b/tests/tests/permission2/CtsSMSRestrictedWithSharedUid/Android.bp
index ec6d128..305125a 100644
--- a/tests/tests/permission2/CtsSMSRestrictedWithSharedUid/Android.bp
+++ b/tests/tests/permission2/CtsSMSRestrictedWithSharedUid/Android.bp
@@ -19,11 +19,4 @@
defaults: ["cts_defaults"],
sdk_version: "current",
-
- test_suites: [
- "cts",
- "vts",
- "general-tests",
- "cts_instant",
- ]
}
\ No newline at end of file
diff --git a/tests/tests/permission2/CtsStoragePermissionsUserDefaultSdk22/Android.bp b/tests/tests/permission2/CtsStoragePermissionsUserDefaultSdk22/Android.bp
index f63f14a..8183468 100644
--- a/tests/tests/permission2/CtsStoragePermissionsUserDefaultSdk22/Android.bp
+++ b/tests/tests/permission2/CtsStoragePermissionsUserDefaultSdk22/Android.bp
@@ -17,11 +17,4 @@
android_test_helper_app {
name: "CtsStoragePermissionsUserDefaultSdk22",
defaults: ["cts_defaults"],
-
- test_suites: [
- "cts",
- "vts",
- "general-tests",
- "cts_instant",
- ]
}
\ No newline at end of file
diff --git a/tests/tests/permission2/CtsStoragePermissionsUserDefaultSdk28/Android.bp b/tests/tests/permission2/CtsStoragePermissionsUserDefaultSdk28/Android.bp
index be2761a..738f20c 100644
--- a/tests/tests/permission2/CtsStoragePermissionsUserDefaultSdk28/Android.bp
+++ b/tests/tests/permission2/CtsStoragePermissionsUserDefaultSdk28/Android.bp
@@ -17,11 +17,4 @@
android_test_helper_app {
name: "CtsStoragePermissionsUserDefaultSdk28",
defaults: ["cts_defaults"],
-
- test_suites: [
- "cts",
- "vts",
- "general-tests",
- "cts_instant",
- ]
}
\ No newline at end of file
diff --git a/tests/tests/permission2/CtsStoragePermissionsUserDefaultSdk29/Android.bp b/tests/tests/permission2/CtsStoragePermissionsUserDefaultSdk29/Android.bp
index eb77c12..53086d3 100644
--- a/tests/tests/permission2/CtsStoragePermissionsUserDefaultSdk29/Android.bp
+++ b/tests/tests/permission2/CtsStoragePermissionsUserDefaultSdk29/Android.bp
@@ -19,11 +19,4 @@
defaults: ["cts_defaults"],
sdk_version: "current",
-
- test_suites: [
- "cts",
- "vts",
- "general-tests",
- "cts_instant",
- ]
}
\ No newline at end of file
diff --git a/tests/tests/permission2/CtsStoragePermissionsUserOptInSdk22/Android.bp b/tests/tests/permission2/CtsStoragePermissionsUserOptInSdk22/Android.bp
index b7f40bf..843176f 100644
--- a/tests/tests/permission2/CtsStoragePermissionsUserOptInSdk22/Android.bp
+++ b/tests/tests/permission2/CtsStoragePermissionsUserOptInSdk22/Android.bp
@@ -17,11 +17,4 @@
android_test_helper_app {
name: "CtsStoragePermissionsUserOptInSdk22",
defaults: ["cts_defaults"],
-
- test_suites: [
- "cts",
- "vts",
- "general-tests",
- "cts_instant",
- ]
}
\ No newline at end of file
diff --git a/tests/tests/permission2/CtsStoragePermissionsUserOptInSdk28/Android.bp b/tests/tests/permission2/CtsStoragePermissionsUserOptInSdk28/Android.bp
index 63062c1..c0b5fd6 100644
--- a/tests/tests/permission2/CtsStoragePermissionsUserOptInSdk28/Android.bp
+++ b/tests/tests/permission2/CtsStoragePermissionsUserOptInSdk28/Android.bp
@@ -17,11 +17,4 @@
android_test_helper_app {
name: "CtsStoragePermissionsUserOptInSdk28",
defaults: ["cts_defaults"],
-
- test_suites: [
- "cts",
- "vts",
- "general-tests",
- "cts_instant",
- ]
}
\ No newline at end of file
diff --git a/tests/tests/permission2/CtsStoragePermissionsUserOptOutSdk29/Android.bp b/tests/tests/permission2/CtsStoragePermissionsUserOptOutSdk29/Android.bp
index 9d6a481..e1a43da 100644
--- a/tests/tests/permission2/CtsStoragePermissionsUserOptOutSdk29/Android.bp
+++ b/tests/tests/permission2/CtsStoragePermissionsUserOptOutSdk29/Android.bp
@@ -19,11 +19,4 @@
defaults: ["cts_defaults"],
sdk_version: "current",
-
- test_suites: [
- "cts",
- "vts",
- "general-tests",
- "cts_instant",
- ]
}
\ No newline at end of file
diff --git a/tests/tests/permission2/res/raw/android_manifest.xml b/tests/tests/permission2/res/raw/android_manifest.xml
index f2a841d..16ad73f 100644
--- a/tests/tests/permission2/res/raw/android_manifest.xml
+++ b/tests/tests/permission2/res/raw/android_manifest.xml
@@ -102,9 +102,6 @@
<protected-broadcast android:name="android.os.action.POWER_SAVE_TEMP_WHITELIST_CHANGED" />
<protected-broadcast android:name="android.os.action.POWER_SAVE_MODE_CHANGED_INTERNAL" />
- <!-- @deprecated This is rarely used and will be phased out soon. -->
- <protected-broadcast android:name="android.os.action.SCREEN_BRIGHTNESS_BOOST_CHANGED" />
-
<protected-broadcast android:name="android.app.action.ENTER_CAR_MODE" />
<protected-broadcast android:name="android.app.action.EXIT_CAR_MODE" />
<protected-broadcast android:name="android.app.action.ENTER_DESK_MODE" />
@@ -1206,6 +1203,15 @@
android:description="@string/permdesc_camera"
android:protectionLevel="dangerous|instant" />
+ <!-- @SystemApi Required in addition to android.permission.CAMERA to be able to access
+ system only camera devices.
+ <p>Protection level: system|signature
+ @hide -->
+ <permission android:name="android.permission.SYSTEM_CAMERA"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_systemCamera"
+ android:description="@string/permdesc_systemCamera"
+ android:protectionLevel="system|signature" />
<!-- ====================================================================== -->
<!-- Permissions for accessing the device sensors -->
@@ -1522,18 +1528,6 @@
<permission android:name="android.permission.OVERRIDE_WIFI_CONFIG"
android:protectionLevel="signature|privileged" />
- <!-- @hide -->
- <permission android:name="android.permission.ACCESS_WIMAX_STATE"
- android:description="@string/permdesc_accessWimaxState"
- android:label="@string/permlab_accessWimaxState"
- android:protectionLevel="normal" />
-
- <!-- @hide -->
- <permission android:name="android.permission.CHANGE_WIMAX_STATE"
- android:description="@string/permdesc_changeWimaxState"
- android:label="@string/permlab_changeWimaxState"
- android:protectionLevel="normal" />
-
<!-- Allows applications to act as network scorers. @hide @SystemApi-->
<permission android:name="android.permission.SCORE_NETWORKS"
android:protectionLevel="signature|privileged" />
@@ -1542,7 +1536,7 @@
recommendations and scores from the NetworkScoreService.
<p>Not for use by third-party applications. @hide -->
<permission android:name="android.permission.REQUEST_NETWORK_SCORES"
- android:protectionLevel="signature|setup" />
+ android:protectionLevel="signature|setup|privileged" />
<!-- Allows network stack services (Connectivity and Wifi) to coordinate
<p>Not for use by third-party or privileged applications.
@@ -1603,12 +1597,6 @@
<permission android:name="android.permission.MANAGE_LOWPAN_INTERFACES"
android:protectionLevel="signature|privileged" />
- <!-- @hide Allows internal management of Wi-Fi connectivity state when on
- wireless consent mode.
- <p>Not for use by third-party applications. -->
- <permission android:name="android.permission.MANAGE_WIFI_WHEN_WIRELESS_CONSENT_REQUIRED"
- android:protectionLevel="signature" />
-
<!-- #SystemApi @hide Allows an app to bypass Private DNS.
<p>Not for use by third-party applications.
TODO: publish as system API in next API release. -->
@@ -2835,6 +2823,11 @@
<permission android:name="android.permission.STATUS_BAR_SERVICE"
android:protectionLevel="signature" />
+ <!-- Allows an application to trigger bugreports via the shell app (which uses bugreport API)
+ @hide -->
+ <permission android:name="android.permission.TRIGGER_SHELL_BUGREPORT"
+ android:protectionLevel="signature" />
+
<!-- Allows an application to bind to third party quick settings tiles.
<p>Should only be requested by the System, should be required by
TileService declarations.-->
@@ -3463,6 +3456,11 @@
<permission android:name="android.permission.OBSERVE_ROLE_HOLDERS"
android:protectionLevel="signature|installer" />
+ <!-- Allows an application to manage the companion devices.
+ @hide -->
+ <permission android:name="android.permission.MANAGE_COMPANION_DEVICES"
+ android:protectionLevel="signature" />
+
<!-- @SystemApi Allows an application to use SurfaceFlinger's low level features.
<p>Not for use by third-party applications.
@hide
@@ -4218,6 +4216,18 @@
android:description="@string/permdesc_bindCarrierServices"
android:protectionLevel="signature|privileged" />
+ <!-- @SystemApi @TestApi Allows an application to forward cell broadcast messages to the cell
+ broadcast module. This is required in order to bind to the cell broadcast service, and
+ ensures that only the system can forward messages to it.
+
+ <p>Protection level: signature|privileged
+
+ @hide -->
+ <permission android:name="android.permission.BIND_CELL_BROADCAST_SERVICE"
+ android:label="@string/permlab_bindCellBroadcastService"
+ android:description="@string/permdesc_bindCellBroadcastService"
+ android:protectionLevel="signature" />
+
<!--
Allows the holder to start the permission usage screen for an app.
<p>Protection level: signature|installer
@@ -4404,12 +4414,12 @@
<!-- @SystemApi Allows to access all app shortcuts.
@hide -->
<permission android:name="android.permission.ACCESS_SHORTCUTS"
- android:protectionLevel="signature|textClassifier" />
+ android:protectionLevel="signature|appPredictor" />
<!-- @SystemApi Allows unlimited calls to shortcut mutation APIs.
@hide -->
<permission android:name="android.permission.UNLIMITED_SHORTCUTS_API_CALLS"
- android:protectionLevel="signature|textClassifier" />
+ android:protectionLevel="signature|appPredictor" />
<!-- @SystemApi Allows an application to read the runtime profiles of other apps.
@hide <p>Not for use by third-party applications. -->
@@ -4469,6 +4479,13 @@
<permission android:name="android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE"
android:protectionLevel="signature|privileged" />
+ <!-- @SystemApi Must be required by an {@link android.service.storage.ExternalStorageService} to
+ ensure that only the system can bind to it.
+ @hide This is not a third-party API (intended for OEMs and system apps).
+ -->
+ <permission android:name="android.permission.BIND_EXTERNAL_STORAGE_SERVICE"
+ android:protectionLevel="signature" />
+
<!-- @hide Permission that allows configuring appops.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.MANAGE_APPOPS"
@@ -4543,6 +4560,11 @@
<permission android:name="android.permission.MONITOR_INPUT"
android:protectionLevel="signature" />
+ <!-- Allows query of any normal app on the device, regardless of manifest declarations. -->
+ <permission android:name="android.permission.QUERY_ALL_PACKAGES"
+ android:protectionLevel="normal" />
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
+
<application android:process="system"
android:persistent="true"
android:hasCode="false"
diff --git a/tests/tests/permission2/src/android/permission2/cts/RestrictedPermissionsTest.java b/tests/tests/permission2/src/android/permission2/cts/RestrictedPermissionsTest.java
index e6b4256..e20b2af 100644
--- a/tests/tests/permission2/src/android/permission2/cts/RestrictedPermissionsTest.java
+++ b/tests/tests/permission2/src/android/permission2/cts/RestrictedPermissionsTest.java
@@ -16,6 +16,8 @@
package android.permission2.cts;
+import static android.Manifest.permission.ACCESS_BACKGROUND_LOCATION;
+import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import static android.Manifest.permission.READ_SMS;
import static android.permission.cts.PermissionUtils.eventually;
import static android.permission.cts.PermissionUtils.isGranted;
@@ -59,6 +61,7 @@
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
@@ -221,28 +224,32 @@
@Test
@AppModeFull
public void testLocationBackgroundPermissionWhitelistedAtInstall29() throws Exception {
- installApp(APK_USES_LOCATION_29, null, null);
+ installApp(APK_USES_LOCATION_29, null, new ArraySet<>(Arrays.asList(ACCESS_FINE_LOCATION,
+ ACCESS_BACKGROUND_LOCATION)));
assertAllRestrictedPermissionWhitelisted();
}
@Test
@AppModeFull
public void testLocationBackgroundPermissionNotWhitelistedAtInstall29() throws Exception {
- installApp(APK_USES_LOCATION_29, Collections.EMPTY_SET, null);
+ installApp(APK_USES_LOCATION_29, Collections.emptySet(),
+ Collections.singleton(ACCESS_FINE_LOCATION));
assertNoRestrictedPermissionWhitelisted();
}
@Test
@AppModeFull
public void testLocationBackgroundPermissionWhitelistedAtInstall22() throws Exception {
- installApp(APK_USES_LOCATION_22, null, null);
+ installApp(APK_USES_LOCATION_22, null, new ArraySet<>(Arrays.asList(ACCESS_FINE_LOCATION,
+ ACCESS_BACKGROUND_LOCATION)));
assertAllRestrictedPermissionWhitelisted();
}
@Test
@AppModeFull
public void testLocationBackgroundPermissionNotWhitelistedAtInstall22() throws Exception {
- installApp(APK_USES_LOCATION_22, Collections.EMPTY_SET, null);
+ installApp(APK_USES_LOCATION_22, Collections.emptySet(),
+ Collections.singleton(ACCESS_FINE_LOCATION));
assertNoRestrictedPermissionWhitelisted();
}
@@ -914,20 +921,15 @@
private @NonNull Set<String> getPermissionsOfAppWithAnyOfFlags(int flags) throws Exception {
final PackageManager packageManager = getContext().getPackageManager();
-
- final PackageInfo packageInfo = packageManager.getPackageInfo(PKG,
- PackageManager.GET_PERMISSIONS);
-
- final Set<String> hardRestrictedPermissions = new ArraySet<>();
- for (String permission : packageInfo.requestedPermissions) {
+ final Set<String> restrictedPermissions = new ArraySet<>();
+ for (String permission : getRequestedPermissionsOfApp()) {
PermissionInfo permInfo = packageManager.getPermissionInfo(permission, 0);
if ((permInfo.flags & flags) != 0) {
- hardRestrictedPermissions.add(permission);
+ restrictedPermissions.add(permission);
}
}
-
- return hardRestrictedPermissions;
+ return restrictedPermissions;
}
private @NonNull Set<String> getRestrictedPermissionsOfApp() throws Exception {
@@ -935,6 +937,13 @@
PermissionInfo.FLAG_HARD_RESTRICTED | PermissionInfo.FLAG_SOFT_RESTRICTED);
}
+ private @NonNull String[] getRequestedPermissionsOfApp() throws Exception {
+ final PackageManager packageManager = getContext().getPackageManager();
+ final PackageInfo packageInfo = packageManager.getPackageInfo(PKG,
+ PackageManager.GET_PERMISSIONS);
+ return packageInfo.requestedPermissions;
+ }
+
private void assertAllRestrictedPermissionWhitelisted() throws Exception {
assertRestrictedPermissionWhitelisted(getRestrictedPermissionsOfApp());
}
@@ -999,7 +1008,7 @@
possibleModes.add(AppOpsManager.MODE_IGNORED);
}
} else {
- possibleModes.add(AppOpsManager.MODE_DEFAULT);
+ possibleModes.add(AppOpsManager.MODE_IGNORED);
}
}
@@ -1120,6 +1129,8 @@
for (String permission : adjustedGrantedPermissions) {
packageManager.grantRuntimePermission(PKG, permission,
getContext().getUser());
+ packageManager.updatePermissionFlags(permission, PKG,
+ PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, 0, getContext().getUser());
}
});
@@ -1127,7 +1138,7 @@
// applied until reviewed
runWithShellPermissionIdentity(() -> {
final PackageManager packageManager = getContext().getPackageManager();
- for (String permission : getRestrictedPermissionsOfApp()) {
+ for (String permission : getRequestedPermissionsOfApp()) {
packageManager.updatePermissionFlags(permission, PKG,
PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED, 0,
getContext().getUser());
diff --git a/tests/tests/permission2/src/android/permission2/cts/RuntimePermissionProperties.kt b/tests/tests/permission2/src/android/permission2/cts/RuntimePermissionProperties.kt
new file mode 100644
index 0000000..e84e3af
--- /dev/null
+++ b/tests/tests/permission2/src/android/permission2/cts/RuntimePermissionProperties.kt
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission2.cts
+
+import android.Manifest.permission.ACCEPT_HANDOVER
+import android.Manifest.permission.ACCESS_COARSE_LOCATION
+import android.Manifest.permission.ACCESS_FINE_LOCATION
+import android.Manifest.permission.ACTIVITY_RECOGNITION
+import android.Manifest.permission.ADD_VOICEMAIL
+import android.Manifest.permission.ANSWER_PHONE_CALLS
+import android.Manifest.permission.BODY_SENSORS
+import android.Manifest.permission.CALL_PHONE
+import android.Manifest.permission.CAMERA
+import android.Manifest.permission.GET_ACCOUNTS
+import android.Manifest.permission.PROCESS_OUTGOING_CALLS
+import android.Manifest.permission.READ_CALENDAR
+import android.Manifest.permission.READ_CALL_LOG
+import android.Manifest.permission.READ_CELL_BROADCASTS
+import android.Manifest.permission.READ_CONTACTS
+import android.Manifest.permission.READ_EXTERNAL_STORAGE
+import android.Manifest.permission.READ_PHONE_NUMBERS
+import android.Manifest.permission.READ_PHONE_STATE
+import android.Manifest.permission.READ_SMS
+import android.Manifest.permission.RECEIVE_MMS
+import android.Manifest.permission.RECEIVE_SMS
+import android.Manifest.permission.RECEIVE_WAP_PUSH
+import android.Manifest.permission.RECORD_AUDIO
+import android.Manifest.permission.SEND_SMS
+import android.Manifest.permission.USE_SIP
+import android.Manifest.permission.WRITE_CALENDAR
+import android.Manifest.permission.WRITE_CALL_LOG
+import android.Manifest.permission.WRITE_CONTACTS
+import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
+import android.Manifest.permission_group.UNDEFINED
+import android.app.AppOpsManager.permissionToOp
+import android.content.pm.PackageManager.GET_PERMISSIONS
+import android.content.pm.PermissionInfo.PROTECTION_DANGEROUS
+import android.content.pm.PermissionInfo.PROTECTION_FLAG_APPOP
+import android.os.Build
+import android.permission.PermissionManager
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.runner.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class RuntimePermissionProperties {
+ private val context = InstrumentationRegistry.getInstrumentation().getTargetContext()
+ private val pm = context.packageManager
+
+ private val platformPkg = pm.getPackageInfo("android", GET_PERMISSIONS)
+ private val platformRuntimePerms = platformPkg.permissions
+ .filter { it.protection == PROTECTION_DANGEROUS }
+ private val platformBgPermNames = platformRuntimePerms.mapNotNull { it.backgroundPermission }
+
+ @Test
+ fun allRuntimeForegroundPermissionNeedAnAppOp() {
+ val platformFgPerms =
+ platformRuntimePerms.filter { !platformBgPermNames.contains(it.name) }
+
+ for (perm in platformFgPerms) {
+ assertThat(permissionToOp(perm.name)).named("AppOp for ${perm.name}").isNotNull()
+ }
+ }
+
+ @Test
+ fun groupOfRuntimePermissionsShouldBeUnknown() {
+ for (perm in platformRuntimePerms) {
+ assertThat(perm.group).named("Group of ${perm.name}").isEqualTo(UNDEFINED)
+ }
+ }
+
+ @Test
+ fun allAppOpPermissionNeedAnAppOp() {
+ val platformAppOpPerms = platformPkg.permissions
+ .filter { (it.protectionFlags and PROTECTION_FLAG_APPOP) != 0 }
+
+ for (perm in platformAppOpPerms) {
+ assertThat(permissionToOp(perm.name)).named("AppOp for ${perm.name}").isNotNull()
+ }
+ }
+
+ /**
+ * The permission of a background permission is the one of its foreground permission
+ */
+ @Test
+ fun allRuntimeBackgroundPermissionCantHaveAnAppOp() {
+ val platformBgPerms =
+ platformRuntimePerms.filter { platformBgPermNames.contains(it.name) }
+
+ for (perm in platformBgPerms) {
+ assertThat(permissionToOp(perm.name)).named("AppOp for ${perm.name}").isNull()
+ }
+ }
+
+ /**
+ * Commonly a new runtime permission is created by splitting an old one into twice
+ */
+ @Test
+ fun runtimePermissionsShouldHaveBeenSplitFromPreviousPermission() {
+ // Runtime permissions in Android P
+ val expectedPerms = mutableSetOf(READ_CONTACTS, WRITE_CONTACTS, GET_ACCOUNTS, READ_CALENDAR,
+ WRITE_CALENDAR, SEND_SMS, RECEIVE_SMS, READ_SMS, RECEIVE_MMS, RECEIVE_WAP_PUSH,
+ READ_CELL_BROADCASTS, READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE,
+ ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION, READ_CALL_LOG, WRITE_CALL_LOG,
+ PROCESS_OUTGOING_CALLS, READ_PHONE_STATE, READ_PHONE_NUMBERS, CALL_PHONE,
+ ADD_VOICEMAIL, USE_SIP, ANSWER_PHONE_CALLS, ACCEPT_HANDOVER, RECORD_AUDIO, CAMERA,
+ BODY_SENSORS)
+
+ // Add permission split since P
+ for (sdkVersion in Build.VERSION_CODES.P + 1..Build.VERSION_CODES.CUR_DEVELOPMENT + 1) {
+ for (splitPerm in
+ context.getSystemService(PermissionManager::class.java)!!.splitPermissions) {
+ if (splitPerm.targetSdk == sdkVersion &&
+ expectedPerms.contains(splitPerm.splitPermission)) {
+ expectedPerms.addAll(splitPerm.newPermissions)
+ }
+ }
+ }
+
+ // Add runtime permission added in Q which were _not_ split from a previously existing
+ // runtime permission
+ expectedPerms.add(ACTIVITY_RECOGNITION)
+
+ assertThat(expectedPerms).containsExactlyElementsIn(platformRuntimePerms.map { it.name })
+ }
+}
diff --git a/tests/tests/preference/OWNERS b/tests/tests/preference/OWNERS
index 827134e..0ea8182 100644
--- a/tests/tests/preference/OWNERS
+++ b/tests/tests/preference/OWNERS
@@ -1,3 +1,4 @@
+# Bug component: 25700
lpf@google.com
pavlis@google.com
clarabayarri@google.com
diff --git a/tests/tests/print/Android.mk b/tests/tests/print/Android.mk
index 908807a..f45aed1 100644
--- a/tests/tests/print/Android.mk
+++ b/tests/tests/print/Android.mk
@@ -1,15 +1,15 @@
# Copyright (C) 2019 The Android Open Source Project
-# #
-# # Licensed under the Apache License, Version 2.0 (the "License");
-# # you may not use this file except in compliance with the License.
-# # You may obtain a copy of the License at
-# #
-# # http://www.apache.org/licenses/LICENSE-2.0
-# #
-# # Unless required by applicable law or agreed to in writing, software
-# # distributed under the License is distributed on an "AS IS" BASIS,
-# # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# # See the License for the specific language governing permissions and
-# # limitations under the License.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
include $(call all-subdir-makefiles)
diff --git a/tests/tests/provider/Android.bp b/tests/tests/provider/Android.bp
index 489025e..0828f57 100644
--- a/tests/tests/provider/Android.bp
+++ b/tests/tests/provider/Android.bp
@@ -35,6 +35,7 @@
srcs: ["src/**/*.java"],
- sdk_version: "test_current",
+ // uncomment when b/140885436 is fixed
+ // sdk_version: "test_current",
min_sdk_version: "21",
}
diff --git a/tests/tests/provider/OWNERS b/tests/tests/provider/OWNERS
new file mode 100644
index 0000000..fea932d
--- /dev/null
+++ b/tests/tests/provider/OWNERS
@@ -0,0 +1,7 @@
+# Bug component: 1344
+jsharkey@android.com
+omakoto@google.com
+yamasani@google.com
+tgunn@google.com
+nicksauer@google.com
+nona@google.com
diff --git a/tests/tests/provider/src/android/provider/cts/ProviderTestUtils.java b/tests/tests/provider/src/android/provider/cts/ProviderTestUtils.java
index 3e47d15..e8eb0b2 100644
--- a/tests/tests/provider/src/android/provider/cts/ProviderTestUtils.java
+++ b/tests/tests/provider/src/android/provider/cts/ProviderTestUtils.java
@@ -16,8 +16,6 @@
package android.provider.cts;
-import static android.provider.cts.MediaStoreTest.TAG;
-
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.fail;
@@ -33,8 +31,9 @@
import android.os.UserManager;
import android.provider.MediaStore;
import android.provider.MediaStore.MediaColumns;
-import android.provider.cts.MediaStoreUtils.PendingParams;
-import android.provider.cts.MediaStoreUtils.PendingSession;
+import android.provider.cts.media.MediaStoreUtils;
+import android.provider.cts.media.MediaStoreUtils.PendingParams;
+import android.provider.cts.media.MediaStoreUtils.PendingSession;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
@@ -65,6 +64,7 @@
* Utility methods for provider cts tests.
*/
public class ProviderTestUtils {
+ static final String TAG = "ProviderTestUtils";
private static final int BACKUP_TIMEOUT_MILLIS = 4000;
private static final Pattern BMGR_ENABLED_PATTERN = Pattern.compile(
@@ -75,7 +75,7 @@
private static final Timeout IO_TIMEOUT = new Timeout("IO_TIMEOUT", 2_000, 2, 2_000);
- static Iterable<String> getSharedVolumeNames() {
+ public static Iterable<String> getSharedVolumeNames() {
// We test both new and legacy volume names
final HashSet<String> testVolumes = new HashSet<>();
testVolumes.addAll(
@@ -84,7 +84,7 @@
return testVolumes;
}
- static String resolveVolumeName(String volumeName) {
+ public static String resolveVolumeName(String volumeName) {
if (MediaStore.VOLUME_EXTERNAL.equals(volumeName)) {
return MediaStore.VOLUME_EXTERNAL_PRIMARY;
} else {
@@ -100,12 +100,12 @@
executeShellCommand(String.format(cmd, packageName, "READ_SMS", mode), uiAutomation);
}
- static String executeShellCommand(String command) throws IOException {
+ public static String executeShellCommand(String command) throws IOException {
return executeShellCommand(command,
InstrumentationRegistry.getInstrumentation().getUiAutomation());
}
- static String executeShellCommand(String command, UiAutomation uiAutomation)
+ public static String executeShellCommand(String command, UiAutomation uiAutomation)
throws IOException {
Log.v(TAG, "$ " + command);
ParcelFileDescriptor pfd = uiAutomation.executeShellCommand(command.toString());
@@ -197,7 +197,7 @@
}
}
- static File stageDir(String volumeName) throws IOException {
+ public static File stageDir(String volumeName) throws IOException {
if (MediaStore.VOLUME_EXTERNAL.equals(volumeName)) {
volumeName = MediaStore.VOLUME_EXTERNAL_PRIMARY;
}
@@ -207,7 +207,7 @@
return dir;
}
- static File stageDownloadDir(String volumeName) throws IOException {
+ public static File stageDownloadDir(String volumeName) throws IOException {
if (MediaStore.VOLUME_EXTERNAL.equals(volumeName)) {
volumeName = MediaStore.VOLUME_EXTERNAL_PRIMARY;
}
@@ -215,7 +215,7 @@
Environment.DIRECTORY_DOWNLOADS, "android.provider.cts");
}
- static File stageFile(int resId, File file) throws IOException {
+ public static File stageFile(int resId, File file) throws IOException {
// The caller may be trying to stage into a location only available to
// the shell user, so we need to perform the entire copy as the shell
final Context context = InstrumentationRegistry.getTargetContext();
@@ -248,11 +248,11 @@
return waitUntilExists(file);
}
- static Uri stageMedia(int resId, Uri collectionUri) throws IOException {
+ public static Uri stageMedia(int resId, Uri collectionUri) throws IOException {
return stageMedia(resId, collectionUri, "image/png");
}
- static Uri stageMedia(int resId, Uri collectionUri, String mimeType) throws IOException {
+ public static Uri stageMedia(int resId, Uri collectionUri, String mimeType) throws IOException {
final Context context = InstrumentationRegistry.getTargetContext();
final String displayName = "cts" + System.nanoTime();
final PendingParams params = new PendingParams(collectionUri, displayName, mimeType);
@@ -266,19 +266,19 @@
}
}
- static Uri scanFile(File file) throws Exception {
+ public static Uri scanFile(File file) throws Exception {
Uri uri = MediaStore.scanFile(InstrumentationRegistry.getTargetContext(), file);
assertWithMessage("no URI for '%s'", file).that(uri).isNotNull();
return uri;
}
- static Uri scanFileFromShell(File file) throws Exception {
+ public static Uri scanFileFromShell(File file) throws Exception {
Uri uri = MediaStore.scanFileFromShell(InstrumentationRegistry.getTargetContext(), file);
assertWithMessage("no URI for '%s'", file).that(uri).isNotNull();
return uri;
}
- static void scanVolume(File file) throws Exception {
+ public static void scanVolume(File file) throws Exception {
MediaStore.scanVolume(InstrumentationRegistry.getTargetContext(), file);
}
diff --git a/tests/tests/provider/src/android/provider/cts/CalendarTest.java b/tests/tests/provider/src/android/provider/cts/calendar/CalendarTest.java
similarity index 99%
rename from tests/tests/provider/src/android/provider/cts/CalendarTest.java
rename to tests/tests/provider/src/android/provider/cts/calendar/CalendarTest.java
index fcd873a..c97d4d9 100644
--- a/tests/tests/provider/src/android/provider/cts/CalendarTest.java
+++ b/tests/tests/provider/src/android/provider/cts/calendar/CalendarTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.provider.cts;
+package android.provider.cts.calendar;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
diff --git a/tests/tests/provider/src/android/provider/cts/CallLogTest.java b/tests/tests/provider/src/android/provider/cts/contacts/CallLogTest.java
similarity index 98%
rename from tests/tests/provider/src/android/provider/cts/CallLogTest.java
rename to tests/tests/provider/src/android/provider/cts/contacts/CallLogTest.java
index 77985c6..be71f6a 100644
--- a/tests/tests/provider/src/android/provider/cts/CallLogTest.java
+++ b/tests/tests/provider/src/android/provider/cts/contacts/CallLogTest.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.provider.cts;
+package android.provider.cts.contacts;
import android.content.ContentResolver;
import android.content.ContentValues;
diff --git a/tests/tests/provider/src/android/provider/cts/contacts/ContactsContract_ContactsTest.java b/tests/tests/provider/src/android/provider/cts/contacts/ContactsContract_ContactsTest.java
index c9d1371..cb3a2a6 100644
--- a/tests/tests/provider/src/android/provider/cts/contacts/ContactsContract_ContactsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/contacts/ContactsContract_ContactsTest.java
@@ -156,6 +156,93 @@
RawContactUtil.delete(mResolver, ids.mRawContactId, true);
}
+ public void testContactDelete_localContactDeletedImmediately() {
+ // Create a raw contact in the local (null) account
+ DatabaseAsserts.ContactIdPair ids = DatabaseAsserts.assertAndCreateContact(
+ mResolver, null);
+
+ ContactUtil.delete(mResolver, ids.mContactId);
+
+ // Assert that the local raw contact is removed from the database and
+ // not merely marked DELETED=1.
+ assertNull(RawContactUtil.queryByRawContactId(mResolver, ids.mRawContactId, null));
+
+ // Nothing to clean up
+ }
+
+ public void testContactDelete_allLocalContactsDeletedImmediately() {
+ // Create two raw contacts in the local (null) account
+ DatabaseAsserts.ContactIdPair ids1 = DatabaseAsserts.assertAndCreateContactWithName(
+ mResolver, null, "John Smith");
+ DatabaseAsserts.ContactIdPair ids2 = DatabaseAsserts.assertAndCreateContactWithName(
+ mResolver, null, "John Smith");
+
+ // Aggregate the two raw contacts together
+ ContactUtil.setAggregationException(mResolver,
+ ContactsContract.AggregationExceptions.TYPE_KEEP_TOGETHER, ids1.mRawContactId,
+ ids2.mRawContactId);
+
+ // Assert that the contacts were aggregated together
+ long contactId1 = RawContactUtil.queryContactIdByRawContactId(mResolver,
+ ids1.mRawContactId);
+ long contactId2 = RawContactUtil.queryContactIdByRawContactId(mResolver,
+ ids2.mRawContactId);
+ assertEquals(contactId1, contactId2);
+
+ // Delete the contact
+ ContactUtil.delete(mResolver, contactId1);
+
+ // Assert that both of the local raw contacts were removed from the database and
+ // not merely marked DELETED=1.
+ assertNull(RawContactUtil.queryByRawContactId(mResolver, ids1.mRawContactId, null));
+ assertNull(RawContactUtil.queryByRawContactId(mResolver, ids2.mRawContactId, null));
+
+ // Nothing to clean up
+ }
+
+ public void testContactDelete_localContactDeletedImmediatelyWhenAggregatedWithNonLocal() {
+ // Create a raw contact in the local (null) account
+ DatabaseAsserts.ContactIdPair ids1 = DatabaseAsserts.assertAndCreateContactWithName(
+ mResolver, null, "John Smith");
+
+ // Create a raw contact in a non-local account with the same name
+ DatabaseAsserts.ContactIdPair ids2 = DatabaseAsserts.assertAndCreateContactWithName(
+ mResolver, StaticAccountAuthenticator.ACCOUNT_1, "John Smith");
+
+ // Aggregate the two raw contacts together
+ ContactUtil.setAggregationException(mResolver,
+ ContactsContract.AggregationExceptions.TYPE_KEEP_TOGETHER, ids1.mRawContactId,
+ ids2.mRawContactId);
+
+ // Assert that the contacts were aggregated together
+ long contactId1 = RawContactUtil.queryContactIdByRawContactId(mResolver,
+ ids1.mRawContactId);
+ long contactId2 = RawContactUtil.queryContactIdByRawContactId(mResolver,
+ ids2.mRawContactId);
+ assertEquals(contactId1, contactId2);
+
+ // Delete the contact
+ ContactUtil.delete(mResolver, contactId1);
+
+ // Assert that the local raw contact was removed from the database
+ assertNull(RawContactUtil.queryByRawContactId(mResolver, ids1.mRawContactId, null));
+
+ // Assert that the non-local raw contact was marked DELETED=1
+ String[] projection = new String[]{
+ ContactsContract.RawContacts.DIRTY,
+ ContactsContract.RawContacts.DELETED
+ };
+ List<String[]> records = RawContactUtil.queryByContactId(mResolver, ids2.mContactId,
+ projection);
+ for (String[] arr : records) {
+ assertEquals("1", arr[0]);
+ assertEquals("1", arr[1]);
+ }
+
+ // Clean up
+ RawContactUtil.delete(mResolver, ids2.mRawContactId, true);
+ }
+
public void testContactUpdate_updatesContactUpdatedTimestamp() {
DatabaseAsserts.ContactIdPair ids = DatabaseAsserts.assertAndCreateContact(mResolver);
diff --git a/tests/tests/provider/src/android/provider/cts/contacts/ContactsContract_RawContactsTest.java b/tests/tests/provider/src/android/provider/cts/contacts/ContactsContract_RawContactsTest.java
index 89889f7..dc49a2e 100644
--- a/tests/tests/provider/src/android/provider/cts/contacts/ContactsContract_RawContactsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/contacts/ContactsContract_RawContactsTest.java
@@ -141,6 +141,18 @@
assertEquals("1", result[1]);
}
+ public void testRawContactDelete_localDeleteRemovesRecord() {
+ long rawContactid = RawContactUtil.insertRawContact(mResolver, null);
+ assertTrue(RawContactUtil.rawContactExistsById(mResolver, rawContactid));
+
+ // Local raw contacts should be deleted immediately even if isSyncAdapter=false
+ RawContactUtil.delete(mResolver, rawContactid, false);
+
+ assertFalse(RawContactUtil.rawContactExistsById(mResolver, rawContactid));
+
+ // Nothing to clean up
+ }
+
public void testRawContactDelete_removesRecord() {
long rawContactid = RawContactUtil.insertRawContact(mResolver,
StaticAccountAuthenticator.ACCOUNT_1);
@@ -153,7 +165,6 @@
// already clean
}
-
// This implicitly tests the Contact create case.
public void testRawContactCreate_updatesContactUpdatedTimestamp() {
long startTime = System.currentTimeMillis();
diff --git a/tests/tests/provider/src/android/provider/cts/contacts/RawContactUtil.java b/tests/tests/provider/src/android/provider/cts/contacts/RawContactUtil.java
index b8e1576..e21190b 100644
--- a/tests/tests/provider/src/android/provider/cts/contacts/RawContactUtil.java
+++ b/tests/tests/provider/src/android/provider/cts/contacts/RawContactUtil.java
@@ -55,8 +55,10 @@
public static long insertRawContact(ContentResolver resolver, Account account) {
ContentValues values = new ContentValues();
- values.put(ContactsContract.RawContacts.ACCOUNT_NAME, account.name);
- values.put(ContactsContract.RawContacts.ACCOUNT_TYPE, account.type);
+ if (account != null) {
+ values.put(ContactsContract.RawContacts.ACCOUNT_NAME, account.name);
+ values.put(ContactsContract.RawContacts.ACCOUNT_TYPE, account.type);
+ }
Uri uri = resolver.insert(URI, values);
return ContentUris.parseId(uri);
}
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStoreAudioTestHelper.java b/tests/tests/provider/src/android/provider/cts/media/MediaStoreAudioTestHelper.java
similarity index 98%
rename from tests/tests/provider/src/android/provider/cts/MediaStoreAudioTestHelper.java
rename to tests/tests/provider/src/android/provider/cts/media/MediaStoreAudioTestHelper.java
index a3ef607..2ae1d6f 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStoreAudioTestHelper.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStoreAudioTestHelper.java
@@ -14,13 +14,14 @@
* limitations under the License.
*/
-package android.provider.cts;
+package android.provider.cts.media;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.net.Uri;
import android.provider.MediaStore;
import android.provider.MediaStore.Audio.Media;
+import android.provider.cts.ProviderTestUtils;
import androidx.test.runner.AndroidJUnit4;
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStoreIntentsTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStoreIntentsTest.java
similarity index 97%
rename from tests/tests/provider/src/android/provider/cts/MediaStoreIntentsTest.java
rename to tests/tests/provider/src/android/provider/cts/media/MediaStoreIntentsTest.java
index 5590a6d..d5442e0 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStoreIntentsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStoreIntentsTest.java
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package android.provider.cts;
+package android.provider.cts.media;
-import static android.provider.cts.MediaStoreTest.TAG;
+import static android.provider.cts.media.MediaStoreTest.TAG;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -25,6 +25,7 @@
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.provider.MediaStore;
+import android.provider.cts.ProviderTestUtils;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
diff --git a/tests/tests/provider/src/android/provider/cts/media/MediaStoreNotificationTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStoreNotificationTest.java
new file mode 100644
index 0000000..e227759
--- /dev/null
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStoreNotificationTest.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.provider.cts.media;
+
+import static android.provider.cts.media.MediaStoreTest.TAG;
+
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.provider.MediaStore;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(Parameterized.class)
+public class MediaStoreNotificationTest {
+ private Context mContext;
+ private ContentResolver mResolver;
+
+ private Uri mSpecificImages;
+ private Uri mSpecificFiles;
+ private Uri mGenericImages;
+ private Uri mGenericFiles;
+
+ @Parameter(0)
+ public String mVolumeName;
+
+ @Parameters
+ public static Iterable<? extends Object> data() {
+ return MediaStore.getExternalVolumeNames(InstrumentationRegistry.getTargetContext());
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mResolver = mContext.getContentResolver();
+
+ Log.d(TAG, "Using volume " + mVolumeName);
+ mSpecificImages = MediaStore.Images.Media.getContentUri(mVolumeName);
+ mSpecificFiles = MediaStore.Files.getContentUri(mVolumeName);
+ mGenericImages = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL);
+ mGenericFiles = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL);
+ }
+
+ @Test
+ public void testSimple() throws Exception {
+ Uri specificImage;
+ Uri specificFile;
+ Uri genericImage;
+ Uri genericFile;
+
+ try (BlockingObserver si = BlockingObserver.createAndRegister(mSpecificImages);
+ BlockingObserver sf = BlockingObserver.createAndRegister(mSpecificFiles);
+ BlockingObserver gi = BlockingObserver.createAndRegister(mGenericImages);
+ BlockingObserver gf = BlockingObserver.createAndRegister(mGenericFiles)) {
+ final ContentValues values = new ContentValues();
+ values.put(MediaStore.Images.Media.IS_PENDING, 1);
+ values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
+ values.put(MediaStore.Images.Media.DISPLAY_NAME, "cts" + System.nanoTime());
+ specificImage = mResolver.insert(mSpecificImages, values);
+
+ final long id = ContentUris.parseId(specificImage);
+ specificFile = ContentUris.withAppendedId(mSpecificFiles, id);
+ genericImage = ContentUris.withAppendedId(mGenericImages, id);
+ genericFile = ContentUris.withAppendedId(mGenericFiles, id);
+ }
+
+ try (BlockingObserver si = BlockingObserver.createAndRegister(specificImage);
+ BlockingObserver sf = BlockingObserver.createAndRegister(specificFile);
+ BlockingObserver gi = BlockingObserver.createAndRegister(genericImage);
+ BlockingObserver gf = BlockingObserver.createAndRegister(genericFile)) {
+ final ContentValues values = new ContentValues();
+ values.put(MediaStore.Images.Media.SIZE, 32);
+ mResolver.update(specificImage, values, null, null);
+ }
+
+ try (BlockingObserver si = BlockingObserver.createAndRegister(specificImage);
+ BlockingObserver sf = BlockingObserver.createAndRegister(specificFile);
+ BlockingObserver gi = BlockingObserver.createAndRegister(genericImage);
+ BlockingObserver gf = BlockingObserver.createAndRegister(genericFile)) {
+ mResolver.delete(specificImage, null, null);
+ }
+ }
+
+ @Test
+ @Ignore("b/139110347")
+ public void testCursor() throws Exception {
+ try (Cursor si = mResolver.query(mSpecificImages, null, null, null);
+ Cursor sf = mResolver.query(mSpecificFiles, null, null, null);
+ Cursor gi = mResolver.query(mGenericImages, null, null, null);
+ Cursor gf = mResolver.query(mGenericFiles, null, null, null)) {
+ try (BlockingObserver sio = BlockingObserver.create();
+ BlockingObserver sfo = BlockingObserver.create();
+ BlockingObserver gio = BlockingObserver.create();
+ BlockingObserver gfo = BlockingObserver.create()) {
+ si.registerContentObserver(sio);
+ sf.registerContentObserver(sfo);
+ gi.registerContentObserver(gio);
+ gf.registerContentObserver(gfo);
+
+ // Insert a simple item that will trigger notifications
+ final ContentValues values = new ContentValues();
+ values.put(MediaStore.Images.Media.IS_PENDING, 1);
+ values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
+ values.put(MediaStore.Images.Media.DISPLAY_NAME, "cts" + System.nanoTime());
+ final Uri uri = mResolver.insert(mSpecificImages, values);
+ mResolver.delete(uri, null, null);
+ }
+ }
+ }
+
+ private static class BlockingObserver extends ContentObserver implements AutoCloseable {
+ private final Uri mUri;
+ private final CountDownLatch mLatch = new CountDownLatch(1);
+
+ private static HandlerThread sHandlerThread;
+ private static Handler sHandler;
+
+ static {
+ sHandlerThread = new HandlerThread(TAG);
+ sHandlerThread.start();
+ sHandler = new Handler(sHandlerThread.getLooper());
+ }
+
+ private BlockingObserver(Uri uri) {
+ super(sHandler);
+ mUri = uri;
+ }
+
+ public static BlockingObserver create() {
+ return new BlockingObserver(null);
+ }
+
+ public static BlockingObserver createAndRegister(Uri uri) {
+ final BlockingObserver obs = new BlockingObserver(uri);
+ InstrumentationRegistry.getTargetContext().getContentResolver()
+ .registerContentObserver(uri, true, obs);
+ return obs;
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ super.onChange(selfChange, uri);
+ Log.v(TAG, "Notified about " + uri);
+ mLatch.countDown();
+ }
+
+ @Override
+ public void close() {
+ try {
+ if (!mLatch.await(5, TimeUnit.SECONDS)) {
+ throw new InterruptedException();
+ }
+ } catch (InterruptedException e) {
+ throw new IllegalStateException("Failed to get notification for " + mUri);
+ } finally {
+ InstrumentationRegistry.getTargetContext().getContentResolver()
+ .unregisterContentObserver(this);
+ }
+ }
+ }
+}
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStorePendingTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStorePendingTest.java
similarity index 98%
rename from tests/tests/provider/src/android/provider/cts/MediaStorePendingTest.java
rename to tests/tests/provider/src/android/provider/cts/media/MediaStorePendingTest.java
index 85d4109..ae5674b 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStorePendingTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStorePendingTest.java
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-package android.provider.cts;
+package android.provider.cts.media;
-import static android.provider.cts.MediaStoreTest.TAG;
import static android.provider.cts.ProviderTestUtils.containsId;
import static android.provider.cts.ProviderTestUtils.getRawFile;
import static android.provider.cts.ProviderTestUtils.getRawFileHash;
import static android.provider.cts.ProviderTestUtils.hash;
+import static android.provider.cts.media.MediaStoreTest.TAG;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -36,11 +36,12 @@
import android.net.Uri;
import android.os.Environment;
import android.os.FileUtils;
-import android.platform.test.annotations.Presubmit;
import android.provider.MediaStore;
import android.provider.MediaStore.MediaColumns;
-import android.provider.cts.MediaStoreUtils.PendingParams;
-import android.provider.cts.MediaStoreUtils.PendingSession;
+import android.provider.cts.ProviderTestUtils;
+import android.provider.cts.R;
+import android.provider.cts.media.MediaStoreUtils.PendingParams;
+import android.provider.cts.media.MediaStoreUtils.PendingSession;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
@@ -62,7 +63,6 @@
import java.util.HashSet;
import java.util.Set;
-@Presubmit
@RunWith(Parameterized.class)
public class MediaStorePendingTest {
private Context mContext;
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStorePlacementTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStorePlacementTest.java
similarity index 98%
rename from tests/tests/provider/src/android/provider/cts/MediaStorePlacementTest.java
rename to tests/tests/provider/src/android/provider/cts/media/MediaStorePlacementTest.java
index c05379c..405a70a 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStorePlacementTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStorePlacementTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.provider.cts;
+package android.provider.cts.media;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -26,9 +26,10 @@
import android.content.Context;
import android.net.Uri;
import android.os.Environment;
-import android.platform.test.annotations.Presubmit;
import android.provider.MediaStore;
import android.provider.MediaStore.MediaColumns;
+import android.provider.cts.ProviderTestUtils;
+import android.provider.cts.R;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
@@ -44,7 +45,6 @@
import java.io.File;
import java.util.Optional;
-@Presubmit
@RunWith(Parameterized.class)
public class MediaStorePlacementTest {
static final String TAG = "MediaStorePlacementTest";
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStoreTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStoreTest.java
similarity index 94%
rename from tests/tests/provider/src/android/provider/cts/MediaStoreTest.java
rename to tests/tests/provider/src/android/provider/cts/media/MediaStoreTest.java
index 937bddf..5f9d6eb 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStoreTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStoreTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.provider.cts;
+package android.provider.cts.media;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -32,9 +32,10 @@
import android.os.SystemClock;
import android.os.storage.StorageManager;
import android.os.storage.StorageVolume;
-import android.platform.test.annotations.Presubmit;
import android.provider.MediaStore;
import android.provider.MediaStore.MediaColumns;
+import android.provider.cts.ProviderTestUtils;
+import android.provider.cts.R;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
@@ -53,7 +54,6 @@
import java.util.Set;
import java.util.UUID;
-@Presubmit
@RunWith(Parameterized.class)
public class MediaStoreTest {
static final String TAG = "MediaStoreTest";
@@ -93,6 +93,18 @@
}
@Test
+ public void testIncludePending() {
+ assertFalse(MediaStore.getIncludePending(mExternalImages));
+ assertTrue(MediaStore.getIncludePending(MediaStore.setIncludePending(mExternalImages)));
+ }
+
+ @Test
+ public void testRequireOriginal() {
+ assertFalse(MediaStore.getRequireOriginal(mExternalImages));
+ assertTrue(MediaStore.getRequireOriginal(MediaStore.setRequireOriginal(mExternalImages)));
+ }
+
+ @Test
public void testGetMediaScannerUri() {
// query
Cursor c = mContentResolver.query(MediaStore.getMediaScannerUri(), null,
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStoreUtils.java b/tests/tests/provider/src/android/provider/cts/media/MediaStoreUtils.java
similarity index 99%
rename from tests/tests/provider/src/android/provider/cts/MediaStoreUtils.java
rename to tests/tests/provider/src/android/provider/cts/media/MediaStoreUtils.java
index 70c6988..0120afb 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStoreUtils.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStoreUtils.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package android.provider.cts;
+package android.provider.cts.media;
import android.content.ContentValues;
import android.content.Context;
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_AudioTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_AudioTest.java
similarity index 96%
rename from tests/tests/provider/src/android/provider/cts/MediaStore_AudioTest.java
rename to tests/tests/provider/src/android/provider/cts/media/MediaStore_AudioTest.java
index a29b93d..2d99160 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_AudioTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_AudioTest.java
@@ -14,12 +14,11 @@
* limitations under the License.
*/
-package android.provider.cts;
+package android.provider.cts.media;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import android.platform.test.annotations.Presubmit;
import android.provider.MediaStore.Audio;
import androidx.test.runner.AndroidJUnit4;
@@ -28,7 +27,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-@Presubmit
@RunWith(AndroidJUnit4.class)
public class MediaStore_AudioTest {
private String mKeyForBeatles;
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_AlbumsTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_AlbumsTest.java
similarity index 96%
rename from tests/tests/provider/src/android/provider/cts/MediaStore_Audio_AlbumsTest.java
rename to tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_AlbumsTest.java
index 9d17b40..ca0413c 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_AlbumsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_AlbumsTest.java
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package android.provider.cts;
+package android.provider.cts.media;
-import static android.provider.cts.MediaStoreTest.TAG;
+import static android.provider.cts.media.MediaStoreTest.TAG;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -32,12 +32,13 @@
import android.database.Cursor;
import android.graphics.BitmapFactory;
import android.net.Uri;
-import android.platform.test.annotations.Presubmit;
import android.provider.MediaStore;
import android.provider.MediaStore.Audio.Albums;
import android.provider.MediaStore.Audio.Media;
-import android.provider.cts.MediaStoreAudioTestHelper.Audio1;
-import android.provider.cts.MediaStoreAudioTestHelper.Audio2;
+import android.provider.cts.ProviderTestUtils;
+import android.provider.cts.R;
+import android.provider.cts.media.MediaStoreAudioTestHelper.Audio1;
+import android.provider.cts.media.MediaStoreAudioTestHelper.Audio2;
import android.util.Log;
import android.util.Size;
@@ -53,7 +54,6 @@
import java.io.File;
import java.io.IOException;
-@Presubmit
@RunWith(Parameterized.class)
public class MediaStore_Audio_AlbumsTest {
private Context mContext;
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_ArtistsTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_ArtistsTest.java
similarity index 94%
rename from tests/tests/provider/src/android/provider/cts/MediaStore_Audio_ArtistsTest.java
rename to tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_ArtistsTest.java
index 5caefc5..6ab7c28 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_ArtistsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_ArtistsTest.java
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package android.provider.cts;
+package android.provider.cts.media;
-import static android.provider.cts.MediaStoreTest.TAG;
+import static android.provider.cts.media.MediaStoreTest.TAG;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -28,10 +28,10 @@
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
-import android.platform.test.annotations.Presubmit;
import android.provider.MediaStore.Audio.Artists;
-import android.provider.cts.MediaStoreAudioTestHelper.Audio1;
-import android.provider.cts.MediaStoreAudioTestHelper.Audio2;
+import android.provider.cts.ProviderTestUtils;
+import android.provider.cts.media.MediaStoreAudioTestHelper.Audio1;
+import android.provider.cts.media.MediaStoreAudioTestHelper.Audio2;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
@@ -43,7 +43,6 @@
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
-@Presubmit
@RunWith(Parameterized.class)
public class MediaStore_Audio_ArtistsTest {
private Context mContext;
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Artists_AlbumsTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_Artists_AlbumsTest.java
similarity index 94%
rename from tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Artists_AlbumsTest.java
rename to tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_Artists_AlbumsTest.java
index 5a28865..a3bd099 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Artists_AlbumsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_Artists_AlbumsTest.java
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package android.provider.cts;
+package android.provider.cts.media;
-import static android.provider.cts.MediaStoreTest.TAG;
+import static android.provider.cts.media.MediaStoreTest.TAG;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -29,12 +29,12 @@
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
-import android.platform.test.annotations.Presubmit;
import android.provider.MediaStore;
import android.provider.MediaStore.Audio.Artists.Albums;
import android.provider.MediaStore.Audio.Media;
-import android.provider.cts.MediaStoreAudioTestHelper.Audio1;
-import android.provider.cts.MediaStoreAudioTestHelper.Audio2;
+import android.provider.cts.ProviderTestUtils;
+import android.provider.cts.media.MediaStoreAudioTestHelper.Audio1;
+import android.provider.cts.media.MediaStoreAudioTestHelper.Audio2;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
@@ -46,7 +46,6 @@
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
-@Presubmit
@RunWith(Parameterized.class)
public class MediaStore_Audio_Artists_AlbumsTest {
private Context mContext;
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_GenresTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_GenresTest.java
similarity index 93%
rename from tests/tests/provider/src/android/provider/cts/MediaStore_Audio_GenresTest.java
rename to tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_GenresTest.java
index 64ed537..7c183e6 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_GenresTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_GenresTest.java
@@ -14,28 +14,25 @@
* limitations under the License.
*/
-package android.provider.cts;
+package android.provider.cts.media;
-import static android.provider.cts.MediaStoreTest.TAG;
+import static android.provider.cts.media.MediaStoreTest.TAG;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
-import android.database.SQLException;
import android.net.Uri;
-import android.platform.test.annotations.Presubmit;
import android.provider.MediaStore.Audio.Genres;
import android.provider.MediaStore.Audio.Genres.Members;
-import android.provider.cts.MediaStoreAudioTestHelper.Audio1;
+import android.provider.cts.ProviderTestUtils;
+import android.provider.cts.media.MediaStoreAudioTestHelper.Audio1;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
@@ -47,7 +44,6 @@
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
-@Presubmit
@RunWith(Parameterized.class)
public class MediaStore_Audio_GenresTest {
private Context mContext;
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Genres_MembersTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_Genres_MembersTest.java
similarity index 97%
rename from tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Genres_MembersTest.java
rename to tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_Genres_MembersTest.java
index 710ebbc..6fd5b9b 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Genres_MembersTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_Genres_MembersTest.java
@@ -14,13 +14,12 @@
* limitations under the License.
*/
-package android.provider.cts;
+package android.provider.cts.media;
-import static android.provider.cts.MediaStoreTest.TAG;
+import static android.provider.cts.media.MediaStoreTest.TAG;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -29,15 +28,14 @@
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
-import android.database.SQLException;
import android.net.Uri;
-import android.platform.test.annotations.Presubmit;
import android.provider.MediaStore;
import android.provider.MediaStore.Audio.Genres;
import android.provider.MediaStore.Audio.Genres.Members;
import android.provider.MediaStore.Audio.Media;
-import android.provider.cts.MediaStoreAudioTestHelper.Audio1;
-import android.provider.cts.MediaStoreAudioTestHelper.Audio2;
+import android.provider.cts.ProviderTestUtils;
+import android.provider.cts.media.MediaStoreAudioTestHelper.Audio1;
+import android.provider.cts.media.MediaStoreAudioTestHelper.Audio2;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
@@ -50,7 +48,6 @@
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
-@Presubmit
@RunWith(Parameterized.class)
public class MediaStore_Audio_Genres_MembersTest {
private Context mContext;
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_MediaTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_MediaTest.java
similarity index 88%
rename from tests/tests/provider/src/android/provider/cts/MediaStore_Audio_MediaTest.java
rename to tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_MediaTest.java
index 4a8afe7..4d834b4 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_MediaTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_MediaTest.java
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package android.provider.cts;
+package android.provider.cts.media;
-import static android.provider.cts.MediaStoreTest.TAG;
+import static android.provider.cts.media.MediaStoreTest.TAG;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -24,15 +24,19 @@
import static org.junit.Assert.assertTrue;
import android.content.ContentResolver;
+import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Environment;
-import android.platform.test.annotations.Presubmit;
import android.provider.MediaStore;
+import android.provider.MediaStore.Audio;
import android.provider.MediaStore.Audio.Media;
-import android.provider.cts.MediaStoreAudioTestHelper.Audio1;
+import android.provider.cts.ProviderTestUtils;
+import android.provider.cts.R;
+import android.provider.cts.media.MediaStoreAudioTestHelper.Audio1;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
@@ -46,7 +50,6 @@
import java.io.File;
-@Presubmit
@RunWith(Parameterized.class)
public class MediaStore_Audio_MediaTest {
private Context mContext;
@@ -78,6 +81,9 @@
Media.getContentUri(mVolumeName), null, null,
null, null));
c.close();
+
+ assertEquals(ContentUris.withAppendedId(Media.getContentUri(mVolumeName), 42),
+ Media.getContentUri(mVolumeName, 42));
}
@Test
@@ -200,4 +206,22 @@
ProviderTestUtils.stageFile(R.raw.testmp3, new File(dir, "d.mp3")));
assertEquals(d, mContentResolver.uncanonicalize(canonicalized));
}
+
+ @Test
+ public void testSortLocale() {
+ final Bundle queryArgs = new Bundle();
+ queryArgs.putStringArray(ContentResolver.QUERY_ARG_SORT_COLUMNS,
+ new String[] { Audio.Media.TITLE });
+
+ for (String locale : new String[] {
+ "zh",
+ "zh@collation=pinyin",
+ "zh@collation=stroke",
+ "zh@collation=zhuyin",
+ }) {
+ queryArgs.putString(ContentResolver.QUERY_ARG_SORT_LOCALE, locale);
+ try (Cursor c = mContentResolver.query(mExternalAudio, null, queryArgs, null)) {
+ }
+ }
+ }
}
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_PlaylistsTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_PlaylistsTest.java
similarity index 94%
rename from tests/tests/provider/src/android/provider/cts/MediaStore_Audio_PlaylistsTest.java
rename to tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_PlaylistsTest.java
index 78cbef0..289d3ac 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_PlaylistsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_PlaylistsTest.java
@@ -14,23 +14,21 @@
* limitations under the License.
*/
-package android.provider.cts;
+package android.provider.cts.media;
-import static android.provider.cts.MediaStoreTest.TAG;
+import static android.provider.cts.media.MediaStoreTest.TAG;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
-import android.database.SQLException;
import android.net.Uri;
-import android.platform.test.annotations.Presubmit;
import android.provider.MediaStore.Audio.Playlists;
+import android.provider.cts.ProviderTestUtils;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
@@ -44,7 +42,6 @@
import java.io.File;
-@Presubmit
@RunWith(Parameterized.class)
public class MediaStore_Audio_PlaylistsTest {
private Context mContext;
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Playlists_MembersTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_Playlists_MembersTest.java
similarity index 97%
rename from tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Playlists_MembersTest.java
rename to tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_Playlists_MembersTest.java
index 0db80d9..9135a4b 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Audio_Playlists_MembersTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_Playlists_MembersTest.java
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package android.provider.cts;
+package android.provider.cts.media;
-import static android.provider.cts.MediaStoreTest.TAG;
+import static android.provider.cts.media.MediaStoreTest.TAG;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -29,17 +29,17 @@
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
-import android.platform.test.annotations.Presubmit;
import android.provider.MediaStore;
import android.provider.MediaStore.Audio.Media;
import android.provider.MediaStore.Audio.Playlists;
import android.provider.MediaStore.Audio.Playlists.Members;
-import android.provider.cts.MediaStoreAudioTestHelper.Audio1;
-import android.provider.cts.MediaStoreAudioTestHelper.Audio2;
-import android.provider.cts.MediaStoreAudioTestHelper.Audio3;
-import android.provider.cts.MediaStoreAudioTestHelper.Audio4;
-import android.provider.cts.MediaStoreAudioTestHelper.Audio5;
-import android.provider.cts.MediaStoreAudioTestHelper.MockAudioMediaInfo;
+import android.provider.cts.ProviderTestUtils;
+import android.provider.cts.media.MediaStoreAudioTestHelper.Audio1;
+import android.provider.cts.media.MediaStoreAudioTestHelper.Audio2;
+import android.provider.cts.media.MediaStoreAudioTestHelper.Audio3;
+import android.provider.cts.media.MediaStoreAudioTestHelper.Audio4;
+import android.provider.cts.media.MediaStoreAudioTestHelper.Audio5;
+import android.provider.cts.media.MediaStoreAudioTestHelper.MockAudioMediaInfo;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
@@ -52,7 +52,6 @@
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
-@Presubmit
@RunWith(Parameterized.class)
public class MediaStore_Audio_Playlists_MembersTest {
private String[] mAudioProjection = {
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_DownloadsTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_DownloadsTest.java
similarity index 97%
rename from tests/tests/provider/src/android/provider/cts/MediaStore_DownloadsTest.java
rename to tests/tests/provider/src/android/provider/cts/media/MediaStore_DownloadsTest.java
index 1c45687..0b6af08 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_DownloadsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_DownloadsTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.provider.cts;
+package android.provider.cts.media;
import static android.provider.cts.ProviderTestUtils.hash;
import static android.provider.cts.ProviderTestUtils.resolveVolumeName;
@@ -32,13 +32,14 @@
import android.net.Uri;
import android.os.Environment;
import android.os.FileUtils;
-import android.platform.test.annotations.Presubmit;
import android.provider.MediaStore;
import android.provider.MediaStore.Downloads;
import android.provider.MediaStore.Files;
import android.provider.MediaStore.Images;
-import android.provider.cts.MediaStoreUtils.PendingParams;
-import android.provider.cts.MediaStoreUtils.PendingSession;
+import android.provider.cts.ProviderTestUtils;
+import android.provider.cts.R;
+import android.provider.cts.media.MediaStoreUtils.PendingParams;
+import android.provider.cts.media.MediaStoreUtils.PendingSession;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
@@ -62,7 +63,6 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
-@Presubmit
@RunWith(Parameterized.class)
public class MediaStore_DownloadsTest {
private static final String TAG = MediaStore_DownloadsTest.class.getSimpleName();
@@ -138,6 +138,9 @@
assertNotNull(c = mContentResolver.query(mExternalDownloads,
null, null, null, null));
c.close();
+
+ assertEquals(ContentUris.withAppendedId(Downloads.getContentUri(mVolumeName), 42),
+ Downloads.getContentUri(mVolumeName, 42));
}
@Test
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_FilesTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_FilesTest.java
similarity index 97%
rename from tests/tests/provider/src/android/provider/cts/MediaStore_FilesTest.java
rename to tests/tests/provider/src/android/provider/cts/media/MediaStore_FilesTest.java
index 9e71c69..757766e 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_FilesTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_FilesTest.java
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package android.provider.cts;
+package android.provider.cts.media;
-import static android.provider.cts.MediaStoreTest.TAG;
import static android.provider.cts.ProviderTestUtils.containsId;
import static android.provider.cts.ProviderTestUtils.resolveVolumeName;
+import static android.provider.cts.media.MediaStoreTest.TAG;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -35,10 +35,11 @@
import android.net.Uri;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
-import android.platform.test.annotations.Presubmit;
import android.provider.MediaStore;
import android.provider.MediaStore.Files.FileColumns;
import android.provider.MediaStore.MediaColumns;
+import android.provider.cts.ProviderTestUtils;
+import android.provider.cts.R;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
@@ -53,7 +54,6 @@
import java.io.File;
import java.io.IOException;
-@Presubmit
@RunWith(Parameterized.class)
public class MediaStore_FilesTest {
private Context mContext;
@@ -158,6 +158,9 @@
} finally {
cursor.close();
}
+
+ assertEquals(ContentUris.withAppendedId(MediaStore.Files.getContentUri(mVolumeName), 42),
+ MediaStore.Files.getContentUri(mVolumeName, 42));
}
@Test
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Images_MediaTest.java
similarity index 97%
rename from tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java
rename to tests/tests/provider/src/android/provider/cts/media/MediaStore_Images_MediaTest.java
index 799e3ac..223a89f 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Images_MediaTest.java
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package android.provider.cts;
+package android.provider.cts.media;
-import static android.provider.cts.MediaStoreTest.TAG;
+import static android.provider.cts.media.MediaStoreTest.TAG;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -27,6 +27,7 @@
import android.app.AppOpsManager;
import android.content.ContentResolver;
+import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -38,19 +39,18 @@
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.storage.StorageManager;
-import android.platform.test.annotations.Presubmit;
import android.provider.MediaStore;
import android.provider.MediaStore.Images.ImageColumns;
import android.provider.MediaStore.Images.Media;
-import android.provider.cts.MediaStoreUtils.PendingParams;
-import android.provider.cts.MediaStoreUtils.PendingSession;
+import android.provider.cts.ProviderTestUtils;
+import android.provider.cts.R;
+import android.provider.cts.media.MediaStoreUtils.PendingParams;
+import android.provider.cts.media.MediaStoreUtils.PendingSession;
import android.util.Log;
import android.util.Size;
import androidx.test.InstrumentationRegistry;
-import com.android.compatibility.common.util.FileUtils;
-
import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
@@ -65,7 +65,6 @@
import java.io.InputStream;
import java.io.OutputStream;
-@Presubmit
@RunWith(Parameterized.class)
public class MediaStore_Images_MediaTest {
private static final String MIME_TYPE_JPEG = "image/jpeg";
@@ -203,7 +202,6 @@
assertEquals(src.getHeight(), result.getHeight());
}
- @Presubmit
@Test
public void testGetContentUri() {
Cursor c = null;
@@ -213,6 +211,9 @@
assertNotNull(c = mContentResolver.query(Media.getContentUri(mVolumeName), null, null, null,
null));
c.close();
+
+ assertEquals(ContentUris.withAppendedId(Media.getContentUri(mVolumeName), 42),
+ Media.getContentUri(mVolumeName, 42));
}
private void cleanExternalMediaFile(String path) {
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Images_ThumbnailsTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Images_ThumbnailsTest.java
similarity index 98%
rename from tests/tests/provider/src/android/provider/cts/MediaStore_Images_ThumbnailsTest.java
rename to tests/tests/provider/src/android/provider/cts/media/MediaStore_Images_ThumbnailsTest.java
index 2d28405..5d0199b 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Images_ThumbnailsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Images_ThumbnailsTest.java
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package android.provider.cts;
+package android.provider.cts.media;
-import static android.provider.cts.MediaStoreTest.TAG;
import static android.provider.cts.ProviderTestUtils.assertExists;
import static android.provider.cts.ProviderTestUtils.assertNotExists;
+import static android.provider.cts.media.MediaStoreTest.TAG;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -41,13 +41,14 @@
import android.graphics.ImageDecoder;
import android.net.Uri;
import android.os.Environment;
-import android.platform.test.annotations.Presubmit;
import android.provider.MediaStore;
import android.provider.MediaStore.Images.Media;
import android.provider.MediaStore.Images.Thumbnails;
import android.provider.MediaStore.MediaColumns;
-import android.provider.cts.MediaStoreUtils.PendingParams;
-import android.provider.cts.MediaStoreUtils.PendingSession;
+import android.provider.cts.ProviderTestUtils;
+import android.provider.cts.R;
+import android.provider.cts.media.MediaStoreUtils.PendingParams;
+import android.provider.cts.media.MediaStoreUtils.PendingSession;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Size;
@@ -69,7 +70,6 @@
import java.io.OutputStream;
import java.util.ArrayList;
-@Presubmit
@RunWith(Parameterized.class)
public class MediaStore_Images_ThumbnailsTest {
private ArrayList<Uri> mRowsAdded;
@@ -125,6 +125,7 @@
mBlue = ProviderTestUtils.stageMedia(R.raw.scenery, mExternalImages);
mRowsAdded.add(mRed);
mRowsAdded.add(mBlue);
+ MediaStore.waitForIdle(mContext);
}
public static void assertMostlyEquals(long expected, long actual, long delta) {
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_VideoTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_VideoTest.java
similarity index 93%
rename from tests/tests/provider/src/android/provider/cts/MediaStore_VideoTest.java
rename to tests/tests/provider/src/android/provider/cts/media/MediaStore_VideoTest.java
index 98242b8..202c3ea 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_VideoTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_VideoTest.java
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package android.provider.cts;
+package android.provider.cts.media;
-import static android.provider.cts.MediaStoreTest.TAG;
+import static android.provider.cts.media.MediaStoreTest.TAG;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -26,10 +26,11 @@
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
-import android.platform.test.annotations.Presubmit;
import android.provider.MediaStore;
import android.provider.MediaStore.Video;
import android.provider.MediaStore.Video.VideoColumns;
+import android.provider.cts.ProviderTestUtils;
+import android.provider.cts.R;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
@@ -43,7 +44,6 @@
import java.io.File;
-@Presubmit
@RunWith(Parameterized.class)
public class MediaStore_VideoTest {
private Context mContext;
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Video_MediaTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Video_MediaTest.java
similarity index 92%
rename from tests/tests/provider/src/android/provider/cts/MediaStore_Video_MediaTest.java
rename to tests/tests/provider/src/android/provider/cts/media/MediaStore_Video_MediaTest.java
index 92ce7e0..915d5fa 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Video_MediaTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Video_MediaTest.java
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package android.provider.cts;
+package android.provider.cts.media;
-import static android.provider.cts.MediaStoreTest.TAG;
import static android.provider.cts.ProviderTestUtils.assertExists;
import static android.provider.cts.ProviderTestUtils.assertNotExists;
+import static android.provider.cts.media.MediaStoreTest.TAG;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -29,28 +29,28 @@
import android.app.AppOpsManager;
import android.content.ContentResolver;
+import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.pm.PackageManager;
import android.database.Cursor;
-import android.media.MediaExtractor;
import android.media.MediaMetadataRetriever;
import android.net.Uri;
+import android.os.FileUtils;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.storage.StorageManager;
-import android.platform.test.annotations.Presubmit;
import android.provider.MediaStore;
import android.provider.MediaStore.Video.Media;
import android.provider.MediaStore.Video.VideoColumns;
-import android.provider.cts.MediaStoreUtils.PendingParams;
-import android.provider.cts.MediaStoreUtils.PendingSession;
+import android.provider.cts.ProviderTestUtils;
+import android.provider.cts.R;
+import android.provider.cts.media.MediaStoreUtils.PendingParams;
+import android.provider.cts.media.MediaStoreUtils.PendingSession;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
-import com.android.compatibility.common.util.FileUtils;
-
import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
@@ -66,7 +66,6 @@
import java.io.OutputStream;
import java.util.Arrays;
-@Presubmit
@RunWith(Parameterized.class)
public class MediaStore_Video_MediaTest {
private Context mContext;
@@ -100,6 +99,9 @@
assertNotNull(c = mContentResolver.query(Media.getContentUri(mVolumeName), null, null, null,
null));
c.close();
+
+ assertEquals(ContentUris.withAppendedId(Media.getContentUri(mVolumeName), 42),
+ Media.getContentUri(mVolumeName, 42));
}
private void cleanExternalMediaFile(String path) {
@@ -109,18 +111,14 @@
@Test
public void testStoreVideoMediaExternal() throws Exception {
- final String externalVideoPath = new File(ProviderTestUtils.stageDir(mVolumeName),
- "testvideo.3gp").getAbsolutePath();
- final String externalVideoPath2 = new File(ProviderTestUtils.stageDir(mVolumeName),
- "testvideo1.3gp").getAbsolutePath();
+ final File dir = ProviderTestUtils.stageDir(mVolumeName);
+ final File videoFile = ProviderTestUtils.stageFile(R.raw.testvideo,
+ new File(dir, "cts" + System.nanoTime() + ".mp4"));
- // clean up any potential left over entries from a previous aborted run
- cleanExternalMediaFile(externalVideoPath);
- cleanExternalMediaFile(externalVideoPath2);
+ final String externalVideoPath = videoFile.getAbsolutePath();
+ final long numBytes = videoFile.length();
- int numBytes = 1337;
- File videoFile = new File(externalVideoPath);
- FileUtils.createFile(videoFile, numBytes);
+ ProviderTestUtils.waitUntilExists(videoFile);
ContentValues values = new ContentValues();
values.put(Media.ALBUM, "cts");
@@ -169,7 +167,7 @@
assertEquals("176x144", c.getString(c.getColumnIndex(Media.RESOLUTION)));
assertEquals("cts, test", c.getString(c.getColumnIndex(Media.TAGS)));
assertEquals(externalVideoPath, c.getString(c.getColumnIndex(Media.DATA)));
- assertEquals("testvideo.3gp", c.getString(c.getColumnIndex(Media.DISPLAY_NAME)));
+ assertEquals(videoFile.getName(), c.getString(c.getColumnIndex(Media.DISPLAY_NAME)));
assertEquals("video/3gpp", c.getString(c.getColumnIndex(Media.MIME_TYPE)));
assertEquals("testvideo", c.getString(c.getColumnIndex(Media.TITLE)));
assertEquals(numBytes, c.getInt(c.getColumnIndex(Media.SIZE)));
@@ -224,7 +222,7 @@
try (PendingSession session = MediaStoreUtils.openPending(mContext, pendingUri)) {
try (InputStream in = mContext.getResources().openRawResource(R.raw.testvideo_meta);
OutputStream out = session.openOutputStream()) {
- android.os.FileUtils.copy(in, out);
+ FileUtils.copy(in, out);
}
publishUri = session.publish();
}
@@ -240,8 +238,10 @@
mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_LOCATION));
assertEquals("2", mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS));
}
- try (InputStream in = mContentResolver.openInputStream(publishUri)) {
- byte[] bytes = FileUtils.readInputStreamFully(in);
+ try (InputStream in = mContentResolver.openInputStream(publishUri);
+ ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+ FileUtils.copy(in, out);
+ byte[] bytes = out.toByteArray();
byte[] xmpBytes = Arrays.copyOfRange(bytes, 3269, 3269 + 13197);
String xmp = new String(xmpBytes);
assertTrue("Failed to read XMP longitude", xmp.contains("10,41.751000E"));
@@ -284,8 +284,10 @@
mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_LOCATION));
assertEquals("2", mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS));
}
- try (InputStream in = mContentResolver.openInputStream(publishUri)) {
- byte[] bytes = FileUtils.readInputStreamFully(in);
+ try (InputStream in = mContentResolver.openInputStream(publishUri);
+ ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+ FileUtils.copy(in, out);
+ byte[] bytes = out.toByteArray();
byte[] xmpBytes = Arrays.copyOfRange(bytes, 3269, 3269 + 13197);
String xmp = new String(xmpBytes);
assertFalse("Failed to redact XMP longitude", xmp.contains("10,41.751000E"));
@@ -310,7 +312,7 @@
try (PendingSession session = MediaStoreUtils.openPending(mContext, pendingUri)) {
try (InputStream in = mContext.getResources().openRawResource(R.raw.testvideo_meta);
OutputStream out = session.openOutputStream()) {
- android.os.FileUtils.copy(in, out);
+ FileUtils.copy(in, out);
}
publishUri = session.publish();
}
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Video_ThumbnailsTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Video_ThumbnailsTest.java
similarity index 98%
rename from tests/tests/provider/src/android/provider/cts/MediaStore_Video_ThumbnailsTest.java
rename to tests/tests/provider/src/android/provider/cts/media/MediaStore_Video_ThumbnailsTest.java
index 9d39c1c..09c45a0 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Video_ThumbnailsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Video_ThumbnailsTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.provider.cts;
+package android.provider.cts.media;
import static android.media.MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT;
import static android.media.MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH;
@@ -36,12 +36,13 @@
import android.media.MediaMetadataRetriever;
import android.net.Uri;
import android.os.FileUtils;
-import android.platform.test.annotations.Presubmit;
import android.provider.MediaStore;
import android.provider.MediaStore.Files;
import android.provider.MediaStore.Video.Media;
import android.provider.MediaStore.Video.Thumbnails;
import android.provider.MediaStore.Video.VideoColumns;
+import android.provider.cts.ProviderTestUtils;
+import android.provider.cts.R;
import android.util.Log;
import android.util.Size;
@@ -62,7 +63,6 @@
import java.io.InputStream;
import java.io.OutputStream;
-@Presubmit
@RunWith(Parameterized.class)
public class MediaStore_Video_ThumbnailsTest {
private static final String TAG = "MediaStore_Video_ThumbnailsTest";
diff --git a/tests/tests/provider/src/android/provider/cts/SettingsTest.java b/tests/tests/provider/src/android/provider/cts/settings/SettingsTest.java
similarity index 99%
rename from tests/tests/provider/src/android/provider/cts/SettingsTest.java
rename to tests/tests/provider/src/android/provider/cts/settings/SettingsTest.java
index 6d808e0..41281b7 100644
--- a/tests/tests/provider/src/android/provider/cts/SettingsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/settings/SettingsTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.provider.cts;
+package android.provider.cts.settings;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
diff --git a/tests/tests/provider/src/android/provider/cts/Settings_NameValueTableTest.java b/tests/tests/provider/src/android/provider/cts/settings/Settings_NameValueTableTest.java
similarity index 98%
rename from tests/tests/provider/src/android/provider/cts/Settings_NameValueTableTest.java
rename to tests/tests/provider/src/android/provider/cts/settings/Settings_NameValueTableTest.java
index c39b927..d2dbc52 100644
--- a/tests/tests/provider/src/android/provider/cts/Settings_NameValueTableTest.java
+++ b/tests/tests/provider/src/android/provider/cts/settings/Settings_NameValueTableTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.provider.cts;
+package android.provider.cts.settings;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
diff --git a/tests/tests/provider/src/android/provider/cts/Settings_SecureTest.java b/tests/tests/provider/src/android/provider/cts/settings/Settings_SecureTest.java
similarity index 99%
rename from tests/tests/provider/src/android/provider/cts/Settings_SecureTest.java
rename to tests/tests/provider/src/android/provider/cts/settings/Settings_SecureTest.java
index 6ba1126..fd84677 100644
--- a/tests/tests/provider/src/android/provider/cts/Settings_SecureTest.java
+++ b/tests/tests/provider/src/android/provider/cts/settings/Settings_SecureTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.provider.cts;
+package android.provider.cts.settings;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
diff --git a/tests/tests/provider/src/android/provider/cts/Settings_SettingNotFoundExceptionTest.java b/tests/tests/provider/src/android/provider/cts/settings/Settings_SettingNotFoundExceptionTest.java
similarity index 96%
rename from tests/tests/provider/src/android/provider/cts/Settings_SettingNotFoundExceptionTest.java
rename to tests/tests/provider/src/android/provider/cts/settings/Settings_SettingNotFoundExceptionTest.java
index fd8a8b6..3fdf062 100644
--- a/tests/tests/provider/src/android/provider/cts/Settings_SettingNotFoundExceptionTest.java
+++ b/tests/tests/provider/src/android/provider/cts/settings/Settings_SettingNotFoundExceptionTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.provider.cts;
+package android.provider.cts.settings;
import android.provider.Settings.SettingNotFoundException;
diff --git a/tests/tests/provider/src/android/provider/cts/Settings_SystemTest.java b/tests/tests/provider/src/android/provider/cts/settings/Settings_SystemTest.java
similarity index 98%
rename from tests/tests/provider/src/android/provider/cts/Settings_SystemTest.java
rename to tests/tests/provider/src/android/provider/cts/settings/Settings_SystemTest.java
index 583f337..338d9f8 100644
--- a/tests/tests/provider/src/android/provider/cts/Settings_SystemTest.java
+++ b/tests/tests/provider/src/android/provider/cts/settings/Settings_SystemTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.provider.cts;
+package android.provider.cts.settings;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
diff --git a/tests/tests/renderscript/OWNERS b/tests/tests/renderscript/OWNERS
new file mode 100644
index 0000000..d61905a
--- /dev/null
+++ b/tests/tests/renderscript/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 43047
+include platform/frameworks/rs:/OWNERS
diff --git a/tests/tests/role/Android.bp b/tests/tests/role/Android.bp
index ac8d967..dfb22f8 100644
--- a/tests/tests/role/Android.bp
+++ b/tests/tests/role/Android.bp
@@ -24,12 +24,17 @@
"androidx.test.rules",
"compatibility-device-util-axt",
"ctstestrunner-axt",
- "truth-prebuilt"
+ "truth-prebuilt",
],
test_suites: [
"cts",
"vts",
"general-tests",
- ]
+ ],
+
+ data: [
+ ":CtsRoleTestApp",
+ ":CtsRoleTestApp28",
+ ],
}
diff --git a/tests/tests/role/CtsRoleTestApp/Android.bp b/tests/tests/role/CtsRoleTestApp/Android.bp
index 74c1b76..ac9a6c1 100644
--- a/tests/tests/role/CtsRoleTestApp/Android.bp
+++ b/tests/tests/role/CtsRoleTestApp/Android.bp
@@ -19,10 +19,4 @@
srcs: [
"src/**/*.java"
],
-
- test_suites: [
- "cts",
- "vts",
- "general-tests",
- ]
}
diff --git a/tests/tests/role/CtsRoleTestApp28/Android.bp b/tests/tests/role/CtsRoleTestApp28/Android.bp
index 0d89f4e..0e82deb 100644
--- a/tests/tests/role/CtsRoleTestApp28/Android.bp
+++ b/tests/tests/role/CtsRoleTestApp28/Android.bp
@@ -19,10 +19,4 @@
srcs: [
"src/**/*.java"
],
-
- test_suites: [
- "cts",
- "vts",
- "general-tests",
- ]
}
diff --git a/tests/tests/role/src/android/app/role/cts/RoleManagerTest.java b/tests/tests/role/src/android/app/role/cts/RoleManagerTest.java
index 0da6988..604261e 100644
--- a/tests/tests/role/src/android/app/role/cts/RoleManagerTest.java
+++ b/tests/tests/role/src/android/app/role/cts/RoleManagerTest.java
@@ -19,6 +19,9 @@
import static com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity;
import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+import static com.android.compatibility.common.util.UiAutomatorUtils.waitFindObject;
+import static com.android.compatibility.common.util.UiAutomatorUtils.waitFindObjectOrNull;
+
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
@@ -37,11 +40,10 @@
import android.os.UserHandle;
import android.provider.Telephony;
import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.BySelector;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject2;
-import android.support.test.uiautomator.Until;
import android.telecom.TelecomManager;
-import android.util.Log;
import android.util.Pair;
import androidx.annotation.NonNull;
@@ -61,9 +63,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.io.ByteArrayOutputStream;
import java.io.IOException;
-import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
@@ -115,7 +115,6 @@
private static final Context sContext = InstrumentationRegistry.getTargetContext();
private static final PackageManager sPackageManager = sContext.getPackageManager();
private static final RoleManager sRoleManager = sContext.getSystemService(RoleManager.class);
- private static final UiDevice sUiDevice = UiDevice.getInstance(sInstrumentation);
@Rule
public ActivityTestRule<WaitForResultActivity> mActivityRule =
@@ -162,6 +161,11 @@
runShellCommand(sInstrumentation, "input keyevent KEYCODE_WAKEUP");
}
+ @Before
+ public void closeNotificationShade() {
+ sContext.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
+ }
+
@Test
public void requestRoleIntentHasPermissionControllerPackage() throws Exception {
Intent intent = sRoleManager.createRequestRoleIntent(ROLE_NAME);
@@ -313,13 +317,7 @@
private void respondToRoleRequest(boolean allow) throws InterruptedException, IOException {
if (allow) {
- UiObject2 item = sUiDevice.wait(Until.findObject(By.text(APP_PACKAGE_NAME)),
- TIMEOUT_MILLIS);
- if (item == null) {
- dumpWindowHierarchy();
- fail("Cannot find item to click");
- }
- item.click();
+ waitFindObject(By.text(APP_PACKAGE_NAME)).click();
}
Pair<Integer, Intent> result = clickButtonAndWaitForResult(allow);
int expectedResult = allow ? Activity.RESULT_OK : Activity.RESULT_CANCELED;
@@ -329,8 +327,10 @@
@Nullable
private UiObject2 findDontAskAgainCheck(boolean expected) {
- return sUiDevice.wait(Until.findObject(By.text("Don\u2019t ask again")), expected
- ? TIMEOUT_MILLIS : UNEXPECTED_TIMEOUT_MILLIS);
+ BySelector selector = By.text("Don\u2019t ask again");
+ return expected
+ ? waitFindObject(selector, TIMEOUT_MILLIS)
+ : waitFindObjectOrNull(selector, UNEXPECTED_TIMEOUT_MILLIS);
}
@Nullable
@@ -341,28 +341,10 @@
@NonNull
private Pair<Integer, Intent> clickButtonAndWaitForResult(boolean positive) throws IOException,
InterruptedException {
- String buttonId = positive ? "android:id/button1" : "android:id/button2";
- UiObject2 button = sUiDevice.wait(Until.findObject(By.res(buttonId)), TIMEOUT_MILLIS);
- if (button == null) {
- dumpWindowHierarchy();
- fail("Cannot find button to click");
- }
- button.click();
+ waitFindObject(By.res(positive ? "android:id/button1" : "android:id/button2")).click();
return waitForResult();
}
- private void dumpWindowHierarchy() throws InterruptedException, IOException {
- ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
- sUiDevice.dumpWindowHierarchy(outputStream);
- String windowHierarchy = outputStream.toString(StandardCharsets.UTF_8.name());
-
- Log.w(LOG_TAG, "Window hierarchy:");
- for (String line : windowHierarchy.split("\n")) {
- Thread.sleep(10);
- Log.w(LOG_TAG, line);
- }
- }
-
@NonNull
private Pair<Integer, Intent> waitForResult() throws InterruptedException {
return mActivityRule.getActivity().waitForActivityResult(TIMEOUT_MILLIS);
@@ -405,7 +387,6 @@
assertThat(result.first).isEqualTo(Activity.RESULT_CANCELED);
}
- @FlakyTest
@Test
public void targetSdk28AndChangeDefaultDialerAndAllowThenIsDefaultDialer() throws Exception {
sContext.startActivity(new Intent()
@@ -413,13 +394,13 @@
APP_28_CHANGE_DEFAULT_DIALER_ACTIVITY_NAME))
.putExtra(Intent.EXTRA_PACKAGE_NAME, APP_28_PACKAGE_NAME)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
- allowRoleRequestForApp28();
+ waitFindObject(By.text(APP_28_LABEL)).click();
+ waitFindObject(By.res("android:id/button1")).click();
TelecomManager telecomManager = sContext.getSystemService(TelecomManager.class);
TestUtils.waitUntil("App is not set as default dialer app", () -> Objects.equals(
telecomManager.getDefaultDialerPackage(), APP_28_PACKAGE_NAME));
}
- @FlakyTest
@Test
public void targetSdk28AndChangeDefaultSmsAndAllowThenIsDefaultSms() throws Exception {
sContext.startActivity(new Intent()
@@ -427,27 +408,12 @@
APP_28_CHANGE_DEFAULT_SMS_ACTIVITY_NAME))
.putExtra(Intent.EXTRA_PACKAGE_NAME, APP_28_PACKAGE_NAME)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
- allowRoleRequestForApp28();
+ waitFindObject(By.text(APP_28_LABEL)).click();
+ waitFindObject(By.res("android:id/button1")).click();
TestUtils.waitUntil("App is not set as default sms app", () -> Objects.equals(
Telephony.Sms.getDefaultSmsPackage(sContext), APP_28_PACKAGE_NAME));
}
- private void allowRoleRequestForApp28() throws InterruptedException, IOException {
- UiObject2 item = sUiDevice.wait(Until.findObject(By.text(APP_28_LABEL)), TIMEOUT_MILLIS);
- if (item == null) {
- dumpWindowHierarchy();
- fail("Cannot find item to click");
- }
- item.click();
- UiObject2 button = sUiDevice.wait(Until.findObject(By.res("android:id/button1")),
- TIMEOUT_MILLIS);
- if (button == null) {
- dumpWindowHierarchy();
- fail("Cannot find button to click");
- }
- button.click();
- }
-
@Test
public void roleIsAvailable() {
assertThat(sRoleManager.isRoleAvailable(ROLE_NAME)).isTrue();
diff --git a/tests/tests/secure_element/access_control/AccessControlApp1/Android.mk b/tests/tests/secure_element/access_control/AccessControlApp1/Android.mk
index d835fcd..feda0fd 100644
--- a/tests/tests/secure_element/access_control/AccessControlApp1/Android.mk
+++ b/tests/tests/secure_element/access_control/AccessControlApp1/Android.mk
@@ -28,7 +28,7 @@
ctstestrunner-axt \
compatibility-device-util-axt
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_SDK_VERSION := 29
+LOCAL_SDK_VERSION := current
LOCAL_JAVA_LIBRARIES += android.test.runner
LOCAL_JAVA_LIBRARIES += android.test.base
# Tag this module as a cts test artifact
diff --git a/tests/tests/secure_element/access_control/AccessControlApp1/src/android/omapi/accesscontrol1/cts/AccessControlTest.java b/tests/tests/secure_element/access_control/AccessControlApp1/src/android/omapi/accesscontrol1/cts/AccessControlTest.java
index 4eb7cb9..ad85ac2 100755
--- a/tests/tests/secure_element/access_control/AccessControlApp1/src/android/omapi/accesscontrol1/cts/AccessControlTest.java
+++ b/tests/tests/secure_element/access_control/AccessControlApp1/src/android/omapi/accesscontrol1/cts/AccessControlTest.java
@@ -162,10 +162,18 @@
return !lowRamDevice || (lowRamDevice && pm.hasSystemFeature("android.hardware.type.watch"));
}
+ private boolean supportOMAPIReaders() {
+ final PackageManager pm = InstrumentationRegistry.getContext().getPackageManager();
+ return (pm.hasSystemFeature(PackageManager.FEATURE_SE_OMAPI_UICC)
+ || pm.hasSystemFeature(PackageManager.FEATURE_SE_OMAPI_ESE)
+ || pm.hasSystemFeature(PackageManager.FEATURE_SE_OMAPI_SD));
+ }
+
@Before
public void setUp() throws Exception {
assumeTrue(PropertyUtil.getFirstApiLevel() > Build.VERSION_CODES.O_MR1);
assumeTrue(supportsHardware());
+ assumeTrue(supportOMAPIReaders());
seService = new SEService(InstrumentationRegistry.getContext(), new SynchronousExecutor(), mListener);
connectionTimer = new Timer();
connectionTimer.schedule(mTimerTask, SERVICE_CONNECTION_TIME_OUT);
diff --git a/tests/tests/secure_element/access_control/AccessControlApp2/Android.mk b/tests/tests/secure_element/access_control/AccessControlApp2/Android.mk
index a92fbfa..7c85540 100644
--- a/tests/tests/secure_element/access_control/AccessControlApp2/Android.mk
+++ b/tests/tests/secure_element/access_control/AccessControlApp2/Android.mk
@@ -28,7 +28,7 @@
ctstestrunner-axt \
compatibility-device-util-axt
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_SDK_VERSION := 29
+LOCAL_SDK_VERSION := current
LOCAL_JAVA_LIBRARIES += android.test.runner
LOCAL_JAVA_LIBRARIES += android.test.base
# Tag this module as a cts test artifact
diff --git a/tests/tests/secure_element/access_control/AccessControlApp2/src/android/omapi/accesscontrol2/cts/AccessControlTest.java b/tests/tests/secure_element/access_control/AccessControlApp2/src/android/omapi/accesscontrol2/cts/AccessControlTest.java
index e3eb557..8bce22b 100755
--- a/tests/tests/secure_element/access_control/AccessControlApp2/src/android/omapi/accesscontrol2/cts/AccessControlTest.java
+++ b/tests/tests/secure_element/access_control/AccessControlApp2/src/android/omapi/accesscontrol2/cts/AccessControlTest.java
@@ -161,10 +161,18 @@
return !lowRamDevice || (lowRamDevice && pm.hasSystemFeature("android.hardware.type.watch"));
}
+ private boolean supportOMAPIReaders() {
+ final PackageManager pm = InstrumentationRegistry.getContext().getPackageManager();
+ return (pm.hasSystemFeature(PackageManager.FEATURE_SE_OMAPI_UICC)
+ || pm.hasSystemFeature(PackageManager.FEATURE_SE_OMAPI_ESE)
+ || pm.hasSystemFeature(PackageManager.FEATURE_SE_OMAPI_SD));
+ }
+
@Before
public void setUp() throws Exception {
assumeTrue(PropertyUtil.getFirstApiLevel() > Build.VERSION_CODES.O_MR1);
assumeTrue(supportsHardware());
+ assumeTrue(supportOMAPIReaders());
seService = new SEService(InstrumentationRegistry.getContext(), new SynchronousExecutor(), mListener);
connectionTimer = new Timer();
connectionTimer.schedule(mTimerTask, SERVICE_CONNECTION_TIME_OUT);
diff --git a/tests/tests/secure_element/access_control/AccessControlApp3/Android.mk b/tests/tests/secure_element/access_control/AccessControlApp3/Android.mk
index 857e578..1520360 100644
--- a/tests/tests/secure_element/access_control/AccessControlApp3/Android.mk
+++ b/tests/tests/secure_element/access_control/AccessControlApp3/Android.mk
@@ -28,7 +28,7 @@
ctstestrunner-axt \
compatibility-device-util-axt
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_SDK_VERSION := 29
+LOCAL_SDK_VERSION := current
LOCAL_JAVA_LIBRARIES += android.test.runner
LOCAL_JAVA_LIBRARIES += android.test.base
# Tag this module as a cts test artifact
diff --git a/tests/tests/secure_element/access_control/AccessControlApp3/src/android/omapi/accesscontrol3/cts/AccessControlTest.java b/tests/tests/secure_element/access_control/AccessControlApp3/src/android/omapi/accesscontrol3/cts/AccessControlTest.java
index 7260c4f..e6dc5f0 100755
--- a/tests/tests/secure_element/access_control/AccessControlApp3/src/android/omapi/accesscontrol3/cts/AccessControlTest.java
+++ b/tests/tests/secure_element/access_control/AccessControlApp3/src/android/omapi/accesscontrol3/cts/AccessControlTest.java
@@ -171,10 +171,18 @@
return !lowRamDevice || (lowRamDevice && pm.hasSystemFeature("android.hardware.type.watch"));
}
+ private boolean supportOMAPIReaders() {
+ final PackageManager pm = InstrumentationRegistry.getContext().getPackageManager();
+ return (pm.hasSystemFeature(PackageManager.FEATURE_SE_OMAPI_UICC)
+ || pm.hasSystemFeature(PackageManager.FEATURE_SE_OMAPI_ESE)
+ || pm.hasSystemFeature(PackageManager.FEATURE_SE_OMAPI_SD));
+ }
+
@Before
public void setUp() throws Exception {
assumeTrue(PropertyUtil.getFirstApiLevel() > Build.VERSION_CODES.O_MR1);
assumeTrue(supportsHardware());
+ assumeTrue(supportOMAPIReaders());
seService = new SEService(InstrumentationRegistry.getContext(), new SynchronousExecutor(), mListener);
connectionTimer = new Timer();
connectionTimer.schedule(mTimerTask, SERVICE_CONNECTION_TIME_OUT);
diff --git a/tests/tests/secure_element/omapi/OWNERS b/tests/tests/secure_element/omapi/OWNERS
index fb599b2..853b7c3 100644
--- a/tests/tests/secure_element/omapi/OWNERS
+++ b/tests/tests/secure_element/omapi/OWNERS
@@ -1,3 +1,6 @@
# Bug component: 456592
-rmojumder@google.com
zachoverflow@google.com
+jackcwyu@google.com
+tokuda@google.com
+georgekgchang@google.com
+jimmychchang@google.com
diff --git a/tests/tests/secure_element/omapi/src/android/omapi/cts/OmapiTest.java b/tests/tests/secure_element/omapi/src/android/omapi/cts/OmapiTest.java
index d8c7b65..0734a9b 100644
--- a/tests/tests/secure_element/omapi/src/android/omapi/cts/OmapiTest.java
+++ b/tests/tests/secure_element/omapi/src/android/omapi/cts/OmapiTest.java
@@ -158,6 +158,29 @@
return !lowRamDevice || (lowRamDevice && pm.hasSystemFeature("android.hardware.type.watch"));
}
+ private boolean supportUICCReaders() {
+ final PackageManager pm = InstrumentationRegistry.getContext().getPackageManager();
+ return pm.hasSystemFeature(PackageManager.FEATURE_SE_OMAPI_UICC);
+ }
+
+ private boolean supportESEReaders() {
+ final PackageManager pm = InstrumentationRegistry.getContext().getPackageManager();
+ return pm.hasSystemFeature(PackageManager.FEATURE_SE_OMAPI_ESE);
+ }
+
+ private boolean supportSDReaders() {
+ final PackageManager pm = InstrumentationRegistry.getContext().getPackageManager();
+ return pm.hasSystemFeature(PackageManager.FEATURE_SE_OMAPI_SD);
+ }
+
+ private boolean supportOMAPIReaders() {
+ final PackageManager pm = InstrumentationRegistry.getContext().getPackageManager();
+ return (pm.hasSystemFeature(PackageManager.FEATURE_SE_OMAPI_UICC)
+ || pm.hasSystemFeature(PackageManager.FEATURE_SE_OMAPI_ESE)
+ || pm.hasSystemFeature(PackageManager.FEATURE_SE_OMAPI_SD));
+ }
+
+
private void assertGreaterOrEqual(long greater, long lesser) {
assertTrue("" + greater + " expected to be greater than or equal to " + lesser,
greater >= lesser);
@@ -167,6 +190,7 @@
public void setUp() throws Exception {
assumeTrue(PropertyUtil.getFirstApiLevel() > Build.VERSION_CODES.O_MR1);
assumeTrue(supportsHardware());
+ assumeTrue(supportOMAPIReaders());
seService = new SEService(InstrumentationRegistry.getContext(), new SynchronousExecutor(), mListener);
connectionTimer = new Timer();
connectionTimer.schedule(mTimerTask, SERVICE_CONNECTION_TIME_OUT);
@@ -206,6 +230,9 @@
try {
waitForConnection();
Reader[] readers = seService.getReaders();
+ ArrayList<Reader> uiccReaders = new ArrayList<Reader>();
+ ArrayList<Reader> eseReaders = new ArrayList<Reader>();
+ ArrayList<Reader> sdReaders = new ArrayList<Reader>();
for (Reader reader : readers) {
assertTrue(reader.isSecureElementPresent());
@@ -215,6 +242,34 @@
fail("Incorrect Reader name");
}
assertNotNull("getseService returned null", reader.getSEService());
+
+ if (reader.getName().startsWith(UICC_READER_PREFIX)) {
+ uiccReaders.add(reader);
+ }
+ if (reader.getName().startsWith(ESE_READER_PREFIX)) {
+ eseReaders.add(reader);
+ }
+ if (reader.getName().startsWith(SD_READER_PREFIX)) {
+ sdReaders.add(reader);
+ }
+ }
+
+ if (supportUICCReaders()) {
+ assertGreaterOrEqual(uiccReaders.size(), 1);
+ } else {
+ assertTrue(uiccReaders.size() == 0);
+ }
+
+ if (supportESEReaders()) {
+ assertGreaterOrEqual(eseReaders.size(), 1);
+ } else {
+ assertTrue(eseReaders.size() == 0);
+ }
+
+ if (supportSDReaders()) {
+ assertGreaterOrEqual(eseReaders.size(), 1);
+ } else {
+ assertTrue(sdReaders.size() == 0);
}
} catch (Exception e) {
fail("Unexpected Exception " + e);
diff --git a/tests/tests/security/Android.bp b/tests/tests/security/Android.bp
index b41f33d..eb35abb 100644
--- a/tests/tests/security/Android.bp
+++ b/tests/tests/security/Android.bp
@@ -22,6 +22,7 @@
"android-common",
"ctstestserver",
"ctstestrunner-axt",
+ "cts-install-lib",
"compatibility-device-util-axt",
"compatibility-common-util-devicesidelib",
"guava",
@@ -32,6 +33,9 @@
"org.apache.http.legacy",
"android.test.base.stubs",
],
+ java_resources: [
+ ":PackageInstallerTestApp",
+ ],
jni_libs: [
"libctssecurity_jni",
"libcts_jni",
@@ -58,4 +62,16 @@
"general-tests",
"sts",
],
+ certificate: ":security_cts_test_certificate",
}
+
+android_test_helper_app {
+ name: "PackageInstallerTestApp",
+ srcs: ["testdata/src/**/*.java"],
+ manifest: "testdata/packageinstallertestapp.xml",
+}
+
+android_app_certificate {
+ name: "security_cts_test_certificate",
+ certificate: "security_cts_test_cert",
+}
\ No newline at end of file
diff --git a/tests/tests/security/AndroidManifest.xml b/tests/tests/security/AndroidManifest.xml
index 54df055..0226ab7 100644
--- a/tests/tests/security/AndroidManifest.xml
+++ b/tests/tests/security/AndroidManifest.xml
@@ -58,6 +58,16 @@
<category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST"/>
</intent-filter>
</activity>
+
+ <receiver android:name="com.android.cts.install.lib.LocalIntentSender"
+ android:exported="true" />
+ <receiver android:name="android.security.cts.PackageVerificationsBroadcastReceiver"
+ android:permission="android.permission.BIND_PACKAGE_VERIFIER" >
+ <intent-filter>
+ <action android:name="android.intent.action.PACKAGE_NEEDS_VERIFICATION"/>
+ <data android:mimeType="application/vnd.android.package-archive" />
+ </intent-filter>
+ </receiver>
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/security/OWNERS b/tests/tests/security/OWNERS
new file mode 100644
index 0000000..c5cfd1e
--- /dev/null
+++ b/tests/tests/security/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 36824
+cbrubaker@google.com
+jeffv@google.com
+nnk@google.com
diff --git a/tests/tests/security/security_cts_test_cert.pk8 b/tests/tests/security/security_cts_test_cert.pk8
new file mode 100644
index 0000000..320e18d
--- /dev/null
+++ b/tests/tests/security/security_cts_test_cert.pk8
Binary files differ
diff --git a/tests/tests/security/security_cts_test_cert.x509.pem b/tests/tests/security/security_cts_test_cert.x509.pem
new file mode 100644
index 0000000..fd10e48
--- /dev/null
+++ b/tests/tests/security/security_cts_test_cert.x509.pem
@@ -0,0 +1,24 @@
+-----BEGIN CERTIFICATE-----
+MIIECzCCAvOgAwIBAgIUBCvKStYix70zHFiAKnzigdwl2z4wDQYJKoZIhvcNAQEL
+BQAwgZQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH
+DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy
+b2lkMRAwDgYDVQQDDAdBbmRyb2lkMSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFu
+ZHJvaWQuY29tMB4XDTE5MDgxOTIxMzcwM1oXDTQ3MDEwNDIxMzcwM1owgZQxCzAJ
+BgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFp
+biBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRyb2lkMRAwDgYD
+VQQDDAdBbmRyb2lkMSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFuZHJvaWQuY29t
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3rB8dYLa9mhYe9GICodU
+FVdjzh00SsfzpdMZ4UGIGF6VY/7D/TCdT5vjdXOdOQtsQnM/nZSgUPgBVX8RObm4
+/PRix68rdl2J58/LstcqdG6EaExb5hPUzHUuvOfd+p+IP+0SFEuRrWeGsmkzvdnx
+C2ZZjzEpE8UNDS8EtC2qULkF0cAGcHdHsjlktXRvn4FO+RN1GW6yxs8mOyCabNHA
+Se3AynYFa894Iamu99+RK51+3iyw+u4cVUeVPH3CzJ2Pu1PyqT+9l4gKUbw0gfC6
+D0/PNEfxe4RPrtn3Z8+ES8+jXPjBLLaMTpT9dFcP25kBwNLiV0MJdTOdZ3f30urt
+JQIDAQABo1MwUTAdBgNVHQ4EFgQUHUCJ6l5sn0M96xgIXBkY7dvm86MwHwYDVR0j
+BBgwFoAUHUCJ6l5sn0M96xgIXBkY7dvm86MwDwYDVR0TAQH/BAUwAwEB/zANBgkq
+hkiG9w0BAQsFAAOCAQEAAXpJFK3Lp6HuEeSkV60YUH7KKFf9FCfIlszxAcnPb7Pu
+8LX8pI59jYXUxp6ig2IRP/3jXWyf5mFyXcfPTES9Xi1yruV/hQ3KvvhrC2FSqF99
+AXPB31NiXxyw554iPGGLqRsxLb1aeRgofiGLG6CE+16RIPX54pDS6Y+MDJ7iaRsG
+L5qPP3JyQ5b3KBHFXE9GHJFEha2mrLThv1V6740ueErt2jkP95BnELmFwo+RH4ha
+sUOe79aEbq4ERKrmKf5vZ4GGS3vHQ6MSk53qeDFrla/05pZRfzUvwu88cLs0EjSI
+o36G2JpHHjd58pH7m4xeqcBX5eUKay/EfoYef4AopA==
+-----END CERTIFICATE-----
diff --git a/tests/tests/security/src/android/security/cts/PackageInstallerTest.java b/tests/tests/security/src/android/security/cts/PackageInstallerTest.java
new file mode 100644
index 0000000..b87b36b
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/PackageInstallerTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+import android.Manifest;
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.SecurityTest;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.cts.install.lib.Install;
+import com.android.cts.install.lib.TestApp;
+import com.android.cts.install.lib.Uninstall;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.concurrent.TimeUnit;
+
+@RunWith(JUnit4.class)
+@SecurityTest
+@AppModeFull
+public class PackageInstallerTest {
+
+ private static final String TEST_APP_NAME = "android.security.cts.packageinstallertestapp";
+
+ private static final TestApp TEST_APP = new TestApp(
+ "PackageInstallerTestApp", TEST_APP_NAME, 1, /*isApex*/ false,
+ "PackageInstallerTestApp.apk");
+
+ @Before
+ public void setUp() {
+ InstrumentationRegistry
+ .getInstrumentation()
+ .getUiAutomation()
+ .adoptShellPermissionIdentity(Manifest.permission.INSTALL_PACKAGES,
+ Manifest.permission.DELETE_PACKAGES,
+ Manifest.permission.PACKAGE_VERIFICATION_AGENT,
+ Manifest.permission.BIND_PACKAGE_VERIFIER);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ Uninstall.packages(TestApp.A);
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .dropShellPermissionIdentity();
+ }
+
+ @Test
+ public void verificationCanNotBeDisabledByInstaller() throws Exception {
+ Install.single(TEST_APP).addInstallFlags(
+ 0x00080000 /* PackageManager.INSTALL_DISABLE_VERIFICATION */).commit();
+ String packageName = PackageVerificationsBroadcastReceiver.packages.poll(30,
+ TimeUnit.SECONDS);
+ Assert.assertNotNull("Did not receive broadcast", packageName);
+ Assert.assertEquals(TEST_APP_NAME, packageName);
+ }
+}
diff --git a/tests/tests/security/src/android/security/cts/PackageSignatureTest.java b/tests/tests/security/src/android/security/cts/PackageSignatureTest.java
index 1f4eba1..6280ce8 100644
--- a/tests/tests/security/src/android/security/cts/PackageSignatureTest.java
+++ b/tests/tests/security/src/android/security/cts/PackageSignatureTest.java
@@ -133,6 +133,8 @@
// Test package to verify upgrades to privileged applications
"com.android.cts.priv.ctsshim",
"com.android.cts.ctsshim",
+ // Test APEX used in CTS tests.
+ "com.android.apex.cts.shim,",
// Oom Catcher package to prevent tests from ooming device.
"com.android.cts.oomcatcher"
diff --git a/tests/tests/security/src/android/security/cts/PackageVerificationsBroadcastReceiver.java b/tests/tests/security/src/android/security/cts/PackageVerificationsBroadcastReceiver.java
new file mode 100644
index 0000000..62d409d
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/PackageVerificationsBroadcastReceiver.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+import static android.content.pm.PackageManager.EXTRA_VERIFICATION_PACKAGE_NAME;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+public final class PackageVerificationsBroadcastReceiver extends BroadcastReceiver {
+
+ private static final String TAG = "PackageInstallerTest";
+
+ static final BlockingQueue<String> packages = new LinkedBlockingQueue<>();
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String packageName = intent.getStringExtra(EXTRA_VERIFICATION_PACKAGE_NAME);
+ Log.i(TAG, "Received PACKAGE_NEEDS_VERIFICATION broadcast for package " + packageName);
+ try {
+ packages.put(packageName);
+ } catch (InterruptedException e) {
+ throw new AssertionError(e);
+ }
+ }
+}
diff --git a/tests/tests/security/testdata/packageinstallertestapp.xml b/tests/tests/security/testdata/packageinstallertestapp.xml
new file mode 100644
index 0000000..7c35c11
--- /dev/null
+++ b/tests/tests/security/testdata/packageinstallertestapp.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.security.cts.packageinstallertestapp"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+
+ <package-verifier android:name="android.security.cts"
+ android:publicKey="MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3rB8dYLa9mhYe9GICodUFVdjzh00SsfzpdMZ4UGIGF6VY/7D/TCdT5vjdXOdOQtsQnM/nZSgUPgBVX8RObm4/PRix68rdl2J58/LstcqdG6EaExb5hPUzHUuvOfd+p+IP+0SFEuRrWeGsmkzvdnxC2ZZjzEpE8UNDS8EtC2qULkF0cAGcHdHsjlktXRvn4FO+RN1GW6yxs8mOyCabNHASe3AynYFa894Iamu99+RK51+3iyw+u4cVUeVPH3CzJ2Pu1PyqT+9l4gKUbw0gfC6D0/PNEfxe4RPrtn3Z8+ES8+jXPjBLLaMTpT9dFcP25kBwNLiV0MJdTOdZ3f30urtJQIDAQAB" />
+
+ <uses-sdk android:minSdkVersion="19" />
+
+ <application android:label="PackageInstallerTest Test App">
+ <activity android:name="android.security.cts.packageinstallertestapp.MainActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/tests/tests/packageinstaller/atomicinstall/testdata/apk/src/com/android/tests/atomicinstall/testapp/MainActivity.java b/tests/tests/security/testdata/src/android/security/cts/packageinstallertestapp/MainActivity.java
similarity index 93%
rename from tests/tests/packageinstaller/atomicinstall/testdata/apk/src/com/android/tests/atomicinstall/testapp/MainActivity.java
rename to tests/tests/security/testdata/src/android/security/cts/packageinstallertestapp/MainActivity.java
index 37276e2..aeb58c5 100644
--- a/tests/tests/packageinstaller/atomicinstall/testdata/apk/src/com/android/tests/atomicinstall/testapp/MainActivity.java
+++ b/tests/tests/security/testdata/src/android/security/cts/packageinstallertestapp/MainActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.tests.atomicinstall.testapp;
+package android.security.cts.packageinstallertestapp;
import android.app.Activity;
import android.os.Bundle;
@@ -26,3 +26,4 @@
super.onCreate(savedInstanceState);
}
}
+
diff --git a/tests/tests/slice/Android.bp b/tests/tests/slice/Android.bp
index bc25d6c..d3b8083 100644
--- a/tests/tests/slice/Android.bp
+++ b/tests/tests/slice/Android.bp
@@ -18,6 +18,7 @@
// Tag this module as a cts test artifact
test_suites: [
"cts",
+ "sts",
"vts",
"general-tests",
],
diff --git a/tests/tests/speech/src/android/speech/tts/cts/TextToSpeechServiceTest.java b/tests/tests/speech/src/android/speech/tts/cts/TextToSpeechServiceTest.java
index 8bca08c..addd14e 100644
--- a/tests/tests/speech/src/android/speech/tts/cts/TextToSpeechServiceTest.java
+++ b/tests/tests/speech/src/android/speech/tts/cts/TextToSpeechServiceTest.java
@@ -15,8 +15,10 @@
*/
package android.speech.tts.cts;
+import android.os.Bundle;
import android.os.ConditionVariable;
import android.os.Environment;
+import android.os.ParcelFileDescriptor;
import android.speech.tts.TextToSpeech;
import android.test.AndroidTestCase;
@@ -61,28 +63,54 @@
int result =
getTts().synthesizeToFile(
UTTERANCE, createParams("mocktofile"), sampleFile.getPath());
- assertEquals("synthesizeToFile() failed", TextToSpeech.SUCCESS, result);
-
- assertTrue("synthesizeToFile() completion timeout", mTts.waitForComplete("mocktofile"));
- assertTrue("synthesizeToFile() didn't produce a file", sampleFile.exists());
- assertTrue("synthesizeToFile() produced a non-sound file",
- TextToSpeechWrapper.isSoundFile(sampleFile.getPath()));
+ verifySynthesisFile(result, mTts, sampleFile);
} finally {
sampleFile.delete();
}
- mTts.verify("mocktofile");
+ verifySynthesisResults(mTts);
+ }
- final Map<String, Integer> chunksReceived = mTts.chunksReceived();
- final Map<String, List<Integer>> timePointsStart = mTts.timePointsStart();
- final Map<String, List<Integer>> timePointsEnd = mTts.timePointsEnd();
- final Map<String, List<Integer>> timePointsFrame = mTts.timePointsFrame();
- assertEquals(Integer.valueOf(10), chunksReceived.get("mocktofile"));
- // In the mock we set the start, end and frame to exactly these values for the time points.
- for (int i = 0; i < 10; i++) {
- assertEquals(Integer.valueOf(i * 5), timePointsStart.get("mocktofile").get(i));
- assertEquals(Integer.valueOf(i * 5 + 5), timePointsEnd.get("mocktofile").get(i));
- assertEquals(Integer.valueOf(i * 10), timePointsFrame.get("mocktofile").get(i));
+ public void testSynthesizeToFileWithFileObject() throws Exception {
+ File sampleFile = new File(getContext().getExternalFilesDir(null), SAMPLE_FILE_NAME);
+ try {
+ assertFalse(sampleFile.exists());
+
+ Bundle params = createParamsBundle("mocktofile");
+
+ int result =
+ getTts().synthesizeToFile(
+ UTTERANCE,
+ params,
+ sampleFile,
+ params.getString(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID));
+ verifySynthesisFile(result, mTts, sampleFile);
+ } finally {
+ sampleFile.delete();
}
+ verifySynthesisResults(mTts);
+ }
+
+ public void testSynthesizeToFileWithFileDescriptor() throws Exception {
+ File sampleFile = new File(getContext().getExternalFilesDir(null), SAMPLE_FILE_NAME);
+ try {
+ assertFalse(sampleFile.exists());
+
+ ParcelFileDescriptor fileDescriptor = ParcelFileDescriptor.open(sampleFile,
+ ParcelFileDescriptor.MODE_WRITE_ONLY
+ | ParcelFileDescriptor.MODE_CREATE
+ | ParcelFileDescriptor.MODE_TRUNCATE);
+
+ Bundle params = createParamsBundle("mocktofile");
+
+ int result =
+ getTts().synthesizeToFile(
+ UTTERANCE, params, fileDescriptor,
+ params.getString(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID));
+ verifySynthesisFile(result, mTts, sampleFile);
+ } finally {
+ sampleFile.delete();
+ }
+ verifySynthesisResults(mTts);
}
public void testMaxSpeechInputLength() {
@@ -184,6 +212,39 @@
return params;
}
+ private Bundle createParamsBundle(String utteranceId) {
+ Bundle bundle = new Bundle();
+ bundle.putString(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, utteranceId);
+ return bundle;
+ }
+
+ private void verifySynthesisFile(int result, TextToSpeechWrapper mTts, File file)
+ throws InterruptedException {
+
+ assertEquals("synthesizeToFile() failed", TextToSpeech.SUCCESS, result);
+
+ assertTrue("synthesizeToFile() completion timeout", mTts.waitForComplete("mocktofile"));
+ assertTrue("synthesizeToFile() didn't produce a file", file.exists());
+ assertTrue("synthesizeToFile() produced a non-sound file",
+ TextToSpeechWrapper.isSoundFile(file.getPath()));
+ }
+
+ private void verifySynthesisResults(TextToSpeechWrapper mTts) {
+ mTts.verify("mocktofile");
+
+ final Map<String, Integer> chunksReceived = mTts.chunksReceived();
+ final Map<String, List<Integer>> timePointsStart = mTts.timePointsStart();
+ final Map<String, List<Integer>> timePointsEnd = mTts.timePointsEnd();
+ final Map<String, List<Integer>> timePointsFrame = mTts.timePointsFrame();
+ assertEquals(Integer.valueOf(10), chunksReceived.get("mocktofile"));
+ // In the mock we set the start, end and frame to exactly these values for the time points.
+ for (int i = 0; i < 10; i++) {
+ assertEquals(Integer.valueOf(i * 5), timePointsStart.get("mocktofile").get(i));
+ assertEquals(Integer.valueOf(i * 5 + 5), timePointsEnd.get("mocktofile").get(i));
+ assertEquals(Integer.valueOf(i * 10), timePointsFrame.get("mocktofile").get(i));
+ }
+ }
+
private boolean waitForUtterance(String utteranceId) throws InterruptedException {
return mTts.waitForComplete(utteranceId);
}
diff --git a/tests/tests/syncmanager/OWNERS b/tests/tests/syncmanager/OWNERS
new file mode 100644
index 0000000..c475c6e
--- /dev/null
+++ b/tests/tests/syncmanager/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 197138
+omakoto@google.com
+yamasani@google.com
diff --git a/tests/tests/systemintents/OWNERS b/tests/tests/systemintents/OWNERS
new file mode 100644
index 0000000..b465964
--- /dev/null
+++ b/tests/tests/systemintents/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 25692
+ctate@google.com
\ No newline at end of file
diff --git a/tests/tests/telecom/AndroidTest.xml b/tests/tests/telecom/AndroidTest.xml
index c90c724..4dce7f5 100644
--- a/tests/tests/telecom/AndroidTest.xml
+++ b/tests/tests/telecom/AndroidTest.xml
@@ -18,6 +18,7 @@
<option name="config-descriptor:metadata" key="component" value="telecom" />
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.TokenRequirement">
<option name="token" value="sim-card" />
</target_preparer>
diff --git a/tests/tests/telecom/OWNERS b/tests/tests/telecom/OWNERS
new file mode 100644
index 0000000..f4921e0
--- /dev/null
+++ b/tests/tests/telecom/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 20868
+include ../telephony/OWNERS
diff --git a/tests/tests/telecom/src/android/telecom/cts/CallDetailsTest.java b/tests/tests/telecom/src/android/telecom/cts/CallDetailsTest.java
index 7ca476c..3a70017 100644
--- a/tests/tests/telecom/src/android/telecom/cts/CallDetailsTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/CallDetailsTest.java
@@ -22,10 +22,12 @@
import static org.junit.Assert.assertThat;
import android.content.Context;
+import android.database.Cursor;
import android.graphics.drawable.Icon;
import android.media.AudioManager;
import android.os.Bundle;
import android.net.Uri;
+import android.provider.CallLog;
import android.telecom.Call;
import android.telecom.Connection;
import android.telecom.ConnectionRequest;
@@ -38,6 +40,8 @@
import java.util.Arrays;
import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
/**
* Suites of tests that verifies the various Call details.
@@ -72,6 +76,7 @@
public static final int TEST_EXTRA_VALUE = 10;
public static final String TEST_EVENT = "com.test.event.TEST";
public static final Uri TEST_DEFLECT_URI = Uri.fromParts("tel", "+16505551212", null);
+ private static final int ASYNC_TIMEOUT = 10000;
private StatusHints mStatusHints;
private Bundle mExtras = new Bundle();
@@ -752,4 +757,29 @@
"Call should have extras key " + expectedKey
);
}
+
+ /**
+ * Tests whether the CallLogManager logs the features of a call(HD call and Wifi call)
+ * correctly.
+ */
+ public void testLogHdAndWifi() throws Exception {
+ if (!mShouldTestTelecom) {
+ return;
+ }
+
+ // Register content observer on call log and get latch
+ CountDownLatch callLogEntryLatch = getCallLogEntryLatch();
+ mCall.disconnect();
+
+ // Wait on the call log latch.
+ callLogEntryLatch.await(ASYNC_TIMEOUT, TimeUnit.MILLISECONDS);
+
+ // Verify the contents of the call log
+ Cursor callsCursor = mContext.getContentResolver().query(CallLog.Calls.CONTENT_URI, null,
+ "features", null, null);
+ int features = callsCursor.getColumnIndex(CallLog.Calls.FEATURES);
+ assertEquals(CallLog.Calls.FEATURES_HD_CALL,
+ features & CallLog.Calls.FEATURES_HD_CALL);
+ assertEquals(CallLog.Calls.FEATURES_WIFI, features & CallLog.Calls.FEATURES_WIFI);
+ }
}
diff --git a/tests/tests/telecom2/OWNERS b/tests/tests/telecom2/OWNERS
new file mode 100644
index 0000000..f4921e0
--- /dev/null
+++ b/tests/tests/telecom2/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 20868
+include ../telephony/OWNERS
diff --git a/tests/tests/telecom3/OWNERS b/tests/tests/telecom3/OWNERS
new file mode 100644
index 0000000..f4921e0
--- /dev/null
+++ b/tests/tests/telecom3/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 20868
+include ../telephony/OWNERS
diff --git a/tests/tests/telephony/OWNERS b/tests/tests/telephony/OWNERS
new file mode 100644
index 0000000..73fa25e
--- /dev/null
+++ b/tests/tests/telephony/OWNERS
@@ -0,0 +1,14 @@
+# Bug component: 20868
+amitmahajan@google.com
+fionaxu@google.com
+jackyu@google.com
+rgreenwalt@google.com
+refuhoo@google.com
+mpq@google.com
+jminjie@google.com
+shuoq@google.com
+hallliu@google.com
+tgunn@google.com
+breadley@google.com
+paulye@google.com
+nazaninb@google.com
diff --git a/tests/tests/telephony/current/OWNERS b/tests/tests/telephony/current/OWNERS
deleted file mode 100644
index 7c8e7d8..0000000
--- a/tests/tests/telephony/current/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-# Bug component: 20868
-rgreenwalt@google.com
-jackyu@google.com
-tgunn@google.com
-amitmahajan@google.com
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/MmsTest.java b/tests/tests/telephony/current/src/android/telephony/cts/MmsTest.java
index 7ca2334..60b0de2 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/MmsTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/MmsTest.java
@@ -49,14 +49,14 @@
import com.google.android.mms.pdu.SendConf;
import com.google.android.mms.pdu.SendReq;
+import org.junit.Before;
+import org.junit.Test;
+
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Random;
-import org.junit.Before;
-import org.junit.Test;
-
/**
* Test sending MMS using {@link android.telephony.SmsManager}.
*/
@@ -185,7 +185,7 @@
Log.i(TAG, "testSendMmsMessage");
// Prime the MmsService so that MMS config is loaded
final SmsManager smsManager = SmsManager.getDefault();
- smsManager.getAutoPersisting();
+ smsManager.getCarrierConfigValues();
// MMS config is loaded asynchronously. Wait a bit so it will be loaded.
try {
Thread.sleep(1000);
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/PhoneStateListenerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/PhoneStateListenerTest.java
index ca4b8e2..cddcba1 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/PhoneStateListenerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/PhoneStateListenerTest.java
@@ -30,6 +30,8 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
+import android.telephony.Annotation.RadioPowerState;
+import android.telephony.Annotation.SimActivationState;
import android.telephony.CellInfo;
import android.telephony.CellLocation;
import android.telephony.PhoneStateListener;
@@ -84,8 +86,8 @@
private boolean mOnRadioPowerStateChangedCalled;
private boolean mVoiceActivationStateChangedCalled;
private boolean mSrvccStateChangedCalled;
- @TelephonyManager.RadioPowerState private int mRadioPowerState;
- @TelephonyManager.SimActivationState private int mVoiceActivationState;
+ @RadioPowerState private int mRadioPowerState;
+ @SimActivationState private int mVoiceActivationState;
private PreciseDataConnectionState mPreciseDataConnectionState;
private PreciseCallState mPreciseCallState;
private SignalStrength mSignalStrength;
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/SmsManagerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/SmsManagerTest.java
index 26358f0..c81240d 100755
--- a/tests/tests/telephony/current/src/android/telephony/cts/SmsManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/SmsManagerTest.java
@@ -443,6 +443,9 @@
@Test
public void testContentProviderAccessRestriction() throws Exception {
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ return;
+ }
Uri dummySmsUri = null;
Context context = getInstrumentation().getContext();
ContentResolver contentResolver = context.getContentResolver();
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java
index 51c1563..febb89d 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java
@@ -134,21 +134,25 @@
}
/**
- * Sanity check that the device has a cellular network and a valid default data subId
- * when {@link PackageManager#FEATURE_TELEPHONY} support.
+ * Sanity check that both {@link PackageManager#FEATURE_TELEPHONY} and
+ * {@link NetworkCapabilities#TRANSPORT_CELLULAR} network must both be
+ * either defined or undefined; you can't cross the streams.
*/
@Test
public void testSanity() throws Exception {
- if (!isSupported()) return;
-
final boolean hasCellular = findCellularNetwork() != null;
- if (!hasCellular) {
+ if (isSupported() && !hasCellular) {
fail("Device claims to support " + PackageManager.FEATURE_TELEPHONY
+ " but has no active cellular network, which is required for validation");
+ } else if (!isSupported() && hasCellular) {
+ fail("Device has active cellular network, but claims to not support "
+ + PackageManager.FEATURE_TELEPHONY);
}
- if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
- fail("Device must have a valid default data subId for validation");
+ if (isSupported()) {
+ if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ fail("Device must have a valid default data subId for validation");
+ }
}
}
@@ -388,12 +392,12 @@
}
// Add into subscription group that doesn't exist. This should fail
- // with IllegalArgumentException.
+ // because we don't have MODIFY_PHONE_STATE or carrier privilege permission.
try {
ParcelUuid groupUuid = new ParcelUuid(UUID.randomUUID());
mSm.addSubscriptionsIntoGroup(subGroup, groupUuid);
fail();
- } catch (IllegalArgumentException expected) {
+ } catch (SecurityException expected) {
}
// Remove from subscription group with current sub Id. This should fail
@@ -449,6 +453,32 @@
}
@Test
+ public void testAddSubscriptionIntoNewGroupWithPermission() throws Exception {
+ if (!isSupported()) return;
+
+ // Set subscription group with current sub Id.
+ List<Integer> subGroup = new ArrayList();
+ subGroup.add(mSubId);
+ ParcelUuid uuid = new ParcelUuid(UUID.randomUUID());
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mSm,
+ (sm) -> sm.addSubscriptionsIntoGroup(subGroup, uuid));
+
+ // Getting subscriptions in group.
+ List<SubscriptionInfo> infoList = mSm.getSubscriptionsInGroup(uuid);
+ assertNotNull(infoList);
+ assertEquals(1, infoList.size());
+ assertEquals(uuid, infoList.get(0).getGroupUuid());
+
+ // Remove from subscription group with current sub Id.
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mSm,
+ (sm) -> sm.removeSubscriptionsFromGroup(subGroup, uuid));
+
+ infoList = mSm.getSubscriptionsInGroup(uuid);
+ assertNotNull(infoList);
+ assertTrue(infoList.isEmpty());
+ }
+
+ @Test
public void testSettingOpportunisticSubscription() throws Exception {
if (!isSupported()) return;
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
index 850c8a0..d736119 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
@@ -29,10 +29,13 @@
import android.Manifest.permission;
import android.app.UiAutomation;
import android.bluetooth.BluetoothAdapter;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.ServiceConnection;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.wifi.WifiInfo;
@@ -41,14 +44,17 @@
import android.os.Build;
import android.os.IBinder;
import android.os.Looper;
+import android.os.PersistableBundle;
import android.os.RemoteException;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.telephony.AccessNetworkConstants;
+import android.telephony.Annotation.RadioPowerState;
import android.telephony.AvailableNetworkInfo;
import android.telephony.CallAttributes;
import android.telephony.CallQuality;
+import android.telephony.CarrierConfigManager;
import android.telephony.CellLocation;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.PhoneStateListener;
@@ -61,6 +67,7 @@
import android.telephony.UiccSlotInfo;
import android.telephony.cts.locationaccessingapp.CtsLocationAccessService;
import android.telephony.cts.locationaccessingapp.ICtsLocationAccessControl;
+import android.telephony.data.ApnSetting;
import android.telephony.emergency.EmergencyNumber;
import android.text.TextUtils;
import android.util.Log;
@@ -70,11 +77,14 @@
import com.android.compatibility.common.util.ShellIdentityUtils;
import com.android.compatibility.common.util.TestThread;
+import com.android.internal.telephony.uicc.IccUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
@@ -83,12 +93,14 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
+
/**
* Build, install and run the tests by running the commands below:
* make cts -j64
@@ -104,6 +116,11 @@
private boolean mHasRadioPowerOff = false;
private ServiceState mServiceState;
private final Object mLock = new Object();
+
+ private CarrierConfigManager mCarrierConfigManager;
+ private String mSelfPackageName;
+ private String mSelfCertHash;
+
private static final int TOLERANCE = 1000;
private PhoneStateListener mListener;
private static ConnectivityManager mCm;
@@ -138,6 +155,13 @@
private static final int EMERGENCY_NUMBER_SOURCE_RIL_ECCLIST = 0;
private static final Set<Integer> EMERGENCY_NUMBER_SOURCE_SET;
+
+ private static final String PLMN_A = "123456";
+ private static final String PLMN_B = "78901";
+ private static final List<String> FPLMN_TEST = Arrays.asList(PLMN_A, PLMN_B);
+ private static final int MAX_FPLMN_NUM = 100;
+ private static final int MIN_FPLMN_NUM = 3;
+
static {
EMERGENCY_NUMBER_SOURCE_SET = new HashSet<Integer>();
EMERGENCY_NUMBER_SOURCE_SET.add(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING);
@@ -160,14 +184,52 @@
EMERGENCY_SERVICE_CATEGORY_SET.add(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AIEC);
}
+ private int mTestSub;
+ private TelephonyManagerTest.CarrierConfigReceiver mReceiver;
+
+ private static class CarrierConfigReceiver extends BroadcastReceiver {
+ private CountDownLatch mLatch = new CountDownLatch(1);
+ private final int mSubId;
+
+ CarrierConfigReceiver(int subId) {
+ mSubId = subId;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(intent.getAction())) {
+ int subId = intent.getIntExtra(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ if (mSubId == subId) {
+ mLatch.countDown();
+ }
+ }
+ }
+
+ void clearQueue() {
+ mLatch = new CountDownLatch(1);
+ }
+
+ void waitForCarrierConfigChanged() throws Exception {
+ mLatch.await(5000, TimeUnit.MILLISECONDS);
+ }
+ }
+
@Before
public void setUp() throws Exception {
- mTelephonyManager =
- (TelephonyManager) getContext().getSystemService(Context.TELEPHONY_SERVICE);
- mCm = (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
- mSubscriptionManager = (SubscriptionManager) getContext()
- .getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+ mCm = getContext().getSystemService(ConnectivityManager.class);
+ mSubscriptionManager = getContext().getSystemService(SubscriptionManager.class);
mPackageManager = getContext().getPackageManager();
+ mCarrierConfigManager = getContext().getSystemService(CarrierConfigManager.class);
+ mSelfPackageName = getContext().getPackageName();
+ mSelfCertHash = getCertHash(mSelfPackageName);
+ mTestSub = SubscriptionManager.getDefaultSubscriptionId();
+ mTelephonyManager = getContext().getSystemService(TelephonyManager.class)
+ .createForSubscriptionId(mTestSub);
+ mReceiver = new CarrierConfigReceiver(mTestSub);
+ IntentFilter filter = new IntentFilter(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+ // ACTION_CARRIER_CONFIG_CHANGED is sticky, so we will get a callback right away.
+ getContext().registerReceiver(mReceiver, filter);
}
@After
@@ -176,6 +238,76 @@
// unregister the listener
mTelephonyManager.listen(mListener, PhoneStateListener.LISTEN_NONE);
}
+ if (mReceiver != null) {
+ getContext().unregisterReceiver(mReceiver);
+ mReceiver = null;
+ }
+ }
+
+ private String getCertHash(String pkgName) throws Exception {
+ try {
+ PackageInfo pInfo = mPackageManager.getPackageInfo(pkgName,
+ PackageManager.GET_SIGNATURES
+ | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS);
+ MessageDigest md = MessageDigest.getInstance("SHA-1");
+ return IccUtils.bytesToHexString(md.digest(pInfo.signatures[0].toByteArray()));
+ } catch (PackageManager.NameNotFoundException ex) {
+ Log.e(TAG, pkgName + " not found", ex);
+ throw ex;
+ } catch (NoSuchAlgorithmException ex) {
+ Log.e(TAG, "Algorithm SHA1 is not found.");
+ throw ex;
+ }
+ }
+
+ /** Checks whether the cellular stack should be running on this device. */
+ private boolean hasCellular() {
+ return mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
+ && mTelephonyManager.getPhoneCount() > 0;
+ }
+
+ @Test
+ public void testHasCarrierPrivilegesViaCarrierConfigs() throws Exception {
+ if (!hasCellular()) return;
+ PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(mTestSub);
+
+ try {
+ assertNotNull("CarrierConfigManager#getConfigForSubId() returned null",
+ carrierConfig);
+ assertFalse("CarrierConfigManager#getConfigForSubId() returned empty bundle",
+ carrierConfig.isEmpty());
+
+ // purge the certs in carrierConfigs first
+ carrierConfig.putStringArray(
+ CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY, new String[]{});
+ overrideCarrierConfig(carrierConfig);
+ // verify we don't have privilege through carrierConfigs or Uicc
+ assertFalse(mTelephonyManager.hasCarrierPrivileges());
+
+ carrierConfig.putStringArray(
+ CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY,
+ new String[]{mSelfCertHash});
+
+ // verify we now have privilege after adding certificate to carrierConfigs
+ overrideCarrierConfig(carrierConfig);
+ assertTrue(mTelephonyManager.hasCarrierPrivileges());
+ } finally {
+ // purge the newly added certificate
+ carrierConfig.putStringArray(
+ CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY, new String[]{});
+ // carrierConfig.remove(CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY);
+ overrideCarrierConfig(carrierConfig);
+
+ // verify we no longer have privilege after removing certificate
+ assertFalse(mTelephonyManager.hasCarrierPrivileges());
+ }
+ }
+
+ private void overrideCarrierConfig(PersistableBundle bundle) throws Exception {
+ mReceiver.clearQueue();
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mCarrierConfigManager,
+ (cm) -> cm.overrideConfig(mTestSub, bundle));
+ mReceiver.waitForCarrierConfigChanged();
}
public static void grantLocationPermissions() {
@@ -235,7 +367,7 @@
mLock.wait(TOLERANCE);
assertTrue("Test register, mOnCellLocationChangedCalled should be true.",
- mOnCellLocationChangedCalled);
+ mOnCellLocationChangedCalled);
}
synchronized (mLock) {
@@ -244,7 +376,7 @@
mLock.wait(TOLERANCE);
assertTrue("Test register, mOnCellLocationChangedCalled should be true.",
- mOnCellLocationChangedCalled);
+ mOnCellLocationChangedCalled);
}
// unregister the listener
@@ -260,7 +392,7 @@
mLock.wait(TOLERANCE);
assertFalse("Test unregister, mOnCellLocationChangedCalled should be false.",
- mOnCellLocationChangedCalled);
+ mOnCellLocationChangedCalled);
}
}
@@ -325,9 +457,8 @@
mTelephonyManager.getPhoneCount();
mTelephonyManager.getDataEnabled();
mTelephonyManager.getNetworkSpecifier();
- mTelephonyManager.getNai();
- TelecomManager telecomManager = (TelecomManager) getContext()
- .getSystemService(Context.TELECOM_SERVICE);
+ ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, (tm) -> tm.getNai());
+ TelecomManager telecomManager = getContext().getSystemService(TelecomManager.class);
PhoneAccountHandle defaultAccount = telecomManager
.getDefaultOutgoingPhoneAccount(PhoneAccount.SCHEME_TEL);
mTelephonyManager.getVoicemailRingtoneUri(defaultAccount);
@@ -375,9 +506,9 @@
@Test
public void testServiceStateListeningWithoutPermissions() {
- if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) return;
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) return;
- withRevokedPermission(() -> {
+ withRevokedPermission(() -> {
ServiceState ss = (ServiceState) performLocationAccessCommand(
CtsLocationAccessService.COMMAND_GET_SERVICE_STATE_FROM_LISTENER);
assertServiceStateSanitization(ss, true);
@@ -661,9 +792,9 @@
String esnPattern = "[0-9a-fA-F]{8}";
String invalidPattern = "[0]{8}";
assertTrue("ESN hex device id " + deviceId + " does not match pattern " + esnPattern,
- Pattern.matches(esnPattern, deviceId));
+ Pattern.matches(esnPattern, deviceId));
assertFalse("ESN hex device id " + deviceId + " must not be a pseudo-ESN",
- "80".equals(deviceId.substring(0, 2)));
+ "80".equals(deviceId.substring(0, 2)));
assertFalse("ESN hex device id " + deviceId + "must not be a zero sequence",
Pattern.matches(invalidPattern, deviceId));
}
@@ -680,7 +811,7 @@
private void assertSerialNumber() {
String serial = ShellIdentityUtils.invokeStaticMethodWithShellPermissions(
- Build::getSerial);
+ Build::getSerial);
assertNotNull("Non-telephony devices must have a Build.getSerial() number.",
serial);
assertTrue("Hardware id must be no longer than 20 characters.",
@@ -697,8 +828,7 @@
/** @return mac address which requires the WiFi system to be enabled */
private String getWifiMacAddress() {
- WifiManager wifiManager = (WifiManager) getContext()
- .getSystemService(Context.WIFI_SERVICE);
+ WifiManager wifiManager = getContext().getSystemService(WifiManager.class);
boolean enabled = wifiManager.isWifiEnabled();
@@ -730,11 +860,19 @@
@Test
public void testGetNetworkCountryIso() {
- String countryCode = mTelephonyManager.getNetworkCountryIso();
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ String countryCode = mTelephonyManager.getNetworkCountryIso();
assertTrue("Country code '" + countryCode + "' did not match "
- + ISO_COUNTRY_CODE_PATTERN,
+ + ISO_COUNTRY_CODE_PATTERN,
Pattern.matches(ISO_COUNTRY_CODE_PATTERN, countryCode));
+
+ for (int i = 0; i < mTelephonyManager.getPhoneCount(); i++) {
+ countryCode = mTelephonyManager.getNetworkCountryIso(i);
+
+ assertTrue("Country code '" + countryCode + "' did not match "
+ + ISO_COUNTRY_CODE_PATTERN + " for slot " + i,
+ Pattern.matches(ISO_COUNTRY_CODE_PATTERN, countryCode));
+ }
} else {
// Non-telephony may still have the property defined if it has a SIM.
}
@@ -745,7 +883,7 @@
String countryCode = mTelephonyManager.getSimCountryIso();
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
assertTrue("Country code '" + countryCode + "' did not match "
- + ISO_COUNTRY_CODE_PATTERN,
+ + ISO_COUNTRY_CODE_PATTERN,
Pattern.matches(ISO_COUNTRY_CODE_PATTERN, countryCode));
} else {
// Non-telephony may still have the property defined if it has a SIM.
@@ -935,7 +1073,7 @@
mListener = new PhoneStateListener() {
@Override
public void onRadioPowerStateChanged(
- @TelephonyManager.RadioPowerState int state) {
+ @RadioPowerState int state) {
synchronized (mLock) {
if (state == TelephonyManager.RADIO_POWER_ON && mHasRadioPowerOff) {
mRadioRebootTriggered = true;
@@ -1089,7 +1227,7 @@
/**
* Basic test to ensure {@link NetworkRegistrationInfo#getAccessNetworkTechnology()} not
* throw any exception and returns valid result
- * @see TelephonyManager.NetworkType
+ * @see android.telephony.Annotation.NetworkType
*/
@Test
public void testNetworkRegistationStateGetAccessNetworkTechnology() {
@@ -1136,8 +1274,7 @@
return;
}
- SubscriptionManager sm = (SubscriptionManager) getContext()
- .getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+ SubscriptionManager sm = getContext().getSystemService(SubscriptionManager.class);
List<SubscriptionInfo> subInfos = sm.getActiveSubscriptionInfoList();
if (subInfos != null) {
@@ -1215,6 +1352,110 @@
}
/**
+ * Tests that the device properly sets and pads the contents of EF_FPLMN
+ */
+ @Test
+ public void testSetForbiddenPlmns() {
+ if (!supportSetFplmn()) {
+ return;
+ }
+ String[] originalFplmns = mTelephonyManager.getForbiddenPlmns();
+ try {
+ int numFplmnsSet = mTelephonyManager.setForbiddenPlmns(FPLMN_TEST);
+ String[] writtenFplmns = mTelephonyManager.getForbiddenPlmns();
+ assertEquals("Wrong return value for setFplmns with less than required fplmns: "
+ + numFplmnsSet, FPLMN_TEST.size(), numFplmnsSet);
+ assertEquals("Wrong Fplmns content written", FPLMN_TEST, Arrays.asList(writtenFplmns));
+ } finally {
+ // Restore
+ mTelephonyManager.setForbiddenPlmns(Arrays.asList(originalFplmns));
+ }
+ }
+
+ /**
+ * Tests that the device properly truncates the contents of EF_FPLMN when provided size
+ * is too big.
+ */
+ @Test
+ public void testSetForbiddenPlmnsTruncate() {
+ if (!supportSetFplmn()) {
+ return;
+ }
+ String[] originalFplmns = mTelephonyManager.getForbiddenPlmns();
+ try {
+ List<String> targetFplmns = new ArrayList<>();
+ for (int i = 0; i < MIN_FPLMN_NUM; i++) {
+ targetFplmns.add(PLMN_A);
+ }
+ for (int i = MIN_FPLMN_NUM; i < MAX_FPLMN_NUM; i++) {
+ targetFplmns.add(PLMN_B);
+ }
+ int numFplmnsSet = mTelephonyManager.setForbiddenPlmns(targetFplmns);
+ String[] writtenFplmns = mTelephonyManager.getForbiddenPlmns();
+ assertTrue("Wrong return value for setFplmns with overflowing fplmns: " + numFplmnsSet,
+ numFplmnsSet < MAX_FPLMN_NUM);
+ assertEquals("Number of Fplmns set does not equal number of Fplmns available",
+ numFplmnsSet, writtenFplmns.length);
+ assertEquals("Wrong Fplmns content written", targetFplmns.subList(0, numFplmnsSet),
+ Arrays.asList(writtenFplmns));
+ } finally {
+ // Restore
+ mTelephonyManager.setForbiddenPlmns(Arrays.asList(originalFplmns));
+ }
+ }
+
+ /**
+ * Tests that the device properly deletes the contents of EF_FPLMN
+ */
+ @Test
+ public void testSetForbiddenPlmnsDelete() {
+ if (!supportSetFplmn()) {
+ return;
+ }
+ String[] originalFplmns = mTelephonyManager.getForbiddenPlmns();
+ try {
+ // Support test for empty SIM
+ List<String> targetDummyFplmns = new ArrayList<>();
+ for (int i = 0; i < MIN_FPLMN_NUM; i++) {
+ targetDummyFplmns.add(PLMN_A);
+ }
+ mTelephonyManager.setForbiddenPlmns(targetDummyFplmns);
+ String[] writtenDummyFplmns = mTelephonyManager.getForbiddenPlmns();
+ assertEquals(targetDummyFplmns, Arrays.asList(writtenDummyFplmns));
+
+ List<String> targetFplmns = new ArrayList<>();
+ int numFplmnsSet = mTelephonyManager.setForbiddenPlmns(targetFplmns);
+ String[] writtenFplmns = mTelephonyManager.getForbiddenPlmns();
+ assertEquals("Wrong return value for setFplmns with empty list", 0, numFplmnsSet);
+ assertEquals("Wrong number of Fplmns written", 0, writtenFplmns.length);
+ // TODO wait for 10 minutes or so for the FPLMNS list to grow back
+ } finally {
+ // Restore
+ mTelephonyManager.setForbiddenPlmns(Arrays.asList(originalFplmns));
+ }
+ }
+
+
+ /**
+ * Tests that setForbiddenPlmns properly handles null input
+ */
+ @Test
+ public void testSetForbiddenPlmnsVoid() {
+ if (!supportSetFplmn()) {
+ return;
+ }
+ String[] originalFplmns = mTelephonyManager.getForbiddenPlmns();
+ try {
+ mTelephonyManager.setForbiddenPlmns(null);
+ fail("Expected IllegalArgumentException. Null input is not allowed");
+ } catch (IllegalArgumentException expected) {
+ } finally {
+ // Restore
+ mTelephonyManager.setForbiddenPlmns(Arrays.asList(originalFplmns));
+ }
+ }
+
+ /**
* Verify that TelephonyManager.getCardIdForDefaultEuicc returns a positive value or either
* UNINITIALIZED_CARD_ID or UNSUPPORTED_CARD_ID.
*/
@@ -1223,8 +1464,8 @@
int cardId = mTelephonyManager.getCardIdForDefaultEuicc();
assertTrue("Card ID for default EUICC is not a valid value",
cardId == TelephonyManager.UNSUPPORTED_CARD_ID
- || cardId == TelephonyManager.UNINITIALIZED_CARD_ID
- || cardId >= 0);
+ || cardId == TelephonyManager.UNINITIALIZED_CARD_ID
+ || cardId >= 0);
}
/**
@@ -1345,8 +1586,8 @@
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return;
}
- Map<Integer, List<EmergencyNumber>> emergencyNumberList
- = mTelephonyManager.getEmergencyNumberList();
+ Map<Integer, List<EmergencyNumber>> emergencyNumberList =
+ mTelephonyManager.getEmergencyNumberList();
assertFalse(emergencyNumberList == null);
@@ -1357,27 +1598,27 @@
// 112 and 911 should always be available
// Reference: 3gpp 22.101, Section 10 - Emergency Calls
assertTrue(checkIfEmergencyNumberListHasSpecificAddress(
- emergencyNumberList.get(defaultSubId), "911"));
+ emergencyNumberList.get(defaultSubId), "911"));
assertTrue(checkIfEmergencyNumberListHasSpecificAddress(
- emergencyNumberList.get(defaultSubId), "112"));
+ emergencyNumberList.get(defaultSubId), "112"));
// 000, 08, 110, 118, 119, and 999 should be always available when sim is absent
// Reference: 3gpp 22.101, Section 10 - Emergency Calls
if (mTelephonyManager.getPhoneCount() > 0
&& mSubscriptionManager.getSimStateForSlotIndex(0)
- == TelephonyManager.SIM_STATE_ABSENT) {
+ == TelephonyManager.SIM_STATE_ABSENT) {
assertTrue(checkIfEmergencyNumberListHasSpecificAddress(
- emergencyNumberList.get(defaultSubId), "000"));
+ emergencyNumberList.get(defaultSubId), "000"));
assertTrue(checkIfEmergencyNumberListHasSpecificAddress(
- emergencyNumberList.get(defaultSubId), "08"));
+ emergencyNumberList.get(defaultSubId), "08"));
assertTrue(checkIfEmergencyNumberListHasSpecificAddress(
- emergencyNumberList.get(defaultSubId), "110"));
+ emergencyNumberList.get(defaultSubId), "110"));
assertTrue(checkIfEmergencyNumberListHasSpecificAddress(
- emergencyNumberList.get(defaultSubId), "118"));
+ emergencyNumberList.get(defaultSubId), "118"));
assertTrue(checkIfEmergencyNumberListHasSpecificAddress(
- emergencyNumberList.get(defaultSubId), "119"));
+ emergencyNumberList.get(defaultSubId), "119"));
assertTrue(checkIfEmergencyNumberListHasSpecificAddress(
- emergencyNumberList.get(defaultSubId), "999"));
+ emergencyNumberList.get(defaultSubId), "999"));
}
}
@@ -1398,7 +1639,7 @@
// Reference: 3gpp 22.101, Section 10 - Emergency Calls
if (mTelephonyManager.getPhoneCount() > 0
&& mSubscriptionManager.getSimStateForSlotIndex(0)
- == TelephonyManager.SIM_STATE_ABSENT) {
+ == TelephonyManager.SIM_STATE_ABSENT) {
assertTrue(mTelephonyManager.isEmergencyNumber("000"));
assertTrue(mTelephonyManager.isEmergencyNumber("08"));
assertTrue(mTelephonyManager.isEmergencyNumber("110"));
@@ -1471,18 +1712,18 @@
fail("This test requires two SIM cards.");
}
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
- (tm) -> tm.setPreferredOpportunisticDataSubscription(
- SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, false,
- null, null));
+ (tm) -> tm.setPreferredOpportunisticDataSubscription(
+ SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, false,
+ null, null));
// wait for the data change to take effect
waitForMs(500);
int subId =
- ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
- (tm) -> tm.getPreferredOpportunisticDataSubscription());
+ ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+ (tm) -> tm.getPreferredOpportunisticDataSubscription());
assertThat(subId).isEqualTo(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
List<SubscriptionInfo> subscriptionInfoList =
- ShellIdentityUtils.invokeMethodWithShellPermissions(mSubscriptionManager,
- (tm) -> tm.getOpportunisticSubscriptions());
+ ShellIdentityUtils.invokeMethodWithShellPermissions(mSubscriptionManager,
+ (tm) -> tm.getOpportunisticSubscriptions());
Consumer<Integer> callbackSuccess = TelephonyManagerTest::assertSetOpportunisticSubSuccess;
Consumer<Integer> callbackFailure =
TelephonyManagerTest::assertSetOpportunisticInvalidParameter;
@@ -1504,7 +1745,7 @@
// wait for the data change to take effect
waitForMs(500);
subId = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
- (tm) -> tm.getPreferredOpportunisticDataSubscription());
+ (tm) -> tm.getPreferredOpportunisticDataSubscription());
assertThat(subId).isEqualTo(subscriptionInfoList.get(0).getSubscriptionId());
}
@@ -1515,7 +1756,7 @@
// wait for the data change to take effect
waitForMs(500);
subId = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
- (tm) -> tm.getPreferredOpportunisticDataSubscription());
+ (tm) -> tm.getPreferredOpportunisticDataSubscription());
assertThat(subId).isEqualTo(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
}
@@ -1598,8 +1839,8 @@
}
List<SubscriptionInfo> subscriptionInfoList =
- ShellIdentityUtils.invokeMethodWithShellPermissions(mSubscriptionManager,
- (tm) -> tm.getOpportunisticSubscriptions());
+ ShellIdentityUtils.invokeMethodWithShellPermissions(mSubscriptionManager,
+ (tm) -> tm.getOpportunisticSubscriptions());
List<String> mccMncs = new ArrayList<String>();
List<Integer> bands = new ArrayList<Integer>();
List<AvailableNetworkInfo> availableNetworkInfos = new ArrayList<AvailableNetworkInfo>();
@@ -1609,7 +1850,7 @@
TelephonyManagerTest::assertUpdateAvailableNetworkInvalidArguments;
if (subscriptionInfoList == null || subscriptionInfoList.size() == 0
|| !mSubscriptionManager.isActiveSubscriptionId(
- subscriptionInfoList.get(0).getSubscriptionId())) {
+ subscriptionInfoList.get(0).getSubscriptionId())) {
AvailableNetworkInfo availableNetworkInfo = new AvailableNetworkInfo(randomSubId,
AvailableNetworkInfo.PRIORITY_HIGH, mccMncs, bands);
availableNetworkInfos.add(availableNetworkInfo);
@@ -1629,15 +1870,15 @@
AvailableNetworkInfo.PRIORITY_HIGH, mccMncs, bands);
availableNetworkInfos.add(availableNetworkInfo);
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
- (tm) -> tm.updateAvailableNetworks(availableNetworkInfos,
- AsyncTask.SERIAL_EXECUTOR, callbackSuccess));
+ (tm) -> tm.updateAvailableNetworks(availableNetworkInfos,
+ AsyncTask.SERIAL_EXECUTOR, callbackSuccess));
// wait for the data change to take effect
waitForMs(500);
// clear all the operations at the end of test.
availableNetworkInfos.clear();
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
- (tm) -> tm.updateAvailableNetworks(availableNetworkInfos,
- AsyncTask.SERIAL_EXECUTOR, callbackSuccess));
+ (tm) -> tm.updateAvailableNetworks(availableNetworkInfos,
+ AsyncTask.SERIAL_EXECUTOR, callbackSuccess));
}
}
@@ -1732,6 +1973,28 @@
}
}
+ @Test
+ public void testIsDataEnabledForApn() {
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ return;
+ }
+ // verify SecurityException
+ try {
+ mTelephonyManager.isDataEnabledForApn(ApnSetting.TYPE_MMS);
+ fail("testIsDataEnabledForApn: Expected SecurityException on isDataEnabledForApn");
+ } catch (SecurityException se) {
+ // expected
+ }
+
+ // test with permission
+ try {
+ ShellIdentityUtils.invokeMethodWithShellPermissions(
+ mTelephonyManager, (tm) -> tm.isDataEnabledForApn(ApnSetting.TYPE_MMS));
+ } catch (SecurityException se) {
+ fail("testIsDataEnabledForApn: SecurityException not expected");
+ }
+ }
+
/**
* Validate Emergency Number address that only contains the dialable character.
*
@@ -1862,8 +2125,8 @@
boolean isInAnyCategory = false;
for (int possibleCategoryValue = EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED;
possibleCategoryValue <= (EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE
- | EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AMBULANCE
- | EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE
+ | EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AMBULANCE
+ | EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE
| EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD
| EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE
| EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MIEC
@@ -1929,4 +2192,17 @@
Log.d(TAG, "InterruptedException while waiting: " + e);
}
}
+
+ /**
+ * Verify that the phone is supporting the action of setForbiddenPlmn.
+ *
+ * @return whether to proceed the test
+ */
+ private boolean supportSetFplmn() {
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ return false;
+ }
+ return mTelephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM;
+ }
}
+
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyRegistryManagerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyRegistryManagerTest.java
new file mode 100644
index 0000000..a6a2c4f
--- /dev/null
+++ b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyRegistryManagerTest.java
@@ -0,0 +1,35 @@
+package android.telephony.cts;
+
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.os.telephony.TelephonyRegistryManager;
+import androidx.test.InstrumentationRegistry;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Test TelephonyRegistryManagerTest APIs.
+ */
+public class TelephonyRegistryManagerTest {
+ private TelephonyRegistryManager mTelephonyRegistryMgr;
+
+ @Before
+ public void setUp() throws Exception {
+ mTelephonyRegistryMgr = (TelephonyRegistryManager) InstrumentationRegistry.getContext()
+ .getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
+ }
+
+ /**
+ * expect security exception as there is no carrier privilege permission.
+ */
+ @Test
+ public void testNotifyCarrierNetworkChange() {
+ try {
+ mTelephonyRegistryMgr.notifyCarrierNetworkChange(true);
+ fail("Expected SecurityException for notifyCarrierNetworkChange");
+ } catch (SecurityException ex) {
+ /* Expected */
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/tests/telephony2/OWNERS b/tests/tests/telephony2/OWNERS
index 7c8e7d8..f4921e0 100644
--- a/tests/tests/telephony2/OWNERS
+++ b/tests/tests/telephony2/OWNERS
@@ -1,5 +1,2 @@
# Bug component: 20868
-rgreenwalt@google.com
-jackyu@google.com
-tgunn@google.com
-amitmahajan@google.com
+include ../telephony/OWNERS
diff --git a/tests/tests/telephony3/OWNERS b/tests/tests/telephony3/OWNERS
new file mode 100644
index 0000000..f4921e0
--- /dev/null
+++ b/tests/tests/telephony3/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 20868
+include ../telephony/OWNERS
diff --git a/tests/tests/telephony3/src/android/telephony3/cts/TelephonyManagerTest.java b/tests/tests/telephony3/src/android/telephony3/cts/TelephonyManagerTest.java
index cd960b8..57f2e9b 100644
--- a/tests/tests/telephony3/src/android/telephony3/cts/TelephonyManagerTest.java
+++ b/tests/tests/telephony3/src/android/telephony3/cts/TelephonyManagerTest.java
@@ -23,7 +23,6 @@
import android.content.Context;
import android.os.Build;
import android.telephony.TelephonyManager;
-import android.util.Log;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
@@ -76,6 +75,10 @@
"An app targeting pre-Q with the READ_PHONE_STATE permission granted must "
+ "receive null when invoking getSimSerialNumber",
mTelephonyManager.getSimSerialNumber());
+ assertNull(
+ "An app targeting pre-Q with the READ_PHONE_STATE permission granted must "
+ + "receive null when invoking getNai",
+ mTelephonyManager.getNai());
// Since Build.getSerial is not documented to return null in previous releases this test
// verifies that the Build.UNKNOWN value is returned when the caller does not have
// permission to access the device identifier.
diff --git a/tests/tests/telephony4/OWNERS b/tests/tests/telephony4/OWNERS
index 5617896..3a905dd 100644
--- a/tests/tests/telephony4/OWNERS
+++ b/tests/tests/telephony4/OWNERS
@@ -1,2 +1,4 @@
# Bug component: 20868
-yinxu@google.com
\ No newline at end of file
+include ../telephony/OWNERS
+
+yinxu@google.com
diff --git a/tests/tests/telephonyprovider/OWNERS b/tests/tests/telephonyprovider/OWNERS
index 92458db..7f7694d 100644
--- a/tests/tests/telephonyprovider/OWNERS
+++ b/tests/tests/telephonyprovider/OWNERS
@@ -1,12 +1,3 @@
-amitmahajan@google.com
-fionaxu@google.com
-jackyu@google.com
-rgreenwalt@google.com
-refuhoo@google.com
-mpq@google.com
-jminjie@google.com
-shuoq@google.com
-hallliu@google.com
-tgunn@google.com
-breadley@google.com
-nazaninb@google.com
+# Bug component: 450841
+include ../telephony/OWNERS
+lelandmiller@google.com
diff --git a/tests/tests/telephonyprovider/src/android/telephonyprovider/cts/TelephonyProviderTest.java b/tests/tests/telephonyprovider/src/android/telephonyprovider/cts/TelephonyProviderTest.java
index d54260b..7933791 100644
--- a/tests/tests/telephonyprovider/src/android/telephonyprovider/cts/TelephonyProviderTest.java
+++ b/tests/tests/telephonyprovider/src/android/telephonyprovider/cts/TelephonyProviderTest.java
@@ -18,7 +18,6 @@
import android.content.ContentResolver;
import android.database.Cursor;
-import android.net.Uri;
import android.provider.Telephony.Carriers;
import android.test.InstrumentationTestCase;
@@ -42,97 +41,18 @@
// In JB MR1 access to the TelephonyProvider's Carriers table was clamped down and would
// throw a SecurityException when queried. That was fixed in JB MR2. Verify that 3rd parties
// can access the APN info the carriers table, after JB MR1.
+
+ // However, in R, a security bug was discovered that let apps read the password by querying
+ // multiple times and matching passwords against a regex in the query. Due to this hole, we're
+ // locking down the API and no longer allowing the exception. Accordingly, the behavior of this
+ // test is now reversed and we expect a SecurityException to be thrown.
public void testAccessToApns() {
try {
String selection = Carriers.CURRENT + " IS NOT NULL";
String[] selectionArgs = null;
Cursor cursor = mContentResolver.query(Carriers.CONTENT_URI,
APN_PROJECTION, selection, selectionArgs, null);
- } catch (SecurityException e) {
- fail("No access to current APN");
- }
- }
-
- public void testNoAccessToPassword() {
- try {
- String selection = Carriers.CURRENT + " IS NOT NULL AND "
- + Carriers.PASSWORD + " IS NOT NULL";
- String[] selectionArgs = null;
- Cursor cursor = mContentResolver.query(Carriers.CONTENT_URI,
- APN_PROJECTION, selection, selectionArgs, null);
- fail("Expected SecurityException");
- } catch (SecurityException e) {
- // expected
- }
- }
-
- public void testNoAccessToPasswordThruSort() {
- try {
- String selection = Carriers.CURRENT + " IS NOT NULL";
- String[] selectionArgs = null;
- String sort = "LIMIT CASE WHEN ((SELECT COUNT(*) FROM carriers WHERE"
- + " password LIKE 'a%') > 0) THEN 1 ELSE 0 END";
- Cursor cursor = mContentResolver.query(Carriers.CONTENT_URI,
- APN_PROJECTION, selection, selectionArgs, sort);
- fail("Expected SecurityException");
- } catch (SecurityException e) {
- // expected
- }
- }
-
- public void testNoAccessToPasswordThruMixedCase() {
- try {
- String selection = Carriers.CURRENT + " IS NOT NULL";
- String[] selectionArgs = null;
- String sort = "LIMIT CASE WHEN ((SELECT COUNT(*) FROM carriers WHERE"
- + " PaSsWoRd LIKE 'a%') > 0) THEN 1 ELSE 0 END";
- Cursor cursor = mContentResolver.query(Carriers.CONTENT_URI,
- APN_PROJECTION, selection, selectionArgs, sort);
- fail("Expected SecurityException");
- } catch (SecurityException e) {
- // expected
- }
- }
-
- public void testNoAccessToUser() {
- try {
- String selection = Carriers.CURRENT + " IS NOT NULL AND "
- + Carriers.USER + " IS NOT NULL";
- String[] selectionArgs = null;
- String sort = "LIMIT CASE WHEN ((SELECT COUNT(*) FROM carriers WHERE"
- + " user LIKE 'a%') > 0) THEN 1 ELSE 0 END";
- Cursor cursor = mContentResolver.query(Carriers.CONTENT_URI,
- APN_PROJECTION, selection, selectionArgs, sort);
- fail("Expected SecurityException");
- } catch (SecurityException e) {
- // expected
- }
- }
-
- public void testNoAccessViaSubqueries() {
- try {
- String selection = Carriers.CURRENT + " IS NOT NULL";
- String[] selectionArgs = null;
- String sort = "LIMIT CASE WHEN ((SELECT COUNT(*) FROM carriers WHERE"
- + " mcc LIKE 'a%') > 0) THEN 1 ELSE 0 END";
- Cursor cursor = mContentResolver.query(Carriers.CONTENT_URI,
- APN_PROJECTION, selection, selectionArgs, sort);
- fail("Expected SecurityException");
- } catch (SecurityException e) {
- // expected
- }
- }
-
- public void testNoAccessToUserWithDifferentUri() {
- try {
- String selection = Carriers.CURRENT + " IS NOT NULL AND "
- + Carriers.USER + " IS NOT NULL";
- String[] selectionArgs = null;
- String sort = "LIMIT CASE WHEN ((SELECT COUNT(*) FROM carriers WHERE"
- + " user LIKE 'a%') > 0) THEN 1 ELSE 0 END";
- Cursor cursor = mContentResolver.query(Uri.parse("content://telephony/siminfo"),
- APN_PROJECTION, selection, selectionArgs, sort);
- fail("Expected SecurityException");
+ fail("No SecurityException thrown");
} catch (SecurityException e) {
// expected
}
diff --git a/tests/tests/text/src/android/text/cts/BoringLayoutTest.java b/tests/tests/text/src/android/text/cts/BoringLayoutTest.java
index 5f17b32..cb62639 100644
--- a/tests/tests/text/src/android/text/cts/BoringLayoutTest.java
+++ b/tests/tests/text/src/android/text/cts/BoringLayoutTest.java
@@ -20,6 +20,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
@@ -264,6 +265,17 @@
}
@Test
+ public void testIsBoringForEmptyString() {
+ Metrics metrics = new Metrics();
+ assertNotNull(BoringLayout.isBoring("", new TextPaint(), metrics));
+
+ // The default font Roboto has non-zero ascent/descent values. If metrics returns zeros, it
+ // means failed to retrieve the font metrics.
+ assertNotEquals(0, metrics.ascent);
+ assertNotEquals(0, metrics.descent);
+ }
+
+ @Test
public void testIsBoring_resetsFontMetrics() {
int someInt = 100;
String text = "some text";
diff --git a/tests/tests/text/src/android/text/cts/StaticLayoutTest.java b/tests/tests/text/src/android/text/cts/StaticLayoutTest.java
index 00856cd..12a4271 100644
--- a/tests/tests/text/src/android/text/cts/StaticLayoutTest.java
+++ b/tests/tests/text/src/android/text/cts/StaticLayoutTest.java
@@ -33,6 +33,7 @@
import android.graphics.Paint.FontMetricsInt;
import android.graphics.Typeface;
import android.os.LocaleList;
+import android.platform.test.annotations.SecurityTest;
import android.text.Editable;
import android.text.Layout;
import android.text.Layout.Alignment;
@@ -1683,6 +1684,7 @@
}
// This is for b/140755449
+ @SecurityTest
@Test
public void testBidiVisibleEnd() {
TextPaint paint = new TextPaint();
diff --git a/tests/tests/text/src/android/text/format/cts/DateUtilsTest.java b/tests/tests/text/src/android/text/format/cts/DateUtilsTest.java
index fc4ef5a..783f83d 100644
--- a/tests/tests/text/src/android/text/format/cts/DateUtilsTest.java
+++ b/tests/tests/text/src/android/text/format/cts/DateUtilsTest.java
@@ -29,6 +29,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -43,16 +44,25 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
public class DateUtilsTest {
+ private TimeZone mDefaultTimeZone;
private long mBaseTime;
private Context mContext;
@Before
public void setup() {
mContext = InstrumentationRegistry.getTargetContext();
+ mDefaultTimeZone = TimeZone.getDefault();
+ // All tests in this class can assume the device time zone is set to GMT.
TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
mBaseTime = System.currentTimeMillis();
}
+ @After
+ public void tearDown() {
+ // Set the default time zone back to what it was before setup().
+ TimeZone.setDefault(mDefaultTimeZone);
+ }
+
@Test
public void testGetDayOfWeekString() {
if (!LocaleUtils.isCurrentLocale(mContext, Locale.US)) {
@@ -249,9 +259,25 @@
@Test
public void testIsToday() {
- final long ONE_DAY_IN_MS = 24 * 60 * 60 * 1000;
- assertTrue(DateUtils.isToday(mBaseTime));
+ // This test assumes TimeZone.getDefault() returns GMT. See setup() and comments below for
+ // details.
+
+ final int ONE_HOUR_IN_MS = 60 * 60 * 1000;
+ final int ONE_DAY_IN_MS = 24 * ONE_HOUR_IN_MS;
+
+ // mBaseTime < System.currentTimeMillis(), so subtracting 24 hours is guaranteed to
+ // be yesterday because this test uses GMT, i.e. no DST to consider.
assertFalse(DateUtils.isToday(mBaseTime - ONE_DAY_IN_MS));
+
+ // We can assume mBaseTime is within a few seconds of the current system clock so adding
+ // one day plus one hour is more than sufficient to ensure isToday() == false.
+ assertFalse(DateUtils.isToday(mBaseTime + ONE_DAY_IN_MS + ONE_HOUR_IN_MS));
+
+ // This assertion is flaky because the method under test uses the system clock. If mBaseTime
+ // is set just before midnight (GMT) and isToday() is run just after midnight (GMT), this
+ // assertion will fail.
+ assertTrue("mBaseTime=" + mBaseTime + ", System.currentTimeMillis() after failure="
+ + System.currentTimeMillis(), DateUtils.isToday(mBaseTime));
}
@Test
diff --git a/tests/tests/text/src/android/text/format/cts/TimeTest.java b/tests/tests/text/src/android/text/format/cts/TimeTest.java
index ad5c3bf..aecf05b 100644
--- a/tests/tests/text/src/android/text/format/cts/TimeTest.java
+++ b/tests/tests/text/src/android/text/format/cts/TimeTest.java
@@ -37,10 +37,14 @@
import java.time.Duration;
import java.time.Instant;
+import java.time.LocalDate;
import java.time.LocalDateTime;
+import java.time.LocalTime;
import java.time.Month;
import java.time.ZoneId;
+import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
+import java.time.temporal.JulianFields;
import java.time.zone.ZoneOffsetTransition;
import java.time.zone.ZoneRules;
import java.util.ArrayList;
@@ -221,11 +225,44 @@
@Test
public void testIsEpoch() {
- Time time = new Time();
+ // Create a Time that uses UTC to provide a behavior baseline.
+ Time time = new Time(Time.TIMEZONE_UTC);
+
+ // Time is initialized to 1970-01-01 00:00:00
assertTrue(Time.isEpoch(time));
- time.set(1, 2, 1970);
+
+ // 1970-01-01 23:59:59
+ checkIsEpochResult(time, 1970, 0 /* Jan */, 1, 23, 59, 59, true);
+
+ // 1970-01-02 00:00:00
+ checkIsEpochResult(time, 1970, 0 /* Jan */, 2, 0, 0, 0, false);
+
+ // 1969-12-31 23:59:59
+ checkIsEpochResult(time, 1969, 11 /* Dec */, 31, 23, 59, 59, false);
+
+ // Now demonstrate that the isEpoch() method just checks against the Julian day
+ // calculated for UTC. America/Los_Angeles is UTC-8 so all times have to be adjusted
+ // by 8 hours.
+ time.timezone = "America/Los_Angeles";
+
+ // 1969-12-31 15:59:59 == 1969-12-31 23:59:59 in UTC
+ checkIsEpochResult(time, 1969, 11 /* Dec */, 31, 15, 59, 59, false);
+
+ // 1969-12-31 16:00:00 == 1970-01-01 00:00:00 in UTC
+ checkIsEpochResult(time, 1969, 11 /* Dec */, 31, 16, 0, 0, true);
+
+ // 1970-01-01 15:59:59 == 1970-01-01 23:59:59 in UTC
+ checkIsEpochResult(time, 1970, 0 /* Jan */, 1, 15, 59, 59, true);
+
+ // 1970-01-01 16:00:00 == 1970-01-02 00:00:00 in UTC
+ checkIsEpochResult(time, 1970, 0 /* Jan */, 1, 16, 0, 0, false);
+ }
+
+ private void checkIsEpochResult(Time time, int year, int month, int monthDay, int hour,
+ int minute, int second, boolean expectedIsEpoch) {
+ time.set(second, minute, hour, monthDay, month, year);
time.normalize(false);
- assertFalse(Time.isEpoch(time));
+ assertEquals(expectedIsEpoch, Time.isEpoch(time));
}
@Test
@@ -1913,50 +1950,71 @@
"Pacific/Midway",
};
+ /**
+ * This test uses java.time classes to construct test times so that it can test various years
+ * including those outside of the int32 seconds range.
+ */
@Test
public void testGetJulianDay() {
- Time time = new Time();
+ int[] years = { 2008, 1900, 1969, 2100 };
+ for (int year : years) {
+ for (String timeZone : mTimeZones) {
+ checkGetJulianDayForYearAndTimeZone(year, timeZone);
+ }
+ }
+ }
- // For every 15th day of 2008, and for each of the timezones listed above,
- // get the Julian day for 12am and then check that if we change the time we get the
- // same Julian day. Note that one of the many problems with the Time class
- // is its lack of error handling. If we accidentally hit a time that doesn't
- // exist (because it was skipped by a daylight savings transition), rather than
- // an error, you'll silently get 1970-01-01. We should @deprecate Time.
- for (int monthDay = 1; monthDay <= 366; monthDay += 15) {
- for (int zoneIndex = 0; zoneIndex < mTimeZones.length; zoneIndex++) {
- // We leave the "month" as zero because we are changing the
- // "monthDay" from 1 to 366. The call to normalize() will
- // then change the "month" (but we don't really care).
- time.set(0, 0, 12, monthDay, 0, 2008);
- time.timezone = mTimeZones[zoneIndex];
- long millis = time.normalize(true);
- if (zoneIndex == 0) {
- Log.i(TAG, time.format("%B %d, %Y"));
- }
+ private static void checkGetJulianDayForYearAndTimeZone(int year, String timeZone) {
+ final LocalTime midday = LocalTime.of(12, 0);
- // This is the Julian day for 12pm for this day of the year
- int julianDay = Time.getJulianDay(millis, time.gmtoff);
+ // For every 15th day of the year get the Julian day for 12pm and then check that if we
+ // change the time we get the same Julian day.
+ final LocalDate startDate = LocalDate.of(year, Month.JANUARY, 1);
+ final LocalDate stopDate = startDate.plusYears(1);
+ for (LocalDate testDate = startDate; testDate.isBefore(stopDate);
+ testDate = testDate.plusDays(15)) {
- // Change the time during the day and check that we get the same
- // Julian day.
- for (int hour = 0; hour < 24; hour++) {
- for (int minute = 0; minute < 60; minute += 15) {
- time.set(0, minute, hour, monthDay, 0, 2008);
- millis = time.normalize(true);
- if (millis == -1) {
- // millis == -1 means the wall time does not exist in the chosen
- // timezone due to a DST change. We cannot calculate a JulianDay for -1.
- continue;
- }
+ LocalDateTime middayLocalDateTime = LocalDateTime.of(testDate, midday);
+ ZoneOffset middayOffset = ZoneId.of(timeZone).getRules().getOffset(middayLocalDateTime);
+ Instant middayInstant = middayLocalDateTime.toInstant(middayOffset);
- int day = Time.getJulianDay(millis, time.gmtoff);
- assertEquals("Julian day: " + day + " at time "
- + time.hour + ":" + time.minute
- + " != today's Julian day: " + julianDay
- + " timezone: " + time.timezone, day, julianDay);
- }
- }
+ // Record the Julian day for the date/time given. Since we want to know the local
+ // calendar date we have to provide the time zone's UTC offset too.
+ int middayJulianDay =
+ Time.getJulianDay(middayInstant.toEpochMilli(), middayOffset.getTotalSeconds());
+
+ // Check Time.getJulianDay() agrees with java.time's Julian day calculations.
+ assertEquals(middayJulianDay, JulianFields.JULIAN_DAY.getFrom(middayLocalDateTime));
+
+ checkGetJulianDayVariousTimes(timeZone, testDate);
+ }
+ }
+
+ private static void checkGetJulianDayVariousTimes(String timeZone, LocalDate testDate) {
+ long expectedJulianDay = JulianFields.JULIAN_DAY.getFrom(testDate);
+
+ // Change the time during the day and check that we get the same Julian day as the one
+ // for midday.
+ for (int hour = 0; hour < 24; hour++) {
+ for (int minute = 0; minute < 60; minute += 15) {
+ LocalTime localTime = LocalTime.of(hour, minute);
+ LocalDateTime localDateTime = LocalDateTime.of(testDate, localTime);
+ ZoneOffset localDateTimeOffset =
+ ZoneId.of(timeZone).getRules().getOffset(localDateTime);
+ Instant instant = localDateTime.toInstant(localDateTimeOffset);
+ long millis = instant.toEpochMilli();
+
+ // Find the Julian day for the date/time by supplying the offset. Since we want
+ // to know the local calendar date we have to provide the UTC offset too.
+ int julianDay = Time.getJulianDay(millis, localDateTimeOffset.getTotalSeconds());
+
+ assertEquals("Julian day: " + julianDay + " at time "
+ + hour + ":" + minute
+ + " != today's Julian day: " + expectedJulianDay
+ + " millis: " + millis
+ + " localDatetime: " + localDateTime
+ + " timeZone: " + timeZone,
+ julianDay, expectedJulianDay);
}
}
}
diff --git a/tests/tests/text/src/android/text/style/cts/ReplacementSpanTest.java b/tests/tests/text/src/android/text/style/cts/ReplacementSpanTest.java
index 7dca324..e0829d9 100644
--- a/tests/tests/text/src/android/text/style/cts/ReplacementSpanTest.java
+++ b/tests/tests/text/src/android/text/style/cts/ReplacementSpanTest.java
@@ -16,6 +16,8 @@
package android.text.style.cts;
+import static org.junit.Assert.assertEquals;
+
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.FontMetricsInt;
@@ -42,6 +44,16 @@
replacementSpan.updateDrawState(null);
}
+ @Test
+ public void testContentDescription() {
+ final String testContentDescription = "testContentDescription";
+ ReplacementSpan replacementSpan = new MockReplacementSpan();
+
+ replacementSpan.setContentDescription(testContentDescription);
+
+ assertEquals(testContentDescription, replacementSpan.getContentDescription());
+ }
+
private class MockReplacementSpan extends ReplacementSpan {
@Override
public void draw(Canvas canvas, CharSequence text, int start, int end,
diff --git a/tests/tests/toast/OWNERS b/tests/tests/toast/OWNERS
new file mode 100644
index 0000000..5cb1f47
--- /dev/null
+++ b/tests/tests/toast/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 137825
+svetoslavganov@google.com
+moltmann@google.com
+toddke@google.com
diff --git a/tests/tests/toastlegacy/OWNERS b/tests/tests/toastlegacy/OWNERS
new file mode 100644
index 0000000..d21846d
--- /dev/null
+++ b/tests/tests/toastlegacy/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 137825
+include ../toast/OWNERS
diff --git a/tests/tests/tools/processors/view_inspector/OWNERS b/tests/tests/tools/processors/view_inspector/OWNERS
new file mode 100644
index 0000000..1572c57
--- /dev/null
+++ b/tests/tests/tools/processors/view_inspector/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 25700
+ashleyrose@google.com
+aurimas@google.com
diff --git a/tests/tests/transition/src/android/transition/cts/ActivityTransitionTest.java b/tests/tests/transition/src/android/transition/cts/ActivityTransitionTest.java
index 8df3484..2301438 100644
--- a/tests/tests/transition/src/android/transition/cts/ActivityTransitionTest.java
+++ b/tests/tests/transition/src/android/transition/cts/ActivityTransitionTest.java
@@ -72,6 +72,7 @@
@Override
public void setup() {
super.setup();
+ ActivityOptions.setExitTransitionTimeout(10000);
setTransitions(new TrackingVisibility(), new TrackingVisibility(),
new TrackingTransition());
}
@@ -102,6 +103,7 @@
}
});
TargetActivity.clearCreated();
+ ActivityOptions.setExitTransitionTimeout(1000);
}
// When using ActivityOptions.makeBasic(), no transitions should run
diff --git a/tests/tests/uiautomation/Android.bp b/tests/tests/uiautomation/Android.bp
index c9d9108..6a0ba7c 100644
--- a/tests/tests/uiautomation/Android.bp
+++ b/tests/tests/uiautomation/Android.bp
@@ -22,7 +22,7 @@
"general-tests",
],
static_libs: [
- "compatibility-device-util-axt",
+ "CtsAccessibilityCommon",
"ctstestrunner-axt",
"ub-uiautomator",
],
diff --git a/tests/tests/uiautomation/AndroidManifest.xml b/tests/tests/uiautomation/AndroidManifest.xml
index af4f4cd..75eab20 100644
--- a/tests/tests/uiautomation/AndroidManifest.xml
+++ b/tests/tests/uiautomation/AndroidManifest.xml
@@ -21,10 +21,12 @@
android:targetSandboxVersion="2">
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
- <uses-permission android:name="android.permission.CAMERA" />
+ <uses-permission android:name="android.permission.ANSWER_PHONE_CALLS" />
- <application android:theme="@android:style/Theme.Holo.NoActionBar" >
+ <application android:theme="@android:style/Theme.Holo.NoActionBar"
+ android:requestLegacyExternalStorage="true">
<uses-library android:name="android.test.runner"/>
diff --git a/tests/tests/uiautomation/AndroidTest.xml b/tests/tests/uiautomation/AndroidTest.xml
index f5cf748..e05dc4b 100644
--- a/tests/tests/uiautomation/AndroidTest.xml
+++ b/tests/tests/uiautomation/AndroidTest.xml
@@ -23,10 +23,14 @@
<option name="test-file-name" value="CtsUiAutomationTestCases.apk" />
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
- <option name="run-command" value="pm revoke android.app.uiautomation.cts android.permission.CAMERA" />
+ <option name="run-command" value="pm revoke android.app.uiautomation.cts android.permission.ANSWER_PHONE_CALLS" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.app.uiautomation.cts" />
<option name="runtime-hint" value="6m42s" />
</test>
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="directory-keys" value="/sdcard/android.app.uiautomation.cts" />
+ <option name="collect-on-run-ended-only" value="true" />
+ </metrics_collector>
</configuration>
diff --git a/tests/tests/uiautomation/OWNERS b/tests/tests/uiautomation/OWNERS
index bbcb1a7..a98c458 100644
--- a/tests/tests/uiautomation/OWNERS
+++ b/tests/tests/uiautomation/OWNERS
@@ -1,2 +1,3 @@
# Bug component: 44215
-pweaver@google.com
\ No newline at end of file
+pweaver@google.com
+rhedjao@google.com
diff --git a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationLogRule.java b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationLogRule.java
deleted file mode 100644
index 3f81d3a..0000000
--- a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationLogRule.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.uiautomation.cts;
-
-import android.app.UiAutomation;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-import androidx.test.InstrumentationRegistry;
-
-import com.android.compatibility.common.util.SystemUtil;
-
-import org.junit.AssumptionViolatedException;
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-
-import java.io.IOException;
-
-/**
- * Improves UiAutomationTest logging, dumps log when a test case gets failed.
- *
- * <ol>
- * <li>Call {@code dumpsys accessibility}.
- * </ol>
- */
-public final class UiAutomationLogRule implements TestRule {
-
- private final String mTestName;
-
- public UiAutomationLogRule(@NonNull String testName) {
- mTestName = testName;
- }
-
- @Override
- public Statement apply(Statement base, Description description) {
- return new Statement() {
- @Override
- public void evaluate() throws Throwable {
- Throwable throwable = null;
- // First run the test
- try {
- base.evaluate();
- } catch (Throwable t) {
- throwable = t;
- }
-
- // Ignore AssumptionViolatedException. It's not a test fail.
- if (throwable != null && throwable instanceof AssumptionViolatedException) {
- throwable = null;
- }
-
- if (throwable != null) {
- try {
- Log.e(mTestName, "TEST FAIL");
- dump();
- } catch (Throwable t) {
- Log.e(mTestName, "Dump fail", t);
- }
- }
-
- // Finally, throw exception!
- if (throwable == null) return;
- throw throwable;
- }
- };
- }
-
- private void dump() throws IOException {
- UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation(
- UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
- try {
- final String a11yDump = SystemUtil.runShellCommand(
- uiAutomation, "dumpsys accessibility");
- Log.e(mTestName, "==== dumpsys accessibility ====\n" + a11yDump);
- } finally {
- uiAutomation.destroy();
- }
- }
-}
diff --git a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
index 828d9b5..9135e56 100755
--- a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
+++ b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
@@ -17,13 +17,13 @@
package android.app.uiautomation.cts;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.Manifest;
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.app.Activity;
import android.app.ActivityManager;
@@ -71,8 +71,8 @@
private static final int TIMEOUT_FOR_SERVICE_ENABLE = 10000; // millis; 10s
@Rule
- public final UiAutomationLogRule mLogRule = new UiAutomationLogRule(
- UiAutomationTest.class.getSimpleName());
+ public final AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+ new AccessibilityDumpOnFailureRule();
@Before
public void setUp() throws Exception {
@@ -99,7 +99,7 @@
}
try {
packageManager.grantRuntimePermission(context.getPackageName(),
- Manifest.permission.CAMERA, Process.myUserHandle());
+ Manifest.permission.ANSWER_PHONE_CALLS, Process.myUserHandle());
fail("Should not be able to access APIs protected by a permission apps cannot get");
} catch (SecurityException e) {
/* expected */
@@ -113,17 +113,17 @@
activityManager.getPackageImportance("foo.bar.baz");
// Grant ourselves a runtime permission (was granted at install)
- assertSame(packageManager.checkPermission(Manifest.permission.CAMERA,
+ assertSame(packageManager.checkPermission(Manifest.permission.ANSWER_PHONE_CALLS,
context.getPackageName()), PackageManager.PERMISSION_DENIED);
packageManager.grantRuntimePermission(context.getPackageName(),
- Manifest.permission.CAMERA, Process.myUserHandle());
+ Manifest.permission.ANSWER_PHONE_CALLS, Process.myUserHandle());
} catch (SecurityException e) {
fail("Should be able to access APIs protected by a permission apps cannot get");
} finally {
getInstrumentation().getUiAutomation().dropShellPermissionIdentity();
}
// Make sure the grant worked
- assertSame(packageManager.checkPermission(Manifest.permission.CAMERA,
+ assertSame(packageManager.checkPermission(Manifest.permission.ANSWER_PHONE_CALLS,
context.getPackageName()), PackageManager.PERMISSION_GRANTED);
@@ -136,7 +136,7 @@
}
try {
packageManager.revokeRuntimePermission(context.getPackageName(),
- Manifest.permission.CAMERA, Process.myUserHandle());
+ Manifest.permission.ANSWER_PHONE_CALLS, Process.myUserHandle());
fail("Should not be able to access APIs protected by a permission apps cannot get");
} catch (SecurityException e) {
/* expected */
diff --git a/tests/tests/uidisolation/OWNERS b/tests/tests/uidisolation/OWNERS
new file mode 100644
index 0000000..94522e3
--- /dev/null
+++ b/tests/tests/uidisolation/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 36824
+include /tests/tests/security/OWNERS
diff --git a/tests/tests/uirendering/res/layout/viewpropertyanimator_test_layout.xml b/tests/tests/uirendering/res/layout/viewpropertyanimator_test_layout.xml
new file mode 100644
index 0000000..b6f67d8
--- /dev/null
+++ b/tests/tests/uirendering/res/layout/viewpropertyanimator_test_layout.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:background="#FFFFFF"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <FrameLayout
+ android:id="@+id/viewalpha_test_container"
+ android:background="#0000FF"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <android.uirendering.cts.testclasses.view.AlphaTestView
+ android:id="@+id/alpha_test_view"
+ android:layout_width="100px"
+ android:layout_height="100px"
+ />
+ </FrameLayout>
+</FrameLayout>
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ColorFilterAlphaTest.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ColorFilterAlphaTest.java
index edeb2e8..f9f47a9 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ColorFilterAlphaTest.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ColorFilterAlphaTest.java
@@ -26,10 +26,11 @@
import android.graphics.PorterDuffXfermode;
import android.graphics.drawable.ColorDrawable;
import android.uirendering.cts.bitmapverifiers.SamplePointVerifier;
+import android.uirendering.cts.runner.SkipPresubmit;
import android.uirendering.cts.testinfrastructure.ActivityTestBase;
import android.uirendering.cts.testinfrastructure.CanvasClient;
-import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
import org.junit.Before;
import org.junit.Test;
@@ -38,7 +39,8 @@
import java.util.List;
-@LargeTest // Temporarily hidden from presubmit
+@SkipPresubmit // Temporarily hidden from presubmit
+@MediumTest
@RunWith(Parameterized.class)
public class ColorFilterAlphaTest extends ActivityTestBase {
// We care about one point in each of the four rectangles of different alpha values, as well as
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/HardwareBitmapTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/HardwareBitmapTests.java
index b6a09c0..1a7151b 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/HardwareBitmapTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/HardwareBitmapTests.java
@@ -225,6 +225,7 @@
NinePatchDrawable ninePatch = (NinePatchDrawable) Drawable.createFromResourceStream(
mRes, null, is, null, HARDWARE_OPTIONS);
ninePatch.setBounds(0, 0, width, height);
+ ninePatch.setFilterBitmap(false);
ninePatch.draw(canvas);
}, true).runWithVerifier(new GoldenImageVerifier(getActivity(),
R.drawable.golden_hardwaretest_ninepatch, new MSSIMComparer(0.95)));
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayerTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayerTests.java
index db37463..f42b004 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayerTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayerTests.java
@@ -562,8 +562,10 @@
// Adjust Y to match the same gradient percentage, regardless of vertical
// fading edge length.
int verticalFadingEdgeLength = webview.getVerticalFadingEdgeLength();
- testPoints[2].y = TEST_HEIGHT - verticalFadingEdgeLength * 10 / 42;
- testPoints[3].y = TEST_HEIGHT - verticalFadingEdgeLength * 5 / 42;
+ testPoints[2].y = TEST_HEIGHT
+ - (int) Math.round(verticalFadingEdgeLength * 10.0 / 42);
+ testPoints[3].y = TEST_HEIGHT
+ - (int) Math.round(verticalFadingEdgeLength * 5.0 / 42);
}, true, hwFence)
.runWithVerifier(new SamplePointVerifier(
testPoints,
@@ -603,8 +605,10 @@
// Adjust Y to match the same gradient percentage, regardless of vertical
// fading edge length.
int verticalFadingEdgeLength = webview.getVerticalFadingEdgeLength();
- testPoints[3].y = TEST_HEIGHT - verticalFadingEdgeLength * 10 / 42;
- testPoints[4].y = TEST_HEIGHT - verticalFadingEdgeLength * 5 / 42;
+ testPoints[3].y = TEST_HEIGHT
+ - (int) Math.round(verticalFadingEdgeLength * 10.0 / 42);
+ testPoints[4].y = TEST_HEIGHT
+ - (int) Math.round(verticalFadingEdgeLength * 5.0 / 42);
}, true, hwFence)
.runWithVerifier(new SamplePointVerifier(
testPoints,
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ViewPropertyAnimatorTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ViewPropertyAnimatorTests.java
new file mode 100644
index 0000000..0f749d9
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ViewPropertyAnimatorTests.java
@@ -0,0 +1,542 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.uirendering.cts.testclasses;
+
+import static junit.framework.Assert.assertEquals;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.graphics.Color;
+import android.uirendering.cts.R;
+import android.uirendering.cts.bitmapverifiers.ColorVerifier;
+import android.uirendering.cts.testclasses.view.AlphaTestView;
+import android.uirendering.cts.testinfrastructure.ActivityTestBase;
+import android.uirendering.cts.testinfrastructure.ViewInitializer;
+import android.view.View;
+import android.view.ViewPropertyAnimator;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ViewPropertyAnimatorTests extends ActivityTestBase {
+
+ @Test
+ public void testViewCustomAlpha() {
+ createViewPropertyAnimatorTest(new ViewPropertyAnimatorTestDelegate<AlphaTestView>() {
+ @Override
+ public void configureView(AlphaTestView target) {
+ target.setStartColor(Color.RED);
+ target.setEndColor(Color.BLUE);
+ }
+
+ @Override
+ public void configureAnimator(ViewPropertyAnimator animator) {
+ animator.alpha(0.0f);
+ }
+
+ @Override
+ public void verifyViewState(AlphaTestView target) {
+ assertEquals(Color.BLUE, target.getBlendedColor());
+ }
+
+ }).runWithVerifier(new ColorVerifier(Color.BLUE));
+ }
+
+ @Test
+ public void testViewNonCustomAlpha() {
+ final CountDownLatch latch = new CountDownLatch(1);
+ createTest().addLayout(R.layout.viewpropertyanimator_test_layout,
+ (ViewInitializer) view -> {
+ View testContent = view.findViewById(R.id.viewalpha_test_container);
+ testContent.animate().alpha(0.0f).setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ latch.countDown();
+ }
+ }).setDuration(16).start();
+ }, true, latch).runWithVerifier(new ColorVerifier(Color.WHITE));
+ }
+
+ @Test
+ public void testViewCustomAlphaBy() {
+ createViewPropertyAnimatorTest(new ViewPropertyAnimatorTestDelegate<AlphaTestView>() {
+ @Override
+ public void configureView(AlphaTestView target) {
+ target.setStartColor(Color.RED);
+ target.setEndColor(Color.BLUE);
+ target.setAlpha(0.5f);
+ }
+
+ @Override
+ public void configureAnimator(ViewPropertyAnimator animator) {
+ animator.alphaBy(-0.5f);
+ }
+
+ @Override
+ public void verifyViewState(AlphaTestView target) {
+ assertEquals(Color.BLUE, target.getBlendedColor());
+ }
+
+ }).runWithVerifier(new ColorVerifier(Color.BLUE));
+ }
+
+ @Test
+ public void testViewTranslateX() {
+ runViewPropertyAnimatorTestWithoutVerification(new ViewPropertyAnimatorTestDelegate() {
+
+ @Override
+ public void configureAnimator(ViewPropertyAnimator animator) {
+ animator.translationX(100.0f);
+ }
+
+ @Override
+ public void verifyViewState(View target) {
+ assertEquals(100.0f, target.getTranslationX());
+ }
+ });
+ }
+
+ @Test
+ public void testViewTranslateXBy() {
+ runViewPropertyAnimatorTestWithoutVerification(new ViewPropertyAnimatorTestDelegate() {
+
+ @Override
+ public void configureView(View target) {
+ target.setTranslationX(20.0f);
+ }
+
+ @Override
+ public void configureAnimator(ViewPropertyAnimator animator) {
+ animator.translationXBy(100.0f);
+ }
+
+ @Override
+ public void verifyViewState(View target) {
+ assertEquals(120.0f, target.getTranslationX());
+ }
+ });
+ }
+
+ @Test
+ public void testViewTranslateY() {
+ runViewPropertyAnimatorTestWithoutVerification(new ViewPropertyAnimatorTestDelegate() {
+
+ @Override
+ public void configureAnimator(ViewPropertyAnimator animator) {
+ animator.translationY(60.0f);
+
+ }
+
+ @Override
+ public void verifyViewState(View target) {
+ assertEquals(60.0f, target.getTranslationY());
+ }
+ });
+ }
+
+ @Test
+ public void testViewTranslateYBy() {
+ runViewPropertyAnimatorTestWithoutVerification(new ViewPropertyAnimatorTestDelegate() {
+
+ @Override
+ public void configureView(View target) {
+ target.setTranslationY(30.0f);
+ }
+
+ @Override
+ public void configureAnimator(ViewPropertyAnimator animator) {
+ animator.translationYBy(60.0f);
+ }
+
+ @Override
+ public void verifyViewState(View target) {
+ assertEquals(90.0f, target.getTranslationY());
+ }
+ });
+ }
+
+ @Test
+ public void testViewTranslateZ() {
+ runViewPropertyAnimatorTestWithoutVerification(new ViewPropertyAnimatorTestDelegate() {
+ @Override
+ public void configureAnimator(ViewPropertyAnimator animator) {
+ animator.translationZ(30.0f);
+ }
+
+ @Override
+ public void verifyViewState(View target) {
+ assertEquals(30.0f, target.getTranslationZ());
+ }
+ });
+ }
+
+ @Test
+ public void testViewTranslateZBy() {
+ runViewPropertyAnimatorTestWithoutVerification(new ViewPropertyAnimatorTestDelegate() {
+
+ @Override
+ public void configureView(View target) {
+ target.setTranslationZ(40.0f);
+ }
+
+ @Override
+ public void configureAnimator(ViewPropertyAnimator animator) {
+ animator.translationZBy(30.0f);
+ }
+
+ @Override
+ public void verifyViewState(View target) {
+ assertEquals(70.0f, target.getTranslationZ());
+ }
+ });
+ }
+
+ @Test
+ public void testViewRotation() {
+ runViewPropertyAnimatorTestWithoutVerification(new ViewPropertyAnimatorTestDelegate() {
+ @Override
+ public void configureAnimator(ViewPropertyAnimator animator) {
+ animator.rotation(20.0f);
+ }
+
+ @Override
+ public void verifyViewState(View target) {
+ assertEquals(20.0f, target.getRotation());
+ }
+ });
+ }
+
+ @Test
+ public void testViewRotationBy() {
+ runViewPropertyAnimatorTestWithoutVerification(new ViewPropertyAnimatorTestDelegate() {
+
+ @Override
+ public void configureView(View target) {
+ target.setRotation(30.0f);
+ }
+
+ @Override
+ public void configureAnimator(ViewPropertyAnimator animator) {
+ animator.rotationBy(20.0f);
+ }
+
+ @Override
+ public void verifyViewState(View target) {
+ assertEquals(50.0f, target.getRotation());
+ }
+ });
+ }
+
+ @Test
+ public void testViewRotationX() {
+ runViewPropertyAnimatorTestWithoutVerification(new ViewPropertyAnimatorTestDelegate() {
+ @Override
+ public void configureAnimator(ViewPropertyAnimator animator) {
+ animator.rotationX(80.0f);
+ }
+
+ @Override
+ public void verifyViewState(View target) {
+ assertEquals(80.0f, target.getRotationX());
+ }
+ });
+ }
+
+ @Test
+ public void testViewRotationXBy() {
+ runViewPropertyAnimatorTestWithoutVerification(new ViewPropertyAnimatorTestDelegate() {
+
+ @Override
+ public void configureView(View target) {
+ target.setRotationX(30.0f);
+ }
+
+ @Override
+ public void configureAnimator(ViewPropertyAnimator animator) {
+ animator.rotationXBy(80.0f);
+ }
+
+ @Override
+ public void verifyViewState(View target) {
+ assertEquals(110.0f, target.getRotationX());
+ }
+ });
+ }
+
+ @Test
+ public void testViewRotationY() {
+ runViewPropertyAnimatorTestWithoutVerification(new ViewPropertyAnimatorTestDelegate() {
+ @Override
+ public void configureAnimator(ViewPropertyAnimator animator) {
+ animator.rotationY(25.0f);
+ }
+
+ @Override
+ public void verifyViewState(View target) {
+ assertEquals(25.0f, target.getRotationY());
+ }
+ });
+ }
+
+ @Test
+ public void testViewRotationYBy() {
+ runViewPropertyAnimatorTestWithoutVerification(new ViewPropertyAnimatorTestDelegate() {
+
+ @Override
+ public void configureView(View target) {
+ target.setRotationY(10.0f);
+ }
+
+ @Override
+ public void configureAnimator(ViewPropertyAnimator animator) {
+ animator.rotationYBy(25.0f);
+ }
+
+ @Override
+ public void verifyViewState(View target) {
+ assertEquals(35.0f, target.getRotationY());
+ }
+ });
+ }
+
+ @Test
+ public void testViewScaleX() {
+ runViewPropertyAnimatorTestWithoutVerification(new ViewPropertyAnimatorTestDelegate() {
+ @Override
+ public void configureAnimator(ViewPropertyAnimator animator) {
+ animator.scaleX(2.5f);
+ }
+
+ @Override
+ public void verifyViewState(View target) {
+ assertEquals(2.5f, target.getScaleX());
+ }
+ });
+ }
+
+ @Test
+ public void testViewScaleXBy() {
+ runViewPropertyAnimatorTestWithoutVerification(new ViewPropertyAnimatorTestDelegate() {
+
+ @Override
+ public void configureView(View target) {
+ target.setScaleX(1.2f);
+ }
+
+ @Override
+ public void configureAnimator(ViewPropertyAnimator animator) {
+ animator.scaleXBy(2.5f);
+ }
+
+ @Override
+ public void verifyViewState(View target) {
+ assertEquals(3.7f, target.getScaleX());
+ }
+ });
+ }
+
+ @Test
+ public void testViewScaleY() {
+ runViewPropertyAnimatorTestWithoutVerification(new ViewPropertyAnimatorTestDelegate() {
+ @Override
+ public void configureAnimator(ViewPropertyAnimator animator) {
+ animator.scaleY(3.2f);
+ }
+
+ @Override
+ public void verifyViewState(View target) {
+ assertEquals(3.2f, target.getScaleY());
+ }
+ });
+ }
+
+ @Test
+ public void testViewScaleYBy() {
+ runViewPropertyAnimatorTestWithoutVerification(new ViewPropertyAnimatorTestDelegate() {
+
+ @Override
+ public void configureView(View target) {
+ target.setScaleY(1.2f);
+ }
+
+ @Override
+ public void configureAnimator(ViewPropertyAnimator animator) {
+ animator.scaleYBy(3.2f);
+ }
+
+ @Override
+ public void verifyViewState(View target) {
+ assertEquals(4.4f, target.getScaleY());
+ }
+ });
+ }
+
+ @Test
+ public void testViewX() {
+ runViewPropertyAnimatorTestWithoutVerification(new ViewPropertyAnimatorTestDelegate() {
+ @Override
+ public void configureAnimator(ViewPropertyAnimator animator) {
+ animator.x(27.0f);
+ }
+
+ @Override
+ public void verifyViewState(View target) {
+ assertEquals(27.0f, target.getX());
+ }
+ });
+ }
+
+ @Test
+ public void testViewXBy() {
+ runViewPropertyAnimatorTestWithoutVerification(new ViewPropertyAnimatorTestDelegate() {
+
+ @Override
+ public void configureView(View target) {
+ target.setX(140.0f);
+ }
+
+ @Override
+ public void configureAnimator(ViewPropertyAnimator animator) {
+ animator.xBy(27.0f);
+ }
+
+ @Override
+ public void verifyViewState(View target) {
+ assertEquals(167.0f, target.getX());
+ }
+ });
+ }
+
+ @Test
+ public void testViewY() {
+ runViewPropertyAnimatorTestWithoutVerification(new ViewPropertyAnimatorTestDelegate() {
+ @Override
+ public void configureAnimator(ViewPropertyAnimator animator) {
+ animator.y(77.0f);
+ }
+
+ @Override
+ public void verifyViewState(View target) {
+ assertEquals(77.0f, target.getY());
+ }
+ });
+ }
+
+ @Test
+ public void testViewYBy() {
+ runViewPropertyAnimatorTestWithoutVerification(new ViewPropertyAnimatorTestDelegate() {
+
+ @Override
+ public void configureView(View target) {
+ target.setY(80.0f);
+ }
+
+ @Override
+ public void configureAnimator(ViewPropertyAnimator animator) {
+ animator.yBy(77.0f);
+ }
+
+ @Override
+ public void verifyViewState(View target) {
+ assertEquals(157.0f, target.getY());
+ }
+ });
+ }
+
+ @Test
+ public void testViewZ() {
+ runViewPropertyAnimatorTestWithoutVerification(new ViewPropertyAnimatorTestDelegate() {
+ @Override
+ public void configureAnimator(ViewPropertyAnimator animator) {
+ animator.z(17.0f);
+ }
+
+ @Override
+ public void verifyViewState(View target) {
+ assertEquals(17.0f, target.getZ());
+ }
+ });
+ }
+
+ @Test
+ public void testViewZBy() {
+ runViewPropertyAnimatorTestWithoutVerification(new ViewPropertyAnimatorTestDelegate() {
+
+ @Override
+ public void configureView(View target) {
+ target.setZ(38.0f);
+ }
+
+ @Override
+ public void configureAnimator(ViewPropertyAnimator animator) {
+ animator.zBy(17.0f);
+ }
+
+ @Override
+ public void verifyViewState(View target) {
+ assertEquals(55.0f, target.getZ());
+ }
+ });
+ }
+
+ private void runViewPropertyAnimatorTestWithoutVerification(
+ ViewPropertyAnimatorTestDelegate delegate) {
+ createViewPropertyAnimatorTest(delegate).runWithoutVerification();
+ }
+
+ private TestCaseBuilder createViewPropertyAnimatorTest(
+ final ViewPropertyAnimatorTestDelegate delegate) {
+ final CountDownLatch latch = new CountDownLatch(1);
+ return createTest().addLayout(R.layout.viewpropertyanimator_test_layout,
+ (ViewInitializer) view -> {
+ AlphaTestView alphaView = view.findViewById(R.id.alpha_test_view);
+ delegate.configureView(alphaView);
+ alphaView.setStartColor(Color.RED);
+ alphaView.setEndColor(Color.BLUE);
+ ViewPropertyAnimator animator = alphaView.animate();
+ delegate.configureAnimator(animator);
+ animator.setListener(
+ new AnimatorListenerAdapter() {
+
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ delegate.verifyViewState(alphaView);
+ latch.countDown();
+ }
+
+ }).setDuration(16).start();
+ }, true, latch);
+ }
+
+ private interface ViewPropertyAnimatorTestDelegate<T extends View> {
+
+ default void configureView(T target) {
+ // NO-OP
+ }
+
+ void configureAnimator(ViewPropertyAnimator animator);
+
+ void verifyViewState(T target);
+ }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/view/AlphaTestView.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/view/AlphaTestView.java
new file mode 100644
index 0000000..d475c8f
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/view/AlphaTestView.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.uirendering.cts.testclasses.view;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.view.View;
+
+/**
+ * Test View used to verify a View's custom alpha implementation logic
+ * in conjunction with {@link android.view.ViewPropertyAnimator}
+ */
+public class AlphaTestView extends View {
+
+ private Paint mPaint = new Paint();
+ private int mStartColor = Color.RED;
+ private int mEndColor = Color.BLUE;
+
+ public AlphaTestView(Context context) {
+ super(context);
+ }
+
+ public AlphaTestView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public AlphaTestView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public AlphaTestView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ public void setStartColor(int startColor) {
+ mStartColor = startColor;
+ mPaint.setColor(mStartColor);
+ }
+
+ public void setEndColor(int endColor) {
+ mEndColor = endColor;
+ }
+
+ @Override
+ protected boolean onSetAlpha(int alpha) {
+ mPaint.setColor(blendColor(mStartColor, mEndColor, 1.0f - alpha / 255.0f));
+ return true;
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ canvas.drawRect(getLeft(), getTop(), getRight(), getBottom(), mPaint);
+ }
+
+ public int getBlendedColor() {
+ return mPaint.getColor();
+ }
+
+ private int blendColor(int color1, int color2, float ratio) {
+ float inverseRatio = 1 - ratio;
+ float a = (float) Color.alpha(color1) * inverseRatio + (float) Color.alpha(color2) * ratio;
+ float r = (float) Color.red(color1) * inverseRatio + (float) Color.red(color2) * ratio;
+ float g = (float) Color.green(color1) * inverseRatio + (float) Color.green(color2) * ratio;
+ float b = (float) Color.blue(color1) * inverseRatio + (float) Color.blue(color2) * ratio;
+ return Color.argb((int) a, (int) r, (int) g, (int) b);
+ }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/util/BitmapAsserter.java b/tests/tests/uirendering/src/android/uirendering/cts/util/BitmapAsserter.java
index 47f4c89..55adc11 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/util/BitmapAsserter.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/util/BitmapAsserter.java
@@ -18,14 +18,16 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import android.content.Context;
import android.graphics.Bitmap;
import android.uirendering.cts.bitmapcomparers.BitmapComparer;
import android.uirendering.cts.bitmapverifiers.BitmapVerifier;
import android.uirendering.cts.differencevisualizers.DifferenceVisualizer;
import android.uirendering.cts.differencevisualizers.PassFailVisualizer;
+import androidx.test.platform.app.InstrumentationRegistry;
+
public class BitmapAsserter {
+ private static final boolean TAKE_SCREENSHOTS_ON_FAILURE = true;
private DifferenceVisualizer mDifferenceVisualizer;
private String mClassName;
@@ -64,6 +66,7 @@
if (!success) {
BitmapDumper.dumpBitmaps(bitmap1, bitmap2, testName, mClassName, mDifferenceVisualizer);
+ onFailure(testName);
}
assertTrue(debugMessage, success);
@@ -83,9 +86,17 @@
BitmapDumper.dumpBitmap(croppedBitmap, testName, mClassName);
BitmapDumper.dumpBitmap(bitmapVerifier.getDifferenceBitmap(), testName + "_verifier",
mClassName);
+ onFailure(testName);
}
assertTrue(debugMessage, success);
}
+ private void onFailure(String testName) {
+ if (TAKE_SCREENSHOTS_ON_FAILURE) {
+ Bitmap screenshot = InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation().takeScreenshot();
+ BitmapDumper.dumpBitmap(screenshot, testName + "_fullscreenshot", mClassName);
+ }
+ }
}
diff --git a/tests/tests/util/Android.bp b/tests/tests/util/Android.bp
index 3533502..0ea529e 100644
--- a/tests/tests/util/Android.bp
+++ b/tests/tests/util/Android.bp
@@ -26,6 +26,7 @@
"androidx.annotation_annotation",
"androidx.test.rules",
"ctstestrunner-axt",
+ "cts-install-lib",
],
srcs: ["src/**/*.java"],
platform_apis: true,
diff --git a/tests/tests/util/AndroidManifest.xml b/tests/tests/util/AndroidManifest.xml
index 0e71890..77ab380 100644
--- a/tests/tests/util/AndroidManifest.xml
+++ b/tests/tests/util/AndroidManifest.xml
@@ -21,6 +21,8 @@
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name="android.permission.READ_LOGS" />
<application>
+ <receiver android:name="com.android.cts.install.lib.LocalIntentSender"
+ android:exported="true" />
<uses-library android:name="android.test.runner" />
</application>
diff --git a/tests/tests/util/src/android/util/cts/InstallUtilTest.java b/tests/tests/util/src/android/util/cts/InstallUtilTest.java
new file mode 100644
index 0000000..5f48f37
--- /dev/null
+++ b/tests/tests/util/src/android/util/cts/InstallUtilTest.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.Manifest;
+import android.content.pm.PackageInstaller;
+import android.platform.test.annotations.AppModeFull;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.cts.install.lib.Install;
+import com.android.cts.install.lib.InstallUtils;
+import com.android.cts.install.lib.LocalIntentSender;
+import com.android.cts.install.lib.TestApp;
+import com.android.cts.install.lib.Uninstall;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@AppModeFull(reason = "Instant apps cannot create installer sessions")
+/**
+ * Test for cts.install.lib.
+ * <p>This test also tries to showcase how to use the library.
+ */
+public class InstallUtilTest {
+ /**
+ * Drops adopted shell permissions and uninstalls the test apps.
+ */
+ @After
+ public void teardown() throws InterruptedException, IOException {
+ // Good tests clean up after themselves.
+ // Remember that other tests will be using the same test apps.
+ Uninstall.packages(TestApp.A, TestApp.B);
+
+ InstallUtils.dropShellPermissionIdentity();
+ }
+
+ /**
+ * Adopts common permissions needed to test rollbacks and uninstalls the
+ * test apps.
+ */
+ @Before
+ public void setup() throws InterruptedException, IOException {
+ InstallUtils.adoptShellPermissionIdentity(
+ Manifest.permission.INSTALL_PACKAGES,
+ Manifest.permission.DELETE_PACKAGES);
+ // Better tests work regardless of whether other tests clean up after themselves or not.
+ Uninstall.packages(TestApp.A, TestApp.B);
+ }
+
+ @Test
+ public void testCommitSingleTestApp() throws Exception {
+ // Assert that the test app was not previously installed
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
+ // Install version 1 of TestApp.A
+ // Install#commit() asserts that the installation succeeds, so if it fails,
+ // an AssertionError would be thrown.
+ Install.single(TestApp.A1).commit();
+ // Even though the install session of TestApp.A1 is guaranteed to be committed by this stage
+ // it's still good practice to assert that the installed version of the app is the desired
+ // one. This is due to the fact that not all committed sessions are finalized sessions, i.e.
+ // staged install session.
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+ // No need to uninstall test app, as #teardown will do the job.
+ }
+
+ @Test
+ public void testCommitMultiTestApp() throws Exception {
+ // Assert that the test app was not previously installed.
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
+ assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(-1);
+ // Install version 1 of TestApp.A and version 2 of TestApp.B in one atomic install.
+ // Same notes as the single install case apply in the multi install case.
+ Install.multi(TestApp.A1, TestApp.B2).commit();
+
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+ assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(2);
+ }
+
+ @Test
+ public void testOpenAndAbandonSessionForSingleApk() throws Exception {
+ int sessionId = Install.single(TestApp.A1).createSession();
+ PackageInstaller.Session session = InstallUtils
+ .openPackageInstallerSession(sessionId);
+ assertThat(session).isNotNull();
+
+ // TODO: is there a way to verify that the APK has been written?
+
+ // At this stage, the session can be directly manipulated using
+ // PackageInstaller.Session API, i.e., it can be abandoned.
+ session.abandon();
+ // TODO: maybe add session callback and verify that session was abandoned?
+ // Assert session has not been installed
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
+ }
+
+ @Test
+ public void testOpenAndCommitSessionForSingleApk() throws Exception {
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
+ int sessionId = Install.single(TestApp.A1).createSession();
+ PackageInstaller.Session session = InstallUtils
+ .openPackageInstallerSession(sessionId);
+ assertThat(session).isNotNull();
+
+ // Session can be committed directly, but a BroadcastReceiver must be provided.
+ session.commit(LocalIntentSender.getIntentSender());
+ InstallUtils.assertStatusSuccess(LocalIntentSender.getIntentSenderResult());
+
+ // Verify app has been installed
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+ }
+
+ @Test
+ public void testOpenSingleSessionWithParameters() throws Exception {
+ int sessionId = Install.single(TestApp.A1).setStaged().createSession();
+ PackageInstaller.Session session = InstallUtils.openPackageInstallerSession(sessionId);
+ assertThat(session.isStaged()).isTrue();
+
+ session.abandon();
+ }
+
+ @Test
+ public void testOpenSessionForMultiPackageSession() throws Exception {
+ int parentSessionId = Install.multi(TestApp.A1, TestApp.B1).setStaged().createSession();
+ PackageInstaller.Session parentSession = InstallUtils
+ .openPackageInstallerSession(parentSessionId);
+ assertThat(parentSession.isMultiPackage()).isTrue();
+
+ assertThat(parentSession.isStaged()).isTrue();
+
+ // Child sessions are consistent with the parent parameters
+ int[] childSessionIds = parentSession.getChildSessionIds();
+ assertThat(childSessionIds.length).isEqualTo(2);
+ for (int childSessionId : childSessionIds) {
+ PackageInstaller.Session childSession = InstallUtils
+ .openPackageInstallerSession(childSessionId);
+ assertThat(childSession.isStaged()).isTrue();
+ }
+
+ parentSession.abandon();
+
+ // Verify apps have not been installed
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
+ assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(-1);
+ }
+
+ @Test
+ public void testMutateInstallFlags() throws Exception {
+ PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
+ PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+ params.setInstallAsApex();
+ params.setStaged();
+ InstallUtils.mutateInstallFlags(params, 0x00080000);
+ final Class<?> clazz = params.getClass();
+ Field installFlagsField = clazz.getDeclaredField("installFlags");
+ int installFlags = installFlagsField.getInt(params);
+ assertThat(installFlags & 0x00080000).isEqualTo(0x00080000);
+ }
+}
diff --git a/tests/tests/view/OWNERS b/tests/tests/view/OWNERS
new file mode 100644
index 0000000..8200a30
--- /dev/null
+++ b/tests/tests/view/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 25700
+adamp@google.com
+shepshapard@google.com
+clarabayarri@google.com
diff --git a/tests/tests/view/src/android/view/cts/TextureViewTest.java b/tests/tests/view/src/android/view/cts/TextureViewTest.java
index 38b993c..9cab157 100644
--- a/tests/tests/view/src/android/view/cts/TextureViewTest.java
+++ b/tests/tests/view/src/android/view/cts/TextureViewTest.java
@@ -300,22 +300,37 @@
public void testSamplingWithTransform() throws Throwable {
final TextureViewCtsActivity activity = mActivityRule.launchActivity(null);
final TextureView textureView = activity.getTextureView();
+ final int viewWidth = textureView.getWidth();
+ final int viewHeight = textureView.getHeight();
WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, activity.getTextureView(), null);
// Remove cover and calculate TextureView position on the screen.
WidgetTestUtils.runOnMainAndDrawSync(mActivityRule,
activity.findViewById(android.R.id.content), () -> activity.removeCover());
float[][] matrices = {
- {1, 0, 0, 0, 1, 0, 0, 0, 1}, // identity matrix
- {1, 0, 0, 0, 1, 10.3f, 0, 0, 1}, // translation matrix with a fractional offset
- {1, 0, 0, 0, 0.75f, 0, 0, 0, 1}, // scaling matrix
- {1, 0, 0, 0, 1, 10f, 0, 0, 1} // translation matrix with an integer offset
+ {1, 0, 0, 0, 1, 0, 0, 0, 1}, // identity matrix
+ {1, 0, 0, 0, 1, 10.3f, 0, 0, 1}, // translation matrix with a fractional offset
+ {1, 0, 0, 0, 0.75f, 0, 0, 0, 1}, // scaling matrix
+ {1, 0, 0, 0, 1, 10f, 0, 0, 1}, // translation matrix with an integer offset
+ {0, -1, viewWidth, 1, 0, 0, 0, 0, 1}, // 90 rotation matrix + integer translate X
+ {0, 1, 0, -1, 0, viewWidth, 0, 0, 1}, // 270 rotation matrix + integer translate Y
+ {-1, 0, viewWidth, 0, 1, 0, 0, 0, 1}, // H flip matrix + integer translate X
+ {1, 0, 0, 0, -1, viewHeight, 0, 0, 1}, // V flip matrix + integer translate Y
+ {-1, 0, viewWidth, 0, -1, viewHeight, 0, 0, 1}, // 180 rotation + integer translate X Y
+ {0, -1, viewWidth - 10.3f, 1, 0, 0, 0, 0, 1}, // 90 rotation matrix with a fractional
+ // offset
};
boolean[] nearestSampling = {
true, // nearest sampling for identity
false, // bilerp sampling for fractional translate
false, // bilerp sampling for scaling
- true // nearest sampling for integer translate
+ true, // nearest sampling for integer translate
+ true, // nearest sampling for 90 rotation with integer translate
+ true, // nearest sampling for 270 rotation with integer translate
+ true, // nearest sampling for H flip with integer translate
+ true, // nearest sampling for V flip with integer translate
+ true, // nearest sampling for 180 rotation with integer translate
+ false, // bilerp sampling for 90 rotation matrix with a fractional offset
};
for (int i = 0; i < nearestSampling.length; i++) {
@@ -345,8 +360,10 @@
// "texturePos" has SurfaceTexture position inside the TextureView.
RectF texturePosF = new RectF(0, 0, viewPos.width(), viewPos.height());
transform.mapRect(texturePosF);
- //clip parts outside TextureView
- texturePosF.intersect(0, 0, viewPos.width(), viewPos.height());
+ // Clip parts outside TextureView.
+ // Matrices are picked, so that the drawing area is not empty.
+ assertTrue("empty test area",
+ texturePosF.intersect(0, 0, viewPos.width(), viewPos.height()));
Rect texturePos = new Rect((int) Math.ceil(texturePosF.left),
(int) Math.ceil(texturePosF.top), (int) Math.floor(texturePosF.right),
(int) Math.floor(texturePosF.bottom));
@@ -367,15 +384,19 @@
}
}
} else {
- // Check there are no black nor white pixels, because bilerp sampling changed
- // pure black/white to a variety of gray intermediates.
+ // Check that a third of pixels are not black nor white, because bilerp sampling
+ // changed pure black/white to a variety of gray intermediates.
+ int nonBlackWhitePixels = 0;
for (int j = 0; j < pixels.length; j++) {
- if (pixels[j] == Color.BLACK || pixels[j] == Color.WHITE) {
- success = false;
+ if (pixels[j] != Color.BLACK && pixels[j] != Color.WHITE) {
+ nonBlackWhitePixels++;
+ } else {
failPosition = j;
- break;
}
}
+ if (nonBlackWhitePixels < pixels.length / 3) {
+ success = false;
+ }
}
assertTrue("Unexpected color at position " + failPosition + " = "
+ Integer.toHexString(pixels[failPosition]) + " " + transform.toString(),
diff --git a/tests/tests/view/src/android/view/textclassifier/cts/TextClassificationSessionIdTest.java b/tests/tests/view/src/android/view/textclassifier/cts/TextClassificationSessionIdTest.java
new file mode 100644
index 0000000..eef6ccd
--- /dev/null
+++ b/tests/tests/view/src/android/view/textclassifier/cts/TextClassificationSessionIdTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.view.textclassifier.SelectionEvent;
+import android.view.textclassifier.TextClassificationContext;
+import android.view.textclassifier.TextClassificationManager;
+import android.view.textclassifier.TextClassificationSessionId;
+import android.view.textclassifier.TextClassifier;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+@SmallTest
+public class TextClassificationSessionIdTest {
+
+ @Test
+ public void flattenToString() {
+ TextClassificationManager textClassificationManager =
+ ApplicationProvider.getApplicationContext().getSystemService(
+ TextClassificationManager.class);
+ textClassificationManager.setTextClassifier(TextClassifier.NO_OP);
+ TextClassifier textClassifier = textClassificationManager.createTextClassificationSession(
+ new TextClassificationContext.Builder(
+ "com.pkg",
+ TextClassifier.WIDGET_TYPE_TEXTVIEW)
+ .build());
+ SelectionEvent startedEvent =
+ SelectionEvent.createSelectionStartedEvent(
+ SelectionEvent.INVOCATION_LINK, /* start= */ 10);
+
+ textClassifier.onSelectionEvent(startedEvent);
+ TextClassificationSessionId sessionId = startedEvent.getSessionId();
+
+ assertThat(sessionId).isNotNull();
+ assertThat(sessionId.flattenToString()).isNotEmpty();
+ }
+}
diff --git a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/DirectActionsTest.java b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/DirectActionsTest.java
index 338c361..6c4a3a7 100644
--- a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/DirectActionsTest.java
+++ b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/DirectActionsTest.java
@@ -134,7 +134,6 @@
mActivityControl.finishActivity();
}
}
-
private final class SessionControl {
private @Nullable RemoteCallback mControl;
diff --git a/tests/tests/voicesettings/AndroidManifest.xml b/tests/tests/voicesettings/AndroidManifest.xml
index 5ea6a84..8be0b80 100644
--- a/tests/tests/voicesettings/AndroidManifest.xml
+++ b/tests/tests/voicesettings/AndroidManifest.xml
@@ -23,7 +23,7 @@
<application>
<uses-library android:name="android.test.runner" />
- <activity android:name="com.android.compatibility.common.util.BroadcastTestStartActivity"
+ <activity android:name=".BroadcastTestStartActivity"
android:label="The Target Activity for VoiceSettings CTS Test">
<intent-filter>
<action android:name="android.intent.action.TEST_START_ACTIVITY_ZEN_MODE" />
diff --git a/tests/tests/voicesettings/AndroidTest.xml b/tests/tests/voicesettings/AndroidTest.xml
index 6de903a..11e749e 100644
--- a/tests/tests/voicesettings/AndroidTest.xml
+++ b/tests/tests/voicesettings/AndroidTest.xml
@@ -25,7 +25,6 @@
<option name="test-file-name" value="CtsVoiceSettingsTestCases.apk" />
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
- <option name="run-command" value="settings put secure voice_interaction_service android.voicesettings.service/.MainInteractionService" />
<!-- Close the "turn on battery saver?" dialog, and wait for the broadcast queue to drain. -->
<option name="teardown-command" value="am broadcast --receiver-foreground -a android.intent.action.CLOSE_SYSTEM_DIALOGS" />
diff --git a/tests/tests/voicesettings/src/android/voicesettings/cts/AirplaneModeTest.java b/tests/tests/voicesettings/src/android/voicesettings/cts/AirplaneModeTest.java
index 2ef1999..2992c5b 100644
--- a/tests/tests/voicesettings/src/android/voicesettings/cts/AirplaneModeTest.java
+++ b/tests/tests/voicesettings/src/android/voicesettings/cts/AirplaneModeTest.java
@@ -18,13 +18,15 @@
import static android.provider.Settings.ACTION_VOICE_CONTROL_AIRPLANE_MODE;
-import com.android.compatibility.common.util.BroadcastTestBase;
-import com.android.compatibility.common.util.BroadcastUtils;
+import static com.google.common.truth.Truth.assertThat;
import android.provider.Settings;
-import android.provider.Settings.Global;
import android.util.Log;
+import com.android.compatibility.common.util.BroadcastUtils;
+
+import org.junit.Test;
+
public class AirplaneModeTest extends BroadcastTestBase {
static final String TAG = "AirplaneModeTest";
private static final String VOICE_SETTINGS_PACKAGE = "android.voicesettings.service";
@@ -36,17 +38,12 @@
private static final int AIRPLANE_MODE_IS_ON = 1;
@Override
- protected void setUp() throws Exception {
- super.setUp();
- mContext = getInstrumentation().getTargetContext();
+ protected void customSetup() throws Exception {
mHasFeature = mContext.getPackageManager().hasSystemFeature(FEATURE_VOICE_RECOGNIZERS);
Log.v(TAG, "setUp(): mHasFeature=" + mHasFeature);
}
- public AirplaneModeTest() {
- super();
- }
-
+ @Test
public void testAll() throws Exception {
if (!mHasFeature) {
Log.i(TAG, "The device doesn't support feature: " + FEATURE_VOICE_RECOGNIZERS);
@@ -96,7 +93,7 @@
// verify the test results
int mode = getMode();
Log.i(TAG, "After testing, AIRPLANE_MODE is set to: " + mode);
- assertEquals(expectedMode, mode);
+ assertThat(mode).isEqualTo(expectedMode);
Log.i(TAG, "Successfully Tested: " + test);
return true;
}
diff --git a/tests/tests/voicesettings/src/android/voicesettings/cts/BatterySaverModeTest.java b/tests/tests/voicesettings/src/android/voicesettings/cts/BatterySaverModeTest.java
index 759d82e..181c4ba 100644
--- a/tests/tests/voicesettings/src/android/voicesettings/cts/BatterySaverModeTest.java
+++ b/tests/tests/voicesettings/src/android/voicesettings/cts/BatterySaverModeTest.java
@@ -18,13 +18,14 @@
import static android.provider.Settings.ACTION_VOICE_CONTROL_BATTERY_SAVER_MODE;
-import com.android.compatibility.common.util.BroadcastTestBase;
-import com.android.compatibility.common.util.BroadcastUtils;
-
import android.content.Context;
import android.os.PowerManager;
import android.util.Log;
+import com.android.compatibility.common.util.BroadcastUtils;
+
+import org.junit.Test;
+
public class BatterySaverModeTest extends BroadcastTestBase {
static final String TAG = "BatterySaverModeTest";
private static final String VOICE_SETTINGS_PACKAGE = "android.voicesettings.service";
@@ -32,17 +33,12 @@
"android.voicesettings.service.VoiceInteractionMain";
protected static final String FEATURE_VOICE_RECOGNIZERS = "android.software.voice_recognizers";
- public BatterySaverModeTest() {
- super();
- }
-
@Override
- protected void setUp() throws Exception {
- super.setUp();
- mContext = getInstrumentation().getTargetContext();
+ protected void customSetup() throws Exception {
mHasFeature = mContext.getPackageManager().hasSystemFeature(FEATURE_VOICE_RECOGNIZERS);
}
+ @Test
public void testAll() throws Exception {
if (!mHasFeature) {
Log.i(TAG, "The device doesn't support feature: " + FEATURE_VOICE_RECOGNIZERS);
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/BroadcastTestBase.java b/tests/tests/voicesettings/src/android/voicesettings/cts/BroadcastTestBase.java
similarity index 66%
rename from common/device-side/util-axt/src/com/android/compatibility/common/util/BroadcastTestBase.java
rename to tests/tests/voicesettings/src/android/voicesettings/cts/BroadcastTestBase.java
index 7500050..95e9433 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/BroadcastTestBase.java
+++ b/tests/tests/voicesettings/src/android/voicesettings/cts/BroadcastTestBase.java
@@ -14,7 +14,13 @@
* limitations under the License.
*/
-package com.android.compatibility.common.util;
+package android.voicesettings.cts;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -23,18 +29,31 @@
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Bundle;
-import android.test.ActivityInstrumentationTestCase2;
+import android.provider.Settings;
import android.util.Log;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.compatibility.common.util.BroadcastUtils;
+import com.android.compatibility.common.util.SettingsStateChangerRule;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.rules.RuleChain;
+import org.junit.runner.RunWith;
+
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
-public class BroadcastTestBase extends ActivityInstrumentationTestCase2<
- BroadcastTestStartActivity> {
+@RunWith(AndroidJUnit4.class)
+public abstract class BroadcastTestBase {
+
static final String TAG = "BroadcastTestBase";
protected static final int TIMEOUT_MS = 20 * 1000;
- protected Context mContext;
+ protected final Context mContext = getInstrumentation().getTargetContext();
protected Bundle mResultExtras;
private CountDownLatch mLatch;
protected ActivityDoneReceiver mActivityDoneReceiver = null;
@@ -42,20 +61,29 @@
private BroadcastUtils.TestcaseType mTestCaseType;
protected boolean mHasFeature;
- public BroadcastTestBase() {
- super(BroadcastTestStartActivity.class);
- }
+ private final SettingsStateChangerRule mServiceSetterRule = new SettingsStateChangerRule(
+ mContext, Settings.Secure.VOICE_INTERACTION_SERVICE,
+ "android.voicesettings.service/.MainInteractionService");
+ private final ActivityTestRule<BroadcastTestStartActivity> mActivityTestRule =
+ new ActivityTestRule<>(BroadcastTestStartActivity.class, false, false);
- @Override
- protected void setUp() throws Exception {
- super.setUp();
+ @Rule
+ public final RuleChain mgRules = RuleChain
+ .outerRule(mServiceSetterRule)
+ .around(mActivityTestRule);
+
+ @Before
+ public void setUp() throws Exception {
mHasFeature = false;
+
+ customSetup();
}
- @Override
- protected void tearDown() throws Exception {
+ @After
+ public final void tearDown() throws Exception {
Log.v(TAG, getClass().getSimpleName() + ".tearDown(): hasFeature=" + mHasFeature
+ " receiver=" + mActivityDoneReceiver);
+
if (mHasFeature && mActivityDoneReceiver != null) {
try {
mContext.unregisterReceiver(mActivityDoneReceiver);
@@ -66,13 +94,18 @@
}
mActivityDoneReceiver = null;
}
- super.tearDown();
+ }
+
+ /**
+ * Test-specific setup - doesn't need to call {@code super} neither use <code>@Before</code>.
+ */
+ protected void customSetup() throws Exception {
}
protected boolean isIntentSupported(String intentStr) {
Intent intent = new Intent(intentStr);
final PackageManager manager = mContext.getPackageManager();
- assertNotNull(manager);
+ assertThat(manager).isNotNull();
if (manager.resolveActivity(intent, 0) == null) {
Log.i(TAG, "No Activity found for the intent: " + intentStr);
return false;
@@ -83,13 +116,12 @@
protected void startTestActivity(String intentSuffix) {
Intent intent = new Intent();
intent.setAction("android.intent.action.TEST_START_ACTIVITY_" + intentSuffix);
- intent.setComponent(new ComponentName(getInstrumentation().getContext(),
- BroadcastTestStartActivity.class));
- setActivityIntent(intent);
- mActivity = getActivity();
+ intent.setComponent(new ComponentName(mContext, BroadcastTestStartActivity.class));
+ mActivity = mActivityTestRule.launchActivity(intent);
}
- protected void registerBroadcastReceiver(BroadcastUtils.TestcaseType testCaseType) throws Exception {
+ protected void registerBroadcastReceiver(BroadcastUtils.TestcaseType testCaseType)
+ throws Exception {
mTestCaseType = testCaseType;
mLatch = new CountDownLatch(1);
mActivityDoneReceiver = new ActivityDoneReceiver();
@@ -98,7 +130,7 @@
}
protected boolean startTestAndWaitForBroadcast(BroadcastUtils.TestcaseType testCaseType,
- String pkg, String cls) throws Exception {
+ String pkg, String cls) throws Exception {
Log.i(TAG, "Begin Testing: " + testCaseType);
registerBroadcastReceiver(testCaseType);
mActivity.startTest(testCaseType.toString(), pkg, cls);
@@ -114,7 +146,7 @@
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(
BroadcastUtils.BROADCAST_INTENT +
- BroadcastTestBase.this.mTestCaseType.toString())) {
+ BroadcastTestBase.this.mTestCaseType.toString())) {
Bundle extras = intent.getExtras();
Log.i(TAG, "received_broadcast for " + BroadcastUtils.toBundleString(extras));
BroadcastTestBase.this.mResultExtras = extras;
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/BroadcastTestStartActivity.java b/tests/tests/voicesettings/src/android/voicesettings/cts/BroadcastTestStartActivity.java
similarity index 95%
rename from common/device-side/util-axt/src/com/android/compatibility/common/util/BroadcastTestStartActivity.java
rename to tests/tests/voicesettings/src/android/voicesettings/cts/BroadcastTestStartActivity.java
index 4b3e85d..163c2e4 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/BroadcastTestStartActivity.java
+++ b/tests/tests/voicesettings/src/android/voicesettings/cts/BroadcastTestStartActivity.java
@@ -14,14 +14,16 @@
* limitations under the License.
*/
-package com.android.compatibility.common.util;
+package android.voicesettings.cts;
import android.app.Activity;
-import android.content.Intent;
import android.content.ComponentName;
+import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
+import com.android.compatibility.common.util.BroadcastUtils;
+
public class BroadcastTestStartActivity extends Activity {
static final String TAG = "BroadcastTestStartActivity";
diff --git a/tests/tests/voicesettings/src/android/voicesettings/cts/ZenModeTest.java b/tests/tests/voicesettings/src/android/voicesettings/cts/ZenModeTest.java
index e01f3b9..5bb2f8c 100644
--- a/tests/tests/voicesettings/src/android/voicesettings/cts/ZenModeTest.java
+++ b/tests/tests/voicesettings/src/android/voicesettings/cts/ZenModeTest.java
@@ -20,13 +20,15 @@
import static android.provider.Settings.EXTRA_DO_NOT_DISTURB_MODE_ENABLED;
import static android.provider.Settings.EXTRA_DO_NOT_DISTURB_MODE_MINUTES;
-import com.android.compatibility.common.util.BroadcastTestBase;
-import com.android.compatibility.common.util.BroadcastUtils;
+import static com.google.common.truth.Truth.assertThat;
import android.provider.Settings;
-import android.provider.Settings.Global;
import android.util.Log;
+import com.android.compatibility.common.util.BroadcastUtils;
+
+import org.junit.Test;
+
public class ZenModeTest extends BroadcastTestBase {
static final String TAG = "ZenModeTest";
private static final String VOICE_SETTINGS_PACKAGE = "android.voicesettings.service";
@@ -35,9 +37,7 @@
protected static final String FEATURE_VOICE_RECOGNIZERS = "android.software.voice_recognizers";
@Override
- protected void setUp() throws Exception {
- super.setUp();
- mContext = getInstrumentation().getTargetContext();
+ protected void customSetup() throws Exception {
mHasFeature = mContext.getPackageManager().hasSystemFeature(FEATURE_VOICE_RECOGNIZERS);
}
@@ -47,10 +47,7 @@
private static final int ZEN_MODE_IS_OFF = 0;
private static final int ZEN_MODE_IS_ALARMS = 3;
- public ZenModeTest() {
- super();
- }
-
+ @Test
public void testAll() throws Exception {
if (!mHasFeature) {
Log.i(TAG, "The device doesn't support feature: " + FEATURE_VOICE_RECOGNIZERS);
@@ -99,15 +96,15 @@
// verify the test results
int mode = getMode();
Log.i(TAG, "After testing, zen-mode is set to: " + mode);
- assertEquals(expectedMode, mode);
+ assertThat(mode).isEqualTo(expectedMode);
Log.i(TAG, "results_received: " + BroadcastUtils.toBundleString(mResultExtras));
- assertNotNull(mResultExtras);
+ assertThat(mResultExtras).isNotNull();
if (expectedMode == ZEN_MODE_IS_ALARMS) {
- assertTrue(mResultExtras.getBoolean(EXTRA_DO_NOT_DISTURB_MODE_ENABLED));
- assertEquals(BroadcastUtils.NUM_MINUTES_FOR_ZENMODE,
- mResultExtras.getInt(EXTRA_DO_NOT_DISTURB_MODE_MINUTES));
+ assertThat(mResultExtras.getBoolean(EXTRA_DO_NOT_DISTURB_MODE_ENABLED)).isTrue();
+ assertThat(mResultExtras.getInt(EXTRA_DO_NOT_DISTURB_MODE_MINUTES)).isEqualTo(
+ BroadcastUtils.NUM_MINUTES_FOR_ZENMODE);
} else {
- assertFalse(mResultExtras.getBoolean(EXTRA_DO_NOT_DISTURB_MODE_ENABLED));
+ assertThat(mResultExtras.getBoolean(EXTRA_DO_NOT_DISTURB_MODE_ENABLED)).isFalse();
}
Log.i(TAG, "Successfully Tested: " + test);
return true;
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java
index ec39a5c..8c3b252 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java
@@ -902,8 +902,11 @@
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
- assertTrue(mOnPageStartedCalled);
- assertTrue(mOnLoadResourceCalled);
+ // TODO(ntfschr): propagate these exceptions to the instrumentation thread.
+ assertTrue("Expected onPageStarted to be called before onPageFinished",
+ mOnPageStartedCalled);
+ assertTrue("Expected onLoadResource to be called before onPageFinished",
+ mOnLoadResourceCalled);
mOnPageFinishedCalled = true;
}
diff --git a/tests/tests/widget/AndroidTest.xml b/tests/tests/widget/AndroidTest.xml
index fd9afe8..8cd40dc 100644
--- a/tests/tests/widget/AndroidTest.xml
+++ b/tests/tests/widget/AndroidTest.xml
@@ -26,5 +26,6 @@
<option name="package" value="android.widget.cts" />
<option name="runtime-hint" value="11m55s" />
<option name="hidden-api-checks" value="false" />
+ <option name="instrumentation-arg" key="thisisignored" value="thisisignored --no-window-animation" />
</test>
</configuration>
diff --git a/tests/tests/widget/OWNERS b/tests/tests/widget/OWNERS
new file mode 100644
index 0000000..12f176d
--- /dev/null
+++ b/tests/tests/widget/OWNERS
@@ -0,0 +1,5 @@
+# Bug component: 25700
+adamp@google.com
+mount@google.com
+shepshapard@google.com
+clarabayarri@google.com
diff --git a/tests/tests/widget/res/layout/listview_layout.xml b/tests/tests/widget/res/layout/listview_layout.xml
index 3094a89..79669c1 100644
--- a/tests/tests/widget/res/layout/listview_layout.xml
+++ b/tests/tests/widget/res/layout/listview_layout.xml
@@ -16,6 +16,7 @@
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent">
diff --git a/tests/tests/widget/res/layout/widget_attribute_layout.xml b/tests/tests/widget/res/layout/widget_attribute_layout.xml
index 1cc5517..872b0eb 100644
--- a/tests/tests/widget/res/layout/widget_attribute_layout.xml
+++ b/tests/tests/widget/res/layout/widget_attribute_layout.xml
@@ -47,4 +47,11 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/ExplicitStyle1" />
+
+ <ViewAnimator
+ android:id="@+id/viewAnimator"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:inAnimation="@android:anim/slide_in_left"
+ android:outAnimation="@android:anim/slide_out_right" />
</LinearLayout>
\ No newline at end of file
diff --git a/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java b/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java
index 612b233..0a57c21 100644
--- a/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java
@@ -45,6 +45,7 @@
import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.Insets;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.text.Editable;
@@ -58,6 +59,7 @@
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
+import android.view.WindowInsets;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.AdapterView;
@@ -128,7 +130,7 @@
private static final float DELTA = 0.001f;
@Before
- public void setup() throws Exception {
+ public void setup() throws Throwable {
mInstrumentation = InstrumentationRegistry.getInstrumentation();
final Activity activity = mActivityRule.getActivity();
// Always use the activity context
@@ -146,6 +148,17 @@
android.R.layout.simple_list_item_1, COUNTRY_LIST);
mListView = (ListView) activity.findViewById(R.id.listview_default);
+
+ // Full-height drag gestures clash with system navigation gestures (such as
+ // swipe up from the bottom of the screen to go home). Get the system
+ // gesture insets and apply bottom padding on the entire content so
+ // that our own drag gestures are processed within the activity.
+ WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mListView, () -> {
+ ViewGroup content = activity.findViewById(R.id.content);
+ WindowInsets rootWindowInsets = content.getRootWindowInsets();
+ Insets systemGestureInsets = rootWindowInsets.getSystemGestureInsets();
+ content.setPadding(0, 0, 0, systemGestureInsets.bottom);
+ });
}
private boolean isWatch() {
diff --git a/tests/tests/widget/src/android/widget/cts/MagnifierTest.java b/tests/tests/widget/src/android/widget/cts/MagnifierTest.java
index a31963c..0264665 100644
--- a/tests/tests/widget/src/android/widget/cts/MagnifierTest.java
+++ b/tests/tests/widget/src/android/widget/cts/MagnifierTest.java
@@ -728,14 +728,15 @@
WidgetTestUtils.runOnMainAndLayoutSync(mActivityRule, () -> {
mActivity.setContentView(R.layout.magnifier_activity_centered_surfaceview_layout);
}, false /* forceLayout */);
- final View view = mActivity.findViewById(R.id.magnifier_centered_view);
- WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, view, () -> {
+ WidgetTestUtils.runOnMainAndLayoutSync(mActivityRule, () -> {
// Draw something in the SurfaceView for the Magnifier to copy.
+ final View view = mActivity.findViewById(R.id.magnifier_centered_view);
final SurfaceHolder surfaceHolder = ((SurfaceView) view).getHolder();
final Canvas canvas = surfaceHolder.lockHardwareCanvas();
canvas.drawColor(Color.BLUE);
surfaceHolder.unlockCanvasAndPost(canvas);
- });
+ }, false /* forceLayout */);
+ final View view = mActivity.findViewById(R.id.magnifier_centered_view);
final Magnifier.Builder builder = new Magnifier.Builder(view)
.setSize(100, 100)
.setInitialZoom(5f) /* 20x20 source size */
diff --git a/tests/tests/widget/src/android/widget/cts/SeekBarTest.java b/tests/tests/widget/src/android/widget/cts/SeekBarTest.java
index 7cd082b..50405dc 100644
--- a/tests/tests/widget/src/android/widget/cts/SeekBarTest.java
+++ b/tests/tests/widget/src/android/widget/cts/SeekBarTest.java
@@ -26,8 +26,11 @@
import android.app.Activity;
import android.app.Instrumentation;
+import android.graphics.Rect;
import android.os.SystemClock;
import android.view.MotionEvent;
+import android.view.View;
+import android.view.WindowInsets;
import android.widget.SeekBar;
import androidx.test.InstrumentationRegistry;
@@ -42,6 +45,9 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Test {@link SeekBar}.
*/
@@ -57,10 +63,37 @@
new ActivityTestRule<>(SeekBarCtsActivity.class);
@Before
- public void setup() {
+ public void setup() throws Throwable {
mInstrumentation = InstrumentationRegistry.getInstrumentation();
mActivity = mActivityRule.getActivity();
- mSeekBar = (SeekBar) mActivity.findViewById(R.id.seekBar);
+ mSeekBar = mActivity.findViewById(R.id.seekBar);
+ if (mSeekBar.isAttachedToWindow()) {
+ updateExclusionRects();
+ } else {
+ mSeekBar.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(View view) {
+ mSeekBar.removeOnAttachStateChangeListener(this);
+ updateExclusionRects();
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View view) {
+ }
+ });
+ }
+ }
+
+ private void updateExclusionRects() {
+ // "Mark" the left edge of our seek bar to be excluded from system gestures.
+ // This does not need to be RTL-aware since the logic in the change listener
+ // always injects the events from left to right.
+ WindowInsets rootWindowInsets = mSeekBar.getRootWindowInsets();
+ List<Rect> exclusion = new ArrayList<>();
+ exclusion.add(new Rect(0, 0,
+ rootWindowInsets.getSystemGestureInsets().left,
+ mSeekBar.getHeight()));
+ mSeekBar.setSystemGestureExclusionRects(exclusion);
}
@Test
diff --git a/tests/tests/widget/src/android/widget/cts/TextClockTest.java b/tests/tests/widget/src/android/widget/cts/TextClockTest.java
index 9a41bbf..9438ed1 100644
--- a/tests/tests/widget/src/android/widget/cts/TextClockTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TextClockTest.java
@@ -104,6 +104,16 @@
}
}
+ // If the time was already set to 12, we want it to start at locale-specified
+ if (mDefaultTime1224 != null) {
+ final CountDownLatch changeDefault = registerForChanges(Settings.System.TIME_12_24);
+ mActivityRule.runOnUiThread(() -> {
+ Settings.System.putString(resolver, Settings.System.TIME_12_24, null);
+ });
+ assertTrue(changeDefault.await(1, TimeUnit.SECONDS));
+ }
+
+ // Change to 12-hour mode
final CountDownLatch change12 = registerForChanges(Settings.System.TIME_12_24);
mActivityRule.runOnUiThread(() -> {
Settings.System.putInt(resolver, Settings.System.TIME_12_24, 12);
@@ -124,6 +134,7 @@
return ok.value;
});
+ // Change to 24-hour mode
final CountDownLatch change24 = registerForChanges(Settings.System.TIME_12_24);
mActivityRule.runOnUiThread(() -> {
Settings.System.putInt(resolver, Settings.System.TIME_12_24, 24);
diff --git a/tests/tests/widget/src/android/widget/cts/TextViewTest.java b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
index 01d5cd8..86e674e 100644
--- a/tests/tests/widget/src/android/widget/cts/TextViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
@@ -169,6 +169,7 @@
import java.io.IOException;
import java.lang.annotation.Retention;
import java.util.Arrays;
+import java.util.List;
import java.util.Locale;
/**
@@ -7149,6 +7150,106 @@
TextUtils.equals(hintText, info.getHintText()));
}
+ @UiThreadTest
+ @Test
+ public void testOnInitializeA11yNodeInfo_removesClickabilityWithLinkMovementMethod() {
+ mTextView = findTextView(R.id.textview_text);
+ mTextView.setMovementMethod(new LinkMovementMethod());
+
+ assertTrue("clickable should be true", mTextView.isClickable());
+ assertFalse("View should not have onClickListeners", mTextView.hasOnClickListeners());
+ assertTrue("longClickable should be true", mTextView.isLongClickable());
+ assertFalse("View should not have onLongClickListeners",
+ mTextView.hasOnLongClickListeners());
+
+ final AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
+ mTextView.onInitializeAccessibilityNodeInfo(info);
+ List<AccessibilityNodeInfo.AccessibilityAction> actionList = info.getActionList();
+ assertFalse("info's isClickable should be false", info.isClickable());
+ assertFalse("info should not have ACTION_CLICK",
+ actionList.contains(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK));
+ assertFalse("info's isLongClickable should be false",
+ info.isLongClickable());
+ assertFalse("info should not have ACTION_LONG_CLICK",
+ actionList.contains(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK));
+ }
+
+ @UiThreadTest
+ @Test
+ public void testOnInitializeA11yNodeInfo_keepsClickabilityWithMovementMethod() {
+ mTextView = findTextView(R.id.textview_text);
+ mTextView.setMovementMethod(new ArrowKeyMovementMethod());
+
+ assertTrue("clickable should be true", mTextView.isClickable());
+ assertFalse("View should not have onClickListeners", mTextView.hasOnClickListeners());
+ assertTrue("longClickable should be false", mTextView.isLongClickable());
+ assertFalse("View should not have onLongClickListeners",
+ mTextView.hasOnLongClickListeners());
+
+ final AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
+ mTextView.onInitializeAccessibilityNodeInfo(info);
+ List<AccessibilityNodeInfo.AccessibilityAction> actionList = info.getActionList();
+ assertTrue("info's isClickable should be true", info.isClickable());
+ assertTrue("info should have ACTION_CLICK",
+ actionList.contains(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK));
+ assertTrue("info's isLongClickable should be true",
+ info.isLongClickable());
+ assertTrue("info should have ACTION_LONG_CLICK",
+ actionList.contains(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK));
+ }
+
+ @UiThreadTest
+ @Test
+ public void testOnInitializeA11yNodeInfo_keepsClickabilityWithOnClickListener() {
+ mTextView = findTextView(R.id.textview_text);
+ mTextView.setMovementMethod(new LinkMovementMethod());
+
+ assertTrue("clickable should be true", mTextView.isClickable());
+ assertFalse("View should not have onClickListeners", mTextView.hasOnClickListeners());
+ assertTrue("longClickable should be true", mTextView.isLongClickable());
+ assertFalse("View should not have onLongClickListeners",
+ mTextView.hasOnLongClickListeners());
+
+ mTextView.setOnClickListener(mock(View.OnClickListener.class));
+
+ final AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
+ mTextView.onInitializeAccessibilityNodeInfo(info);
+ List<AccessibilityNodeInfo.AccessibilityAction> actionList = info.getActionList();
+ assertTrue("info's isClickable should be true", info.isClickable());
+ assertTrue("info should have ACTION_CLICK",
+ actionList.contains(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK));
+ assertFalse("info's isLongClickable should not be true",
+ info.isLongClickable());
+ assertFalse("info should have ACTION_LONG_CLICK",
+ actionList.contains(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK));
+ }
+
+ @UiThreadTest
+ @Test
+ public void testOnInitializeA11yNodeInfo_keepsLongClickabilityWithOnLongClickListener() {
+ mTextView = findTextView(R.id.textview_text);
+ mTextView.setMovementMethod(new LinkMovementMethod());
+
+ assertTrue("clickable should be true", mTextView.isClickable());
+ assertFalse("View should not have onClickListeners", mTextView.hasOnClickListeners());
+ assertTrue("longClickable should be true", mTextView.isLongClickable());
+ assertFalse("View should not have onLongClickListeners",
+ mTextView.hasOnLongClickListeners());
+
+ mTextView.setOnLongClickListener(mock(View.OnLongClickListener.class));
+
+ final AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
+ mTextView.onInitializeAccessibilityNodeInfo(info);
+ List<AccessibilityNodeInfo.AccessibilityAction> actionList = info.getActionList();
+ assertFalse("info's isClickable should be false", info.isClickable());
+ assertFalse("info should not have ACTION_CLICK",
+ actionList.contains(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK));
+ assertTrue("info's isLongClickable should be true",
+ info.isLongClickable());
+ assertTrue("info should have ACTION_LONG_CLICK",
+ actionList.contains(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK));
+ }
+
@Test
public void testAutosizeWithMaxLines_shouldNotThrowException() throws Throwable {
// the layout contains an instance of CustomTextViewWithTransformationMethod
diff --git a/tests/tests/widget/src/android/widget/cts/WidgetAttributeTest.kt b/tests/tests/widget/src/android/widget/cts/WidgetAttributeTest.kt
index 5b41755..2296ae2 100644
--- a/tests/tests/widget/src/android/widget/cts/WidgetAttributeTest.kt
+++ b/tests/tests/widget/src/android/widget/cts/WidgetAttributeTest.kt
@@ -17,10 +17,6 @@
package android.widget.cts
import android.app.Activity
-import androidx.test.InstrumentationRegistry
-import androidx.test.filters.MediumTest
-import androidx.test.rule.ActivityTestRule
-import androidx.test.runner.AndroidJUnit4
import android.support.test.uiautomator.UiDevice
import android.view.LayoutInflater
import android.widget.LinearLayout
@@ -28,6 +24,11 @@
import android.widget.Switch
import android.widget.TextView
import android.widget.Toolbar
+import android.widget.ViewAnimator
+import androidx.test.InstrumentationRegistry
+import androidx.test.filters.MediumTest
+import androidx.test.rule.ActivityTestRule
+import androidx.test.runner.AndroidJUnit4
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Before
@@ -113,14 +114,12 @@
assertEquals(R.style.ExplicitStyle1, stackTextView1textSize[1])
assertEquals(R.style.ParentOfExplicitStyle1, stackTextView1textSize[2])
assertEquals(R.style.TextViewWithoutColorAndAppearance, stackTextView1textSize[3])
- val stackTextView1textColorHighlight =
- textview1.getAttributeResolutionStack(android.R.attr.textColorHighlight)
- assertEquals(5, stackTextView1textColorHighlight.size.toLong())
- assertEquals(R.layout.widget_attribute_layout, stackTextView1textColorHighlight[0])
- assertEquals(R.style.ExplicitStyle1, stackTextView1textColorHighlight[1])
- assertEquals(R.style.ParentOfExplicitStyle1, stackTextView1textColorHighlight[2])
- assertEquals(android.R.style.Widget_Material_TextView, stackTextView1textColorHighlight[3])
- assertEquals(android.R.style.Widget_TextView, stackTextView1textColorHighlight[4])
+
+ val viewAnimator = rootView.findViewById<ViewAnimator>(R.id.viewAnimator)
+ val viewAnimatorOutAnimation =
+ viewAnimator.getAttributeResolutionStack(android.R.attr.outAnimation)
+ assertEquals(1, viewAnimatorOutAnimation.size.toLong())
+ assertEquals(R.layout.widget_attribute_layout, viewAnimatorOutAnimation[0])
}
@Test
diff --git a/tests/vr/OWNERS b/tests/vr/OWNERS
new file mode 100644
index 0000000..b0936ba
--- /dev/null
+++ b/tests/vr/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 189591
+krzysio@google.com
diff --git a/tools/release-parser/src/com/android/cts/releaseparser/RcParser.java b/tools/release-parser/src/com/android/cts/releaseparser/RcParser.java
index a7ef925..e8c8afe 100644
--- a/tools/release-parser/src/com/android/cts/releaseparser/RcParser.java
+++ b/tools/release-parser/src/com/android/cts/releaseparser/RcParser.java
@@ -66,8 +66,13 @@
Map<String, Integer> dependencies = new HashMap<>();
for (Service service : mServices) {
// skip /, e.g. /system/bin/sh
- String file = service.getFile().substring(1);
- dependencies.put(file, 1);
+ try {
+ String file = service.getFile().substring(1);
+ dependencies.put(file, 1);
+ } catch (Exception e) {
+ System.err.println(
+ "err Service " + service.getName() + " File:" + service.getFile());
+ }
}
for (String importRc : mImportRc) {
@@ -140,7 +145,7 @@
private void parseService(String line, BufferedReader buffReader) throws IOException {
Service.Builder serviceBld = Service.newBuilder();
- String[] phases = line.split(" ");
+ String[] phases = line.split("\\s+");
serviceBld.setName(phases[1]);
serviceBld.setFile(phases[2]);
if (phases.length > 3) {
diff --git a/tools/release-parser/src/com/android/cts/releaseparser/ReleaseParser.java b/tools/release-parser/src/com/android/cts/releaseparser/ReleaseParser.java
index 5d4b9b4..13a01bf 100644
--- a/tools/release-parser/src/com/android/cts/releaseparser/ReleaseParser.java
+++ b/tools/release-parser/src/com/android/cts/releaseparser/ReleaseParser.java
@@ -122,80 +122,84 @@
List<Entry> entryList = new ArrayList<Entry>();
// walks through all files
- for (File file : fileList) {
- if (file.isFile()) {
- String fileRelativePath =
- mRootPath.relativize(Paths.get(file.getAbsolutePath())).toString();
- FileParser fParser = FileParser.getParser(file);
- Entry.Builder fileEntryBuilder = fParser.getFileEntryBuilder();
- fileEntryBuilder.setRelativePath(fileRelativePath);
+ System.out.println("Parsing: " + folderRelativePath);
+ // skip if it's a symbolic link to a folder
+ if (fileList != null) {
+ for (File file : fileList) {
+ if (file.isFile()) {
+ String fileRelativePath =
+ mRootPath.relativize(Paths.get(file.getAbsolutePath())).toString();
+ FileParser fParser = FileParser.getParser(file);
+ Entry.Builder fileEntryBuilder = fParser.getFileEntryBuilder();
+ fileEntryBuilder.setRelativePath(fileRelativePath);
- if (folderRelativePath.isEmpty()) {
- fileEntryBuilder.setParentFolder(ROOT_FOLDER_TAG);
- } else {
- fileEntryBuilder.setParentFolder(folderRelativePath);
- }
+ if (folderRelativePath.isEmpty()) {
+ fileEntryBuilder.setParentFolder(ROOT_FOLDER_TAG);
+ } else {
+ fileEntryBuilder.setParentFolder(folderRelativePath);
+ }
- Entry.EntryType eType = fParser.getType();
- switch (eType) {
- case TEST_SUITE_TRADEFED:
- mRelContentBuilder.setTestSuiteTradefed(fileRelativePath);
- TestSuiteTradefedParser tstParser = (TestSuiteTradefedParser) fParser;
- // get [cts]-known-failures.xml
- mRelContentBuilder.addAllKnownFailures(tstParser.getKnownFailureList());
- mRelContentBuilder.setName(tstParser.getName());
- mRelContentBuilder.setFullname(tstParser.getFullName());
- mRelContentBuilder.setBuildNumber(tstParser.getBuildNumber());
- mRelContentBuilder.setTargetArch(tstParser.getTargetArch());
- mRelContentBuilder.setVersion(tstParser.getVersion());
- mRelContentBuilder.setReleaseType(ReleaseType.TEST_SUITE);
- break;
- case BUILD_PROP:
- BuildPropParser bpParser = (BuildPropParser) fParser;
- try {
- mRelContentBuilder.setReleaseType(ReleaseType.DEVICE_BUILD);
- mRelContentBuilder.setName(bpParser.getName());
- mRelContentBuilder.setFullname(bpParser.getFullName());
- mRelContentBuilder.setBuildNumber(bpParser.getBuildNumber());
- mRelContentBuilder.setVersion(bpParser.getVersion());
- mRelContentBuilder.putAllProperties(bpParser.getProperties());
- } catch (Exception e) {
- System.err.println(
- "No product name, version & etc. in "
- + file.getAbsoluteFile()
- + ", err:"
- + e.getMessage());
- }
- break;
- default:
- }
- // System.err.println("File:" + file.getAbsoluteFile());
- if (fParser.getDependencies() != null) {
- fileEntryBuilder.addAllDependencies(fParser.getDependencies());
- }
- if (fParser.getDynamicLoadingDependencies() != null) {
- fileEntryBuilder.addAllDynamicLoadingDependencies(
- fParser.getDynamicLoadingDependencies());
- }
- fileEntryBuilder.setAbiBits(fParser.getAbiBits());
- fileEntryBuilder.setAbiArchitecture(fParser.getAbiArchitecture());
+ Entry.EntryType eType = fParser.getType();
+ switch (eType) {
+ case TEST_SUITE_TRADEFED:
+ mRelContentBuilder.setTestSuiteTradefed(fileRelativePath);
+ TestSuiteTradefedParser tstParser = (TestSuiteTradefedParser) fParser;
+ // get [cts]-known-failures.xml
+ mRelContentBuilder.addAllKnownFailures(tstParser.getKnownFailureList());
+ mRelContentBuilder.setName(tstParser.getName());
+ mRelContentBuilder.setFullname(tstParser.getFullName());
+ mRelContentBuilder.setBuildNumber(tstParser.getBuildNumber());
+ mRelContentBuilder.setTargetArch(tstParser.getTargetArch());
+ mRelContentBuilder.setVersion(tstParser.getVersion());
+ mRelContentBuilder.setReleaseType(ReleaseType.TEST_SUITE);
+ break;
+ case BUILD_PROP:
+ BuildPropParser bpParser = (BuildPropParser) fParser;
+ try {
+ mRelContentBuilder.setReleaseType(ReleaseType.DEVICE_BUILD);
+ mRelContentBuilder.setName(bpParser.getName());
+ mRelContentBuilder.setFullname(bpParser.getFullName());
+ mRelContentBuilder.setBuildNumber(bpParser.getBuildNumber());
+ mRelContentBuilder.setVersion(bpParser.getVersion());
+ mRelContentBuilder.putAllProperties(bpParser.getProperties());
+ } catch (Exception e) {
+ System.err.println(
+ "No product name, version & etc. in "
+ + file.getAbsoluteFile()
+ + ", err:"
+ + e.getMessage());
+ }
+ break;
+ default:
+ }
+ // System.err.println("File:" + file.getAbsoluteFile());
+ if (fParser.getDependencies() != null) {
+ fileEntryBuilder.addAllDependencies(fParser.getDependencies());
+ }
+ if (fParser.getDynamicLoadingDependencies() != null) {
+ fileEntryBuilder.addAllDynamicLoadingDependencies(
+ fParser.getDynamicLoadingDependencies());
+ }
+ fileEntryBuilder.setAbiBits(fParser.getAbiBits());
+ fileEntryBuilder.setAbiArchitecture(fParser.getAbiArchitecture());
- Entry fEntry = fileEntryBuilder.build();
- entryList.add(fEntry);
- mEntries.put(fEntry.getRelativePath(), fEntry);
- folderSize += file.length();
- } else if (file.isDirectory()) {
- // Checks subfolders
- Entry.Builder subFolderEntry = parseFolder(file.getAbsolutePath());
- if (folderRelativePath.isEmpty()) {
- subFolderEntry.setParentFolder(ROOT_FOLDER_TAG);
- } else {
- subFolderEntry.setParentFolder(folderRelativePath);
+ Entry fEntry = fileEntryBuilder.build();
+ entryList.add(fEntry);
+ mEntries.put(fEntry.getRelativePath(), fEntry);
+ folderSize += file.length();
+ } else if (file.isDirectory()) {
+ // Checks subfolders
+ Entry.Builder subFolderEntry = parseFolder(file.getAbsolutePath());
+ if (folderRelativePath.isEmpty()) {
+ subFolderEntry.setParentFolder(ROOT_FOLDER_TAG);
+ } else {
+ subFolderEntry.setParentFolder(folderRelativePath);
+ }
+ Entry sfEntry = subFolderEntry.build();
+ entryList.add(sfEntry);
+ mEntries.put(sfEntry.getRelativePath(), sfEntry);
+ folderSize += sfEntry.getSize();
}
- Entry sfEntry = subFolderEntry.build();
- entryList.add(sfEntry);
- mEntries.put(sfEntry.getRelativePath(), sfEntry);
- folderSize += sfEntry.getSize();
}
}
folderEntry.setName(folderRelativePath);
diff --git a/tools/vm-tests-tf/OWNERS b/tools/vm-tests-tf/OWNERS
new file mode 100644
index 0000000..29fea99
--- /dev/null
+++ b/tools/vm-tests-tf/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 86431
+include /hostsidetests/classloaders/OWNERS