Merge "Merge "Remove unused dasm tool" am: 2931e87c70 am: f0e5906924 am: 134136a435" into qt-r1-dev-plus-aosp am: d32273e99e
am: 1248b80504
Change-Id: I9424f159511651c681ea7b8ca4b5d1daec994571
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..24fae6d 100644
--- a/apps/CameraITS/pymodules/its/device.py
+++ b/apps/CameraITS/pymodules/its/device.py
@@ -320,6 +320,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.
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..a9d7601 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
@@ -51,21 +49,62 @@
VGA_HEIGHT = 480
VGA_WIDTH = 640
+# 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
NOT_YET_MANDATED = {
'scene0': [
'test_test_patterns',
'test_tonemap_curve'
],
- 'scene1': [
+ 'scene1_1': [
'test_ae_precapture_trigger',
'test_channel_saturation'
],
- 'scene2': [
+ 'scene1_2': [],
+ 'scene2_a': [
'test_auto_per_frame_control'
],
- 'scene2b': [],
- 'scene2c': [],
+ 'scene2_b': [],
+ 'scene2_c': [],
'scene3': [],
'scene4': [],
'scene5': [],
@@ -80,19 +119,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 +144,41 @@
]
}
+# 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 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 +267,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 +325,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 +335,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 +355,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 +451,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
@@ -416,9 +472,12 @@
tot_pass = 0
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 +487,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 +510,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'),
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 dec48ea..74c7c80 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.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/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 22109e7..a2e7ce9 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/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..5530ff3 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
@@ -53,10 +53,6 @@
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;
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/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/PermissionsHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java
index d0cc258..b14239b 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,13 @@
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");
- }
+ runThrowingTest("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 +152,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 +236,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 +283,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 +302,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 +479,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 b2f9f3f..e08bcf0 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))));
}
@@ -500,6 +488,9 @@
}
private static AccessibilityNodeInfo findByText(AccessibilityNodeInfo root, String text) {
+ if (root == null) {
+ return null;
+ }
List<AccessibilityNodeInfo> nodes = root.findAccessibilityNodeInfosByText(text);
for (AccessibilityNodeInfo node : nodes) {
if (node.getText().toString().equals(text)) {
@@ -587,12 +578,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/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/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..d9a3975 100644
--- a/hostsidetests/backup/src/android/cts/backup/BackupPreparer.java
+++ b/hostsidetests/backup/src/android/cts/backup/BackupPreparer.java
@@ -16,6 +16,9 @@
package android.cts.backup;
+import static org.junit.Assert.fail;
+
+import com.android.compatibility.common.util.BackupHostSideUtils;
import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.config.Option;
import com.android.tradefed.config.OptionClass;
@@ -27,7 +30,11 @@
import com.android.tradefed.targetprep.ITargetCleaner;
import com.android.tradefed.targetprep.TargetSetupError;
+import java.io.IOException;
+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 +45,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;
@@ -53,9 +61,6 @@
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 boolean mIsBackupSupported;
private boolean mWasBackupEnabled;
private String mOldTransport;
@@ -74,10 +79,8 @@
if (mIsBackupSupported) {
// 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 +90,11 @@
mOldTransport = setBackupTransport(LOCAL_TRANSPORT);
CLog.d("Old transport : %s", mOldTransport);
}
- waitForBackupInitialization();
+ try {
+ BackupHostSideUtils.createBackupUtils(mDevice).waitForBackupInitialization();
+ } catch (IOException e) {
+ throw new TargetSetupError("Backup not initialized", e);
+ }
}
}
}
@@ -110,17 +117,56 @@
}
}
- // 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 +195,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 +215,13 @@
}
}
+ 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/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..a5ee3e2
--- /dev/null
+++ b/hostsidetests/devicepolicy/TEST_MAPPING
@@ -0,0 +1,20 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsDevicePolicyManagerTestCases",
+ "options": [
+ {
+ "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ },
+ {
+ "exclude-annotation": "android.platform.test.annotations.LargeTest"
+ }
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "CtsDevicePolicyManagerTestCases"
+ }
+ ]
+}
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..5892c5c 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
@@ -378,17 +378,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/PermissionsTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PermissionsTest.java
index fefe9cd..4b689d7 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,32 +359,32 @@
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.checkPermission(mContext, PERMISSION_NAME, -1,
+ PermissionChecker.checkPermission(mContext, permission, -1,
packageInfo.applicationInfo.uid, SIMPLE_PRE_M_APP_PACKAGE_NAME));
}
- 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);
@@ -365,7 +393,7 @@
// 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.checkPermission(mContext, PERMISSION_NAME, -1,
+ boolean isGranted = (PermissionChecker.checkPermission(mContext, permission, -1,
packageInfo.applicationInfo.uid, SIMPLE_PRE_M_APP_PACKAGE_NAME)
== PackageManager.PERMISSION_GRANTED);
switch (value) {
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/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/NoLaunchableActivityApp/Android.bp b/hostsidetests/devicepolicy/app/NoLaunchableActivityApp/Android.bp
index 2e8d9f4..f0f4716 100644
--- a/hostsidetests/devicepolicy/app/NoLaunchableActivityApp/Android.bp
+++ b/hostsidetests/devicepolicy/app/NoLaunchableActivityApp/Android.bp
@@ -25,6 +25,7 @@
srcs: ["src/**/*.java"],
// tag this module as a cts test artifact
test_suites: [
+ "arcts",
"cts",
"vts",
"general-tests",
@@ -45,6 +46,7 @@
srcs: ["src/**/*.java"],
// tag this module as a cts test artifact
test_suites: [
+ "arcts",
"cts",
"vts",
"general-tests",
@@ -66,6 +68,7 @@
srcs: ["src/**/*.java"],
// tag this module as a cts test artifact
test_suites: [
+ "arcts",
"cts",
"vts",
"general-tests",
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/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..09c900a 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsHostSideTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsHostSideTest.java
@@ -2,6 +2,8 @@
import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.assertMetricsLogged;
+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 +12,7 @@
import java.io.FileNotFoundException;
import java.util.Collections;
import java.util.Map;
+
import javax.annotation.Nullable;
/**
@@ -57,10 +60,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 +75,7 @@
verifyCrossProfileAppsApi(mPrimaryUserId, mProfileId, TARGET_USER_TEST_CLASS);
}
+ @LargeTest
public void testManagedProfileToPrimaryUser() throws Exception {
if (!mHasManagedUserFeature) {
return;
@@ -75,6 +83,7 @@
verifyCrossProfileAppsApi(mProfileId, mPrimaryUserId, TARGET_USER_TEST_CLASS);
}
+ @LargeTest
public void testStartActivity() throws Exception {
if (!mHasManagedUserFeature) {
return;
@@ -82,6 +91,7 @@
verifyCrossProfileAppsApi(mProfileId, mPrimaryUserId, START_ACTIVITY_TEST_CLASS);
}
+ @LargeTest
public void testPrimaryUserToSecondaryUser() throws Exception {
if (!mCanTestMultiUser) {
return;
@@ -89,6 +99,7 @@
verifyCrossProfileAppsApi(mPrimaryUserId, mSecondaryUserId, NON_TARGET_USER_TEST_CLASS);
}
+ @LargeTest
public void testSecondaryUserToManagedProfile() throws Exception {
if (!mCanTestMultiUser || !mHasManagedUserFeature) {
return;
@@ -97,6 +108,7 @@
}
+ @LargeTest
public void testManagedProfileToSecondaryUser() throws Exception {
if (!mCanTestMultiUser || !mHasManagedUserFeature) {
return;
@@ -104,6 +116,7 @@
verifyCrossProfileAppsApi(mProfileId, mSecondaryUserId, NON_TARGET_USER_TEST_CLASS);
}
+ @LargeTest
public void testStartMainActivity_logged() throws Exception {
if (!mHasManagedUserFeature) {
return;
@@ -123,6 +136,7 @@
.build());
}
+ @LargeTest
public void testGetTargetUserProfiles_logged() throws Exception {
if (!mHasManagedUserFeature) {
return;
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..7b0a5bb 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
@@ -18,9 +18,12 @@
import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.assertMetricsLogged;
+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;
@@ -564,6 +567,14 @@
executeDeviceTestMethod(".PermissionsTest", "testPermissionPolicy");
}
+ public void testAutoGrantMultiplePermissionsInGroup() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ installAppPermissionAppAsUser();
+ executeDeviceTestMethod(".PermissionsTest", "testAutoGrantMultiplePermissionsInGroup");
+ }
+
public void testPermissionMixedPolicies() throws Exception {
if (!mHasFeature) {
return;
@@ -848,6 +859,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 +898,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 +919,10 @@
if (!mHasFeature) {
return;
}
+ if (!hasService("wallpaper")) {
+ CLog.d("testDisallowSetWallpaper_allowed(): device does not support wallpapers");
+ return;
+ }
executeDeviceTestMethod(".CustomizationRestrictionsTest",
"testDisallowSetWallpaper_allowed");
}
@@ -1058,6 +1108,7 @@
.build());
}
+ @FlakyTest(bugId = 132226089)
public void testLockTask() throws Exception {
if (!mHasFeature) {
return;
@@ -1084,6 +1135,7 @@
}
}
+ @LargeTest
public void testLockTaskAfterReboot() throws Exception {
if (!mHasFeature) {
return;
@@ -1104,6 +1156,7 @@
}
}
+ @LargeTest
public void testLockTaskAfterReboot_tryOpeningSettings() throws Exception {
if (!mHasFeature) {
return;
@@ -1285,6 +1338,7 @@
.build());
}
+ @LockSettingsTest
public void testResetPasswordWithToken() throws Exception {
if (!mHasFeature || !mHasSecureLockScreen) {
return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerPlusProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerPlusProfileOwnerTest.java
index 25168ff..42b1135 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerPlusProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerPlusProfileOwnerTest.java
@@ -18,11 +18,14 @@
import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.assertMetricsLogged;
+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 +96,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 +120,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 +142,7 @@
* {@link #testBindDeviceAdminServiceAsUser_corpOwnedManagedProfileWithManagedProvisioning}
* except we don't enable the profile.
*/
+ @FlakyTest
public void testBindDeviceAdminServiceAsUser_canBindEvenIfProfileNotEnabled() throws Exception {
if (!mHasFeature) {
return;
@@ -174,6 +180,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 +202,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;
@@ -416,6 +424,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 d223f03..91a09d0 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
@@ -18,6 +18,8 @@
import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.assertMetricsLogged;
+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 +102,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();
@@ -150,6 +161,7 @@
.build());
}
+ @FlakyTest(bugId = 137088260)
public void testWifi() throws Exception {
if (!mHasFeature || !hasDeviceFeature("android.hardware.wifi")) {
return;
@@ -175,6 +187,7 @@
}
}
+ @FlakyTest(bugId = 137071121)
public void testCreateAndManageUser_LowStorage() throws Exception {
if (!mHasCreateAndManageUserFeature) {
return;
@@ -231,6 +244,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 +428,7 @@
}
}
+ @FlakyTest(bugId = 126955083)
public void testUserAddedOrRemovedBroadcasts() throws Exception {
if (mHasCreateAndManageUserFeature) {
executeDeviceTestMethod(".CreateAndManageUserTest",
@@ -448,6 +463,7 @@
}
}
+ @FlakyTest(bugId = 137093665)
public void testSecurityLoggingWithSingleUser() throws Exception {
if (!mHasFeature) {
return;
@@ -531,6 +547,7 @@
}
}
+ @FlakyTest(bugId = 137092833)
public void testNetworkLoggingWithSingleUser() throws Exception {
if (!mHasFeature) {
return;
@@ -548,6 +565,7 @@
Collections.singletonMap(ARG_NETWORK_LOGGING_BATCH_COUNT, Integer.toString(2)));
}
+ @LargeTest
public void testNetworkLogging_rebootResetsId() throws Exception {
if (!mHasFeature) {
return;
@@ -573,6 +591,7 @@
executeDeviceTestMethod(".AffiliationTest", "testSetAffiliationId_containsEmptyString");
}
+ @LargeTest
public void testSystemUpdatePolicy() throws Exception {
if (!mHasFeature) {
return;
@@ -610,6 +629,7 @@
.build());
}
+ @FlakyTest(bugId = 127101449)
public void testWifiConfigLockdown() throws Exception {
final boolean hasWifi = hasDeviceFeature("android.hardware.wifi");
if (hasWifi && mHasFeature) {
@@ -698,6 +718,7 @@
"testIsProvisioningAllowedTrueForManagedProfileAction");
}
+ @FlakyTest(bugId = 137096267)
public void testAdminActionBookkeeping() throws Exception {
if (!mHasFeature) {
return;
@@ -837,6 +858,7 @@
}
}
+ @LargeTest
public void testPackageInstallCache_multiUser() throws Exception {
if (!mHasFeature || !canCreateAdditionalUsers(1)) {
return;
@@ -906,6 +928,7 @@
executeDeviceOwnerTest("OverrideApnTest");
}
+ @FlakyTest(bugId = 134487729)
public void testPrivateDnsPolicy() throws Exception {
if (!mHasFeature) {
return;
@@ -1011,6 +1034,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 c756d16..bf2f6d4 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..f667024
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileContactsTest.java
@@ -0,0 +1,416 @@
+/*
+ * 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 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();
+ 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..83bcf5b
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileCrossProfileTest.java
@@ -0,0 +1,512 @@
+/*
+ * 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 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);
+
+ 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) {
+ 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);
+ 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);
+ }
+ }
+
+ 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..44c1a72
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfilePasswordTest.java
@@ -0,0 +1,247 @@
+/*
+ * 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 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) {
+ 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/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..1dcaa62 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
@@ -13,12 +13,11 @@
* 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 android.platform.test.annotations.LargeTest;
import android.stats.devicepolicy.EventId;
import com.android.cts.devicepolicy.metrics.DevicePolicyEventWrapper;
@@ -28,130 +27,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 +59,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 +100,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 +137,7 @@
assertAppLinkResult("testReceivedByAppLinkActivityInManaged");
}
+ @LargeTest
public void testAppLinks_enabledStatus() throws Exception {
if (!mHasFeature) {
return;
@@ -580,134 +200,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 +285,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;
@@ -900,14 +304,6 @@
.build());
}
- public void testPasswordMinimumRestrictions() throws Exception {
- if (!mHasFeature) {
- return;
- }
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".PasswordMinimumRestrictionsTest",
- mProfileUserId);
- }
-
public void testDevicePolicyManagerParentSupport() throws Exception {
if (!mHasFeature) {
return;
@@ -950,6 +346,7 @@
/*expectFailure*/ true));
}
+ @LargeTest
public void testCannotSetDeviceOwnerWhenProfilePresent() throws Exception {
if (!mHasFeature) {
return;
@@ -985,84 +382,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 +396,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 +436,7 @@
}
}
+ @LargeTest
public void testManagedCall() throws Exception {
if (!mHasFeature) {
return;
@@ -1177,52 +487,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,330 +562,6 @@
"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) {
return;
@@ -1635,23 +575,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 +585,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 +593,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 +601,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..0445897
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileWipeTest.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 com.android.cts.devicepolicy;
+
+import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.assertMetricsLogged;
+
+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) {
+ 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..0b837de 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
+ @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..74f9c21 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTestApi25.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTestApi25.java
@@ -16,6 +16,10 @@
package com.android.cts.devicepolicy;
+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 +78,7 @@
* unlocked, and cannot remove the password even when FBE is unlocked.
*/
@Override
+ @LargeTest
public void testResetPasswordFbe() throws Exception {
if (!mHasFeature || !mSupportsFbe || !mHasSecureLockScreen) {
return;
@@ -94,4 +99,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..be1a414 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,28 @@
}
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();
+ }
}
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/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/OWNERS b/hostsidetests/dumpsys/OWNERS
index 8bbfc48..b5deebe 100644
--- a/hostsidetests/dumpsys/OWNERS
+++ b/hostsidetests/dumpsys/OWNERS
@@ -1,2 +1,4 @@
# Bug component: 153446
-felipeal@google.com
+nandana@google.com
+omakoto@google.com
+kwekua@google.com
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/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/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/rollback/Android.bp b/hostsidetests/rollback/Android.bp
index c3dbe18..817a339 100644
--- a/hostsidetests/rollback/Android.bp
+++ b/hostsidetests/rollback/Android.bp
@@ -24,7 +24,7 @@
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: [
":StagedInstallTestApexV2",
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..8baf21f 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;
@@ -49,8 +48,7 @@
*/
@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 +59,7 @@
*/
@After
public void teardown() throws InterruptedException, IOException {
- InstrumentationRegistry.getInstrumentation().getUiAutomation()
- .dropShellPermissionIdentity();
+ InstallUtils.dropShellPermissionIdentity();
}
@@ -72,7 +69,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 +85,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 +105,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 +118,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 +133,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 +151,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 +164,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 +181,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 +194,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 +203,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 +248,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 +263,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 +277,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 +299,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 +313,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 +338,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 +352,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 +366,7 @@
*/
@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();
}
}
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..736b47c 100644
--- a/hostsidetests/rollback/src/com/android/cts/rollback/host/RollbackManagerHostTest.java
+++ b/hostsidetests/rollback/src/com/android/cts/rollback/host/RollbackManagerHostTest.java
@@ -36,7 +36,7 @@
public class RollbackManagerHostTest extends BaseHostJUnit4Test {
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.
@@ -133,6 +133,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
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/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 01bb8be..585a26a 100644
--- a/hostsidetests/stagedinstall/Android.bp
+++ b/hostsidetests/stagedinstall/Android.bp
@@ -44,54 +44,34 @@
manifest : "app/AndroidManifest.xml",
java_resources: [
+ ":ApexKeyRotationTestV2_SignedBob",
+ ":ApexKeyRotationTestV2_SignedBobRot",
+ ":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"],
@@ -100,6 +80,41 @@
}
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: "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",
@@ -154,3 +169,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..cde5dcd 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,26 @@
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 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 +155,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 +177,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 +204,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 +236,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 +246,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 +257,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 +267,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 +295,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 +315,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 +323,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 +344,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 +358,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 +418,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 +428,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 +440,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 +494,14 @@
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 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 +515,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 +534,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 +555,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 +637,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 +660,139 @@
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 {
+ // Assume updateWithDifferentKey_Commit has been run already
+ 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 {
+ // Assume updateWithDifferentKey_Commit has been run already
+ 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 {
+ // Assume updateWithDifferentKey_Commit has been run already
+ 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();
+ }
+
+ @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 +809,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 +906,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 +977,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..7999e34 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,6 +285,18 @@
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();
@@ -274,16 +310,95 @@
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 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 +412,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..6ce5185
--- /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..58b7a8e 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..53f0ed1 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..a1ece1a 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..6365654
--- /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..95fa522
--- /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..fb1f45b
--- /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_with_post_install_hook.apex b/hostsidetests/stagedinstall/testdata/apex/com.android.apex.cts.shim.v2_with_post_install_hook.apex
index c32b139..437f7a8 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..0e92266 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..b1c77fa 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..5c54d73 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..cf3e3eb
--- /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..7c59900
--- /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..788f475 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/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/assets/R/140dpi.zip b/hostsidetests/theme/assets/R/140dpi.zip
new file mode 100644
index 0000000..48de32b
--- /dev/null
+++ b/hostsidetests/theme/assets/R/140dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/180dpi.zip b/hostsidetests/theme/assets/R/180dpi.zip
new file mode 100644
index 0000000..4def986
--- /dev/null
+++ b/hostsidetests/theme/assets/R/180dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/200dpi.zip b/hostsidetests/theme/assets/R/200dpi.zip
new file mode 100644
index 0000000..fe2495e
--- /dev/null
+++ b/hostsidetests/theme/assets/R/200dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/220dpi.zip b/hostsidetests/theme/assets/R/220dpi.zip
new file mode 100644
index 0000000..a2c2ea2
--- /dev/null
+++ b/hostsidetests/theme/assets/R/220dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/260dpi.zip b/hostsidetests/theme/assets/R/260dpi.zip
index ba5d363..74890a2 100644
--- a/hostsidetests/theme/assets/R/260dpi.zip
+++ b/hostsidetests/theme/assets/R/260dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/280dpi.zip b/hostsidetests/theme/assets/R/280dpi.zip
index 34ea14c..83fd290 100644
--- a/hostsidetests/theme/assets/R/280dpi.zip
+++ b/hostsidetests/theme/assets/R/280dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/300dpi.zip b/hostsidetests/theme/assets/R/300dpi.zip
index 7595d24..25334d8 100644
--- a/hostsidetests/theme/assets/R/300dpi.zip
+++ b/hostsidetests/theme/assets/R/300dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/340dpi.zip b/hostsidetests/theme/assets/R/340dpi.zip
index 8ce960c..2092326 100644
--- a/hostsidetests/theme/assets/R/340dpi.zip
+++ b/hostsidetests/theme/assets/R/340dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/360dpi.zip b/hostsidetests/theme/assets/R/360dpi.zip
index aae3adc..c13ad5c 100644
--- a/hostsidetests/theme/assets/R/360dpi.zip
+++ b/hostsidetests/theme/assets/R/360dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/400dpi.zip b/hostsidetests/theme/assets/R/400dpi.zip
index 363d602..1df1a95 100644
--- a/hostsidetests/theme/assets/R/400dpi.zip
+++ b/hostsidetests/theme/assets/R/400dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/420dpi.zip b/hostsidetests/theme/assets/R/420dpi.zip
index 0f2ce47..d58d418 100644
--- a/hostsidetests/theme/assets/R/420dpi.zip
+++ b/hostsidetests/theme/assets/R/420dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/440dpi.zip b/hostsidetests/theme/assets/R/440dpi.zip
index 2328c61..535102f 100644
--- a/hostsidetests/theme/assets/R/440dpi.zip
+++ b/hostsidetests/theme/assets/R/440dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/560dpi.zip b/hostsidetests/theme/assets/R/560dpi.zip
index 5f1bb0b..20f1c7b 100644
--- a/hostsidetests/theme/assets/R/560dpi.zip
+++ b/hostsidetests/theme/assets/R/560dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/hdpi.zip b/hostsidetests/theme/assets/R/hdpi.zip
index 6d82318..582989d 100644
--- a/hostsidetests/theme/assets/R/hdpi.zip
+++ b/hostsidetests/theme/assets/R/hdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/ldpi.zip b/hostsidetests/theme/assets/R/ldpi.zip
index cc60027..3035146 100644
--- a/hostsidetests/theme/assets/R/ldpi.zip
+++ b/hostsidetests/theme/assets/R/ldpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/mdpi.zip b/hostsidetests/theme/assets/R/mdpi.zip
index 66d41d4..a831e7b 100644
--- a/hostsidetests/theme/assets/R/mdpi.zip
+++ b/hostsidetests/theme/assets/R/mdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/tvdpi.zip b/hostsidetests/theme/assets/R/tvdpi.zip
index b43032f..bd4bf75 100644
--- a/hostsidetests/theme/assets/R/tvdpi.zip
+++ b/hostsidetests/theme/assets/R/tvdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/xhdpi.zip b/hostsidetests/theme/assets/R/xhdpi.zip
index 64905f3..0dd72e2 100644
--- a/hostsidetests/theme/assets/R/xhdpi.zip
+++ b/hostsidetests/theme/assets/R/xhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/xxhdpi.zip b/hostsidetests/theme/assets/R/xxhdpi.zip
index b2cb422..64fc846 100644
--- a/hostsidetests/theme/assets/R/xxhdpi.zip
+++ b/hostsidetests/theme/assets/R/xxhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/R/xxxhdpi.zip b/hostsidetests/theme/assets/R/xxxhdpi.zip
index d00dbbd..87beb9a 100644
--- a/hostsidetests/theme/assets/R/xxxhdpi.zip
+++ b/hostsidetests/theme/assets/R/xxxhdpi.zip
Binary files differ
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/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..842a674
--- /dev/null
+++ b/libs/install/testapp/src/com/android/cts/install/lib/testapp/ProcessUserData.java
@@ -0,0 +1,109 @@
+/*
+ * 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 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
+ * 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.
+ *
+ * @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();
+
+ 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.
+ File versionFile = new File(context.getFilesDir(), "version.txt");
+ try {
+ Scanner s = new Scanner(versionFile);
+ int userDataVersion = s.nextInt();
+ s.close();
+
+ if (userDataVersion > appVersion) {
+ throw new UserDataException("User data is from version " + userDataVersion
+ + ", which is not compatible with this version " + appVersion
+ + " of the RollbackTestApp");
+ }
+ } 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.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/AccessibilityGestureInfoTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityGestureInfoTest.java
new file mode 100644
index 0000000..0d851cf
--- /dev/null
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityGestureInfoTest.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.AccessibilityGestureInfo;
+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.AccessibilityGestureInfo}.
+ */
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityGestureInfoTest {
+
+ @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.
+ AccessibilityGestureInfo sentGestureInfo = new AccessibilityGestureInfo(
+ SENT_GESTURE, TARGET_DISPLAY);
+
+ // Marshal and unmarshal the gesture info.
+ Parcel parcel = Parcel.obtain();
+ sentGestureInfo.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ AccessibilityGestureInfo receivedGestureInfo =
+ AccessibilityGestureInfo.CREATOR.createFromParcel(parcel);
+
+ // Make sure all fields properly marshaled.
+ assertEqualsGestureInfo(sentGestureInfo, receivedGestureInfo);
+
+ parcel.recycle();
+ }
+
+ /**
+ * Tests whether the value of Getter method is as same as the parameter of the constructor.
+ *
+ */
+ @SmallTest
+ @Test
+ public void testGetterMethods() {
+ AccessibilityGestureInfo actualGesture = new AccessibilityGestureInfo(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() {
+ AccessibilityGestureInfo gesture1 = new AccessibilityGestureInfo(SENT_GESTURE,TARGET_DISPLAY);
+ assertSame("accessibility gesture infos always return 0 for this method.", 0,
+ gesture1.describeContents());
+ AccessibilityGestureInfo gesture2 = new AccessibilityGestureInfo(
+ AccessibilityService.GESTURE_SWIPE_LEFT, TARGET_DISPLAY);
+ assertSame("accessibility gesture infos always return 0 for this method.", 0,
+ gesture2.describeContents());
+ }
+
+ private void assertEqualsGestureInfo(AccessibilityGestureInfo sentGestureInfo,
+ AccessibilityGestureInfo receivedGestureInfo) {
+ assertEquals("getDisplayId has incorrectValue", sentGestureInfo.getDisplayId(),
+ receivedGestureInfo.getDisplayId());
+ assertEquals("getGestureId has incorrectValue", sentGestureInfo.getGestureId(),
+ receivedGestureInfo.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..c7c9b0d 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
@@ -16,13 +16,20 @@
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.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.TextUtils;
import android.util.ArrayMap;
@@ -35,6 +42,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 +57,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 +87,7 @@
* Tests if {@link AccessibilityNodeInfo}s are properly reused.
*/
@SmallTest
+ @Test
public void testReuse() {
AccessibilityEvent firstInfo = AccessibilityEvent.obtain();
firstInfo.recycle();
@@ -78,6 +99,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 +117,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 +131,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 +157,7 @@
* Tests whether we catch addition of an action with invalid id.
*/
@SmallTest
+ @Test
public void testCreateInvalidActionId() {
try {
new AccessibilityAction(3, null);
@@ -145,6 +170,7 @@
* Tests whether accessibility actions are properly removed.
*/
@SmallTest
+ @Test
public void testRemoveActions() {
AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
@@ -173,6 +199,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";
@@ -191,6 +218,7 @@
}
@SmallTest
+ @Test
public void testIsHeadingTakesOldApiIntoAccount() {
final AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
assertFalse(info.isHeading());
@@ -399,11 +427,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());
}
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 cf37486..8b2a0ec 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..3fd45bd 100644
--- a/tests/accessibilityservice/res/values/strings.xml
+++ b/tests/accessibilityservice/res/values/strings.xml
@@ -147,7 +147,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 +172,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..f2eeb34
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEmbeddedDisplayTest.java
@@ -0,0 +1,250 @@
+/*
+ * 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.ViewGroup;
+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();
+
+ private final static int DEFAULT_ACTIVITYVIEW_HEIGHT = 500;
+ private final static int DEFAULT_ACTIVITYVIEW_WIDTH = 500;
+
+ @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 oldWindowBound = new Rect();
+ oldActivityWindow.getBoundsInScreen(oldWindowBound);
+ assertEquals(DEFAULT_ACTIVITYVIEW_HEIGHT, oldWindowBound.height());
+ assertEquals(DEFAULT_ACTIVITYVIEW_WIDTH, oldWindowBound.width());
+
+ final int width = DEFAULT_ACTIVITYVIEW_WIDTH / 2;
+ final int height = DEFAULT_ACTIVITYVIEW_WIDTH / 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);
+ final Rect newWindowBound = new Rect();
+ newActivityWindow.getBoundsInScreen(newWindowBound);
+
+ assertEquals(height, newWindowBound.height());
+ assertEquals(width, newWindowBound.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);
+
+ ViewGroup.LayoutParams layoutParams = mActivityView.getLayoutParams();
+ layoutParams.width = DEFAULT_ACTIVITYVIEW_WIDTH;
+ layoutParams.height = DEFAULT_ACTIVITYVIEW_HEIGHT;
+ mActivityView.requestLayout();
+ }
+
+ 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..b28b419 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDetectorTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDetectorTest.java
@@ -14,20 +14,24 @@
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.AccessibilityGestureInfo;
import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.GestureDescription;
import android.accessibilityservice.GestureDescription.StrokeDescription;
+import android.accessibilityservice.cts.utils.DisplayUtils;
import android.app.Instrumentation;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -36,15 +40,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 +65,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 +115,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 +152,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 +247,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));
+ }
+
+ AccessibilityGestureInfo expectedGestureInfo = new AccessibilityGestureInfo(gestureId,
+ displayId);
+ assertEquals(1, mService.getGestureInfoSize());
+ assertEquals(expectedGestureInfo.toString(), mService.getGestureInfo(0).toString());
}
/** Create a path from startPoint, moving by delta1, then delta2. (delta2 may be null.) */
@@ -196,25 +277,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 +360,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..0ca3c31 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextActionTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityTextActionTest.java
@@ -30,7 +30,7 @@
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;
@@ -60,6 +60,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;
@@ -80,10 +81,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/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..c33b945
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java
@@ -48,9 +48,9 @@
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.ActivityTaskManager;
import android.app.Instrumentation;
@@ -81,6 +81,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;
@@ -109,13 +110,16 @@
private AccessibilityWindowQueryActivity mActivity;
- @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 {
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..1bc62f6 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.AccessibilityGestureInfo;
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<AccessibilityGestureInfo> mCollectedGestureInfos = 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(AccessibilityGestureInfo gestureInfo) {
+ super.onGesture(gestureInfo);
+ synchronized (mCollectedGestureInfos) {
+ mCollectedGestureInfos.add(gestureInfo);
+ mCollectedGestureInfos.notifyAll(); // Stop waiting for gesture.
}
return true;
}
@@ -48,6 +55,9 @@
synchronized (mCollectedGestures) {
mCollectedGestures.clear();
}
+ synchronized (mCollectedGestureInfos) {
+ mCollectedGestureInfos.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(AccessibilityGestureInfo)} to collect next gesture. */
+ public void waitUntilGestureInfo() {
+ synchronized (mCollectedGestureInfos) {
+ //Assume the size of mCollectedGestures is changed before mCollectedGestureInfos.
+ if (mCollectedGestureInfos.size() > 0) {
return;
}
try {
- mCollectedGestures.wait(GESTURE_RECOGNIZE_TIMEOUT_MS);
+ mCollectedGestureInfos.wait(GESTURE_RECOGNIZE_TIMEOUT_MS);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
+ public int getGestureInfoSize() {
+ synchronized (mCollectedGestureInfos) {
+ return mCollectedGestureInfos.size();
+ }
+ }
+
+ public AccessibilityGestureInfo getGestureInfo(int index) {
+ synchronized (mCollectedGestureInfos) {
+ return mCollectedGestureInfos.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..9db2478
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/TouchExplorationStubAccessibilityService.java
@@ -0,0 +1,46 @@
+/**
+ * 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_FOCUSED;
+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_FOCUSED:
+ 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..c29c268
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/TouchExplorerTest.java
@@ -0,0 +1,352 @@
+/*
+ * 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_FOCUSED;
+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() {
+ 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_VIEW_FOCUSED,
+ 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() {
+ if (!mHasTouchscreen || !mScreenBigEnough) return;
+ dispatch(swipe(mTapLocation, add(mTapLocation, mSwipeDistance, 0)));
+ mHoverListener.assertNonePropagated();
+ mTouchListener.assertNonePropagated();
+ mService.assertPropagated(
+ TYPE_VIEW_FOCUSED,
+ 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() {
+ 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() {
+ if (!mHasTouchscreen || !mScreenBigEnough) return;
+ dispatch(click(mTapLocation));
+ mHoverListener.assertPropagated(ACTION_HOVER_ENTER, ACTION_HOVER_EXIT);
+ mTouchListener.assertNonePropagated();
+ mService.assertPropagated(
+ TYPE_VIEW_FOCUSED,
+ 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() {
+ 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_FOCUSED,
+ 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() {
+ if (!mHasTouchscreen || !mScreenBigEnough) return;
+ dispatch(doubleTap(mTapLocation));
+ mHoverListener.assertNonePropagated();
+ mTouchListener.assertNonePropagated();
+ mService.assertPropagated(
+ TYPE_VIEW_FOCUSED, 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() {
+ 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_FOCUSED,
+ 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() {
+ if (!mHasTouchscreen || !mScreenBigEnough) return;
+ dispatch(doubleTap(mTapLocation));
+ mHoverListener.assertNonePropagated();
+ mTouchListener.assertNonePropagated();
+ mService.assertPropagated(
+ TYPE_VIEW_FOCUSED, 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/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..76e6d54 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"/>
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..8446f2e
--- /dev/null
+++ b/tests/app/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 316234
+yamasani@google.com
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..c3fc069 100644
--- a/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
+++ b/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
@@ -61,7 +61,6 @@
import androidx.test.InstrumentationRegistry;
-import com.android.compatibility.common.util.CommonTestUtils;
import com.android.compatibility.common.util.SystemUtil;
public class ActivityManagerProcessStateTest extends InstrumentationTestCase {
@@ -159,18 +158,6 @@
private void turnScreenOn() throws Exception {
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();
- });
}
private void removeTestAppFromWhitelists() throws Exception {
@@ -1477,9 +1464,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 +1545,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 +1621,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 +1744,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 +1781,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 +1908,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 +1924,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/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..39addcd 100644
--- a/tests/app/src/android/app/cts/NotificationManagerTest.java
+++ b/tests/app/src/android/app/cts/NotificationManagerTest.java
@@ -18,6 +18,8 @@
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.Policy.SUPPRESSED_EFFECT_AMBIENT;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
@@ -91,9 +93,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 +110,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,6 +120,7 @@
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;
@@ -138,12 +140,21 @@
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();
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);
@@ -257,14 +268,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 +292,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);
@@ -1292,7 +1310,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 +1485,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 +1517,7 @@
}
}
- public void testInboxStyle() throws Exception {
+ public void testInboxStyle() {
final int id = 100;
final Notification notification =
@@ -1523,7 +1541,7 @@
}
}
- public void testBigTextStyle() throws Exception {
+ public void testBigTextStyle() {
final int id = 101;
final Notification notification =
@@ -1549,7 +1567,7 @@
}
}
- public void testBigPictureStyle() throws Exception {
+ public void testBigPictureStyle() {
final int id = 102;
final Notification notification =
@@ -1577,59 +1595,59 @@
}
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);
@@ -1638,16 +1656,16 @@
public void testNewNotificationsAddedToAutogroup_ifOriginalNotificationsCanceled()
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 +1685,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) {
@@ -1813,7 +1831,6 @@
assertExpectedDndState(INTERRUPTION_FILTER_ALL);
}
- @FlakyTest
public void testSetAutomaticZenRuleState_multipleRules() throws Exception {
if (mActivityManager.isLowRamDevice()) {
return;
@@ -1932,7 +1949,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 +2003,77 @@
.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);
+
+ 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);
+
+ 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 +2192,7 @@
.build();
mNotificationManager.notify(id, notification);
- StatusBarNotification n = findPostedNotification(id);
+ StatusBarNotification n = findPostedNotification(id, false);
assertNotNull(n);
}
@@ -2192,15 +2279,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 +2375,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 +2425,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) {
@@ -2376,7 +2463,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 +2717,7 @@
}
// Should be foreground now
- a.sendBubble(1);
+ a.sendBubble(4000);
boolean shouldBeBubble = !mActivityManager.isLowRamDevice();
@@ -2643,7 +2731,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 +2742,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 +2757,7 @@
}
}
- public void testNotificationManagerBubblePolicy_noFlag_notEmbeddable() throws Exception {
+ public void testNotificationManagerBubblePolicy_noFlag_notEmbeddable() {
Person person = new Person.Builder()
.setName("bubblebot")
.build();
@@ -2705,7 +2793,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 +2828,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/WallpaperManagerTest.java b/tests/app/src/android/app/cts/WallpaperManagerTest.java
index fe5f183..3b9934a 100644
--- a/tests/app/src/android/app/cts/WallpaperManagerTest.java
+++ b/tests/app/src/android/app/cts/WallpaperManagerTest.java
@@ -16,6 +16,7 @@
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;
@@ -72,6 +73,7 @@
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());
@@ -477,4 +479,4 @@
public void onColorsChanged(WallpaperColors colors, int which) {
}
}
-}
\ No newline at end of file
+}
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/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/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 0dbfc73..e8f7fb9 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/OWNERS b/tests/backup/OWNERS
index 3637e32..c28c4d8 100644
--- a/tests/backup/OWNERS
+++ b/tests/backup/OWNERS
@@ -1,3 +1,4 @@
+# Bug component: 41666
# Use this reviewer by default.
br-framework-team+reviews@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/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/libctscamera2jni/native-camera-jni.cpp b/tests/camera/libctscamera2jni/native-camera-jni.cpp
index b9fc256..5b9f74c 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;
diff --git a/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java b/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java
index a64cd50..23f406f 100644
--- a/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java
@@ -2698,4 +2698,166 @@
}
}
}
+
+ /**
+ * Verify audio restrictions are set properly for single CameraDevice usage
+ */
+ 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 < mCameraIds.length; i++) {
+ try {
+ openDevice(mCameraIds[i], mCameraMockListener);
+ waitForDeviceState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
+
+ for (int mode : testModes) {
+ int retMode = mCamera.setCameraAudioRestriction(mode);
+ 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(mCameraIds[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;
+ int retMode = cam0.setCameraAudioRestriction(mode0);
+ 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;
+ retMode = cam1.setCameraAudioRestriction(mode1);
+ 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;
+ retMode = cam1.setCameraAudioRestriction(mode1);
+ 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;
+ retMode = cam0.setCameraAudioRestriction(mode0);
+ 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;
+ retMode = cam1.setCameraAudioRestriction(mode1);
+ 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;
+ retMode = cam0.setCameraAudioRestriction(mode0);
+ assertTrue("Audio restriction mode mismatch: expect: " + expectMode +
+ ", output:" + retMode, retMode == expectMode);
+
+ mode1 = CameraDevice.AUDIO_RESTRICTION_VIBRATION;
+ expectMode = mode0 | mode1;
+ retMode = cam1.setCameraAudioRestriction(mode1);
+ assertTrue("Audio restriction mode mismatch: expect: " + expectMode +
+ ", output:" + retMode, retMode == expectMode);
+
+ mode1 = CameraDevice.AUDIO_RESTRICTION_NONE;
+ expectMode = mode0 | mode1;
+ retMode = cam1.setCameraAudioRestriction(mode1);
+ 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;
+ retMode = cam1.setCameraAudioRestriction(mode1);
+ 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;
+ }
+ }
+ }
+
+ public void testAudioRestrictionMultipleDevices() throws Exception {
+ if (mCameraIds.length < 2) {
+ Log.i(TAG, "device doesn't have multiple cameras, skipping");
+ return;
+ }
+
+ for (int i = 0; i < mCameraIds.length; i++) {
+ for (int j = i+1; j < mCameraIds.length; j++) {
+ testTwoCameraDevicesAudioRestriction(mCameraIds[i], mCameraIds[j]);
+ }
+ }
+ }
}
diff --git a/tests/camera/src/android/hardware/camera2/cts/CaptureResultTest.java b/tests/camera/src/android/hardware/camera2/cts/CaptureResultTest.java
index ab65015..6f6b13c 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;
@@ -401,16 +402,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 +425,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/ImageWriterTest.java b/tests/camera/src/android/hardware/camera2/cts/ImageWriterTest.java
index 38f8816..59e89a2 100644
--- a/tests/camera/src/android/hardware/camera2/cts/ImageWriterTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/ImageWriterTest.java
@@ -19,6 +19,7 @@
import static android.hardware.camera2.cts.CameraTestUtils.*;
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;
@@ -170,6 +171,23 @@
}
}
+ 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..7a2e339 100644
--- a/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java
@@ -1109,8 +1109,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 +1133,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/contentcaptureservice/OWNERS b/tests/contentcaptureservice/OWNERS
index 4df1ffa..595c93d 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
+felipeal@google.com
+adamhe@google.com
+svetoslavganov@google.com
\ No newline at end of file
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/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..0e1a498 100644
--- a/tests/framework/base/windowmanager/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/AndroidManifest.xml
@@ -150,6 +150,9 @@
<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.StartActivityTests$TestActivity2" />
<activity android:name="android.server.wm.MultiDisplaySystemDecorationTests$ImeTestActivity" />
@@ -233,6 +236,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 +271,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"
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..08c32d1 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
@@ -273,6 +273,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/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/src/android/server/wm/ActivityMetricsLoggerTests.java b/tests/framework/base/windowmanager/src/android/server/wm/ActivityMetricsLoggerTests.java
index b827353..13713f4 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,7 +249,6 @@
* 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());
@@ -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..1eddce9 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();
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/AmStartOptionsTests.java b/tests/framework/base/windowmanager/src/android/server/wm/AmStartOptionsTests.java
index 839371f..e8bd4a4 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/AmStartOptionsTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/AmStartOptionsTests.java
@@ -70,7 +70,6 @@
}
@Test
- @FlakyTest
public void testDashW_Indirect() throws Exception {
testDashW(ENTRY_POINT_ALIAS_ACTIVITY, SINGLE_TASK_ACTIVITY);
}
@@ -120,4 +119,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
index 2126f83..b9de595 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/AnimationBackgroundTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/AnimationBackgroundTests.java
@@ -52,14 +52,11 @@
assumeActivityNotInFreeformDisplay(ANIMATION_TEST_ACTIVITY);
// Make sure we are in the middle of the animation.
- mAmWmState.waitForWithWmState(state -> state
- .getStandardStackByWindowingMode(WINDOWING_MODE_FULLSCREEN)
+ assertTrue("window animation background needs to be showing",
+ 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());
+ "animation background showing"));
}
@Test
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..2056380 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());
@@ -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());
@@ -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/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..1193a73 100755
--- a/tests/framework/base/windowmanager/src/android/server/wm/KeyguardTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/KeyguardTests.java
@@ -553,7 +553,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/MultiDisplayActivityLaunchTests.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayActivityLaunchTests.java
index 67f1db8..9d6f2d4 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayActivityLaunchTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayActivityLaunchTests.java
@@ -19,8 +19,11 @@
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;
@@ -35,6 +38,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 +46,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 +55,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 +266,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");
@@ -848,4 +856,90 @@
SINGLE_TASK_INSTANCE_DISPLAY_ACTIVITY3));
}
}
+
+ @Test
+ public void testLaunchPendingIntentActivity() throws Exception {
+ // Prevent the launch of pure PendingIntent from being delayed after home key is pressed.
+ resumeAppSwitches();
+ 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..9d2921c 100755
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayClientTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayClientTests.java
@@ -28,6 +28,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,8 +37,8 @@
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.WindowManager;
import android.view.inputmethod.InputMethodManager;
@@ -73,13 +74,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 */);
}
@@ -128,17 +127,17 @@
}
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));
+ 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) {
- mAmWmState.waitForWithAmState((state) ->
- getCallbackCount(activityName, ON_RESUME) == 1, "waitForResume");
- assertEquals("Must be resumed once", 1, getCallbackCount(activityName, ON_RESUME));
+ assertTrue("Must be resumed once",
+ mAmWmState.waitForWithAmState(
+ state -> getCallbackCount(activityName, ON_RESUME) == 1,
+ activityName + " performs resume"));
}
private int getCallbackCount(ComponentName activityName,
@@ -148,7 +147,6 @@
}
@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);
@@ -156,7 +154,6 @@
}
@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);
@@ -202,7 +199,6 @@
}
@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.
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..b508760 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayLockedKeyguardTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayLockedKeyguardTests.java
@@ -29,8 +29,6 @@
import android.platform.test.annotations.Presubmit;
import android.server.wm.ActivityManagerState.ActivityDisplay;
-import androidx.test.filters.FlakyTest;
-
import org.junit.Before;
import org.junit.Test;
@@ -56,7 +54,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()) {
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/MultiDisplaySecurityTests.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySecurityTests.java
index b2654ee..46101f5 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,17 +687,9 @@
}
}
- 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");
+ private static void assertSecurityExceptionFromActivityLauncher() {
+ waitForOrFail("SecurityException from " + ActivityLauncher.TAG,
+ ActivityLauncher::hasCaughtSecurityException);
}
/**
@@ -727,7 +714,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 +757,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 +787,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 +833,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 +854,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..db52031 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,7 +212,6 @@
* 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.
@@ -236,7 +230,6 @@
* 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.
@@ -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..afd6ab9 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,11 @@
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);
+ });
}
/**
@@ -510,6 +500,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 +526,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 +545,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 +559,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 +580,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..d6e8b3f 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
@@ -64,7 +64,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 +107,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 +215,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 +224,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 +243,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 +252,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 +262,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();
@@ -640,7 +628,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 +662,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 +677,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,
@@ -805,6 +790,7 @@
assertPinnedStackExists();
}
+ @FlakyTest(bugId = 139111392)
@Test
public void testDisallowEnterPipActivityLocked() throws Exception {
launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP_ON_PAUSE, "true");
@@ -827,7 +813,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
@@ -1123,6 +1109,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 +1132,6 @@
}
/** Test that reported display size corresponds to fullscreen after exiting PiP. */
- @FlakyTest
@Test
public void testDisplayMetricsPinUnpin() throws Exception {
separateTestJournal();
@@ -1180,6 +1166,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 +1215,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);
@@ -1413,7 +1399,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 +1409,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 +1431,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 +1440,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..04e1fb6 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
@@ -498,7 +489,6 @@
}
}
- @FlakyTest(bugId = 73813034)
@Test
public void testFinishDockActivityWhileMinimized() throws Exception {
launchActivityInDockStackAndMinimize(TEST_ACTIVITY);
@@ -552,7 +542,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 +570,6 @@
}
@Test
- @FlakyTest(bugId = 131005232)
public void testDifferentProcessActivityResumedPreQ() {
launchActivitiesInSplitScreen(
getLaunchActivityBuilder().setTargetActivity(SDK_27_TEST_ACTIVITY),
@@ -655,7 +643,6 @@
}
@Test
- @FlakyTest(bugId = 131005232)
public void testStackListOrderOnSplitScreenDismissed() throws Exception {
launchActivitiesInSplitScreen(
getLaunchActivityBuilder().setTargetActivity(DOCKED_ACTIVITY),
@@ -710,11 +697,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..bef21f8 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/StartActivityTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/StartActivityTests.java
@@ -16,7 +16,10 @@
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.TEST_ACTIVITY;
+import static android.server.wm.app.Components.TRANSLUCENT_ACTIVITY;
import static android.server.wm.app27.Components.SDK_27_LAUNCHING_ACTIVITY;
import static org.junit.Assert.assertFalse;
@@ -25,7 +28,6 @@
import android.platform.test.annotations.Presubmit;
import androidx.test.rule.ActivityTestRule;
-import androidx.test.filters.FlakyTest;
import org.junit.Rule;
import org.junit.Test;
@@ -46,7 +48,7 @@
* {@link android.content.Context}.
*/
@Test
- public void testStartActivityContexts() throws Exception {
+ public void testStartActivityContexts() {
// Launch Activity from application context.
getLaunchActivityBuilder()
.setTargetActivity(TEST_ACTIVITY)
@@ -85,14 +87,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)
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..2a04d2e 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/TransitionSelectionTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/TransitionSelectionTests.java
@@ -58,7 +58,6 @@
* atest CtsWindowManagerDeviceTestCases:TransitionSelectionTests
*/
@Presubmit
-@FlakyTest(bugId = 71792333)
public class TransitionSelectionTests extends ActivityManagerTestBase {
@Override
@@ -118,7 +117,6 @@
false /*slowStop*/, TRANSIT_TASK_OPEN);
}
- @FlakyTest(bugId = 71792333)
@Test
public void testCloseTask_NeitherWallpaper() {
testCloseTask(false /*bottomWallpaper*/, false /*topWallpaper*/,
@@ -177,7 +175,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*/,
@@ -190,6 +187,7 @@
true /*slowStop*/, TRANSIT_WALLPAPER_OPEN);
}
+ @FlakyTest(bugId = 139111132)
@Test
public void testCloseTask_BothWallpaper_SlowStop() {
testCloseTask(true /*bottomWallpaper*/, true /*topWallpaper*/,
@@ -223,7 +221,6 @@
TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE);
}
- @FlakyTest(bugId = 71792333)
@Test
public void testCloseTask_BottomWallpaper_Translucent() {
testCloseTaskTranslucent(true /*bottomWallpaper*/, false /*topWallpaper*/,
@@ -306,13 +303,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..62bbee3 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInputTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInputTests.java
@@ -32,7 +32,6 @@
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 +47,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);
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..08d04f4 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
@@ -42,7 +42,7 @@
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..dd4f2c1 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,7 @@
package android.server.wm.lifecycle;
+import static android.app.Activity.RESULT_OK;
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;
@@ -58,8 +59,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 +120,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;
@@ -137,6 +148,7 @@
/** Launch an activity given a class. */
protected Activity launchActivity(Class<? extends Activity> activityClass) {
final Intent intent = new Intent(mTargetContext, activityClass);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
return getInstrumentation().startActivitySync(intent);
}
@@ -339,6 +351,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 +362,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 +387,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 +416,26 @@
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 {
}
/** Test activity that can call {@link Activity#recreate()} if requested in a new intent. */
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..2fcafd8 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
@@ -50,7 +50,6 @@
* Build/Install/Run:
* atest CtsWindowManagerDeviceTestCases:ActivityLifecyclePipTests
*/
-@FlakyTest(bugId = 77652261)
@MediumTest
@Presubmit
public class ActivityLifecyclePipTests extends ActivityLifecycleClientTestBase {
@@ -249,6 +248,7 @@
}
@Test
+ @FlakyTest(bugId = 127741025)
public void testPipAboveSplitScreen() throws Exception {
// Launch first activity
final Activity firstActivity =
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..8a4fbaf 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,20 +93,26 @@
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);
// 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
+ @FlakyTest(bugId = 127741025)
public void testOccludingMovedBetweenStacks() throws Exception {
// Launch first activity
final Activity firstActivity =
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..de26026 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,8 @@
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.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 +36,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,6 +54,7 @@
import androidx.test.filters.FlakyTest;
import androidx.test.filters.MediumTest;
+import androidx.test.rule.ActivityTestRule;
import com.android.compatibility.common.util.AmUtils;
@@ -63,7 +67,6 @@
* Build/Install/Run:
* atest CtsWindowManagerDeviceTestCases:ActivityLifecycleTests
*/
-@FlakyTest(bugId = 77652261)
@MediumTest
@Presubmit
public class ActivityLifecycleTests extends ActivityLifecycleClientTestBase {
@@ -273,6 +276,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());
@@ -730,4 +812,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..7d0b4b3 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
@@ -19,6 +19,7 @@
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 +69,6 @@
* Build/Install/Run:
* atest CtsWindowManagerDeviceTestCases:ActivityLifecycleTopResumedStateTests
*/
-@FlakyTest(bugId = 117135575)
@MediumTest
@Presubmit
public class ActivityLifecycleTopResumedStateTests extends ActivityLifecycleClientTestBase {
@@ -988,6 +988,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());
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..6d2bab9
--- /dev/null
+++ b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityTests.java
@@ -0,0 +1,81 @@
+/*
+ * 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.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_DESTROY;
+import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_RESUME;
+import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_STOP;
+
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+
+import android.app.Activity;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.MediumTest;
+
+import org.junit.Test;
+
+/**
+ * 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());
+ }
+}
\ 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..928d618 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,46 @@
|| 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(
+ /** @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 +905,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..0259e60 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
@@ -67,6 +67,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";
@@ -167,12 +172,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);
}
}
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..f9c66f7 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;
@@ -62,7 +63,6 @@
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 +93,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 +123,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;
@@ -163,7 +162,6 @@
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;
@@ -450,6 +448,15 @@
}
+ /**
+ * 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) {
SystemUtil.runWithShellPermissionIdentity(
() -> mAtm.moveTopActivityToPinnedStack(stackId, new Rect(0, 0, 500, 500))
@@ -498,6 +505,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,6 +577,12 @@
getInstrumentation().waitForIdleSync();
}
+ static void waitForOrFail(String message, BooleanSupplier condition) {
+ Condition.waitFor(new Condition<>(message, condition)
+ .setRetryIntervalMs(500)
+ .setOnFailure(unusedResult -> fail("FAILED because unsatisfied: " + message)));
+ }
+
/** Returns the set of stack ids. */
private HashSet<Integer> getStackIds() {
mAmWmState.computeState(true);
@@ -833,9 +853,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 +1111,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 +1231,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 +1252,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 +1410,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 +1430,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 +1494,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 +1502,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 +1510,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 +1518,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 +1526,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 +1568,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 +1576,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 +1588,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 +1609,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 +1620,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 +1673,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 +1708,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 +1734,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 +1788,11 @@
return this;
}
+ public LaunchActivityBuilder setLaunchTaskBehind(boolean launchTaskBehind) {
+ mLaunchTaskBehind = launchTaskBehind;
+ return this;
+ }
+
public LaunchActivityBuilder setReorderToFront(boolean reorderToFront) {
mReorderToFront = reorderToFront;
return this;
@@ -1961,6 +1923,7 @@
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/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/WindowManagerState.java b/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerState.java
index 0ca2748..2af720d 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;
}
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 8a9e49c..1b68764 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..8f6d37b
--- /dev/null
+++ b/tests/tests/appenumeration/AndroidTest.xml
@@ -0,0 +1,36 @@
+<?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="CtsAppEnumerationForceQueryableApp.apk" />
+ <option name="test-file-name" value="CtsAppEnumerationFiltersApp.apk" />
+ <option name="test-file-name" value="CtsAppEnumerationNoApiApp.apk" />
+ <option name="test-file-name" value="CtsAppEnumerationQueriesNothingApp.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/common/device-side/util/tests/Android.bp b/tests/tests/appenumeration/app/source/Android.bp
similarity index 65%
rename from common/device-side/util/tests/Android.bp
rename to tests/tests/appenumeration/app/source/Android.bp
index 2abba37..8919822 100644
--- a/common/device-side/util/tests/Android.bp
+++ b/tests/tests/appenumeration/app/source/Android.bp
@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -12,17 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-java_test {
- name: "compatibility-device-util-tests",
-
+android_test_helper_app {
+ name: "CtsAppEnumerationQueriesNothingApp",
+ manifest: "AndroidManifest-queriesNothing.xml",
+ defaults: ["cts_support_defaults"],
srcs: ["src/**/*.java"],
-
- static_libs: [
- "compatibility-device-util",
- "junit",
- "testng", // TODO: remove once Android migrates to JUnit 4.12, which provide assertThrows
- "truth-prebuilt"
+ // 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-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/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..260014d
--- /dev/null
+++ b/tests/tests/appenumeration/app/source/src/android/appenumeration/cts/query/TestActivity.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.appenumeration.cts.query;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.os.Bundle;
+import android.os.RemoteCallback;
+
+public class TestActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ RemoteCallback remoteCallback =
+ getIntent().getParcelableExtra("remoteCallback");
+ Bundle result = new Bundle();
+ try {
+
+ final String action = getIntent().getAction();
+ switch (action) {
+ case "android.appenumeration.cts.action.GET_PACKAGE_INFO":
+ final String packageName = getIntent().getStringExtra(
+ Intent.EXTRA_PACKAGE_NAME);
+ final PackageInfo pi = getPackageManager().getPackageInfo(packageName, 0);
+ result.putParcelable(Intent.EXTRA_RETURN_RESULT, pi);
+ break;
+ default:
+ result.putSerializable("error",
+ new Exception("unknown action " + action));
+ break;
+ }
+ } catch (Exception failure) {
+ result.putSerializable("error", failure);
+ } finally {
+ 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..e4d494a
--- /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: "CtsAppEnumerationForceQueryableApp",
+ 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: "CtsAppEnumerationFiltersApp",
+ 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: "CtsAppEnumerationNoApiApp",
+ 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..04aae68
--- /dev/null
+++ b/tests/tests/appenumeration/app/target/AndroidManifest-filters.xml
@@ -0,0 +1,40 @@
+<?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" />
+ <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..775b4a8
--- /dev/null
+++ b/tests/tests/appenumeration/src/android/appenumeration/cts/AppEnumerationTests.java
@@ -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.appenumeration.cts;
+
+import static org.junit.Assert.assertTrue;
+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.os.Bundle;
+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.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+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 TARGET_NO_API_NAME = "android.appenumeration.noapi";
+ private static final String TARGET_FORCEQUERYABLE_NAME = "android.appenumeration.forcequeryable";
+ private static final String TARGET_FILTERS_NAME = "android.appenumeration.filters";
+
+ private static final String QUERIES_NOTHING_NAME = "android.appenumeration.queries.nothing";
+
+ private Context mContext;
+ private Handler mResponseHandler;
+ private HandlerThread mResponseThread;
+
+ private boolean mGlobalFeatureEnabled;
+
+ @Before
+ public void setup() {
+ mGlobalFeatureEnabled = Boolean.parseBoolean(SystemUtil.runShellCommand(
+ "device_config get package_manager_service package_query_filtering_enabled"));
+ if (!mGlobalFeatureEnabled) return;
+
+ mContext = InstrumentationRegistry.getInstrumentation().getContext();
+ mResponseThread = new HandlerThread("response");
+ mResponseThread.start();
+ mResponseHandler = new Handler(mResponseThread.getLooper());
+
+ enableFeatureForPackage(QUERIES_NOTHING_NAME);
+ }
+
+ private void enableFeatureForPackage(String packageName) {
+ final String response =
+ SystemUtil.runShellCommand("am compat enable 135549675 " + packageName);
+ assertTrue(response.contains("Enabled"));
+ }
+
+ @After
+ public void tearDown() {
+ if (!mGlobalFeatureEnabled) return;
+ mResponseThread.quit();
+ }
+
+ @Test
+ public void queriesNothing_canSeeForceQueryable() throws Exception {
+ if (!mGlobalFeatureEnabled) return;
+ final PackageInfo packageInfo =
+ getPackageInfo(QUERIES_NOTHING_NAME, TARGET_FORCEQUERYABLE_NAME);
+ Assert.assertTrue(packageInfo != null);
+ }
+
+ @Test
+ public void queriesNothing_cannotSeeNoApi() throws Exception {
+ if (!mGlobalFeatureEnabled) return;
+ try {
+ getPackageInfo(QUERIES_NOTHING_NAME, TARGET_NO_API_NAME);
+ fail("App that queries nothing should not see other packages.");
+ } catch (PackageManager.NameNotFoundException e) {
+ }
+ }
+
+ private PackageInfo getPackageInfo(String sourcePackageName, String targetPackageName)
+ throws Exception {
+ Bundle response = sendCommand(sourcePackageName, targetPackageName,
+ "android.appenumeration.cts.action.GET_PACKAGE_INFO");
+ return response.getParcelable(Intent.EXTRA_RETURN_RESULT);
+ }
+
+ private Bundle sendCommand(String sourcePackageName, String targetPackageName, String action)
+ throws Exception {
+ Intent intent = new Intent(action)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ .putExtra(Intent.EXTRA_PACKAGE_NAME, targetPackageName)
+ .setComponent(new ComponentName(
+ sourcePackageName, "android.appenumeration.cts.query.TestActivity"));
+ final CountDownLatch countDownLatch = new CountDownLatch(1);
+ final AtomicReference<Bundle> resultReference = new AtomicReference<>();
+ RemoteCallback callback = new RemoteCallback(
+ bundle -> {
+ resultReference.set(bundle);
+ countDownLatch.countDown();
+ },
+ mResponseHandler);
+ intent.putExtra("remoteCallback", callback);
+ mContext.startActivity(intent);
+ if (!countDownLatch.await(5, TimeUnit.SECONDS)) {
+ throw new TimeoutException("Latch timed out!");
+ }
+ 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..301f288 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,50 @@
"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",
+ "libhidltransport",
+ "libhwbinder",
+ "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..985c73e
--- /dev/null
+++ b/tests/tests/appop/src/android/app/appops/cts/AppOpsLoggingTest.kt
@@ -0,0 +1,465 @@
+/*
+ * 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 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.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
+)
+
+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)
+ }
+ })
+ }
+
+ 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..ffb95e6 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,27 @@
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 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
@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
// Start an activity to make sure this app counts as being in the foreground
@Rule @JvmField
@@ -51,58 +47,51 @@
@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()
+ appOpsManager.clearHistory()
+ appOpsManager.resetHistoryParameters()
}
@After
fun tearDownTest() {
- appOpsManager!!.clearHistory()
- appOpsManager!!.resetHistoryParameters()
- val uiAutomation = getInstrumentation().getUiAutomation()
+ appOpsManager.clearHistory()
+ appOpsManager.resetHistoryParameters()
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 +100,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 +137,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 +156,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 +166,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 +189,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 +199,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 +214,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 +235,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 +260,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 +275,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 +325,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 +336,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 +367,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 +418,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 +434,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 +457,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 +482,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/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/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/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/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/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/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/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..ae9ecb2 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,29 @@
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 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 },
};
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/BitmapTest.java b/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java
index a2025bd..232d087 100644
--- a/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java
@@ -1959,7 +1959,7 @@
nValidateBitmapInfo(bitmap, 10, 20, true);
bitmap.recycle();
nValidateBitmapInfo(bitmap, 10, 20, true);
- nValidateNdkAccessAfterRecycle(bitmap);
+ nValidateNdkAccessFails(bitmap);
}
@Test
@@ -2022,15 +2022,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 +2161,48 @@
}
}
+ @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 void strictModeTest(Runnable runnable) {
StrictMode.ThreadPolicy originalPolicy = StrictMode.getThreadPolicy();
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
@@ -2177,12 +2219,35 @@
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;
private static final int ANDROID_BITMAP_FORMAT_RGB_565 = 4;
+ 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),
+ };
+
private static native int nGetFormat(Bitmap bitmap);
private static HardwareBuffer createTestBuffer(int width, int height, boolean cpuAccess) {
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..9ba5006 100644
--- a/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java
@@ -379,46 +379,44 @@
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);
+ ImageDecoder.Source src = ImageDecoder.createSource(mRes, 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);
- }
- assertNotNull(bm);
+ 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());
+ 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;
- }
+ 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;
}
}
}
@@ -519,32 +517,30 @@
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(mRes, 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);
- }
+ 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);
}
+ } else {
+ assertEquals(bitmap.getConfig(), Bitmap.Config.HARDWARE);
}
}
}
@@ -1219,47 +1215,41 @@
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 = f.apply(record.resId);
-
- 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);
- }
- }
+ 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);
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());
+ assertEquals(l.width, drawable.getIntrinsicWidth());
+ assertEquals(l.height, 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());
+ Bitmap bm = ImageDecoder.decodeBitmap(src, l);
+ assertEquals(l.width, bm.getWidth());
+ assertEquals(l.height, bm.getHeight());
} catch (IOException e) {
- fail("Failed with exception " + 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);
+ }
}
}
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..5516727 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/GradientDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/GradientDrawableTest.java
@@ -751,6 +751,14 @@
}
@Test
+ public void testGradientNoAngle() {
+ final Context context = InstrumentationRegistry.getTargetContext();
+ GradientDrawable drawable = (GradientDrawable)
+ context.getDrawable(R.drawable.gradientdrawable_no_angle);
+ assertEquals(Orientation.LEFT_RIGHT, drawable.getOrientation());
+ }
+
+ @Test
public void testGradientDrawableOrientationConstructor() {
GradientDrawable drawable = new GradientDrawable(Orientation.TOP_BOTTOM, null);
assertEquals(Orientation.TOP_BOTTOM, drawable.getOrientation());
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/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp b/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp
index 25f4b0e..415bef0 100644
--- a/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp
+++ b/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp
@@ -47,7 +47,7 @@
#endif
static const std::string kSystemLibraryPath = "/system/" LIB_DIR;
-static const std::string kRuntimeApexLibraryPath = "/apex/com.android.runtime/" LIB_DIR;
+static const std::string kArtApexLibraryPath = "/apex/com.android.art/" LIB_DIR;
static const std::string kVendorLibraryPath = "/vendor/" LIB_DIR;
static const std::string kProductLibraryPath = "/product/" LIB_DIR;
@@ -377,7 +377,7 @@
// These paths should be tested too - this is because apps may rely on some
// libraries being available there.
system_library_search_paths.insert(kSystemLibraryPath);
- system_library_search_paths.insert(kRuntimeApexLibraryPath);
+ system_library_search_paths.insert(kArtApexLibraryPath);
if (!check_path(env, clazz, kSystemLibraryPath, system_library_search_paths,
system_public_libraries,
@@ -391,7 +391,7 @@
bool check_absence = !android::base::GetBoolProperty("ro.vndk.lite", false);
// Check the runtime libraries.
- if (!check_path(env, clazz, kRuntimeApexLibraryPath, {kRuntimeApexLibraryPath},
+ if (!check_path(env, clazz, kArtApexLibraryPath, {kArtApexLibraryPath},
runtime_public_libraries,
// System.loadLibrary("icuuc") would fail since a copy exists in /system.
// TODO(b/124218500): Change to true when the bug is resolved.
diff --git a/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java b/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java
index 439284d..e8d23fa 100644
--- a/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java
+++ b/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java
@@ -45,7 +45,6 @@
private final static Pattern EXTENSION_CONFIG_FILE_PATTERN = Pattern.compile(
"public\\.libraries-([A-Za-z0-9\\-_.]+)\\.txt");
private final static String VENDOR_CONFIG_FILE = "/vendor/etc/public.libraries.txt";
- private final static String RUNTIME_APEX_DIR = "/apex/com.android.runtime";
private final static String[] PUBLIC_SYSTEM_LIBRARIES = {
"libaaudio.so",
"libamidi.so",
@@ -73,8 +72,8 @@
"libz.so"
};
- // Libraries listed in public.libraries.android.txt, located in RUNTIME_APEX_DIR path
- private final static String[] PUBLIC_RUNTIME_LIBRARIES = {
+ // Libraries listed in public.libraries.android.txt, located in ART_APEX_DIR path
+ private final static String[] PUBLIC_ART_LIBRARIES = {
"libicui18n.so",
"libicuuc.so",
};
@@ -154,7 +153,7 @@
public static String runAccessibilityTest() throws IOException {
List<String> systemLibs = new ArrayList<>();
- List<String> runtimeApexLibs = new ArrayList<>();
+ List<String> artApexLibs = new ArrayList<>();
Collections.addAll(systemLibs, PUBLIC_SYSTEM_LIBRARIES);
@@ -163,7 +162,7 @@
systemLibs.add(WEBVIEW_PLAT_SUPPORT_LIB);
}
- Collections.addAll(runtimeApexLibs, PUBLIC_RUNTIME_LIBRARIES);
+ Collections.addAll(artApexLibs, PUBLIC_ART_LIBRARIES);
// Check if public.libraries.txt contains libs other than the
// public system libs (NDK libs).
@@ -197,7 +196,7 @@
}
return runAccessibilityTestImpl(systemLibs.toArray(new String[systemLibs.size()]),
- runtimeApexLibs.toArray(new String[runtimeApexLibs.size()]),
+ artApexLibs.toArray(new String[artApexLibs.size()]),
vendorLibs.toArray(new String[vendorLibs.size()]),
productLibs.toArray(new String[productLibs.size()]));
}
@@ -355,7 +354,7 @@
try {
List<String> publicLibs = new ArrayList<>();
Collections.addAll(publicLibs, PUBLIC_SYSTEM_LIBRARIES);
- Collections.addAll(publicLibs, PUBLIC_RUNTIME_LIBRARIES);
+ Collections.addAll(publicLibs, PUBLIC_ART_LIBRARIES);
error = readExtensionConfigFiles(PUBLIC_CONFIG_DIR, publicLibs);
if (error != null) return error;
error = readExtensionConfigFiles(PRODUCT_CONFIG_DIR, publicLibs);
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/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 a15b1ec..eec4cd8 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/src/android/media/cts/AudioManagerTest.java b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
index 4aa72d0..659bea9 100644
--- a/tests/tests/media/src/android/media/cts/AudioManagerTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
@@ -178,13 +178,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;
- }
-
final MyBlockingIntentReceiver receiver = new MyBlockingIntentReceiver(
AudioManager.ACTION_MICROPHONE_MUTE_CHANGED);
final boolean initialMicMute = mAudioManager.isMicrophoneMute();
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..ddd1be7 100644
--- a/tests/tests/media/src/android/media/cts/AudioRecordTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioRecordTest.java
@@ -35,6 +35,7 @@
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;
@@ -1649,4 +1650,46 @@
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..660094a 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;
@@ -481,6 +482,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);
}
diff --git a/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java b/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java
index 94f334c..f7231fd 100644
--- a/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java
@@ -40,7 +40,8 @@
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;
@@ -246,6 +247,10 @@
}
private void testThumbnail(int resId) {
+ testThumbnail(resId, null /*outPath*/);
+ }
+
+ private void testThumbnail(int resId, String outPath) {
if (!MediaUtils.hasCodecForResourceAndDomain(getContext(), resId, "video/")) {
MediaUtils.skipTest("no video codecs for resource");
return;
@@ -263,7 +268,29 @@
fail("Unable to open file");
}
- assertNotNull(retriever.getFrameAtTime(-1 /* timeUs (any) */));
+ Bitmap thumbnail = retriever.getFrameAtTime(-1 /* timeUs (any) */);
+ retriever.release();
+
+ 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");
+ }
+ }
}
public void testThumbnailH264() {
@@ -290,6 +317,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.
diff --git a/tests/tests/media/src/android/media/cts/MediaRecorderTest.java b/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
index 70d5c20..f929e4b 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;
@@ -1703,5 +1704,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/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/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..dc5e5af 100644
--- a/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTest.java
+++ b/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTest.java
@@ -222,6 +222,10 @@
@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()");
@@ -250,6 +254,10 @@
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));
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 1762edc..c772572 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: [
@@ -40,28 +38,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/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..8fcdc9c 100644
--- a/tests/tests/permission/AndroidTest.xml
+++ b/tests/tests/permission/AndroidTest.xml
@@ -50,6 +50,7 @@
<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" />
</target_preparer>
<!-- Remove additional apps if installed -->
diff --git a/common/device-side/util/tests/Android.bp b/tests/tests/permission/AppThatRunsRationaleTests/Android.bp
similarity index 62%
copy from common/device-side/util/tests/Android.bp
copy to tests/tests/permission/AppThatRunsRationaleTests/Android.bp
index 2abba37..6bd320b 100644
--- a/common/device-side/util/tests/Android.bp
+++ b/tests/tests/permission/AppThatRunsRationaleTests/Android.bp
@@ -1,28 +1,32 @@
-// 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",
+ "cts_instant",
+ ],
+
+ 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 e37d064..9c17e51 100644
--- a/tests/tests/permission/OWNERS
+++ b/tests/tests/permission/OWNERS
@@ -3,3 +3,4 @@
per-file PowerManagerServicePermissionTest.java = dehboxturtle@google.com
per-file RequestLocation.java = hallliu@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 db30b83..49af2bf 100644
--- a/tests/tests/permission/src/android/permission/cts/LocationAccessCheckTest.java
+++ b/tests/tests/permission/src/android/permission/cts/LocationAccessCheckTest.java
@@ -54,7 +54,6 @@
import android.os.Bundle;
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;
@@ -67,6 +66,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;
@@ -77,8 +77,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;
@@ -213,26 +211,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..a03b61d
--- /dev/null
+++ b/tests/tests/permission/src/android/permission/cts/PermissionUpdateListenerTest.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 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.addOnPermissionsChangeListener(listenerCalled);
+ sPm.grantRuntimePermission(PACKAGE_NAME, PERMISSION_NAME, sContext.getUser());
+ });
+ waitForLatchAndRemoveListener(listenerCalled);
+ }
+
+ @Test
+ public void revokeNotifiesListener() throws Exception {
+ LatchWithPermissionsChangedListener listenerCalled =
+ new LatchWithPermissionsChangedListener();
+
+ runWithShellPermissionIdentity(() -> {
+ 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/permission2/Android.bp b/tests/tests/permission2/Android.bp
index f4f25b0..a816a55 100644
--- a/tests/tests/permission2/Android.bp
+++ b/tests/tests/permission2/Android.bp
@@ -26,6 +26,7 @@
],
libs: ["android.test.base.stubs"],
static_libs: [
+ "androidx.test.core",
"compatibility-device-util-axt",
"ctstestrunner-axt",
"guava",
@@ -35,4 +36,22 @@
],
srcs: ["src/**/*.java"],
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 d43b866..d345d4f 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. -->
@@ -2829,6 +2817,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.-->
@@ -3457,6 +3450,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
@@ -4398,12 +4396,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. -->
@@ -4463,6 +4461,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"
@@ -4537,6 +4542,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/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
new file mode 100644
index 0000000..f45aed1
--- /dev/null
+++ b/tests/tests/print/Android.mk
@@ -0,0 +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.
+
+include $(call all-subdir-makefiles)
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 95%
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..ae57425 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,17 @@
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.Environment;
-import android.platform.test.annotations.Presubmit;
import android.provider.MediaStore;
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 +48,6 @@
import java.io.File;
-@Presubmit
@RunWith(Parameterized.class)
public class MediaStore_Audio_MediaTest {
private Context mContext;
@@ -78,6 +79,9 @@
Media.getContentUri(mVolumeName), null, null,
null, null));
c.close();
+
+ assertEquals(ContentUris.withAppendedId(Media.getContentUri(mVolumeName), 42),
+ Media.getContentUri(mVolumeName, 42));
}
@Test
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 ec78a94..67df33a 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;
@@ -26,6 +26,7 @@
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;
@@ -35,19 +36,18 @@
import android.net.Uri;
import android.os.ParcelFileDescriptor;
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;
@@ -62,7 +62,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";
@@ -200,7 +199,6 @@
assertEquals(src.getHeight(), result.getHeight());
}
- @Presubmit
@Test
public void testGetContentUri() {
Cursor c = null;
@@ -210,6 +208,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 6a7a1f9..c53536e 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;
@@ -28,26 +28,26 @@
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.media.MediaExtractor;
import android.media.MediaMetadataRetriever;
import android.net.Uri;
+import android.os.FileUtils;
import android.os.ParcelFileDescriptor;
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;
@@ -63,7 +63,6 @@
import java.io.OutputStream;
import java.util.Arrays;
-@Presubmit
@RunWith(Parameterized.class)
public class MediaStore_Video_MediaTest {
private Context mContext;
@@ -97,6 +96,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) {
@@ -106,18 +108,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");
@@ -166,7 +164,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)));
@@ -221,7 +219,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();
}
@@ -237,8 +235,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"));
@@ -261,8 +261,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"));
@@ -287,7 +289,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 8a78d0c..ef992e6 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 494125f..38f12ee 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 11f0200..335c08a 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..719e062
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/PackageInstallerTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.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
+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/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/selinux/common/src/android/security/SELinuxTargetSdkTestBase.java b/tests/tests/selinux/common/src/android/security/SELinuxTargetSdkTestBase.java
index 587c3e8..e4dc206 100644
--- a/tests/tests/selinux/common/src/android/security/SELinuxTargetSdkTestBase.java
+++ b/tests/tests/selinux/common/src/android/security/SELinuxTargetSdkTestBase.java
@@ -82,15 +82,15 @@
* Check expectations of being able to read/execute dex2oat.
*/
protected static void checkDex2oatAccess(boolean expectedAllowed) throws Exception {
- // First check whether there is an Android Runtime APEX dex2oat binary.
- File dex2oatRuntimeApexBinary = new File("/apex/com.android.runtime/bin/dex2oat");
- if (dex2oatRuntimeApexBinary.exists()) {
- checkDex2oatBinaryAccess(dex2oatRuntimeApexBinary, expectedAllowed);
- }
- // Also check whether there is a "legacy" system binary.
- File dex2oatSystemBinary = new File("/system/bin/dex2oat");
- if (dex2oatSystemBinary.exists()) {
- checkDex2oatBinaryAccess(dex2oatSystemBinary, expectedAllowed);
+ // Check the dex2oat binary in its current and legacy locations.
+ String[] locations = {"/apex/com.android.art/bin",
+ "/apex/com.android.runtime/bin",
+ "/system/bin"};
+ for (String loc : locations) {
+ File dex2oatBinary = new File(loc + "/dex2oat");
+ if (dex2oatBinary.exists()) {
+ checkDex2oatBinaryAccess(dex2oatBinary, expectedAllowed);
+ }
}
}
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/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/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 6803431..340841e 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/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/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/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/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 22dd8f6..67f3ed7 100644
--- a/tests/tests/uiautomation/Android.bp
+++ b/tests/tests/uiautomation/Android.bp
@@ -23,7 +23,7 @@
"cts_instant",
],
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..b1e5821 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.RECORD_AUDIO" />
- <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..05bfc9c 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.RECORD_AUDIO" />
</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..f5df2c0 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.RECORD_AUDIO, 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.RECORD_AUDIO,
context.getPackageName()), PackageManager.PERMISSION_DENIED);
packageManager.grantRuntimePermission(context.getPackageName(),
- Manifest.permission.CAMERA, Process.myUserHandle());
+ Manifest.permission.RECORD_AUDIO, 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.RECORD_AUDIO,
context.getPackageName()), PackageManager.PERMISSION_GRANTED);
@@ -136,7 +136,7 @@
}
try {
packageManager.revokeRuntimePermission(context.getPackageName(),
- Manifest.permission.CAMERA, Process.myUserHandle());
+ Manifest.permission.RECORD_AUDIO, 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/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/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..e97ddb8 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));
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/OWNERS b/tests/tests/voiceinteraction/OWNERS
index dbffaa0..fcb224c 100644
--- a/tests/tests/voiceinteraction/OWNERS
+++ b/tests/tests/voiceinteraction/OWNERS
@@ -1,2 +1,4 @@
# Bug component: 533220
+adamhe@google.com
+svetoslavganov@google.com
felipeal@google.com
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/OWNERS b/tests/tests/voicesettings/OWNERS
index dbffaa0..fcb224c 100644
--- a/tests/tests/voicesettings/OWNERS
+++ b/tests/tests/voicesettings/OWNERS
@@ -1,2 +1,4 @@
# Bug component: 533220
+adamhe@google.com
+svetoslavganov@google.com
felipeal@google.com
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/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/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/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..63ad006 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
@@ -121,6 +122,12 @@
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